From a69e97943539a8abc4d2762638c169dc19c88516 Mon Sep 17 00:00:00 2001 From: Javier Date: Sun, 7 Jun 2015 21:22:45 +0200 Subject: initial import --- afdnotebook.cc | 399 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 399 insertions(+) create mode 100644 afdnotebook.cc (limited to 'afdnotebook.cc') diff --git a/afdnotebook.cc b/afdnotebook.cc new file mode 100644 index 0000000..38f1fdc --- /dev/null +++ b/afdnotebook.cc @@ -0,0 +1,399 @@ +#include +#include +#include +#include "xmlutils.h" +#include "smartpen.h" +#include "afdnotebook.h" + +AfdNotebook::AfdNotebook(QObject *parent) + : QObject(parent) +{ +} + +AfdNotebook::~AfdNotebook() +{ +} + +bool AfdNotebook::open(const QString &path) +{ + _dir.setPath(path); + if (!_dir.exists()) { + qWarning() << "Directory" << _dir.absolutePath() << "does not exist"; + return false; + } + if (!parseMainInfo()) { + return false; + } + if (!parseMainDocument()) { + return false; + } + if (!findPenData()) { + return false; + } + return true; +} + +void AfdNotebook::close() +{ + _title.clear(); + _lastPage = _firstPage = PageAddress(0, 0, 0, 0, 0); + _pagesPerBook = 0; + _gfx.clear(); + _pages.clear(); + _penData.clear(); + _dir.setPath(QString()); +} + +QString AfdNotebook::title() const +{ + return _title; +} + +int AfdNotebook::numPages() const +{ + return _pages.size(); +} + +QString AfdNotebook::getPageBackgroundName(int page) const +{ + const Page& p = _pages.at(page); + return p.gfx->basename; +} + +QPixmap AfdNotebook::getPageBackground(int page) +{ + const Page& p = _pages.at(page); + QPixmap pix; + if (QPixmapCache::find(p.gfx->basename, &pix)) { + return pix; + } + + const QString file = QString("userdata/lsac_data/%1.png").arg(p.gfx->basename); + QImage img; + + if (!img.load(_dir.filePath(file), "PNG")) { + qWarning() << "Could not load background file:" << _dir.absoluteFilePath(file); + return pix; + } + + QRect cropRect = img.rect(); + QRect trim = getPageTrim(page); + QPointF scale(cropRect.width() / double(p.size.width()), + cropRect.height() / double(p.size.height())); + + cropRect.adjust(lround(trim.x() * scale.x()), lround(trim.y() * scale.y()), + lround(-(p.size.width() - trim.bottomRight().x()) * scale.x()), + lround(-(p.size.height() - trim.bottomRight().y()) * scale.y())); + + qDebug() << "Cropping image from" << img.rect() << "to" << cropRect; + + pix = QPixmap::fromImage(img.copy(cropRect)); + QPixmapCache::insert(p.gfx->basename, pix); + + return pix; +} + +QSize AfdNotebook::getPageSize(int page) const +{ + const Page &p = _pages.at(page); + return p.size; +} + +QRect AfdNotebook::getPageTrim(int page) const +{ + const Page &p = _pages.at(page); + QRect trim(QPoint(0, 0), p.size); + if (p.size.width() > SMARTPEN_BLEED_X * 2 && p.size.height() > SMARTPEN_BLEED_Y * 2) { + trim.adjust(SMARTPEN_BLEED_X, SMARTPEN_BLEED_Y, -SMARTPEN_BLEED_X, -SMARTPEN_BLEED_Y); + } + return trim; +} + +QStringList AfdNotebook::penSerials() const +{ + return _penData.keys(); +} + +QList AfdNotebook::pagesWithStrokes(const QString &penSerial) const +{ + if (_penData.contains(penSerial)) { + const PenData &data = _penData[penSerial]; + return data.strokes.uniqueKeys(); + } else { + return QList(); + } +} + +QStringList AfdNotebook::strokeFiles(const QString &penSerial, int page) const +{ + QStringList l; + if (!_penData.contains(penSerial)) return l; + + const PenData &data = _penData[penSerial]; + QMultiMap::const_iterator it = data.strokes.find(page); + while (it != data.strokes.end() && it.key() == page) { + const StrokeData &stroke = it.value(); + l.append(_dir.filePath(stroke.file)); + ++it; + } + + return l; +} + +bool AfdNotebook::readStrokes(const QString &penSerial, int page, StfReader::StrokeHandler *handler) +{ + if (!_penData.contains(penSerial)) return true; + + StfReader stf; + stf.setStrokeHandler(handler); + + const PenData &data = _penData[penSerial]; + QMultiMap::const_iterator it = data.strokes.find(page); + while (it != data.strokes.end() && it.key() == page) { + const StrokeData &stroke = it.value(); + qDebug() << "Reading strokes from" << stroke.file; + if (!stf.parse(_dir.filePath(stroke.file))) { + qWarning() << "Could not parse stroke file" << stroke.file; + return false; + } + ++it; + } + + return true; +} + +AfdNotebook::PageAddress::PageAddress(const QString &str) +{ + QStringList parts = str.split('.'); + if (parts.count() == 5) { + series = parts[0].toUInt(); + shelf = parts[1].toUInt(); + segment = parts[2].toUInt(); + book = parts[3].toUInt(); + page = parts[4].toUInt(); + } else if (parts.count() == 4) { + series = 0; + shelf = parts[0].toUInt(); + segment = parts[1].toUInt(); + book = parts[2].toUInt(); + page = parts[3].toUInt(); + } else { + qWarning() << "Unknown page address syntax:" << str; + } +} + +QString AfdNotebook::PageAddress::toString() const +{ + QStringList l; + l.reserve(5); + + if (series) { + l.append(QString::number(series)); + } + + l.append(QString::number(shelf)); + l.append(QString::number(segment)); + l.append(QString::number(book)); + l.append(QString::number(page)); + + return l.join("."); +} + +QMap AfdNotebook::parsePropertyList(QIODevice *dev) +{ + QMap result; + QByteArray line; + + while (!(line = dev->readLine()).isEmpty()) { + int sep = line.indexOf(':'); + QString key = QString::fromLatin1(line.constData(), sep); + result[key] = QString::fromUtf8(line.constData() + sep + 1).trimmed(); + } + + return result; +} + +QMap AfdNotebook::parsePropertyList(const QString &relativePath) const +{ + QFile f(_dir.filePath(relativePath)); + if (!f.open(QIODevice::ReadOnly | QIODevice::Text)) { + return QMap(); + } + return parsePropertyList(&f); +} + +bool AfdNotebook::parseMainInfo() +{ + QMap info = parsePropertyList("main.info"); + + if (info.isEmpty()) { + qWarning() << "Empty main.info"; + return false; + } + + _title = info["title"]; + _firstPage = PageAddress(info["pagestart"]); + _lastPage = PageAddress(info["pagestop"]); + _pagesPerBook = info.value("segment-pages-per-book", "108").toUInt(); + + return true; +} + +bool AfdNotebook::parseMainDocument() +{ + QFile f(_dir.filePath("main.document")); + if (!f.open(QIODevice::ReadOnly | QIODevice::Text)) { + return false; + } + + QXmlStreamReader r(&f); + + Q_ASSERT(_pages.isEmpty()); + Q_ASSERT(_gfx.isEmpty()); + + advanceToFirstChildElement(r, "document"); + while (r.readNextStartElement()) { + if (r.name() == "page") { + QXmlStreamAttributes attrs = r.attributes(); + QString gfxfile; + gfxfile += attrs.value("basepath"); + gfxfile += '/'; + gfxfile += attrs.value("gfxfile_ref"); + + Page p; + p.gfx = &_gfx[gfxfile]; + p.size.setWidth(attrs.value("width").toString().toInt()); + p.size.setHeight(attrs.value("height").toString().toInt()); + _pages.append(p); + + r.skipCurrentElement(); + } else { + qWarning() << "Ignoring unknown element" << r.name() << "in main.document"; + } + } + + if (_pages.isEmpty()) { + qWarning() << "Notebook has no pages"; + return false; + } + + foreach (const QString &gfxfile, _gfx.keys()) { + if (!parseGfx(gfxfile)) { + return false; + } + } + + return true; +} + +bool AfdNotebook::parseGfx(const QString &file) +{ + QFile f(_dir.filePath(file)); + if (!f.open(QIODevice::ReadOnly | QIODevice::Text)) { + return false; + } + + QXmlStreamReader r(&f); + + Gfx &gfx = _gfx[file]; + Q_ASSERT(gfx.basename.isEmpty()); + + advanceToFirstChildElement(r, "graphics"); + advanceToFirstChildElement(r, "setbase"); + advanceToFirstChildElement(r, "image"); + if (!r.atEnd()) { + QXmlStreamAttributes attrs = r.attributes(); + QString imageSrc = attrs.value("src").toString(); + qDebug() << "image src" << imageSrc; + int lastSlash = imageSrc.lastIndexOf('/'); + int lastDot = imageSrc.lastIndexOf('.'); + if (lastSlash >= 0 && lastDot > lastSlash) { + gfx.basename = imageSrc.mid(lastSlash + 1, lastDot - lastSlash - 1); + qDebug() << "Got gfx" << gfx.basename; + } + } + + return true; +} + +bool AfdNotebook::findPenData() +{ + QDir dir(_dir.filePath("data")); + if (!dir.exists()) { + return false; + } + + QStringList pageDirs = dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot); + foreach (QString pageName, pageDirs) { + pageName.remove('/'); + qDebug() << " page data" << pageName; + int pageNum = getPageNumber(PageAddress(pageName)); + if (pageNum < 0) continue; + + QDir pageDir(dir.filePath(pageName)); + if (!pageDir.exists()) continue; + QStringList penDirs = pageDir.entryList(QDir::Dirs | QDir::NoDotAndDotDot); + foreach (QString penName, penDirs) { + penName.remove('/'); + qDebug() << " pen data" << penName; + QDir penDir(pageDir.filePath(penName)); + if (!penDir.exists()) continue; + + QStringList strokeFiles = penDir.entryList(QStringList("*.stf"), QDir::Files); + foreach (const QString &strokeFile, strokeFiles) { + qDebug() << " stroke data" << strokeFile; + + if (strokeFile.length() != 25) { + qWarning() << "Invalid stroke filename format" << strokeFile << endl; + continue; + } + + StrokeData stroke; + stroke.file = penDir.filePath(strokeFile); + bool ok = true; + + qDebug() << " " << strokeFile.mid(2, 8) << strokeFile.mid(13, 8); + qDebug() << " " << strokeFile.mid(2, 8).toUInt(0, 16) << strokeFile.mid(13, 8).toUInt(0, 16); + + if (ok) stroke.begin = Smartpen::fromPenTime(strokeFile.mid(2, 8).toLongLong(&ok, 16) * 1000ULL); + if (ok) stroke.end = Smartpen::fromPenTime(strokeFile.mid(13, 8).toLongLong(&ok, 16) * 1000ULL); + + if (!ok) { + qWarning() << "Invalid stroke filename format" << strokeFile << endl; + continue; + } + + qDebug() << " from" << stroke.begin << "to" << stroke.end; + + _penData[penName].strokes.insert(pageNum, stroke); + } + } + } + + return true; +} + +AfdNotebook::PageAddress AfdNotebook::getPageAddress(int page) const +{ + PageAddress addr = _firstPage; + Q_ASSERT(page >= 0); + uint new_page = addr.page + page; + addr.book += new_page / _pagesPerBook; + addr.page = new_page % _pagesPerBook; + return addr; +} + +int AfdNotebook::getPageNumber(const PageAddress &addr) +{ + // series(0), shelf(0), segment(0), book(0), page(0) + if (addr.series == _firstPage.series && addr.shelf == _firstPage.shelf && addr.segment == _firstPage.segment) { + int firstPage = (_firstPage.book * _pagesPerBook) + _firstPage.page; + int page = (addr.book * _pagesPerBook) + addr.page - firstPage; + if (page >= 0 && page < _pages.size()) { + return page; + } + } + + qWarning() << "Invalid address for notebook" << _title; + return -1; +} -- cgit v1.2.3