Files
pdf/engine/src/parser/pdfium_document.hpp
T
2026-08-12 18:33:34 +05:30

192 lines
9.3 KiB
C++

#pragma once
#include <pdfengine/pdf_document.hpp>
#ifdef PDFENGINE_WITH_PDFIUM
#include <fpdfview.h>
#include <fpdf_text.h>
#endif
#include <memory>
#include <string>
#include <vector>
#include <mutex>
#include <optional>
#include <unordered_map>
#include <nlohmann/json_fwd.hpp>
#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<PdfiumDocument> 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<PageImage, EngineError> render(int dpi = 96) const override;
std::expected<PageImage, EngineError> renderRegionRaw(int dpi, double yTopPt, double heightPt) const override;
std::expected<PageImage, EngineError> renderTile(int dpi, double xPt, double yPt, double wPt, double hPt) const override;
std::expected<std::string, EngineError> extractText() const override;
std::expected<std::vector<GlyphBounds>, EngineError> extractTextWithBounds() const override;
std::expected<PageModel, EngineError> extractDocumentModel() const override;
std::expected<std::vector<FontInfo>, EngineError> getFonts() const override;
std::expected<std::vector<std::string>, EngineError> extractAnnotationsText() const override;
std::expected<std::vector<PdfPage::AnnotationInfo>, EngineError> extractAnnotations() const override;
std::expected<double, EngineError> getGlyphWidth(const std::string& fontName, uint32_t charcode, double fontSize) const override;
std::expected<std::string, EngineError> extractDisplayListJson() const override;
std::expected<std::vector<uint8_t>, 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<PdfiumDocument> ownerDoc_;
#ifdef PDFENGINE_WITH_PDFIUM
mutable std::unordered_map<std::string, FPDF_FONT> fontHandleCache_;
#endif
void ensureTextPageLoaded() const;
};
class PdfiumDocument : public PdfDocument, public std::enable_shared_from_this<PdfiumDocument> {
public:
explicit PdfiumDocument(NativeDocHandle docHandle);
PdfiumDocument(NativeDocHandle docHandle, std::vector<uint8_t> 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<std::vector<OutlineItem>, EngineError> extractOutline() const override;
std::expected<std::shared_ptr<PdfPage>, EngineError> getPage(int pageIndex) override;
std::expected<std::vector<FontInfo>, EngineError> getFonts(int startPage = 0, int endPage = -1) const override;
std::expected<std::vector<uint8_t>, EngineError> getFontData(const std::string& internalFontId) const override;
std::expected<std::shared_ptr<fonts::pdf_fonts::Font>, std::string> getResolvedFont(const FontInfo& fontInfo) override;
void invalidateCaches();
std::expected<std::vector<InvalidatedRegion>, EngineError> applyEdits(const std::string& editsJson) override;
std::string validateLayout(int pageIndex, const std::string& jsonStr) override;
std::expected<std::vector<uint8_t>, EngineError> saveIncremental() const override;
std::expected<std::vector<uint8_t>, EngineError> saveFull() const override;
std::expected<std::vector<uint8_t>, EngineError> saveFullForExport() const override;
std::string lastReflowLayout() const { return lastReflowLayout_; }
bool lastReflowOverflowed() const { return lastReflowOverflowed_; }
const std::vector<uint8_t>& getMemoryBuffer() const { return memoryBuffer_; }
private:
mutable std::string lastReflowLayout_;
mutable bool lastReflowOverflowed_ = false;
NativeDocHandle doc_ = nullptr;
std::vector<uint8_t> memoryBuffer_;
mutable std::vector<FontInfo> cachedFonts_;
mutable bool hasCachedFonts_ = false;
mutable std::mutex fontsMutex_;
mutable std::unordered_map<std::string, std::vector<uint8_t>> fontDataCache_;
mutable int fontDataScannedPages_ = 0;
std::optional<std::vector<uint8_t>>
getFontDataFromObjects(int pageIndex, const std::vector<int>& objectIndices,
const std::string& internalFontId) const;
mutable std::unordered_map<int, std::weak_ptr<PdfPage>> pageCache_;
mutable std::mutex pageCacheMutex_;
std::unique_ptr<pdfengine::fonts::loader::FontResolver> fontResolver_;
std::unordered_map<std::string, std::shared_ptr<fonts::pdf_fonts::Font>> resolvedFontsCache_;
std::mutex resolvedFontsMutex_;
#ifdef PDFENGINE_WITH_PDFIUM
mutable std::unordered_map<std::string, FPDF_FONT> loadedFontsCache_;
mutable std::unordered_map<std::string, std::vector<uint8_t>> loadedFontDataBuffers_;
mutable std::unordered_map<std::string, std::shared_ptr<fonts::FontFace>> loadedMeasureFaces_;
mutable std::mutex loadedFontsMutex_;
struct EmissionFont {
FPDF_FONT font = nullptr;
std::shared_ptr<fonts::pdf_fonts::Font> resolved;
std::shared_ptr<fonts::FontFace> measureFace;
};
EmissionFont loadEmissionFont(int pageIndex, const std::string& internalFontId,
double fontSize, const std::vector<uint32_t>& codepoints,
const std::vector<int>& srcObjects = {},
FPDF_FONT reuseFont = nullptr);
#endif
mutable std::unordered_map<std::string, fonts::pdf_fonts::ReconstructedFont> reconstructedFonts_;
mutable std::mutex reconstructedFontsMutex_;
void registerAuxFont(const std::string& internalFontId, const std::vector<uint8_t>& sfnt) override;
std::expected<std::vector<uint8_t>, 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<int>& editedPages);
std::expected<void, EngineError> applyOp_replaceText(const nlohmann::json& op, int pageIndex);
std::expected<void, EngineError> applyOp_reflow(const nlohmann::json& op, int pageIndex);
std::expected<void, EngineError> applyOp_textOverlay(const nlohmann::json& op, int pageIndex);
std::expected<void, EngineError> applyOp_stamp(const nlohmann::json& op, int pageIndex);
std::expected<void, EngineError> applyOp_watermark(const nlohmann::json& op, int pageIndex);
std::expected<void, EngineError> applyOp_decoration(const nlohmann::json& op, int pageIndex);
std::expected<void, EngineError> applyOp_redaction(const nlohmann::json& op, int pageIndex);
std::expected<void, EngineError> applyOp_updateField(const nlohmann::json& op, int pageIndex);
std::expected<void, EngineError> applyOp_imageOverlay(const nlohmann::json& op, int pageIndex);
std::expected<void, EngineError> applyOp_highlight(const nlohmann::json& op, int pageIndex);
std::expected<void, EngineError> applyOp_freeText(const nlohmann::json& op, int pageIndex);
std::expected<void, EngineError> applyOp_comment(const nlohmann::json& op, int pageIndex);
std::expected<void, EngineError> applyOp_freehand(const nlohmann::json& op, int pageIndex);
std::expected<void, EngineError> applyOp_pageRotation(const nlohmann::json& op, int pageIndex);
std::expected<void, EngineError> applyOp_pageDeletion(const nlohmann::json& op, int pageIndex);
std::expected<void, EngineError> applyOp_pageReorder(const nlohmann::json& op, int pageIndex);
std::expected<void, EngineError> applyOp_deleteAnnotation(const nlohmann::json& op, int pageIndex);
std::expected<void, EngineError> applyOp_updateAnnotation(const nlohmann::json& op, int pageIndex);
};
std::vector<unsigned short> 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);
}