/* -*- 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_transformpage.h>
#include <fpdf_save.h>
#include <fpdf_signature.h>
#include <fpdf_formfill.h>
#include <fpdf_attachment.h>
#include <fpdf_structtree.h>
 
#include <osl/endian.h>
#include <vcl/bitmap.hxx>
#include <vcl/embeddedfontsmanager.hxx>
#include <vcl/font.hxx>
#include <tools/stream.hxx>
#include <tools/UnitConversion.hxx>
#include <o3tl/string_view.hxx>
#include <rtl/ustrbuf.hxx>
 
#include <vcl/BitmapWriteAccess.hxx>
#include <vcl/dibtools.hxx>
#include <functional>
 
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");
 
static_assert(int(vcl::pdf::PDFAnnotationSubType::Unknown) == FPDF_ANNOT_UNKNOWN);
static_assert(int(vcl::pdf::PDFAnnotationSubType::Text) == FPDF_ANNOT_TEXT);
static_assert(int(vcl::pdf::PDFAnnotationSubType::Link) == FPDF_ANNOT_LINK);
static_assert(int(vcl::pdf::PDFAnnotationSubType::FreeText) == FPDF_ANNOT_FREETEXT);
static_assert(int(vcl::pdf::PDFAnnotationSubType::Line) == FPDF_ANNOT_LINE);
static_assert(int(vcl::pdf::PDFAnnotationSubType::Square) == FPDF_ANNOT_SQUARE);
static_assert(int(vcl::pdf::PDFAnnotationSubType::Circle) == FPDF_ANNOT_CIRCLE);
static_assert(int(vcl::pdf::PDFAnnotationSubType::Polygon) == FPDF_ANNOT_POLYGON);
static_assert(int(vcl::pdf::PDFAnnotationSubType::Polyline) == FPDF_ANNOT_POLYLINE);
static_assert(int(vcl::pdf::PDFAnnotationSubType::Highlight) == FPDF_ANNOT_HIGHLIGHT);
static_assert(int(vcl::pdf::PDFAnnotationSubType::Underline) == FPDF_ANNOT_UNDERLINE);
static_assert(int(vcl::pdf::PDFAnnotationSubType::Squiggly) == FPDF_ANNOT_SQUIGGLY);
static_assert(int(vcl::pdf::PDFAnnotationSubType::Strikeout) == FPDF_ANNOT_STRIKEOUT);
static_assert(int(vcl::pdf::PDFAnnotationSubType::Stamp) == FPDF_ANNOT_STAMP);
static_assert(int(vcl::pdf::PDFAnnotationSubType::Caret) == FPDF_ANNOT_CARET);
static_assert(int(vcl::pdf::PDFAnnotationSubType::Ink) == FPDF_ANNOT_INK);
static_assert(int(vcl::pdf::PDFAnnotationSubType::Popup) == FPDF_ANNOT_POPUP);
static_assert(int(vcl::pdf::PDFAnnotationSubType::FileAttachment) == FPDF_ANNOT_FILEATTACHMENT);
static_assert(int(vcl::pdf::PDFAnnotationSubType::Sound) == FPDF_ANNOT_SOUND);
static_assert(int(vcl::pdf::PDFAnnotationSubType::Movie) == FPDF_ANNOT_MOVIE);
static_assert(int(vcl::pdf::PDFAnnotationSubType::Widget) == FPDF_ANNOT_WIDGET);
static_assert(int(vcl::pdf::PDFAnnotationSubType::Screen) == FPDF_ANNOT_SCREEN);
static_assert(int(vcl::pdf::PDFAnnotationSubType::Printermark) == FPDF_ANNOT_PRINTERMARK);
static_assert(int(vcl::pdf::PDFAnnotationSubType::Trapnet) == FPDF_ANNOT_TRAPNET);
static_assert(int(vcl::pdf::PDFAnnotationSubType::Watermark) == FPDF_ANNOT_WATERMARK);
static_assert(int(vcl::pdf::PDFAnnotationSubType::Threed) == FPDF_ANNOT_THREED);
static_assert(int(vcl::pdf::PDFAnnotationSubType::Richmedia) == FPDF_ANNOT_RICHMEDIA);
static_assert(int(vcl::pdf::PDFAnnotationSubType::XFAWidget) == FPDF_ANNOT_XFAWIDGET);
static_assert(int(vcl::pdf::PDFAnnotationSubType::Redact) == FPDF_ANNOT_REDACT);
 
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;
}
 
OUString getUnicodeString(std::function<int(FPDF_WCHAR*, unsigned long)> aPDFiumFunctionCall)
{
    OUString sReturnText;
 
    int nBytes = aPDFiumFunctionCall(nullptr, 0);
    if (nBytes == 0)
        return sReturnText;
    assert(nBytes % 2 == 0);
    nBytes /= 2;
 
    std::vector<sal_Unicode> pText(nBytes, 0);
 
    int nActualBytes = aPDFiumFunctionCall(reinterpret_cast<FPDF_WCHAR*>(pText.data()), nBytes * 2);
    assert(nActualBytes % 2 == 0);
    nActualBytes /= 2;
    if (nActualBytes > 1)
    {
#ifdef OSL_BIGENDIAN
        for (int i = 0; i != nActualBytes; ++i)
        {
            pText[i] = OSL_SWAPWORD(pText[i]);
        }
#endif
        sReturnText = OUString(pText.data());
    }
 
    return sReturnText;
}
}
 
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;
    Bitmap createBitmapFromBuffer() 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 PDFiumClipPathImpl final : public PDFiumClipPath
{
private:
    FPDF_CLIPPATH mpClipPath;
 
    PDFiumClipPathImpl(const PDFiumClipPathImpl&) = delete;
    PDFiumClipPathImpl& operator=(const PDFiumClipPathImpl&) = delete;
 
public:
    PDFiumClipPathImpl(FPDF_CLIPPATH pClipPath);
 
    int getPathCount() override;
    int getPathSegmentCount(int nPathIndex) override;
    std::unique_ptr<PDFiumPathSegment> getPathSegment(int nPathIndex, int nSegmentIndex) 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;
    Color getFontColor(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;
    int getOptionCount(PDFiumDocument* pDoc) override;
};
 
class VCL_DLLPUBLIC PDFiumLinkImpl final : public PDFiumLink
{
    FPDF_LINK mpLink;
    OUString maURI;
 
    PDFiumLinkImpl(const PDFiumLinkImpl&) = delete;
    PDFiumLinkImpl& operator=(const PDFiumLinkImpl&) = delete;
 
public:
    PDFiumLinkImpl(FPDF_DOCUMENT pDocument, FPDF_LINK pLink);
    basegfx::B2DRectangle getRectangle() override;
    OUString getURIPath() override;
};
 
class PDFiumStructureElementImpl final : public PDFiumStructureElement
{
private:
    FPDF_STRUCTELEMENT mpStructureElement;
 
    PDFiumStructureElementImpl(const PDFiumStructureElementImpl&) = delete;
    PDFiumStructureElementImpl& operator=(const PDFiumStructureElementImpl&) = delete;
 
public:
    PDFiumStructureElementImpl(FPDF_STRUCTELEMENT pStructureElement);
 
    OUString getAltText() override;
    OUString getActualText() override;
    OUString getID() override;
    OUString getLang() override;
    OUString getTitle() override;
    OUString getType() override;
    OUString getObjectType() override;
 
    int getNumberOfChildren() override;
    int getChildMarkedContentID(int nIndex) override;
    std::unique_ptr<PDFiumStructureElement> getChild(int nIndex) override;
    std::unique_ptr<PDFiumStructureElement> getParent() override;
};
 
class PDFiumStructureTreeImpl final : public PDFiumStructureTree
{
private:
    FPDF_STRUCTTREE mpStructureTree;
 
    PDFiumStructureTreeImpl(const PDFiumStructureTreeImpl&) = delete;
    PDFiumStructureTreeImpl& operator=(const PDFiumStructureTreeImpl&) = delete;
 
public:
    PDFiumStructureTreeImpl(FPDF_STRUCTTREE pStructureTree);
    ~PDFiumStructureTreeImpl();
 
    int getNumberOfChildren() override;
    std::unique_ptr<PDFiumStructureElement> getChild(int nIndex) 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;
    OUString getBaseFontName() override;
    int getFontAngle() override;
    std::unique_ptr<PDFiumFont> getFont() override;
    PDFTextRenderMode getTextRenderMode() override;
    Color getFillColor() override;
    std::unique_ptr<PDFiumBitmap> getRenderedFillPattern(PDFiumDocument& rDoc,
                                                         PDFiumPage& rPage) override;
    Color getStrokeColor() override;
    double getStrokeWidth() override;
    std::unique_ptr<PDFiumBitmap> getRenderedStrokePattern(PDFiumDocument& rDoc,
                                                           PDFiumPage& rPage) 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;
 
    // ClipPath
    std::unique_ptr<PDFiumClipPath> getClipPath() override;
};
 
class PDFiumFontImpl final : public PDFiumFont
{
private:
    FPDF_FONT mpFont;
 
    PDFiumFontImpl(const PDFiumFontImpl&) = delete;
    PDFiumFontImpl& operator=(const PDFiumFontImpl&) = delete;
 
public:
    PDFiumFontImpl(FPDF_FONT pFont);
 
    bool getFontData(std::vector<uint8_t>& rData) const override;
    bool getFontToUnicode(std::vector<uint8_t>& rData) const override;
    bool getIsEmbedded() const override;
 
    sal_uInt32 getGlyphIndexFromCharCode(const sal_uInt32 nCharCode) const override;
 
    sal_Int32 getFontDictObjNum() const 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();
 
    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;
 
    OUString getText(FPDF_PAGEOBJECT pPageObject)
    {
        OUStringBuffer aResult;
 
        bool containsPreChar = false;
        bool addLineFeed = false;
        double posY(0), originX(0.0), originY(0.0);
 
        // FPDFTextObj_GetText also does a similar loop over the entire
        // contents of the text page, this is the intended to be the equivalent
        // of that except for (currently) added recovery of hyphens.
        int count = FPDFText_CountChars(mpTextPage);
        for (int i = 0; i < count; ++i)
        {
            FPDF_PAGEOBJECT pOwner = FPDFText_GetTextObject(mpTextPage, i);
            sal_Unicode cUnicode = FPDFText_GetUnicode(mpTextPage, i);
            if (pOwner == pPageObject)
            {
                FPDFText_GetCharOrigin(mpTextPage, i, &originX, &originY);
 
                if (fabs(posY - originY) > 0 && !containsPreChar && addLineFeed)
                {
                    posY = originY;
                    if (!aResult.isEmpty())
                        aResult.append("\r\n");
                }
                containsPreChar = true;
                addLineFeed = false;
 
                switch (cUnicode)
                {
                    case 0:
                        SAL_INFO("vcl.filter", "PDFiumImpl: cannot get unicode for char");
                        break;
                    default:
                        aResult.append(cUnicode);
                        break;
                    case 0x2: // oddly pdfium replaces some '-' with 2.
                    {
                        int isHyphen = FPDFText_IsHyphen(mpTextPage, i);
                        if (isHyphen == 1)
                            aResult.append('-');
                        else
                        {
                            SAL_WARN_IF(isHyphen == -1, "vcl.filter",
                                        "PDFiumImpl: FPDFText_IsHyphen failure");
                            aResult.append(cUnicode);
                        }
                    }
                    break;
                }
            }
            else if (cUnicode == ' ' && containsPreChar)
            {
                aResult.append(' ');
                containsPreChar = false;
                addLineFeed = false;
            }
            else
            {
                containsPreChar = false;
                addLineFeed = true;
            }
        }
        return aResult.makeStringAndClear();
    }
};
 
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;
 
    std::unique_ptr<PDFiumStructureTree> getStructureTree() override;
 
    BitmapChecksum getChecksum(int nMDPPerm) override;
 
    double getWidth() override;
    double getHeight() override;
 
    bool hasTransparency() override;
 
    bool hasLinks() override;
    std::unique_ptr<PDFiumLink> enumerateLink(int* nStartIndex, PDFiumDocument* pDoc) 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 PDFiumAttachmentImpl final : public PDFiumAttachment
{
private:
    FPDF_ATTACHMENT mpAttachment;
    PDFiumAttachmentImpl(const PDFiumSignatureImpl&) = delete;
    PDFiumAttachmentImpl& operator=(const PDFiumSignatureImpl&) = delete;
 
public:
    PDFiumAttachmentImpl(FPDF_ATTACHMENT pAttachment)
        : mpAttachment(pAttachment)
    {
    }
 
    OUString getName() override;
    bool getFile(std::vector<unsigned char>& rOutBuffer) override;
};
 
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();
    FPDF_DOCUMENT getPointer() { return mpPdfDocument; }
 
    // Page size in points
    basegfx::B2DSize getPageSize(int nIndex) override;
    int getPageCount() override;
    int getSignatureCount() override;
    int getAttachmentCount() override;
    int getFileVersion() override;
    bool saveWithVersion(SvMemoryStream& rStream, int nFileVersion, bool bRemoveSecurity) override;
 
    std::unique_ptr<PDFiumPage> openPage(int nIndex) override;
    std::unique_ptr<PDFiumSignature> getSignature(int nIndex) override;
    std::unique_ptr<PDFiumAttachment> getAttachment(int nIndex) override;
    std::vector<unsigned int> getTrailerEnds() override;
    OUString getBookmarks() override;
};
 
class PDFiumImpl : public PDFium
{
private:
    PDFiumImpl(const PDFiumImpl&) = delete;
    PDFiumImpl& operator=(const PDFiumImpl&) = delete;
 
    OUString maLastError;
    void setLastError(OUString const& rErrorString);
 
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(); }
 
void PDFiumImpl::setLastError(OUString const& rErrorString)
{
    if (!rErrorString.isEmpty())
    {
        // Report what error was set (useful in test failures)
        SAL_WARN("vcl.filter", "PDFiumImpl Error: '" << rErrorString << "' Error number: "
                                                     << sal_Int32(getLastErrorCode()));
    }
    maLastError = rErrorString;
}
 
std::unique_ptr<PDFiumDocument> PDFiumImpl::openDocument(const void* pData, int nSize,
                                                         const OString& rPassword)
{
    setLastError(u""_ustr);
    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:
                setLastError(u"Success"_ustr);
                break;
            case FPDF_ERR_UNKNOWN:
                setLastError(u"Unknown error"_ustr);
                break;
            case FPDF_ERR_FILE:
                setLastError(u"File not found"_ustr);
                break;
            case FPDF_ERR_FORMAT:
                setLastError(u"Input is not a PDF format"_ustr);
                break;
            case FPDF_ERR_PASSWORD:
                setLastError(u"Incorrect password or password is required"_ustr);
                break;
            case FPDF_ERR_SECURITY:
                setLastError(u"Security error"_ustr);
                break;
            case FPDF_ERR_PAGE:
                setLastError(u"Content error"_ustr);
                break;
            default:
                setLastError(u"Unknown error number"_ustr);
                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)
    {
        setLastError(u"Failed to create bitmap"_ustr);
    }
    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;
}
 
OUString PDFiumAttachmentImpl::getName()
{
    return getUnicodeString([this](FPDF_WCHAR* buffer, unsigned long length) {
        return FPDFAttachment_GetName(mpAttachment, buffer, length);
    });
}
 
bool PDFiumAttachmentImpl::getFile(std::vector<unsigned char>& rOutBuffer)
{
    rOutBuffer.clear();
 
    unsigned long nLength{};
    if (!FPDFAttachment_GetFile(mpAttachment, nullptr, 0, &nLength))
        return false;
 
    rOutBuffer.resize(nLength);
    unsigned long nActualLength{};
    if (!FPDFAttachment_GetFile(mpAttachment, rOutBuffer.data(), nLength, &nActualLength))
        return false;
    rOutBuffer.resize(nActualLength);
    return true;
}
 
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::unique_ptr<PDFiumAttachment> PDFiumDocumentImpl::getAttachment(int nIndex)
{
    std::unique_ptr<PDFiumAttachment> pPDFiumAttachment;
    FPDF_ATTACHMENT pAttachment = FPDFDoc_GetAttachment(mpPdfDocument, nIndex);
    if (pAttachment)
    {
        pPDFiumAttachment = std::make_unique<PDFiumAttachmentImpl>(pAttachment);
    }
    return pPDFiumAttachment;
}
 
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;
}
 
static void lcl_getBookmarks(int nLevel, OUStringBuffer& rBuf, FPDF_DOCUMENT pDoc,
                             FPDF_BOOKMARK pBookmark)
{
    // no first child or too much levels
    if (!pBookmark || nLevel > 10)
        return;
 
    OUString aString;
    int nBytes = FPDFBookmark_GetTitle(pBookmark, nullptr, 0);
    assert(nBytes % 2 == 0);
    nBytes /= 2;
 
    std::unique_ptr<sal_Unicode[]> pText(new sal_Unicode[nBytes]);
 
    int nActualBytes = FPDFBookmark_GetTitle(pBookmark, 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
        // insert nLevel spaces before the title
        rBuf.append(OUString("          ").subView(0, nLevel));
        aString = OUString(pText.get());
    }
 
    rBuf.append(aString);
    rBuf.append("\n");
 
    // get children
    lcl_getBookmarks(nLevel + 1, rBuf, pDoc, FPDFBookmark_GetFirstChild(pDoc, pBookmark));
 
    // get siblings
    while (nullptr != (pBookmark = FPDFBookmark_GetNextSibling(pDoc, pBookmark)))
        lcl_getBookmarks(nLevel, rBuf, pDoc, pBookmark);
}
 
OUString PDFiumDocumentImpl::getBookmarks()
{
    OUStringBuffer aBuf;
    FPDF_BOOKMARK pBookmark = FPDFBookmark_GetFirstChild(mpPdfDocument, nullptr);
    lcl_getBookmarks(0, aBuf, mpPdfDocument, pBookmark);
    return aBuf.makeStringAndClear();
}
 
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::getAttachmentCount() { return FPDFDoc_GetAttachmentCount(mpPdfDocument); }
 
int PDFiumDocumentImpl::getFileVersion()
{
    int nFileVersion = 0;
    FPDF_GetFileVersion(mpPdfDocument, &nFileVersion);
    return nFileVersion;
}
 
bool PDFiumDocumentImpl::saveWithVersion(SvMemoryStream& rStream, int nFileVersion,
                                         bool bRemoveSecurity)
{
    CompatibleWriter aWriter(rStream);
    aWriter.version = 1;
    aWriter.WriteBlock = &CompatibleWriterCallback;
    if (!FPDF_SaveWithVersion(mpPdfDocument, &aWriter, bRemoveSecurity ? FPDF_REMOVE_SECURITY : 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;
}
 
std::unique_ptr<PDFiumStructureTree> PDFiumPageImpl::getStructureTree()
{
    std::unique_ptr<PDFiumStructureTree> pPDFiumStructureTree;
    FPDF_STRUCTTREE pStructTree = FPDF_StructTree_GetForPage(mpPage);
    if (pStructTree)
    {
        pPDFiumStructureTree = std::make_unique<PDFiumStructureTreeImpl>(pStructTree);
    }
    return pPDFiumStructureTree;
}
 
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);
}
 
std::unique_ptr<PDFiumLink> PDFiumPageImpl::enumerateLink(int* nStartIndex, PDFiumDocument* pDoc)
{
    std::unique_ptr<PDFiumLink> pPDFiumLink;
    FPDF_LINK pLinkAnnot = nullptr;
    if (FPDFLink_Enumerate(mpPage, nStartIndex, &pLinkAnnot))
    {
        auto pDocImpl = static_cast<PDFiumDocumentImpl*>(pDoc);
        pPDFiumLink = std::make_unique<PDFiumLinkImpl>(pDocImpl->getPointer(), pLinkAnnot);
    }
    return pPDFiumLink;
}
 
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)
{
    auto pTextPage = static_cast<PDFiumTextPageImpl*>(rTextPage.get());
    // FPDFTextObj_GetText may report some hyphens as 0x2
    return pTextPage->getText(mpPageObject);
}
 
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::getBaseFontName()
{
    OUString sBaseFontName;
    const int nBaseFontName = 80 + 1;
    std::unique_ptr<char[]> pBaseFontName(new char[nBaseFontName]); // + terminating null
    FPDF_FONT pFontObject = FPDFTextObj_GetFont(mpPageObject);
    int nBaseFontNameChars
        = FPDFFont_GetBaseFontName(pFontObject, pBaseFontName.get(), nBaseFontName);
    if (nBaseFontName >= nBaseFontNameChars)
    {
        sBaseFontName = OUString::createFromAscii(pBaseFontName.get());
    }
    return sBaseFontName;
}
 
OUString PDFiumPageObjectImpl::getFontName()
{
    OUString sFamilyName;
    const int nFamilyName = 80 + 1;
    std::unique_ptr<char[]> pFamilyName(new char[nFamilyName]); // + terminating null
    FPDF_FONT pFontObject = FPDFTextObj_GetFont(mpPageObject);
    int nFamilyNameChars = FPDFFont_GetFamilyName(pFontObject, pFamilyName.get(), nFamilyName);
    if (nFamilyName >= nFamilyNameChars)
    {
        sFamilyName = OUString::createFromAscii(pFamilyName.get());
    }
    return sFamilyName;
}
 
int PDFiumPageObjectImpl::getFontAngle()
{
    int nFontAngle(0);
    FPDF_FONT pFontObject = FPDFTextObj_GetFont(mpPageObject);
    FPDFFont_GetItalicAngle(pFontObject, &nFontAngle);
    return nFontAngle;
}
 
std::unique_ptr<PDFiumFont> PDFiumPageObjectImpl::getFont()
{
    std::unique_ptr<PDFiumFont> pPDFiumFont;
    if (FPDF_FONT pFont = FPDFTextObj_GetFont(mpPageObject))
        pPDFiumFont = std::make_unique<PDFiumFontImpl>(pFont);
    return pPDFiumFont;
}
 
PDFiumFontImpl::PDFiumFontImpl(FPDF_FONT pFont)
    : mpFont(pFont)
{
}
 
bool PDFiumFontImpl::getFontData(std::vector<uint8_t>& rData) const
{
    size_t buflen(0);
    bool bOk = FPDFFont_GetFontData(mpFont, nullptr, 0, &buflen);
    if (!bOk)
    {
        SAL_WARN("vcl.filter", "PDFiumImpl: failed to get font data");
        return false;
    }
    rData.resize(buflen);
    bOk = FPDFFont_GetFontData(mpFont, rData.data(), rData.size(), &buflen);
    assert(bOk && rData.size() == buflen);
    return bOk;
}
 
bool PDFiumFontImpl::getFontToUnicode(std::vector<uint8_t>& rData) const
{
    size_t buflen(0);
    bool bOk = FPDFFont_GetToUnicodeContent(mpFont, nullptr, 0, &buflen);
    if (!bOk)
    {
        SAL_WARN("vcl.filter", "PDFiumImpl: failed to get font data");
        return false;
    }
    rData.resize(buflen);
    bOk = FPDFFont_GetToUnicodeContent(mpFont, rData.data(), rData.size(), &buflen);
    assert(bOk && rData.size() == buflen);
    return bOk;
}
 
bool PDFiumFontImpl::getIsEmbedded() const { return FPDFFont_GetIsEmbedded(mpFont) == 1; }
 
sal_uInt32 PDFiumFontImpl::getGlyphIndexFromCharCode(const sal_uInt32 nCharCode) const
{
    return FPDFFont_GetGlyphIndexFromCharCode(mpFont, nCharCode);
}
 
sal_Int32 PDFiumFontImpl::getFontDictObjNum() const { return FPDFFont_GetFontDictObjNum(mpFont); }
 
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;
}
 
std::unique_ptr<PDFiumClipPath> PDFiumPageObjectImpl::getClipPath()
{
    std::unique_ptr<PDFiumClipPath> pPDFiumClipPath;
    FPDF_CLIPPATH pClipPath = FPDFPageObj_GetClipPath(mpPageObject);
    if (pClipPath)
    {
        pPDFiumClipPath = std::make_unique<PDFiumClipPathImpl>(pClipPath);
    }
    return pPDFiumClipPath;
}
 
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;
}
 
std::unique_ptr<PDFiumBitmap> PDFiumPageObjectImpl::getRenderedStrokePattern(PDFiumDocument& rDoc,
                                                                             PDFiumPage& rPage)
{
    auto& rDocImpl = static_cast<PDFiumDocumentImpl&>(rDoc);
    auto& rPageImpl = static_cast<PDFiumPageImpl&>(rPage);
    std::unique_ptr<PDFiumBitmap> pPDFiumBitmap;
    FPDF_BITMAP pBitmap = FPDFPageObj_GetRenderedStrokePattern(
        rDocImpl.getPointer(), rPageImpl.getPointer(), mpPageObject);
    if (pBitmap)
    {
        pPDFiumBitmap = std::make_unique<PDFiumBitmapImpl>(pBitmap);
    }
    return pPDFiumBitmap;
}
 
std::unique_ptr<PDFiumBitmap> PDFiumPageObjectImpl::getRenderedFillPattern(PDFiumDocument& rDoc,
                                                                           PDFiumPage& rPage)
{
    auto& rDocImpl = static_cast<PDFiumDocumentImpl&>(rDoc);
    auto& rPageImpl = static_cast<PDFiumPageImpl&>(rPage);
    std::unique_ptr<PDFiumBitmap> pPDFiumBitmap;
    FPDF_BITMAP pBitmap = FPDFPageObj_GetRenderedFillPattern(rDocImpl.getPointer(),
                                                             rPageImpl.getPointer(), 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));
}
 
PDFiumClipPathImpl::PDFiumClipPathImpl(FPDF_CLIPPATH pClipPath)
    : mpClipPath(pClipPath)
{
}
 
int PDFiumClipPathImpl::getPathCount() { return FPDFClipPath_CountPaths(mpClipPath); }
 
int PDFiumClipPathImpl::getPathSegmentCount(int nPathIndex)
{
    return FPDFClipPath_CountPathSegments(mpClipPath, nPathIndex);
}
 
std::unique_ptr<PDFiumPathSegment> PDFiumClipPathImpl::getPathSegment(int nPathIndex,
                                                                      int nSegmentIndex)
{
    std::unique_ptr<PDFiumPathSegment> pPDFiumPathSegment;
    FPDF_PATHSEGMENT pPathSegment
        = FPDFClipPath_GetPathSegment(mpClipPath, nPathIndex, nSegmentIndex);
    if (pPathSegment)
    {
        pPDFiumPathSegment = std::make_unique<PDFiumPathSegmentImpl>(pPathSegment);
    }
    return pPDFiumPathSegment;
}
 
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));
}
 
Bitmap PDFiumBitmapImpl::createBitmapFromBuffer()
{
    Bitmap aBitmap;
 
    const vcl::pdf::PDFBitmapType eFormat = getFormat();
    if (eFormat == vcl::pdf::PDFBitmapType::Unknown)
        return aBitmap;
 
    const int nWidth = getWidth();
    const int nHeight = getHeight();
    const int nStride = getStride();
 
    switch (eFormat)
    {
        case vcl::pdf::PDFBitmapType::Gray:
        {
            aBitmap = Bitmap(Size(nWidth, nHeight), vcl::PixelFormat::N8_BPP,
                             &Bitmap::GetGreyPalette(256));
            ReadRawDIB(aBitmap, getBuffer(), ScanlineFormat::N8BitPal, nHeight, nStride);
        }
        break;
 
        case vcl::pdf::PDFBitmapType::BGR:
        {
            aBitmap = Bitmap(Size(nWidth, nHeight), vcl::PixelFormat::N24_BPP);
            ReadRawDIB(aBitmap, getBuffer(), ScanlineFormat::N24BitTcBgr, nHeight, nStride);
        }
        break;
 
        case vcl::pdf::PDFBitmapType::BGRx:
        {
            aBitmap = Bitmap(Size(nWidth, nHeight), vcl::PixelFormat::N24_BPP);
            ReadRawDIB(aBitmap, getBuffer(), ScanlineFormat::N32BitTcRgbx, nHeight, nStride);
        }
        break;
 
        case vcl::pdf::PDFBitmapType::BGRA:
        {
            aBitmap = Bitmap(Size(nWidth, nHeight), vcl::PixelFormat::N32_BPP);
            {
                BitmapScopedWriteAccess pWriteAccess(aBitmap);
                ConstScanline pBuffer = getBuffer();
                for (int nRow = 0; nRow < nHeight; ++nRow)
                {
                    ConstScanline pLine = pBuffer + (nStride * nRow);
                    pWriteAccess->CopyScanline(nRow, pLine, ScanlineFormat::N32BitTcBgra, nStride);
                }
            }
        }
        break;
 
        default:
            break;
    }
 
    return aBitmap;
}
 
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;
}
 
Color PDFiumAnnotationImpl::getFontColor(PDFiumDocument* pDoc)
{
    auto pDocImpl = static_cast<PDFiumDocumentImpl*>(pDoc);
    unsigned int nR, nG, nB;
    if (!FPDFAnnot_GetFontColor(pDocImpl->getFormHandlePointer(), mpAnnotation, &nR, &nG, &nB))
    {
        return Color();
    }
 
    return Color(nR, nG, nB);
}
 
OUString PDFiumAnnotationImpl::getFormFieldAlternateName(PDFiumDocument* pDoc)
{
    auto* pDocImpl = static_cast<PDFiumDocumentImpl*>(pDoc);
    return getUnicodeString([this, pDocImpl](FPDF_WCHAR* buffer, unsigned long length) {
        return FPDFAnnot_GetFormFieldAlternateName(pDocImpl->getFormHandlePointer(), mpAnnotation,
                                                   buffer, length);
    });
}
 
OUString PDFiumAnnotationImpl::getFormFieldValue(PDFiumDocument* pDoc)
{
    auto* pDocImpl = static_cast<PDFiumDocumentImpl*>(pDoc);
    return getUnicodeString([this, pDocImpl](FPDF_WCHAR* buffer, unsigned long length) {
        return FPDFAnnot_GetFormFieldValue(pDocImpl->getFormHandlePointer(), mpAnnotation, buffer,
                                           length);
    });
}
int PDFiumAnnotationImpl::getOptionCount(PDFiumDocument* pDoc)
{
    auto* pDocImpl = static_cast<PDFiumDocumentImpl*>(pDoc);
    return FPDFAnnot_GetOptionCount(pDocImpl->getFormHandlePointer(), mpAnnotation);
}
 
OUString PDFiumAnnotationImpl::getFormAdditionalActionJavaScript(PDFiumDocument* pDoc,
                                                                 PDFAnnotAActionType eEvent)
{
    auto* pDocImpl = static_cast<PDFiumDocumentImpl*>(pDoc);
    return getUnicodeString([this, pDocImpl, eEvent](FPDF_WCHAR* buffer, unsigned long length) {
        return FPDFAnnot_GetFormAdditionalActionJavaScript(pDocImpl->getFormHandlePointer(),
                                                           mpAnnotation, static_cast<int>(eEvent),
                                                           buffer, length);
    });
}
 
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)
{
    return getUnicodeString([this, rKey](FPDF_WCHAR* buffer, unsigned long length) {
        return FPDFAnnot_GetStringValue(mpAnnotation, rKey.getStr(), buffer, length);
    });
}
 
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;
}
 
PDFiumLinkImpl::PDFiumLinkImpl(FPDF_DOCUMENT pDocument, FPDF_LINK pLink)
    : mpLink(pLink)
{
    FPDF_ACTION pAction = FPDFLink_GetAction(pLink);
    int nType = FPDFAction_GetType(pAction);
    if (nType != PDFACTION_URI)
        return;
 
    size_t nLen = FPDFAction_GetURIPath(pDocument, pAction, nullptr, 0);
    if (nLen == 0)
        return;
 
    char* pBuffer = new char[nLen];
    FPDFAction_GetURIPath(pDocument, pAction, pBuffer, nLen);
    maURI = OUString::fromUtf8(std::string_view(pBuffer, nLen - 1));
    delete[] pBuffer;
}
 
basegfx::B2DRectangle PDFiumLinkImpl::getRectangle()
{
    basegfx::B2DRectangle aB2DRectangle;
    FS_RECTF aRect;
    if (FPDFLink_GetAnnotRect(mpLink, &aRect))
    {
        aB2DRectangle = basegfx::B2DRectangle(aRect.left, aRect.top, aRect.right, aRect.bottom);
    }
    return aB2DRectangle;
}
 
OUString PDFiumLinkImpl::getURIPath() { return maURI; }
 
PDFiumStructureElementImpl::PDFiumStructureElementImpl(FPDF_STRUCTELEMENT pStructureElement)
    : mpStructureElement(pStructureElement)
{
}
 
OUString PDFiumStructureElementImpl::getAltText()
{
    return getUnicodeString([this](FPDF_WCHAR* buffer, unsigned long length) {
        return FPDF_StructElement_GetAltText(mpStructureElement, buffer, length);
    });
}
 
OUString PDFiumStructureElementImpl::getActualText()
{
    return getUnicodeString([this](FPDF_WCHAR* buffer, unsigned long length) {
        return FPDF_StructElement_GetActualText(mpStructureElement, buffer, length);
    });
}
 
OUString PDFiumStructureElementImpl::getID()
{
    return getUnicodeString([this](FPDF_WCHAR* buffer, unsigned long length) {
        return FPDF_StructElement_GetID(mpStructureElement, buffer, length);
    });
}
 
OUString PDFiumStructureElementImpl::getLang()
{
    return getUnicodeString([this](FPDF_WCHAR* buffer, unsigned long length) {
        return FPDF_StructElement_GetLang(mpStructureElement, buffer, length);
    });
}
 
OUString PDFiumStructureElementImpl::getType()
{
    return getUnicodeString([this](FPDF_WCHAR* buffer, unsigned long length) {
        return FPDF_StructElement_GetType(mpStructureElement, buffer, length);
    });
}
 
OUString PDFiumStructureElementImpl::getObjectType()
{
    return getUnicodeString([this](FPDF_WCHAR* buffer, unsigned long length) {
        return FPDF_StructElement_GetObjType(mpStructureElement, buffer, length);
    });
}
 
int PDFiumStructureElementImpl::getChildMarkedContentID(int nIndex)
{
    return FPDF_StructElement_GetChildMarkedContentID(mpStructureElement, nIndex);
}
 
OUString PDFiumStructureElementImpl::getTitle()
{
    return getUnicodeString([this](FPDF_WCHAR* buffer, unsigned long length) {
        return FPDF_StructElement_GetTitle(mpStructureElement, buffer, length);
    });
}
 
int PDFiumStructureElementImpl::getNumberOfChildren()
{
    return FPDF_StructElement_CountChildren(mpStructureElement);
}
 
std::unique_ptr<PDFiumStructureElement> PDFiumStructureElementImpl::getChild(int nIndex)
{
    std::unique_ptr<PDFiumStructureElement> pPDFiumStructureElement;
    FPDF_STRUCTELEMENT pElement = FPDF_StructElement_GetChildAtIndex(mpStructureElement, nIndex);
    if (pElement)
    {
        pPDFiumStructureElement = std::make_unique<PDFiumStructureElementImpl>(pElement);
    }
    return pPDFiumStructureElement;
}
 
std::unique_ptr<PDFiumStructureElement> PDFiumStructureElementImpl::getParent()
{
    std::unique_ptr<PDFiumStructureElement> pPDFiumStructureElement;
    FPDF_STRUCTELEMENT pElement = FPDF_StructElement_GetParent(mpStructureElement);
    if (pElement)
    {
        pPDFiumStructureElement = std::make_unique<PDFiumStructureElementImpl>(pElement);
    }
    return pPDFiumStructureElement;
}
 
PDFiumStructureTreeImpl::PDFiumStructureTreeImpl(FPDF_STRUCTTREE pStructureTree)
    : mpStructureTree(pStructureTree)
{
}
 
PDFiumStructureTreeImpl::~PDFiumStructureTreeImpl()
{
    if (mpStructureTree)
        FPDF_StructTree_Close(mpStructureTree);
}
 
int PDFiumStructureTreeImpl::getNumberOfChildren()
{
    return FPDF_StructTree_CountChildren(mpStructureTree);
}
 
std::unique_ptr<PDFiumStructureElement> PDFiumStructureTreeImpl::getChild(int nIndex)
{
    std::unique_ptr<PDFiumStructureElement> pPDFiumStructureElement;
    FPDF_STRUCTELEMENT pElement = FPDF_StructTree_GetChildAtIndex(mpStructureTree, nIndex);
    if (pElement)
    {
        pPDFiumStructureElement = std::make_unique<PDFiumStructureElementImpl>(pElement);
    }
    return pPDFiumStructureElement;
}
 
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: */

V530 The return value of function 'append' is required to be utilized.

V530 The return value of function 'append' is required to be utilized.

V530 The return value of function 'append' is required to be utilized.

V530 The return value of function 'append' is required to be utilized.

V530 The return value of function 'append' is required to be utilized.