#pragma once #include #ifdef PDFENGINE_WITH_PDFIUM #include #include #endif #include #include #include #include #include #include #include #include "fonts/pdf_fonts/embedded_font_reconstructor.hpp" namespace pdfengine::fonts::loader { class FontResolver; } namespace pdfengine::fonts { class FontFace; } namespace pdfengine::parser { class PdfiumDocument; #ifdef PDFENGINE_WITH_PDFIUM using NativeDocHandle = FPDF_DOCUMENT; using NativePageHandle = FPDF_PAGE; using NativeTextHandle = FPDF_TEXTPAGE; #else using NativeDocHandle = void*; using NativePageHandle = void*; using NativeTextHandle = void*; #endif class PdfiumPage : public PdfPage { public: PdfiumPage(NativeDocHandle docHandle, NativePageHandle pageHandle, int pageIndex, std::shared_ptr owner = nullptr); ~PdfiumPage() override; PdfiumPage(const PdfiumPage&) = delete; PdfiumPage& operator=(const PdfiumPage&) = delete; PdfiumPage(PdfiumPage&& other) noexcept; PdfiumPage& operator=(PdfiumPage&& other) noexcept; double width() const noexcept override; double height() const noexcept override; std::expected render(int dpi = 96) const override; std::expected renderRegionRaw(int dpi, double yTopPt, double heightPt) const override; std::expected renderTile(int dpi, double xPt, double yPt, double wPt, double hPt) const override; std::expected extractText() const override; std::expected, EngineError> extractTextWithBounds() const override; std::expected extractDocumentModel() const override; std::expected, EngineError> getFonts() const override; std::expected, EngineError> extractAnnotationsText() const override; std::expected, EngineError> extractAnnotations() const override; std::expected getGlyphWidth(const std::string& fontName, uint32_t charcode, double fontSize) const override; std::expected extractDisplayListJson() const override; std::expected, EngineError> extractImageXObject(const std::string& name) const override; DevicePoint pageToDevice(const Point2D& pagePoint, int deviceWidth, int deviceHeight, int rotate = 0) const noexcept override; Point2D deviceToPage(const DevicePoint& devicePoint, int deviceWidth, int deviceHeight, int rotate = 0) const noexcept override; private: NativeDocHandle doc_ = nullptr; NativePageHandle page_ = nullptr; mutable NativeTextHandle textPage_ = nullptr; int pageIndex_ = 0; mutable std::mutex textMutex_; std::shared_ptr ownerDoc_; #ifdef PDFENGINE_WITH_PDFIUM mutable std::unordered_map fontHandleCache_; #endif void ensureTextPageLoaded() const; }; class PdfiumDocument : public PdfDocument, public std::enable_shared_from_this { public: explicit PdfiumDocument(NativeDocHandle docHandle); PdfiumDocument(NativeDocHandle docHandle, std::vector memoryBuffer); ~PdfiumDocument() override; PdfiumDocument(const PdfiumDocument&) = delete; PdfiumDocument& operator=(const PdfiumDocument&) = delete; PdfiumDocument(PdfiumDocument&& other) noexcept; PdfiumDocument& operator=(PdfiumDocument&& other) noexcept; int pageCount() const noexcept override; DocumentMetadata metadata() const noexcept override; DocumentPermissions permissions() const noexcept override; std::expected, EngineError> extractOutline() const override; std::expected, EngineError> getPage(int pageIndex) override; std::expected, EngineError> getFonts(int startPage = 0, int endPage = -1) const override; std::expected, EngineError> getFontData(const std::string& internalFontId) const override; std::expected, std::string> getResolvedFont(const FontInfo& fontInfo) override; void invalidateCaches(); std::expected, EngineError> applyEdits(const std::string& editsJson) override; std::string validateLayout(int pageIndex, const std::string& jsonStr) override; std::expected, EngineError> saveIncremental() const override; std::expected, EngineError> saveFull() const override; std::expected, EngineError> saveFullForExport() const override; std::string lastReflowLayout() const { return lastReflowLayout_; } bool lastReflowOverflowed() const { return lastReflowOverflowed_; } const std::vector& getMemoryBuffer() const { return memoryBuffer_; } private: mutable std::string lastReflowLayout_; mutable bool lastReflowOverflowed_ = false; NativeDocHandle doc_ = nullptr; std::vector memoryBuffer_; mutable std::vector cachedFonts_; mutable bool hasCachedFonts_ = false; mutable std::mutex fontsMutex_; mutable std::unordered_map> fontDataCache_; mutable int fontDataScannedPages_ = 0; std::optional> getFontDataFromObjects(int pageIndex, const std::vector& objectIndices, const std::string& internalFontId) const; mutable std::unordered_map> pageCache_; mutable std::mutex pageCacheMutex_; std::unique_ptr fontResolver_; std::unordered_map> resolvedFontsCache_; std::mutex resolvedFontsMutex_; #ifdef PDFENGINE_WITH_PDFIUM mutable std::unordered_map loadedFontsCache_; mutable std::unordered_map> loadedFontDataBuffers_; mutable std::unordered_map> loadedMeasureFaces_; mutable std::mutex loadedFontsMutex_; struct EmissionFont { FPDF_FONT font = nullptr; std::shared_ptr resolved; std::shared_ptr measureFace; }; EmissionFont loadEmissionFont(int pageIndex, const std::string& internalFontId, double fontSize, const std::vector& codepoints, const std::vector& srcObjects = {}, FPDF_FONT reuseFont = nullptr); #endif mutable std::unordered_map reconstructedFonts_; mutable std::mutex reconstructedFontsMutex_; void registerAuxFont(const std::string& internalFontId, const std::vector& sfnt) override; std::expected, EngineError> getReconstructedFontData(const std::string& internalFontId) override; const fonts::pdf_fonts::ReconstructedFont* lookupReconFont(const std::string& internalFontId); #if defined(PDFENGINE_WITH_PDFIUM) && defined(PDFENGINE_WITH_QPDF) const fonts::pdf_fonts::ReconstructedFont* getReconstructedEmbeddedFont(const std::string& internalFontId); #endif void rebalanceEditedPages(const std::vector& editedPages); std::expected applyOp_replaceText(const nlohmann::json& op, int pageIndex); std::expected applyOp_reflow(const nlohmann::json& op, int pageIndex); std::expected applyOp_textOverlay(const nlohmann::json& op, int pageIndex); std::expected applyOp_stamp(const nlohmann::json& op, int pageIndex); std::expected applyOp_watermark(const nlohmann::json& op, int pageIndex); std::expected applyOp_decoration(const nlohmann::json& op, int pageIndex); std::expected applyOp_redaction(const nlohmann::json& op, int pageIndex); std::expected applyOp_updateField(const nlohmann::json& op, int pageIndex); std::expected applyOp_imageOverlay(const nlohmann::json& op, int pageIndex); std::expected applyOp_highlight(const nlohmann::json& op, int pageIndex); std::expected applyOp_freeText(const nlohmann::json& op, int pageIndex); std::expected applyOp_comment(const nlohmann::json& op, int pageIndex); std::expected applyOp_freehand(const nlohmann::json& op, int pageIndex); std::expected applyOp_pageRotation(const nlohmann::json& op, int pageIndex); std::expected applyOp_pageDeletion(const nlohmann::json& op, int pageIndex); std::expected applyOp_pageReorder(const nlohmann::json& op, int pageIndex); std::expected applyOp_deleteAnnotation(const nlohmann::json& op, int pageIndex); std::expected applyOp_updateAnnotation(const nlohmann::json& op, int pageIndex); }; std::vector utf8_to_utf16le(const std::string& utf8); std::string utf16le_to_utf8(const char16_t* utf16, size_t length); std::string code_point_to_utf8(unsigned int cp); }