Add plaintext support

This commit is contained in:
Anton Thomasson 2021-06-12 12:45:58 +02:00
parent 76fdbb987f
commit 4e8692c431
17 changed files with 267 additions and 5 deletions

View file

@ -12,6 +12,11 @@ GalleryFilterUnion {
value: ".ps" value: ".ps"
} }
GalleryEndsWithFilter {
property: "fileName"
value: ".txt"
}
GalleryEndsWithFilter { GalleryEndsWithFilter {
property: "fileName" property: "fileName"
value: ".PDF" value: ".PDF"
@ -22,4 +27,9 @@ GalleryFilterUnion {
value: ".PS" value: ".PS"
} }
GalleryEndsWithFilter {
property: "fileName"
value: ".TXT"
}
} }

View file

@ -12,6 +12,11 @@ GalleryFilterUnion {
value: ".ps" value: ".ps"
} }
GalleryEndsWithFilter {
property: "fileName"
value: ".txt"
}
GalleryEndsWithFilter { GalleryEndsWithFilter {
property: "fileName" property: "fileName"
value: ".PDF" value: ".PDF"
@ -22,6 +27,11 @@ GalleryFilterUnion {
value: ".PS" value: ".PS"
} }
GalleryEndsWithFilter {
property: "fileName"
value: ".TXT"
}
GalleryEndsWithFilter { GalleryEndsWithFilter {
property: "fileName" property: "fileName"
value: ".odt" value: ".odt"

View file

@ -276,7 +276,8 @@ Page {
spacing: Theme.paddingMedium spacing: Theme.paddingMedium
Label { Label {
id: format_unsupported_label id: format_unsupported_label
visible: !supported_formats.pdf && !supported_formats.postscript && !supported_formats.office && !supported_formats.images visible: !supported_formats.pdf && !supported_formats.postscript && !supported_formats.plaintext
&& !supported_formats.office && !supported_formats.images
color: "red" color: "red"
font.pixelSize: Theme.fontSizeExtraSmall font.pixelSize: Theme.fontSizeExtraSmall
text: qsTr("No compatible formats supported") text: qsTr("No compatible formats supported")
@ -299,6 +300,14 @@ Page {
source: "image://theme/icon-m-file-other" source: "image://theme/icon-m-file-other"
} }
HighlightImage {
height: Theme.itemSizeExtraSmall/2
width: Theme.itemSizeExtraSmall/2
visible: supported_formats.plaintext
highlightColor: "red"
highlighted: !(selectedFile == "" || canPrint)
source: "image://theme/icon-m-file-document"
}
HighlightImage { HighlightImage {
height: Theme.itemSizeExtraSmall/2 height: Theme.itemSizeExtraSmall/2
width: Theme.itemSizeExtraSmall/2 width: Theme.itemSizeExtraSmall/2
@ -441,7 +450,7 @@ Page {
onSelectedContentPropertiesChanged: { onSelectedContentPropertiesChanged: {
var mimeType = Mimer.get_type(selectedContentProperties.filePath) var mimeType = Mimer.get_type(selectedContentProperties.filePath)
if(mimeType == "application/pdf" || mimeType == "application/postscript" || Mimer.isOffice(mimeType)) if(mimeType == Mimer.PDF || mimeType == Mimer.Postscript || mimeType == Mimer.Plaintext || Mimer.isOffice(mimeType))
{ {
page.selectedFile = selectedContentProperties.filePath page.selectedFile = selectedContentProperties.filePath
page.selectedFileType = mimeType page.selectedFileType = mimeType

View file

@ -15,12 +15,15 @@ function supported_formats(printer, ConvertChecker, considerAdditionalFormats)
var postscript = false; var postscript = false;
var office = false; var office = false;
var images = false; var images = false;
var plaintext = false;
if(has(formats, Mimer.PDF) || if(has(formats, Mimer.PDF) ||
(ConvertChecker.pdf && ( has(formats, Mimer.Postscript) || raster ))) (ConvertChecker.pdf && ( has(formats, Mimer.Postscript) || raster )))
{ {
pdf = true; pdf = true;
mimetypes.push(Mimer.PDF); mimetypes.push(Mimer.PDF);
plaintext = true;
mimetypes.push(Mimer.Plaintext);
} }
if(has(formats, Mimer.Postscript)) if(has(formats, Mimer.Postscript))
{ {
@ -44,7 +47,7 @@ function supported_formats(printer, ConvertChecker, considerAdditionalFormats)
mimetypes.push(Mimer.GIF); mimetypes.push(Mimer.GIF);
} }
return {pdf: pdf, postscript: postscript, office: office, images: images, mimetypes: mimetypes}; return {pdf: pdf, postscript: postscript, plaintext: plaintext, office: office, images: images, mimetypes: mimetypes};
} }
function has(arrayish, what) function has(arrayish, what)
@ -316,6 +319,18 @@ function limitChoices(name, choices, mimeType, ConvertChecker)
} }
} }
else if(mimeType == Mimer.Plaintext)
{
// We convert plaintext to PDF internally
if(ConvertChecker.pdf)
{
return choices.filter(canConvertPdfTo)
}
else
{
return choices.filter(canTransferPdfAs)
}
}
else if(mimeType == Mimer.Postscript) else if(mimeType == Mimer.Postscript)
{ {
return choices.filter(canTransferPostscriptAs) return choices.filter(canTransferPostscriptAs)

View file

@ -6,6 +6,8 @@
#include <QImage> #include <QImage>
#include <QMatrix> #include <QMatrix>
#include <QPainter> #include <QPainter>
#include <QTextDocument>
#include <QPdfWriter>
void ppm2PwgEnv(QStringList& env, bool urf, quint32 Quality, QString PaperSize, void ppm2PwgEnv(QStringList& env, bool urf, quint32 Quality, QString PaperSize,
quint32 HwResX, quint32 HwResY, bool TwoSided, bool Tumble, quint32 HwResX, quint32 HwResY, bool TwoSided, bool Tumble,
@ -379,6 +381,171 @@ catch(const ConvertFailedException& e)
} }
} }
void ConvertWorker::convertPlaintext(QNetworkRequest request, QString filename, QTemporaryFile* tempfile,
QString targetFormat, quint32 Colors, quint32 Quality, QString PaperSize,
quint32 HwResX, quint32 HwResY, bool TwoSided, bool Tumble)
{
try {
if(!PaperSizes.contains(PaperSize))
{
qDebug() << "Unsupported paper size" << PaperSize;
throw ConvertFailedException(tr("Unsupported paper size"));
}
QPair<float,float> wh = PaperSizes[PaperSize];
QFile inFile(filename);
if(!inFile.open(QIODevice::ReadOnly))
{
throw ConvertFailedException(tr("Failed to open file"));
}
quint32 resolution = std::min(HwResX, HwResY);
QTemporaryFile tmpPdfFile;
tmpPdfFile.open();
QPdfWriter pdfWriter(tmpPdfFile.fileName());
QPageSize pageSize(QSizeF {wh.first, wh.second}, QPageSize::Millimeter);
pdfWriter.setPageSize(pageSize);
pdfWriter.setResolution(resolution);
qreal docHeight = pageSize.sizePixels(resolution).height();
QTextDocument doc;
QFont font = QFont("Courier");
font.setPointSizeF(1);
qreal charHeight = 0;
// Find the optimal font size
while(true) {
QFont tmpFont = font;
tmpFont.setPointSizeF(font.pointSizeF()+0.5);
QFontMetricsF qfm(tmpFont, &pdfWriter);
charHeight = qfm.lineSpacing();
if(charHeight*66 > docHeight)
{
break;
}
font=tmpFont;
}
QFontMetricsF qfm(font, &pdfWriter);
charHeight = qfm.height();
int textHeight = 60*charHeight;
qreal margin = ((docHeight-textHeight)/2);
doc.setDefaultFont(font);
(void)doc.documentLayout(); // wat
// Needs to be before painter
pdfWriter.setMargins({0, 0, 0, 0});
QPainter painter(&pdfWriter);
doc.documentLayout()->setPaintDevice(painter.device());
doc.setDocumentMargin(margin);
QRectF body = QRectF(0, 0, pdfWriter.width(), pdfWriter.height());
doc.setPageSize(body.size());
QString allText = inFile.readAll();
if(allText.startsWith("\f"))
{
allText.remove(0, 1);
}
if(allText.endsWith("\f"))
{
allText.chop(1);
}
else if(allText.endsWith("\f\n"))
{
allText.chop(2);
}
QStringList pages = allText.split('\f');
bool first = true;
int pageCount = 0;
foreach(QString page, pages)
{
if(!first)
{
pdfWriter.newPage();
}
first = false;
if(page.endsWith("\n"))
{
page.chop(1);
}
doc.setPlainText(page);
int p = 0; // Page number in this document, starting from 0
while(true)
{
painter.translate(body.left(), body.top() - p*body.height());
QRectF view(0, p*body.height(), body.width(), body.height());
painter.setClipRect(view);
QAbstractTextDocumentLayout::PaintContext context;
context.clip = view;
context.palette.setColor(QPalette::Text, Qt::black);
doc.documentLayout()->draw(&painter, context);
p++;
pageCount++;
if(p >= doc.pageCount())
break;
pdfWriter.newPage();
}
}
painter.end();
if(targetFormat == Mimer::PDF)
{
QFile tempfileAsFile(tempfile->fileName());
tempfileAsFile.open(QIODevice::Append);
tempfileAsFile.write(tmpPdfFile.readAll());
tempfileAsFile.close();
}
else if(targetFormat == Mimer::Postscript)
{
pdftoPs(PaperSize, TwoSided, 0, 0, tmpPdfFile.fileName(), tempfile);
}
else
{
pdfToRaster(targetFormat, Colors, Quality, PaperSize,
HwResX, HwResY, TwoSided, Tumble,
0, 0, pageCount,
tmpPdfFile.fileName(), tempfile, false);
}
qDebug() << "Finished";
emit done(request, tempfile);
}
catch(const ConvertFailedException& e)
{
tempfile->deleteLater();
emit failed(e.what() == QString("") ? tr("Conversion error") : e.what());
}
}
QString ConvertWorker::getPopplerShortPaperSize(QString PaperSize) QString ConvertWorker::getPopplerShortPaperSize(QString PaperSize)
{ {
QString ShortPaperSize; QString ShortPaperSize;
@ -456,7 +623,10 @@ void ConvertWorker::pdftoPs(QString PaperSize, bool TwoSided, quint32 PageRangeL
QString ShortPaperSize = getPopplerShortPaperSize(PaperSize); QString ShortPaperSize = getPopplerShortPaperSize(PaperSize);
if(PageRangeLow != 0 && PageRangeHigh != 0)
{
PdfToPsArgs << QStringList {"-f", QString::number(PageRangeLow), "-l", QString::number(PageRangeHigh)}; PdfToPsArgs << QStringList {"-f", QString::number(PageRangeLow), "-l", QString::number(PageRangeHigh)};
}
PdfToPsArgs << QStringList {"-paper", ShortPaperSize, pdfFileName, "-"}; PdfToPsArgs << QStringList {"-paper", ShortPaperSize, pdfFileName, "-"};

View file

@ -36,6 +36,10 @@ public slots:
quint32 HwResX, quint32 HwResY, bool TwoSided, bool Tumble, quint32 HwResX, quint32 HwResY, bool TwoSided, bool Tumble,
quint32 PageRangeLow, quint32 PageRangeHigh); quint32 PageRangeLow, quint32 PageRangeHigh);
void convertPlaintext(QNetworkRequest request, QString filename, QTemporaryFile* tempfile,
QString targetFormat, quint32 Colors, quint32 Quality, QString PaperSize,
quint32 HwResX, quint32 HwResY, bool TwoSided, bool Tumble);
signals: signals:
void done(QNetworkRequest request, QTemporaryFile* data); void done(QNetworkRequest request, QTemporaryFile* data);
void progress(qint64 done, qint64 pages); void progress(qint64 done, qint64 pages);

View file

@ -34,6 +34,7 @@ IppPrinter::IppPrinter()
connect(this, &IppPrinter::doConvertPdf, _worker, &ConvertWorker::convertPdf); connect(this, &IppPrinter::doConvertPdf, _worker, &ConvertWorker::convertPdf);
connect(this, &IppPrinter::doConvertImage, _worker, &ConvertWorker::convertImage); connect(this, &IppPrinter::doConvertImage, _worker, &ConvertWorker::convertImage);
connect(this, &IppPrinter::doConvertOfficeDocument, _worker, &ConvertWorker::convertOfficeDocument); connect(this, &IppPrinter::doConvertOfficeDocument, _worker, &ConvertWorker::convertOfficeDocument);
connect(this, &IppPrinter::doConvertPlaintext, _worker, &ConvertWorker::convertPlaintext);
connect(_worker, &ConvertWorker::done, this, &IppPrinter::convertDone); connect(_worker, &ConvertWorker::done, this, &IppPrinter::convertDone);
connect(_worker, &ConvertWorker::progress, this, &IppPrinter::setProgress); connect(_worker, &ConvertWorker::progress, this, &IppPrinter::setProgress);
connect(_worker, &ConvertWorker::failed, this, &IppPrinter::convertFailed); connect(_worker, &ConvertWorker::failed, this, &IppPrinter::convertFailed);
@ -310,7 +311,7 @@ QString targetFormatIfAuto(QString documentFormat, QString mimeType, QJsonArray
{ {
if(documentFormat == Mimer::OctetStream) if(documentFormat == Mimer::OctetStream)
{ {
if(mimeType == Mimer::PDF) if(mimeType == Mimer::PDF || mimeType == Mimer::Plaintext)
{ {
return firstMatch(supportedMimeTypes, {Mimer::PDF, Mimer::Postscript, Mimer::PWG, Mimer::URF }); return firstMatch(supportedMimeTypes, {Mimer::PDF, Mimer::Postscript, Mimer::PWG, Mimer::URF });
} }
@ -534,6 +535,11 @@ void IppPrinter::print(QJsonObject attrs, QString filename, bool alwaysUseMediaC
emit doConvertPdf(request, filename, tempfile, documentFormat, Colors, Quality, emit doConvertPdf(request, filename, tempfile, documentFormat, Colors, Quality,
PaperSize, HwResX, HwResY, TwoSided, Tumble, PageRangeLow, PageRangeHigh); PaperSize, HwResX, HwResY, TwoSided, Tumble, PageRangeLow, PageRangeHigh);
} }
else if(mimeType == Mimer::Plaintext)
{
emit doConvertPlaintext(request, filename, tempfile, documentFormat, Colors, Quality,
PaperSize, HwResX, HwResY, TwoSided, Tumble);
}
else if (Mimer::isImage(mimeType)) else if (Mimer::isImage(mimeType))
{ {
emit doConvertImage(request, filename, tempfile, documentFormat, Colors, Quality, emit doConvertImage(request, filename, tempfile, documentFormat, Colors, Quality,

View file

@ -54,6 +54,10 @@ signals:
quint32 HwResX, quint32 HwResY, bool TwoSided, bool Tumble, quint32 HwResX, quint32 HwResY, bool TwoSided, bool Tumble,
quint32 PageRangeLow, quint32 PageRangeHigh); quint32 PageRangeLow, quint32 PageRangeHigh);
void doConvertPlaintext(QNetworkRequest request, QString filename, QTemporaryFile* tempfile,
QString targetFormat, quint32 Colors, quint32 Quality, QString PaperSize,
quint32 HwResX, quint32 HwResY, bool TwoSided, bool Tumble);
void additionalDocumentFormatsChanged(); void additionalDocumentFormatsChanged();
void busyMessageChanged(); void busyMessageChanged();
void progressChanged(); void progressChanged();

View file

@ -18,6 +18,8 @@ const QString Mimer::RTF = "text/rtf";
const QString Mimer::RTF_APP = "application/rtf"; const QString Mimer::RTF_APP = "application/rtf";
const QString Mimer::ODT = "application/vnd.oasis.opendocument.text"; const QString Mimer::ODT = "application/vnd.oasis.opendocument.text";
const QString Mimer::Plaintext = "text/plain";
const QStringList Mimer::OfficeFormats = {DOC, DOCX, RTF, RTF_APP, ODT}; const QStringList Mimer::OfficeFormats = {DOC, DOCX, RTF, RTF_APP, ODT};
Mimer::Mimer() Mimer::Mimer()

View file

@ -30,6 +30,8 @@ public:
Q_PROPERTY(const QString RTF MEMBER RTF CONSTANT); Q_PROPERTY(const QString RTF MEMBER RTF CONSTANT);
Q_PROPERTY(const QString ODT MEMBER ODT CONSTANT); Q_PROPERTY(const QString ODT MEMBER ODT CONSTANT);
Q_PROPERTY(const QString Plaintext MEMBER Plaintext CONSTANT);
Q_PROPERTY(const QStringList OfficeFormats MEMBER OfficeFormats CONSTANT); Q_PROPERTY(const QStringList OfficeFormats MEMBER OfficeFormats CONSTANT);
static const QString OctetStream; static const QString OctetStream;
@ -50,6 +52,8 @@ public:
static const QString RTF_APP; static const QString RTF_APP;
static const QString ODT; static const QString ODT;
static const QString Plaintext;
static const QStringList OfficeFormats; static const QStringList OfficeFormats;
Q_INVOKABLE static bool isImage(QString mimeType) Q_INVOKABLE static bool isImage(QString mimeType)

View file

@ -171,6 +171,10 @@
<source>Failed to get info about PDF file</source> <source>Failed to get info about PDF file</source>
<translation>PDF Info nicht abrufbar</translation> <translation>PDF Info nicht abrufbar</translation>
</message> </message>
<message>
<source>Failed to open file</source>
<translation>Öffnen der Datei fehlgeschlagen</translation>
</message>
</context> </context>
<context> <context>
<name>CoverPage</name> <name>CoverPage</name>

View file

@ -171,6 +171,10 @@
<source>Failed to get info about PDF file</source> <source>Failed to get info about PDF file</source>
<translation>Error al obtener info de archivo PDF</translation> <translation>Error al obtener info de archivo PDF</translation>
</message> </message>
<message>
<source>Failed to open file</source>
<translation>Error al abrir archivo</translation>
</message>
</context> </context>
<context> <context>
<name>CoverPage</name> <name>CoverPage</name>

View file

@ -171,6 +171,10 @@
<source>Failed to get info about PDF file</source> <source>Failed to get info about PDF file</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message>
<source>Failed to open file</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>CoverPage</name> <name>CoverPage</name>

View file

@ -171,6 +171,10 @@
<source>Failed to get info about PDF file</source> <source>Failed to get info about PDF file</source>
<translation>Informatie over het PDF-bestand ophalen mislukt</translation> <translation>Informatie over het PDF-bestand ophalen mislukt</translation>
</message> </message>
<message>
<source>Failed to open file</source>
<translation>Bestand openen mislukt</translation>
</message>
</context> </context>
<context> <context>
<name>CoverPage</name> <name>CoverPage</name>

View file

@ -171,6 +171,10 @@
<source>Failed to load image</source> <source>Failed to load image</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message>
<source>Failed to open file</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>CoverPage</name> <name>CoverPage</name>

View file

@ -171,6 +171,10 @@
<source>Failed to get info about PDF file</source> <source>Failed to get info about PDF file</source>
<translation>PDF文件信息错误</translation> <translation>PDF文件信息错误</translation>
</message> </message>
<message>
<source>Failed to open file</source>
<translation></translation>
</message>
</context> </context>
<context> <context>
<name>CoverPage</name> <name>CoverPage</name>

View file

@ -171,6 +171,10 @@
<source>Failed to get info about PDF file</source> <source>Failed to get info about PDF file</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message>
<source>Failed to open file</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>CoverPage</name> <name>CoverPage</name>