aboutsummaryrefslogtreecommitdiff
path: root/afdnotebook.cc
diff options
context:
space:
mode:
authorJavier <dev.git@javispedro.com>2015-06-07 21:22:45 +0200
committerJavier <dev.git@javispedro.com>2015-06-07 21:22:45 +0200
commita69e97943539a8abc4d2762638c169dc19c88516 (patch)
treef3516ea29745db65971247cee4c260b49f1067b2 /afdnotebook.cc
downloadscribiu-a69e97943539a8abc4d2762638c169dc19c88516.tar.gz
scribiu-a69e97943539a8abc4d2762638c169dc19c88516.zip
initial import
Diffstat (limited to 'afdnotebook.cc')
-rw-r--r--afdnotebook.cc399
1 files changed, 399 insertions, 0 deletions
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 <cmath>
+#include <QtCore/QDebug>
+#include <QtGui/QPixmapCache>
+#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<int> AfdNotebook::pagesWithStrokes(const QString &penSerial) const
+{
+ if (_penData.contains(penSerial)) {
+ const PenData &data = _penData[penSerial];
+ return data.strokes.uniqueKeys();
+ } else {
+ return QList<int>();
+ }
+}
+
+QStringList AfdNotebook::strokeFiles(const QString &penSerial, int page) const
+{
+ QStringList l;
+ if (!_penData.contains(penSerial)) return l;
+
+ const PenData &data = _penData[penSerial];
+ QMultiMap<int, StrokeData>::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<int, StrokeData>::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<QString, QString> AfdNotebook::parsePropertyList(QIODevice *dev)
+{
+ QMap<QString, QString> 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<QString, QString> AfdNotebook::parsePropertyList(const QString &relativePath) const
+{
+ QFile f(_dir.filePath(relativePath));
+ if (!f.open(QIODevice::ReadOnly | QIODevice::Text)) {
+ return QMap<QString, QString>();
+ }
+ return parsePropertyList(&f);
+}
+
+bool AfdNotebook::parseMainInfo()
+{
+ QMap<QString, QString> 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;
+}