diff options
Diffstat (limited to 'vcl/source/pdf')
-rw-r--r-- | vcl/source/pdf/Matrix3.cxx | 117 | ||||
-rw-r--r-- | vcl/source/pdf/PDFiumLibrary.cxx | 96 | ||||
-rw-r--r-- | vcl/source/pdf/ResourceDict.cxx | 60 | ||||
-rw-r--r-- | vcl/source/pdf/XmpMetadata.cxx | 190 |
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: */ |