aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJavier <dev.git@javispedro.com>2021-09-19 20:55:17 +0200
committerJavier <dev.git@javispedro.com>2021-09-19 20:55:17 +0200
commitfd2a247a065548422b828d3055729e435a918f42 (patch)
treea8564151389b7c968b0b1ae133e1f7494a6a3623
parent419cd14ea25bc1585552bf73f0f29aa62518c175 (diff)
downloadscribiu-fd2a247a065548422b828d3055729e435a918f42.tar.gz
scribiu-fd2a247a065548422b828d3055729e435a918f42.zip
add InkML export support, fix bug with page layout in notebookview
-rw-r--r--mainwindow.cc19
-rw-r--r--mainwindow.h1
-rw-r--r--notebookview.cc13
-rw-r--r--notebookview.h1
-rw-r--r--scribiu.pro4
-rw-r--r--stfexporter.cc183
-rw-r--r--stfexporter.h (renamed from stftxtexport.h)14
-rw-r--r--stftxtexport.cc90
8 files changed, 225 insertions, 100 deletions
diff --git a/mainwindow.cc b/mainwindow.cc
index e7a73fa..d8a935c 100644
--- a/mainwindow.cc
+++ b/mainwindow.cc
@@ -154,6 +154,18 @@ void MainWindow::exportCurrentPageAsTXYP(const QString &file, bool relativeTime)
f.close();
}
+void MainWindow::exportCurrentPageAsInkML(const QString &file)
+{
+ QFile f(file);
+ if (!f.open(QIODevice::WriteOnly | QIODevice::Truncate | QIODevice::Text)) {
+ QMessageBox::warning(this, tr("Export page"),
+ tr("Could not export current page to '%s'").arg(file));
+ return;
+ }
+ ui->notebookView->exportPageAsInkML(&f, ui->notebookView->curPage());
+ f.close();
+}
+
void MainWindow::exportCurrentPaperReplayAsAac(const QString &file)
{
QString src = _media->currentSource().fileName();
@@ -349,6 +361,7 @@ void MainWindow::handleExport()
filters << tr("Current page as PNG image (*.png)")
<< tr("Current page as SVG image (*.svg)")
<< tr("Current page as TXYP (*.txyp)")
+ << tr("Current page as InkML (*.inkml)")
<< tr("Current audio as AAC (*.aac)");
int filterIndex = settings.value("filetype").toInt();
QString filter = filters.value(filterIndex);
@@ -378,6 +391,12 @@ void MainWindow::handleExport()
exportCurrentPageAsTXYP(fileName, settings.value("txyp_relative_t", true).toBool());
break;
case 3:
+ if (!fileName.endsWith(".inkml", Qt::CaseInsensitive)) {
+ fileName.append(".inkml");
+ }
+ exportCurrentPageAsInkML(fileName);
+ break;
+ case 4:
if (!fileName.endsWith(".aac", Qt::CaseInsensitive)) {
fileName.append(".aac");
}
diff --git a/mainwindow.h b/mainwindow.h
index dc06243..2f5575c 100644
--- a/mainwindow.h
+++ b/mainwindow.h
@@ -46,6 +46,7 @@ public slots:
void exportCurrentPageAsPng(const QString &file);
void exportCurrentPageAsSvg(const QString &file);
void exportCurrentPageAsTXYP(const QString &file, bool relativeTime);
+ void exportCurrentPageAsInkML(const QString &file);
void exportCurrentPaperReplayAsAac(const QString &file);
private slots:
diff --git a/notebookview.cc b/notebookview.cc
index 06d035b..f8394c0 100644
--- a/notebookview.cc
+++ b/notebookview.cc
@@ -18,7 +18,7 @@
#include <QtCore/QDebug>
#include <QtGui/QResizeEvent>
-#include "stftxtexport.h"
+#include "stfexporter.h"
#include "notebookview.h"
#define VIEW_MARGIN 2
@@ -152,10 +152,16 @@ void NotebookView::renderPage(QPainter *painter, int pageNum, const QRectF &targ
void NotebookView::exportPageAsTXYP(QIODevice *device, int pageNum, bool relativeTime)
{
- StfTxtExport writer(_nb);
+ StfExporter writer(_nb);
writer.exportToTXYP(device, pageNum, relativeTime);
}
+void NotebookView::exportPageAsInkML(QIODevice *device, int pageNum)
+{
+ StfExporter writer(_nb);
+ writer.exportToInkML(device, pageNum);
+}
+
void NotebookView::requestPaperReplay(const QString &file, qint64 time)
{
emit paperReplayRequested(file, time);
@@ -294,7 +300,8 @@ void NotebookView::calculateScale()
void NotebookView::layoutPages()
{
- const int numRows = (_pages.size() + 1) / _numColumns;
+ const int numRows = _pages.size() / _numColumns
+ + (_pages.size() % _numColumns > 0 ? 1 : 0);
const QSizeF pageSpace( _maxPageSize.width() + PAGE_SEPARATION,
_maxPageSize.height() + PAGE_SEPARATION);
diff --git a/notebookview.h b/notebookview.h
index cced421..7d62fab 100644
--- a/notebookview.h
+++ b/notebookview.h
@@ -60,6 +60,7 @@ public:
void renderPage(QPainter *painter, int pageNum, const QRectF &target = QRectF(), const QRectF &source = QRectF()) const;
void exportPageAsTXYP(QIODevice *device, int pageNum, bool relativeTime);
+ void exportPageAsInkML(QIODevice *device, int pageNum);
void requestPaperReplay(const QString &file, qint64 time);
diff --git a/scribiu.pro b/scribiu.pro
index c47865e..d25f2fc 100644
--- a/scribiu.pro
+++ b/scribiu.pro
@@ -24,12 +24,12 @@ SOURCES += main.cc \
notebookmodel.cc \
smartpensyncer.cc \
smartpen.cc bitreader.cc stfreader.cc \
+ stfexporter.cc \
xmlutils.cc \
notebookview.cc \
afdnotebook.cc \
pageitem.cc \
stfgraphicsitem.cc \
- stftxtexport.cc \
paperreplay.cc \
afdpageaddress.cc \
stfstrokeitem.cc \
@@ -40,12 +40,12 @@ HEADERS += mainwindow.h \
notebookmodel.h \
smartpensyncer.h \
smartpen.h bitreader.h stfreader.h \
+ stfexporter.h \
xmlutils.h \
notebookview.h \
afdnotebook.h \
pageitem.h \
stfgraphicsitem.h \
- stftxtexport.h \
paperreplay.h \
afdpageaddress.h \
stfstrokeitem.h \
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;
+}
diff --git a/stftxtexport.h b/stfexporter.h
index b9ca821..c67a75c 100644
--- a/stftxtexport.h
+++ b/stfexporter.h
@@ -16,20 +16,24 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef STFTXTEXPORT_H
-#define STFTXTEXPORT_H
+#ifndef STFEXPORTER_H
+#define STFEXPORTER_H
#include "afdnotebook.h"
-class StfTxtExport
+class StfExporter
{
public:
- explicit StfTxtExport(AfdNotebook *nb);
+ explicit StfExporter(AfdNotebook *nb);
void exportToTXYP(QIODevice *out, int pageNum, bool relativeTime);
+ void exportToInkML(QIODevice *out, int pageNum);
+
+private:
+ bool exportPage(StfReader::StrokeHandler *handler, int pageNum);
private:
AfdNotebook *_nb;
};
-#endif // STFTXTEXPORT_H
+#endif // STFEXPORTER_H
diff --git a/stftxtexport.cc b/stftxtexport.cc
deleted file mode 100644
index e6793b2..0000000
--- a/stftxtexport.cc
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * 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 "stftxtexport.h"
-
-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;
- }
-};
-
-StfTxtExport::StfTxtExport(AfdNotebook *nb)
- : _nb(nb)
-{
-}
-
-void StfTxtExport::exportToTXYP(QIODevice *out, int pageNum, bool relativeTime)
-{
- QStringList pens = _nb->penSerials();
- if (pens.isEmpty()) return;
-
- StfToTXYP h(out, relativeTime);
- StfReader r;
- r.setStrokeHandler(&h);
-
- QStringList strokeFiles = _nb->strokeFiles(pens.first(), 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;
- }
- }
-}