summaryrefslogtreecommitdiffstats
path: root/vcl/source/pdf
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:06:44 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:06:44 +0000
commited5640d8b587fbcfed7dd7967f3de04b37a76f26 (patch)
tree7a5f7c6c9d02226d7471cb3cc8fbbf631b415303 /vcl/source/pdf
parentInitial commit. (diff)
downloadlibreoffice-ed5640d8b587fbcfed7dd7967f3de04b37a76f26.tar.xz
libreoffice-ed5640d8b587fbcfed7dd7967f3de04b37a76f26.zip
Adding upstream version 4:7.4.7.upstream/4%7.4.7upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'vcl/source/pdf')
-rw-r--r--vcl/source/pdf/DummyPDFiumLibrary.cxx23
-rw-r--r--vcl/source/pdf/ExternalPDFStreams.cxx42
-rw-r--r--vcl/source/pdf/Matrix3.cxx116
-rw-r--r--vcl/source/pdf/PDFiumLibrary.cxx1333
-rw-r--r--vcl/source/pdf/PDFiumTools.cxx73
-rw-r--r--vcl/source/pdf/PdfConfig.cxx36
-rw-r--r--vcl/source/pdf/ResourceDict.cxx60
-rw-r--r--vcl/source/pdf/XmpMetadata.cxx190
8 files changed, 1873 insertions, 0 deletions
diff --git a/vcl/source/pdf/DummyPDFiumLibrary.cxx b/vcl/source/pdf/DummyPDFiumLibrary.cxx
new file mode 100644
index 000000000..7c42084eb
--- /dev/null
+++ b/vcl/source/pdf/DummyPDFiumLibrary.cxx
@@ -0,0 +1,23 @@
+/* -*- 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 <vcl/filter/PDFiumLibrary.hxx>
+
+namespace vcl::pdf
+{
+std::shared_ptr<PDFium>& PDFiumLibrary::get()
+{
+ static std::shared_ptr<PDFium> pInstance;
+ return pInstance;
+}
+
+} // end vcl::pdf
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/pdf/ExternalPDFStreams.cxx b/vcl/source/pdf/ExternalPDFStreams.cxx
new file mode 100644
index 000000000..e3716e1e0
--- /dev/null
+++ b/vcl/source/pdf/ExternalPDFStreams.cxx
@@ -0,0 +1,42 @@
+/* -*- 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/ExternalPDFStreams.hxx>
+#include <comphelper/hash.hxx>
+
+namespace vcl
+{
+sal_Int32 ExternalPDFStreams::store(BinaryDataContainer const& rDataContainer)
+{
+ sal_Int32 nIndex = -1;
+
+ std::vector<sal_uInt8> aHash = comphelper::Hash::calculateHash(
+ rDataContainer.getData(), rDataContainer.getSize(), comphelper::HashType::SHA1);
+
+ auto it = maStreamIndexMap.find(aHash);
+ if (it == maStreamIndexMap.end())
+ {
+ auto& rExternalStream = maStreamList.emplace_back();
+ rExternalStream.maDataContainer = rDataContainer;
+ nIndex = maStreamList.size() - 1;
+ maStreamIndexMap.emplace(aHash, nIndex);
+ }
+ else
+ {
+ nIndex = it->second;
+ }
+
+ return nIndex;
+}
+
+ExternalPDFStream& ExternalPDFStreams::get(sal_uInt32 nIndex) { return maStreamList.at(nIndex); }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/pdf/Matrix3.cxx b/vcl/source/pdf/Matrix3.cxx
new file mode 100644
index 000000000..475f87d96
--- /dev/null
+++ b/vcl/source/pdf/Matrix3.cxx
@@ -0,0 +1,116 @@
+/* -*- 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(x * f[0] + y * f[2] + f[4], 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..b02358d43
--- /dev/null
+++ b/vcl/source/pdf/PDFiumLibrary.cxx
@@ -0,0 +1,1333 @@
+/* -*- 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 <vcl/filter/PDFiumLibrary.hxx>
+
+#include <cassert>
+
+#include <fpdf_doc.h>
+#include <fpdf_annot.h>
+#include <fpdf_edit.h>
+#include <fpdf_text.h>
+#include <fpdf_save.h>
+#include <fpdf_signature.h>
+#include <fpdf_formfill.h>
+
+#include <osl/endian.h>
+#include <vcl/bitmap.hxx>
+#include <tools/stream.hxx>
+#include <tools/UnitConversion.hxx>
+#include <o3tl/string_view.hxx>
+
+#include <bitmap/BitmapWriteAccess.hxx>
+
+using namespace com::sun::star;
+
+static_assert(static_cast<int>(vcl::pdf::PDFPageObjectType::Unknown) == FPDF_PAGEOBJ_UNKNOWN,
+ "PDFPageObjectType::Unknown value mismatch");
+static_assert(static_cast<int>(vcl::pdf::PDFPageObjectType::Text) == FPDF_PAGEOBJ_TEXT,
+ "PDFPageObjectType::Text value mismatch");
+static_assert(static_cast<int>(vcl::pdf::PDFPageObjectType::Path) == FPDF_PAGEOBJ_PATH,
+ "PDFPageObjectType::Path value mismatch");
+static_assert(static_cast<int>(vcl::pdf::PDFPageObjectType::Image) == FPDF_PAGEOBJ_IMAGE,
+ "PDFPageObjectType::Image value mismatch");
+static_assert(static_cast<int>(vcl::pdf::PDFPageObjectType::Shading) == FPDF_PAGEOBJ_SHADING,
+ "PDFPageObjectType::Shading value mismatch");
+static_assert(static_cast<int>(vcl::pdf::PDFPageObjectType::Form) == FPDF_PAGEOBJ_FORM,
+ "PDFPageObjectType::Form value mismatch");
+
+static_assert(static_cast<int>(vcl::pdf::PDFSegmentType::Unknown) == FPDF_SEGMENT_UNKNOWN,
+ "PDFSegmentType::Unknown value mismatch");
+static_assert(static_cast<int>(vcl::pdf::PDFSegmentType::Lineto) == FPDF_SEGMENT_LINETO,
+ "PDFSegmentType::Lineto value mismatch");
+static_assert(static_cast<int>(vcl::pdf::PDFSegmentType::Bezierto) == FPDF_SEGMENT_BEZIERTO,
+ "PDFSegmentType::Bezierto value mismatch");
+static_assert(static_cast<int>(vcl::pdf::PDFSegmentType::Moveto) == FPDF_SEGMENT_MOVETO,
+ "PDFSegmentType::Moveto value mismatch");
+
+static_assert(static_cast<int>(vcl::pdf::PDFBitmapType::Unknown) == FPDFBitmap_Unknown,
+ "PDFBitmapType::Unknown value mismatch");
+static_assert(static_cast<int>(vcl::pdf::PDFBitmapType::Gray) == FPDFBitmap_Gray,
+ "PDFBitmapType::Gray value mismatch");
+static_assert(static_cast<int>(vcl::pdf::PDFBitmapType::BGR) == FPDFBitmap_BGR,
+ "PDFBitmapType::BGR value mismatch");
+static_assert(static_cast<int>(vcl::pdf::PDFBitmapType::BGRx) == FPDFBitmap_BGRx,
+ "PDFBitmapType::BGRx value mismatch");
+static_assert(static_cast<int>(vcl::pdf::PDFBitmapType::BGRA) == FPDFBitmap_BGRA,
+ "PDFBitmapType::BGRA value mismatch");
+
+static_assert(static_cast<int>(vcl::pdf::PDFObjectType::Unknown) == FPDF_OBJECT_UNKNOWN,
+ "PDFObjectType::Unknown value mismatch");
+static_assert(static_cast<int>(vcl::pdf::PDFObjectType::Boolean) == FPDF_OBJECT_BOOLEAN,
+ "PDFObjectType::Boolean value mismatch");
+static_assert(static_cast<int>(vcl::pdf::PDFObjectType::Number) == FPDF_OBJECT_NUMBER,
+ "PDFObjectType::Number value mismatch");
+static_assert(static_cast<int>(vcl::pdf::PDFObjectType::String) == FPDF_OBJECT_STRING,
+ "PDFObjectType::String value mismatch");
+static_assert(static_cast<int>(vcl::pdf::PDFObjectType::Name) == FPDF_OBJECT_NAME,
+ "PDFObjectType::Name value mismatch");
+static_assert(static_cast<int>(vcl::pdf::PDFObjectType::Array) == FPDF_OBJECT_ARRAY,
+ "PDFObjectType::Array value mismatch");
+static_assert(static_cast<int>(vcl::pdf::PDFObjectType::Dictionary) == FPDF_OBJECT_DICTIONARY,
+ "PDFObjectType::Dictionary value mismatch");
+static_assert(static_cast<int>(vcl::pdf::PDFObjectType::Stream) == FPDF_OBJECT_STREAM,
+ "PDFObjectType::Stream value mismatch");
+static_assert(static_cast<int>(vcl::pdf::PDFObjectType::Nullobj) == FPDF_OBJECT_NULLOBJ,
+ "PDFObjectType::Nullobj value mismatch");
+static_assert(static_cast<int>(vcl::pdf::PDFObjectType::Reference) == FPDF_OBJECT_REFERENCE,
+ "PDFObjectType::Reference value mismatch");
+
+static_assert(static_cast<int>(vcl::pdf::PDFTextRenderMode::Unknown) == FPDF_TEXTRENDERMODE_UNKNOWN,
+ "PDFTextRenderMode::Unknown value mismatch");
+static_assert(static_cast<int>(vcl::pdf::PDFTextRenderMode::Fill) == FPDF_TEXTRENDERMODE_FILL,
+ "PDFTextRenderMode::Fill value mismatch");
+static_assert(static_cast<int>(vcl::pdf::PDFTextRenderMode::Stroke) == FPDF_TEXTRENDERMODE_STROKE,
+ "PDFTextRenderMode::Stroke value mismatch");
+static_assert(static_cast<int>(vcl::pdf::PDFTextRenderMode::FillStroke)
+ == FPDF_TEXTRENDERMODE_FILL_STROKE,
+ "PDFTextRenderMode::FillStroke value mismatch");
+static_assert(static_cast<int>(vcl::pdf::PDFTextRenderMode::Invisible)
+ == FPDF_TEXTRENDERMODE_INVISIBLE,
+ "PDFTextRenderMode::Invisible value mismatch");
+static_assert(static_cast<int>(vcl::pdf::PDFTextRenderMode::FillClip)
+ == FPDF_TEXTRENDERMODE_FILL_CLIP,
+ "PDFTextRenderMode::FillClip value mismatch");
+static_assert(static_cast<int>(vcl::pdf::PDFTextRenderMode::StrokeClip)
+ == FPDF_TEXTRENDERMODE_STROKE_CLIP,
+ "PDFTextRenderMode::StrokeClip value mismatch");
+static_assert(static_cast<int>(vcl::pdf::PDFTextRenderMode::FillStrokeClip)
+ == FPDF_TEXTRENDERMODE_FILL_STROKE_CLIP,
+ "PDFTextRenderMode::FillStrokeClip value mismatch");
+static_assert(static_cast<int>(vcl::pdf::PDFTextRenderMode::Clip) == FPDF_TEXTRENDERMODE_CLIP,
+ "PDFTextRenderMode::Clip value mismatch");
+
+static_assert(static_cast<int>(vcl::pdf::PDFFillMode::None) == FPDF_FILLMODE_NONE,
+ "PDFFillMode::None value mismatch");
+static_assert(static_cast<int>(vcl::pdf::PDFFillMode::Alternate) == FPDF_FILLMODE_ALTERNATE,
+ "PDFFillMode::Alternate value mismatch");
+static_assert(static_cast<int>(vcl::pdf::PDFFillMode::Winding) == FPDF_FILLMODE_WINDING,
+ "PDFFillMode::Winding value mismatch");
+
+static_assert(static_cast<int>(vcl::pdf::PDFFindFlags::MatchCase) == FPDF_MATCHCASE,
+ "PDFFindFlags::MatchCase value mismatch");
+static_assert(static_cast<int>(vcl::pdf::PDFFindFlags::MatchWholeWord) == FPDF_MATCHWHOLEWORD,
+ "PDFFindFlags::MatchWholeWord value mismatch");
+static_assert(static_cast<int>(vcl::pdf::PDFFindFlags::Consecutive) == FPDF_CONSECUTIVE,
+ "PDFFindFlags::Consecutive value mismatch");
+
+static_assert(static_cast<int>(vcl::pdf::PDFErrorType::Success) == FPDF_ERR_SUCCESS,
+ "PDFErrorType::Success value mismatch");
+static_assert(static_cast<int>(vcl::pdf::PDFErrorType::Unknown) == FPDF_ERR_UNKNOWN,
+ "PDFErrorType::Unknown value mismatch");
+static_assert(static_cast<int>(vcl::pdf::PDFErrorType::File) == FPDF_ERR_FILE,
+ "PDFErrorType::File value mismatch");
+static_assert(static_cast<int>(vcl::pdf::PDFErrorType::Format) == FPDF_ERR_FORMAT,
+ "PDFErrorType::Format value mismatch");
+static_assert(static_cast<int>(vcl::pdf::PDFErrorType::Password) == FPDF_ERR_PASSWORD,
+ "PDFErrorType::Password value mismatch");
+static_assert(static_cast<int>(vcl::pdf::PDFErrorType::Security) == FPDF_ERR_SECURITY,
+ "PDFErrorType::Security value mismatch");
+static_assert(static_cast<int>(vcl::pdf::PDFErrorType::Page) == FPDF_ERR_PAGE,
+ "PDFErrorType::Page value mismatch");
+
+namespace
+{
+/// Callback class to be used with FPDF_SaveWithVersion().
+struct CompatibleWriter : public FPDF_FILEWRITE
+{
+ CompatibleWriter(SvMemoryStream& rStream)
+ : m_rStream(rStream)
+ {
+ }
+
+ SvMemoryStream& m_rStream;
+};
+
+int CompatibleWriterCallback(FPDF_FILEWRITE* pFileWrite, const void* pData, unsigned long nSize)
+{
+ auto pImpl = static_cast<CompatibleWriter*>(pFileWrite);
+ pImpl->m_rStream.WriteBytes(pData, nSize);
+ return 1;
+}
+}
+
+namespace vcl::pdf
+{
+namespace
+{
+class PDFiumBitmapImpl final : public PDFiumBitmap
+{
+private:
+ FPDF_BITMAP mpBitmap;
+
+ PDFiumBitmapImpl(const PDFiumBitmapImpl&) = delete;
+ PDFiumBitmapImpl& operator=(const PDFiumBitmapImpl&) = delete;
+
+public:
+ PDFiumBitmapImpl(FPDF_BITMAP pBitmap);
+ ~PDFiumBitmapImpl() override;
+ FPDF_BITMAP getPointer() { return mpBitmap; }
+
+ void fillRect(int left, int top, int width, int height, sal_uInt32 nColor) override;
+ void renderPageBitmap(PDFiumDocument* pDoc, PDFiumPage* pPage, int nStartX, int nStartY,
+ int nSizeX, int nSizeY) override;
+ ConstScanline getBuffer() override;
+ int getStride() override;
+ int getWidth() override;
+ int getHeight() override;
+ PDFBitmapType getFormat() override;
+};
+
+class PDFiumPathSegmentImpl final : public PDFiumPathSegment
+{
+private:
+ FPDF_PATHSEGMENT mpPathSegment;
+
+ PDFiumPathSegmentImpl(const PDFiumPathSegmentImpl&) = delete;
+ PDFiumPathSegmentImpl& operator=(const PDFiumPathSegmentImpl&) = delete;
+
+public:
+ PDFiumPathSegmentImpl(FPDF_PATHSEGMENT pPathSegment);
+
+ basegfx::B2DPoint getPoint() const override;
+ bool isClosed() const override;
+ PDFSegmentType getType() const override;
+};
+
+class PDFiumAnnotationImpl final : public PDFiumAnnotation
+{
+private:
+ FPDF_ANNOTATION mpAnnotation;
+
+ PDFiumAnnotationImpl(const PDFiumAnnotationImpl&) = delete;
+ PDFiumAnnotationImpl& operator=(const PDFiumAnnotationImpl&) = delete;
+
+public:
+ PDFiumAnnotationImpl(FPDF_ANNOTATION pAnnotation);
+ ~PDFiumAnnotationImpl();
+ FPDF_ANNOTATION getPointer() { return mpAnnotation; }
+
+ PDFAnnotationSubType getSubType() override;
+ basegfx::B2DRectangle getRectangle() override;
+ bool hasKey(OString const& rKey) override;
+ PDFObjectType getValueType(OString const& rKey) override;
+ OUString getString(OString const& rKey) override;
+ std::unique_ptr<PDFiumAnnotation> getLinked(OString const& rKey) override;
+ int getObjectCount() override;
+ std::unique_ptr<PDFiumPageObject> getObject(int nIndex) override;
+ std::vector<std::vector<basegfx::B2DPoint>> getInkStrokes() override;
+ std::vector<basegfx::B2DPoint> getVertices() override;
+ Color getColor() override;
+ Color getInteriorColor() override;
+ float getBorderWidth() override;
+ basegfx::B2DSize getBorderCornerRadius() override;
+ size_t getAttachmentPointsCount() override;
+ std::vector<basegfx::B2DPoint> getAttachmentPoints(size_t nIndex) override;
+ std::vector<basegfx::B2DPoint> getLineGeometry() override;
+};
+
+class PDFiumPageObjectImpl final : public PDFiumPageObject
+{
+private:
+ FPDF_PAGEOBJECT mpPageObject;
+
+ PDFiumPageObjectImpl(const PDFiumPageObjectImpl&) = delete;
+ PDFiumPageObjectImpl& operator=(const PDFiumPageObjectImpl&) = delete;
+
+public:
+ PDFiumPageObjectImpl(FPDF_PAGEOBJECT pPageObject);
+
+ PDFPageObjectType getType() override;
+ OUString getText(std::unique_ptr<PDFiumTextPage> const& pTextPage) override;
+
+ int getFormObjectCount() override;
+ std::unique_ptr<PDFiumPageObject> getFormObject(int nIndex) override;
+
+ basegfx::B2DHomMatrix getMatrix() override;
+ basegfx::B2DRectangle getBounds() override;
+ double getFontSize() override;
+ OUString getFontName() override;
+ PDFTextRenderMode getTextRenderMode() override;
+ Color getFillColor() override;
+ Color getStrokeColor() override;
+ double getStrokeWidth() override;
+ // Path
+ int getPathSegmentCount() override;
+ std::unique_ptr<PDFiumPathSegment> getPathSegment(int index) override;
+ Size getImageSize(PDFiumPage& rPage) override;
+ std::unique_ptr<PDFiumBitmap> getImageBitmap() override;
+ bool getDrawMode(PDFFillMode& eFillMode, bool& bStroke) override;
+};
+
+class PDFiumSearchHandleImpl final : public PDFiumSearchHandle
+{
+private:
+ FPDF_SCHHANDLE mpSearchHandle;
+
+ PDFiumSearchHandleImpl(const PDFiumSearchHandleImpl&) = delete;
+ PDFiumSearchHandleImpl& operator=(const PDFiumSearchHandleImpl&) = delete;
+
+public:
+ PDFiumSearchHandleImpl(FPDF_SCHHANDLE pSearchHandle);
+ ~PDFiumSearchHandleImpl();
+
+ bool findNext() override;
+ bool findPrev() override;
+ int getSearchResultIndex() override;
+ int getSearchCount() override;
+};
+
+class PDFiumTextPageImpl final : public PDFiumTextPage
+{
+private:
+ FPDF_TEXTPAGE mpTextPage;
+
+ PDFiumTextPageImpl(const PDFiumTextPageImpl&) = delete;
+ PDFiumTextPageImpl& operator=(const PDFiumTextPageImpl&) = delete;
+
+public:
+ PDFiumTextPageImpl(FPDF_TEXTPAGE pTextPage);
+ ~PDFiumTextPageImpl();
+
+ FPDF_TEXTPAGE getPointer() { return mpTextPage; }
+
+ int countChars() override;
+ unsigned int getUnicode(int index) override;
+ std::unique_ptr<PDFiumSearchHandle> findStart(const OUString& rFindWhat, PDFFindFlags nFlags,
+ sal_Int32 nStartIndex) override;
+
+ /// Returned rect is no longer upside down and is in mm100.
+ basegfx::B2DRectangle getCharBox(int nIndex, double fPageHeight) override;
+};
+
+class PDFiumSignatureImpl final : public PDFiumSignature
+{
+private:
+ FPDF_SIGNATURE mpSignature;
+ PDFiumSignatureImpl(const PDFiumSignatureImpl&) = delete;
+ PDFiumSignatureImpl& operator=(const PDFiumSignatureImpl&) = delete;
+
+public:
+ PDFiumSignatureImpl(FPDF_SIGNATURE pSignature);
+
+ std::vector<int> getByteRange() override;
+ int getDocMDPPermission() override;
+ std::vector<unsigned char> getContents() override;
+ OString getSubFilter() override;
+ OUString getReason() override;
+ css::util::DateTime getTime() override;
+};
+
+class PDFiumPageImpl final : public PDFiumPage
+{
+private:
+ FPDF_PAGE mpPage;
+
+private:
+ PDFiumPageImpl(const PDFiumPageImpl&) = delete;
+ PDFiumPageImpl& operator=(const PDFiumPageImpl&) = delete;
+
+public:
+ PDFiumPageImpl(FPDF_PAGE pPage)
+ : mpPage(pPage)
+ {
+ }
+
+ ~PDFiumPageImpl() override
+ {
+ if (mpPage)
+ FPDF_ClosePage(mpPage);
+ }
+
+ FPDF_PAGE getPointer() { return mpPage; }
+
+ int getObjectCount() override;
+ std::unique_ptr<PDFiumPageObject> getObject(int nIndex) override;
+
+ int getAnnotationCount() override;
+ int getAnnotationIndex(std::unique_ptr<PDFiumAnnotation> const& rAnnotation) override;
+
+ std::unique_ptr<PDFiumAnnotation> getAnnotation(int nIndex) override;
+
+ std::unique_ptr<PDFiumTextPage> getTextPage() override;
+
+ BitmapChecksum getChecksum(int nMDPPerm) override;
+
+ double getWidth() override;
+ double getHeight() override;
+
+ bool hasTransparency() override;
+
+ bool hasLinks() override;
+};
+
+/// Wrapper around FPDF_FORMHANDLE.
+class PDFiumFormHandle final
+{
+private:
+ FPDF_FORMHANDLE mpHandle;
+
+ PDFiumFormHandle(const PDFiumFormHandle&) = delete;
+ PDFiumFormHandle& operator=(const PDFiumFormHandle&) = delete;
+
+public:
+ PDFiumFormHandle(FPDF_FORMHANDLE pHandle);
+ ~PDFiumFormHandle();
+ FPDF_FORMHANDLE getPointer();
+};
+
+class PDFiumDocumentImpl : public PDFiumDocument
+{
+private:
+ FPDF_DOCUMENT mpPdfDocument;
+ FPDF_FORMFILLINFO m_aFormCallbacks;
+ std::unique_ptr<PDFiumFormHandle> m_pFormHandle;
+
+private:
+ PDFiumDocumentImpl(const PDFiumDocumentImpl&) = delete;
+ PDFiumDocumentImpl& operator=(const PDFiumDocumentImpl&) = delete;
+
+public:
+ PDFiumDocumentImpl(FPDF_DOCUMENT pPdfDocument);
+ ~PDFiumDocumentImpl() override;
+ FPDF_FORMHANDLE getFormHandlePointer();
+
+ // Page size in points
+ basegfx::B2DSize getPageSize(int nIndex) override;
+ int getPageCount() override;
+ int getSignatureCount() override;
+ int getFileVersion() override;
+ bool saveWithVersion(SvMemoryStream& rStream, int nFileVersion) override;
+
+ std::unique_ptr<PDFiumPage> openPage(int nIndex) override;
+ std::unique_ptr<PDFiumSignature> getSignature(int nIndex) override;
+ std::vector<unsigned int> getTrailerEnds() override;
+};
+
+class PDFiumImpl : public PDFium
+{
+private:
+ PDFiumImpl(const PDFiumImpl&) = delete;
+ PDFiumImpl& operator=(const PDFiumImpl&) = delete;
+
+ OUString maLastError;
+
+public:
+ PDFiumImpl();
+ ~PDFiumImpl() override;
+
+ const OUString& getLastError() const override { return maLastError; }
+
+ std::unique_ptr<PDFiumDocument> openDocument(const void* pData, int nSize,
+ const OString& rPassword) override;
+ PDFErrorType getLastErrorCode() override;
+ std::unique_ptr<PDFiumBitmap> createBitmap(int nWidth, int nHeight, int nAlpha) override;
+};
+}
+
+PDFiumImpl::PDFiumImpl()
+{
+ FPDF_LIBRARY_CONFIG aConfig;
+ aConfig.version = 2;
+ aConfig.m_pUserFontPaths = nullptr;
+ aConfig.m_pIsolate = nullptr;
+ aConfig.m_v8EmbedderSlot = 0;
+ FPDF_InitLibraryWithConfig(&aConfig);
+}
+
+PDFiumImpl::~PDFiumImpl() { FPDF_DestroyLibrary(); }
+
+std::unique_ptr<PDFiumDocument> PDFiumImpl::openDocument(const void* pData, int nSize,
+ const OString& rPassword)
+{
+ maLastError = OUString();
+ std::unique_ptr<PDFiumDocument> pPDFiumDocument;
+
+ FPDF_BYTESTRING pPassword = nullptr;
+ if (!rPassword.isEmpty())
+ {
+ pPassword = rPassword.getStr();
+ }
+ FPDF_DOCUMENT pDocument = FPDF_LoadMemDocument(pData, nSize, pPassword);
+
+ if (!pDocument)
+ {
+ switch (FPDF_GetLastError())
+ {
+ case FPDF_ERR_SUCCESS:
+ maLastError = "Success";
+ break;
+ case FPDF_ERR_UNKNOWN:
+ maLastError = "Unknown error";
+ break;
+ case FPDF_ERR_FILE:
+ maLastError = "File not found";
+ break;
+ case FPDF_ERR_FORMAT:
+ maLastError = "Input is not a PDF format";
+ break;
+ case FPDF_ERR_PASSWORD:
+ maLastError = "Incorrect password or password is required";
+ break;
+ case FPDF_ERR_SECURITY:
+ maLastError = "Security error";
+ break;
+ case FPDF_ERR_PAGE:
+ maLastError = "Content error";
+ break;
+ default:
+ break;
+ }
+ }
+ else
+ {
+ pPDFiumDocument = std::make_unique<PDFiumDocumentImpl>(pDocument);
+ }
+
+ return pPDFiumDocument;
+}
+
+PDFErrorType PDFiumImpl::getLastErrorCode()
+{
+ return static_cast<PDFErrorType>(FPDF_GetLastError());
+}
+
+std::unique_ptr<PDFiumBitmap> PDFiumImpl::createBitmap(int nWidth, int nHeight, int nAlpha)
+{
+ std::unique_ptr<PDFiumBitmap> pPDFiumBitmap;
+ FPDF_BITMAP pPdfBitmap = FPDFBitmap_Create(nWidth, nHeight, nAlpha);
+ if (!pPdfBitmap)
+ {
+ maLastError = "Failed to create bitmap";
+ }
+ else
+ {
+ pPDFiumBitmap = std::make_unique<PDFiumBitmapImpl>(pPdfBitmap);
+ }
+ return pPDFiumBitmap;
+}
+
+PDFiumSignatureImpl::PDFiumSignatureImpl(FPDF_SIGNATURE pSignature)
+ : mpSignature(pSignature)
+{
+}
+
+std::vector<int> PDFiumSignatureImpl::getByteRange()
+{
+ int nByteRangeLen = FPDFSignatureObj_GetByteRange(mpSignature, nullptr, 0);
+ std::vector<int> aByteRange(nByteRangeLen);
+ if (nByteRangeLen <= 0)
+ {
+ return aByteRange;
+ }
+
+ FPDFSignatureObj_GetByteRange(mpSignature, aByteRange.data(), aByteRange.size());
+ return aByteRange;
+}
+
+int PDFiumSignatureImpl::getDocMDPPermission()
+{
+ return FPDFSignatureObj_GetDocMDPPermission(mpSignature);
+}
+
+std::vector<unsigned char> PDFiumSignatureImpl::getContents()
+{
+ int nContentsLen = FPDFSignatureObj_GetContents(mpSignature, nullptr, 0);
+ std::vector<unsigned char> aContents(nContentsLen);
+ if (aContents.empty())
+ {
+ return aContents;
+ }
+
+ FPDFSignatureObj_GetContents(mpSignature, aContents.data(), aContents.size());
+ return aContents;
+}
+
+OString PDFiumSignatureImpl::getSubFilter()
+{
+ int nSubFilterLen = FPDFSignatureObj_GetSubFilter(mpSignature, nullptr, 0);
+ std::vector<char> aSubFilterBuf(nSubFilterLen);
+ FPDFSignatureObj_GetSubFilter(mpSignature, aSubFilterBuf.data(), aSubFilterBuf.size());
+ // Buffer is NUL-terminated.
+ OString aSubFilter(aSubFilterBuf.data(), aSubFilterBuf.size() - 1);
+ return aSubFilter;
+}
+
+OUString PDFiumSignatureImpl::getReason()
+{
+ int nReasonLen = FPDFSignatureObj_GetReason(mpSignature, nullptr, 0);
+ OUString aRet;
+ if (nReasonLen > 0)
+ {
+ std::vector<char16_t> aReasonBuf(nReasonLen);
+ FPDFSignatureObj_GetReason(mpSignature, aReasonBuf.data(), aReasonBuf.size());
+ aRet = OUString(aReasonBuf.data(), aReasonBuf.size() - 1);
+ }
+
+ return aRet;
+}
+
+util::DateTime PDFiumSignatureImpl::getTime()
+{
+ util::DateTime aRet;
+ int nTimeLen = FPDFSignatureObj_GetTime(mpSignature, nullptr, 0);
+ if (nTimeLen <= 0)
+ {
+ return aRet;
+ }
+
+ // Example: "D:20161027100104".
+ std::vector<char> aTimeBuf(nTimeLen);
+ FPDFSignatureObj_GetTime(mpSignature, aTimeBuf.data(), aTimeBuf.size());
+ OString aM(aTimeBuf.data(), aTimeBuf.size() - 1);
+ if (aM.startsWith("D:") && aM.getLength() >= 16)
+ {
+ aRet.Year = o3tl::toInt32(aM.subView(2, 4));
+ aRet.Month = o3tl::toInt32(aM.subView(6, 2));
+ aRet.Day = o3tl::toInt32(aM.subView(8, 2));
+ aRet.Hours = o3tl::toInt32(aM.subView(10, 2));
+ aRet.Minutes = o3tl::toInt32(aM.subView(12, 2));
+ aRet.Seconds = o3tl::toInt32(aM.subView(14, 2));
+ }
+ return aRet;
+}
+
+PDFiumDocumentImpl::PDFiumDocumentImpl(FPDF_DOCUMENT pPdfDocument)
+ : mpPdfDocument(pPdfDocument)
+ , m_aFormCallbacks()
+{
+ m_aFormCallbacks.version = 1;
+ m_pFormHandle = std::make_unique<PDFiumFormHandle>(
+ FPDFDOC_InitFormFillEnvironment(pPdfDocument, &m_aFormCallbacks));
+}
+
+PDFiumDocumentImpl::~PDFiumDocumentImpl()
+{
+ m_pFormHandle.reset();
+ if (mpPdfDocument)
+ FPDF_CloseDocument(mpPdfDocument);
+}
+
+FPDF_FORMHANDLE PDFiumDocumentImpl::getFormHandlePointer() { return m_pFormHandle->getPointer(); }
+
+std::unique_ptr<PDFiumPage> PDFiumDocumentImpl::openPage(int nIndex)
+{
+ std::unique_ptr<PDFiumPage> pPDFiumPage;
+ FPDF_PAGE pPage = FPDF_LoadPage(mpPdfDocument, nIndex);
+ if (pPage)
+ {
+ pPDFiumPage = std::make_unique<PDFiumPageImpl>(pPage);
+ }
+ return pPDFiumPage;
+}
+
+std::unique_ptr<PDFiumSignature> PDFiumDocumentImpl::getSignature(int nIndex)
+{
+ std::unique_ptr<PDFiumSignature> pPDFiumSignature;
+ FPDF_SIGNATURE pSignature = FPDF_GetSignatureObject(mpPdfDocument, nIndex);
+ if (pSignature)
+ {
+ pPDFiumSignature = std::make_unique<PDFiumSignatureImpl>(pSignature);
+ }
+ return pPDFiumSignature;
+}
+
+std::vector<unsigned int> PDFiumDocumentImpl::getTrailerEnds()
+{
+ int nNumTrailers = FPDF_GetTrailerEnds(mpPdfDocument, nullptr, 0);
+ std::vector<unsigned int> aTrailerEnds(nNumTrailers);
+ FPDF_GetTrailerEnds(mpPdfDocument, aTrailerEnds.data(), aTrailerEnds.size());
+ return aTrailerEnds;
+}
+
+basegfx::B2DSize PDFiumDocumentImpl::getPageSize(int nIndex)
+{
+ basegfx::B2DSize aSize;
+ FS_SIZEF aPDFSize;
+ if (FPDF_GetPageSizeByIndexF(mpPdfDocument, nIndex, &aPDFSize))
+ {
+ aSize = basegfx::B2DSize(aPDFSize.width, aPDFSize.height);
+ }
+ return aSize;
+}
+
+int PDFiumDocumentImpl::getPageCount() { return FPDF_GetPageCount(mpPdfDocument); }
+
+int PDFiumDocumentImpl::getSignatureCount() { return FPDF_GetSignatureCount(mpPdfDocument); }
+
+int PDFiumDocumentImpl::getFileVersion()
+{
+ int nFileVersion = 0;
+ FPDF_GetFileVersion(mpPdfDocument, &nFileVersion);
+ return nFileVersion;
+}
+
+bool PDFiumDocumentImpl::saveWithVersion(SvMemoryStream& rStream, int nFileVersion)
+{
+ CompatibleWriter aWriter(rStream);
+ aWriter.version = 1;
+ aWriter.WriteBlock = &CompatibleWriterCallback;
+ if (!FPDF_SaveWithVersion(mpPdfDocument, &aWriter, 0, nFileVersion))
+ {
+ return false;
+ }
+
+ return true;
+}
+
+int PDFiumPageImpl::getObjectCount() { return FPDFPage_CountObjects(mpPage); }
+
+std::unique_ptr<PDFiumPageObject> PDFiumPageImpl::getObject(int nIndex)
+{
+ std::unique_ptr<PDFiumPageObject> pPDFiumPageObject;
+ FPDF_PAGEOBJECT pPageObject = FPDFPage_GetObject(mpPage, nIndex);
+ if (pPageObject)
+ {
+ pPDFiumPageObject = std::make_unique<PDFiumPageObjectImpl>(pPageObject);
+ }
+ return pPDFiumPageObject;
+}
+
+int PDFiumPageImpl::getAnnotationCount() { return FPDFPage_GetAnnotCount(mpPage); }
+
+int PDFiumPageImpl::getAnnotationIndex(std::unique_ptr<PDFiumAnnotation> const& rAnnotation)
+{
+ auto pAnnotation = static_cast<PDFiumAnnotationImpl*>(rAnnotation.get());
+ return FPDFPage_GetAnnotIndex(mpPage, pAnnotation->getPointer());
+}
+
+std::unique_ptr<PDFiumAnnotation> PDFiumPageImpl::getAnnotation(int nIndex)
+{
+ std::unique_ptr<PDFiumAnnotation> pPDFiumAnnotation;
+ FPDF_ANNOTATION pAnnotation = FPDFPage_GetAnnot(mpPage, nIndex);
+ if (pAnnotation)
+ {
+ pPDFiumAnnotation = std::make_unique<PDFiumAnnotationImpl>(pAnnotation);
+ }
+ return pPDFiumAnnotation;
+}
+
+std::unique_ptr<PDFiumTextPage> PDFiumPageImpl::getTextPage()
+{
+ std::unique_ptr<PDFiumTextPage> pPDFiumTextPage;
+ FPDF_TEXTPAGE pTextPage = FPDFText_LoadPage(mpPage);
+ if (pTextPage)
+ {
+ pPDFiumTextPage = std::make_unique<PDFiumTextPageImpl>(pTextPage);
+ }
+ return pPDFiumTextPage;
+}
+
+bool PDFiumPageImpl::hasLinks()
+{
+ // This could be a full iterator, but at the moment we just determine if the list is empty or
+ // not.
+ int nStartPos = 0;
+ FPDF_LINK pLinkAnnot = nullptr;
+ return FPDFLink_Enumerate(mpPage, &nStartPos, &pLinkAnnot);
+}
+
+PDFiumPageObjectImpl::PDFiumPageObjectImpl(FPDF_PAGEOBJECT pPageObject)
+ : mpPageObject(pPageObject)
+{
+}
+
+OUString PDFiumPageObjectImpl::getText(std::unique_ptr<PDFiumTextPage> const& rTextPage)
+{
+ OUString sReturnText;
+
+ auto pTextPage = static_cast<PDFiumTextPageImpl*>(rTextPage.get());
+ int nBytes = FPDFTextObj_GetText(mpPageObject, pTextPage->getPointer(), nullptr, 0);
+ assert(nBytes % 2 == 0);
+ nBytes /= 2;
+
+ std::unique_ptr<sal_Unicode[]> pText(new sal_Unicode[nBytes]);
+
+ int nActualBytes = FPDFTextObj_GetText(mpPageObject, pTextPage->getPointer(),
+ reinterpret_cast<FPDF_WCHAR*>(pText.get()), nBytes * 2);
+ assert(nActualBytes % 2 == 0);
+ nActualBytes /= 2;
+ if (nActualBytes > 1)
+ {
+#if defined OSL_BIGENDIAN
+ // The data returned by FPDFTextObj_GetText is documented to always be UTF-16LE:
+ for (int i = 0; i != nActualBytes; ++i)
+ {
+ pText[i] = OSL_SWAPWORD(pText[i]);
+ }
+#endif
+ sReturnText = OUString(pText.get());
+ }
+
+ return sReturnText;
+}
+
+PDFPageObjectType PDFiumPageObjectImpl::getType()
+{
+ return static_cast<PDFPageObjectType>(FPDFPageObj_GetType(mpPageObject));
+}
+
+int PDFiumPageObjectImpl::getFormObjectCount() { return FPDFFormObj_CountObjects(mpPageObject); }
+
+std::unique_ptr<PDFiumPageObject> PDFiumPageObjectImpl::getFormObject(int nIndex)
+{
+ std::unique_ptr<PDFiumPageObject> pPDFiumFormObject;
+ FPDF_PAGEOBJECT pFormObject = FPDFFormObj_GetObject(mpPageObject, nIndex);
+ if (pFormObject)
+ {
+ pPDFiumFormObject = std::make_unique<PDFiumPageObjectImpl>(pFormObject);
+ }
+ return pPDFiumFormObject;
+}
+
+basegfx::B2DHomMatrix PDFiumPageObjectImpl::getMatrix()
+{
+ basegfx::B2DHomMatrix aB2DMatrix;
+ FS_MATRIX matrix;
+ if (FPDFPageObj_GetMatrix(mpPageObject, &matrix))
+ aB2DMatrix = basegfx::B2DHomMatrix::abcdef(matrix.a, matrix.b, matrix.c, matrix.d, matrix.e,
+ matrix.f);
+ return aB2DMatrix;
+}
+
+basegfx::B2DRectangle PDFiumPageObjectImpl::getBounds()
+{
+ basegfx::B2DRectangle aB2DRectangle;
+
+ float left = 0;
+ float bottom = 0;
+ float right = 0;
+ float top = 0;
+ if (FPDFPageObj_GetBounds(mpPageObject, &left, &bottom, &right, &top))
+ {
+ aB2DRectangle = basegfx::B2DRectangle(left, top, right, bottom);
+ }
+ return aB2DRectangle;
+}
+
+double PDFiumPageObjectImpl::getFontSize()
+{
+ float nSize{};
+ FPDFTextObj_GetFontSize(mpPageObject, &nSize);
+ return nSize;
+}
+
+OUString PDFiumPageObjectImpl::getFontName()
+{
+ OUString sFontName;
+ const int nFontName = 80 + 1;
+ std::unique_ptr<char[]> pFontName(new char[nFontName]); // + terminating null
+ FPDF_FONT pFontObject = FPDFTextObj_GetFont(mpPageObject);
+ int nFontNameChars = FPDFFont_GetFontName(pFontObject, pFontName.get(), nFontName);
+ if (nFontName >= nFontNameChars)
+ {
+ sFontName = OUString::createFromAscii(pFontName.get());
+ }
+ return sFontName;
+}
+
+PDFTextRenderMode PDFiumPageObjectImpl::getTextRenderMode()
+{
+ return static_cast<PDFTextRenderMode>(FPDFTextObj_GetTextRenderMode(mpPageObject));
+}
+
+Color PDFiumPageObjectImpl::getFillColor()
+{
+ Color aColor = COL_TRANSPARENT;
+ unsigned int nR, nG, nB, nA;
+ if (FPDFPageObj_GetFillColor(mpPageObject, &nR, &nG, &nB, &nA))
+ {
+ aColor = Color(ColorAlpha, nA, nR, nG, nB);
+ }
+ return aColor;
+}
+
+Color PDFiumPageObjectImpl::getStrokeColor()
+{
+ Color aColor = COL_TRANSPARENT;
+ unsigned int nR, nG, nB, nA;
+ if (FPDFPageObj_GetStrokeColor(mpPageObject, &nR, &nG, &nB, &nA))
+ {
+ aColor = Color(ColorAlpha, nA, nR, nG, nB);
+ }
+ return aColor;
+}
+
+double PDFiumPageObjectImpl::getStrokeWidth()
+{
+ float fWidth = 1;
+ FPDFPageObj_GetStrokeWidth(mpPageObject, &fWidth);
+ return fWidth;
+}
+
+int PDFiumPageObjectImpl::getPathSegmentCount() { return FPDFPath_CountSegments(mpPageObject); }
+
+std::unique_ptr<PDFiumPathSegment> PDFiumPageObjectImpl::getPathSegment(int index)
+{
+ std::unique_ptr<PDFiumPathSegment> pPDFiumPathSegment;
+ FPDF_PATHSEGMENT pPathSegment = FPDFPath_GetPathSegment(mpPageObject, index);
+ if (pPathSegment)
+ {
+ pPDFiumPathSegment = std::make_unique<PDFiumPathSegmentImpl>(pPathSegment);
+ }
+ return pPDFiumPathSegment;
+}
+
+Size PDFiumPageObjectImpl::getImageSize(PDFiumPage& rPage)
+{
+ FPDF_IMAGEOBJ_METADATA aMeta;
+ auto& rPageImpl = static_cast<PDFiumPageImpl&>(rPage);
+ FPDFImageObj_GetImageMetadata(mpPageObject, rPageImpl.getPointer(), &aMeta);
+ return Size(aMeta.width, aMeta.height);
+}
+
+std::unique_ptr<PDFiumBitmap> PDFiumPageObjectImpl::getImageBitmap()
+{
+ std::unique_ptr<PDFiumBitmap> pPDFiumBitmap;
+ FPDF_BITMAP pBitmap = FPDFImageObj_GetBitmap(mpPageObject);
+ if (pBitmap)
+ {
+ pPDFiumBitmap = std::make_unique<PDFiumBitmapImpl>(pBitmap);
+ }
+ return pPDFiumBitmap;
+}
+
+bool PDFiumPageObjectImpl::getDrawMode(PDFFillMode& rFillMode, bool& rStroke)
+{
+ auto nFillMode = static_cast<int>(rFillMode);
+ auto bStroke = static_cast<FPDF_BOOL>(rStroke);
+ bool bRet = FPDFPath_GetDrawMode(mpPageObject, &nFillMode, &bStroke);
+ rFillMode = static_cast<PDFFillMode>(nFillMode);
+ rStroke = static_cast<bool>(bStroke);
+ return bRet;
+}
+
+BitmapChecksum PDFiumPageImpl::getChecksum(int nMDPPerm)
+{
+ size_t nPageWidth = getWidth();
+ size_t nPageHeight = getHeight();
+ auto pPdfBitmap = std::make_unique<PDFiumBitmapImpl>(
+ 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->getPointer(), mpPage, /*start_x=*/0, /*start_y=*/0,
+ nPageWidth, nPageHeight,
+ /*rotate=*/0, nFlags);
+ Bitmap aBitmap(Size(nPageWidth, nPageHeight), vcl::PixelFormat::N24_BPP);
+ {
+ BitmapScopedWriteAccess pWriteAccess(aBitmap);
+ const auto pPdfBuffer
+ = static_cast<ConstScanline>(FPDFBitmap_GetBuffer(pPdfBitmap->getPointer()));
+ const int nStride = FPDFBitmap_GetStride(pPdfBitmap->getPointer());
+ for (size_t nRow = 0; nRow < nPageHeight; ++nRow)
+ {
+ ConstScanline pPdfLine = pPdfBuffer + (nStride * nRow);
+ pWriteAccess->CopyScanline(nRow, pPdfLine, ScanlineFormat::N32BitTcBgra, nStride);
+ }
+ }
+ return aBitmap.GetChecksum();
+}
+
+double PDFiumPageImpl::getWidth() { return FPDF_GetPageWidth(mpPage); }
+
+double PDFiumPageImpl::getHeight() { return FPDF_GetPageHeight(mpPage); }
+
+bool PDFiumPageImpl::hasTransparency() { return FPDFPage_HasTransparency(mpPage); }
+
+PDFiumPathSegmentImpl::PDFiumPathSegmentImpl(FPDF_PATHSEGMENT pPathSegment)
+ : mpPathSegment(pPathSegment)
+{
+}
+
+basegfx::B2DPoint PDFiumPathSegmentImpl::getPoint() const
+{
+ basegfx::B2DPoint aPoint;
+ float fx, fy;
+ if (FPDFPathSegment_GetPoint(mpPathSegment, &fx, &fy))
+ aPoint = basegfx::B2DPoint(fx, fy);
+ return aPoint;
+}
+
+bool PDFiumPathSegmentImpl::isClosed() const { return FPDFPathSegment_GetClose(mpPathSegment); }
+
+PDFSegmentType PDFiumPathSegmentImpl::getType() const
+{
+ return static_cast<PDFSegmentType>(FPDFPathSegment_GetType(mpPathSegment));
+}
+
+PDFiumFormHandle::PDFiumFormHandle(FPDF_FORMHANDLE pHandle)
+ : mpHandle(pHandle)
+{
+}
+
+PDFiumFormHandle::~PDFiumFormHandle() { FPDFDOC_ExitFormFillEnvironment(mpHandle); }
+
+FPDF_FORMHANDLE PDFiumFormHandle::getPointer() { return mpHandle; }
+
+PDFiumBitmapImpl::PDFiumBitmapImpl(FPDF_BITMAP pBitmap)
+ : mpBitmap(pBitmap)
+{
+}
+
+PDFiumBitmapImpl::~PDFiumBitmapImpl()
+{
+ if (mpBitmap)
+ {
+ FPDFBitmap_Destroy(mpBitmap);
+ }
+}
+
+void PDFiumBitmapImpl::fillRect(int left, int top, int width, int height, sal_uInt32 nColor)
+{
+ FPDFBitmap_FillRect(mpBitmap, left, top, width, height, nColor);
+}
+
+void PDFiumBitmapImpl::renderPageBitmap(PDFiumDocument* pDoc, PDFiumPage* pPage, int nStartX,
+ int nStartY, int nSizeX, int nSizeY)
+{
+ auto pPageImpl = static_cast<PDFiumPageImpl*>(pPage);
+ FPDF_RenderPageBitmap(mpBitmap, pPageImpl->getPointer(), nStartX, nStartY, nSizeX, nSizeY,
+ /*rotate=*/0, /*flags=*/0);
+
+ // Render widget annotations for FormFields.
+ auto pDocImpl = static_cast<PDFiumDocumentImpl*>(pDoc);
+ FPDF_FFLDraw(pDocImpl->getFormHandlePointer(), mpBitmap, pPageImpl->getPointer(), nStartX,
+ nStartY, nSizeX, nSizeY, /*rotate=*/0, /*flags=*/0);
+}
+
+ConstScanline PDFiumBitmapImpl::getBuffer()
+{
+ return static_cast<ConstScanline>(FPDFBitmap_GetBuffer(mpBitmap));
+}
+
+int PDFiumBitmapImpl::getStride() { return FPDFBitmap_GetStride(mpBitmap); }
+
+int PDFiumBitmapImpl::getWidth() { return FPDFBitmap_GetWidth(mpBitmap); }
+
+int PDFiumBitmapImpl::getHeight() { return FPDFBitmap_GetHeight(mpBitmap); }
+
+PDFBitmapType PDFiumBitmapImpl::getFormat()
+{
+ return static_cast<PDFBitmapType>(FPDFBitmap_GetFormat(mpBitmap));
+}
+
+PDFiumAnnotationImpl::PDFiumAnnotationImpl(FPDF_ANNOTATION pAnnotation)
+ : mpAnnotation(pAnnotation)
+{
+}
+
+PDFiumAnnotationImpl::~PDFiumAnnotationImpl()
+{
+ if (mpAnnotation)
+ FPDFPage_CloseAnnot(mpAnnotation);
+}
+
+PDFAnnotationSubType PDFiumAnnotationImpl::getSubType()
+{
+ return PDFAnnotationSubType(FPDFAnnot_GetSubtype(mpAnnotation));
+}
+
+basegfx::B2DRectangle PDFiumAnnotationImpl::getRectangle()
+{
+ basegfx::B2DRectangle aB2DRectangle;
+ FS_RECTF aRect;
+ if (FPDFAnnot_GetRect(mpAnnotation, &aRect))
+ {
+ aB2DRectangle = basegfx::B2DRectangle(aRect.left, aRect.top, aRect.right, aRect.bottom);
+ }
+ return aB2DRectangle;
+}
+
+Color PDFiumAnnotationImpl::getColor()
+{
+ Color aColor = COL_TRANSPARENT;
+ unsigned int nR, nG, nB, nA;
+ if (FPDFAnnot_GetColor(mpAnnotation, FPDFANNOT_COLORTYPE_Color, &nR, &nG, &nB, &nA))
+ {
+ aColor = Color(ColorAlpha, nA, nR, nG, nB);
+ }
+ return aColor;
+}
+
+Color PDFiumAnnotationImpl::getInteriorColor()
+{
+ Color aColor = COL_TRANSPARENT;
+ unsigned int nR, nG, nB, nA;
+ if (FPDFAnnot_GetColor(mpAnnotation, FPDFANNOT_COLORTYPE_InteriorColor, &nR, &nG, &nB, &nA))
+ {
+ aColor = Color(ColorAlpha, nA, nR, nG, nB);
+ }
+ return aColor;
+}
+
+size_t PDFiumAnnotationImpl::getAttachmentPointsCount()
+{
+ return FPDFAnnot_CountAttachmentPoints(mpAnnotation);
+}
+
+std::vector<basegfx::B2DPoint> PDFiumAnnotationImpl::getAttachmentPoints(size_t nIndex)
+{
+ std::vector<basegfx::B2DPoint> aQuads;
+
+ FS_QUADPOINTSF aQuadpoints;
+ if (FPDFAnnot_GetAttachmentPoints(mpAnnotation, nIndex, &aQuadpoints))
+ {
+ aQuads.emplace_back(aQuadpoints.x1, aQuadpoints.y1);
+ aQuads.emplace_back(aQuadpoints.x2, aQuadpoints.y2);
+ aQuads.emplace_back(aQuadpoints.x3, aQuadpoints.y3);
+ aQuads.emplace_back(aQuadpoints.x4, aQuadpoints.y4);
+ }
+ return aQuads;
+}
+
+std::vector<basegfx::B2DPoint> PDFiumAnnotationImpl::getLineGeometry()
+{
+ std::vector<basegfx::B2DPoint> aLine;
+ FS_POINTF aStart;
+ FS_POINTF aEnd;
+ if (FPDFAnnot_GetLine(mpAnnotation, &aStart, &aEnd))
+ {
+ aLine.emplace_back(aStart.x, aStart.y);
+ aLine.emplace_back(aEnd.x, aEnd.y);
+ }
+ return aLine;
+}
+
+namespace
+{
+bool getBorderProperties(FPDF_ANNOTATION mpAnnotation, float& rHorizontalCornerRadius,
+ float& rVerticalCornerRadius, float& rBorderWidth)
+{
+ float fHoriRadius = 0.0f;
+ float fVertRadius = 0.0f;
+ float fWidth = 0.0f;
+
+ if (!FPDFAnnot_GetBorder(mpAnnotation, &fHoriRadius, &fVertRadius, &fWidth))
+ return false;
+
+ rHorizontalCornerRadius = fHoriRadius;
+ rVerticalCornerRadius = fVertRadius;
+ rBorderWidth = fWidth;
+ return true;
+}
+}
+
+float PDFiumAnnotationImpl::getBorderWidth()
+{
+ float fHorizontalCornerRadius;
+ float fVerticalCornerRadius;
+ float fBorderWidth;
+
+ if (!getBorderProperties(mpAnnotation, fHorizontalCornerRadius, fVerticalCornerRadius,
+ fBorderWidth))
+ return 0.0f;
+ return fBorderWidth;
+}
+
+basegfx::B2DSize PDFiumAnnotationImpl::getBorderCornerRadius()
+{
+ float fHorizontalCornerRadius;
+ float fVerticalCornerRadius;
+ float fBorderWidth;
+
+ if (!getBorderProperties(mpAnnotation, fHorizontalCornerRadius, fVerticalCornerRadius,
+ fBorderWidth))
+ return basegfx::B2DSize(0.0, 0.0);
+ return basegfx::B2DSize(fHorizontalCornerRadius, fVerticalCornerRadius);
+}
+
+bool PDFiumAnnotationImpl::hasKey(OString const& rKey)
+{
+ return FPDFAnnot_HasKey(mpAnnotation, rKey.getStr());
+}
+
+PDFObjectType PDFiumAnnotationImpl::getValueType(OString const& rKey)
+{
+ return static_cast<PDFObjectType>(FPDFAnnot_GetValueType(mpAnnotation, rKey.getStr()));
+}
+
+OUString PDFiumAnnotationImpl::getString(OString const& rKey)
+{
+ OUString rString;
+ unsigned long nSize = FPDFAnnot_GetStringValue(mpAnnotation, rKey.getStr(), nullptr, 0);
+ assert(nSize % 2 == 0);
+ nSize /= 2;
+ if (nSize > 1)
+ {
+ std::unique_ptr<sal_Unicode[]> pText(new sal_Unicode[nSize]);
+ unsigned long nStringSize = FPDFAnnot_GetStringValue(
+ mpAnnotation, rKey.getStr(), reinterpret_cast<FPDF_WCHAR*>(pText.get()), nSize * 2);
+ assert(nStringSize % 2 == 0);
+ nStringSize /= 2;
+ if (nStringSize > 0)
+ {
+#if defined OSL_BIGENDIAN
+ // The data returned by FPDFAnnot_GetStringValue is documented to always be UTF-16LE:
+ for (unsigned long i = 0; i != nStringSize; ++i)
+ {
+ pText[i] = OSL_SWAPWORD(pText[i]);
+ }
+#endif
+ rString = OUString(pText.get());
+ }
+ }
+ return rString;
+}
+
+std::vector<std::vector<basegfx::B2DPoint>> PDFiumAnnotationImpl::getInkStrokes()
+{
+ std::vector<std::vector<basegfx::B2DPoint>> aB2DPointList;
+ int nInkStrokes = FPDFAnnot_GetInkListCount(mpAnnotation);
+ for (int i = 0; i < nInkStrokes; i++)
+ {
+ std::vector<basegfx::B2DPoint> aB2DPoints;
+ int nPoints = FPDFAnnot_GetInkListPath(mpAnnotation, i, nullptr, 0);
+ if (nPoints)
+ {
+ std::vector<FS_POINTF> aPoints(nPoints);
+ if (FPDFAnnot_GetInkListPath(mpAnnotation, i, aPoints.data(), aPoints.size()))
+ {
+ for (auto const& rPoint : aPoints)
+ {
+ aB2DPoints.emplace_back(rPoint.x, rPoint.y);
+ }
+ aB2DPointList.push_back(aB2DPoints);
+ }
+ }
+ }
+ return aB2DPointList;
+}
+
+std::vector<basegfx::B2DPoint> PDFiumAnnotationImpl::getVertices()
+{
+ std::vector<basegfx::B2DPoint> aB2DPoints;
+ int nPoints = FPDFAnnot_GetVertices(mpAnnotation, nullptr, 0);
+ if (nPoints)
+ {
+ std::vector<FS_POINTF> aPoints(nPoints);
+ if (FPDFAnnot_GetVertices(mpAnnotation, aPoints.data(), aPoints.size()))
+ {
+ for (auto const& rPoint : aPoints)
+ aB2DPoints.emplace_back(rPoint.x, rPoint.y);
+ }
+ }
+ return aB2DPoints;
+}
+
+std::unique_ptr<PDFiumAnnotation> PDFiumAnnotationImpl::getLinked(OString const& rKey)
+{
+ std::unique_ptr<PDFiumAnnotation> pPDFiumAnnotation;
+ FPDF_ANNOTATION pAnnotation = FPDFAnnot_GetLinkedAnnot(mpAnnotation, rKey.getStr());
+ if (pAnnotation)
+ {
+ pPDFiumAnnotation = std::make_unique<PDFiumAnnotationImpl>(pAnnotation);
+ }
+ return pPDFiumAnnotation;
+}
+
+int PDFiumAnnotationImpl::getObjectCount() { return FPDFAnnot_GetObjectCount(mpAnnotation); }
+
+std::unique_ptr<PDFiumPageObject> PDFiumAnnotationImpl::getObject(int nIndex)
+{
+ std::unique_ptr<PDFiumPageObject> pPDFiumPageObject;
+ FPDF_PAGEOBJECT pPageObject = FPDFAnnot_GetObject(mpAnnotation, nIndex);
+ if (pPageObject)
+ {
+ pPDFiumPageObject = std::make_unique<PDFiumPageObjectImpl>(pPageObject);
+ }
+ return pPDFiumPageObject;
+}
+
+PDFiumTextPageImpl::PDFiumTextPageImpl(FPDF_TEXTPAGE pTextPage)
+ : mpTextPage(pTextPage)
+{
+}
+
+PDFiumTextPageImpl::~PDFiumTextPageImpl()
+{
+ if (mpTextPage)
+ FPDFText_ClosePage(mpTextPage);
+}
+
+int PDFiumTextPageImpl::countChars() { return FPDFText_CountChars(mpTextPage); }
+
+basegfx::B2DRectangle PDFiumTextPageImpl::getCharBox(int nIndex, double fPageHeight)
+{
+ double left = 0.0;
+ double right = 0.0;
+ double bottom = 0.0;
+ double top = 0.0;
+
+ if (FPDFText_GetCharBox(mpTextPage, nIndex, &left, &right, &bottom, &top))
+ {
+ left = convertPointToMm100(left);
+ right = convertPointToMm100(right);
+ top = fPageHeight - convertPointToMm100(top);
+ bottom = fPageHeight - convertPointToMm100(bottom);
+
+ return basegfx::B2DRectangle(left, bottom, right, top);
+ }
+
+ return basegfx::B2DRectangle();
+}
+
+unsigned int PDFiumTextPageImpl::getUnicode(int index)
+{
+ return FPDFText_GetUnicode(mpTextPage, index);
+}
+
+std::unique_ptr<PDFiumSearchHandle>
+PDFiumTextPageImpl::findStart(const OUString& rFindWhat, PDFFindFlags nFlags, sal_Int32 nStartIndex)
+{
+ FPDF_WIDESTRING pFindWhat = reinterpret_cast<FPDF_WIDESTRING>(rFindWhat.getStr());
+ return std::make_unique<vcl::pdf::PDFiumSearchHandleImpl>(
+ FPDFText_FindStart(mpTextPage, pFindWhat, static_cast<sal_uInt32>(nFlags), nStartIndex));
+}
+
+PDFiumSearchHandleImpl::PDFiumSearchHandleImpl(FPDF_SCHHANDLE pSearchHandle)
+ : mpSearchHandle(pSearchHandle)
+{
+}
+
+PDFiumSearchHandleImpl::~PDFiumSearchHandleImpl()
+{
+ if (mpSearchHandle)
+ FPDFText_FindClose(mpSearchHandle);
+}
+
+bool PDFiumSearchHandleImpl::findNext() { return FPDFText_FindNext(mpSearchHandle); }
+
+bool PDFiumSearchHandleImpl::findPrev() { return FPDFText_FindPrev(mpSearchHandle); }
+
+int PDFiumSearchHandleImpl::getSearchResultIndex()
+{
+ return FPDFText_GetSchResultIndex(mpSearchHandle);
+}
+
+int PDFiumSearchHandleImpl::getSearchCount() { return FPDFText_GetSchCount(mpSearchHandle); }
+
+std::shared_ptr<PDFium>& PDFiumLibrary::get()
+{
+ static std::shared_ptr<PDFium> pInstance = std::make_shared<PDFiumImpl>();
+ return pInstance;
+}
+
+} // end vcl::pdf
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/pdf/PDFiumTools.cxx b/vcl/source/pdf/PDFiumTools.cxx
new file mode 100644
index 000000000..28fa53b83
--- /dev/null
+++ b/vcl/source/pdf/PDFiumTools.cxx
@@ -0,0 +1,73 @@
+/* -*- 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 <vcl/filter/PDFiumLibrary.hxx>
+
+namespace vcl::pdf
+{
+OUString convertPdfDateToISO8601(OUString const& rInput)
+{
+ if (rInput.getLength() < 6)
+ return {};
+
+ std::u16string_view prefix = rInput.subView(0, 2);
+ if (prefix != u"D:")
+ return {};
+
+ std::u16string_view sYear = rInput.subView(2, 4);
+
+ std::u16string_view sMonth(u"01");
+ if (rInput.getLength() >= 8)
+ sMonth = rInput.subView(6, 2);
+
+ std::u16string_view sDay(u"01");
+ if (rInput.getLength() >= 10)
+ sDay = rInput.subView(8, 2);
+
+ std::u16string_view sHours(u"00");
+ if (rInput.getLength() >= 12)
+ sHours = rInput.subView(10, 2);
+
+ std::u16string_view sMinutes(u"00");
+ if (rInput.getLength() >= 14)
+ sMinutes = rInput.subView(12, 2);
+
+ std::u16string_view sSeconds(u"00");
+ if (rInput.getLength() >= 16)
+ sSeconds = rInput.subView(14, 2);
+
+ OUString sTimeZoneMark("Z");
+ if (rInput.getLength() >= 17)
+ sTimeZoneMark = rInput.subView(16, 1);
+
+ std::u16string_view sTimeZoneHours(u"00");
+ std::u16string_view sTimeZoneMinutes(u"00");
+ if ((sTimeZoneMark == "+" || sTimeZoneMark == "-") && rInput.getLength() >= 22)
+ {
+ std::u16string_view sTimeZoneSeparator = rInput.subView(19, 1);
+ if (sTimeZoneSeparator == u"'")
+ {
+ sTimeZoneHours = rInput.subView(17, 2);
+ sTimeZoneMinutes = rInput.subView(20, 2);
+ }
+ }
+
+ OUString sTimeZoneString;
+ if (sTimeZoneMark == "+" || sTimeZoneString == "-")
+ sTimeZoneString = sTimeZoneMark + sTimeZoneHours + ":" + sTimeZoneMinutes;
+ else if (sTimeZoneMark == "Z")
+ sTimeZoneString = sTimeZoneMark;
+
+ return OUString::Concat(sYear) + "-" + sMonth + "-" + sDay + "T" + sHours + ":" + sMinutes + ":"
+ + sSeconds + sTimeZoneString;
+}
+} // end vcl::pdf
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/pdf/PdfConfig.cxx b/vcl/source/pdf/PdfConfig.cxx
new file mode 100644
index 000000000..0e6a04360
--- /dev/null
+++ b/vcl/source/pdf/PdfConfig.cxx
@@ -0,0 +1,36 @@
+/* -*- 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/PdfConfig.hxx>
+#include <cstdlib>
+
+#include <vcl/svapp.hxx>
+#include <vcl/outdev.hxx>
+
+namespace vcl::pdf
+{
+/// Get the default PDF rendering resolution in DPI.
+double getDefaultPdfResolutionDpi()
+{
+ // If an overriding default is set, use it.
+ const char* envar = ::getenv("PDFIMPORT_RESOLUTION_DPI");
+ if (envar)
+ {
+ const double dpi = atof(envar);
+ if (dpi > 0)
+ return dpi;
+ }
+
+ // Fallback to a sensible default.
+ return Application::GetDefaultDevice()->GetDPIX();
+}
+}
+
+/* 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..156e849f4
--- /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(
+ OStringLiteral(u8"<?xpacket begin=\"\uFEFF\" 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();
+ }
+
+ // XMP Basic schema
+ 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: */