aboutsummaryrefslogtreecommitdiff
path: root/stfexporter.cc
diff options
context:
space:
mode:
Diffstat (limited to 'stfexporter.cc')
-rw-r--r--stfexporter.cc183
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;
+}