From 43373d159e0e7e6e371b31ac3c5e54eb7763e28b Mon Sep 17 00:00:00 2001 From: Anton Thomasson Date: Sat, 6 Mar 2021 12:59:47 +0100 Subject: [PATCH] Add calligraconverter support --- qml/pages/FirstPage.qml | 16 +-- qml/pages/utils.js | 18 +++- src/convertchecker.h | 4 +- src/convertworker.cpp | 229 ++++++++++++++++++++++++++++++++++++++++ src/convertworker.h | 5 + src/ippprinter.cpp | 30 +++--- src/ippprinter.h | 5 + src/mimer.cpp | 2 + src/mimer.h | 15 ++- src/papersizes.h | 34 ++++++ 10 files changed, 332 insertions(+), 26 deletions(-) diff --git a/qml/pages/FirstPage.qml b/qml/pages/FirstPage.qml index 6440037..f7433fc 100644 --- a/qml/pages/FirstPage.qml +++ b/qml/pages/FirstPage.qml @@ -76,6 +76,7 @@ Page { if(status==PageStatus.Active && !nagged && nagScreenSetting.value != nagScreenSetting.expectedValue) { console.log("Can convert from PDF:", ConvertChecker.pdf) + console.log("Can convert from Office:", ConvertChecker.calligra) if(!ConvertChecker.pdf) { nagged=true @@ -345,24 +346,25 @@ Page { DocumentPickerPage { allowedOrientations: Orientation.All - Component.onCompleted: { - var thingy = Qt.createComponent("../components/DocumentFilter.notqml"); - if (thingy.status == Component.Ready) { - _contentModel.contentFilter = thingy.createObject(this); - } - } +// Component.onCompleted: { +// var thingy = Qt.createComponent("../components/DocumentFilter.notqml"); +// if (thingy.status == Component.Ready) { +// _contentModel.contentFilter = thingy.createObject(this); +// } +// } title: qsTr("Choose file") onSelectedContentPropertiesChanged: { var mimeType = Mimer.get_type(selectedContentProperties.filePath) - if(mimeType == "application/pdf" || mimeType == "application/postscript") + if(mimeType == "application/pdf" || mimeType == "application/postscript" || Mimer.isOffice(mimeType)) { page.selectedFile = selectedContentProperties.filePath page.selectedFileType = mimeType } else { + console.log("UNSUPPORTED", mimeType); notifier.notify(qsTr("Unsupported document format")) page.selectedFile = "" page.selectedFileType = "" diff --git a/qml/pages/utils.js b/qml/pages/utils.js index 2a215eb..57ac6a2 100644 --- a/qml/pages/utils.js +++ b/qml/pages/utils.js @@ -22,6 +22,12 @@ function supported_formats(printer, ConvertChecker, considerAdditionalFormats) supported.push("Postscript"); } + if((ConvertChecker.pdf && ConvertChecker.calligra) && + ( has(formats, Mimer.PDF) || has(formats, Mimer.Postscript) || raster )) + { + mimetypes = mimetypes.concat(Mimer.OfficeFormats); + } + if (raster || has(formats, Mimer.JPEG) || has(formats, Mimer.PNG)) { mimetypes.push(Mimer.JPEG); @@ -266,6 +272,12 @@ function canTransferPostscriptAs(type) return has(targets, type) } +function canConvertOfficeDocumentTo(type) +{ + var targets = [Mimer.OctetStream, Mimer.PDF, Mimer.Postscript, Mimer.PWG, Mimer.URF]; + return has(targets, type) +} + function canConvertImageTo(type) { var targets = [Mimer.OctetStream, Mimer.JPEG, Mimer.PNG, Mimer.PWG, Mimer.URF]; @@ -298,7 +310,11 @@ function limitChoices(name, choices, mimeType, ConvertChecker) { return choices.filter(canTransferPostscriptAs) } - else if(mimeType.indexOf("image") != -1) + else if(Mimer.isOffice(mimeType)) + { + return choices.filter(canConvertOfficeDocumentTo) + } + else if(Mimer.isImage(mimeType)) { return choices.filter(canConvertImageTo); } diff --git a/src/convertchecker.h b/src/convertchecker.h index c3580c2..ffc1b01 100644 --- a/src/convertchecker.h +++ b/src/convertchecker.h @@ -8,8 +8,8 @@ class ConvertChecker : public QObject Q_OBJECT public: static ConvertChecker* instance(); - Q_PROPERTY(bool pdf MEMBER _pdf) - Q_PROPERTY(bool calligra MEMBER _calligra) + Q_PROPERTY(bool pdf MEMBER _pdf CONSTANT) + Q_PROPERTY(bool calligra MEMBER _calligra CONSTANT) Q_INVOKABLE quint32 pdfPages(QString pdf); diff --git a/src/convertworker.cpp b/src/convertworker.cpp index c8ac479..4f42527 100644 --- a/src/convertworker.cpp +++ b/src/convertworker.cpp @@ -430,3 +430,232 @@ catch(const ConvertFailedException& e) emit failed(e.what() == QString("") ? tr("Conversion error") : e.what()); } } + +void ConvertWorker::convertOfficeDocument(QNetworkRequest request, QString filename, QTemporaryFile* tempfile, + QString targetFormat, quint32 Colors, quint32 Quality, QString PaperSize, + quint32 HwResX, quint32 HwResY, bool TwoSided, bool Tumble, + quint32 PageRangeLow, quint32 PageRangeHigh) +{ +try { + + if(targetFormat == Mimer::URF && (HwResX != HwResY)) + { // URF only supports symmetric resolutions + qDebug() << "Unsupported URF resolution" << PaperSize; + throw ConvertFailedException(tr("Unsupported resolution (dpi)")); + } + + QString ShortPaperSize; + if(CalligraPaperSizes.contains(PaperSize)) + { + ShortPaperSize = CalligraPaperSizes[PaperSize]; + } + else + { + qDebug() << "Unsupported PDF paper size" << PaperSize; + throw ConvertFailedException(tr("Unsupported PDF paper size")); + } + + QProcess* CalligraConverter = new QProcess(this); + CalligraConverter->setProgram("calligraconverter"); + QStringList CalligraConverterArgs = {"--batch", "--mimetype", Mimer::PDF, "--print-orientation", "Portrait", "--print-papersize", ShortPaperSize}; + + CalligraConverterArgs << filename; + + QTemporaryFile tmpPdfFile; + tmpPdfFile.open(); + CalligraConverterArgs << tmpPdfFile.fileName(); + + qDebug() << "CalligraConverteArgs is" << CalligraConverterArgs; + CalligraConverter->setArguments(CalligraConverterArgs); + + connect(CalligraConverter, SIGNAL(finished(int, QProcess::ExitStatus)), CalligraConverter, SLOT(deleteLater())); + + CalligraConverter->start(); + + qDebug() << "CalligraConverter Starting"; + + if(!CalligraConverter->waitForStarted()) + { + qDebug() << "CalligraConverter died"; + throw ConvertFailedException(); + } + + qDebug() << "CalligraConverter Started"; + + if(!CalligraConverter->waitForFinished(-1)) + { + qDebug() << "CalligraConverter failed"; + throw ConvertFailedException(); + } + +// qDebug() << CalligraConverter->readAllStandardError(); + + quint32 pages = ConvertChecker::instance()->pdfPages(tmpPdfFile.fileName()); + if (!pages) + { + qDebug() << "pdfinfo returned 0 pages"; + throw ConvertFailedException(tr("Failed to get info about PDF file")); + } + + if(PageRangeLow==0) + { + PageRangeLow=1; + } + + if(PageRangeHigh==0) + { + PageRangeHigh=pages; + } + + // Actual number of pages to print + pages = PageRangeHigh-PageRangeLow+1; + + qDebug() << "PageRangeLow" << PageRangeLow << "PageRangeHigh" << PageRangeHigh << "pages" << pages; + + if(targetFormat == Mimer::PDF) + { + + // TODO Page ranges + + QFile tempfileAsFile(tempfile->fileName()); + tempfileAsFile.open(QIODevice::Append); + tempfileAsFile.write(tmpPdfFile.readAll()); + tempfileAsFile.close(); + + } + else if(targetFormat == Mimer::Postscript) + { + QProcess* PdfToPs = new QProcess(this); + PdfToPs->setProgram("pdftops"); + QStringList PdfToPsArgs; + if(TwoSided) + { + PdfToPsArgs.append("-duplex"); + } + + PdfToPsArgs << QStringList {"-f", QString::number(PageRangeLow), "-l", QString::number(PageRangeHigh)}; + PdfToPsArgs << QStringList {tmpPdfFile.fileName(), "-"}; + + + qDebug() << "pdftops args is " << PdfToPsArgs; + PdfToPs->setArguments(PdfToPsArgs); + + + PdfToPs->setStandardOutputFile(tempfile->fileName(), QIODevice::Append); + connect(PdfToPs, SIGNAL(finished(int, QProcess::ExitStatus)), PdfToPs, SLOT(deleteLater())); + + PdfToPs->start(); + + qDebug() << "PdfToPs Starting"; + + if(!PdfToPs->waitForStarted()) + { + qDebug() << "PdfToPs died"; + throw ConvertFailedException(); + } + + qDebug() << "PdfToPs Started"; + + if(!PdfToPs->waitForFinished(-1)) + { + qDebug() << "PdfToPs failed"; + throw ConvertFailedException(); + } + } + else + { + + QProcess* pdftoppm = new QProcess(this); + pdftoppm->setProgram("pdftoppm"); + QStringList Pdf2PpmArgs = {"-rx", QString::number(HwResX), "-ry", QString::number(HwResY)}; + Pdf2PpmArgs << QStringList {"-f", QString::number(PageRangeLow), "-l", QString::number(PageRangeHigh)}; + + if(Colors == 1) + { + Pdf2PpmArgs.append("-gray"); + } + qDebug() << "pdf2ppm args is " << Pdf2PpmArgs; + pdftoppm->setArguments(Pdf2PpmArgs); + + + QProcess* ppm2pwg = new QProcess(this); + // Yo dawg, I heard you like programs... + ppm2pwg->setProgram("harbour-seaprint"); + ppm2pwg->setArguments({"ppm2pwg"}); + + bool urf = targetFormat == Mimer::URF; + + QStringList env; + ppm2PwgEnv(env, urf, Quality, PaperSize, HwResX, HwResY, TwoSided, Tumble, true, pages); + qDebug() << "ppm2pwg env is " << env; + + ppm2pwg->setEnvironment(env); + + pdftoppm->setStandardInputFile(tmpPdfFile.fileName()); + pdftoppm->setStandardOutputProcess(ppm2pwg); + ppm2pwg->setStandardOutputFile(tempfile->fileName(), QIODevice::Append); + + connect(pdftoppm, SIGNAL(finished(int, QProcess::ExitStatus)), pdftoppm, SLOT(deleteLater())); + connect(ppm2pwg, SIGNAL(finished(int, QProcess::ExitStatus)), ppm2pwg, SLOT(deleteLater())); + + qDebug() << "All connected"; + + pdftoppm->start(); + ppm2pwg->start(); + + qDebug() << "Starting"; + + if(!pdftoppm->waitForStarted()) + { + qDebug() << "pdftoppm died"; + throw ConvertFailedException(); + } + if(!ppm2pwg->waitForStarted()) + { + qDebug() << "ppm2pwg died"; + throw ConvertFailedException(); + } + qDebug() << "All started"; + + bool ppm2pwgSuccess = false; + + for(;;) + { + if(ppm2pwg->waitForFinished(1000)) + { + ppm2pwgSuccess = true; + break; + } + else + { + QList ppm2pwgOutput = ppm2pwg->readAllStandardError().split('\n'); + for(QList::iterator it = ppm2pwgOutput.begin(); it != ppm2pwgOutput.end(); it++) + { + if(it->startsWith("Page")) + { + QList ppm2pwgTokens = it->split(' '); + emit progress(ppm2pwgTokens.last().toInt()-1, pages); + } + } + } + } + if(!ppm2pwgSuccess) + { + qDebug() << "ppm2pwg failed"; + throw ConvertFailedException(); + } + } + + + qDebug() << "Finished"; + + emit done(request, tempfile); + qDebug() << "posted"; + +} +catch(const ConvertFailedException& e) +{ + tempfile->deleteLater(); + emit failed(e.what() == QString("") ? tr("Conversion error") : e.what()); +} +} diff --git a/src/convertworker.h b/src/convertworker.h index dc4cb8b..768ac07 100644 --- a/src/convertworker.h +++ b/src/convertworker.h @@ -31,6 +31,11 @@ public slots: QString targetFormat, quint32 Colors, quint32 Quality, QString PaperSize, quint32 HwResX, quint32 HwResY); + void convertOfficeDocument(QNetworkRequest request, QString filename, QTemporaryFile* tempfile, + QString targetFormat, quint32 Colors, quint32 Quality, QString PaperSize, + quint32 HwResX, quint32 HwResY, bool TwoSided, bool Tumble, + quint32 PageRangeLow, quint32 PageRangeHigh); + signals: void done(QNetworkRequest request, QTemporaryFile* data); void progress(qint64 done, qint64 pages); diff --git a/src/ippprinter.cpp b/src/ippprinter.cpp index ce6dde4..7e8f59b 100644 --- a/src/ippprinter.cpp +++ b/src/ippprinter.cpp @@ -32,6 +32,7 @@ IppPrinter::IppPrinter() connect(this, &IppPrinter::doConvertPdf, _worker, &ConvertWorker::convertPdf); connect(this, &IppPrinter::doConvertImage, _worker, &ConvertWorker::convertImage); + connect(this, &IppPrinter::doConvertOfficeDocument, _worker, &ConvertWorker::convertOfficeDocument); connect(_worker, &ConvertWorker::done, this, &IppPrinter::convertDone); connect(_worker, &ConvertWorker::progress, this, &IppPrinter::setProgress); connect(_worker, &ConvertWorker::failed, this, &IppPrinter::convertFailed); @@ -503,22 +504,22 @@ void IppPrinter::print(QJsonObject attrs, QString filename, bool alwaysConvert, qDebug() << tempfile->fileName(); tempfile->close(); + bool TwoSided = false; + bool Tumble = false; + if(Sides=="two-sided-long-edge") + { + TwoSided = true; + } + else if(Sides=="two-sided-short-edge") + { + TwoSided = true; + Tumble = true; + } + setBusyMessage("Converting"); if(mimeType == Mimer::PDF) { - bool TwoSided = false; - bool Tumble = false; - if(Sides=="two-sided-long-edge") - { - TwoSided = true; - } - else if(Sides=="two-sided-short-edge") - { - TwoSided = true; - Tumble = true; - } - emit doConvertPdf(request, filename, tempfile, documentFormat, Colors, Quality, PaperSize, HwResX, HwResY, TwoSided, Tumble, PageRangeLow, PageRangeHigh); } @@ -527,6 +528,11 @@ void IppPrinter::print(QJsonObject attrs, QString filename, bool alwaysConvert, emit doConvertImage(request, filename, tempfile, documentFormat, Colors, Quality, PaperSize, HwResX, HwResY); } + else if(Mimer::isOffice(mimeType)) + { + emit doConvertOfficeDocument(request, filename, tempfile, documentFormat, Colors, Quality, + PaperSize, HwResX, HwResY, TwoSided, Tumble, PageRangeLow, PageRangeHigh); + } else { emit convertFailed(tr("Cannot convert this file format")); diff --git a/src/ippprinter.h b/src/ippprinter.h index 4e02a69..9131cf3 100644 --- a/src/ippprinter.h +++ b/src/ippprinter.h @@ -49,6 +49,11 @@ signals: QString targetFormat, quint32 Colors, quint32 Quality, QString PaperSize, quint32 HwResX, quint32 HwResY); + void doConvertOfficeDocument(QNetworkRequest request, QString filename, QTemporaryFile* tempfile, + QString targetFormat, quint32 Colors, quint32 Quality, QString PaperSize, + quint32 HwResX, quint32 HwResY, bool TwoSided, bool Tumble, + quint32 PageRangeLow, quint32 PageRangeHigh); + void additionalDocumentFormatsChanged(); void busyMessageChanged(); void progressChanged(); diff --git a/src/mimer.cpp b/src/mimer.cpp index bc7a4d6..04f92aa 100644 --- a/src/mimer.cpp +++ b/src/mimer.cpp @@ -15,8 +15,10 @@ const QString Mimer::TIFF = "image/tiff"; const QString Mimer::DOC = "application/msword"; const QString Mimer::DOCX = "application/vnd.openxmlformats-officedocument.wordprocessingml.document"; const QString Mimer::RTF = "text/rtf"; +const QString Mimer::RTF_APP = "application/rtf"; const QString Mimer::ODT = "application/vnd.oasis.opendocument.text"; +const QStringList Mimer::OfficeFormats = {DOC, DOCX, RTF, RTF_APP, ODT}; Mimer::Mimer() { diff --git a/src/mimer.h b/src/mimer.h index c612c73..948323f 100644 --- a/src/mimer.h +++ b/src/mimer.h @@ -25,6 +25,13 @@ public: Q_PROPERTY(const QString JPEG MEMBER JPEG CONSTANT); Q_PROPERTY(const QString TIFF MEMBER TIFF CONSTANT); + Q_PROPERTY(const QString DOC MEMBER DOC CONSTANT); + Q_PROPERTY(const QString DOCX MEMBER DOCX CONSTANT); + Q_PROPERTY(const QString RTF MEMBER RTF CONSTANT); + Q_PROPERTY(const QString ODT MEMBER ODT CONSTANT); + + Q_PROPERTY(const QStringList OfficeFormats MEMBER OfficeFormats CONSTANT); + static const QString OctetStream; static const QString PDF; @@ -40,20 +47,20 @@ public: static const QString DOC; static const QString DOCX; static const QString RTF; + static const QString RTF_APP; static const QString ODT; + static const QStringList OfficeFormats; + Q_INVOKABLE static bool isImage(QString mimeType) { return mimeType.startsWith("image/"); } Q_INVOKABLE static bool isOffice(QString mimeType) { - static QStringList OfficeForamts {DOC, DOCX, RTF, ODT}; - return OfficeForamts.contains(mimeType); + return OfficeFormats.contains(mimeType); } - - private: Mimer(); static Mimer* m_Instance; diff --git a/src/papersizes.h b/src/papersizes.h index c4d8858..a13fbe7 100644 --- a/src/papersizes.h +++ b/src/papersizes.h @@ -205,5 +205,39 @@ static QMap> PaperSizes = {"roc_16k_7.75x10.75in", {196.85, 273.05}}, {"roc_8k_10.75x15.5in", {273.05, 393.70}}}; +static QMap CalligraPaperSizes = + {{"iso_a0_841x1189mm", "A0"}, + {"iso_a1_594x841mm", "A1"}, + {"iso_a2_420x594mm", "A2"}, + {"iso_a3_297x420mm", "A3"}, + {"iso_a4_210x297mm", "A4"}, + {"iso_a5_148x210mm", "A5"}, + {"iso_a6_105x148mm", "A6"}, + {"iso_a7_74x105mm", "A7"}, + {"iso_a8_52x74mm", "A8"}, + {"iso_a9_37x52mm", "A9"}, + {"iso_b0_1000x1414mm", "B0"}, + {"iso_b1_707x1000mm", "B1"}, + {"iso_b2_500x707mm", "B2"}, + {"iso_b3_353x500mm", "B3"}, + {"iso_b4_250x353mm", "B4"}, + {"iso_b5_176x250mm", "B5"}, + {"iso_b6_125x176mm", "B6"}, + {"iso_b7_88x125mm", "B7"}, + {"iso_b8_62x88mm", "B8"}, + {"iso_b9_44x62mm", "B9"}, + {"iso_b10_31x44mm", "B10"}, + {"iso_c5_162x229mm", "C5E"}, + {"na_number-10_4.125x9.5in", "Comm10E"}, + {"iso_dl_110x220mm", "DLE"}, + {"na_executive_7.25x10.5in", "Executive"}, + {"om_folio_210x330mm", "Folio"}, + {"na_ledger_11x17in", "Ledger"}, + {"na_legal_8.5x14in", "Legal"}, + {"na_letter_8.5x11in", "Letter"}, + {"na_ledger_11x17in", "Tabloid"} // Dimension match + }; + + #endif // PAPERSIZES_H