diff options
Diffstat (limited to 'stfexporter.cc')
-rw-r--r-- | stfexporter.cc | 183 |
1 files changed, 183 insertions, 0 deletions
diff --git a/stfexporter.cc b/stfexporter.cc new file mode 100644 index 0000000..84eaad7 --- /dev/null +++ b/stfexporter.cc @@ -0,0 +1,183 @@ +/* + * scribiu -- read notebooks and voice memos from Livescribe pens + * Copyright (C) 2021 Javier S. Pedro <javier@javispedro.com> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <QtCore/QDebug> +#include <QtCore/QTextStream> +#include <QtCore/QXmlStreamWriter> + +#include "stfexporter.h" + +#define XMLNS_INK "http://www.w3.org/2003/InkML" + +class StfToTXYP : public StfReader::StrokeHandler { + QTextStream _out; + QPoint _lastP; + int _lastForce; + qint64 _startTime; + bool _relativeTime; + +public: + StfToTXYP(QIODevice *out, bool relativeTime) + : _out(out), _lastP(), _lastForce(0), _startTime(0), _relativeTime(relativeTime) { + _out << "T\tX\tY\tP\n"; + } + + bool startStroke(const QPoint& p, int force, qint64 time) { + if (_relativeTime && _startTime == 0) { + _startTime = time; + } + _out << (time - _startTime) << '\t' << p.x() << '\t' << p.y() << '\t' << force << '\n'; + _lastP = p; + _lastForce = force; + return true; + } + + bool strokePoint(const QPoint& p, int force, qint64 time) { + _out << (time - _startTime) << '\t' << p.x() << '\t' << p.y() << '\t' << force << '\n'; + _lastP = p; + _lastForce = force; + return true; + } + + bool endStroke(qint64 time) { + // Ensure there is a entry with force=0, in case the pen didn't provide it + if (_lastForce != 0) { + _out << (time - _startTime) << '\t' << _lastP.x() << '\t' << _lastP.y() << '\t' << 0 << '\n'; + _lastForce = 0; + } + return true; + } +}; + +class StfToInkML : public StfReader::StrokeHandler { + QXmlStreamWriter *_out; + QPoint _lastP; + qint64 _startTime; + +public: + StfToInkML(QXmlStreamWriter *out) + : _out(out), _lastP(), _startTime(0) { + } + + bool startStroke(const QPoint& p, int force, qint64 time) { + if (_startTime == 0) { + _startTime = time; + } + + _out->writeStartElement(XMLNS_INK, "trace"); + _out->writeAttribute("timeOffset", QString::number(time - _startTime)); + + _out->writeCharacters(QString("%1 %2").arg(p.x()).arg(p.y())); + + _lastP = p; + return true; + } + + bool strokePoint(const QPoint& p, int force, qint64 time) { + QPoint delta = p - _lastP; + + _out->writeCharacters(QString(", %1 %2").arg(delta.x()).arg(delta.y())); + + _lastP = p; + return true; + } + + bool endStroke(qint64 time) { + _out->writeEndElement(); + return true; + } +}; + +StfExporter::StfExporter(AfdNotebook *nb) + : _nb(nb) +{ +} + +void StfExporter::exportToTXYP(QIODevice *out, int pageNum, bool relativeTime) +{ + QStringList pens = _nb->penSerials(); + if (pens.isEmpty()) return; + + StfToTXYP h(out, relativeTime); + + exportPage(&h, pageNum); +} + +void StfExporter::exportToInkML(QIODevice *out, int pageNum) +{ + QStringList pens = _nb->penSerials(); + if (pens.isEmpty()) return; + + QXmlStreamWriter writer(out); + writer.setAutoFormatting(true); + writer.writeStartDocument(); + writer.writeDefaultNamespace(XMLNS_INK); + writer.writeStartElement(XMLNS_INK, "ink"); + +#if 0 /* No need to write out inkSource element, since default trace format is OK for now */ + writer.writeStartElement(XMLNS_INK, "inkSource"); + writer.writeAttribute("manufacturer", "Livescribe"); + writer.writeAttribute("description", "Dumped by Scribiu"); + writer.writeStartElement(XMLNS_INK, "traceFormat"); + writer.writeEmptyElement(XMLNS_INK, "channel"); + writer.writeAttribute("name", "X"); + writer.writeAttribute("type", "integer"); + writer.writeEmptyElement(XMLNS_INK, "channel"); + writer.writeAttribute("name", "Y"); + writer.writeAttribute("type", "integer"); + writer.writeEndElement(); + writer.writeEmptyElement(XMLNS_INK, "sampleRate"); + writer.writeAttribute("uniform", "true"); + writer.writeAttribute("value", "75"); + writer.writeEndElement(); +#endif + + StfToInkML h(&writer); + + exportPage(&h, pageNum); + + writer.writeEndElement(); + writer.writeEndDocument(); +} + +bool StfExporter::exportPage(StfReader::StrokeHandler *handler, int pageNum) +{ + QStringList pens = _nb->penSerials(); + if (pens.isEmpty()) return true; // No pen wrote on this page + + StfReader r; + r.setStrokeHandler(handler); + + foreach (const QString &pen, pens) { + QStringList strokeFiles = _nb->strokeFiles(pen, pageNum); + foreach (const QString &strokeFile, strokeFiles) { + QFile in(strokeFile); + if (!in.open(QIODevice::ReadOnly)) { + qWarning() << "Could not open stroke file:" << strokeFile; + continue; + } + + if (!r.parse(&in)) { + qWarning() << "Could not parse stroke file:" << strokeFile; + continue; + } + } + } + + return true; +} |