diff options
Diffstat (limited to 'vcl/source/pdf')
-rw-r--r-- | vcl/source/pdf/DummyPDFiumLibrary.cxx | 23 | ||||
-rw-r--r-- | vcl/source/pdf/ExternalPDFStreams.cxx | 42 | ||||
-rw-r--r-- | vcl/source/pdf/Matrix3.cxx | 122 | ||||
-rw-r--r-- | vcl/source/pdf/PDFiumLibrary.cxx | 1531 | ||||
-rw-r--r-- | vcl/source/pdf/PDFiumTools.cxx | 73 | ||||
-rw-r--r-- | vcl/source/pdf/PdfConfig.cxx | 36 | ||||
-rw-r--r-- | vcl/source/pdf/ResourceDict.cxx | 60 | ||||
-rw-r--r-- | vcl/source/pdf/XmpMetadata.cxx | 367 |
8 files changed, 2254 insertions, 0 deletions
diff --git a/vcl/source/pdf/DummyPDFiumLibrary.cxx b/vcl/source/pdf/DummyPDFiumLibrary.cxx new file mode 100644 index 0000000000..7c42084ebe --- /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 0000000000..e3716e1e0a --- /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 0000000000..0f7aead024 --- /dev/null +++ b/vcl/source/pdf/Matrix3.cxx @@ -0,0 +1,122 @@ +/* -*- 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]); +} + +basegfx::B2DPoint Matrix3::transform(const basegfx::B2DPoint& rOrig) const +{ + double x = rOrig.getX(), y = rOrig.getY(); + return basegfx::B2DPoint(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 0000000000..553be738c8 --- /dev/null +++ b/vcl/source/pdf/PDFiumLibrary.cxx @@ -0,0 +1,1531 @@ +/* -*- 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 <sal/log.hxx> +#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 <vcl/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"); + +static_assert(static_cast<int>(vcl::pdf::PDFFormFieldType::Unknown) == FPDF_FORMFIELD_UNKNOWN, + "PDFFormFieldType::Unknown value mismatch"); +static_assert(static_cast<int>(vcl::pdf::PDFFormFieldType::PushButton) == FPDF_FORMFIELD_PUSHBUTTON, + "PDFFormFieldType::PushButton value mismatch"); +static_assert(static_cast<int>(vcl::pdf::PDFFormFieldType::CheckBox) == FPDF_FORMFIELD_CHECKBOX, + "PDFFormFieldType::CheckBox value mismatch"); +static_assert(static_cast<int>(vcl::pdf::PDFFormFieldType::RadioButton) + == FPDF_FORMFIELD_RADIOBUTTON, + "PDFFormFieldType::RadioButton value mismatch"); +static_assert(static_cast<int>(vcl::pdf::PDFFormFieldType::ComboBox) == FPDF_FORMFIELD_COMBOBOX, + "PDFFormFieldType::ComboBox value mismatch"); +static_assert(static_cast<int>(vcl::pdf::PDFFormFieldType::ListBox) == FPDF_FORMFIELD_LISTBOX, + "PDFFormFieldType::ListBox value mismatch"); +static_assert(static_cast<int>(vcl::pdf::PDFFormFieldType::TextField) == FPDF_FORMFIELD_TEXTFIELD, + "PDFFormFieldType::TextField value mismatch"); +static_assert(static_cast<int>(vcl::pdf::PDFFormFieldType::Signature) == FPDF_FORMFIELD_SIGNATURE, + "PDFFormFieldType::Signature value mismatch"); + +static_assert(static_cast<int>(vcl::pdf::PDFAnnotAActionType::KeyStroke) + == FPDF_ANNOT_AACTION_KEY_STROKE, + "PDFAnnotAActionType::KeyStroke) value mismatch"); +static_assert(static_cast<int>(vcl::pdf::PDFAnnotAActionType::Format) == FPDF_ANNOT_AACTION_FORMAT, + "PDFAnnotAActionType::Format) value mismatch"); +static_assert(static_cast<int>(vcl::pdf::PDFAnnotAActionType::Validate) + == FPDF_ANNOT_AACTION_VALIDATE, + "PDFAnnotAActionType::Validate) value mismatch"); +static_assert(static_cast<int>(vcl::pdf::PDFAnnotAActionType::Calculate) + == FPDF_ANNOT_AACTION_CALCULATE, + "PDFAnnotAActionType::Calculate) 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; + PDFFormFieldType getFormFieldType(PDFiumDocument* pDoc) override; + float getFontSize(PDFiumDocument* pDoc) override; + OUString getFormFieldAlternateName(PDFiumDocument* pDoc) override; + int getFormFieldFlags(PDFiumDocument* pDoc) override; + OUString getFormAdditionalActionJavaScript(PDFiumDocument* pDoc, + PDFAnnotAActionType eEvent) override; + OUString getFormFieldValue(PDFiumDocument* pDoc) 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; + + void onAfterLoadPage(PDFiumDocument* pDoc) 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; + /// @brief creates bitmap, can reduce size if needed, check nWidth and nHeight + 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) + { + int nOriginal = nHeight; + // PDFium cannot create big bitmaps, max 2^14 x 2^14 x 4 bytes per pixel + if (nHeight > 16384) + nHeight = 16384; + + if (nWidth > 16384) + { + nWidth = 16384.0 / nOriginal * nWidth; + } + + if (nWidth * nHeight > 16384 * 16384) + { + nOriginal = nWidth; + nHeight = 16384.0 / nOriginal * nHeight; + } + + pPdfBitmap = FPDFBitmap_Create(nWidth, nHeight, nAlpha); + } + + if (!pPdfBitmap) + { + maLastError = "Failed to create bitmap"; + SAL_WARN("vcl.filter", "PDFiumImpl: " << getLastError()); + } + 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); +} + +void PDFiumPageImpl::onAfterLoadPage(PDFiumDocument* pDoc) +{ + auto pDocImpl = static_cast<PDFiumDocumentImpl*>(pDoc); + FORM_OnAfterLoadPage(mpPage, pDocImpl->getFormHandlePointer()); +} + +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) +{ + int nPageWidth = getWidth(); + int nPageHeight = getHeight(); + std::unique_ptr<PDFiumBitmap> pPdfBitmap + = PDFiumLibrary::get()->createBitmap(nPageWidth, nPageHeight, /*nAlpha=*/1); + if (!pPdfBitmap) + return 0; + + PDFiumBitmapImpl* pBitmapImpl = static_cast<PDFiumBitmapImpl*>(pPdfBitmap.get()); + + int nFlags = 0; + if (nMDPPerm != 3) + { + // Annotations/commenting should affect the checksum, signature verification wants this. + nFlags = FPDF_ANNOT; + } + FPDF_RenderPageBitmap(pBitmapImpl->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(pBitmapImpl->getPointer())); + const int nStride = FPDFBitmap_GetStride(pBitmapImpl->getPointer()); + for (int 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() +{ + unsigned int nR, nG, nB, nA; + if (FPDFAnnot_GetColor(mpAnnotation, FPDFANNOT_COLORTYPE_Color, &nR, &nG, &nB, &nA)) + { + return Color(ColorAlpha, nA, nR, nG, nB); + } + // FPDFAnnot_GetColor can return false if there is an appearance stream + // So we search for a color with getStrokeColor + for (int i = 0; i < getObjectCount(); ++i) + { + if (getObject(i)->getType() == PDFPageObjectType::Path) + return getObject(i)->getStrokeColor(); + } + return COL_TRANSPARENT; +} + +Color PDFiumAnnotationImpl::getInteriorColor() +{ + unsigned int nR, nG, nB, nA; + if (FPDFAnnot_GetColor(mpAnnotation, FPDFANNOT_COLORTYPE_InteriorColor, &nR, &nG, &nB, &nA)) + { + return Color(ColorAlpha, nA, nR, nG, nB); + } + // FPDFAnnot_GetColor can return false if there is an appearance stream + // So we search for a color with getFillColor + for (int i = 0; i < getObjectCount(); ++i) + { + if (getObject(i)->getType() == PDFPageObjectType::Path) + return getObject(i)->getFillColor(); + } + return COL_TRANSPARENT; +} + +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; +} + +PDFFormFieldType PDFiumAnnotationImpl::getFormFieldType(PDFiumDocument* pDoc) +{ + auto pDocImpl = static_cast<PDFiumDocumentImpl*>(pDoc); + return PDFFormFieldType( + FPDFAnnot_GetFormFieldType(pDocImpl->getFormHandlePointer(), mpAnnotation)); +} + +int PDFiumAnnotationImpl::getFormFieldFlags(PDFiumDocument* pDoc) +{ + auto pDocImpl = static_cast<PDFiumDocumentImpl*>(pDoc); + return FPDFAnnot_GetFormFieldFlags(pDocImpl->getFormHandlePointer(), mpAnnotation); +} + +float PDFiumAnnotationImpl::getFontSize(PDFiumDocument* pDoc) +{ + auto pDocImpl = static_cast<PDFiumDocumentImpl*>(pDoc); + float fRet{}; + if (!FPDFAnnot_GetFontSize(pDocImpl->getFormHandlePointer(), mpAnnotation, &fRet)) + { + return 0.0f; + } + + return fRet; +} + +OUString PDFiumAnnotationImpl::getFormFieldAlternateName(PDFiumDocument* pDoc) +{ + auto pDocImpl = static_cast<PDFiumDocumentImpl*>(pDoc); + OUString aString; + unsigned long nSize = FPDFAnnot_GetFormFieldAlternateName(pDocImpl->getFormHandlePointer(), + mpAnnotation, 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_GetFormFieldAlternateName( + pDocImpl->getFormHandlePointer(), mpAnnotation, + reinterpret_cast<FPDF_WCHAR*>(pText.get()), nSize * 2); + assert(nStringSize % 2 == 0); + nStringSize /= 2; + if (nStringSize > 0) + { +#if defined OSL_BIGENDIAN + for (unsigned long i = 0; i != nStringSize; ++i) + { + pText[i] = OSL_SWAPWORD(pText[i]); + } +#endif + aString = OUString(pText.get()); + } + } + return aString; +} + +OUString PDFiumAnnotationImpl::getFormFieldValue(PDFiumDocument* pDoc) +{ + auto pDocImpl = static_cast<PDFiumDocumentImpl*>(pDoc); + OUString aString; + unsigned long nSize + = FPDFAnnot_GetFormFieldValue(pDocImpl->getFormHandlePointer(), mpAnnotation, 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_GetFormFieldValue(pDocImpl->getFormHandlePointer(), mpAnnotation, + reinterpret_cast<FPDF_WCHAR*>(pText.get()), nSize * 2); + assert(nStringSize % 2 == 0); + nStringSize /= 2; + if (nStringSize > 0) + { +#if defined OSL_BIGENDIAN + for (unsigned long i = 0; i != nStringSize; ++i) + { + pText[i] = OSL_SWAPWORD(pText[i]); + } +#endif + aString = OUString(pText.get()); + } + } + return aString; +} + +OUString PDFiumAnnotationImpl::getFormAdditionalActionJavaScript(PDFiumDocument* pDoc, + PDFAnnotAActionType eEvent) +{ + auto pDocImpl = static_cast<PDFiumDocumentImpl*>(pDoc); + OUString aString; + unsigned long nSize = FPDFAnnot_GetFormAdditionalActionJavaScript( + pDocImpl->getFormHandlePointer(), mpAnnotation, static_cast<int>(eEvent), 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_GetFormAdditionalActionJavaScript( + pDocImpl->getFormHandlePointer(), mpAnnotation, static_cast<int>(eEvent), + reinterpret_cast<FPDF_WCHAR*>(pText.get()), nSize * 2); + assert(nStringSize % 2 == 0); + nStringSize /= 2; + if (nStringSize > 0) + { +#if defined OSL_BIGENDIAN + for (unsigned long i = 0; i != nStringSize; ++i) + { + pText[i] = OSL_SWAPWORD(pText[i]); + } +#endif + aString = OUString(pText.get()); + } + } + return aString; +} + +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 0000000000..cb392a8cb8 --- /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(std::u16string_view rInput) +{ + if (rInput.size() < 6) + return {}; + + std::u16string_view prefix = rInput.substr(0, 2); + if (prefix != u"D:") + return {}; + + std::u16string_view sYear = rInput.substr(2, 4); + + std::u16string_view sMonth(u"01"); + if (rInput.size() >= 8) + sMonth = rInput.substr(6, 2); + + std::u16string_view sDay(u"01"); + if (rInput.size() >= 10) + sDay = rInput.substr(8, 2); + + std::u16string_view sHours(u"00"); + if (rInput.size() >= 12) + sHours = rInput.substr(10, 2); + + std::u16string_view sMinutes(u"00"); + if (rInput.size() >= 14) + sMinutes = rInput.substr(12, 2); + + std::u16string_view sSeconds(u"00"); + if (rInput.size() >= 16) + sSeconds = rInput.substr(14, 2); + + OUString sTimeZoneMark("Z"); + if (rInput.size() >= 17) + sTimeZoneMark = rInput.substr(16, 1); + + std::u16string_view sTimeZoneHours(u"00"); + std::u16string_view sTimeZoneMinutes(u"00"); + if ((sTimeZoneMark == "+" || sTimeZoneMark == "-") && rInput.size() >= 22) + { + std::u16string_view sTimeZoneSeparator = rInput.substr(19, 1); + if (sTimeZoneSeparator == u"'") + { + sTimeZoneHours = rInput.substr(17, 2); + sTimeZoneMinutes = rInput.substr(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 0000000000..0e6a043603 --- /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 0000000000..f4647cb383 --- /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("<<\n"); + if (nFontDictObject) + rBuf.append("/Font " + OString::number(nFontDictObject) + " 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 0000000000..53bf3902ab --- /dev/null +++ b/vcl/source/pdf/XmpMetadata.cxx @@ -0,0 +1,367 @@ +/* -*- 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"_ostr, "xmpmeta"_ostr, "adobe:ns:meta/"_ostr); + aXmlWriter.startElement("rdf"_ostr, "RDF"_ostr, + "http://www.w3.org/1999/02/22-rdf-syntax-ns#"_ostr); + + // PDF/A part ( ISO 19005-1:2005 - 6.7.11 ) + if (mnPDF_A > 0) + { + OString sPdfVersion = OString::number(mnPDF_A); + + aXmlWriter.startElement("rdf:Description"); + aXmlWriter.attribute("rdf:about", ""_ostr); + aXmlWriter.attribute("xmlns:pdfaid", "http://www.aiim.org/pdfa/ns/id/"_ostr); + + aXmlWriter.startElement("pdfaid:part"); + aXmlWriter.content(sPdfVersion); + aXmlWriter.endElement(); + + aXmlWriter.startElement("pdfaid:conformance"); + aXmlWriter.content("B"_ostr); + aXmlWriter.endElement(); + + aXmlWriter.endElement(); + } + + // Dublin Core properties + if (!msTitle.isEmpty() || !msAuthor.isEmpty() || !msSubject.isEmpty() + || !maContributor.empty() || !msCoverage.isEmpty() || !msIdentifier.isEmpty() + || !maPublisher.empty() || !maRelation.empty() || !msRights.isEmpty() + || !msSource.isEmpty() || !msType.isEmpty()) + { + aXmlWriter.startElement("rdf:Description"); + aXmlWriter.attribute("rdf:about", ""_ostr); + aXmlWriter.attribute("xmlns:dc", "http://purl.org/dc/elements/1.1/"_ostr); + + aXmlWriter.startElement("dc:format"); + aXmlWriter.content("application/pdf"_ostr); + aXmlWriter.endElement(); + + 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", "x-default"_ostr); + 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", "x-default"_ostr); + aXmlWriter.content(msSubject); + aXmlWriter.endElement(); + aXmlWriter.endElement(); + aXmlWriter.endElement(); + } + if (!maContributor.empty()) + { + aXmlWriter.startElement("dc:contributor"); + aXmlWriter.startElement("rdf:Bag"); + for (const OString& rContributor : maContributor) + { + aXmlWriter.startElement("rdf:li"); + aXmlWriter.content(rContributor); + aXmlWriter.endElement(); + } + aXmlWriter.endElement(); + aXmlWriter.endElement(); + } + if (!msCoverage.isEmpty()) + { + aXmlWriter.startElement("dc:coverage"); + aXmlWriter.content(msCoverage); + aXmlWriter.endElement(); + } + if (!msIdentifier.isEmpty()) + { + aXmlWriter.startElement("dc:identifier"); + aXmlWriter.content(msIdentifier); + aXmlWriter.endElement(); + } + if (!maPublisher.empty()) + { + aXmlWriter.startElement("dc:publisher"); + aXmlWriter.startElement("rdf:Bag"); + for (const OString& rPublisher : maPublisher) + { + aXmlWriter.startElement("rdf:li"); + aXmlWriter.content(rPublisher); + aXmlWriter.endElement(); + } + aXmlWriter.endElement(); + aXmlWriter.endElement(); + } + if (!maRelation.empty()) + { + aXmlWriter.startElement("dc:relation"); + aXmlWriter.startElement("rdf:Bag"); + for (const OString& rRelation : maRelation) + { + aXmlWriter.startElement("rdf:li"); + aXmlWriter.content(rRelation); + aXmlWriter.endElement(); + } + aXmlWriter.endElement(); + aXmlWriter.endElement(); + } + if (!msRights.isEmpty()) + { + aXmlWriter.startElement("dc:rights"); + aXmlWriter.startElement("rdf:Alt"); + aXmlWriter.startElement("rdf:li"); + aXmlWriter.attribute("xml:lang", "x-default"_ostr); + aXmlWriter.content(msRights); + aXmlWriter.endElement(); + aXmlWriter.endElement(); + aXmlWriter.endElement(); + } + if (!msSource.isEmpty()) + { + aXmlWriter.startElement("dc:source"); + aXmlWriter.content(msSource); + aXmlWriter.endElement(); + } + if (!msType.isEmpty()) + { + aXmlWriter.startElement("dc:type"); + aXmlWriter.content(msType); + aXmlWriter.endElement(); + } + aXmlWriter.endElement(); + } + + // PDF/UA + if (mbPDF_UA) + { + if (mnPDF_A != 0) + { // tdf#157517 PDF/A extension schema is required + aXmlWriter.startElement("rdf:Description"); + aXmlWriter.attribute("rdf:about", ""_ostr); + aXmlWriter.attribute("xmlns:pdfaExtension", + "http://www.aiim.org/pdfa/ns/extension/"_ostr); + aXmlWriter.attribute("xmlns:pdfaSchema", + "http://www.aiim.org/pdfa/ns/schema#"_ostr); + aXmlWriter.attribute("xmlns:pdfaProperty", + "http://www.aiim.org/pdfa/ns/property#"_ostr); + aXmlWriter.startElement("pdfaExtension:schemas"); + aXmlWriter.startElement("rdf:Bag"); + aXmlWriter.startElement("rdf:li"); + aXmlWriter.attribute("rdf:parseType", "Resource"_ostr); + aXmlWriter.startElement("pdfaSchema:namespaceURI"); + aXmlWriter.content("http://www.aiim.org/pdfua/ns/id/"_ostr); + aXmlWriter.endElement(); + aXmlWriter.startElement("pdfaSchema:prefix"); + aXmlWriter.content("pdfuaid"_ostr); + aXmlWriter.endElement(); + aXmlWriter.startElement("pdfaSchema:schema"); + aXmlWriter.content("PDF/UA identification schema"_ostr); + aXmlWriter.endElement(); + aXmlWriter.startElement("pdfaSchema:property"); + aXmlWriter.startElement("rdf:Seq"); + + aXmlWriter.startElement("rdf:li"); + aXmlWriter.attribute("rdf:parseType", "Resource"_ostr); + aXmlWriter.startElement("pdfaProperty:category"); + aXmlWriter.content("internal"_ostr); + aXmlWriter.endElement(); + aXmlWriter.startElement("pdfaProperty:description"); + aXmlWriter.content("PDF/UA version identifier"_ostr); + aXmlWriter.endElement(); + aXmlWriter.startElement("pdfaProperty:name"); + aXmlWriter.content("part"_ostr); + aXmlWriter.endElement(); + aXmlWriter.startElement("pdfaProperty:valueType"); + aXmlWriter.content("Integer"_ostr); + aXmlWriter.endElement(); + aXmlWriter.endElement(); // rdf:li + + aXmlWriter.startElement("rdf:li"); + aXmlWriter.attribute("rdf:parseType", "Resource"_ostr); + aXmlWriter.startElement("pdfaProperty:category"); + aXmlWriter.content("internal"_ostr); + aXmlWriter.endElement(); + aXmlWriter.startElement("pdfaProperty:description"); + aXmlWriter.content("PDF/UA amendment identifier"_ostr); + aXmlWriter.endElement(); + aXmlWriter.startElement("pdfaProperty:name"); + aXmlWriter.content("amd"_ostr); + aXmlWriter.endElement(); + aXmlWriter.startElement("pdfaProperty:valueType"); + aXmlWriter.content("Text"_ostr); + aXmlWriter.endElement(); + aXmlWriter.endElement(); // rdf:li + + aXmlWriter.startElement("rdf:li"); + aXmlWriter.attribute("rdf:parseType", "Resource"_ostr); + aXmlWriter.startElement("pdfaProperty:category"); + aXmlWriter.content("internal"_ostr); + aXmlWriter.endElement(); + aXmlWriter.startElement("pdfaProperty:description"); + aXmlWriter.content("PDF/UA corrigenda identifier"_ostr); + aXmlWriter.endElement(); + aXmlWriter.startElement("pdfaProperty:name"); + aXmlWriter.content("corr"_ostr); + aXmlWriter.endElement(); + aXmlWriter.startElement("pdfaProperty:valueType"); + aXmlWriter.content("Text"_ostr); + aXmlWriter.endElement(); + aXmlWriter.endElement(); // rdf:li + + aXmlWriter.endElement(); // rdf:Seq + aXmlWriter.endElement(); // pdfaSchema:property + aXmlWriter.endElement(); // rdf:li + aXmlWriter.endElement(); // rdf:Bag + aXmlWriter.endElement(); // pdfaExtension:schemas + aXmlWriter.endElement(); // rdf:Description + } + OString sPdfUaVersion = OString::number(1); + aXmlWriter.startElement("rdf:Description"); + aXmlWriter.attribute("rdf:about", ""_ostr); + aXmlWriter.attribute("xmlns:pdfuaid", "http://www.aiim.org/pdfua/ns/id/"_ostr); + + aXmlWriter.startElement("pdfuaid:part"); + aXmlWriter.content(sPdfUaVersion); + aXmlWriter.endElement(); + + aXmlWriter.endElement(); + } + + // PDF properties + if (!msProducer.isEmpty() || !msKeywords.isEmpty() || !msPDFVersion.isEmpty()) + { + aXmlWriter.startElement("rdf:Description"); + aXmlWriter.attribute("rdf:about", ""_ostr); + aXmlWriter.attribute("xmlns:pdf", "http://ns.adobe.com/pdf/1.3/"_ostr); + if (!msProducer.isEmpty()) + { + aXmlWriter.startElement("pdf:Producer"); + aXmlWriter.content(msProducer); + aXmlWriter.endElement(); + } + if (!msKeywords.isEmpty()) + { + aXmlWriter.startElement("pdf:Keywords"); + aXmlWriter.content(msKeywords); + aXmlWriter.endElement(); + } + if (!msPDFVersion.isEmpty()) + { + aXmlWriter.startElement("pdf:PDFVersion"); + aXmlWriter.content(msPDFVersion); + aXmlWriter.endElement(); + } + aXmlWriter.endElement(); + } + + // XMP Basic schema + aXmlWriter.startElement("rdf:Description"); + aXmlWriter.attribute("rdf:about", ""_ostr); + aXmlWriter.attribute("xmlns:xmp", "http://ns.adobe.com/xap/1.0/"_ostr); + 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.startElement("xmp:ModifyDate"); + aXmlWriter.content(m_sCreateDate); + aXmlWriter.endElement(); + + aXmlWriter.startElement("xmp:MetadataDate"); + 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: */ |