Add FPdfStyledEdit and provide an IPdfRegexEngine to plan and execute replacements against native top-level text objects
PlanStyledPdfRegexReplacements captures a reproducible source snapshot, concatenates actual Unicode glyphs in page and object order, and maps matches back to stable object anchors; generated extraction spaces and line breaks are excluded
ExecuteStyledPdfReplacements validates the snapshot SHA-256, current source identity, anchors and nonoverlapping intervals before changing an independently loaded document
The source identity uses reproducible serialization, including normalized timestamps and identifiers, rather than the original file bytes; the plan stores the actual snapshot bytes and their SHA-256
Native font, text size, color and matrix are reused where possible; replacements spanning objects or pages retain the first object's style and remove consumed characters from later objects while preserving remaining text and page order
FallbackFontName explicitly selects a standard PostScript font when native Unicode cannot be reproduced; ForceFallbackFont requests that path directly
Fallback preserves painter order, matrix, rendering mode, fill and stroke attributes; clipped, transparent, marked or tagged objects are rejected rather than silently losing their graphics semantics
Text wider than its source bounds may shrink horizontally when its matrix is upright and nonsheared; MinHorizontalScale defaults to 0.5, with narrower replacement text retaining the original matrix
This workflow supports single-line replacement text and bounded horizontal fitting; it does not traverse nested Form XObjects, rewrite Tagged PDF semantics or perform paragraph reflow
Signed or encrypted documents and XFA are rejected; cancellation, stale plans, invalid UTF-16, unsupported fonts and exhausted budgets leave the caller document unchanged
PreviewOnly returns the independently reopened result in PreviewPdf without replacing the caller; successful commit replaces the document only after glyph extraction and page count have been verified after saving and reopening
All matches are assembled into final object text before changing native objects; fitting and the minimum scale are checked once against original bounds, preventing repeated matches from accumulating excessive shrinkage
Empty replacement text removes a fully consumed text object after all anchored edits; multiple replacements run backward so earlier source offsets remain valid
Default limits are 256 MiB of document bytes, 10,000 pages, 100,000 text objects, 16,777,216 UTF-16 units and 100,000 replacements; the text-scan work budget is 67,108,864 native character visits and UTF-16 planning-copy units
Hard maxima retain those page, object, character and replacement limits, allow 512 MiB of document bytes and 1,073,741,824 scan visits, and bound a fallback font name to 1,024 bytes
The report includes source and output SHA-256 values, applied replacement, fallback and scaling counts, and per-object original/final text and bounds in PDF user-space points; page and object indexes are zero-based source indexes
A replacement operation is not a security redaction workflow; use the dedicated redaction APIs for removal of sensitive content and associated document data
Options := TPdfStyledEditOptions.Default; Plan := PlanStyledPdfRegexReplacements(Pdf, Pattern, Replacement, Engine, Options); Options.PreviewOnly := True; if not ExecuteStyledPdfReplacements(Pdf, Plan, Options, PreviewPdf, Report) then raise EPdfProduction.Create(Report.ErrorMessage);
type
TPdfStyledEditOptions= record
MaxDocumentBytes: Integer;
MaxPages, MaxTextObjects, MaxCharacters, MaxReplacements: Integer;
MaxTextScanWork: Int64;
MinHorizontalScale: Double;
FallbackFontName: AnsiString;
ForceFallbackFont: Boolean;
PreviewOnly: Boolean;
CancellationToken: IPdfCancellationToken;
class function Default: TPdfStyledEditOptions; static;
end;
TPdfStyledEditPlan= record
SourceSha256: string;
SourceBytes: TBytes;
Spans: TPdfStyledSpans;
Replacements: TPdfStyledReplacements;
end;
TPdfStyledObjectChange= record
PageIndex, ObjectIndex: Integer;
BeforeText, AfterText: WideString;
BeforeBounds, AfterBounds: TPdfRectangle;
HorizontalScale: Double;
FontFallback: Boolean;
end;
TPdfStyledObjectChanges= array of TPdfStyledObjectChange;
TPdfStyledEditReport= record
Succeeded, Cancelled, PreviewOnly: Boolean;
AppliedReplacementCount, FontFallbackCount, ScaledObjectCount: Integer;
SourceSha256, OutputSha256, ErrorMessage: string;
Changes: TPdfStyledObjectChanges;
end;
function PlanStyledPdfRegexReplacements(Pdf: TPdf;
const Pattern, Replacement: WideString; Engine: IPdfRegexEngine;
const Options: TPdfStyledEditOptions): TPdfStyledEditPlan;
function ExecuteStyledPdfReplacements(Pdf: TPdf;
const Plan: TPdfStyledEditPlan; const Options: TPdfStyledEditOptions;
out PreviewPdf: TBytes; out Report: TPdfStyledEditReport): Boolean;