feat: implemented multi page reflow

This commit is contained in:
Furqan-14
2026-06-18 16:48:29 +05:30
parent ff0962b111
commit a124a43877
23 changed files with 1559 additions and 890 deletions
+35 -46
View File
@@ -28,23 +28,20 @@ struct DocumentMetadata {
std::string modificationDate;
};
// Encryption + permission state of a loaded document. Booleans are the EFFECTIVE
// permissions for how the doc was opened (an owner-unlocked doc reports all true).
// The engine only surfaces these — enforcement happens in the gateway/app layers.
struct DocumentPermissions {
bool isEncrypted = false;
std::string encryption = "None"; // "None", "RC4-40", "RC4-128", "AES-128", "AES-256"
int securityRevision = -1; // FPDF_GetSecurityHandlerRevision (-1 if unencrypted)
bool ownerUnlocked = false; // encrypted but opened with full (owner) access
std::string encryption = "None";
int securityRevision = -1;
bool ownerUnlocked = false;
bool canPrint = true;
bool canPrintHighRes = true;
bool canModify = true;
bool canCopy = true; // extract text / graphics
bool canAnnotate = true; // add/modify annotations
bool canCopy = true;
bool canAnnotate = true;
bool canFillForms = true;
bool canExtractForAccessibility = true;
bool canAssemble = true; // insert / rotate / delete pages
bool canAssemble = true;
};
struct PageImage {
@@ -72,45 +69,40 @@ struct GlyphBounds {
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
int glyphIndex = -1;
int caret = 0;
int line = -1;
};
// 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
int startGlyph = 0;
int endGlyph = 0;
std::string text;
std::vector<GlyphBounds> rects; // per-line union rects (text field left empty)
std::vector<GlyphBounds> rects;
};
struct FontInfo {
std::string fontName;
std::string type; // "TrueType", "Type1", "CIDFontType0", "CIDFontType2"
std::string type;
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
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 {
@@ -139,7 +131,8 @@ struct TextRun {
std::vector<Glyph> glyphs;
std::vector<int> objectIndices;
double x = 0.0, y = 0.0, w = 0.0, h = 0.0;
std::string fillColor = "#000000"; // hex of the run's fill (or stroke) color, for in-place editing
std::string fillColor = "#000000";
std::string paraId;
};
struct TextLine {
@@ -183,10 +176,8 @@ public:
[[nodiscard]] std::expected<std::vector<GlyphBounds>, EngineError> orderedGlyphs() const;
// Nearest glyph/caret to a point.
[[nodiscard]] std::expected<HitResult, EngineError> hitGlyph(double x, double y) const;
// Reading-order selection between two points (e.g. drag anchor → focus).
[[nodiscard]] std::expected<TextSelection, EngineError>
selectRange(double ax, double ay, double bx, double by) const;
@@ -197,7 +188,7 @@ public:
struct AnnotationInfo {
std::string id;
std::string type; // "highlight", "comment", "ink", "strikeout", "signature", "widget"
std::string type;
double x = 0.0, y = 0.0, width = 0.0, height = 0.0;
std::string color;
std::string author;
@@ -205,8 +196,7 @@ public:
std::string timestamp;
int pageIndex = 0;
std::vector<std::vector<Point2D>> paths;
// Form field specific properties
std::string fieldName;
std::string fieldValue;
std::string fieldType;
@@ -237,11 +227,10 @@ public:
[[nodiscard]] virtual DocumentMetadata metadata() const noexcept = 0;
[[nodiscard]] virtual DocumentPermissions permissions() 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
int pageIndex = -1;
int level = 0;
};
[[nodiscard]] virtual std::expected<std::vector<OutlineItem>, EngineError> extractOutline() const = 0;
@@ -259,10 +248,10 @@ public:
virtual std::expected<void, EngineError> applyEdits(const std::string& editsJson) = 0;
// Per-character caret layout (JSON, PDF coords) of the most recent reflow_paragraph op, or
// empty if none. Lets the WASM live preview align a caret exactly to the rendered glyphs.
[[nodiscard]] virtual std::string lastReflowLayout() const { return {}; }
[[nodiscard]] virtual bool lastReflowOverflowed() const { return false; }
[[nodiscard]] virtual std::expected<std::vector<uint8_t>, EngineError>
saveIncremental() const = 0;