summaryrefslogtreecommitdiffstats
path: root/vcl/source/pdf
diff options
context:
space:
mode:
Diffstat (limited to 'vcl/source/pdf')
-rw-r--r--vcl/source/pdf/Matrix3.cxx117
-rw-r--r--vcl/source/pdf/PDFiumLibrary.cxx96
-rw-r--r--vcl/source/pdf/ResourceDict.cxx60
-rw-r--r--vcl/source/pdf/XmpMetadata.cxx190
4 files changed, 463 insertions, 0 deletions
diff --git a/vcl/source/pdf/Matrix3.cxx b/vcl/source/pdf/Matrix3.cxx
new file mode 100644
index 000000000..22e5c4d73
--- /dev/null
+++ b/vcl/source/pdf/Matrix3.cxx
@@ -0,0 +1,117 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ */
+
+#include <pdf/Matrix3.hxx>
+#include <cmath>
+
+namespace vcl::pdf
+{
+Matrix3::Matrix3()
+{
+ // initialize to unity
+ f[0] = 1.0;
+ f[1] = 0.0;
+ f[2] = 0.0;
+ f[3] = 1.0;
+ f[4] = 0.0;
+ f[5] = 0.0;
+}
+
+Point Matrix3::transform(const Point& rOrig) const
+{
+ double x = static_cast<double>(rOrig.X()), y = static_cast<double>(rOrig.Y());
+ return Point(static_cast<int>(x * f[0] + y * f[2] + f[4]),
+ static_cast<int>(x * f[1] + y * f[3] + f[5]));
+}
+
+void Matrix3::skew(double alpha, double beta)
+{
+ double fn[6];
+ double tb = tan(beta);
+ fn[0] = f[0] + f[2] * tb;
+ fn[1] = f[1];
+ fn[2] = f[2] + f[3] * tb;
+ fn[3] = f[3];
+ fn[4] = f[4] + f[5] * tb;
+ fn[5] = f[5];
+ if (alpha != 0.0)
+ {
+ double ta = tan(alpha);
+ fn[1] += f[0] * ta;
+ fn[3] += f[2] * ta;
+ fn[5] += f[4] * ta;
+ }
+ set(fn);
+}
+
+void Matrix3::scale(double sx, double sy)
+{
+ double fn[6];
+ fn[0] = sx * f[0];
+ fn[1] = sy * f[1];
+ fn[2] = sx * f[2];
+ fn[3] = sy * f[3];
+ fn[4] = sx * f[4];
+ fn[5] = sy * f[5];
+ set(fn);
+}
+
+void Matrix3::rotate(double angle)
+{
+ double fn[6];
+ double fSin = sin(angle);
+ double fCos = cos(angle);
+ fn[0] = f[0] * fCos - f[1] * fSin;
+ fn[1] = f[0] * fSin + f[1] * fCos;
+ fn[2] = f[2] * fCos - f[3] * fSin;
+ fn[3] = f[2] * fSin + f[3] * fCos;
+ fn[4] = f[4] * fCos - f[5] * fSin;
+ fn[5] = f[4] * fSin + f[5] * fCos;
+ set(fn);
+}
+
+void Matrix3::translate(double tx, double ty)
+{
+ f[4] += tx;
+ f[5] += ty;
+}
+
+void Matrix3::invert()
+{
+ // short circuit trivial cases
+ if (f[1] == f[2] && f[1] == 0.0 && f[0] == f[3] && f[0] == 1.0)
+ {
+ f[4] = -f[4];
+ f[5] = -f[5];
+ return;
+ }
+
+ // check determinant
+ const double fDet = f[0] * f[3] - f[1] * f[2];
+ if (fDet == 0.0)
+ return;
+
+ // invert the matrix
+ double fn[6];
+ fn[0] = +f[3] / fDet;
+ fn[1] = -f[1] / fDet;
+ fn[2] = -f[2] / fDet;
+ fn[3] = +f[0] / fDet;
+
+ // apply inversion to translation
+ fn[4] = -(f[4] * fn[0] + f[5] * fn[2]);
+ fn[5] = -(f[4] * fn[1] + f[5] * fn[3]);
+
+ set(fn);
+}
+
+} // end vcl::pdf
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/pdf/PDFiumLibrary.cxx b/vcl/source/pdf/PDFiumLibrary.cxx
new file mode 100644
index 000000000..f481078ab
--- /dev/null
+++ b/vcl/source/pdf/PDFiumLibrary.cxx
@@ -0,0 +1,96 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ */
+
+#include <config_features.h>
+
+#if HAVE_FEATURE_PDFIUM
+
+#include <vcl/filter/PDFiumLibrary.hxx>
+#include <fpdf_doc.h>
+
+#include <vcl/bitmap.hxx>
+
+#include <bitmapwriteaccess.hxx>
+
+namespace vcl::pdf
+{
+PDFium::PDFium()
+{
+ FPDF_LIBRARY_CONFIG aConfig;
+ aConfig.version = 2;
+ aConfig.m_pUserFontPaths = nullptr;
+ aConfig.m_pIsolate = nullptr;
+ aConfig.m_v8EmbedderSlot = 0;
+ FPDF_InitLibraryWithConfig(&aConfig);
+}
+
+PDFium::~PDFium() { FPDF_DestroyLibrary(); }
+
+PDFiumDocument::PDFiumDocument(FPDF_DOCUMENT pPdfDocument)
+ : mpPdfDocument(pPdfDocument)
+{
+}
+
+PDFiumDocument::~PDFiumDocument()
+{
+ if (mpPdfDocument)
+ FPDF_CloseDocument(mpPdfDocument);
+}
+
+std::unique_ptr<PDFiumPage> PDFiumDocument::openPage(int nIndex)
+{
+ std::unique_ptr<PDFiumPage> pPDFiumPage;
+ FPDF_PAGE pPage = FPDF_LoadPage(mpPdfDocument, nIndex);
+ if (pPage)
+ {
+ pPDFiumPage = std::make_unique<PDFiumPage>(pPage);
+ }
+ return pPDFiumPage;
+}
+
+int PDFiumDocument::getPageCount() { return FPDF_GetPageCount(mpPdfDocument); }
+
+BitmapChecksum PDFiumPage::getChecksum(int nMDPPerm)
+{
+ size_t nPageWidth = FPDF_GetPageWidth(mpPage);
+ size_t nPageHeight = FPDF_GetPageHeight(mpPage);
+ FPDF_BITMAP pPdfBitmap = FPDFBitmap_Create(nPageWidth, nPageHeight, /*alpha=*/1);
+ if (!pPdfBitmap)
+ {
+ return 0;
+ }
+
+ int nFlags = 0;
+ if (nMDPPerm != 3)
+ {
+ // Annotations/commenting should affect the checksum, signature verification wants this.
+ nFlags = FPDF_ANNOT;
+ }
+ FPDF_RenderPageBitmap(pPdfBitmap, mpPage, /*start_x=*/0, /*start_y=*/0, nPageWidth, nPageHeight,
+ /*rotate=*/0, nFlags);
+ Bitmap aBitmap(Size(nPageWidth, nPageHeight), 24);
+ {
+ BitmapScopedWriteAccess pWriteAccess(aBitmap);
+ const auto pPdfBuffer = static_cast<ConstScanline>(FPDFBitmap_GetBuffer(pPdfBitmap));
+ const int nStride = FPDFBitmap_GetStride(pPdfBitmap);
+ for (size_t nRow = 0; nRow < nPageHeight; ++nRow)
+ {
+ ConstScanline pPdfLine = pPdfBuffer + (nStride * nRow);
+ pWriteAccess->CopyScanline(nRow, pPdfLine, ScanlineFormat::N32BitTcBgra, nStride);
+ }
+ }
+ return aBitmap.GetChecksum();
+}
+
+} // end vcl::pdf
+
+#endif // HAVE_FEATURE_PDFIUM
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/pdf/ResourceDict.cxx b/vcl/source/pdf/ResourceDict.cxx
new file mode 100644
index 000000000..3490da30b
--- /dev/null
+++ b/vcl/source/pdf/ResourceDict.cxx
@@ -0,0 +1,60 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ */
+
+#include <pdf/ResourceDict.hxx>
+
+namespace vcl::pdf
+{
+namespace
+{
+void appendResourceMap(OStringBuffer& rBuf, const char* pPrefix,
+ std::map<OString, sal_Int32> const& rList)
+{
+ if (rList.empty())
+ return;
+ rBuf.append('/');
+ rBuf.append(pPrefix);
+ rBuf.append("<<");
+ int ni = 0;
+ for (auto const& item : rList)
+ {
+ if (!item.first.isEmpty() && item.second > 0)
+ {
+ rBuf.append('/');
+ rBuf.append(item.first);
+ rBuf.append(' ');
+ rBuf.append(item.second);
+ rBuf.append(" 0 R");
+ if (((++ni) & 7) == 0)
+ rBuf.append('\n');
+ }
+ }
+ rBuf.append(">>\n");
+}
+}
+
+void ResourceDict::append(OStringBuffer& rBuf, sal_Int32 nFontDictObject)
+{
+ rBuf.append("<</Font ");
+ rBuf.append(nFontDictObject);
+ rBuf.append(" 0 R\n");
+ appendResourceMap(rBuf, "XObject", m_aXObjects);
+ appendResourceMap(rBuf, "ExtGState", m_aExtGStates);
+ appendResourceMap(rBuf, "Shading", m_aShadings);
+ appendResourceMap(rBuf, "Pattern", m_aPatterns);
+ rBuf.append("/ProcSet[/PDF/Text");
+ if (!m_aXObjects.empty())
+ rBuf.append("/ImageC/ImageI/ImageB");
+ rBuf.append("]\n>>\n");
+}
+
+} // end vcl::pdf
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/pdf/XmpMetadata.cxx b/vcl/source/pdf/XmpMetadata.cxx
new file mode 100644
index 000000000..70588dab3
--- /dev/null
+++ b/vcl/source/pdf/XmpMetadata.cxx
@@ -0,0 +1,190 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ */
+
+#include <pdf/XmpMetadata.hxx>
+#include <tools/XmlWriter.hxx>
+
+namespace vcl::pdf
+{
+namespace
+{
+constexpr const char* constPadding = " "
+ " "
+ " "
+ " "
+ " "
+ "\n";
+}
+
+XmpMetadata::XmpMetadata()
+ : mbWritten(false)
+ , mnPDF_A(0)
+ , mbPDF_UA(false)
+{
+}
+
+void XmpMetadata::write()
+{
+ mpMemoryStream = std::make_unique<SvMemoryStream>(4096 /*Initial*/, 64 /*Resize*/);
+
+ // Header
+ mpMemoryStream->WriteOString("<?xpacket begin=\"");
+ mpMemoryStream->WriteOString(OUStringToOString(OUString(u'\xFEFF'), RTL_TEXTENCODING_UTF8));
+ mpMemoryStream->WriteOString("\" id=\"W5M0MpCehiHzreSzNTczkc9d\"?>\n");
+
+ {
+ tools::XmlWriter aXmlWriter(mpMemoryStream.get());
+ aXmlWriter.startDocument(2, false);
+ aXmlWriter.startElement("x", "xmpmeta", "adobe:ns:meta/");
+ aXmlWriter.startElement("rdf", "RDF", "http://www.w3.org/1999/02/22-rdf-syntax-ns#");
+
+ // PDF/A part ( ISO 19005-1:2005 - 6.7.11 )
+ if (mnPDF_A > 0)
+ {
+ OString sPdfVersion = OString::number(mnPDF_A);
+ OString sPdfConformance = (mnPDF_A == 1) ? "A" : "B";
+
+ aXmlWriter.startElement("rdf:Description");
+ aXmlWriter.attribute("rdf:about", OString(""));
+ aXmlWriter.attribute("xmlns:pdfaid", OString("http://www.aiim.org/pdfa/ns/id/"));
+
+ aXmlWriter.startElement("pdfaid:part");
+ aXmlWriter.content(sPdfVersion);
+ aXmlWriter.endElement();
+
+ aXmlWriter.startElement("pdfaid:conformance");
+ aXmlWriter.content(sPdfConformance);
+ aXmlWriter.endElement();
+
+ aXmlWriter.endElement();
+ }
+
+ // Dublin Core properties
+ if (!msTitle.isEmpty() || !msAuthor.isEmpty() || !msSubject.isEmpty())
+ {
+ aXmlWriter.startElement("rdf:Description");
+ aXmlWriter.attribute("rdf:about", OString(""));
+ aXmlWriter.attribute("xmlns:dc", OString("http://purl.org/dc/elements/1.1/"));
+ if (!msTitle.isEmpty())
+ {
+ // this is according to PDF/A-1, technical corrigendum 1 (2007-04-01)
+ aXmlWriter.startElement("dc:title");
+ aXmlWriter.startElement("rdf:Alt");
+ aXmlWriter.startElement("rdf:li");
+ aXmlWriter.attribute("xml:lang", OString("x-default"));
+ aXmlWriter.content(msTitle);
+ aXmlWriter.endElement();
+ aXmlWriter.endElement();
+ aXmlWriter.endElement();
+ }
+ if (!msAuthor.isEmpty())
+ {
+ aXmlWriter.startElement("dc:creator");
+ aXmlWriter.startElement("rdf:Seq");
+ aXmlWriter.startElement("rdf:li");
+ aXmlWriter.content(msAuthor);
+ aXmlWriter.endElement();
+ aXmlWriter.endElement();
+ aXmlWriter.endElement();
+ }
+ if (!msSubject.isEmpty())
+ {
+ aXmlWriter.startElement("dc:description");
+ aXmlWriter.startElement("rdf:Alt");
+ aXmlWriter.startElement("rdf:li");
+ aXmlWriter.attribute("xml:lang", OString("x-default"));
+ aXmlWriter.content(msSubject);
+ aXmlWriter.endElement();
+ aXmlWriter.endElement();
+ aXmlWriter.endElement();
+ }
+ aXmlWriter.endElement();
+ }
+
+ // PDF/UA
+ if (mbPDF_UA)
+ {
+ OString sPdfUaVersion = OString::number(1);
+ aXmlWriter.startElement("rdf:Description");
+ aXmlWriter.attribute("rdf:about", OString(""));
+ aXmlWriter.attribute("xmlns:pdfuaid", OString("http://www.aiim.org/pdfua/ns/id/"));
+
+ aXmlWriter.startElement("pdfuaid:part");
+ aXmlWriter.content(sPdfUaVersion);
+ aXmlWriter.endElement();
+
+ aXmlWriter.endElement();
+ }
+
+ // PDF properties
+ if (!msProducer.isEmpty() || !msKeywords.isEmpty())
+ {
+ aXmlWriter.startElement("rdf:Description");
+ aXmlWriter.attribute("rdf:about", OString(""));
+ aXmlWriter.attribute("xmlns:pdf", OString("http://ns.adobe.com/pdf/1.3/"));
+ if (!msProducer.isEmpty())
+ {
+ aXmlWriter.startElement("pdf:Producer");
+ aXmlWriter.content(msProducer);
+ aXmlWriter.endElement();
+ }
+ if (!msKeywords.isEmpty())
+ {
+ aXmlWriter.startElement("pdf:Keywords");
+ aXmlWriter.content(msKeywords);
+ aXmlWriter.endElement();
+ }
+ aXmlWriter.endElement();
+ }
+
+ aXmlWriter.startElement("rdf:Description");
+ aXmlWriter.attribute("rdf:about", OString(""));
+ aXmlWriter.attribute("xmlns:xmp", OString("http://ns.adobe.com/xap/1.0/"));
+ if (!m_sCreatorTool.isEmpty())
+ {
+ aXmlWriter.startElement("xmp:CreatorTool");
+ aXmlWriter.content(m_sCreatorTool);
+ aXmlWriter.endElement();
+ }
+ aXmlWriter.startElement("xmp:CreateDate");
+ aXmlWriter.content(m_sCreateDate);
+ aXmlWriter.endElement();
+ aXmlWriter.endElement();
+
+ aXmlWriter.endElement();
+ aXmlWriter.endElement();
+ aXmlWriter.endDocument();
+ }
+
+ // add padding (needed so the metadata can be changed in-place"
+ for (sal_Int32 nSpaces = 1; nSpaces <= 21; nSpaces++)
+ mpMemoryStream->WriteOString(constPadding);
+
+ mpMemoryStream->WriteOString("<?xpacket end=\"w\"?>\n");
+ mbWritten = true;
+}
+
+sal_uInt64 XmpMetadata::getSize()
+{
+ if (!mbWritten)
+ write();
+ return mpMemoryStream->GetSize();
+}
+
+const void* XmpMetadata::getData()
+{
+ if (!mbWritten)
+ write();
+ return mpMemoryStream->GetData();
+}
+
+} // end vcl::pdf
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */