summaryrefslogtreecommitdiffstats
path: root/src/gui/painting/qpdf.cpp
diff options
context:
space:
mode:
authorJordi Pujol Foyo <jordi@vikingsoftware.com>2019-12-13 14:27:11 +0100
committerJordi Pujol Foyo <jordi@vikingsoftware.com>2019-12-19 21:23:43 +0100
commit9d6b4d11424cdcd6c572cdcc50b3b790b6e673c0 (patch)
treeb51c3f23188465627df655339166f5c1241a5d25 /src/gui/painting/qpdf.cpp
parent6227a0d2e2e924cf717fa2a3d7c3e1223c4aed5f (diff)
New features for QPdfWriter
Added new API setDocumentXmpMetadata/documentXmpMetadata and addFileAttachment [ChangeLog][QtGui][QPdfWriter] New API to provide external document XMP metadata and attach files to PDF. Fixes: QTBUG-78651 Fixes: QTBUG-78764 Change-Id: Ic0b37e8d12899f907001db469080594c14c87655 Reviewed-by: Eirik Aavitsland <eirik.aavitsland@qt.io>
Diffstat (limited to 'src/gui/painting/qpdf.cpp')
-rw-r--r--src/gui/painting/qpdf.cpp159
1 files changed, 128 insertions, 31 deletions
diff --git a/src/gui/painting/qpdf.cpp b/src/gui/painting/qpdf.cpp
index 932c3e6f5a..de9fc13331 100644
--- a/src/gui/painting/qpdf.cpp
+++ b/src/gui/painting/qpdf.cpp
@@ -1390,6 +1390,18 @@ void QPdfEngine::setPdfVersion(PdfVersion version)
d->pdfVersion = version;
}
+void QPdfEngine::setDocumentXmpMetadata(const QByteArray &xmpMetadata)
+{
+ Q_D(QPdfEngine);
+ d->xmpDocumentMetadata = xmpMetadata;
+}
+
+QByteArray QPdfEngine::documentXmpMetadata() const
+{
+ Q_D(const QPdfEngine);
+ return d->xmpDocumentMetadata;
+}
+
void QPdfEngine::setPageLayout(const QPageLayout &pageLayout)
{
Q_D(QPdfEngine);
@@ -1520,6 +1532,8 @@ bool QPdfEngine::begin(QPaintDevice *pdev)
d->xrefPositions.clear();
d->pageRoot = 0;
+ d->embeddedfilesRoot = 0;
+ d->namesRoot = 0;
d->catalog = 0;
d->info = 0;
d->graphicsState = 0;
@@ -1555,10 +1569,18 @@ bool QPdfEngine::end()
d->outDevice = nullptr;
}
+ d->fileCache.clear();
+
setActive(false);
return true;
}
+void QPdfEngine::addFileAttachment(const QString &fileName, const QByteArray &data, const QString &mimeType)
+{
+ Q_D(QPdfEngine);
+ d->fileCache.push_back({fileName, data, mimeType});
+}
+
QPdfEnginePrivate::~QPdfEnginePrivate()
{
qDeleteAll(fonts);
@@ -1586,13 +1608,19 @@ void QPdfEnginePrivate::writeHeader()
int metaDataObj = -1;
int outputIntentObj = -1;
+ if (pdfVersion == QPdfEngine::Version_A1b || !xmpDocumentMetadata.isEmpty()) {
+ metaDataObj = writeXmpDcumentMetaData();
+ }
if (pdfVersion == QPdfEngine::Version_A1b) {
- metaDataObj = writeXmpMetaData();
outputIntentObj = writeOutputIntent();
}
catalog = addXrefEntry(-1);
pageRoot = requestObject();
+ if (!fileCache.isEmpty()) {
+ namesRoot = requestObject();
+ embeddedfilesRoot = requestObject();
+ }
// catalog
{
@@ -1602,10 +1630,15 @@ void QPdfEnginePrivate::writeHeader()
<< "/Type /Catalog\n"
<< "/Pages " << pageRoot << "0 R\n";
- if (pdfVersion == QPdfEngine::Version_A1b) {
- s << "/OutputIntents [" << outputIntentObj << "0 R]\n";
+ // Embedded files, if any
+ if (!fileCache.isEmpty())
+ s << "/Names " << embeddedfilesRoot << "0 R\n";
+
+ if (pdfVersion == QPdfEngine::Version_A1b || !xmpDocumentMetadata.isEmpty())
s << "/Metadata " << metaDataObj << "0 R\n";
- }
+
+ if (pdfVersion == QPdfEngine::Version_A1b)
+ s << "/OutputIntents [" << outputIntentObj << "0 R]\n";
s << ">>\n"
<< "endobj\n";
@@ -1613,6 +1646,12 @@ void QPdfEnginePrivate::writeHeader()
write(catalog);
}
+ if (!fileCache.isEmpty()) {
+ addXrefEntry(embeddedfilesRoot);
+ xprintf("<</EmbeddedFiles %d 0 R>>\n"
+ "endobj\n", namesRoot);
+ }
+
// graphics state
graphicsState = addXrefEntry(-1);
xprintf("<<\n"
@@ -1664,39 +1703,45 @@ void QPdfEnginePrivate::writeInfo()
"endobj\n");
}
-int QPdfEnginePrivate::writeXmpMetaData()
+int QPdfEnginePrivate::writeXmpDcumentMetaData()
{
const int metaDataObj = addXrefEntry(-1);
+ QByteArray metaDataContent;
+
+ if (xmpDocumentMetadata.isEmpty()) {
+ const QString producer(QString::fromLatin1("Qt " QT_VERSION_STR));
+
+ const QDateTime now = QDateTime::currentDateTime();
+ const QDate date = now.date();
+ const QTime time = now.time();
+ const QString timeStr =
+ QString::asprintf("%d-%02d-%02dT%02d:%02d:%02d",
+ date.year(), date.month(), date.day(),
+ time.hour(), time.minute(), time.second());
+
+ const int offset = now.offsetFromUtc();
+ const int hours = (offset / 60) / 60;
+ const int mins = (offset / 60) % 60;
+ QString tzStr;
+ if (offset < 0)
+ tzStr = QString::asprintf("-%02d:%02d", -hours, -mins);
+ else if (offset > 0)
+ tzStr = QString::asprintf("+%02d:%02d", hours , mins);
+ else
+ tzStr = QLatin1String("Z");
- const QString producer(QString::fromLatin1("Qt " QT_VERSION_STR));
-
- const QDateTime now = QDateTime::currentDateTime();
- const QDate date = now.date();
- const QTime time = now.time();
- const QString timeStr =
- QString::asprintf("%d-%02d-%02dT%02d:%02d:%02d",
- date.year(), date.month(), date.day(),
- time.hour(), time.minute(), time.second());
+ const QString metaDataDate = timeStr + tzStr;
- const int offset = now.offsetFromUtc();
- const int hours = (offset / 60) / 60;
- const int mins = (offset / 60) % 60;
- QString tzStr;
- if (offset < 0)
- tzStr = QString::asprintf("-%02d:%02d", -hours, -mins);
- else if (offset > 0)
- tzStr = QString::asprintf("+%02d:%02d", hours , mins);
+ QFile metaDataFile(QLatin1String(":/qpdf/qpdfa_metadata.xml"));
+ metaDataFile.open(QIODevice::ReadOnly);
+ metaDataContent = QString::fromUtf8(metaDataFile.readAll()).arg(producer.toHtmlEscaped(),
+ title.toHtmlEscaped(),
+ creator.toHtmlEscaped(),
+ metaDataDate).toUtf8();
+ }
else
- tzStr = QLatin1String("Z");
+ metaDataContent = xmpDocumentMetadata;
- const QString metaDataDate = timeStr + tzStr;
-
- QFile metaDataFile(QLatin1String(":/qpdf/qpdfa_metadata.xml"));
- metaDataFile.open(QIODevice::ReadOnly);
- const QByteArray metaDataContent = QString::fromUtf8(metaDataFile.readAll()).arg(producer.toHtmlEscaped(),
- title.toHtmlEscaped(),
- creator.toHtmlEscaped(),
- metaDataDate).toUtf8();
xprintf("<<\n"
"/Type /Metadata /Subtype /XML\n"
"/Length %d\n"
@@ -1774,6 +1819,56 @@ void QPdfEnginePrivate::writePageRoot()
"endobj\n");
}
+void QPdfEnginePrivate::writeAttachmentRoot()
+{
+ if (fileCache.isEmpty())
+ return;
+
+ QVector<int> attachments;
+ const int size = fileCache.size();
+ for (int i = 0; i < size; ++i) {
+ auto attachment = fileCache.at(i);
+ const int attachmentID = addXrefEntry(-1);
+ xprintf("<<\n");
+ if (do_compress)
+ xprintf("/Filter /FlateDecode\n");
+
+ const int lenobj = requestObject();
+ xprintf("/Length %d 0 R\n", lenobj);
+ int len = 0;
+ xprintf(">>\nstream\n");
+ len = writeCompressed(attachment.data);
+ xprintf("\nendstream\n"
+ "endobj\n");
+ addXrefEntry(lenobj);
+ xprintf("%d\n"
+ "endobj\n", len);
+
+ attachments.push_back(addXrefEntry(-1));
+ xprintf("<<\n"
+ "/F (%s)", attachment.fileName.toLatin1().constData());
+
+ xprintf("\n/EF <</F %d 0 R>>\n"
+ "/Type/Filespec\n"
+ , attachmentID);
+ if (!attachment.mimeType.isEmpty())
+ xprintf("/Subtype/%s\n",
+ attachment.mimeType.replace(QLatin1String("/"),
+ QLatin1String("#2F")).toLatin1().constData());
+ xprintf(">>\nendobj\n");
+ }
+
+ // names
+ addXrefEntry(namesRoot);
+ xprintf("<</Names[");
+ for (int i = 0; i < size; ++i) {
+ auto attachment = fileCache.at(i);
+ printString(attachment.fileName);
+ xprintf("%d 0 R\n", attachments.at(i));
+ }
+ xprintf("]>>\n"
+ "endobj\n");
+}
void QPdfEnginePrivate::embedFont(QFontSubset *font)
{
@@ -2031,6 +2126,8 @@ void QPdfEnginePrivate::writeTail()
writePage();
writeFonts();
writePageRoot();
+ writeAttachmentRoot();
+
addXrefEntry(xrefPositions.size(),false);
xprintf("xref\n"
"0 %d\n"