#pragma once #include #include #include #include #include #if __has_include() #include #elif __has_include() #include namespace std { using tl::expected; using tl::unexpected; } #endif namespace pdfengine { enum class EngineError { FileNotFound, InvalidFormat, PasswordRequired, InvalidPassword, PageOutOfBounds, RenderFailed, WriteFailed, Unknown }; struct DocumentMetadata { std::string title; std::string author; std::string creator; std::string producer; std::string creationDate; std::string modificationDate; }; struct DocumentPermissions { bool isEncrypted = false; std::string encryption = "None"; int securityRevision = -1; bool ownerUnlocked = false; bool canPrint = true; bool canPrintHighRes = true; bool canModify = true; bool canCopy = true; bool canAnnotate = true; bool canFillForms = true; bool canExtractForAccessibility = true; bool canAssemble = true; }; struct PageImage { int width; int height; std::vector data; }; struct Point2D { double x; double y; }; struct DevicePoint { int x; int y; }; struct InvalidatedRegion { int pageIndex; double x; double y; double width; double height; }; struct GlyphBounds { std::string text; double x; double y; double w; double h; double fontSize; }; struct HitResult { int glyphIndex = -1; int caret = 0; int line = -1; }; struct TextSelection { int startGlyph = 0; int endGlyph = 0; std::string text; std::vector rects; }; struct FontInfo { std::string fontName; std::string type; bool isEmbedded = false; bool isSubset = false; bool isVertical = false; std::string encoding; bool hasToUnicode = false; std::string cmapName; std::string cidSystemInfo; std::string subsetTag; std::string sourceType; std::string substitutedFrom; std::string substitutedTo; std::string normalizedFamily; std::string internalFontId; uint32_t flags = 0; double ascent = 0.0; double descent = 0.0; double capHeight = 0.0; }; struct Glyph { std::string text; uint32_t unicode = 0; std::string fontName; uint32_t flags = 0; double fontSize = 0.0; double originX = 0.0; double originY = 0.0; double bboxX = 0.0, bboxY = 0.0, bboxW = 0.0, bboxH = 0.0; double angle = 0.0; int pageObjectIndex = -1; int srcIndex = 0; }; struct TextRun { std::string text; std::string fontName; uint32_t flags = 0; double fontSize = 0.0; std::string internalFontId; bool isEmbedded = false; std::string type; std::vector glyphs; std::vector objectIndices; double x = 0.0, y = 0.0, w = 0.0, h = 0.0; std::string fillColor = "#000000"; std::string paraId; std::string fontFidelity = "exact"; }; struct TextLine { std::vector runs; std::vector glyphs; double angle = 0.0; double baselineY = 0.0; double x = 0.0, y = 0.0, w = 0.0, h = 0.0; }; struct Paragraph { std::vector lines; double x = 0.0, y = 0.0, w = 0.0, h = 0.0; }; struct PageModel { std::vector paragraphs; double width = 0.0; double height = 0.0; int pageIndex = 0; }; class PdfPage { public: virtual ~PdfPage() = default; [[nodiscard]] virtual double width() const noexcept = 0; [[nodiscard]] virtual double height() const noexcept = 0; [[nodiscard]] virtual std::expected render(int dpi = 96) const = 0; [[nodiscard]] virtual std::expected renderRegionRaw(int dpi, double yTopPt, double heightPt) const { (void)dpi; (void)yTopPt; (void)heightPt; return std::unexpected(EngineError::Unknown); } [[nodiscard]] virtual std::expected renderTile(int dpi, double xPt, double yPt, double wPt, double hPt) const { (void)dpi; (void)xPt; (void)yPt; (void)wPt; (void)hPt; return std::unexpected(EngineError::Unknown); } [[nodiscard]] virtual std::expected extractText() const = 0; [[nodiscard]] virtual std::expected, EngineError> extractTextWithBounds() const = 0; [[nodiscard]] std::expected, EngineError> orderedGlyphs() const; [[nodiscard]] std::expected hitGlyph(double x, double y) const; [[nodiscard]] std::expected selectRange(double ax, double ay, double bx, double by) const; [[nodiscard]] virtual std::expected extractDocumentModel() const = 0; [[nodiscard]] virtual std::expected, EngineError> getFonts() const = 0; [[nodiscard]] virtual std::expected, EngineError> extractAnnotationsText() const = 0; struct AnnotationInfo { std::string id; std::string type; double x = 0.0, y = 0.0, width = 0.0, height = 0.0; std::string color; std::string author; std::string content; std::string timestamp; int pageIndex = 0; double thickness = 0.0; std::vector> paths; std::vector> quadPoints; std::string fieldName; std::string fieldValue; std::string fieldType; int fieldFlags = 0; std::vector fieldOptions; }; [[nodiscard]] virtual std::expected, EngineError> extractAnnotations() const = 0; [[nodiscard]] virtual std::expected getGlyphWidth(const std::string& fontName, uint32_t charcode, double fontSize) const = 0; [[nodiscard]] virtual DevicePoint pageToDevice(const Point2D& pagePoint, int deviceWidth, int deviceHeight, int rotate = 0) const noexcept = 0; [[nodiscard]] virtual Point2D deviceToPage(const DevicePoint& devicePoint, int deviceWidth, int deviceHeight, int rotate = 0) const noexcept = 0; [[nodiscard]] virtual std::expected extractDisplayListJson() const = 0; [[nodiscard]] virtual std::expected, EngineError> extractImageXObject(const std::string& name) const = 0; }; namespace fonts::pdf_fonts { class Font; } class PdfDocument { public: virtual ~PdfDocument() = default; [[nodiscard]] static std::expected, EngineError> loadFromFile(const std::string& path, const std::string& password = ""); [[nodiscard]] static std::expected, EngineError> loadFromMemory(const std::vector& data, const std::string& password = ""); [[nodiscard]] virtual int pageCount() const noexcept = 0; [[nodiscard]] virtual DocumentMetadata metadata() const noexcept = 0; [[nodiscard]] virtual DocumentPermissions permissions() const noexcept = 0; struct OutlineItem { std::string title; int pageIndex = -1; int level = 0; }; [[nodiscard]] virtual std::expected, EngineError> extractOutline() const = 0; [[nodiscard]] virtual std::expected, EngineError> getPage(int pageIndex) = 0; [[nodiscard]] virtual std::expected, EngineError> getFonts(int startPage = 0, int endPage = -1) const = 0; [[nodiscard]] virtual std::expected, EngineError> getFontData(const std::string& internalFontId) const = 0; [[nodiscard]] virtual std::expected, EngineError> getReconstructedFontData(const std::string& internalFontId) { (void)internalFontId; return std::unexpected(EngineError::Unknown); } virtual void registerAuxFont(const std::string& internalFontId, const std::vector& sfnt) { (void)internalFontId; (void)sfnt; } [[nodiscard]] virtual std::expected, std::string> getResolvedFont(const FontInfo& fontInfo) = 0; virtual std::expected, EngineError> applyEdits(const std::string& editsJson) = 0; [[nodiscard]] virtual std::string validateLayout(int pageIndex, const std::string& jsonStr) { (void)pageIndex; (void)jsonStr; return "{}"; } [[nodiscard]] virtual std::string lastReflowLayout() const { return {}; } [[nodiscard]] virtual bool lastReflowOverflowed() const { return false; } [[nodiscard]] virtual std::expected, EngineError> saveIncremental() const = 0; [[nodiscard]] virtual std::expected, EngineError> saveFull() const = 0; [[nodiscard]] virtual std::expected, EngineError> saveFullForExport() const { return saveFull(); } }; }