137 lines
5.6 KiB
C++
137 lines
5.6 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 <unordered_map>
|
|
namespace pdfengine::fonts::loader { class FontResolver; }
|
|
|
|
namespace pdfengine::parser {
|
|
|
|
class PdfiumDocument; // a page keeps its owning document alive (see ownerDoc_)
|
|
|
|
#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<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;
|
|
|
|
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_;
|
|
// Keeps the owning document (and thus the native FPDF_DOCUMENT) alive for as
|
|
// long as this page exists, so page_/textPage_ can never dangle. Declared
|
|
// here (destroyed last) so it outlives the handle teardown in the destructor.
|
|
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;
|
|
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<void, EngineError> applyEdits(const std::string& editsJson) override;
|
|
std::expected<std::vector<uint8_t>, EngineError> saveIncremental() const override;
|
|
std::expected<std::vector<uint8_t>, EngineError> saveFull() const override;
|
|
|
|
private:
|
|
NativeDocHandle doc_ = nullptr;
|
|
std::vector<uint8_t> memoryBuffer_;
|
|
|
|
mutable std::vector<FontInfo> cachedFonts_;
|
|
mutable bool hasCachedFonts_ = false;
|
|
mutable std::mutex fontsMutex_;
|
|
|
|
// Cache to prevent O(N*M) full-document scans for font data extraction
|
|
mutable std::unordered_map<std::string, std::vector<uint8_t>> fontDataCache_;
|
|
mutable int fontDataScannedPages_ = 0;
|
|
|
|
// Weak so the document never co-owns its pages: ownership runs page → document
|
|
// only. A cached entry is reused while a page is still referenced elsewhere,
|
|
// and lazily rebuilt once it expires.
|
|
mutable std::unordered_map<int, std::weak_ptr<PdfPage>> pageCache_;
|
|
mutable std::mutex pageCacheMutex_;
|
|
|
|
// Font Engine Bridge
|
|
std::unique_ptr<pdfengine::fonts::loader::FontResolver> fontResolver_;
|
|
std::unordered_map<std::string, std::shared_ptr<fonts::pdf_fonts::Font>> resolvedFontsCache_;
|
|
std::mutex resolvedFontsMutex_;
|
|
|
|
// Loaded PDFium Font Cache for Font Reuse
|
|
#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::mutex loadedFontsMutex_;
|
|
#endif
|
|
};
|
|
|
|
// Exposed for testing
|
|
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);
|
|
|
|
} |