#pragma once #include #include #include #include #include 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 PageImage { int width; int height; std::vector data; }; struct Point2D { double x; double y; }; struct DevicePoint { int x; int y; }; struct GlyphBounds { std::string text; double x; double y; double w; double h; double fontSize; }; // Result of a point hit-test against a page's glyphs (page-point space, top-left // origin — the same space GlyphBounds use). struct HitResult { int glyphIndex = -1; // glyph directly under the point in reading order, -1 if none int caret = 0; // nearest caret position (0..N) for selection anchoring int line = -1; // line band the point resolved to, -1 if the page has no text }; // A resolved text selection: a half-open glyph range plus its reconstructed text // and per-line union rectangles (for drawing the selection). struct TextSelection { int startGlyph = 0; // inclusive, reading order int endGlyph = 0; // exclusive std::string text; std::vector rects; // per-line union rects (text field left empty) }; struct FontInfo { std::string fontName; std::string type; // "TrueType", "Type1", "CIDFontType0", "CIDFontType2" bool isEmbedded = false; bool isSubset = false; bool isVertical = false; // Advanced Introspection & Diagnostics std::string encoding; // "WinAnsiEncoding", "MacRomanEncoding", "Identity-H", "Identity-V", "Symbol", "Custom", "None" bool hasToUnicode = false; // True if font has an active /ToUnicode map std::string cmapName; // e.g. "Identity-H", "Identity-V", "UniJIS-UTF16-H" std::string cidSystemInfo; // e.g. "Adobe-Japan1", "Adobe-GB1", "Adobe-Korea1" std::string subsetTag; // e.g. "ABCDEE" (6-character uppercase tag) std::string sourceType; // "Embedded", "SystemFallback", "Substituted" std::string substitutedFrom; // e.g. "Helvetica" (Original requested font) std::string substitutedTo; // e.g. "Liberation Sans" (Actual fallback font used) std::string normalizedFamily; // e.g. "Arial" (Normalized family grouping name) std::string internalFontId; // Unique stable identifier for internal tracking uint32_t flags = 0; // PDF font descriptor flags double ascent = 0.0; // Font descriptor Ascent metric double descent = 0.0; // Font descriptor Descent metric double capHeight = 0.0; // Font descriptor CapHeight metric }; 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; }; 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; double x = 0.0, y = 0.0, w = 0.0, h = 0.0; }; 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 extractText() const = 0; [[nodiscard]] virtual std::expected, EngineError> extractTextWithBounds() const = 0; // Adobe-grade hit-testing & selection, layered on extractTextWithBounds(). // Concrete (non-virtual) so every page implementation gets them for free. // Coordinates are in page-point space (top-left origin), matching GlyphBounds. // Glyphs in reading order: clustered into lines top-to-bottom, left-to-right. [[nodiscard]] std::expected, EngineError> orderedGlyphs() const; // Nearest glyph/caret to a point. [[nodiscard]] std::expected hitGlyph(double x, double y) const; // Reading-order selection between two points (e.g. drag anchor → focus). [[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; // "highlight", "comment", "ink", "strikeout", "signature", "widget" 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; std::vector> paths; // Form field specific properties 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; }; 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; // A single entry in the document outline (bookmarks), flattened with a depth level. struct OutlineItem { std::string title; int pageIndex = -1; // -1 if the destination page can't be resolved int level = 0; // 0 = top-level }; [[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, std::string> getResolvedFont(const FontInfo& fontInfo) = 0; virtual std::expected applyEdits(const std::string& editsJson) = 0; [[nodiscard]] virtual std::expected, EngineError> saveIncremental() const = 0; [[nodiscard]] virtual std::expected, EngineError> saveFull() const = 0; }; }