HotPDF Notas de la versión

Version history for HotPDF user-visible features, fixes, compatibility updates, and documentation changes.

Version 2.137.6

  • Added THotPDF.DACopyFile for large-file workflows that only need a page count and an original byte-for-byte saved copy without building the full normal object graph.
  • Improved file-based encrypted PDF loading so password opens no longer cache the whole source PDF just to recover raw encryption dictionary values.
  • Reduced AES-256 stream decryption memory copying by decrypting from existing buffers where possible, including loaded stream memory for large encrypted image streams.
  • The HugeFileBenchmark demo now includes a direct-copy row while the default operation set still covers the full LoadFromFile + SaveLoadedDocument path.

Version 2.137.5

  • Added Direct File API object-source editing for large-file workflows: applications can open a PDF with DAOpenFileReadOnly, inspect object counts and source bodies, replace or append object bodies, update document information, and save a full rewritten copy.
  • Expanded Direct File API indexing to cover traditional xref tables and xref streams, including compressed object entries, so image-heavy PDFs and object-heavy PDFs can use the lightweight path.
  • Optimized Direct File API full rewrites by copying unchanged direct objects in source-offset order and merging contiguous ranges, greatly reducing random I/O on PDFs with hundreds of thousands of objects.
  • The HugeFileBenchmark demo now includes a direct-rewrite operation and documents HTML/CSV benchmark runs for image-only and mixed text/image huge PDFs.

Version 2.137.4

  • La demo Delphi PreflightReport se actualizó de un console sample a una herramienta VCL GUI interactiva para single-file preflight workflows.
  • La GUI ahora cubre informes estándar text, JSON y HTML, contraseñas opcionales, profile INI files, presets integrados, single-file PDF/VT validation, embedded-report PDF output, report preview, status logging, automatic output naming y acceso rápido a los archivos generados.
  • Se eliminó el comportamiento anterior de inicio que creaba automáticamente un sample PDF y un report cuando no se proporcionaban command-line arguments.

2026-05-26 Version 2.137.3

  • PDF/VT validation now recognizes XMP properties written as RDF attributes as well as element text, covering common pdfvtid:GTS_PDFVTVersion, pdfvtid:GTS_PDFVTModDate, and xmp:ModifyDate packet styles.
  • Added Delphi regression coverage that keeps attribute-style PDF/VT metadata detection separate from the remaining structural PDF/VT checks.

2026-05-26 Version 2.137.2

  • Improved embedded preflight report validation so source PDFs that legitimately end with a final line break after %%EOF keep matching their stored fingerprint.
  • Preflight profiles saved as UTF-8 with a BOM now load correctly when the first line is a section header.
  • Hardened PDF literal string escape decoding, improving password validation and unencrypted-copy output for encrypted PDFs with escaped owner or user entries.

2026-05-26 Version 2.137.1

  • Extended CFF parser safety to direct byte-array checks, restoring the reader position after malformed input and clearing stale layout state on failed checks.
  • Hardened the C++Builder wrapper-compatible CFF stream loader so nil stream input returns False and resets the wrapper state instead of dereferencing the stream.

2026-05-26 Version 2.137.0

  • Added THotPDF.ValidatePDFVT with password-aware overloads for focused PDF/VT validation. The text report checks the XMP PDF/VT claim, metadata namespace, PDF/VT modification date matching, PDF/X base marker, output intent, catalog DPartRoot structure, loadable pages, and page-level DPart coverage.
  • The standalone HotPDFPreflight CLI now accepts --pdfvt to write .pdfvt.txt validation reports for a single PDF or recursive directory scan.

2026-05-26 Version 2.136.8

  • Improved Type 1 PFB and CFF parser failure handling so malformed stream checks preserve the input position and clear stale metadata instead of leaving a previous font name visible after a failed check.
  • Added additional Type 1/CFF smoke coverage for malformed CFF and Type 1 streams, including stream-position preservation and stale-metadata cleanup after failed parser calls.

2026-05-26 Version 2.136.7

  • Hardened the Type 1 PFB and CFF parser entry points so stream-based checks preserve the caller's stream position, reject malformed data safely, and expose basic Type 1/CFF font metadata through the Delphi and C++Builder wrapper paths.
  • Expanded the Type 1/CFF smoke test with in-memory CFF data, ASCII Type 1 font data, wrapper-level PFB file loading, and wrapper-level CFF stream loading to protect both Delphi and C++Builder integration surfaces.

2026-05-26 Version 2.136.6

  • Added Delphi feature demos for CJK Unicode text output, large multi-page document generation, PFX signing, and expanded image-compression coverage with CCITT Group 3 1D and 2D output.

2026-05-26 Version 2.136.5

  • Preflight reports now continue when a loaded page entry cannot return a MediaBox. The report marks that page as unavailable instead of aborting, so large or inconsistent page trees still produce diagnostic output.
  • Optimized several byte-level preflight scanners to avoid repeated full-tail string copies and linear duplicate-object searches. The 10,000-page implementation-limit sample now completes within the 120-second stress-test budget.
  • The preflight stress runner now defaults to a 120-second per-file timeout, and the PreflightReport demo rebuilds cleanly after the profile-aware output path.

2026-05-26 Version 2.136.4

  • Added a preflight cookbook, a generated preflight API map, and a large-directory stress-test workflow for the HotPDFPreflight command-line tool. The stress runner isolates each PDF behind a per-file timeout and records JSON, Markdown, CSV, report, and log evidence.
  • Fixed a full rebuild issue in the preflight fingerprint helper by declaring the helper before CreatePreflightReport uses it, so console tools that rebuild HPDFDoc.pas from source compile cleanly.
  • Recorded a 1000-file baseline against D:\PDFdoc\PDF-Samples: 997 passed, 1 failed, 2 timed out, 111.422 seconds elapsed, 8.975 files per second.

2026-05-25 Version 2.136.3

  • The standalone HotPDFPreflight CLI tool gains an --aggregate <file> option that writes a batch summary produced by THotPDF.AggregatePreflightReports after the per-file reports finish. The aggregate lists every processed PDF with status / size / warning counts and totals.
  • Works with single files and recursive directory walks, with or without --profile / --preset. The aggregate is computed from the text body of each report so filtering by profile is reflected in the summary.
  • Example: HotPDFPreflight C:\Archive -r --preset silent-actions -f json -o C:\Archive\reports --aggregate C:\Archive\Aggregate.txt writes JSON reports for every PDF plus a single batch summary.

2026-05-25 Version 2.136.2

  • The standalone HotPDFPreflight CLI tool gains --profile <file> and --preset <name> options to filter each report through a preflight profile before writing it. Works alongside -f text|json|html, -p <password>, and -r recursive directory walks.
  • The two new options are mutually exclusive (passing both, the later one wins). The preset values match what THotPDF.GetBuiltInPreflightProfile recognises so CI pipelines can pull a canonical configuration without staging an INI file.
  • Example: HotPDFPreflight C:\Archive -r --preset silent-actions -f json -o C:\Archive\reports writes JSON reports for every PDF in the directory tree with the silent-actions preset applied.

2026-05-25 Version 2.136.1

  • The Delphi PreflightReport demo now accepts profile=<file> or preset=<name> as an additional CLI argument to filter the report through a profile before writing it. Works alongside the existing json / html / embed output format flag.
  • Examples: PreflightReport.exe Input.pdf Report.txt "" text profile=tuned.ini or preset=compact / preset=silent-actions.
  • The preset values mirror the names recognised by THotPDF.GetBuiltInPreflightProfile so the demo doubles as a profile testbed without having to author an INI file.

2026-05-25 Version 2.136.0

  • Added THotPDF.MergePreflightProfiles for layering profiles. The result is the deduplicated union of both inputs (DisableChecks and DisableWarnings are merged, DisableHints is the logical OR), useful for combining a preset such as compact with project-specific tweaks.
  • Added THotPDF.DiffPreflightProfiles for structural comparison. Returns True when two profiles are equivalent, otherwise the OnlyInA and OnlyInB out parameters list the entries exclusive to each side as newline-separated check:<name> / warn:<name> / option:hints=false tokens.
  • The two helpers complete the profile lifecycle: Load / GetBuiltIn / Save / Apply / Validate / Merge / Diff.

2026-05-25 Version 2.135.0

  • Added THotPDF.CreatePreflightReportWithProfile, a one-stop convenience wrapper that composes CreatePreflightReport, LoadPreflightProfile, ApplyPreflightProfile, and the format converters into a single call.
  • The new overload accepts a source PDF, optional password, optional profile file, and target format (pfText, pfJSON, or pfHTML) and returns the final report body. Passing an empty ProfileFile skips the profile step entirely so callers can keep a single call site regardless of whether they have a profile configured.
  • The four underlying APIs remain available so existing call chains keep working unchanged; the new wrapper is purely additive.

2026-05-25 Version 2.134.0

  • Added THotPDF.SavePreflightProfile for writing a THPDFPreflightProfile record back to an INI file. The output format matches what LoadPreflightProfile consumes, so the two functions are exact inverses for well-formed profiles.
  • Enables a 'load preset, tweak, save' workflow: pull a built-in preset with GetBuiltInPreflightProfile (v2.133.0), modify the lists or flags, then SavePreflightProfile the result for later reuse or sharing across projects.
  • Sections with no content are omitted from the output so an empty profile produces a single comment line instead of three empty section headers.

2026-05-25 Version 2.133.0

  • Added THotPDF.GetBuiltInPreflightProfile, which returns ready-to-use THPDFPreflightProfile records for common workflows so callers do not have to maintain INI files for canonical configurations.
  • Recognised names (case-insensitive): default or empty string returns an empty profile; compact disables every Hint line for shorter reports; silent-actions disables every PDF 1.7 sec 12.6.4 action warning plus EmbeddedFile and RichMedia, intended for workflows that intentionally embed multimedia or interactive actions.
  • Unknown names also return an empty profile so the function never raises on a typo; pair with ValidatePreflightProfile (v2.132.0) when the caller wants typos surfaced as errors.

2026-05-25 Version 2.132.0

  • Added THotPDF.ValidatePreflightProfile for defensive profile checking. The function walks the loaded profile's DisableChecks and DisableWarnings lists and flags any name that is not recognised by the current preflight implementation, returning the unknown names joined by ', ' in the UnknownNames out parameter.
  • Useful to detect profile files authored against a newer or older HotPDF version. Without validation a typo (OpneAction instead of OpenAction) or a renamed check would silently disable nothing.
  • The check is purely defensive; the profile itself is not modified. Callers can decide whether to abort, log a warning, or proceed with the partially-effective profile.

2026-05-25 Version 2.131.0

  • Added THotPDF.ConvertPreflightReportToVeraPDFStyle, a converter that shapes a HotPDF preflight report into a JSON document modeled on veraPDF's validation output: top-level profile, a jobs array containing itemDetails / taskResult / validationResult, plus a ruleViolations array under validationResult.details.
  • HotPDF-styled rather than wire-compatible with veraPDF; the goal is to let downstream tooling that already consumes veraPDF JSON adapt to HotPDF output with minimal field-name remapping rather than re-learning a different data layout.
  • Rule violations carry the same specification ISO clause cross-reference that the native HotPDF JSON output emits, so callers retain spec-level traceability when consuming via the veraPDF-style path.

2026-05-25 Version 2.130.0

  • Added THotPDF.EmbedPreflightReportAsXMP with two overloads (with and without an optional Password argument). The function writes a copy of the source PDF with the preflight report appended as a PDF-style comment block whose payload is XMP / RDF with the xmlns:hotpdf namespace.
  • Each report line becomes a hotpdf:<name> element. PASS / FAIL checks carry the severity as an attribute; warnings, hints, and info lines use warn, hint, and info prefixes on the element name.
  • XMP-aware tools that scan a file for xpacket markers can surface the embedded report. PDF readers continue to ignore the appended bytes because they fall outside the object graph. This is archival-friendly XMP embedding, not a spec-compliant XMP integration; the XMP payload is not referenced from the catalog /Metadata entry.

2026-05-25 Version 2.129.0

  • Added THotPDF.LoadPreflightProfile and THotPDF.ApplyPreflightProfile with a new THPDFPreflightProfile record. Profiles let callers tailor the report output without re-running the analysis, suppressing specific check / warning names or every Hint line.
  • Profile files use a simple INI-style format with [disable-checks], [disable-warnings], and [options] sections. Comments and blank lines are ignored. Empty profiles pass the report through unchanged.
  • After filtering, the trailing Summary and Warnings lines are recomputed so the suppressed entries no longer contribute to the Failed and Warnings totals. The rest of the report passes through unchanged, so JSON / HTML converters, diff helper, validate / embed / aggregate APIs continue to work on the filtered report without code changes.

2026-05-25 Version 2.128.2

  • Added a Delphi PreflightDashboard console demo that scans a directory of PDFs and writes a self-contained static HTML dashboard: one <stem>.preflight.html per file plus an index.html summary table.
  • The dashboard shows total / passed / failed counts at the top and a table of every PDF with status, size, summary, and a link to the detailed report. Failed rows are highlighted; PASS and FAIL badges are colour-coded for quick scanning.
  • Output is a static directory of files (no HTTP server required); publish the directory to a CI artifact page, serve it from any web server, or open index.html directly in a browser.

2026-05-25 Version 2.128.1

  • Added tools/validate-preflight-json.py, a Python utility that validates HotPDF preflight JSON output against the published schema (Docs/preflight-schema.json). The script reports one PASS/FAIL line per input and exits with a non-zero status when any input fails validation, so CI pipelines can gate on schema conformance.
  • Validation is strict because the schema declares additionalProperties: false on every object, so unknown fields cause a hard failure rather than silently slipping through. Required dependency is the standard jsonschema Python package.

2026-05-25 Version 2.128.0

  • Added THotPDF.AggregatePreflightReports for batch summaries. The function accepts an array of per-file preflight reports and emits a single aggregate that lists each file's pass/fail status with byte size and warning count, plus totals for passed, failed, warnings, and bytes scanned.
  • Pairs naturally with the v2.125.1 HotPDFPreflight standalone CLI: run the CLI to produce one report per PDF, then feed the report bodies into AggregatePreflightReports for a dashboard-ready batch summary.
  • Empty input arrays yield an empty result string so callers can append the aggregate to other output unconditionally without guarding against zero-file directories.

2026-05-25 Version 2.127.0

  • Added THotPDF.RepairPDFFromPreflightReport with two overloads (with and without an optional Password argument). The function applies a conservative subset of byte-level repairs to damaged PDFs: it drops trailing bytes that follow the final %%EOF marker, and appends a missing %%EOF when one is not present anywhere.
  • Returns True when at least one repair was applied; RepairsApplied is an out parameter that lists the repairs one per line, so callers can decide whether to keep the repaired file or escalate to a heavier recovery tool.
  • Object-graph repairs (rebuilding xref tables, patching stream lengths, fixing trailers) are intentionally out of scope because such fixes can make a partially-recoverable file less recoverable. The repairs in this overload are limited to operations that are unambiguously safe on well-formed and lightly-damaged PDFs alike.

2026-05-25 Version 2.126.1

  • Published a formal JSON Schema for the preflight JSON output at Docs/preflight-schema.json, conforming to JSON Schema draft 2020-12.
  • The schema documents every top-level field (input, size, pdfVersion, xrefStyle), the checks / info / hints / warnings arrays, and the summary object so consumer pipelines can validate the JSON output before parsing.
  • The help topic now points downstream consumers at the schema file for reference.

2026-05-25 Version 2.126.0

  • Added THotPDF.ComparePreflightReports for line-by-line preflight report comparison. The function emits a unified-diff-like body where shared lines use a two-space prefix, lines unique to ReportA use , and lines unique to ReportB use .
  • Pairs naturally with LoadAndValidatePreflightReport: when a roundtrip validation fails, feed the original and current reports into ComparePreflightReports to surface exactly which lines changed.
  • The diff algorithm is a small greedy walk tuned for the deterministic key/value output of CreatePreflightReport; it produces compact diffs for typical inputs without pulling in a general-purpose LCS implementation.

2026-05-25 Version 2.125.1

  • Added a standalone Delphi console tool HotPDFPreflight for batch-running the preflight report against single files or whole directories. The tool accepts -o output directory, -f text|json|html output format, -p password, -r recursive directory walk, and -v verbose progress flags.
  • Exit codes follow standard CLI conventions: 0 success, 1 usage error, 2 input not found, 3 one or more PDFs failed preflight, so the tool can be integrated into CI pipelines and shell scripts.
  • Each report is named <basename>.preflight.<ext> with the extension picked from the requested format.

2026-05-25 Version 2.125.0

  • Added THotPDF.LoadAndValidatePreflightReport with two overloads (with and without an optional Password argument). The function extracts the report that EmbedPreflightReportInPDF previously appended, re-runs the current preflight algorithm against the source bytes, and returns True when the embedded InputFingerprint still matches the freshly-generated one.
  • Out parameters OriginalReport and CurrentReport receive both reports so callers can diff them when validation fails, identifying what changed between the time the report was embedded and the validation run.
  • Pairs naturally with EmbedPreflightReportInPDF: embed once during archival, validate any time later to detect tampering or accidental modification.

2026-05-25 Version 2.124.2

  • Added two fingerprint lines to THotPDF.CreatePreflightReport for CI stability checks. InputFingerprint emits a 64-bit FNV-1a hash of the source PDF bytes as 16 lowercase hex digits, immediately after the Size: line. ReportFingerprint emits a 64-bit FNV-1a hash of the assembled report (everything above the fingerprint line) at the end of the report.
  • CI pipelines can compare fingerprints across runs to detect unintended drift in either the input file or the report content without parsing every diagnostic line.
  • Both fingerprints are non-cryptographic; FNV-1a is deliberately chosen for low overhead and inline implementation without pulling in an external hashing module.
  • JSON output places InputFingerprint in the info array and ReportFingerprint at the tail of info; both entries carry a spec field labelled "FNV-1a 64-bit (non-cryptographic)".

2026-05-25 Version 2.124.1

  • Added two conditional incremental-update revision diagnostic lines to THotPDF.CreatePreflightReport. When IncrementalUpdates > 0, the report now includes RevisionStartXRefPositions (byte offsets of each startxref keyword in the file, ordered oldest first) and RevisionXRefTargets (the xref offsets each marker points to), so callers can trace the incremental update chain without parsing the file manually.
  • Clean and single-revision PDFs remain byte-stable: the new lines are emitted only when more than one startxref marker is found.
  • ISO 32000-1 sec 7.5.6 spec references are added in the JSON output for the new fields.

2026-05-25 Version 2.124.0

  • Added THotPDF.EmbedPreflightReportInPDF with two overloads, one with an optional Password argument for supported encrypted PDFs. The method writes a copy of the source PDF with the preflight report appended after the last %%EOF as PDF-style comment lines.
  • The report travels with the document for archive workflows: text editors, grep, and audit tools can surface it long after the file leaves the originating workstation.
  • PDF readers continue to render the document as-is because the appended bytes fall outside the object graph; the cross-reference table and trailer dictionary are untouched.
  • The Delphi PreflightReport demo gains an embed CLI argument that writes the embedded variant alongside the standalone report.

2026-05-25 Version 2.123.1

  • Added a regression test that hand-crafts a PDF with duplicate object numbers, a malformed xref row, and an unbalanced stream/endstream pair, then verifies that the three Phase F root-cause diagnostic lines (DuplicateObjectList, FirstMalformedXRefOffset, StreamEndStreamDelta) trigger as expected.
  • No library behaviour change: the test fills a gap in the regression suite that previously only covered the clean and "not a PDF" cases.

2026-05-25 Version 2.123.0

  • Added HTML output format to THotPDF.CreatePreflightReport and THotPDF.SavePreflightReport. The THPDFPreflightFormat enum gains a third value pfHTML alongside pfText and pfJSON.
  • HTML output is a self-contained dashboard-style document with inline CSS, severity-coloured rows (green for PASS, red for FAIL, yellow for WARN), per-section tables for checks, hints, info, and warnings, plus a summary card. No external CSS, JavaScript, or fonts are required.
  • Each table row includes the ISO 32000-1 / ISO 19005 / ISO 15930 / ISO 14289 spec reference when available, matching the JSON output spec field added in v2.122.1.
  • HTML output is byte-safe: ampersand, less-than, greater-than, double-quote, and single-quote are escaped; UTF-8 byte sequences pass through unchanged.
  • The Delphi PreflightReport demo accepts html as the fourth CLI argument to request HTML output.

2026-05-25 Version 2.122.4

  • Added PDF/X subset detection to preflight reports. When a PDF/X claim is detected, the new Hint PDFXVersion line names the specific subset (X-1a:2001, X-1a:2003, X-3:2002, X-3:2003, X-4, X-4p, X-5g, X-5pg, X-5n, or X-5).
  • Added PDF/UA part extraction. When a PDF/UA claim is detected, the new Hint PDFUAPart line reports the value from pdfuaid:part in the XMP metadata.
  • ISO 15930 and ISO 14289 spec cross-references are added to the JSON output for the new hint entries.
  • Clean PDFs with no PDF/X or PDF/UA claim remain byte-stable: the new hints are only emitted when the underlying claim is detected.

2026-05-25 Version 2.122.3

  • Added root-cause diagnostic lines to THotPDF.CreatePreflightReport. When a damaged PDF triggers a defect count, the report now lists actionable detail.
  • DuplicateObjectList appears whenever DuplicateObjectNumbers > 0 and lists up to five repeated object numbers, so callers can pinpoint which objects collide.
  • FirstMalformedXRefOffset appears whenever XRefMalformedEntries > 0 and gives the 1-based byte offset of the first malformed cross-reference row.
  • StreamEndStreamDelta appears whenever the stream and endstream marker counts differ and gives the signed difference, indicating how many markers are missing or duplicated.
  • Clean PDFs remain byte-stable: the new diagnostic lines are only emitted when the underlying defect counters are non-zero.

2026-05-25 Version 2.122.2

  • Extended preflight action warnings to cover the full PDF 1.7 §12.6.4 action set. Added 15 advisory warnings: GoToRAction, GoToEAction, ThreadAction, URIAction, SoundAction, MovieAction, HideAction, NamedAction, SubmitFormAction, ResetFormAction, ImportDataAction, SetOCGStateAction, RenditionAction, TransAction, and GoTo3DViewAction.
  • Warnings are emitted only when the corresponding action token is detected; clean PDFs that contain no actions stay byte-stable.
  • The ISO 32000-1 spec cross-references in the JSON output now cover the new action warnings as well.

2026-05-25 Version 2.122.1

  • Extended THotPDF.CreatePreflightReport JSON output with ISO specification cross-references. Every checks, hints, and warnings entry now carries a spec field naming the ISO 32000-1, ISO 19005, ISO 15930, or ISO 14289 clause for that diagnostic.
  • The plain-text report is unchanged and remains byte-stable; the spec field appears only in pfJSON output.
  • The mapping covers every check, hint, and warning emitted by the report through v2.122.0, so JSON consumers can route findings to the matching spec clause without an external lookup table.

2026-05-25 Version 2.122.0

  • Added machine-readable JSON output to THotPDF.CreatePreflightReport and THotPDF.SavePreflightReport. New THPDFPreflightFormat enum exposes pfText (default plain-text path) and pfJSON (CI-friendly JSON document) values.
  • New format-aware overloads accept the Format parameter while keeping the legacy text overloads byte-stable. Existing applications continue to work without code changes.
  • JSON output groups entries into top-level input, size, pdfVersion, and xrefStyle fields plus checks, info, hints, warnings arrays and a summary object with failed, warnings, and result counters.
  • Built-in JSON encoder escapes ", \, control bytes, and preserves UTF-8 byte sequences without requiring an external JSON library.
  • The Delphi PreflightReport demo accepts a fourth CLI argument json to request JSON output for an input PDF.

2026-05-25 Version 2.121.36

  • Enhanced preflight reports with compliance marker hints. Reports now expose Hint PDFAClaimed, Hint PDFAPart, Hint PDFAConformance, Hint PDFXClaimed, Hint PDFUAClaimed, Hint TaggedPDF, and Hint HasTransparency lines for downstream tooling.
  • Added advisory warnings PDFAWithEncryption, PDFAWithJavaScript, and PDFA1WithTransparency that fire when a PDF/A-claimed document also carries encryption, JavaScript, or PDF/A-1-forbidden transparency markers.
  • Hint lines never increment the Failed counter, and the helpers remain advisory; they do not replace a full PDF/A, PDF/X, PDF/UA, or ISO 32000 conformance engine.

2026-05-25 Version 2.121.35

  • Enhanced preflight reports with content resource integrity diagnostics. Reports now include resource dictionary, font, embedded font program, image XObject, form XObject, ColorSpace, annotation, widget, and link counts.
  • Reports now include filter chain usage counts for FlateDecode, DCTDecode, CCITTFaxDecode, JBIG2Decode, LZWDecode, ASCIIHexDecode, ASCII85Decode, RunLengthDecode, and JPXDecode.
  • Added pass/fail check FontsHaveEmbeddedPrograms that verifies at least one /FontFile, /FontFile2, or /FontFile3 is present whenever font resources are declared.
  • Added pass/fail check AnnotationCountConsistent that verifies the sum of widget and link annotation counts does not exceed the overall annotation count.

2026-05-25 Version 2.121.34

  • Enhanced preflight reports with trailer root type diagnostics. Reports now verify that the trailer /Root indirect reference targets an object declared as /Type /Catalog.

2026-05-25 Version 2.121.33

  • Enhanced preflight reports with trailer ID diagnostics. Reports now count hex string entries in the trailer /ID array and verify that the file identifier is present as a valid two-entry pair when an ID array is supplied.

2026-05-25 Version 2.121.32

  • Enhanced preflight reports with trailer indirect-reference diagnostics. Reports now show the trailer /Root and /Info object references and verify that the referenced objects are defined in the file.

2026-05-25 Version 2.121.31

  • Enhanced preflight reports with stream length coverage diagnostics. Reports now count stream /Length entries and verify that detected streams have length entries available.

2026-05-25 Version 2.121.30

  • Enhanced preflight reports with revision marker diagnostics. Reports now count %%EOF markers and startxref sections, verify that the counts are balanced, and check that the final startxref marker appears before the final EOF marker.

2026-05-25 Version 2.121.29

  • Enhanced preflight reports with page tree consistency diagnostics. Reports now include the declared page tree /Count and pass/fail checks that compare it, and the counted page objects, with the pages loaded by HotPDF.

2026-05-25 Version 2.121.28

  • Enhanced preflight reports with xref table row diagnostics. Reports now include xref subsection, entry, free-entry, in-use-entry, and malformed-row counts, plus pass/fail checks for xref row syntax and whether xref coverage reaches the highest object number.

2026-05-25 Version 2.121.27

  • Enhanced preflight reports with bounded PDF name-pair counting and additional consistency checks. Reports now distinguish catalog, page tree, and page objects; verify that object numbers are unique; and check whether trailer /Size covers the highest object number and whether trailer /Root is present.

2026-05-25 Version 2.121.26

  • Enhanced the preflight report object and trailer diagnostics. Reports now include indirect object definition counts, highest object number, duplicate object number count, balanced stream/endstream checks, and trailer details for /Size, /Root, /Info, /ID, and /Encrypt.

2026-05-25 Version 2.121.25

  • Enhanced THotPDF.CreatePreflightReport and THotPDF.SavePreflightReport with deeper cross-reference diagnostics. Reports now verify that the final %%EOF marker is near the end of the file, parse the last startxref offset, identify whether it targets an xref table or xref stream, and include xref table, xref stream, object stream, trailer, incremental update, and linearization counts.

2026-05-25 Version 2.121.24

  • Expanded the preflight report helpers with password-aware overloads and richer diagnostics. Reports now include the PDF header version, xref style, resolved loaded page MediaBox entries, form field count, stream count, feature presence flags, JavaScript/action/media attachment warnings, warning totals, and pass/fail summaries for damaged files. The Delphi PreflightReport demo accepts an optional password argument for supported encrypted PDFs.

2026-05-25 Version 2.121.23

  • Added library-level preflight report helpers with THotPDF.CreatePreflightReport and THotPDF.SavePreflightReport. Applications can now generate a text summary for an input PDF covering the header, EOF marker, startxref, trailer or XRef stream, loadable page count, encryption state, catalog, page tree, page object, page box, information dictionary, root reference, indirect object count, and overall pass/fail status. The Delphi PreflightReport demo now uses these APIs directly.

2026-05-25 Version 2.121.22

  • Added a Delphi PreflightReport console demo that creates a sample PDF and writes a text report with lightweight structural checks, including the PDF header, EOF marker, loadable page count, encryption state, catalog, page tree, page box, and document information dictionary. The workflow keeps AutoLaunch disabled and can also report on an existing input PDF from the command line.

2026-05-25 Version 2.121.21

  • Added a Delphi SearchAndSelect console demo that builds a report, searches controlled text rows, and marks every matching term with PDF highlight annotations and /QuadPoints. The sample keeps AutoLaunch disabled so it is suitable for command-line and automated validation.

2026-05-25 Version 2.121.20

  • Added password loading for RC4-40 and RC4-128 Standard encrypted PDFs. Applications can now call LoadFromFile or LoadFromStream with a password, or call DecryptLoadedDocument after loading, then save an unencrypted loaded-document copy with SaveLoadedDocument. The new Delphi DecryptPDF console demo creates a password-protected sample and writes a decrypted copy without launching a PDF viewer.

2026-05-25 Version 2.121.19

  • Added a Delphi Printer console demo that generates a print-ready PDF preset. The sample writes print-focused /ViewerPreferences, page BleedBox and TrimBox entries, printable annotation flags, and a print-control ExtGState without launching a PDF viewer or sending a job to a physical printer.

2026-05-25 Version 2.121.18

  • Added a Delphi ConvertTo console demo that converts supported source graphics into PDF output files. The workflow creates separate PDF files from JPEG, BMP, TIFF, and EMF inputs with AutoLaunch disabled, making the sample suitable for command-line and automated checks.

2026-05-25 Version 2.121.17

  • Added loaded page operation helpers with InsertPagesFromDocument, ExtractPagesToFile, and MovePage. Applications can now insert pages from another loaded document, extract selected pages to a new PDF, and reorder loaded pages while keeping subsequent saves and extraction calls in the updated page order. The Delphi PageOperations demo shows the insert, extract, and reorder workflow without auto-launching a PDF viewer.

2026-05-25 Version 2.121.16

  • Expanded loaded AcroForm editing with RemoveFormField and FlattenFormFields. Applications can now remove loaded fields by index or name, or flatten loaded fields into static page content while removing the interactive AcroForm tree and widget annotations. The Delphi FormFields demo now shows descriptions, removal, and flattening without auto-launching a PDF viewer.

2026-05-25 Version 2.121.15

  • Added loaded bookmark title search with THotPDF.FindLoadedBookmarkPageIndex. Applications can now load an existing PDF, search the outline tree by bookmark title, and resolve matching /Dest or local GoTo action destinations to zero-based page indexes.

2026-05-24 Version 2.121.14

  • Added THPDFPage.AddTextWatermark for drawing rotated transparent text watermarks on generated or loaded pages. The new Delphi Watermark demo creates a source PDF, loads it, applies the watermark to every page, and saves the edited document.

2026-05-24 Version 2.121.13

  • Added loaded AcroForm field description inspection with GetFormFieldDescription. Applications can now read a loaded field's /TU tooltip text by index or name alongside field values, options, read-only state, and rename operations.

2026-05-24 Version 2.121.12

  • Added loaded choice-field option inspection with GetFormFieldOptionCount and GetFormFieldOptionValue. Applications can now load an existing combo box or list box field, enumerate its allowed /Opt values, and continue using the same loaded-form update and save workflow.

2026-05-24 Version 2.121.11

  • Expanded loaded AcroForm editing with IsFormFieldReadOnly and RenameFormField. Applications can now inspect a loaded field's read-only flag, rename fields by index or name, save the loaded document, and reload it with the updated field names intact.

2026-05-24 Version 2.121.10

  • Added THotPDF.GetLoadedPageBox for inspecting page boundaries after LoadFromFile. Applications can now read a loaded page's MediaBox, CropBox, BleedBox, TrimBox, and ArtBox, including the standard fallback chain for boxes that are not declared directly.

2026-05-24 Version 2.121.9

  • Added loaded AcroForm field inspection and update APIs. Applications can now load an existing form PDF, enumerate field names and types, read and update field values, toggle a field's read-only flag, and save the loaded document with SaveLoadedDocument. The new Delphi FormFields demo shows the full load, edit, and save workflow.

2026-05-24 Version 2.121.8

  • Added THotPDF.RegisterLineGraphicsState for reusable line drawing ExtGState resources. Applications can now register line width, cap style, join style, and miter limit once, then apply the named state with THPDFPage.SetGraphicsState before stroking paths.

2026-05-24 Version 2.121.7

  • Added annotation interaction helpers for writing annotation /F flags and link /H highlight modes. Link helper methods now update LastAnnotation, so flags, highlight modes, border styles, and popup helpers can be chained consistently after link creation.

2026-05-24 Version 2.121.6

  • Added reusable Form XObject authoring APIs with page-level drawing helpers, including placement, scaling, and rotation support. Added current-transformation-matrix convenience helpers and bounded text helpers for single-line alignment, wrapped text output, and row-count calculation.

2026-05-24 Version 2.121.5

  • Added reusable Type 1 PFB and CFF parser units for HotPDF, including byte-array readers, Type 1 glyph-outline command extraction, CFF dictionary, INDEX, charset, and encoding readers, plus Type 1 PFB metadata and glyph lookup helpers. Delphi and C++Builder package projects now include the new parser units.

2026-05-24 Version 2.121.4

  • PDFACompliance='2A' and PDFACompliance='3A' now enable Tagged PDF basics without implicitly emitting a PDF/UA-1 XMP claim. PDF/A Level A files keep /MarkInfo, /StructTreeRoot, document title, language, and archival metadata, while pdfuaid metadata is emitted only when PDFUACompliance is explicitly enabled.
  • The PDF/A-2/3 smoke coverage now includes PDF/A-3A and asserts that Level A archival output does not advertise PDF/UA unless requested separately. The generated PDF/A-2A and PDF/A-3A smoke files pass veraPDF validation.

2026-05-24 Version 2.121.3

  • THPDFPage.UnicodeTextOut ahora reutiliza el recurso de fuente Type 0 generado por RegisterUnicodeTTF para la salida Unicode de planos suplementarios, de modo que el texto de página y los flujos de apariencia AcroForm comparten la misma ruta de codificación CID interna.
  • La pasada de finalización compartida mantiene ahora sincronizada la salida SMP de página mediante FUnicodeUsedCps, /CIDToGIDMap, la entrada /W de la fuente descendiente y /ToUnicode, evitando el antiguo mapa SMP local de página.

2026-05-24 Version 2.121.2

  • THPDFPage.UnicodeTextOut ahora dirige los caracteres Unicode del plano suplementario a través del mapeo de glifos de formato 12 registrado en la fuente de la página cuando RegisterUnicodeTTF ha cargado la fuente activa. Los emojis y otros valores escalares U+10000+ se emiten como un solo glifo CID en el flujo de texto de la página en lugar de dos CIDs de medio sustituto.
  • Los registros de la tabla de mapeo `/ToUnicode` para las fuentes de la página convierten los CID de los glifos SMP de nuevo al par de sustitutos UTF-16BE original, preservando la extracción de texto y la accesibilidad, al tiempo que se mantiene la ruta de fuente de página existente `/CIDToGIDMap /Identity`.

2026-05-24 Version 2.121.1

  • Los flujos de apariencia de AcroForm respaldados por RegisterUnicodeTTF ahora renderizan caracteres Unicode del plano suplementario, como emoji, mediante un único CID interno en lugar de escribir un par sustituto UTF-16 como dos CID Identity-H. El /CIDToGIDMap generado apunta ese CID al glyph real, y /ToUnicode lo asigna de vuelta al par sustituto UTF-16BE original para la extracción de texto.
  • La fase de finalización de la fuente Unicode actualiza /CIDToGIDMap, /W y /ToUnicode después de que todos los flujos de apariencia hayan registrado sus asignaciones internas tardías de CID, de modo que el subsetting TrueType y las apariencias de formulario generadas utilicen el conjunto final de CID usados.

2026-05-23 Version 2.121.0

  • PDF 1.7 §12.6.4.13–15: las acciones multimedia (Rendición / Sonido / Película) ahora incluyen tres nuevos métodos auxiliares para botones en `THPDFPage`: `AddPushButtonWithRenditionAction` para §12.6.4.13 (asocia una anotación de pantalla a un MediaRendition + MediaClipData que describe el clip de audio/video y una operación /OP de 0 a 4); `AddPushButtonWithSoundAction` para §12.6.4.14, con dos sobrecargas (ruta de archivo o carga útil `TBytes` sin procesar), un registro `THPDFSoundParams` proporcionado por el llamador (Frecuencia de muestreo / Canales / Bits por muestra / Codificación) y opcionalmente /Volumen + /Repetir + /Sincrónico; `AddPushButtonWithMovieAction` para §12.6.4.15 (referencia una anotación de película a través de /Annotation y selecciona /Operación Reproducir / Detener / Pausar / Reanudar).
  • AddScreenAnnotation y AddMovieAnnotation ahora devuelven el diccionario de anotación creado, para que los que las llamen puedan encadenarlo al helper de acción correspondiente. El cambio de firma de `procedure` a `function` es compatible con la ABI de Delphi; las llamadas existentes que las invocan y descartan el resultado aún se compilan y generan archivos PDF idénticos en bytes.
  • Implementación estándar: cada función auxiliar genera las entradas obligatorias especificadas en la norma, además de los campos opcionales más comunes (Sonido/Volumen/Repetición/Sincronización; Representación/JS). Los parámetros de reproducción multimedia, los parámetros de pantalla multimedia, la representación del selector y la mezcla de sonido se omiten deliberadamente; los valores predeterminados del lector cubren el 95% de los casos, y las funciones auxiliares se mantienen lo más simples posible.
  • PDF/A (todos los niveles) y PDF/X (todos los perfiles) rechazan las tres acciones multimedia en el punto de entrada del helper, mostrando un diagnóstico referenciado por la norma ISO (ISO 19005-1 §6.6.1 / ISO 19005-2 §6.5.1 / ISO 19005-3 §6.5.1 / ISO 15930). Las funciones de anotación existentes, `AddScreenAnnotation`, `AddSoundAnnotation` y `AddMovieAnnotation`, permanecen activas.
  • La acción Rendition asocia el archivo de especificaciones de datos de clip multimedia con los permisos de medios /P /TF (TEMPACCESS) según la sección §13.2.6.1, Tabla 280, que es el nivel de permiso mínimo que permite al lector pasar el contenido a los códecs de audio/video de la plataforma.
  • Los flujos de acción de sonido establecen los valores predeterminados para los campos opcionales según las especificaciones (Volumen=1.0, Repetición=falso, Sincrónico=falso) y los emiten solo cuando el llamador proporciona valores distintos a los predeterminados, manteniendo el diccionario de acción lo más pequeño posible para el caso común.
  • Sound stream metadata (/R sample rate / /C channels / /B bits per sample / /E encoding) is caller-supplied via the new THPDFSoundParams record. The v2.16-era AddSoundAnnotation entry point still hard-codes 22050 / 16-bit / Stereo / Signed; the new Sound action path avoids that pitfall so callers can play 44.1 kHz mp3, 8 kHz µ-law telephony audio, or any other source at the correct rate.
  • Verificado con las nuevas versiones de Win32 y Win64. Las pruebas `smoke_multimedia_actions` (que cubren todas las variantes de acciones y la concatenación de anotaciones de pantalla/película) y `smoke_multimedia_actions_pdfa_pdfx` (que incluyen 8 aserciones que cubren 3 acciones × PDF/A + PDF/X) se completaron con éxito. La prueba de regresión `smoke_button_actions` sigue pasando después de la refactorización de los procedimientos `AddScreenAnnotation` y `AddMovieAnnotation` en funciones.

2026-05-23 Version 2.120.15

  • Corrección: EndDoc serializaba el grafo de objetos PDF antes de que se ejecutaran dos modificadores posteriores a la compilación (el subsetter de TrueType de la versión v2.84.0 y el auto-estampador de PDF/UA-1 §7.18.3 /Tabs /S de la versión v2.94.0), por lo que ninguno de los cambios se aplicó al archivo resultante. Los documentos que llamaban a RegisterUnicodeTTF + AddTextField incluían el archivo de fuente completo (~1 MB en latín / ~14 MB en CJK) en lugar del subset (~150 KB / ~500 KB), faltaba el prefijo de subset de seis letras "AAAAAA" en /BaseFont, el flujo /CIDSet de PDF/A en el FontDescriptor estaba ausente (veraPDF marcó una falla de ISO 19005-1 §6.3.5 / -2 -3 §6.2.11), y los archivos PDF/UA-1 que no tenían la clave §7.18.3 /Tabs /S en las páginas con anotaciones fallaban la regla 21-001 de Matterhorn y la prueba PAC 3 "Tabs".
  • Ambos modificadores ahora se ejecutan ANTES de SaveToStream / SaveToFile. Recorren la lista de objetos indirectos y escriben el PDF. La salida es idéntica en bytes a la versión v2.120.14 para documentos que no registran una fuente Unicode TTF ni habilitan PDFUACompliance; ambos modificadores ya eran operaciones no efectivas en esos casos, por lo que el cambio de orden solo afecta a los documentos cuya salida en la versión v2.120.14 se corrompió silenciosamente.
  • Verificado con las pruebas Win32 smoke_unicode_ttf_subset (el subconjunto representa el 33.6% de Arial en disco; se ahorran 676 KB; /BaseFont el prefijo UYENHH+ArialMT es consistente en Type 0 / CIDFontType2 / FontDescriptor.FontName) y las pruebas Win32 smoke_pdfua_annot_structparents (/Tabs /S está estampado en el diccionario de la página anotada; las cinco aserciones de estructura de anotaciones §7.18.3 y las conexiones son correctas).

2026-05-23 Version 2.120.14

  • PDF/UA-1: se ha implementado una estricta validación de la estructura y el rol (ISO 14289-1 §7.7 + ISO 32000-1 §14.7.3). Con PDFUACompliance habilitado, AddStructureElement ahora rechaza cualquier rol "/S" que no sea uno de los 41 tipos de estructura estándar definidos en PDF 1.7 §14.8.4, Tabla 333 (Document / Part / Art / Sect / Div / BlockQuote / Caption / TOC / TOCI / Index / NonStruct / Private / H / H1..H6 / P / L / LI / Lbl / LBody / Table / TR / TH / TD / THead / TBody / TFoot / Span / Quote / Note / Reference / BibEntry / Code / Link / Annot / Ruby / RB / RT / RP / Warichu / WT / WP / Figure / Formula / Form), ni un rol personalizado que el llamador haya pre-registrado a través de AddStructRoleMap. La excepción de diagnóstico incluye la cadena de rol problemática, hace referencia a PDF/UA-1 §7.7 / Tabla 333 y sugiere la solución AddStructRoleMap. También se rechaza un rol vacío con un mensaje específico. Sin esta validación, los roles no conformes terminarían silenciosamente en el árbol de estructura y se manifestarían como errores de "tipo de estructura desconocido" en las herramientas veraPDF / PAC.
  • La nueva función solo se activa cuando PDFUACompliance es True; las llamadas que no utilizan UA conservan el comportamiento predeterminado de freeform-role de versiones anteriores a v2.120.14, byte por byte. Las cuatro sobrecargas de AddStructureElement (la versión base de 4 argumentos con AnsiString, la sobrecarga de 6 argumentos de v2.88.0 para /Alt y /ActualText, la sobrecarga de 7 argumentos de v2.95.0 para /ID y la sobrecarga de la enumeración THPDFStandardStructureType de v2.119.41) convergen en la misma ruta, por lo que un único control es suficiente. La sobrecarga de la enumeración siempre pasa un nombre de la Tabla 333 y, por lo tanto, siempre pasa la verificación; los usuarios prefieren esta opción para eliminar errores tipográficos o problemas de sensibilidad a mayúsculas y minúsculas en tiempo de compilación (por ejemplo, 'Para' vs 'P', 'Lbody' vs 'LBody').
  • v2.119.41 ya incluyó el enum THPDFStandardStructureType (41 nombres de especificaciones agrupados en 5 categorías de roles de PDF: §14.8.4.1 Agrupación / §14.8.4.2 Nivel de bloque / §14.8.4.3 Nivel de línea / §14.8.4.4 Ruby+Warichu / §14.8.4.5 Ilustración), StandardStructureTypeToName(T) para la búsqueda de nombres de enum, e IsStandardStructureType(Name) para la validación en el lado del llamador; v2.120.14 integra estas funcionalidades en el punto de entrada AddStructureElement para que la verificación estricta sea automática. Un nuevo helper privado, _IsCustomRoleInRoleMap, recorre el array dinámico FStructRoleMap utilizando CompareMem, que es seguro para bytes, y que coincide correctamente con nombres de roles personalizados no ASCII en hosts con CP_ACP=65001.

2026-05-23 Version 2.120.13

  • Corrección: La fuente de HotPDF v2.120.12 no se compilaba en RAD Studio porque las dos sobrecargas de EnableShapingFeatureForSubset se encontraban dentro de la sección publicada de THotPDF, lo que Delphi rechazaba con el error E2266 ("solo uno de un conjunto de métodos sobrecargados puede ser publicado"). En v2.120.13, las dos sobrecargas se encierran con un par de secciones públicas localizadas, de modo que la misma API pública se compila correctamente en dcc32 / dcc64; las propiedades y funciones auxiliares circundantes permanecen publicadas sin cambios. No hay cambios en el comportamiento en tiempo de ejecución.

2026-05-23 Version 2.120.12

  • Habilitar la función de formato para subconjuntos (completado en la fase 8f): THotPDF.EnableShapingFeatureForSubset (FeatureTag) / the La sobrecarga de la enumeración THPDFShapingFeature recorre cada búsqueda GSUB conectada a la función solicitada en la versión v2.119.49. Establecer el estado de selección de GSUBScript / GSUBLanguage, enumerar cada posible GID sustituto en LookupType 1 (Simple — delta sobre glifos de Coverage + array substituteGlyphIDs de Formato 2), LookupType 2 (Múltiple — Sequence.substituteGlyphIDs[]), LookupType 3 (Alternativo — AlternateSet.alternateGlyphIDs[]), LookupType 4 (Ligadura — Ligature.ligatureGlyph), LookupType 5 / 6 (Contextual / Contextual encadenado — recursar en registros de búsqueda secuenciales con un límite de profundidad de 8), LookupType 7 (Extensión — desempaquetar automáticamente al tipo de búsqueda efectivo), y LookupType 8 (Encadenado inverso — substituteGlyphIDs[]), y combinar mediante OR los GID en FUnicodeExtraUsedGlyphs para que el subconjunto EndDoc de la versión v2.84.0 incluya cada glifo que la función pueda producir en el subconjunto de la fuente incrustada. La fase 8f complementa el MarkUnicodeGlyphUsed a nivel de glifo de la versión v2.119.50 con una opción de inclusión por lotes a nivel de función para los clientes cuyo proceso de producción habilita una función de forma universal.
  • La sobrecarga de la enumeración asigna cada valor de THPDFShapingFeature a su conjunto de etiquetas de función OpenType convencional de 4 bytes: sfArabicGSUB → init / medi / fina / isol / rlig; sfStandardLigatures → liga; sfContextualLigatures → clig + rlig; sfStylisticAlternates → salt; sfIndicShaping → nukt / akhn / rphf / rkrf / pref / half / vatu / cjct / pres / blws / abvs / psts / haln / pstf; sfContextualAlternates → rclt + calt. La sobrecarga de la cadena acepta cualquier etiqueta de función OpenType de 4 bytes para una segmentación precisa, más allá de los casos de la enumeración incluidos. Advertencia de hinchazón de subconjuntos: habilitar funciones amplias (por ejemplo, aalt Access All Alternates, algunas fuentes incluyen cada glifo en un script) puede incluir una gran fracción del conjunto de glifos de la fuente en el subconjunto, lo que anula el trabajo de reducción de tamaño de v2.84.0; los llamadores deben preferir Phase 9 MarkUnicodeGlyphUsed cuando conozcan los GID emitidos. Predeterminado desactivado / activación explícita. Cierra la última subfase restante del GSUB Engine Roadmap Phase 8.
  • Las reglas de contrato defensivo reflejan la versión v2.119.50: no se registra ninguna fuente, no hay tabla GSUB, la etiqueta no tiene 4 bytes, la función está ausente en la ruta (guion, idioma), la cadena de búsqueda está vacía; cada condición de "no hay nada que hacer" es una operación silenciosa (sin excepción). Las llamadas repetidas son idempotentes (el mapa de bits se combina mediante OR). La recursión anidada de subtables contextuales de formato 1/2 se pospone a una revisión futura; el formato 3 es el dominante en las fuentes del mundo real y está implementado. Los usuarios que necesiten una cobertura contextual precisa de los formatos 1/2 pueden recurrir a MarkUnicodeGlyphUsed por cada glifo.

2026-05-23 Version 2.120.11

  • ToUnicode CMap caller-side reverse mapping (Phase 8d completion): three new public methods on THotPDF let callers register additional (substitute codepoint, original Unicode source codepoint sequence) reverse mappings into the EndDoc Adobe-Identity-UCS ToUnicode CMap. The v2.119.61 / v2.119.62 / v2.119.65 release series added 35 hard-coded bfchar entries for the Arabic ligatures (LAM-ALEF / YEH-HAMZA / Allah / Bismillah) and Latin Alphabetic Presentation Forms (fi / fl / ffi / ffl / ff / ſt / st) that HotPDF's producer-side shaping pipeline emits automatically; v2.120.11 closes Phase 8d by letting callers register their own mappings for the substitute glyphs they drive via the v2.119.43-50 GSUB engine APIs (GetSingleSubstituteGlyph, GetMultipleSubstituteGlyphs, GetAlternateGlyph, ApplyLigatureSubstitution, ApplyContextualSubst, ApplyReverseChainedContextualSubst).
  • RegisterToUnicodeReverseMapping (SubstCodepoint, SourceCodepoints) registra una entrada (secuencia de sustitución → secuencia de origen). Los casos de uso típicos son: después de la sustitución múltiple (1 GID → N GIDs), se emite la secuencia de sustitución y se registra la correspondencia inversa para que el lector pueda copiar/pegar y volver al codepunto original; después de la sustitución de ligaduras (N GIDs → 1 GID de ligadura), se emite la ligadura y se registra el codepunto de la ligadura, que se invierte a la secuencia de origen de N elementos (por ejemplo, una ligadura contextual CALT arbitraria se invierte a sus codepuntos componentes). ClearToUnicodeReverseMappings elimina todos los registros realizados por el llamador; ToUnicodeReverseMappingCount informa de cuántos están pendientes.
  • Se emiten los bloques adicionales de bfchar después del bloque integrado de 35 entradas, agregando las entradas registradas por el llamador. Estos bloques se dividen automáticamente en subbloques de 100 entradas por cada mapa CMap de Adobe y según la especificación de archivos CIDFont (Nota técnica #5014) §1.4.2, respetando el límite del operador bfchar. Los puntos de código BMP emiten 4 dígitos hexadecimales; los puntos de código SMP emiten un par sustituto UTF-16BE (8 dígitos hexadecimales) según la especificación del mapa CMap de Adobe. El orden de registro se conserva, por lo que la semántica de "última escritura gana" de los bfchar de los lectores de PDF permite a los llamadores obtener un comportamiento determinista de sobrescritura en caso de colisiones con las 35 entradas integradas (un llamador que registra un mapeo personalizado para <FB01> "fi" sobrescribe el valor predeterminado de v2.119.65 en el lector). Los llamadores que nunca llaman a RegisterToUnicodeReverseMapping mantienen el mapa CMap ToUnicode idéntico al valor predeterminado de v2.120.10. SetFormUnicodeFontDict ('', nil) restablece la ruta y borra el registro del llamador, de modo que el siguiente registro de fuente Unicode comienza de forma limpia.

2026-05-23 Version 2.120.10

  • El formateador de texto de Birmania (fase 8f.10) registra Birmania (etiqueta OpenType mymr) como el décimo y último sistema de escritura Indic en HotPDF, cubriendo tanto el bloque principal de Birmania (U+1000-U+109F: birmano, mon, karen sgaw, pwo occidental, shan, rumai palaung, pa'o) como el bloque extendido-A (U+AA60-U+AA7F). Se ha añadido una nueva función ApplyMyanmarReorder para la reordenación preliminar y una función GetMyanmarCategory para la búsqueda de puntos de código, que se pueden reutilizar a través del despachador ApplyIndicReorder.
  • La estructura de sílaba más compleja en el lote: Kinzi 3-CP El prefijo (U+1004 + U+103A + U+1039) al inicio de la sílaba se detecta, se reserva y se emite al principio de la salida según R8; la vocal pre-básica E (U+1031) se mueve al inicio de la sílaba según R10; los cuatro consonantes mediales Y / R / W / H (U+103B-U+103E) se recopilan y se emiten en un orden fijo Y → R → W → H según R9, independientemente del orden de origen.
  • ASAT (U+103A) y VIRAMA (U+1039) se tratan ambos como virama para el manejo de consonantes apiladas. DOT BELOW (U+1037) se renderiza debajo de la base; otras marcas de tono se renderizan arriba. ANUSVARA (Bindu) se muestra arriba; VISARGA se muestra después. No se aplica Repha.
  • Dos entradas de registro de `IndicScripts` (bloque principal y bloque Extended-A) comparten las mismas funciones de reordenamiento de Myanmar, por lo que las consonantes de Extended-A y los signos diacríticos de tono Pa'o Karen se procesan de forma transparente a través del despachador.
  • **Completa el procesamiento por lotes de 11 fases para la adaptación de escrituras indicas que no son Devanagari:** infraestructura + 10 escrituras indicas registradas: Devanagari, bengalí, gujarati, tamil, telugu, kannada, malayalam, sinhala (familia Brahmic SIA), jemer, birmania (sureste asiático).
  • Según Unicode 16.0 §16.3 (Myanmar) y la especificación de formato Myanmar de OpenType.

2026-05-23 Version 2.120.9

  • El formateador de texto jemer (fase 8f.9): registra el jemer (etiqueta OpenType khmr, bloque Unicode U+1780-U+17FF) como el noveno sistema de escritura indic en HotPDF y el primer sistema de escritura del sudeste asiático. Un nuevo ApplyKhmerReorder realiza una fase de reordenamiento previa y GetKhmerCategory busca el punto de código, y ambos son reutilizables a través del despachador ApplyIndicReorder.
  • Estructura de sílaba independiente (no el patrón brahmico R1-R5 utilizado por las fases 8f.1-8f.8): las vocales pre-básicas (E / AE / AI / OE / OO / AU) se mueven al inicio de la sílaba; los modificadores de registro (MUUSIKATOAN, TRIISAP) y otros signos superiores se dirigen al búfer superior; Bindu (NIKAHIT) se renderiza en la parte superior; Visarga (REAHMUK, YUUKALEAPINTU) se renderiza en la parte inferior.
  • Manejo de subíndices COENG: cada COENG (U+17D2) + un par de consonantes forma un grupo de consonantes apiladas que permanece en el búfer base en el orden original, permitiendo que las funciones GSUB 'pres' / 'blws' de la fuente manejen el posicionamiento del subíndice. Se admite el coeng anidado.
  • No Repha: Las escrituras Khmer no forman una unidad visual "Repha", por lo que `Ra + COENG + Consonante` permanece en el orden original en lugar de rotar hacia el final de la sílaba.
  • Según Unicode 16.0 §16.4 (jemer) y la especificación de formación OpenType para el idioma jemer.

2026-05-23 Version 2.120.8

  • PAdES adbe-revocationInfoArchival CMS atributo firmado (Fase 9 de PAdES): emite el OID privado de Adobe 1.2.840.113583.1.1.8 que contiene los valores de revocación CRL y OCSP dentro de la firma CMS. Adobe Acrobat da prioridad a este atributo para la verificación de la revocación de la firma, antes de las búsquedas en el diccionario DSS y las descargas de red de OCSP/CRL; esto es fundamental para los archivos PDF que se pueden validar sin conexión y para la mejor interoperabilidad con Adobe Acrobat.
  • Nuevos campos en `THPDFCMSSignOptions`:
    • AdobeCRLsDER: array of TBytes - cada entrada es una instancia de `CertificateList` (RFC 5280) en formato DER, el mismo formato que contienen los flujos `/CRLs` de DSS.
    • AdobeOCSPsDER: array of TBytes - cada entrada es una secuencia `BasicOCSPResponse` (RFC 6960 §4.2.1) en formato DER. Nota: *no* es el contenedor `OCSPResponse` externo; las aplicaciones que reciban respuestas OCSP completas deben primero "desempaquetar" la cadena de octetos `responseBytes.response` para extraer la `BasicOCSPResponse`.
  • Activación: atributo emitido cuando al menos uno de los dos arreglos no está vacío. El valor predeterminado, que es un arreglo vacío (ambos arreglos), mantiene la estabilidad a nivel de bytes en la versión v2.120.7.
  • ASN.1 DER según la especificación de Adobe: RevocationInfoArchival ::= SEQUENCE { crl [0] EXPLICIT SEQUENCE OF CRL OPTIONAL, ocsp [1] EXPLICIT SEQUENCE OF BasicOCSPResponse OPTIONAL, otherRevInfo [2] EXPLICIT SEQUENCE OF OtherRevInfo OPTIONAL }. HotPDF utiliza los identificadores [0] y [1]; [2] (otherRevInfo para sistemas de revocación que no son X.509) queda fuera del alcance.
  • Forma del cable (ambas ramas pobladas): 30 A0 30 <...> A1 30 <...>
  • **Coexiste con el diccionario DSS:** este atributo es *paralelo* (y no un reemplazo para) el diccionario PAdES-B-LT DSS (en el nivel de catálogo, `/DSS /Certs /OCSPs /CRLs /VRI, introducido en v2.110.0). Los validadores estrictos de PAdES (EU DSS, mTOM) tienden a respetar el DSS; Adobe Acrobat respeta `adbe-revocationInfoArchival`. Para una interoperabilidad óptima, complete ambas capas con los mismos bytes CRL/OCSP a través de `AddPAdESDSSCRL` / `AddPAdESDSSOCSP` y `AdobeCRLsDER` / `AdobeOCSPsDER`.
  • El wrapper `HPDFCMSBuildSignedData` de 4 argumentos establece los nuevos campos a cero para que los paquetes CMS antiguos permanezcan idénticos en tamaño.
  • Para Adobe Acrobat PDF Reference + RFC 5280 (CRL) + RFC 6960 (OCSP).

2026-05-23 Version 2.120.7

  • PAdES content-time-stamp CMS firmado, atributo (Fase 8 de PAdES): emite id-aa-ets-contentTimestamp (OID 1.2.840.113549.1.9.16.2.20, ETSI EN 319 122-1 §5.2.8 + RFC 5126 §5.11.4) que contiene un TimeStampToken pre-firma, que demuestra que el contenido del documento existía en el momento reclamado por la TSA antes de que el firmante lo firmara. Es diferente de signature-time-stamp (atributo no firmado de la Fase 4, posterior a la firma).
  • Se agregó un nuevo campo `THPDFCMSSignOptions`: `GetContentTimeStamp: THPDFCMSTimestampCallback`. Diseño espejo de `GetSignatureTimeStamp` en la versión v2.120.3: HotPDF invoca la función de devolución de llamada con el hash SHA-256 del documento (el mismo hash que lleva el atributo `messageDigest` firmado), y incrusta el TimeStampToken DER devuelto como el valor del atributo content-time-stamp dentro de SignedAttributes.
  • `nil` predeterminado = no se emite marca de tiempo de contenido. Los que lo requieran deben asignar un bloque de código que impulse un TSA (Timestamping Authority) RFC 3161 a través de HTTP. Un valor de `TBytes` vacío = se omite silenciosamente (degradación gradual cuando el TSA no está disponible).
  • **Puntos finales de TSA gratuitos** (no se requiere suscripción) para casos de uso no calificados: http://timestamp.digicert.com, http://timestamp.sectigo.com, http://timestamp.globalsign.com/tsa/r6advanced1, https://freetsa.org/tsr, http://timestamp.apple.com/ts01. Para escenarios calificados por eIDAS, se necesita un QTSA comercial; la firma para uso no legal funciona con los puntos finales gratuitos (con límites de velocidad, pero adecuados para los flujos de trabajo típicos).
  • Contrato de devolución de llamada: se invoca una vez por cada operación de firma, *antes* de que se sellen los atributos firmados. Los bytes TST se convierten en parte de lo que se firma, por lo que la firma en sí misma cubre la marca de tiempo del contenido; esta es la razón por la cual es un atributo firmado (en lugar de una marca de tiempo de la firma, que no está firmada y se encuentra en unsignedAttrs después de la firma).
  • Se han establecido conjuntos de funciones de envoltura `HPDFCMSBuildSignedData` de 4 argumentos para que `GetContentTimeStamp` sea nulo, de modo que los paquetes CMS heredados permanezcan idénticos en bytes.
  • Responsabilidad del llamador: Los marcadores de posición de PAdES necesitan el campo `ContentsBytes` con un tamaño adecuado para la firma, más la marca de tiempo del contenido (TST) (normalmente de ~4 a 8 KB). Para los flujos de trabajo B-T que combinan la marca de tiempo del contenido y la marca de tiempo de la firma, se debe tener en cuenta ambas marcas de tiempo (TST). El valor predeterminado de 16 KB para AddPAdESSignatureField(Profile='B-T') ya cubre la mayoría de las combinaciones.
  • Según ETSI EN 319 122-1 V1.2.1 §5.2.8 + RFC 3161 §6 + RFC 5126 §5.11.4.

2026-05-23 Version 2.120.6

  • El atributo de firma PAdES "signer-attributes-v2" permite incluir signedAssertions [2]. En la rama (Fase 7 de PAdES), se cierra el tercer y último campo opcional "SignerAttributeV2". Esto permite al firmante adjuntar afirmaciones firmadas identificadas por OID (afirmaciones SAML 2.0, tokens compactos JWT, acreditaciones OAuth, tokens de acreditación específicos de la organización) dentro de la firma CMS.
  • Nuevo registro `THPDFSignedAssertion`: `SignedAssertionID` (OID punteado que identifica el tipo de aserción) más `AssertionBody: TBytes` (cualquier secuencia de bytes pre-codificada que el esquema del OID define; normalmente, una cadena de octetos que envuelve el XML de SAML o la forma compacta de JWT). Si `AssertionBody` está vacío, se omite el campo ANY, dejando solo `signedAssertionID` (válido según la especificación para las aserciones de tipo booleano registradas por OID).
  • Se agregó un nuevo campo `THPDFCMSSignOptions`: `SignedAssertions: array of THPDFSignedAssertion`. Se admiten múltiples aserciones; HotPDF encapsula la secuencia en `[2] EXPLICIT` (etiqueta `0xA2`).
  • Se ha modificado la condición de activación de `SignerAttributeV2` para que ahora se active con cualquiera de los tres campos OPCIONALES. Al completar los tres campos, se genera lo siguiente: `SignerAttributeV2 SEQUENCE { [0] claimed..., [1] certified..., [2] signedAssertions... } en el orden especificado en el campo.
  • ASN.1 según ETSI EN 319 122-1 §5.2.6: SignedAssertion ::= SEQUENCE { signedAssertionID OBJECT IDENTIFIER, signedAssertion ANY DEFINED BY signedAssertionID OPTIONAL }. Estructura de transmisión para una única aserción con cuerpo: A2 30 30 06 .
  • Reutiliza `CMSEncodeOIDFromString` (v2.120.1) para la codificación de OID en las aserciones; cualquier OID en notación de puntos es aceptado.
  • El wrapper `HPDFCMSBuildSignedData` de 4 argumentos establece el nuevo campo a cero para que los paquetes CMS heredados permanezcan idénticos en tamaño.
  • signer-attributes-v2 收口: all three OPTIONAL fields ([0] claimedAttributes, [1] certifiedAttributesV2, [2] signedAssertions) now wired through Phases 5/6/7. Full ETSI EN 319 122-1 §5.2.6 attribute coverage from the PAdES producer side.
  • Según ETSI EN 319 122-1 V1.2.1 §5.2.6 + ANEXO A.1.

2026-05-23 Version 2.120.5

  • El branch `PAdES signer-attributes-v2 certifiedAttributesV2 [1]` (Fase 6 de PAdES): emite certificados de atributos X.509 (RFC 5755) dentro de la secuencia `SignerAttributeV2`, junto con o en lugar de los atributos `claimedAttributes [0]` de la versión v2.120.4. Esto permite que el firmante adjunte certificados de rol emitidos por la Autoridad de Atributos (AA) que respaldan criptográficamente la identidad/capacidad declarada.
  • Se agregó el nuevo campo `THPDFCMSSignOptions`: `CertifiedAttributeCertsDER: array of TBytes`. Cada elemento es el certificado `AttributeCertificate` (RFC 5755) codificado en DER que el llamador recibió del AA emisor. HotPDF los emite textualmente dentro de una secuencia `CertifiedAttributesV2` envuelta con `[1] EXPLICIT` (etiqueta `0xA1`).
  • El atributo `signer-attributes-v2` ahora se emite cuando uno o ambos de los siguientes elementos no están vacíos: ClaimedRoles o CertifiedAttributeCertsDER. Cuando ambos se completan, se crea una secuencia `SignerAttributeV2` que contiene `[0]` y `[1]`, manteniendo el orden especificado.
  • Formato de cable ASN.1 DER (solo para CertifiedAttributesV2): A1 30 donde cada ACn es la secuencia de bytes proporcionada por el llamador, que debe ser exactamente la secuencia AttributeCertificate definida en RFC 5755. La elección entre AttributeCertificate y OtherAttributeCertificate se resuelve mediante la entrada DER proporcionada por el llamador (ambas alternativas comienzan con una etiqueta SEQUENCE 0x30; HotPDF no realiza una inspección interna).
  • El wrapper `HPDFCMSBuildSignedData` de 4 argumentos establece el nuevo campo a cero para que los paquetes CMS antiguos sigan siendo idénticos en bytes a la versión v2.119.27.
  • Alcance: las validaciones firmadas [2] (tokens SAML / JWT / OID arbitrarios) aún están pendientes y se incluirán en la próxima fase.
  • Según ETSI EN 319 122-1 V1.2.1 §5.2.6 + ANEXO A.1 + RFC 5755.

2026-05-23 Version 2.120.4

  • PAdES signer-attributes-v2: atributo firmado por CMS (Fase 5 de PAdES): emite el atributo id-aa-ets-signerAttrV2 (OID 0.4.0.19122.1.1, ETSI EN 319 122-1 §5.2.6), permitiendo al firmante declarar los roles declarados (por ejemplo, "Director Financiero", "Representante Autorizado") dentro del paquete CMS. Reemplaza el atributo signerAttr (RFC 5126 §5.10) obsoleto para nuevas firmas.
  • Nuevo registro `THPDFClaimedRole`: `RoleOID` (tipo de atributo OID en notación de puntos, típicamente un OID de rol organizacional o el ID `1.3.6.1.5.5.7.20.1 id-id-aa-PERMrole`) + `RoleValue` (cadena UTF-8 que contiene la etiqueta de rol legible por humanos).
  • Se agregó el nuevo campo `THPDFCMSSignOptions`. `ClaimedRoles: array of THPDFClaimedRole`. Se admiten múltiples roles; cada uno se convierte en un `Attribute` dentro de la secuencia `claimedAttributes [0]`. Un array vacío (por defecto) suprime el atributo, manteniendo la estabilidad a nivel de bytes en la versión v2.120.3 para los usuarios que no optaron por esta funcionalidad.
  • ASN.1 DER según ETSI EN 319 122-1 §5.2.6 + ANEXO A.1 (módulo DEFINITIONS EXPLICIT TAGS): SignerAttributeV2 SEQUENCE { [0] EXPLICIT ClaimedAttributes } donde ClaimedAttributes ::= SEQUENCE OF Attribute. Cada rol se serializa como Attribute { type OID, values SET OF UTF8String }. La etiqueta EXPLICIT significa que 0xA0 envuelve literalmente la SEQUENCE OF interna (la etiqueta interna 0x30 se conserva).
  • El cuerpo del OID está codificado manualmente: 04 00 81 95 32 01 01 (arco 0.4.0.19122.1.1; base-128 y continuación en 19122 → 0x81 0x95 0x32). En el futuro, los valores personalizados podrán utilizar `CMSEncodeOIDFromString` a partir de la versión v2.120.1.
  • **Límite de alcance:** solo Se ha implementado claimedAttributes [0] en esta fase. certifiedAttributesV2 [1] (certificados de atributos X.509 según RFC 5755) y signedAssertions [2] (tokens tipo SAML/JWT) requieren mecanismos adicionales y quedan fuera del alcance hasta que exista una demanda concreta.
  • Se establecen a cero los valores del envoltorio `HPDFCMSBuildSignedData` de 4 argumentos para que la longitud de `ClaimedRoles` coincida, lo que permite que los paquetes CMS heredados permanezcan idénticos en bytes a la versión v2.119.27.
  • Según ETSI EN 319 122-1 V1.2.1 §5.2.6 + EN 319 142-1 V1.2.1, §6.3 Tabla 1, fila signer-attributes-v2 (OPCIONAL, 0 o 1).

2026-05-23 Version 2.120.3

  • PAdES signature-time-stamp CMS unsigned attribute (PAdES Phase 4): emite id-aa-signatureTimeStampToken (OID 1.2.840.113549.1.9.16.2.14, RFC 3161 §6 + ETSI EN 319 122-1 §5.3) dentro del conjunto [1] IMPLICIT unsignedAttrs. Proporciona la signature-time-stamp ruta para el servicio de tiempo confiable PAdES-B-T (Tabla 1: B-T debe ser proporcionado a través de signature-time-stamp o document-time-stamp).
  • Se ha añadido un nuevo tipo de método anónimo `THPDFCMSTimestampCallback`: `reference to function(const SigValueSHA256: TBytes): TBytes`. HotPDF invoca la función de devolución de llamada con el valor SHA-256 de `SignerInfo.signatureValue` (la "messageImprint" de la RFC 3161 según la sección 2.4.2) y incrusta el DER del `TimeStampToken` devuelto como el valor del atributo.
  • Se agregó el nuevo campo `THPDFCMSSignOptions`. `GetSignatureTimeStamp`. El valor predeterminado es `nil`, lo que significa que no se utiliza ninguna marca de tiempo; las llamadas a PAdES-B-T asignan un bloque de código que gestiona la interacción HTTP/RFC 3161 con el TSA y devuelve los bytes de la marca de tiempo. Si se devuelve un valor de `TBytes` vacío, se omite sin error (por ejemplo, si el TSA no está disponible, pero la firma aún debe incluirse).
  • Las funciones de red / autenticación / gestión de cuentas TSA permanecen en el código del llamador; HotPDF solo transmite el resultado al atributo `unsignedAttrs`. Esto se complementa naturalmente con el tamaño predefinido de `AddPAdESSignatureField(Profile='B-T', ContentsBytes >= 16384)`.
  • ASN.1 wire: La secuencia SignerInfo ahora incluye opcionalmente un `[1] IMPLICIT SET OF Attribute` al final (etiqueta `0xA1`) que contiene un atributo `signature-time-stamp` cuyo valor es el `TimeStampToken ContentInfo` proporcionado por el llamador, tal cual.
  • Se han establecido conjuntos de funciones de envoltura `HPDFCMSBuildSignedData` de 4 argumentos para que `GetSignatureTimeStamp:=nil`, de modo que los paquetes CMS heredados permanezcan idénticos en bytes.
  • Según RFC 3161 §6 + ETSI EN 319 122-1 §5.3 + EN 319 142-1 V1.2.1 §6.3 Tabla 1, nota q (B-T puede utilizar tanto la marca de tiempo de firma como la marca de tiempo del documento para la confianza temporal).

2026-05-23 Version 2.120.2

  • PAdES commitment-type-indication CMS Se agregó soporte para el atributo SignedAttribute (Fase 3 de PAdES): se emite el atributo id-aa-ets-commitmentType (OID 1.2.840.113549.1.9.16.2.16, ETSI EN 319 122-1 §5.2.3 + RFC 5126 §5.11), que declara el tipo de compromiso que el firmante realiza a través de la firma (por ejemplo, prueba de origen / recibo / entrega / remitente / aprobación / creación).
  • Se ha añadido un nuevo tipo de enumeración `THPDFCommitmentType`: `ctNone` (por defecto, suprime el atributo), `ctProofOfOrigin`, `ctProofOfReceipt`, `ctProofOfDelivery`, `ctProofOfSender`, `ctProofOfApproval`, `ctProofOfCreation` (seis OID estándar `id-cti-ets-*` de RFC 5126 §5.11.1, arc `1.2.840.113549.1.9.16.6.{1..6}`), y `ctCustom` (el llamador proporciona un OID arbitrario a través del nuevo campo `CommitmentTypeOID`).
  • Activación: se emite cada vez que `THPDFCMSSignOptions.CommitmentType` no es `ctNone`. El valor predeterminado vacío mantiene la estabilidad a nivel de bytes en la versión v2.120.1 para los usuarios que no optaron por esta función.
  • **Responsabilidad del llamador (PAdES Parte 1 §6.3 Tabla 1, nota d):** cuando se emite la indicación del tipo de compromiso, la entrada `/Reason` en el diccionario de firma debe estar ausente (exclusión mutua). HPDFCMS no detecta el estado del diccionario de firma; el llamador elige una opción u otra mediante `AddPAdESSignatureField(Reason='', ...)` más `CommitmentType`, o dejando `CommitmentType=ctNone` y proporcionando `Reason`.
  • Estructura ASN.1 DER: CommitmentTypeIndication SEQUENCE { commitmentTypeId OBJECT IDENTIFIER }. Se omite el CommitmentTypeQualifier (RFC 5126 §5.11.2) porque los seis OID estándar id-cti-ets-* no definen calificadores. Los compromisos personalizados que requieran calificadores pueden extender la función auxiliar.
  • El wrapper `HPDFCMSBuildSignedData` de 4 argumentos establece el nuevo campo `CommitmentType` en cero, de modo que los paquetes CMS heredados permanezcan idénticos en tamaño de bytes a la versión v2.119.27.
  • Según ETSI EN 319 122-1 §5.2.3 + RFC 5126 §5.11 + ETSI EN 319 142-1 V1.2.1 §6.3 Tabla 1, nota d.

2026-05-23 Version 2.120.1

  • PAdES signature-policy-identifier CMS Se agregó el soporte para el atributo SignedAttribute (Fase 2 de PAdES): se emite el atributo id-aa-ets-sigPolicyId (OID 1.2.840.113549.1.9.16.2.15, ETSI EN 319 122-1 §5.2.9 + RFC 5126 §5.8) que declara la política de firma bajo la cual se generó la firma. Es requerido por PAdES-E-EPES (Parte 2 V1.2.1 §5.4 Tabla 2: *debe estar presente*); puede estar presente en todos los demás niveles de PAdES.
  • Se han añadido nuevos campos al registro `THPDFCMSSignOptions`: `SignaturePolicyOID` (cadena de OID punteada del documento de política), `SignaturePolicyHash` (resumen del documento de política), `SignaturePolicyHashAlgOID` (OID del algoritmo de resumen, que por defecto es SHA-256 si está vacío) y `SignaturePolicyURI` (calificador SPuri opcional según RFC 5126 §5.8.1, OID `1.2.840.113549.1.9.16.5.1`, que apunta a la URL del documento de política).
  • Activación: el atributo de política se emite solo cuando tanto `SignaturePolicyOID` como `SignaturePolicyHash` están definidos por el llamador. Los valores predeterminados vacíos hacen que se suprima el atributo; los firmantes de la línea de base mantienen la estabilidad a nivel de bytes con la versión v2.120.0.
  • Se ha añadido una nueva función auxiliar `CMSEncodeOIDFromString` que codifica OID con notación de puntos arbitraria a DER según X.690 §8.19 (los dos primeros segmentos se combinan como `40*arc1 + arc2`, los segmentos posteriores utilizan base-128 con bits de continuación). Los OID de política son específicos de cada empresa, por lo que el productor no puede codificarlos como literales de bytes.
  • Estructura ASN.1 DER: SignaturePolicyId SEQUENCE { sigPolicyId OBJECT IDENTIFIER, sigPolicyHash OtherHashAlgAndValue [, sigPolicyQualifiers SEQUENCE OF SigPolicyQualifierInfo] }. El calificador SPuri encapsula la URL como IA5String (etiqueta 0x16) dentro de un SigPolicyQualifierInfo.
  • Compatibilidad: las llamadas a la versión v2.120.0 que no completaron los nuevos campos de política producen una salida idéntica en bytes. El wrapper `HPDFCMSBuildSignedData` de 4 argumentos establece los nuevos campos en cero para que los paquetes CMS heredados permanezcan sin cambios.
  • Según ETSI EN 319 122-1 §5.2.9 + ETSI EN 319 142-2 V1.2.1 §5.4 Tabla 2 + RFC 5126 §5.8.

2026-05-23 Version 2.120.0

  • PAdES: cumplimiento de los atributos básicos del sistema de gestión de certificados (CMS) (Fase 1 de PAdES): `SignPDFWithPFX` ahora emite el atributo firmado `ESS signing-certificate-v2` (RFC 5035 / OID `1.2.840.113549.1.9.16.2.47`) de forma predeterminada, protegiendo la identidad del certificado de firma contra ataques de suplantación. ETSI EN 319 142-1 V1.2.1 §6.3 Tabla 1 indica que esto *debe proporcionarse* para todos los niveles básicos (B-B / B-T / B-LT / B-LTA).
  • La implementación básica de PAdES ahora omite el atributo `signing-time` del CMS de forma predeterminada. La Tabla 1 exige una cardinalidad de 0 para los perfiles básicos (el tiempo de firma indicado se incluye en la entrada `/M` del diccionario de firma, según la nota g de la Tabla 1). La versión anterior, v2.119.27, incluía `signing-time` independientemente del perfil, lo que provocaba que los verificadores estrictos de PAdES (como el modo básico de PAdES de Adobe Acrobat y los sistemas DSS de la UE) lo rechazaran.
  • Nueva API pública: `THPDFPAdESLevel` (enum con 8 valores que cubren los niveles B-B / B-T / B-LT / B-LTA, extendidos E-BES / E-EPES / E-LTV, y el formato heredado `adbe.pkcs7`), `THPDFCMSSignOptions` (registro con opciones de nivel, hora de firma, indicador de firma v2 y marca de tiempo UTC), `HPDFCMSDefaultOptions(Level)` (función auxiliar que devuelve los valores predeterminados según la especificación para cada nivel) y `HPDFCMSBuildSignedDataEx` (constructor de CMS con opciones).
  • Compatibilidad con versiones anteriores: la firma HPDFCMSBuildSignedData de cuatro argumentos de la versión v2.119.27 se mantiene como una capa delgada y genera una salida idéntica en bytes (el certificado de firma v2 no está presente, y la hora de firma se establece según lo solicitado). Las llamadas que calcularon el hash del paquete CMS de la versión v2.119.27 en las pruebas siguen funcionando correctamente.
  • Se ha modificado el comportamiento en la salida de PDF con firma: cualquier llamada a `THotPDF.SignPDFWithPFX` con un marcador de posición PAdES (mediante `AddPAdESSignatureField`, SubFilter `ETSI.CAdES.detached`) ahora genera firmas que pasan la validación de línea base PAdES estricta. Para obtener el conjunto de atributos heredado de la versión 2.119.27, los usuarios deben llamar directamente a `HPDFCMSBuildSignedDataEx` con `HPDFCMSDefaultOptions(palLegacy_adbePkcs7)`.
  • Estructura ASN.1 DER según RFC 5035 §3: `SigningCertificateV2 SEQUENCE { certs SEQUENCE OF ESSCertIDv2 }` con el identificador de algoritmo SHA-256 omitido (regla DER de valor predeterminado de X.690 §11.5), hash del certificado SHA-256, y `IssuerSerial` que reutiliza la extracción existente del emisor + número de serie de `CMSExtractIssuerAndSerial`.
  • Según ETSI EN 319 142-1 V1.2.1 §6.3 Tabla 1 y RFC 5035 §3.

2026-05-23 Version 2.119.77

  • Se agregó el formateador de la escritura Sinhala (Fase 8f.8). La escritura Sinhala ('sinh', U+0D80-U+0DFF) se convierte en la octava escritura indic registrada. Se agregaron los nuevos métodos ApplySinhalaReorder y GetSinhalaCategory a THotPDF.
  • Características del cingalés: incluye Repha (Ra=U+0DBB + Halant=U+0DCA AL-LAKUNA); tres matras pre-base (E=U+0DD9, EE=U+0DDA, AI=U+0DDB) — únicas por tener tres matras pre-base; 3 matras divididas (U+0DDC O, U+0DDD OO, U+0DDE AU) con descomposición Unicode canónica; U+0DDD OO es de 3 partes (E + AA + AL-LAKUNA).
  • El componente `ApplyIndicReorder` ahora admite el idioma Sinhala de forma transparente (el registro IndicScripts ahora es un array de tamaño 0 a 7).
  • Se agregaron 21 nuevas pruebas de DUnitX. En total, 187 de 187 pruebas se aprobaron en Win32 y Win64.
  • Según Unicode 16.0 §12.11 (Sinhala) y la especificación de formato Sinhala de OpenType.

2026-05-23 Version 2.119.76

  • Se ha añadido el formateador de malayalam (Fase 8f.7). El malayalam ('mlym', U+0D00-U+0D7F) se convierte en el séptimo sistema de escritura indic registrado. Se han añadido los nuevos métodos `ApplyMalayalamReorder` y `GetMalayalamCategory` a THotPDF.
  • Características del malayalam: incluye Repha (Ra=U+0D30 + Halant=U+0D4D, CHANDRAKKALA); I-matra (U+0D3F) es una base POST, similar al tamil (único en comparación con Devanagari/Bengalí/Gujarati, que usan una base PRE); bases pre-combinadas para E/EE/AI; 3 matras separadas (U+0D4A O / U+0D4B OO / U+0D4C AU) con descomposición canónica pre+post; letras chillu (U+0D54-U+0D56, U+0D7A-U+0D7F) y DOT REPH (U+0D4E) clasificadas como consonantes.
  • El componente `ApplyIndicReorder` ahora admite el idioma Malayalam de forma transparente (el registro IndicScripts ahora es un array de tamaño 7).
  • Se agregaron 21 nuevas pruebas de DUnitX, que incluyen la cobertura de la categoría chillu/DOT-REPH. En total, 166 de 166 pruebas pasaron en Win32 y Win64.
  • Según Unicode 16.0 §12.10 (malayalam) y la especificación de formato Malayalam de OpenType.

2026-05-23 Version 2.119.75

  • Se ha añadido el motor de formación para el idioma Kannada (GSUB engine roadmap Phase 8f.6). Kannada ('knda', U+0C80-U+0CFF) se convierte en el sexto sistema de escritura indic registrado. Se han añadido nuevos métodos públicos, ApplyKannadaReorder y GetKannadaCategory, en THotPDF.
  • El idioma Kannada tiene Repha (Ra=U+0CB0 + Halant=U+0CCD) similar al Devanagari / bengalí / gujarati / telugu. Al igual que el telugu, el Kannada no tiene matras pre-base; cada matra en Kannada está por encima de la base, por debajo de la base, después de la base o dividida.
  • Se dividieron 5 matras utilizando la descomposición canónica de Unicode 16.0: U+0CC0 II → U+0CBF (superior) + U+0CD5 (inferior); U+0CC7 EE → U+0CC6 (superior) + U+0CD5 (inferior); U+0CC8 AI → U+0CC6 + U+0CD6 (ambos superiores); U+0CCA O → U+0CC6 (superior) + U+0CC2 (inferior); **U+0CCB OO es de tres partes:** U+0CC6 (superior) + U+0CC2 (inferior) + U+0CD5 (inferior) — la primera división de tres partes está gestionada por la familia de formateadores Phase 8f.
  • Matras situados por encima de la línea base (R3): I (U+0CBF), E (U+0CC6), AU (U+0CCC), marca de longitud AI (U+0CD6). Matras situados por debajo de la línea base (R4): R/RR vocálicos (U+0CC3-U+0CC4), matras vocálicos L/LL (U+0CE2-U+0CE3). Matras situados después de la línea base (R5): AA (U+0CBE), U/UU (U+0CC1-U+0CC2), marca de longitud post-base (U+0CD5).
  • El despachador `ApplyIndicReorder` ahora selecciona automáticamente el idioma Kannada a través del registro (ahora array[0..5]).
  • Pruebas: Se agregaron 21 nuevos casos de DUnitX, que incluyen los 5 casos de matra dividida, además de una verificación dedicada de tres partes para "OO". En total, 145 de 145 pruebas pasaron en Win32 y Win64.
  • Según Unicode 16.0 §12.9 (Kannada) y la especificación de formato Kannada de OpenType.

2026-05-23 Version 2.119.74

  • Se ha añadido el motor de formación para el idioma telugu (GSUB, fase 8f.5 del plan de desarrollo). El telugu ('telu', U+0C00-U+0C7F) se convierte en el quinto sistema de escritura indic registrado. Se han añadido nuevos métodos públicos, ApplyTeluguReorder y GetTeluguCategory, a la clase THotPDF.
  • En Telugu, Repha (Ra=U+0C30 + Halant=U+0C4D) es similar a Devanagari, bengalí y gujarati. A diferencia de todos los sistemas de escritura indicos anteriores: no hay diacríticos pre-base; cada diacrítico en Telugu está por encima de la base, por debajo de la base o dividido.
  • Arriba de la línea base (R3): AA/I/II/E/EE/O/OO/AU más signo de longitud. U+0C55. Debajo de la línea base (R4): U/UU/R vocal/RR (U+0C41-U+0C44), marca de longitud AI (U+0C56), matras vocálicas L/LL (U+0C62-U+0C63).
  • 1. Se divide el signo diacrítico "matra": U+0C48 AI se descompone en U+0C46 (E por encima de la base) + U+0C56 (signo de longitud AI debajo de la base) durante la reordenación.
  • El despachador `ApplyIndicReorder` ahora selecciona automáticamente el idioma Telugu a través del registro (ahora en un array de tamaño 0 a 4).
  • Pruebas: Se agregaron 17 nuevos casos de prueba DUnitX. Un total de 124 de 124 pruebas pasaron en Win32 y Win64.
  • Según Unicode 16.0 §12.8 (Telugu) y la especificación de formato Telugu de OpenType.

2026-05-23 Version 2.119.73

  • Se ha añadido el motor de formación para el idioma tamil (GSUB, fase 8f.4). El tamil ('taml', U+0B80-U+0BFF) se convierte en el cuarto sistema de escritura indic registrado. Se han añadido nuevos métodos públicos, ApplyTamilReorder y GetTamilCategory, a la clase THotPDF.
  • Las diferencias del tamil con respecto a otras escrituras brámicas: NO Repha (el tamil tradicionalmente no forma Repha visual); I-matra (U+0BBF) es POST-base, única entre las escrituras brámicas; II (U+0BC0) está encima de la base; E/EE/AI (U+0BC6-U+0BC8) es pre-base; 3 matras divididas (U+0BCA O = U+0BC6+U+0BBE, U+0BCB OO = U+0BC7+U+0BBE, U+0BCC AU = U+0BC6+U+0BD7).
  • Halant se llama PULLI en tamil, en el punto de código U+0BCD. `ApplyIndicReorder` El componente dispatcher ahora detecta automáticamente el idioma tamil a través del registro (ahora en un array de tamaño 0 a 3).
  • Pruebas: Se agregaron 20 nuevos casos de DUnitX. Un total de 107 de 107 pruebas pasaron en Win32 + Win64.
  • Según Unicode 16.0 §12.7 (Tamil) y la especificación de formación Tamil de OpenType.

2026-05-23 Version 2.119.72

  • Se ha añadido el formateador de gujarati (hoja de ruta del motor GSUB, fase 8f.3). El gujarati ('gujr', U+0A80-U+0AFF) se convierte en el tercer sistema de escritura indic registrado, después del Devanagari y el bengalí. Se han añadido nuevos métodos públicos ApplyGujaratiReorder y GetGujaratiCategory en THotPDF.
  • Reorganizar reglas: R1 Repha (Ra=U+0AB0 + Halant=U+0ACD), R2 pre-base (U+0ABF I), R3 above-base (U+0AC5 CANDRA E, U+0AC7 E, U+0AC8 AI — tenga en cuenta que en gujarati, "above-base" es como en Devanagari, NO como "pre-base" en bengalí), R4 below-base (U+0AC1-U+0AC4, U+0AE2-U+0AE3), R5 post-base (U+0ABE AA, U+0AC0 II, U+0AC9 CANDRA O, U+0ACB-U+0ACC O/AU). No se utilizan "matras" divididas.
  • El componente `ApplyIndicReorder` selecciona automáticamente el gujarati a través del registro `IndicScripts` (ahora un array[0..2]). La integración de `BuildUnicode*FieldContent` con `sfIndicShaping` cubre el gujarati sin cambios en la integración.
  • Pruebas: Se agregaron 18 nuevos casos de DUnitX, incluyendo una cobertura superior a la base para diferenciar el manejo de E/AI en Gujarati del manejo en bengalí. En total, 87 de 87 pruebas pasaron en Win32 y Win64.
  • Según Unicode 16.0 §12.6 (gujarati) y la especificación de formación OpenType para gujarati.

2026-05-23 Version 2.119.71

  • Se ha añadido el motor de formación para el idioma bengalí (hoja de ruta del motor GSUB, fase 8f.2). El idioma bengalí ('beng', U+0980-U+09FF) se convierte en el segundo sistema de escritura indic registrado, después del Devanagari. Se han añadido nuevos métodos públicos, ApplyBengaliReorder y GetBengaliCategory, a la clase THotPDF, que son equivalentes a la API de Devanagari de la fase 8e.
  • Bengali: Reglas de reordenamiento: R1 Repha (Ra=U+09B0 + Halant=U+09CD), R2 matras pre-base (I=U+09BF, E=U+09C7, AI=U+09C8 — tenga en cuenta que E/AI son pre-base en bengalí, a diferencia de Devanagari que están por encima de la base), R4 por debajo de la base (U+09C1-U+09C4, U+09E2-U+09E3), R5 post-base (U+09BE, U+09C0, U+09D7). No hay matras por encima de la base en el bloque principal del bengalí.
  • Descomposición de matra dividida: U+09CB Oo y U+09CC AU se expanden a sus componentes visuales durante el reordenamiento (U+09C7 + U+09BE / U+09C7 + U+09D7) para que la canalización GSUB los vea en posiciones canónicas pre y posteriores.
  • El componente `ApplyIndicReorder` redirige de forma transparente los puntos de código bengalíes al módulo correspondiente; las funciones auxiliares `BuildUnicode*FieldContent` con `sfIndicShaping` habilitado ahora cubren el bengalí junto con el Devanagari, sin cambios en la integración.
  • Pruebas: Se agregaron 18 nuevos casos de DUnitX que cubren los modos R1/R2/R4/R5 del bengalí, la descomposición, la preservación de conjunciones, el paso de scripts mixtos, la idempotencia y la equivalencia del despachador. En total, 69 de 69 pruebas pasaron en Win32 y Win64.
  • De acuerdo con Unicode 16.0 §12.2 (Bengalí) y la especificación de formación bengalí de OpenType.

2026-05-23 Version 2.119.70

  • Mejora completa del motor de formación de Devanagari (hoja de ruta del motor GSUB, Fase 8f.1): `ApplyDevanagariReorder` ahora aplica el conjunto completo de 5 reglas de reordenamiento (R1 Repha + R2 pre-base + R3 arriba de la base + R4 debajo de la base + R5 post-base) en lugar del subconjunto R1 + R2-only de v2.119.55. El orden de los clústeres de salida es por sílaba: `[pre-matras] + [base + halant + nukta + bindu/visarga/ modificador] + [above-matras] + [below-matras] + [post-matras] + [Repha]`.
  • Conservación de ligaduras: los grupos de consonantes-halant-consonantes permanecen agrupados en el bloque base; el reordenamiento solo mueve las marcas diacríticas y las Repha. Es un proceso de una sola pasada e idempotente.
  • Cambios de comportamiento en comparación con la versión v2.119.69: para las sílabas que contienen matras superiores/inferiores/ post-base (o grupos de múltiples matras), el flujo de bytes del PDF ahora refleja el diseño de reordenamiento canónico. La representación visual después de GSUB/GPOS no ha cambiado. Las entradas que contienen solo el subconjunto R1 Repha o R2 pre-base I-matra (la versión v2.119.55) producen una salida idéntica en bytes.
  • Pruebas: Se agregaron 8 nuevas pruebas de DUnitX que cubren individualmente R3, R4 y R5, el orden de múltiples diacríticos, la preservación de conjunciones bajo diacríticos, la combinación de Repha con elementos superiores y posteriores, y la idempotencia de múltiples diacríticos. Un total de 51 de 51 pruebas pasaron en Win32 y Win64.
  • Según Unicode 16.0 §12.1 (Devanagari) y la especificación de formación Devanagari de OpenType.

2026-05-23 Version 2.119.69

  • Infraestructura de formación de texto indic (hoja de ruta del motor GSUB, fase 8f.0): se ha refactorizado la pre-pasada de reordenación Devanagari de las versiones v2.119.55 / v2.119.67 en un marco de despacho independiente del sistema de escritura. Se han introducido nuevos tipos `TIndicScriptInfo` / `TIndicCategoryFunc` / `TIndicFindSyllableFunc` / `TIndicReorderFunc` y un nuevo registro `IndicScripts` que permite que los próximos sistemas de escritura indic se integren a través de punteros de función.
  • Se ha añadido un nuevo método público `ApplyIndicReorder(Wide)` que envía cada carácter de `Wide` a su script registrado correspondiente y aplica las funciones de reordenamiento de sílabas asociadas a ese script. El contenido no relacionado con escrituras indic se transmite sin modificaciones.
  • Los tres métodos auxiliares `BuildUnicode*FieldContent` ahora invocan `ApplyIndicReorder` (en lugar del envoltorio específico para Devanagari) cuando `sfIndicShaping` está en `FShapingFeatures`. La fase 8f.0 incluye solo Devanagari; las fases posteriores 8f.2 a 8f.10 agregan bengalí, gujarati, tamil, telugu, kannada, malayalam, cingalés, jemer y birmano, sin cambiar este punto de integración.
  • Se mantiene la función existente `ApplyDevanagariReorder` como un envoltorio específico para Devanagari, para garantizar la compatibilidad con versiones anteriores de v2.119.55. La salida no ha cambiado desde la versión v2.119.67.
  • Según Unicode 16.0 §12 (guiones del sur de Asia) e ISO 32000-1 §9.10.

2026-05-22 Version 2.119.68

  • Phase 8c.6 — Emisión a nivel de GID en el lado del productor mediante mapeo de codepoint sintético en el Area de Uso Privado: dos nuevos métodos públicos en THotPDF permiten a los llamadores emitir GIDs sustitutos que no tienen un codepoint Unicode natural, asignando codepoints sintéticos PUA (U+E000-U+F8FF) bajo demanda. AssignSyntheticCodepointForGID (GID; out SyntheticCP): Boolean asigna el siguiente slot PUA disponible, refleja la asignación en FUnicodeCpToGid + FAcroFormUnicodeAdvances + una nueva tabla de lookup por-GID FUnicodeSyntheticCpForGID de modo que el pipeline existente de codificación hex en el lado del productor emite el codepoint sintético como un token de 4 dígitos hex que el reader del consumidor resuelve a través de /CIDToGIDMap al glifo sustituto. GetSyntheticCodepointForGID (GID): Word consulta asignaciones existentes (devuelve 0 cuando no hay sintético asignado). Ambos métodos son idempotentes — llamadas repetidas con el mismo GID devuelven el mismo codepoint sintético.
  • Desbloquea la emisión en el lado del productor para sustitutos GSUB específicos de fuente que no tienen codepoints de Unicode Presentation Form: formas de cluster devanagari (las salidas índicas 'akhn' / 'rphf' / 'pres' / 'blws' / 'psts' / 'haln' aterrizan normalmente en GIDs internos a la fuente), alternativas estilísticas (sustitutos 'salt' / 'ss01-20' sin codepoints), ligaduras discrecionales (glifos específicos de fuente 'dlig' / 'hlig') y sustitutos de secuencias de variación ideográfica CJK. Combinado con las APIs del motor GSUB de v2.119.43-50 y el cierre del subsetter MarkUnicodeGlyphUsed de v2.119.50, los llamadores pueden ahora construir pipelines completos de shaping en el lado del productor que emiten GIDs sustitutos arbitrarios a través del pipeline existente de hex basado en codepoints.
  • Vida útil del estado: los mapeos sintéticos persisten hasta que se llama de nuevo a RegisterUnicodeTTF o se dispara BeginDoc (ambos reinician todo el estado por-fuente, incluyendo la tabla sintética). El rango PUA U+E000-U+F8FF proporciona 6400 slots, suficiente para cualquier conjunto práctico de sustitutos GSUB de una fuente. El mapeo inverso del CMap ToUnicode es responsabilidad del llamador — los codepoints sintéticos PUA no tienen codepoints fuente para mapear inversamente; los llamadores preocupados por la extracción de texto deberían envolver la emisión de texto en contenido marcado PDF con la propiedad /ActualText especificando los codepoints fuente originales. Los PDFs cuyos llamadores no invocan AssignSyntheticCodepointForGID emiten idéntico a nivel de byte a la salida de v2.119.67 (los nuevos métodos son asistentes sin estado de consulta / asignación explícita; sin efectos secundarios automáticos). Este commit completa la matriz de capacidades de la Fase 8 de la hoja de ruta del motor GSUB.

2026-05-22 Version 2.119.67

  • Aplicación automática del pre-pass de reordenación devanagari (hoja de ruta del motor GSUB Fase 8e): los tres asistentes BuildUnicode*FieldContent ahora invocan automáticamente el método ApplyDevanagariReorder de v2.119.55 cuando sfIndicShaping está incluido en FShapingFeatures. El pre-pass recorre la Wide de entrada de izquierda a derecha, aplicando las dos reordenaciones específicas del devanagari (reubicación de Repha para Ra-Halant inicial de sílaba, y movimiento de la I-matra pre-base U+093F al inicio de su cluster silábico), de forma que el flujo de codepoints emitido coincide con el orden visual de lectura. El motor GSUB del reader del consumidor aplica entonces la cadena de shaping índico ('nukt' / 'akhn' / 'rphf' / 'rkrf' / 'pref' / 'half' / 'vatu' / 'cjct' / 'pres' / 'blws' / 'abvs' / 'psts' / 'haln') contra los codepoints reordenados para producir los glifos de cluster finales.
  • Alcance: la Fase 8e entrega solo la reordenación en el lado del productor. La aplicación de la cadena GSUB en el lado del productor (donde el productor compromete el shaping del cluster en el momento de emisión del PDF en lugar de confiar en el reader) queda diferida a la Fase 8c.6 porque la mayoría de los sustitutos GSUB devanagari aterrizan en GIDs específicos de fuente sin codepoint Unicode Presentation Form — A diferencia del árabe / latín, donde los bloques Forms-A / Forms-B proporcionan sustitutos alcanzables por codepoint, devanagari no tiene un rango Presentation Form equivalente en Unicode. El mapeo de codepoints sintéticos PUA de la Fase 8c.6 desbloqueará la emisión GSUB en el lado del productor para devanagari y otros scripts índicos / del sudeste asiático donde los GIDs sustitutos son específicos de fuente.
  • El contenido no devanagari pasa a través de ApplyDevanagariReorder idéntico a nivel de byte (el método solo recorre las sílabas devanagari; otros rangos de codepoints se emiten sin cambios). Los PDFs cuyos llamadores dejan sfIndicShaping desactivado (predeterminado) emiten idénticos a nivel de byte a la salida de v2.119.66. Combinado con la capa de capacidades de v2.119.55 (GetDevanagariCategory + ApplyDevanagariReorder como métodos públicos), este commit completa la integración de reordenación devanagari en el lado del productor: los llamadores que habilitan sfIndicShaping ya no necesitan pre-reordenar aguas arriba ni llamar manualmente a ApplyDevanagariReorder antes de BuildUnicode*FieldContent — HotPDF lo hace automáticamente.

2026-05-22 Version 2.119.66

  • Integración automática de GSUB 'rclt' (Required Contextual Alternates): el nuevo método THotPDF.ApplyArabicGSUBContextualRefinement (Wide) es paralelo a ApplyArabicGSUBRefinement de v2.119.63 pero aplica sustitución contextual (GSUB Tipo 5 / 6) vía la API ApplyContextualSubst de v2.119.47 en lugar de sustitución por ligadura (Tipo 4). 'rclt' codifica sustituciones requeridas dependientes del contexto — usado típicamente por fuentes árabes para variantes posicionales adicionales más allá del mapeo Forms-B del walker estático de v2.85.0, y por fuentes del sudeste asiático / índicas para formas reordenadas a nivel de cluster que dependen del contexto vecino.
  • Manejo de salida de longitud variable: a diferencia de ApplyLigatureSubstitution (N GIDs de entrada -> 1 GID sustituto), ApplyContextualSubst puede producir M GIDs sustitutos a partir de N GIDs de entrada. Cuando una regla contextual se dispara, TODOS los GIDs sustitutos deben ser alcanzables a través de codepoints Unicode Presentation Form vía cmap inverso (FB00-FDFF + FE70-FEFF, ~770 codepoints escaneados) para que la sustitución se confirme; si algún sustituto carece de mapeo inverso, la ventana de entrada pasa sin cambios (la emisión parcial corrompería la secuencia). Rango del cmap inverso ampliado para cubrir Presentation Forms latinas / armenias / hebreas (FB00-FB4F) más los rangos árabes completos, de modo que los sustitutos 'rclt' de cualquier script pueden ser emitidos.
  • Nuevo miembro de enumeración THPDFShapingFeature sfContextualAlternates (añadido al final por compatibilidad regresiva — los llamadores existentes que usan aritmética de conjuntos FShapingFeatures con la enumeración de 5 miembros de v2.119.59 compilan sin cambios). Conectado en los tres asistentes BuildUnicode*FieldContent condicionado a sfContextualAlternates en FShapingFeatures (opt-in, por defecto desactivado). Independiente de FAutoShapeArabic — 'rclt' puede aplicarse a scripts no árabes (latín / hebreo / índico). Los GIDs sustitutos fluyen a través de MarkUnicodeGlyphUsed para el cierre del subsetter de v2.84.0. No-op defensivo cuando la fuente no tiene reglas 'rclt' o los GIDs sustitutos carecen de codepoints Presentation Form. Los PDFs cuyos llamadores dejan sfContextualAlternates desactivado emiten idénticos a nivel de byte a la salida de v2.119.65.

2026-05-22 Version 2.119.65

  • Emisión de ligaduras estándar latinas 'liga' (hoja de ruta del motor GSUB Fase 8b): el nuevo método THotPDF.ApplyLatinLigatureRefinement (Wide) es paralelo a ApplyArabicGSUBRefinement de v2.119.63 pero apunta a las Alphabetic Presentation Forms latinas / armenias / hebreas en U+FB00-U+FB4F. Recorre la cadena Wide de entrada, construye un array paralelo de GIDs vía FUnicodeCpToGid, y consulta la característica GSUB 'liga' (Standard Ligatures) de la fuente vía la API ApplyLigatureSubstitution de v2.119.43-50 en cada posición. Si una sustitución de ligadura se dispara Y el GID sustituto es alcanzable a través de un codepoint Unicode Alphabetic Presentation Form, la ventana de codepoints fuente se reemplaza por el codepoint del sustituto. Conectado en los tres asistentes BuildUnicode*FieldContent (constructores de stream de apariencia /AP de AcroForm de una sola línea, multilínea y comb) condicionado a sfStandardLigatures en FShapingFeatures (opt-in, estable a nivel de byte cuando no está activo).
  • Segunda pasada opcional para 'clig' (Contextual Ligatures): cuando sfContextualLigatures también está incluido en FShapingFeatures, el método recorre de nuevo el resultado post-'liga' con 'clig' aplicado para captar ligación contextual específica de fuente más allá del conjunto estándar fi / fl / ffi / ffl. Cada GID sustituto emitido se pasa a través de MarkUnicodeGlyphUsed para el cierre del subsetter TTF de v2.84.0. El cmap inverso es un escaneo lineal sobre ~80 codepoints (FB00-FB4F); las sustituciones son escasas, el coste es insignificante.
  • Bloque bfchar del CMap ToUnicode extendido de 28 a 35 entradas: 7 nuevas entradas cubren las Alphabetic Presentation Forms latinas FB00 (ff -> f + f), FB01 (fi -> f + i), FB02 (fl -> f + l), FB03 (ffi -> f + f + i), FB04 (ffl -> f + f + l), FB05 (ſt -> long s + t), FB06 (st -> s + t). La extracción de texto en el lado del consumidor (copiar / pegar / accesibilidad) ahora restaura la secuencia ASCII original de letras cuando aparece un codepoint de ligadura latina en el flujo de texto del PDF. Los PDFs cuyos llamadores dejan sfStandardLigatures desactivado (predeterminado) emiten idénticos a nivel de byte a la salida de v2.119.64. Alcance: solo emite sustitutos de ligadura alcanzables a través de codepoints Alphabetic Presentation Form — las ligaduras latinas decorativas / discrecionales sin codepoint Unicode (GIDs específicos de fuente) no pueden emitirse a través de este camino y quedan reservadas para la emisión a nivel de GID de la Fase 8c.6.

2026-05-22 Version 2.119.64

  • API pública de consulta de avance por codepoint (hoja de ruta del motor GSUB Fase 8c.5): el nuevo método THotPDF.GetCodepointAdvance (CP: Cardinal): Single expone la caché /W de v2.76.0 para que los llamadores que construyen matemáticas personalizadas de word-wrap fuera del asistente BuildUnicodeMultilineFieldContent puedan consultar el avance em escalado real para cualquier codepoint, derivado de la tabla hmtx de la fuente vía la cmap cacheada. Después de que la cadena de post-pass de ligadura estática de v2.119.32 / v2.119.58 / v2.119.60 / v2.119.62 + la emisión GSUB de refinamiento a nivel de GID de v2.119.63 emiten codepoints de ligadura (LAM-ALEF FEF5-FEFC, YEH-HAMZA FBEA-FBFB, Allah FDF2, Bismillah FDFD y Presentation Forms sustituidas por GSUB en FB50-FDFF / FE70-FEFF), el método devuelve el avance hmtx real del glifo de ligadura — típicamente más estrecho que la suma de avances de los codepoints fuente — de modo que los presupuestos de wrap de los llamadores se alinean con la renderización del lado del consumidor.
  • Refinamiento del fallback heurístico de BuildUnicodeMultilineFieldContent: el camino de fallback de v2.65 (usado cuando la caché /W no está disponible, p. ej., camino RegisterUnicodeFontDict en lugar de RegisterUnicodeTTF) trataba previamente todos los codepoints >= U+2E80 como anchos (1.0 em). Las Arabic Presentation Forms (FB50-FBFF letras Forms-A, FC00-FDFF ligaduras Forms-A + FDF0-FDFD coránicas, FE70-FEFF formas básicas Forms-B + LAM-ALEF) son en realidad estrechas (~0.55 em de promedio en las principales fuentes árabes), no anchas. v2.119.64 enruta estos tres rangos de Arabic Presentation Form al camino estrecho (0.5 em) en el fallback heurístico, corrigiendo el caso límite donde las ligaduras árabes emitidas por los post-passes de v2.119.32 / v2.119.58 / v2.119.60 / v2.119.62 / v2.119.63 obtendrían avances erróneos de 1.0 em cuando la caché no está poblada.
  • Devuelve 0 para el camino típico sin información (sin fuente registrada, caché aún no poblada, codepoint por encima del BMP, codepoint sin glifo en la cmap de la fuente). Puramente funcional, sin efectos secundarios, seguro de llamar desde cualquier contexto después de que RegisterUnicodeTTF tenga éxito. Los PDFs cuya fuente cargada tiene la caché /W poblada y que no usan el nuevo método de consulta emiten idénticos a nivel de byte a la salida de v2.119.63. La Fase 8c.5 cierra el §8c del pipeline automático de shaping árabe GSUB en el lado del productor de la hoja de ruta del motor GSUB a nivel de capacidad — Las Fases 8c.1-8c.5 juntas entregan post-passes de tabla estática (4 familias de ligaduras) + mapeo inverso ToUnicode + refinamiento GSUB a nivel de GID + consulta pública de avance, todo trabajando junto para hacer que la renderización de ligaduras árabes en el lado del productor sea exacta a nivel de byte con la visualización del lado del consumidor.

2026-05-22 Version 2.119.63

  • Post-pass de refinamiento GSUB 'rlig' a nivel de GID (hoja de ruta del motor GSUB Fase 8c.2): el nuevo método THotPDF.ApplyArabicGSUBRefinement (Wide) recorre la cadena Wide de entrada de izquierda a derecha, construyendo un array paralelo de GIDs vía la cmap cacheada (FUnicodeCpToGid), y en cada posición con GID no cero consulta la característica GSUB 'rlig' (Required Ligatures) de la fuente vía la API ApplyLigatureSubstitution de v2.119.43-50. Si una sustitución de ligadura se dispara Y el GID sustituto es alcanzable a través de un codepoint Unicode Arabic Presentation Form (escaneo inverso de cmap a través de U+FB50-U+FDFF + U+FE70-U+FEFF, unos 690 codepoints), la ventana de codepoints fuente se reemplaza por el codepoint del sustituto. Los glifos sustitutos también se pasan a través de MarkUnicodeGlyphUsed para el cierre del subsetter TTF de v2.84.0.
  • Conectado en los tres asistentes BuildUnicode*FieldContent inmediatamente después de la cadena de post-pass estático a nivel de codepoint de v2.85.0 / v2.119.32 / v2.119.58 / v2.119.60 / v2.119.62 (LAM-ALEF / YEH-HAMZA / Allah / Bismillah). Complementa la tabla estática captando reglas 'rlig' específicas de fuente que las cuatro familias hardcoded no codifican — ejemplos típicos incluyen variantes contextuales en fuentes persas / urdu / sindhi / kurdas (Vazirmatn, Markazi Text, Lateef, Scheherazade, Amiri) que mapean a Unicode Presentation Forms más allá del conjunto LAM-ALEF / YEH-HAMZA / Allah / Bismillah. Se ejecuta solo cuando FAutoShapeArabic es true Y sfArabicGSUB NO está en FShapingFeatures (la exclusión mutua preserva el camino "dirigido por llamador" de v2.119.59) Y la fuente cargada tiene una tabla GSUB.
  • Límite de alcance: solo se emiten sustitutos cuyos GIDs SON alcanzables a través de un codepoint Presentation Form vía la cmap. Los sustitutos GSUB específicos de fuente que aterrizan en glyph IDs arbitrarios (sin mapeo inverso de codepoint) no pueden emitirse a través de este camino — necesitan emisión a nivel de GID completa en el pipeline del productor reservada para la Fase 8c.5+. El cmap inverso es un escaneo lineal sobre ~690 codepoints por intento de sustitución; las sustituciones son raras en la práctica, así que el coste es insignificante. Los PDFs cuya fuente cargada no tiene contenido GSUB 'rlig' o cuyos sustitutos no tienen codepoints Presentation Form emiten idénticos a nivel de byte a la salida de v2.119.62 (el método es un no-op defensivo en esos casos). Con esta porción, el pipeline automático de shaping árabe en el lado del productor del motor GSUB gana folding de ligaduras consciente de la fuente más allá de la cadena de tabla estática hardcoded.

2026-05-22 Version 2.119.62

  • Post-pass de ligadura a nivel de frase Bismillah (hoja de ruta del motor GSUB Fase 8c.4): el shaper árabe del lado del productor de v2.85.0 ahora pliega la frase canónica Bismillah de 22 codepoints "بسم الله الرحمن الرحيم" ("En el nombre de Dios, el Más Misericordioso, el Más Compasivo") a un solo codepoint U+FDFD ARABIC LIGATURE BISMILLAH AR-RAHMAN AR-RAHEEM. Se ejecuta PRIMERO en la cadena de post-pass (antes de los folds LAM-ALEF / YEH-HAMZA / Allah), de modo que la frase completa se detecta antes de que la sub-frase الله sea plegada por el post-pass Allah de v2.119.60. Cada posición de letra coincide con el codepoint base o cualquiera de sus variantes shapeadas Forms-B de v2.85.0 (letras R-joining 3 variantes cada una, D-joining 5 variantes cada una); las tres posiciones de espacio requieren U+0020 exacto. Detección a nivel de frase sobre la grafía canónica sin vocalizar.
  • CMap ToUnicode extendido con una 28ª entrada bfchar que mapea U+FDFD de vuelta a la secuencia fuente completa de 22 codepoints (BEH + SEEN + MEEM + SPACE + ALEF + LAM + LAM + HEH + SPACE + ALEF + LAM + REH + HAH + MEEM + NOON + SPACE + ALEF + LAM + REH + HAH + YEH + MEEM). La extracción de texto en el lado del consumidor (copiar / pegar / accesibilidad) ahora restaura la frase original de 22 codepoints en lugar del único glifo de ligadura, completando el comportamiento de ida y vuelta para las cuatro familias de ligaduras de tabla estática (LAM-ALEF / YEH-HAMZA / Allah / Bismillah).
  • Limitaciones conocidas (reducciones de alcance deliberadas): tatweel (U+0640) entre letras no se empareja; harakat / diacríticos entre letras no se emparejan (el fold canónico apunta a Bismillah sin vocalizar); espacio sin separación U+00A0 o múltiples espacios entre palabras no se emparejan (solo un único U+0020 ASCII); grafías alternativas (الرحمٰن con alef superscript) no se emparejan; la frase debe aparecer exactamente sin modificadores finales. El riesgo de falso positivo es esencialmente cero porque la secuencia canónica de 22 codepoints es por intención exclusivamente Bismillah. Otros honoríficos coránicos (FDFA SALLALLAHOU ALAYHE WASALLAM, FDFB JALLAJALALOUHOU) y ligaduras de una sola palabra (FDF3 AKBAR / FDF4 MOHAMMAD / FDF5 SALAM / etc.) quedan fuera de alcance porque esas palabras fuente también aparecen como términos árabes ordinarios en texto no religioso y generarían falsos positivos.

2026-05-22 Version 2.119.61

  • Mapeo inverso del CMap ToUnicode para ligaduras árabes (hoja de ruta del motor GSUB Fase 8c.3): el Adobe-Identity-UCS CMap de RegisterUnicodeTTF de v2.83.0 se extiende ahora con 27 entradas bfchar que sobreescriben el mapeo identidad bfrange para cada codepoint de ligadura emitido por la cadena de post-pass del shaper árabe en el lado del productor de v2.85.0. Cada entrada restaura la secuencia de codepoints fuente en la extracción de texto del lado del consumidor, corrigiendo la limitación donde copiar / pegar desde lectores PDF previamente devolvía un solo carácter de ligadura (p. ej., "ﷲ" para Allah) en lugar de la palabra fuente original ("الله"). Bfchar tiene precedencia sobre bfrange según la especificación de Adobe CMap and CIDFont Files.
  • Cobertura: 8 entradas LAM-ALEF U+FEF5-U+FEFC (ligadura mandatoria de v2.119.32, 4 variantes ALEF × 2 formas) → pares fuente LAM + ALEF / ALEF MADDA / ALEF HAMZA ABOVE / ALEF HAMZA BELOW; 18 entradas de la familia YEH-HAMZA U+FBEA-U+FBFB (v2.119.58, incluye las formas iniciales FBF8 y FBFB que el post-pass automático no produce pero los llamadores pueden usar directamente) → pares fuente YEH-HAMZA + ALEF / AE / WAW / U / OE / YU / E / ALEF MAKSURA; 1 entrada Allah U+FDF2 (v2.119.60) → secuencia fuente de cuatro codepoints ALEF + LAM + LAM + HEH. Con esta porción, el ida y vuelta de copiar / pegar / accesibilidad está completo para las tres familias de ligaduras de tabla estática.
  • Estable a nivel de byte para contenido no árabe: el bloque bfchar añade ~700 bytes de texto PostScript sin comprimir al stream del CMap, que FlateDecode típicamente reduce a ~150-200 bytes. Los PDFs sin contenido árabe todavía emiten el mismo layout del stream /ToUnicode — la extensión es aditiva dentro del mismo objeto indirecto del stream FlateDecode. Los lectores del consumidor (Adobe Reader, Foxit, PDF.js, Apple Preview, etc.) todos honran la precedencia de bfchar sobre bfrange y aplican el mapeo inverso correctamente. El bloque de 27 entradas está hardcoded en el texto del CMap (no se necesita personalización por PDF); la futura Fase 8c.4 añadirá entradas bfchar adicionales para la ligadura Bismillah U+FDFD.

2026-05-22 Version 2.119.60

  • Post-pass estático de ligadura Allah: el shaper árabe en el lado del productor de v2.85.0 ahora pliega la secuencia de cuatro codepoints ALEF + LAM + LAM + HEH (الله en árabe estándar) al único codepoint U+FDF2 ARABIC LIGATURE ALLAH ISOLATED FORM después de que los post-passes LAM-ALEF de v2.119.32 y YEH-HAMZA de v2.119.58 terminan. Mismo modelo que los dos post-passes de ligadura anteriores: detectar la secuencia fuente en cualquier combinación de formas crudas y shapeadas, emitir una salida de un solo codepoint, confiar en que la fuente tenga el glifo de ligadura en la entrada cmap definida por Unicode. Esta es la primera porción de la Fase 8c de la hoja de ruta del motor GSUB (pipeline automático de shaping árabe GSUB en el lado del productor) — folding de ligadura a nivel de codepoint para la ligadura religiosa árabe más prevalente.
  • El emparejamiento de formas de letras fuente cubre todas las combinaciones válidas después de que el walker de v2.85.0 ha shapeado la secuencia: ALEF en forma cruda U+0627 o aislada FE8D o final FE8E (R-joining, sin init / medi); LAM en forma cruda U+0644 o cualquiera de las cuatro formas shapeadas FEDD-FEE0 (D-joining, las 4 formas posicionales completas); HEH en forma cruda U+0647 o cualquiera de las cuatro formas shapeadas FEE9-FEEC (D-joining). Una palabra "الله" típica autónoma emerge del walker como FE8D + FEDF + FEE0 + FEEA (ALEF isol + LAM init + LAM medi + HEH final), que se pliega a FDF2. Los PDFs sin la secuencia de codepoints Allah emiten idénticos a nivel de byte a la salida de v2.119.59.
  • Limitaciones conocidas del folding de ligadura a nivel de codepoint: (1) La salida es solo la forma aislada FDF2 — Unicode no define formas final / inicial / medial para la ligadura Allah, pero en la práctica Allah casi siempre se renderiza autónomo, así que el único codepoint FDF2 es suficiente para la renderización visual. (2) La extracción de texto en el lado del consumidor (copiar / pegar / accesibilidad) produce el único carácter "ﷲ" en lugar de la palabra de cuatro caracteres "الله" porque el CMap ToUnicode aún no ha sido extendido para mapear inversamente FDF2 a su secuencia fuente de 4 codepoints. Una futura entrega Fase 8c.3 añadirá el mapeo inverso bfchar. (3) Dependencia de fuente: el TTF / OTF cargado debe tener una entrada cmap mapeando U+FDF2 a su glifo Allah; las principales fuentes árabes (Noto Sans Arabic, Amiri, Scheherazade, Lateef, Markazi Text, Tahoma / Times New Roman árabe) todas lo incluyen. Otras ligaduras coránicas (honoríficos de la familia Allah FDF0 / FDF1 / FDF3-FDFB, Bismillah FDFD) más integración GSUB a nivel de GID para sustituciones específicas de fuente quedan reservadas para futuras entregas Fase 8c.2-8c.4.

2026-05-22 Version 2.119.59

  • Framework opt-in del pipeline de shaping en el lado del productor (hoja de ruta del motor GSUB Fase 8a): la nueva enumeración THPDFShapingFeature más el tipo de conjunto THPDFShapingFeatures modelan las banderas de característica planificadas para la integración del shaping de texto en el lado del productor de las Fases 8a-8e. Los cinco miembros del enum son sfArabicGSUB (implementado en esta release), sfStandardLigatures (Fase 8b, futuro), sfContextualLigatures (Fase 8b, futuro), sfStylisticAlternates (Fase 8d, futuro) y sfIndicShaping (Fase 8e, futuro). THotPDF gana una propiedad ShapingFeatures de tipo THPDFShapingFeatures con valor predeterminado [] (todas las características desactivadas); los llamadores de v2.119.32-58 ven salida idéntica a nivel de byte mientras la propiedad se deje en su predeterminado.
  • sfArabicGSUB actúa como exclusión mutua contra el shaper árabe de tabla estática de v2.85.0. Cuando sfArabicGSUB está incluido en ShapingFeatures, los tres asistentes BuildUnicode*FieldContent (constructores de stream de apariencia /AP de AcroForm de una sola línea, multilínea y comb) omiten _ApplyArabicShaping por completo — sin reescritura de codepoints de cuatro posiciones de v2.85.0, sin post-pass LAM-ALEF de v2.119.32, sin post-pass YEH-HAMZA + vocal de v2.119.58. El llamador es responsable de dirigir la selección de formas árabes a través de las APIs del motor GSUB de v2.119.43-50 (SetGSUBScript ('arab') + GetSingleSubstituteGlyph contra los feature tags 'init' / 'medi' / 'fina' / 'isol' + MarkUnicodeGlyphUsed para el cierre del subsetter de v2.84.0). Combinado con sfArabicGSUB, los llamadores que usan Noto Sans Arabic / Amiri / Scheherazade / Markazi Text / fuentes árabes similares pueden ahora dirigir el shaping árabe GSUB completo desde el lado de la fuente en tiempo de productor en lugar de confiar en el fallback de tabla estática.
  • API complementaria de capacidad árabe: dos nuevos métodos públicos GetArabicJoiningClass (CP) y GetArabicPosition (Wide, Index) reflejan el patrón GetSyriacJoiningClass de v2.119.53 / GetMongolianJoiningClass de v2.119.54, exponiendo la tabla interna de joining-class y el walker contextual de 4 posiciones que el shaper estático usa. Los llamadores que dirigen shaping árabe GSUB manualmente ya no necesitan re-derivar datos de joining-class por codepoint — pueden reutilizar la tabla acumulativa de v2.85.0 / v2.119.35 / v2.119.52 / v2.119.57 que ya cubre el árabe básico (U+0600-U+06FF), Arabic Supplement (U+0750-U+077F) y Arabic Extended-A (U+08A0-U+08FF). Con esta porción aterrizada, la solicitud de dos elementos del usuario "Forms-A FBEA-FBFB ligaduras decorativas (v2.119.58) + Pipeline automático de shaping árabe GSUB en el lado del productor (esta release)" se cumple a nivel de capacidad + framework opt-in. La aplicación completa automática de GSUB en el lado del productor dentro de TextOut / BuildUnicode*FieldContent (auto cmap-to-GID + sustituto GSUB por posición + emisión de GID sustituto, sin código del llamador) queda reservada para los commits más profundos de integración Fase 8c.

2026-05-22 Version 2.119.58

  • Post-pass de ligadura YEH-HAMZA + letra-vocal: el shaper árabe en el lado del productor de v2.85.0 ahora pliega los 8 pares de ligadura codificados en el bloque Arabic Presentation Forms-A en U+FBEA-U+FBFB en codepoints de ligadura únicos después de que el walker de 4 posiciones y el post-pass LAM-ALEF de v2.119.32 se ejecuten. Mismo modelo y forma de integración que LAM-ALEF (sustitución mandatoria de tabla estática, salida de 2 formas por ligadura). Pares cubiertos: YEH-HAMZA + ALEF -> FBEA/FBEB (árabe estándar / persa / urdu), YEH-HAMZA + AE U+06D5 -> FBEC/FBED (cachemiri / uigur), YEH-HAMZA + WAW -> FBEE/FBEF (árabe estándar), YEH-HAMZA + U U+06C7 -> FBF0/FBF1 (uigur / kazajo / kirguís), YEH-HAMZA + OE U+06C6 -> FBF2/FBF3 (igual), YEH-HAMZA + YU U+06C8 -> FBF4/FBF5 (igual), YEH-HAMZA + E U+06D0 -> FBF6/FBF7 (igual), YEH-HAMZA + ALEF MAKSURA -> FBF9/FBFA (árabe estándar).
  • YEH-HAMZA se empareja en forma cruda U+0626 o en cualquiera de sus formas shapeadas de v2.85.0 (FE89 isol, FE8A final, FE8B init, FE8C medial); la letra-vocal siguiente se empareja en forma cruda o cualquiera de sus variantes shapeadas Forms-A de v2.119.57 (FBD7-FBD8 para U, FBD9-FBDA para OE, FBDB-FBDC para YU, FBE4-FBE7 para E) o variantes shapeadas Forms-B de v2.85.0 (FE8D-FE8E para ALEF, FEED-FEEE para WAW, FEEF-FEF0 para ALEF MAKSURA). La selección de forma final (base + 1) sigue la regla LAM-ALEF: cuando la YEH-HAMZA ha sido shapeada a FE8A final o FE8C medial (es decir, está precedida por una letra dual-joining), la ligadura se emite en su forma final; en otro caso la forma aislada.
  • Límites de alcance: solo las formas aislada y final (salida de 2 formas) son emitidas por este post-pass. Las formas iniciales definidas por Unicode FBF8 (YEH-HAMZA + E) y FBFB (UIGHUR YEH-HAMZA + ALEF MAKSURA) no se producen — los llamadores que necesitan esas variantes de tres formas dirigen los lookups GSUB chained-contextual 'rlig' / 'clig' de la fuente vía ApplyContextualSubst. Otras regiones de ligadura árabe (Allah U+FDFA / FDFB, ligaduras decorativas en FC00-FDC7) requieren dirigir GSUB 'rlig' / 'dlig' y quedan fuera de alcance para el shaper de tabla estática. Los PDFs sin YEH-HAMZA seguida por una de las ocho letras-vocal soportadas emiten idénticos a nivel de byte a v2.119.57; el nuevo post-pass solo se dispara en las secuencias específicas de 2 codepoints.

2026-05-22 Version 2.119.57

  • Cobertura de letras Arabic Extended persas / urdu / sindhi / cachemiri / uigur / kazajas / kirguís: la tabla de joining-class del shaper árabe en el lado del productor de v2.85.0 abarca ahora todo el rango U+0672-U+06D5 según Unicode 16.0 ArabicShaping.txt, cerrando el ítem de trabajo "Persa/Urdu Form-B 扩展" que fue diferido cuando v2.119.35 entregó el núcleo persa/urdu de 9 letras y v2.119.52 añadió las variantes ALEF WASLA / NOON GHUNNA / HEH. Unas 80 letras se añaden a la tabla de joining-class cubriendo variantes REH / DAL / SEEN / SAD / TAH / AIN / FEH / QAF / KAF / GAF / LAM / NOON / HEH / WAW / YEH — de modo que las letras adyacentes ahora eligen formas posicionales correctas independientemente de cuál letra Arabic Extended-A se sitúe junto a ellas.
  • 26 de las letras recién clasificadas tienen adicionalmente entradas estáticas Presentation Forms-A en U+FB52-U+FBFC y ahora son mapeadas directamente por el shaper de tabla estática sin requerir un shaper GSUB de fuente: 15 letras D-joining de 4 formas (TTEHEH U+067A → FB5E-FB61, BEEH U+067B → FB52-FB55, TEHEH U+067F → FB62-FB65, BEHEH U+0680 → FB5A-FB5D, NYEH U+0683 → FB76-FB79, DYEH U+0684 → FB72-FB75, TCHEHEH U+0687 → FB7E-FB81, VEH U+06A4 → FB6A-FB6D, PEHEH U+06A6 → FB6E-FB71, NG U+06AD → FBD3-FBD6, NGOEH U+06B1 → FB9A-FB9D, GUEH U+06B3 → FB96-FB99, RNOON U+06BB → FBA0-FBA3, HEH DOACHASHMEE U+06BE → FBAA-FBAD que es la letra estándar urdu 'h', E U+06D0 → FBE4-FBE7 para árabe kazajo/kirguís), y 11 letras R-joining de 2 formas (DAHAL U+068C → FB84-FB85, DDAHAL U+068D → FB82-FB83, DUL U+068E → FB86-FB87, KIRGHIZ OE U+06C5 → FBE0-FBE1, OE U+06C6 → FBD9-FBDA, U U+06C7 → FBD7-FBD8, YU U+06C8 → FBDB-FBDC, KIRGHIZ YU U+06C9 → FBE2-FBE3, VE U+06CB → FBDE-FBDF, YEH BARREE U+06D2 → FBAE-FBAF que es el yeh urdu final de palabra, YEH BARREE WITH HAMZA ABOVE U+06D3 → FBB0-FBB1).
  • Las dos letras HEH DOACHASHMEE y YEH BARREE merecen mención especial porque sus slots Forms-A (FBAA-FBAD y FBAE-FBAF) fueron lo que el bug de v2.119.52 asignó erróneamente a U+06C2 HEH GOAL WITH HAMZA ABOVE y U+06C3 TEH MARBUTA GOAL. Con v2.119.56 revirtiendo esos mapeos incorrectos y v2.119.57 vinculando los slots a las letras fuente correctas, el rastro de auditoría está limpio y la confusión queda permanentemente resuelta. Las letras en el rango U+0672-U+06D5 sin entradas Presentation Forms-A en Unicode 16 (unos 50 codepoints adicionales, mayormente variantes REH / DAL / SEEN / SAD / TAH / AIN / FEH / QAF / KAF sin formas shapeadas pre-codificadas) aún participan en el análisis de joining-class de modo que los vecinos se shapean correctamente; las letras mismas pasan a través como codepoints crudos y dependen de las tablas de lookup GSUB de la fuente bajo el motor GSUB de v2.119.43-50 para su propia selección de forma. Compilado en 78018 líneas; los PDFs sin estos nuevos codepoints emiten idénticos a nivel de byte a v2.119.56.

2026-05-22 Version 2.119.56

  • Corrección de bug para shaping árabe: v2.119.52 introdujo mapeos Arabic Presentation Forms-A incorrectos para dos caracteres de la región urdu/sindhi. U+06C2 HEH GOAL WITH HAMZA ABOVE fue mapeado a FBAA-FBAD, que es en realidad el rango de codepoints para U+06BE HEH DOACHASHMEE; los lectores del consumidor renderizarían el glifo urdu Goal-Heh-with-Hamza como el glifo urdu estándar Heh-Doachashmee. U+06C3 TEH MARBUTA GOAL fue mapeado a FBAE-FBAF, que es en realidad el rango de codepoints para U+06D2 YEH BARREE; los lectores renderizarían Teh-Marbuta-Goal como el Yeh-Barree urdu final de palabra. Según Unicode 16.0, ni U+06C2 ni U+06C3 tienen entradas Presentation Forms-A pre-codificadas; ambos deben pasar sin cambios como codepoints crudos (los lectores del consumidor los muestran correctamente vía la cmap de la fuente y cualquier shaping dirigido por GSUB).
  • Fix: las dos entradas case erróneas en la tabla de lookup Presentation Forms-A de _ArabicShape del shaper árabe en el lado del productor de v2.85.0 han sido eliminadas; U+06C2 y U+06C3 ahora caen al camino de passthrough y emiten sin cambios. Las entradas de joining-class de v2.119.52 (D para U+06C2, R para U+06C3) se conservan de modo que las letras adyacentes aún elijan formas posicionales correctas cuando estos caracteres aparezcan en una palabra. El fix solo afecta a PDFs que contengan U+06C2 o U+06C3 con AutoShapeArabic habilitado; los PDFs sin estos dos codepoints son idénticos a nivel de byte a la salida de v2.119.55. La release acompañante v2.119.57 expande la cobertura Forms-A a letras persas / urdu / sindhi / cachemiri / uigur / kazajas / kirguís adicionales, incluyendo los genuinos HEH DOACHASHMEE y YEH BARREE codificados Forms-A que el error de v2.119.52 confundía.

2026-05-22 Version 2.119.55

  • Capacidad de shaping índico devanagari: dos nuevos métodos públicos en THotPDF cubren el paradigma de shaping índico que no encaja en el walker de joining-class de 4 posiciones usado por árabe / siriaco / mongol (v2.85.0 + v2.119.32-54). GetDevanagariCategory (CP) devuelve la categoría silábica índica simplificada (0 = Otro, 1 = Consonante, 2 = Vocal independiente, 3 = Matra, 4 = Virama, 5 = Nukta, 6 = Bindu, 7 = Visarga, 8 = Danda, 9 = Dígito, 10 = ZWJ, 11 = ZWNJ, 12 = Modificador) según Unicode 16.0 §12.1 + IndicSyllabicCategory.txt; la tabla cubre el bloque devanagari completo U+0900-U+097F incluyendo las extensiones marwari / sindhi / védicas en U+0978-U+097F.
  • ApplyDevanagariReorder (Wide) recorre la cadena de entrada de izquierda a derecha y aplica el pre-pass de reordenación devanagari a cada sílaba devanagari encontrada, devolviendo una UnicodeString reordenada lista para consumo cmap + GSUB. El contenido no devanagari (latín, dígitos, puntuación, otros scripts) pasa idéntico a nivel de byte. Se implementan dos reordenaciones (las dos que dominan el shaping devanagari del mundo real): (1) Repha — cuando una sílaba empieza con Ra (U+0930) + Halant (U+094D) + Consonante, el par (Ra, Halant) se mueve al FINAL de la sílaba de modo que la característica GSUB 'rphf' de la fuente lo sustituye por el glifo Repha que se adhiere visualmente arriba-derecha de la base de la sílaba; (2) I-matra pre-base — U+093F se mueve al INICIO mismo de la sílaba (tras cualquier eliminación de Repha) de modo que el procesamiento GSUB de izquierda a derecha la ve delante del cluster consonántico, coincidiendo con su orden de renderización visual.
  • Pipeline del llamador: ApplyDevanagariReorder sobre el texto en orden lógico -> SetGSUBScript ('deva') -> recorrer codepoints reordenados aplicando características GSUB ('nukt' / 'akhn' / 'rphf' / 'rkrf' / 'half' / 'vatu' / 'cjct' / 'pres' / 'blws' / 'abvs' / 'psts' / 'haln') vía GetSingleSubstituteGlyph + ApplyLigatureSubstitution + ApplyContextualSubst -> emitir los GIDs resultantes al flujo de texto del PDF llamando a MarkUnicodeGlyphUsed (GID) por emisión para el cierre del subsetter de v2.84.0. Con la Fase A (v2.119.52 cierre Arabic Extended-A) + Fase B (v2.119.53 capacidad siriaca) + Fase C (v2.119.54 capacidad mongola) + Fase D (v2.119.55 capacidad índica devanagari) todas aterrizadas, el gap de la matriz "shaping mongol / siriaco / devanagari" queda completamente cerrado. Fuera de alcance: scripts índicos no devanagari (bengalí, tamil, telugu, gujarati — cada uno tiene sus propios datos de categoría y reglas de reordenación), reglas de reordenación más allá de Repha + I-matra pre-base, y shaping devanagari automático en el lado del productor dentro de TextOut / BuildUnicode*FieldContent (reservado para la Fase 8 de la hoja de ruta del motor GSUB).

2026-05-22 Version 2.119.54

  • Capacidad de shaping mongol: dos nuevos métodos públicos en THotPDF exponen la consulta de joining-class y el análisis contextual de 4 posiciones del mongol (U+1800-U+18AF) para que los llamadores puedan controlar el shaping de texto mongol desde el lado del productor. GetMongolianJoiningClass (CP) devuelve la joining class mongola según Unicode 16.0 §13.5 (0 = U no-conectivo, 2 = D conectivo doble, 4 = T transparente / selector de variación) para cualquier codepoint; la tabla cubre el mongol básico más las extensiones de letras Todo, Sibe, Manchú y Ali Gali (U+1820-U+1878 + U+1887-U+18A8 + ALI GALI DAGALGA U+18A9 + MANCHU ALI GALI LHA U+18AA) y clasifica NIRUGU (U+180A), los tres Free Variation Selectors FVS1-FVS3 (U+180B-U+180D) y las marcas vocálicas Ali Gali BALUDA / THREE BALUDA (U+1885-U+1886) como transparentes. GetMongolianPosition (Wide, Index) recorre el texto circundante saltando las marcas transparentes y devuelve 0 = aislado, 1 = final, 2 = inicial o 3 = medial para la letra en el Index 0-based.
  • Al igual que el siriaco (v2.119.53), el mongol NO tiene Presentation Forms preencodificados en Unicode — cada fuente con capacidad mongola (Mongolian Baiti, Noto Sans Mongolian, Noto Serif Mongolian, etc.) controla el shaping posicional mediante lookups OpenType GSUB en sus características 'init' / 'medi' / 'fina' / 'isol' bajo el script tag 'mong'. v2.119.54 sigue por tanto el modelo de solo capacidad de v2.119.53; los llamadores componen la salida de posición con las APIs del motor GSUB de v2.119.43-50 (SetGSUBScript ('mong'), GetSingleSubstituteGlyph contra el feature tag posicional apropiado, MarkUnicodeGlyphUsed para el cierre del subsetter de v2.84.0) para producir GIDs ya shapeados que se emiten directamente en el flujo de texto PDF.
  • El mongol se escribe verticalmente de arriba abajo en su modo de escritura nativo; el walker opera sobre el orden LÓGICO de codepoints (predecesor / sucesor en el flujo de codepoints), independiente de la orientación visual. La renderización efectiva de arriba abajo se gestiona en el momento de emisión del PDF mediante /WMode 1 (escritura vertical) o rotación de la font-matrix, no por el shaper. La clasificación de los Free Variation Selectors como transparentes implica que el walker resuelve correctamente las formas posicionales incluso cuando un FVS sigue a una letra; el lookup GSUB de la fuente bajo las características 'fina' / 'medi' / 'init' / 'isol' selecciona automáticamente la variante condicionada por FVS cuando un FVS aparece en la secuencia de entrada. El shaping devanagari (índico) queda reservado para versiones futuras porque requiere una pasada previa de reordenación índica según UAX #38 (virama / cluster consonántico / reordenación pre-base) que no se ajusta al walker de 4 posiciones usado por árabe / siriaco / mongol. El shaping mongol automático dentro de TextOut / BuildUnicode*FieldContent queda reservado para la Fase 8 de la hoja de ruta del motor GSUB.

2026-05-22 Version 2.119.53

  • Capacidad de shaping siriaco: dos nuevos métodos públicos en THotPDF exponen la consulta de joining-class y el análisis contextual de 4 posiciones del siriaco (U+0700-U+074F) para que los llamadores puedan controlar el shaping de texto siriaco desde el lado del productor. GetSyriacJoiningClass (CP) devuelve la joining class según SyriacShaping.txt de Unicode 16.0 (0 = U no-conectivo, 1 = R conectivo a la derecha, 2 = D conectivo doble, 4 = T transparente / NSM) para cualquier codepoint; la tabla cubre las 35 letras siriacas del bloque base más las extensiones persa y sogdiana (PERSIAN BHETH / GHAMAL / DHALATH en U+072D-U+072F, SOGDIAN ZHAIN / KHAPH / FE en U+074D-U+074F) y clasifica el SUPERSCRIPT ALAPH (U+0711) y las marcas combinatorias siriacas (U+0730-U+074A) como transparentes. GetSyriacPosition (Wide, Index) recorre el texto circundante saltando las marcas transparentes y devuelve 0 = aislado, 1 = final, 2 = inicial o 3 = medial para la letra en el Index 0-based.
  • A diferencia del árabe (que dispone de los bloques Presentation Forms-B U+FE70-U+FEFF y Forms-A U+FB50-U+FBFF preencodificados que permiten a HotPDF reescribir directamente los codepoints a sus variantes shapeadas), el bloque siriaco NO tiene presentation forms preencodificadas en Unicode — cada fuente con capacidad siriaca (Estrangelo Edessa, Serto Jerusalem, East Syriac Adiabene, Noto Sans Syriac, etc.) controla el shaping posicional mediante lookups OpenType GSUB en sus características 'init' / 'medi' / 'fina' / 'isol'. Por ello v2.119.53 entrega solo la capa de capacidad; el llamador compone la salida de posición con las APIs del motor GSUB de v2.119.43-50 (SetGSUBScript ('syrc'), GetSingleSubstituteGlyph contra el feature tag posicional apropiado, MarkUnicodeGlyphUsed para el cierre del subsetter de v2.84.0) para producir GIDs ya shapeados que se emiten directamente en el flujo de texto PDF.
  • Alcance de la forma terminal de Alaph: Unicode §9.3 documenta dos características terminales contextuales ('fin2' aplicada tras DALATH / RISH, 'fin3' aplicada tras FINAL LAMADH) que las fuentes siriacas usan para intercambiar el glifo final de Alaph. v2.119.53 clasifica Alaph (U+0710) como básicamente conectivo a la derecha y devuelve únicamente 0 (aislado) o 1 (final); los llamadores que necesiten diferenciación 'fin2' / 'fin3' activan los chained-contextual lookups de la fuente mediante ApplyContextualSubst con esos feature tags. El shaping mongol y devanagari (índico) queda reservado para versiones futuras: el mongol usa Free Variation Selectors más armonía vocálica, el devanagari requiere pasada previa de reordenación índica según UAX #38, ninguno se ajusta al walker de 4 posiciones tipo siriaco. El shaping siriaco automático dentro de TextOut / BuildUnicode*FieldContent queda reservado para la Fase 8 de la hoja de ruta del motor GSUB.

2026-05-22 Version 2.119.52

  • Extensión del shaping de la familia árabe: el shaper estático árabe del lado del productor de v2.85.0 cubre ahora los caracteres restantes de Arabic Extended-A (ALEF WASLA U+0671, NOON GHUNNA U+06BA, variantes HEH U+06C0-U+06C3), el bloque Arabic Supplement U+0750-U+077F (letras árabes africanas usadas en hausa, wolofal y otras lenguas africanas) y la nueva región Arabic Extended-A U+08A0-U+08FF (árabe coránico + letras extendidas wolofal/hausa + marcas combinatorias coránicas). Las joining classes se toman de ArabicShaping.txt de Unicode 16.0 y se aplican durante el análisis posicional de cuatro posiciones de v2.85.0 para que los vecinos elijan la forma init / medi / fina / isol correcta sin importar junto a qué variante árabe se sitúen.
  • Las letras que disponen de presentation forms estáticas preencodificadas en el bloque Arabic Presentation Forms-A (U+FB50-U+FBFF) se mapean ahora directamente a esas formas sin necesitar un shaper OpenType GSUB: ALEF WASLA -> FB50/FB51 (conectivo a la derecha, dos formas), NOON GHUNNA -> FB9E/FB9F (conectivo doble según Unicode pero solo con dos formas preencodificadas; init/medi degradan limpiamente a isol), HEH WITH YEH ABOVE -> FBA4/FBA5 (conectivo a la derecha, dos formas), HEH GOAL -> FBA6/FBA7/FBA8/FBA9 (conectivo doble, las cuatro formas completas), HEH GOAL WITH HAMZA ABOVE -> FBAA/FBAB/FBAC/FBAD (conectivo doble, las cuatro formas completas), TEH MARBUTA GOAL -> FBAE/FBAF (conectivo a la derecha, dos formas). Combinado con el post-pass de ligadura obligatoria LAM-ALEF de v2.119.32 y el subconjunto persa/urdu de 9 letras núcleo de v2.119.35, el shaper estático cubre ya prácticamente todo el texto árabe moderno, persa, urdu, sindhi, pashto, kurdo, uigur, coránico y árabe africano (hausa, wolofal, etc.) sin depender de la tabla GSUB de la fuente.
  • Arabic Supplement (U+0750-U+077F) y la región más reciente de letras Arabic Extended-A (U+08A0-U+08C7) no tienen presentation forms estáticas preencodificadas; sus entradas de joining-class permiten que los vecinos se shapeen correctamente, pero las letras propiamente dichas permanecen como codepoints crudos y dependen de una fuente con soporte GSUB (o de las APIs del motor GSUB de v2.119.43-50 manejadas por el llamador) para su propio shaping posicional. La región de marcas combinatorias coránicas Arabic Extended-A (U+08CA-U+08E1, U+08E3-U+08FF) se clasifica como transparente (T-joining) para que el análisis posicional de los vecinos las salte, alineándose con el tratamiento existente de las harakat U+064B-U+065F. El texto que no incluya estos caracteres extendidos sigue siendo idéntico a nivel de byte respecto a la salida de v2.119.51. El shaping mongol / siriaco / devanagari (con modelos de shaping distintos del árabe) queda reservado para revisiones futuras.

2026-05-22 Version 2.119.51

  • Soporte de contenedor XFA (XML Forms Architecture) del lado del productor: PDF 1.7 §12.7.6 + §12.7.8 permite que un AcroForm lleve una entrada /XFA junto al (o en lugar del) array estático /Fields. El nuevo THotPDF.AddXFAPacket (PacketName, XMLBytes) registra un paquete XFA con nombre — los nombres típicos son 'template' (layout del formulario + scripts), 'datasets' (vinculación de datos), 'config' (configuración del visor), 'connectionSet', 'localeSet', 'stylesheet', 'xfdf', 'xmpmeta', 'signature' y 'sourceSet'. Cada paquete registrado se convierte en un stream indirecto comprimido con FlateDecode en EndDoc, y el array /XFA alterna cadenas PDF de nombre de paquete con referencias al stream en orden de registro, de modo que el layout es determinístico a nivel de byte. THotPDF.ClearXFAPackets y THotPDF.XFAPacketCount completan la superficie de la API.
  • Solo soporte a nivel de contenedor — HotPDF NO parsea el XML XFA, NO implementa el motor de layout dinámico XFA, NO ejecuta FormCalc ni JavaScript dentro de la plantilla XFA y NO valida la estructura de la plantilla. El llamador redacta el XML XFA por su cuenta (típicamente con Adobe LiveCycle Designer o componiéndolo a mano contra la especificación XFA 3.3) y HotPDF emite los bytes verbatim. Los lectores con motor XFA (Acrobat / Reader antiguos, FormsCentral, algunos productos de quiosco) renderizan el formulario; los lectores sin motor XFA (Adobe Reader DC desde 2017 en adelante, la mayoría de los lectores open-source) muestran los campos AcroForm de fallback si el documento sigue un flujo híbrido AcroForm + XFA.
  • Gates de conformidad PDF/A y PDF/X: ISO 19005-1 §6.4.2 / ISO 19005-2 §6.4.2 / ISO 19005-3 §6.4.2 prohíben explícitamente los formularios XFA (el motor de layout dinámico no es archivable de manera determinística). Los flujos de preimpresión ISO 15930 rechazan por completo los formularios interactivos. AddXFAPacket lanza inmediatamente bajo cualquier PDFACompliance o PDFXCompliance no vacío, con la referencia de spec relevante. Los nombres de paquete duplicados lanzan excepción. PacketName vacío o XMLBytes vacíos lanzan excepción. RequirePDFVersion (pdf15) sube automáticamente la versión del documento. El dict AcroForm se emite ahora cuando /Fields tiene al menos una entrada O se ha registrado al menos un paquete XFA; los llamadores anteriores a v2.119.51 que no registran paquetes mantienen una salida /AcroForm idéntica a nivel de byte.

2026-05-22 Version 2.119.50

  • Cierre del TTF subsetter para los glifos sustituidos por GSUB: el nuevo THotPDF.MarkUnicodeGlyphUsed (GID: Word) incorpora un glifo al subsetter de EndDoc de v2.84.0 independientemente de que el cmap tenga algún codepoint que llegue a ese glifo. El cierre de v2.84.0 deriva su conjunto de glifos usados desde FUnicodeUsedCps a través del cmap, por lo que cada glifo sustituido introducido por GSUB — alternativas estilísticas, ligaduras, variantes contextuales, la salida completa de las APIs de consulta de las Fases 1-6 — era previamente invisible para el subsetter y el lector consumidor renderizaba .notdef en su lugar. Tras emitir cualquier GID devuelto por GetSingleSubstituteGlyph / GetMultipleSubstituteGlyphs / GetAlternateGlyph / ApplyLigatureSubstitution / ApplyContextualSubst / ApplyReverseChainedContextualSubst en un flujo de texto PDF, el llamador llama ahora MarkUnicodeGlyphUsed (GID) una vez por cada GID emitido para incorporarlo al subset embebido.
  • El helper es idempotente (las llamadas repetidas con el mismo GID son un no-op), defensivo (GID >= FUnicodeNumGlyphs se descarta silenciosamente, las llamadas anteriores a un RegisterUnicodeTTF exitoso son no-ops) y se integra con la pasada de cierre de glifos compuestos de v2.84.0 dentro de _BuildSubsetTTF: los llamadores solo necesitan marcar el GID sustituto de nivel superior — los componentes compuestos los incorpora automáticamente el walk existente _TTFWalkCompositeClosure. El conjunto se asigna de forma diferida en la primera llamada y se reinicia a vacío en cada RegisterUnicodeTTF ('', nil) junto con el resto del estado del subset. Con la Fase 9 terminada, cada API de consulta GSUB de las Fases 1-7 puede emparejarse de forma segura con la emisión del subset sin que los glifos sustituidos queden fuera de la fuente embebida.

2026-05-22 Version 2.119.49

  • API de selección Script / LangSys de OpenType GSUB: cinco nuevos métodos públicos en THotPDF permiten a los llamadores fijar los lookups GSUB a un script y lenguaje concretos, en lugar del fallback histórico de script-DFLT / LangSys por defecto. SetGSUBScript ('latn' / 'arab' / 'cyrl' / 'hani' / 'kana' / 'deva' / 'beng' / 'taml' / etc.) selecciona un script tag de OpenType; la cadena vacía por defecto mantiene la línea base v2.119.43-48 (preferir 'DFLT', si no el primer script). SetGSUBLanguage ('ENG ' / 'TUR ' / 'AZE ' / 'JAN ' / 'KOR ' / 'ARA ' / etc., rellenado con espacios al final hasta 4 bytes) selecciona un language tag de OpenType; la cadena vacía es el LangSys por defecto del script. Ambos ajustes persisten entre consultas GSUB y se reinician a vacío en RegisterUnicodeTTF ('', nil).
  • Tres nuevos helpers de enumeración exponen lo que la fuente cargada anuncia: GetGSUBScripts: TGSUBStringArray devuelve todos los script tags en orden de ScriptList; GetGSUBLanguages (ScriptTag) devuelve todos los language tags bajo el script dado (con un placeholder '' en el índice 0 cuando el script tiene un LangSys por defecto); GetGSUBFeatures (ScriptTag, LangTag) devuelve todos los feature tags (p. ej. 'liga', 'salt', 'aalt', 'ss01' hasta 'ss20') anunciados por la ruta (script, lenguaje) en orden de los featureIndices del LangSys. ScriptTag / LangTag vacíos usan el mismo fallback DFLT-primero / LangSys-por-defecto que las APIs de sustitución, de modo que los llamadores pueden invocar GetGSUBFeatures ('', '') para enumerar las características del camino por defecto sin inspeccionar antes la lista de scripts.
  • Semántica estricta-vs-fallback en la selección: cuando SetGSUBScript se fija a un tag que la fuente cargada no anuncia, las consultas GSUB posteriores devuelven resultados vacíos / no-op — el motor NO cae silenciosamente al DFLT, así los llamadores saben con claridad que su script elegido no está disponible. Cuando SetGSUBLanguage se fija a un tag no reconocido, SÍ cae al LangSys por defecto del script según la recomendación de la spec OpenType. Los siete métodos GSUB de sustitución existentes (GetSingleSubstituteGlyph / GetMultipleSubstituteGlyphs / GetAlternateGlyphCount / GetAlternateGlyph / ApplyLigatureSubstitution / ApplyContextualSubst / ApplyReverseChainedContextualSubst) mantienen sus firmas públicas idénticas a nivel de byte; solo el helper interno _GSUBFindFeatureLookups crece con dos parámetros finales que las siete sites de llamada ahora encaminan. Con la API Script / LangSys y la matriz LookupType 1-8 ya entregadas, el motor GSUB está feature-complete para uso solo-capacidad; las fases restantes de la hoja de ruta (pipeline de shaping automático + cierre del TTF subsetter) apuntan a la integración del lado del productor en lugar de a nueva superficie de consulta.

2026-05-22 Version 2.119.48

  • OpenType GSUB Reverse Chained Contextual Single Substitution (LookupType 8): el nuevo THotPDF.ApplyReverseChainedContextualSubst (const InputGIDs; StartIndex; FeatureTag; out OutGID): Boolean es el último tipo de lookup GSUB en aterrizar. Type 8 realiza una sustitución 1:1 con conciencia de contexto cuyo sustituto se selecciona por el índice Coverage del glifo de entrada, y cuya característica distintiva es que los llamadores deben aplicarlo en orden de escaneo INVERSO sobre una secuencia multi-glifo (fin → inicio) porque cada sustituto puede depender del contexto lookahead FUTURO que aún no debe haber sido sustituido. El helper es un applier de punto único — el llamador maneja el bucle de escaneo inverso. Coverage Format 1 + 2, honor de LookupFlag (input + backtrack + lookahead todos skip-aware) y empaquetado Extension (LookupType 7) están todos soportados.
  • Caso de uso canónico: ciertas alternativas contextuales árabes / siriacas / N'Ko / índicas cuya forma final depende de que el glifo siguiente tenga una forma específica. Algunas fuentes lo conectan también para desambiguación de secuencias latinas. A diferencia de LookupType 5/6, Type 8 no tiene maquinaria de nested-lookup, ya que la sustitución es intrínsecamente 1:1. Sin coincidencia devuelve False, OutGID = InputGIDs[StartIndex] (passthrough de mejor esfuerzo que casa con el contrato de GetSingleSubstituteGlyph de v2.119.43).
  • Cierra la matriz LookupType: con v2.119.48, cada LookupType de OpenType GSUB (de 1 a 8) tiene una capacidad pública dedicada en THotPDF. La API de selección Script / LangSys, la integración del pipeline de shaping automático y el auto-pull del TTF subsetter para los glifos sustituidos quedan reservados para las Fases 7-9; los llamadores siguen siendo responsables de marcar los glifos sustitutos elegidos en el subset de la fuente embebida y de manejar por sí mismos el bucle de escaneo inverso.

2026-05-22 Version 2.119.47

  • Sustitución Contextual + Chained Contextual de OpenType GSUB (LookupType 5 + 6): el nuevo THotPDF.ApplyContextualSubst (const InputGIDs; StartIndex; FeatureTag; var OutGIDs; out ConsumedLen): Boolean es un punto de entrada único que cubre tanto LookupType 5 (contexto solo de entrada) como LookupType 6 (contexto backtrack / input / lookahead) a través de los tres formatos de subtabla — Format 1 (secuencias literales de glyph-IDs), Format 2 (secuencias de clases ClassDef) y Format 3 (secuencias de Coverage-tables, la forma canónica usada por la mayoría de fuentes contemporáneas). Los usuarios canónicos son 'rclt' (Required Contextual Alternates — formas posicionales árabes init/medi/fina/isol cuando la fuente las maneja vía GSUB), 'clig' (Contextual Ligatures), 'calt' (Contextual Alternates), y las características de shaping índico 'pres' / 'blws' / 'psts' / 'half' / 'pstf' / 'cjct'.
  • Dispatcher de nested lookup SequenceLookupRecord: cuando una regla contextual coincide, las entradas SequenceLookupRecord disparan cada una un nested lookup en una posición específica dentro de la sub-secuencia de entrada coincidente. El nuevo helper _GSUBApplyNestedLookup reentra en el GSUB LookupList, resuelve los wrappers Extension de forma transparente y despacha a los appliers Single (Type 1) / Multiple (Type 2) / Alternate (Type 3, primera alternativa) / Ligature (Type 4). El dispatcher rastrea los índices MatchPositions[] en vivo para que las entradas SequenceLookupRecord posteriores sigan apuntando a las posiciones de trabajo correctas incluso cuando una Multiple Substitution previa expande 1→N o una Ligature Substitution colapsa N→1 glifos a su alrededor. El nested LookupType 5 / 6 / 8 (contextual recursivo) se difiere intencionadamente; la mayoría de fuentes reales conectan sus nested lookups a Type 1 / 4 de todos modos.
  • Cuando hay coincidencia, el método devuelve True junto con la secuencia de glifos de salida que debe reemplazar InputGIDs[StartIndex..StartIndex+ConsumedLen-1]. La longitud de OutGIDs puede diferir de ConsumedLen cuando se disparan nested lookups 1→N o N→1. ConsumedLen es siempre el span total de entrada que absorbió la regla contextual (incluyendo las marcas saltadas por LookupFlag entre glifos significativos); un llamador típico en bucle de escaneo avanza ConsumedLen y emite OutGIDs. Sin coincidencia devuelve False, OutGIDs vacío, ConsumedLen = 1 — el mismo contrato seguro de no-op que los helpers de v2.119.43-46. El honor de LookupFlag (Fase 4) se aplica durante toda la coincidencia contextual: los walks de backtrack / input / lookahead saltan todos los glifos marcados como ignorables. Script / LangSys permanecen anclados al DFLT por defecto en este slice; la API de selección es el alcance de la Fase 7.

2026-05-22 Version 2.119.46

  • Extension Lookups de OpenType GSUB (LookupType 7): el motor GSUB ahora desempaqueta de forma transparente las subtablas Extension Substitution — una capa de indirección pura que la spec OpenType define para fuentes tan grandes que su subtabla de sustitución real vive más allá del alcance de 16 bits del Offset16 de la LookupList. Cada API pública v2.119.43-45 (GetSingleSubstituteGlyph / GetMultipleSubstituteGlyphs / GetAlternateGlyphCount / GetAlternateGlyph / ApplyLigatureSubstitution) sigue automáticamente la indirección Offset32 de 32 bits hasta la subtabla LookupType 1 / 2 / 3 / 4 real. Este es el cambio de layout binario que desbloquea las pesadas fuentes OpenType CJK / índicas (Noto Sans CJK, Noto Sans Devanagari, etc.) cuyas tablas GSUB superan los 64 KB y llevan tiempo enviando sus lookups detrás de wrappers LookupType 7; previamente esas fuentes parecían vacías de características para HotPDF.
  • Parseo de la tabla GDEF (Glyph Definition) de OpenType: RegisterUnicodeTTF ahora también cachea la tabla GDEF de la fuente cuando está presente. Tres subtablas GDEF gobiernan la lógica de honor de LookupFlag — GlyphClassDef clasifica cada GID como base (1) / ligadura (2) / marca (3) / componente (4); MarkAttachClassDef etiqueta cada glifo de marca con una clase de attachment; MarkGlyphSetsDef (v1.2+) declara conjuntos con nombre de glifos de marca. ClassDef Format 1 (startGlyphID + classValueArray) y Format 2 (classRangeRecords ordenados) están ambos implementados; las cabeceras GDEF v1.0 / v1.1 / v1.2 se aceptan todas (el itemVariationStore de v1.3 se ignora). Las fuentes sin tabla GDEF retroceden al comportamiento de línea base de v2.119.43-45 ("ningún glifo se ignora"), de modo que se preserva la salida idéntica a nivel de byte para los llamadores que usan fuentes sin GDEF.
  • Honor del LookupFlag GSUB de OpenType: las cinco APIs públicas GSUB leen ahora el LookupFlag de cada tabla Lookup (y el uint16 markFilteringSet final opcional cuando LookupFlag.useMarkFilteringSet está activo) y saltan los glifos de entrada marcados como ignorables. Los bits de flag definidos por la spec se honran: 0x0002 ignoreBaseGlyphs (saltar clase 1 de GDEF.GlyphClassDef), 0x0004 ignoreLigatures (saltar clase 2), 0x0008 ignoreMarks (saltar clase 3), 0x0010 useMarkFilteringSet (solo las marcas del MarkGlyphSet nombrado participan) y el byte alto markAttachmentType (solo las marcas con la clase MarkAttachClassDef solicitada participan). Para Ligature Substitution (LookupType 4) esto es lo más consecuente: las ligaduras 'rlig' árabes / 'akhn' índicas cuyos componentes están separados por glifos de marca (LAM + Fatha + ALEF, por ejemplo) ahora coinciden correctamente con LookupFlag.ignoreMarks fijado, y el ConsumedCount devuelto incluye las marcas saltadas para que el bucle de escaneo del llamador avance más allá de cada glifo de entrada absorbido. El bit 0x0001 (rightToLeft) es específico de GPOS y permanece ignorado para los lookups GSUB.
  • Se mantiene el mismo contrato defensivo: las fuentes sin tabla GSUB, los llamadores que pasan un tag que no es de 4 bytes, las características que el script DFLT de la fuente no anuncia, los GIDs que ninguna subtabla cubre y ahora los glifos de entrada ignorados por LookupFlag devuelven todos el mismo resultado seguro de no-op de v2.119.43-45. Las firmas de las cinco APIs públicas son estables a nivel de byte; solo el walk interno de lookup recoge el dispatch Extension + el honor de LookupFlag + la clasificación GDEF. La API de selección Script / LangSys, la integración del pipeline de shaping automático y el auto-pull del TTF subsetter para los glifos sustituidos quedan reservados para revisiones futuras.

2026-05-22 Version 2.119.45

  • OpenType GSUB Ligature Substitution (LookupType 4): el nuevo THotPDF.ApplyLigatureSubstitution (const InputGIDs: array of Word; StartIndex; FeatureTag; out OutGID; out ConsumedCount): Boolean colapsa una secuencia de componentes multi-glifo en un único glifo de ligadura. Los usuarios canónicos son 'liga' (Standard Ligatures — fi / fl / ffi / ffl), 'clig' (Contextual Ligatures), 'dlig' (Discretionary Ligatures), 'hlig' (Historical Ligatures), 'rlig' (Required Ligatures — LAM-ALEF árabe y similares cuando la fuente maneja el shaping vía GSUB) y las características de ligadura de scripts índicos ('akhn', 'pres', 'blws', 'psts'). El llamador pasa la secuencia de glyph-IDs post-cmap y una posición de inicio 0-based; ante una coincidencia completa el helper devuelve True, OutGID = el glifo de ligadura sustituto y ConsumedCount = el número total de componentes de entrada consumidos (>= 2 en la práctica). Sin coincidencia devuelve False, OutGID = 0, ConsumedCount = 1 para que el llamador pueda avanzar un paso y continuar el escaneo. La implementación honra la convención OpenType de que las entradas LigatureSet se listan típicamente de más largo a más corto, permitiendo a las fuentes con prefijos solapados (p. ej. una ligadura 'ffi' de tres componentes junto a una ligadura 'ff' de dos componentes) obtener la coincidencia más larga. Mismo contrato defensivo que los helpers de v2.119.43/.44: tag vacío / no de 4 bytes, sin GSUB, característica no anunciada, StartIndex fuera de rango y ninguna subtabla LookupType 4 coincidiendo en la posición devuelven todos el no-op seguro (False / OutGID = 0 / ConsumedCount = 1). El mark-skipping vía LookupFlag, la integración de GDEF GlyphClassDef, la aplicación automática durante la emisión de texto y el auto-pull del TTF subsetter para glifos de ligadura quedan reservados para revisiones futuras.

2026-05-22 Version 2.119.44

  • OpenType GSUB Multiple Substitution (LookupType 2): el nuevo THotPDF.GetMultipleSubstituteGlyphs (InputGID, FeatureTag, var OutGIDs) devuelve un glifo de entrada como una secuencia de glifos sustitutos. El usuario canónico es la característica 'ccmp' Glyph Composition / Decomposition, que descompone letras latinas acentuadas precompuestas en base + marcas combinatorias para el mark positioning aguas abajo. El helper recorre la misma ruta de DFLT script / LangSys por defecto / FeatureList / LookupList que GetSingleSubstituteGlyph de v2.119.43 y aplica cada subtabla LookupType 2 conectada a la característica solicitada contra InputGID; la primera coincidencia gana. OutGIDs se reinicia al entrar para que los llamadores no necesiten limpiarlo previamente. La Sequence vacía permitida por la spec (glyphCount = 0, eliminación canónica de glifos en Unicode) se reporta como Result = True con Length(OutGIDs) = 0 para que el llamador pueda descartar el glifo de entrada de la secuencia.
  • OpenType GSUB Alternate Substitution (LookupType 3): dos nuevos métodos exponen el ciclo completo de alternate-glyph para características estilísticas. THotPDF.GetAlternateGlyphCount (InputGID, FeatureTag) devuelve el número de formas alternativas que una fuente ofrece para InputGID bajo FeatureTag, y THotPDF.GetAlternateGlyph (InputGID, FeatureTag, AlternateIndex) devuelve el glifo alternativo en el índice 0-based solicitado. Los usuarios canónicos son 'aalt' (Access All Alternates), 'salt' (Stylistic Alternates), 'titl' (Titling Alternates) y 'ss01' hasta 'ss20' (Stylistic Sets 1-20) cuando se conectan con LookupType 3 en lugar de LookupType 1; GetSingleSubstituteGlyph de v2.119.43 saltaba silenciosamente estas subtablas, por lo que los llamadores de 'aalt' solo veían previamente los reemplazos transitivos de LookupType 1. El par permite a los llamadores recorrer ciclos de glifos decorativos (diferentes formas de 'a' / 'g' / '7'), variantes titling-cap y alternativas de stylistic-set de forma explícita. Un AlternateIndex fuera de rango / negativo devuelve InputGID sin cambios para que los llamadores puedan sondear de forma segura.
  • Ambos helpers honran el mismo contrato defensivo que v2.119.43: las fuentes sin tabla GSUB, los llamadores que pasan un tag que no es de 4 bytes, las características que el script DFLT de la fuente no anuncia y los GIDs que ninguna subtabla LookupType 2 / 3 cubre devuelven todos el no-op seguro (False + OutGIDs vacío para Multiple; 0 / InputGID para Alternate). Las subtablas LookupType 1 / 4-8 encontradas en la cadena de lookups de la característica se saltan silenciosamente en este slice. El mark-filtering de LookupFlag, la integración con GDEF GlyphClassDef, la API de selección Script / LangSys, la integración del pipeline de shaping automático y el auto-pull del TTF subsetter para los glifos sustitutos elegidos quedan reservados para revisiones futuras; los llamadores siguen siendo responsables de marcar los glifos elegidos en el subset de la fuente embebida.

2026-05-22 Version 2.119.43

  • Lookup de stylistic-alternates de OpenType GSUB: el nuevo THotPDF.GetSingleSubstituteGlyph (InputGID, FeatureTag) recorre la cadena GSUB ScriptList / FeatureList / LookupList de la fuente bajo el LangSys por defecto del script DFLT y aplica cada subtabla LookupType 1 (Single Substitution) conectada a la característica solicitada, devolviendo el ID del glifo sustituido. Los feature tags más comunes de stylistic-alternates son 'salt' (Stylistic Alternates), 'ss01' hasta 'ss20' (Stylistic Sets 1-20), 'aalt' (Access All Alternates), 'titl' (Titling Alternates), 'subs' / 'sups' (Subscript / Superscript), 'frac' (Fractions) y 'ordn' (Ordinals); se soporta cualquier feature tag OpenType de 4 bytes cuya cadena de característica contenga subtablas LookupType 1. Coverage Format 1 (array de glifos ordenado, binary-searched) y Format 2 (range records) están ambos honrados. Single Substitution Format 1 (delta) y Format 2 (substitute array) están ambos implementados. RegisterUnicodeTTF cachea ahora el offset / longitud de la tabla GSUB junto al cmap para que los walks GSUB en tiempo de consulta eviten reescanear el table directory. Las fuentes sin tabla GSUB, los llamadores que pasan un tag de 4 bytes que el script DFLT de la fuente no anuncia, y los GIDs no incluidos en la Coverage del lookup devuelven todos InputGID sin cambios, por lo que la API es segura de llamar como sustituto-o-passthrough de mejor esfuerzo. Las subtablas LookupType 2-8 encontradas en la cadena de lookups de la característica se saltan silenciosamente en este primer slice — las cadenas Multiple Substitution / Alternate Substitution / Ligature Substitution / Contextual y la integración del pipeline de shaping automático quedan reservadas para revisiones futuras.

2026-05-22 Version 2.119.42

  • AcroForm /DR Resources acepta ahora múltiples fuentes Unicode. SetFormUnicodeFontDict de v2.56.0 solo rastrea una única fuente suministrada por el llamador que se convierte en el /DA por defecto del AcroForm; los formularios multilingües que necesitan scripts diferentes por campo (árabe + CJK + latín en un solo PDF) tenían que autorizar el dict /DR /Font a mano. El nuevo THotPDF.RegisterAcroFormFont (LogicalName, FontDict) registra una fuente Unicode adicional bajo cualquier nombre lógico, la pone disponible para las cadenas /DA por campo (/ 12 Tf) y la emite en el dict /DR /Font del AcroForm junto a la fuente por defecto de v2.56.0. El orden de registro se preserva, de modo que el /DR emitido es determinístico. Las colisiones de nombre con el SetFormUnicodeFontDict por defecto o con una fuente previamente registrada lanzan excepción con el nombre infractor. LogicalName vacío / FontDict nil también lanzan excepción. La ruta de reinicio SetFormUnicodeFontDict ('', nil) limpia ahora también el registro de fuentes adicionales para que un flujo Unicode fresco arranque desde un estado conocido y limpio. Permitido en todos los niveles PDF/A / PDF/X — las fuentes Unicode compuestas son metadatos estructurales de formulario, no script interactivo.

2026-05-22 Version 2.119.41

  • Enum de structure-type estándar que cubre PDF 1.7 §14.8.4 Table 333: el nuevo enum THPDFStandardStructureType lista los ~40 roles de estructura prescritos por la spec agrupados por su bucket de rol PDF (Grouping: Document / Part / Art / Sect / Div / BlockQuote / Caption / TOC / TOCI / Index / NonStruct / Private; nivel de bloque: H / H1-H6 / P / L / LI / Lbl / LBody / Table / TR / TH / TD / THead / TBody / TFoot; nivel inline: Span / Quote / Note / Reference / BibEntry / Code / Link / Annot; Ruby + Warichu: Ruby / RB / RT / RP / Warichu / WT / WP; ilustración: Figure / Formula / Form). Dos helpers a nivel de unidad convierten el enum en nombres PDF exactos según la spec — StandardStructureTypeToName (T) devuelve el nombre sensible a mayúsculas usado como clave /S del elemento de estructura, e IsStandardStructureType (Name) prueba una AnsiString arbitraria contra el conjunto estándar para que los llamadores puedan pre-validar nombres de rol personalizados y encaminarlos vía AddStructRoleMap cuando sea necesario. Un nuevo overload de AddStructureElement acepta THPDFStandardStructureType directamente, eliminando el peligro de typo / sensibilidad a mayúsculas ('Para' vs 'P', 'Lbody' vs 'LBody') de los overloads open-string mientras reenvía a la misma implementación base. Los overloads existentes basados en AnsiString permanecen idénticos a nivel de byte para los llamadores que usan nombres personalizados o enrutados vía RoleMap.

2026-05-22 Version 2.119.40

  • Setters de atributos de elemento de estructura: tres nuevos helpers completan los atributos PDF text-string por elemento que casan con /Alt + /ActualText de v2.88.0 y /ID de v2.95.0. THotPDF.SetStructureElementLanguage (Elem, BCP-47 Lang) escribe /Lang de PDF 1.7 §14.9.2, sobrescribiendo el lenguaje por defecto del documento para el contenido bajo este elemento hasta que se aplique otra sobreescritura /Lang — el uso típico son pasajes citados en otro idioma o secciones con contenido de scripts mixtos. THotPDF.SetStructureElementExpansionText (Elem, ExpansionText) escribe /E de §14.9.5, la expansión de abreviatura leída por lectores de pantalla en lugar de un acrónimo ('NASA' → 'National Aeronautics and Space Administration') y el valor cosechado por la extracción de contenido. THotPDF.SetStructureElementTitle (Elem, Title) escribe /T de §14.7.5.2, el título legible por humanos que distingue el elemento de hermanos del mismo rol en el árbol de estructura (el panel Tags de Acrobat muestra /T junto al nombre del rol). Los tres son setters simples con semántica no-op para valor vacío, Elem nil lanza excepción, y están permitidos en todos los niveles PDF/A y PDF/X dado que las claves son metadatos estructurales.

2026-05-22 Version 2.119.39

  • Completado de la familia de triggers de campos de formulario AcroForm: tres nuevos helpers se unen a AttachFieldKeyStrokeAction (v2.119.37) para cerrar PDF 1.7 §12.7.5.3 Table 246. THotPDF.AttachFieldFormatAction instala /AA /F (evento format, se dispara cuando el valor de visualización del campo necesita reformatearse tras un commit — uso típico es aplicar máscaras de moneda, fecha o separador de millares vía AFNumber_Format / AFDate_FormatEx). THotPDF.AttachFieldValidateAction instala /AA /V (evento validate, se dispara después de que el usuario hace commit de un nuevo valor — uso típico es validar rango o regex de la entrada vía event.rc := false). THotPDF.AttachFieldCalculateAction instala /AA /C (evento calculate, se dispara cuando cambia cualquier campo de dependencia según el orden de cálculo Catalog /AcroForm /CO — uso típico es acumular totales o calcular impuestos). Los cuatro wrappers comparten la misma búsqueda FFormFields /T, semántica de reemplazo idempotente last-call-wins y guardas PDF/A + PDF/X de JavaScript. Las demás claves de trigger en el mismo dict /AA (los triggers de anotación /E /X /D /U /Fo /Bl y los tres triggers hermanos de campo de formulario) se preservan intactas. Refactor interno: AttachFieldKeyStrokeAction delega ahora en un helper compartido _InstallFieldTriggerJSAction; los llamadores existentes de v2.119.37 ven mensajes de excepción idénticos a nivel de byte.

2026-05-22 Version 2.119.38

  • RegisterUnicodeTTF captura ahora los mapeos codepoint-a-glifo del Supplementary Multilingual Plane (SMP, U+10000-U+10FFFF) desde la subtabla cmap format 12 de la fuente cargada. Versiones previas descartaban silenciosamente las entradas SMP porque la consulta codepoint-a-glifo era un array de tamaño BMP; v2.119.38 añade una lista sparse paralela (FUnicodeCpToGidSMP) poblada junto al array BMP. El nuevo método público GetUnicodeGlyphForCodepoint(Cp) devuelve el ID de glifo para cualquier codepoint Unicode hasta U+10FFFF (busca por binary-search en la lista SMP para codepoints sobre el BMP), o 0 (.notdef) cuando el codepoint no está mapeado en el cmap de la fuente cargada. Este es un slice solo-de-capacidad: el pipeline de codificación hex del lado del productor, el stream /CIDToGIDMap y la emisión del ToUnicode CMap permanecen anclados al BMP, por lo que escribir un carácter SMP en un flujo de texto PDF sigue requiriendo que los llamadores emitan ellos mismos pares surrogados UTF-16BE. La emisión completa de flujo de texto con conocimiento de surrogados queda reservada para una revisión futura. Las fuentes que solo tienen Format 4 obtienen una lista SMP vacía como se espera.

2026-05-22 Version 2.119.37

  • Nuevo helper de trigger de keystroke AcroForm: THotPDF.AttachFieldKeyStrokeAction(FieldName, JavaScriptBody) localiza el widget de formulario nombrado por su valor /T e instala una acción JavaScript /AA /K para que los lectores PDF disparen el script en cada keystroke durante la entrada de texto. Los casos de uso incluyen validación de entrada en tiempo real (numérica / fecha / regex), enmascaramiento de entrada y actualizaciones de campos calculados disparadas por ediciones en otro campo. El helper es idempotente: las llamadas repetidas reemplazan la entrada /K conservando cualquier otra clave de trigger (/F format, /V validate, /C recalc, /Bl blur, /Fo focus, etc.) ya presente en el mismo dict /AA. PDF/A todos los niveles (ISO 19005-1 §6.6.1, -2/-3 §6.5.1) y PDF/X todos los niveles (ISO 15930 prepress) prohíben las acciones JavaScript, por lo que el helper lanza excepción inmediatamente con la referencia de spec relevante cuando cualquiera de las flags de conformidad es no-vacía. Respaldado por el diccionario de eventos de trigger de PDF 1.7 §12.6.3 + la clave /K de campo de formulario §12.7.5.3 Table 246.

2026-05-22 Version 2.119.36

  • BeginTaggedContent acepta ahora cuatro propiedades opcionales de marked-content sequence de PDF 1.7 §14.8.4 Table 322 (Lang / Alt / ActualText / ExpansionText) y las emite como un único dict de propiedades BDC inline junto con la entrada MCID existente. Lang lleva el tag BCP-47 de lenguaje natural para la secuencia (usado por lectores de pantalla y extracción de texto con conciencia de lenguaje); Alt es la descripción alternativa para secuencias no-textuales o decorativas; ActualText es el texto de reemplazo usado por la extracción de contenido y copy-paste en lugar de la secuencia visual de glifos (esencial para ligaduras y glifos estilizados donde el carácter visible difiere del texto semántico subyacente); ExpansionText lleva la clave /E que expande abreviaturas. Los parámetros vacíos saltan la entrada correspondiente de modo que el dict inline solo contiene las claves que el llamador puebla explícitamente, y la ruta de llamada legacy de dos argumentos permanece idéntica a nivel de byte respecto a la salida de v2.119.35. El escapado de literal string PDF (paréntesis y backslash) es automático; para payloads no-ASCII (ActualText CJK, Alt índico, etc.) los llamadores preencodifican sus bytes como UTF-16BE con el BOM U+FEFF y empaquetan el resultado en el argumento AnsiString. La API low-level de page-stream BeginMarkedContentMCIDProps que casa con esto también se expone para llamadores que necesiten control BDC directo sin el cableado high-level de StructElem.

2026-05-22 Version 2.119.35

  • El subconjunto núcleo persa / urdu de 9 letras de Arabic Extended-A participa ahora en el shaper de cuatro posiciones del lado del productor de v2.85.0. Con AutoShapeArabic activo, las siguientes letras se mapean a sus glifos Arabic Presentation Forms-A U+FB50-U+FBFF basándose en el mismo algoritmo de contexto de joining usado para el bloque árabe básico: PEH U+067E, TCHEH U+0686, JEH U+0698, KEHEH U+06A9, GAF U+06AF, FARSI YEH U+06CC (núcleo persa), más TTEH U+0679, DDAL U+0688, RREH U+0691 (núcleo retroflejo urdu). Las letras de conexión doble exponen el conjunto completo aislada / final / inicial / medial; las letras de conexión a la derecha (JEH / DDAL / RREH) exponen solo aislada / final según el modelo de shaping árabe. Otras letras de Arabic Extended-A (ALEF WASLA U+0671, NOON GHUNNA U+06BA, variantes HEH U+06C0-U+06C3, Arabic Supplement completo U+0750-U+077F) permanecen como pass-through y se difieren a una revisión futura. El texto árabe no-persa / no-urdu permanece idéntico a nivel de byte respecto a la salida de v2.119.34.

2026-05-22 Version 2.119.34

  • El manejo de nombres PDF decodifica ahora las secuencias de escape #XX de ISO 32000-1 al importar o copiar objetos PDF. Los nombres de recursos, nombres de spot color, nombres de roles de estructura y otros objetos de nombre como /PANTONE#20216#20CVC ahora hacen round-trip a través de HotPDF como sus valores de byte previstos en lugar de ser copiados de vuelta con un signo de número doblemente escapado.

2026-05-22 Version 2.119.33

  • El manejo de fechas PDF sigue ahora más estrictamente las reglas de cadena de fecha de ISO 32000-1. La información del documento, las fechas derivadas de XMP y las marcas de tiempo de firma usan el TDateTime suministrado por el llamador en lugar de sustituir silenciosamente la hora actual del reloj, mientras que el parser acepta campos de fecha opcionales estándar y sufijos de zona horaria sin corromper los campos locales de fecha/hora.
  • El manejo de filtros de importación PDF tiene ahora helpers explícitos ASCIIHexDecode, ASCII85Decode y RunLengthDecode, y el valor por defecto EarlyChange de LZWDecode coincide ahora con el valor por defecto PDF de 1. Esto mejora la compatibilidad con páginas importadas o copiadas que usan filtros de stream PDF legacy.

2026-05-21 Version 2.119.32

  • El plegado de la ligadura obligatoria árabe LAM-ALEF aterriza ahora dentro del shaper del lado del productor de v2.85.0. Con AutoShapeArabic activo, cualquier secuencia de U+0644 LAM (en forma raw o cualquiera de sus cuatro formas shapeadas FEDD-FEE0) seguida por una de las cuatro variantes ALEF (MADDA U+0622, HAMZA-above U+0623, HAMZA-below U+0625, plain U+0627; en forma raw o post-shape) colapsa en un único glifo de ligadura del bloque U+FEF5-U+FEFC. La forma conectada-vs-aislada de la ligadura se selecciona en función de si el LAM ha sido shapeado a una forma final (FEDE) o medial (FEE0) por el walker de cuatro posiciones. Otras ligaduras obligatorias árabes (Allah U+FDFB y ligaduras honoríficas decorativas) siguen requiriendo un motor GSUB completo y permanecen fuera del alcance del modelo de shaping de tabla estática. El texto no-LAM-ALEF y los llamadores con AutoShapeArabic desactivado permanecen idénticos a nivel de byte respecto a la salida de v2.119.31. Tres helpers AcroForm Unicode /AP (single-line, multi-line, comb) heredan el cambio automáticamente a través del entry point compartido _ApplyArabicShaping.

2026-05-21 Version 2.119.31

  • Nuevo adaptador de exportación DevExpress ExpressPrinting System: dxHotPDFExportReportLinkToFile / dxHotPDFExportReportLinkToStream toman cualquier TBasedxReportLink (la familia de links abstractos que enlaza cxGrid / cxRichEdit / cxScheduler / cxPivotGrid y componentes imprimibles similares a TdxComponentPrinter) y canalizan cada página renderizada a través de HotPDF en lugar del propio emisor dxPSExportToPDF de DevExpress. El adaptador refleja el bucle de exportación interno de DevExpress: RebuildReport → para cada página → PaintPage sobre un TMetafileCanvas → ShowEnhancedMetafile hacia HotPDF. El record Options expone Title / Author / Subject / Keywords / PDFVersion / Compression / RenderDPI. Mismas limitaciones de metafile-bridge que los adaptadores FastReport / QuickReport / ReportBuilder: sin campos AcroForm, sin outlines, sin gates PDF/A / PDF/X. El adaptador vive en Lib/Addons/DevExpress/ y no está en el HotPDF*.dpk principal.

2026-05-20 Version 2.119.30

  • Nueva clase de dispositivo ReportBuilder: TppHotPDFDevice desciende de TppGraphicsDevice de modo que se enchufa en la cadena estándar publisher → device de ReportBuilder cuando se asigna a TppReport.PrinterDevice o a un TppPublisher.Device. Cada página se renderiza sobre un TMetafileCanvas transitorio (creado en BeforeRenderPage, finalizado en AfterRenderPage), se captura como un TMetafile y se enruta a través del importador EMF de HotPDF (HPDFEmf.ShowEnhancedMetafile). Las propiedades exponen Title / Author / Subject / Keywords / PDFVersion / CompressionLevel / Compressed; SetOutputStream encamina los bytes del PDF a un TStream suministrado por el llamador. Mismas limitaciones de metafile-bridge que los adaptadores FastReport / QuickReport: sin campos AcroForm, sin outlines, sin gates PDF/A / PDF/X. El adaptador vive en Lib/Addons/ReportBuilder/ y no está en el HotPDF*.dpk principal.

2026-05-20 Version 2.119.29

  • Nuevo filtro de exportación QuickReport: TQRHotPDFExportFilter desciende de TQRExportFilter de modo que se enchufa en el flujo regular MyReport.ExportToFilter(MyFilterInstance) sin cambios en el core de QuickReport. El adaptador cosecha los TMetafiles por página que QuickReport ya cachea en QRPrinter.PageList y alimenta cada uno a través del importador EMF de HotPDF (HPDFEmf.ShowEnhancedMetafile). Las propiedades exponen Title / Author / Subject / Keywords / PDFVersion / CompressionLevel / Compressed; SetOutputStream encamina los bytes del PDF a un TStream suministrado por el llamador en lugar de a disco. Mismas limitaciones de metafile-bridge que el adaptador FastReport: sin campos AcroForm, sin outlines, sin gates PDF/A / PDF/X. El adaptador vive en Lib/Addons/QuickReport/ y no está en el HotPDF*.dpk principal.

2026-05-20 Version 2.119.28

  • Nuevo adaptador de exportación FastReport 4 / FastReport VCL: TfrxHotPDFExport desciende de TfrxCustomExportFilter de modo que se enchufa en el flujo regular MyReport.Export(MyExportInstance) sin cambios en el core de FastReport. El adaptador enruta cada página preparada a través del importador EMF de HotPDF (vía TfrxPreviewPages.DrawPage de FastReport → TMetafileCanvas → HotPDF.ShowEnhancedMetafile), de modo que el contenido vectorial + raster renderizado por el motor de layout estándar de FastReport se preserva sin código de emisión por objeto. Las propiedades exponen Title / Author / Subject / Keywords / PDFVersion / CompressionLevel / RenderDPI para controlar el PDF resultante. Limitaciones: sin campos AcroForm, sin outlines, sin gates PDF/A / PDF/X (esos necesitan un pipeline de emisión por objeto, planeado para una revisión futura). El adaptador se entrega en Lib/Addons/FastReport4/ y NO está en el HotPDF*.dpk principal; los usuarios lo añaden a su propio proyecto junto al package FastReport correspondiente.

2026-05-20 Version 2.119.27

  • Nueva firma PDF end-to-end basada en PFX: THotPDF.SignPDFWithPFX(InputPDFPath, OutputPDFPath, PFXFilePath, Password) carga un PDF sin firmar que ya contiene un placeholder AddSignedSignatureField, parsea el archivo PFX / PKCS#12, construye un CMS SignedData completo (RFC 5652) sobre los bytes cubiertos por /ByteRange y escribe el PDF firmado en una sola llamada. También se proporciona un overload basado en TStream para flujos en memoria. El presupuesto /Contents del PDF debe ser suficientemente grande para contener el hex DER del CMS; el valor por defecto de 8192 bytes cubre RSA de 1024 / 2048 bits. Solo archivos PFX PBES2 + AES-256-CBC (el límite del parser PKCS#12 de v2.119.26).
  • Nueva unidad HPDFCMS: constructor de CMS SignedData. HPDFCMSBuildSignedData compone ContentInfo → SignedData → SignerInfo con contenido id-data detached, atributos firmados contentType + messageDigest + signingTime (ordenados ascendentemente por DER según RFC 5652 §5.4), identificador de firmante IssuerAndSerialNumber extraído del cert X.509, algoritmo de firma RSA + SHA-256 y el cert X.509 envuelto en [0] IMPLICIT certificates. También expone HPDFCMSSHA256ByteRanges para digest streamed de dos ventanas y HPDFCMSBytesToHex para la inyección del placeholder /Contents. Cierra la pila de firma in-library iniciada con HPDFASN1 (v2.119.23) + HPDFRSA (v2.119.24) + extensiones HPDFCrypt (v2.119.25) + HPDFPFX (v2.119.26).

2026-05-20 Version 2.119.26

  • Nueva unidad HPDFPFX: parser de contenedores PKCS#12 / PFX. Lee archivos .pfx / .p12 de OpenSSL ≥ 3.0, certutil de Windows 11+ y macOS Keychain Access, descifra payloads SafeBag PBES2 (PBKDF2-HMAC-SHA-256 + AES-256-CBC) y extrae el DER del certificado X.509 + los parámetros de clave privada RSA. Devuelve un record THPDFPFXKeyMaterial con CertDER, Modulus, PublicExponent, PrivateExponent que el código CMS / de firma aguas abajo puede consumir directamente. Una contraseña incorrecta se detecta vía el fallo de unpadding PKCS#7 que sigue a una clave AES errónea. Los archivos legacy PBE-SHA1-3DES lanzan excepción con una sugerencia clara de migración para re-exportar con `-keypbe AES-256-CBC -certpbe AES-256-CBC`.

2026-05-20 Version 2.119.25

  • Nuevas primitivas criptográficas: cifrado inverso AES-256 con AES256DecryptBlock + AES256CBCDecryptPKCS7 + AES256CBCDecryptNoPad (FIPS-197 §5.3, complementando los helpers de encriptación existentes), HMAC-SHA-256 (RFC 2104) y PBKDF2-HMAC-SHA-256 (RFC 8018 §5.2). Verificadas contra los vectores HMAC del RFC 4231 y las salidas de referencia estándar PBKDF2-SHA-256. Junto con el decoder ASN.1 de v2.119.23 y las primitivas RSA de v2.119.24, esto completa la base criptográfica necesaria para la ruta de descifrado PBES2-AES-256 SafeBag usada por los archivos PKCS#12 / PFX modernos (OpenSSL ≥ 3.0 / Windows 11+).

2026-05-20 Version 2.119.24

  • Nueva unidad HPDFRSA: aritmética de enteros de multi-precisión + exponenciación modular RSA + codificación EMSA PKCS#1 v1.5. Lo suficientemente grande como para firmar con claves RSA reales de 2048 / 4096 bits. Junto con el decoder ASN.1 de v2.119.23 esto completa las primitivas criptográficas necesarias para el próximo pipeline de firma PDF basado en PFX in-library. Ninguna API de cara al usuario lo usa todavía — es infraestructura interna esperando a las capas PKCS#12 + CMS.

2026-05-20 Version 2.119.23

  • Nueva unidad HPDFASN1: un decoder BER/DER compacto para el subset ASN.1 que HotPDF necesita para leer keystores PKCS#12, certificados X.509 y contenedores CMS/PKCS#7 SignedData. Expone THPDFASN1Node (descriptor Tag-Length-Value parseado), THPDFASN1ParseNode / ParseAt, accessors Content / ToInteger / ToBigInt / ToOID / ToString e iteradores FirstChild / NextSibling / Children. Rechaza explícitamente la forma BER de longitud indefinida y las codificaciones high-tag-number (>= 31), reflejando el subset estricto de DER. Esta es infraestructura interna para el pipeline de firma PFX in-library que llegará en próximos releases — ninguna API de cara al usuario depende aún de ella.

2026-05-20 Version 2.119.22

  • Nuevos overloads de anotaciones PolyLine + Polygon con metadatos completos: THPDFPage.AddPolylineAnnotation y AddPolygonAnnotation reciben dos nuevos overloads que aceptan un array de vértices THPDFCurrPoint y un record THPDFPolyExtras opcional. El record extras expone las entradas opcionales de ISO 32000-1 §12.5.6.9 — /IC color interior, /BE borde cloudy (besSolid / besCloudy con /I intensity), /IT intent (PolyLineDimension / PolyLineFlight / PolygonCloud / PolygonDimension) y estilos /LE line-ending para PolyLine. Use HPDFDefaultPolyExtras() para un record inicial inicializado a cero. Los nuevos overloads validan que se suministren al menos dos vértices y suben automáticamente la versión del documento (PDF 1.5 / 1.6 / 1.7 dependiendo del intent). Los overloads previos con Single intercalados siguen compilando sin cambios.

2026-05-20 Version 2.119.21

  • Nuevos extras para anotaciones Line: AddLineAnnotation obtiene un tercer overload que acepta un record THPDFLineExtras que controla las entradas opcionales de PDF 1.6/1.7 §12.5.6.7 Table 175 — /IC color interior (cabezas de flecha rellenas, diamantes, etc.), /LL longitud de leader-line, /LLE extensión del leader, /LLO offset del leader (PDF 1.7), flag /Cap caption con colocación /CP (/Inline o /Top, PDF 1.7) + offset /CO caption, y /IT intent (LineArrow / LineDimension). Use HPDFDefaultLineExtras() para partir de un record inicializado a cero. El nuevo overload sube automáticamente la versión del documento a PDF 1.6 (o 1.7 cuando se solicitan /LLO / /CP / /CO). Los dos overloads anteriores siguen emitiendo salida idéntica a nivel de byte, y solo se escriben las entradas que el llamador realmente solicita.

2026-05-20 Version 2.119.20

  • Nuevas acciones push-button baShow / baImportData: THPDFButtonAction incluye ahora baShow (espejo de baHide que emite /H false para volver a mostrar un widget oculto) y baImportData (/S /ImportData /F filespec para cargar datos de formulario FDF al hacer clic, ISO 32000-1 §12.7.5.4). baShow está permitido por la spec bajo PDF/A y PDF/X; baImportData está bloqueado bajo ambos según ISO 19005-1 §6.6.1 e ISO 15930 prepress respectivamente.
  • Nuevo overload Hide/Show multi-campo: THPDFPage.AddPushButtonWithHideAction(FieldName, Caption, Targets, Hide, Rectangle, Flags) acepta un array de nombres de campo objetivo y un booleano Hide explícito de modo que un único botón puede alternar la visibilidad de múltiples widgets con un solo clic. Un array Targets vacío lanza excepción. PDF/A y PDF/X permiten ambos este overload (solo visibilidad de widget).

2026-05-20 Version 2.119.19

  • Nuevo control de usuario /Flags de SubmitForm: THPDFPage.AddPushButtonWithSubmitAction(FieldName, Caption, URL, Rectangle, SubmitFlags) expone el conjunto completo de flags de ISO 32000-1 §12.7.5.2 Table 237 mediante el nuevo tipo de conjunto THPDFSubmitFormFlags. Los llamadores pueden mezclar ahora sffExportFormatHTML / sffXFDF / sffSubmitPDF (formato de envío), sffGetMethod (HTTP GET vs POST), sffIncludeAnnotations, sffSubmitCoordinates, sffCanonicalFormat, sffIncludeNoValueFields, sffIncludeAppendSaves, sffExclNonUserAnnots, sffEmbedForm y otros, reemplazando el /Flags 0 hardcoded (FDF + POST) de la ruta baSubmitURL legacy. AddPushButtonWithAction(.., baSubmitURL, ..) existente sigue emitiendo /Flags 0 por compatibilidad hacia atrás a nivel de byte. Mismos gates PDF/A / PDF/X que baSubmitURL: permitido bajo PDF/A, bloqueado bajo PDF/X.

2026-05-20 Version 2.119.18

  • Tipos de acción push-button extendidos: THPDFButtonAction incluye ahora baNamed y baHide. baNamed emite una acción /S /Named /N <name> (ISO 32000-1 §12.6.4.11) adecuada para los cuatro comandos estándar de navegación NextPage / PrevPage / FirstPage / LastPage (PDF/A permite los cuatro según ISO 19005-1 §6.6.1) y las extensiones de Acrobat como Print y SaveAs. baHide emite una acción /S /Hide /T <field> /H true (§12.6.4.10) que oculta el campo de formulario nombrado cuando se hace clic en el botón. Ambos tipos de acción están permitidos por la spec bajo PDF/A y PDF/X; solo baJavaScript / baResetForm / baSubmitURL mantienen sus gates de conformidad existentes.

2026-05-20 Version 2.119.17

  • Nuevo registro de destinos nombrados: THotPDF.RegisterNamedDestination(Name, PageIndex, FitMode, ...) registra un destino simbólico en el name tree Catalog /Names /Dests (ISO 32000-1 §12.3.2.3). Las acciones GoTo / GoToR y las entradas /Dest de outline pueden entonces referenciar un destino por nombre en lugar de hardcodear números de página, lo que mantiene las referencias cruzadas estables ante inserción / borrado / reordenación de páginas. El nuevo enum THPDFDestinationFitMode cubre los ocho modos de fit de la spec (XYZ, Fit, FitH, FitV, FitR, FitB, FitBH, FitBV). El método lanza excepción ante Name vacío, PageIndex fuera de rango y Name duplicado; sube automáticamente la versión del documento a PDF 1.2 cuando es necesario. Sin gate PDF/A / PDF/X — los auxiliares de navegación están permitidos por la spec en todos los perfiles.

2026-05-20 Version 2.119.16

  • Nuevos estilos /LE line-ending para anotaciones de línea: THPDFPage.AddLineAnnotation tiene ahora un overload que acepta estilos de line-ending begin/end. El conjunto completo de ISO 32000-1 §12.5.6.7 Table 176 se expone vía el nuevo enum THPDFLineEndingStyle: None, Square, Circle, Diamond, OpenArrow, ClosedArrow, Butt, ROpenArrow, RClosedArrow, Slash. Las anotaciones de línea pueden ahora renderizarse como flechas propias, marcadores de cota o callouts puntero dentro de Adobe Acrobat / Foxit sin que el host tenga que dibujar manualmente las cabezas de flecha en la página. El overload legacy de cuatro parámetros sigue funcionando de forma idéntica a nivel de byte.

2026-05-20 Version 2.119.15

  • Nuevo estilizado de outline (bookmark): THPDFDocOutlineObject expone ahora las propiedades Color, Bold e Italic (ISO 32000-1 §12.3.3 Table 153 /C y /F). El panel de outline puede ahora renderizar bookmarks en color y con énfasis bold o italic, útil para agrupar capítulos, marcar secciones incompletas o que requieren atención, y casar con el estilo de marca. Color por defecto es clNone (no se emite entrada /C, idéntico a nivel de byte con la salida previa); Bold e Italic por defecto son false. Fijar propiedades después de AddChild actualiza el dict de outline vivo in situ, y fijar Color de vuelta a clNone o tanto Bold como Italic a false elimina las entradas /C / /F.

2026-05-20 Version 2.119.14

  • Nuevo registro de JavaScript nombrado a nivel de documento: THotPDF expone ahora RegisterDocumentJavaScript(Name, Body). Cada entrada registrada se serializa en el name tree Catalog /Names /JavaScript (ISO 32000-1 §7.7.4.4 + §12.6.4.16) como un dict indirecto /S /JavaScript /JS. Las acciones de widgets AcroForm y las acciones document-open pueden invocar la función registrada por nombre a través del motor JavaScript del visor (Acrobat, Foxit). Útil para compartir una librería de helpers de validación, formateo o cálculo entre muchos widgets sin duplicar el source JS en cada dict /A action. El método lanza excepción bajo cualquier nivel de PDFACompliance (ISO 19005-1 §6.6.1) y cualquier nivel de PDFXCompliance (flujos de preimpresión ISO 15930), y rechaza Name / Body vacíos así como el registro duplicado de Name.

2026-05-20 Version 2.119.13

  • Nuevo soporte de page thumbnail: THPDFPage expone ahora SetPageThumbnail(ImageIndex) y ClearPageThumbnail. Los visores PDF (Acrobat, Foxit, visores de navegador) usan la entrada /Thumb de página (ISO 32000-1 §12.3.4) para renderizar una vista previa prefabricada en su panel de navegación sin rasterizar la página, lo cual es especialmente útil para archivos con muchas imágenes donde el propio rasterizador del visor sería lento. El helper reutiliza cualquier imagen registrada a través de AddImage / AddImageFromFile, de modo que los llamadores pueden preparar un bitmap de vista previa con downsampling y adjuntarlo con una sola llamada; SetPageThumbnail(-1) o ClearPageThumbnail elimina un thumbnail previamente adjunto. Los índices de imagen fuera de rango lanzan una excepción descriptiva.

2026-05-20 Version 2.119.12

  • Corrección de conformidad PDF/A: AddPushButtonWithAction lanza ahora excepción cuando se usan los tipos de acción baJavaScript o baResetForm bajo cualquier nivel PDFACompliance (PDF/A-1/2/3). ISO 19005-1 §6.6.1 / ISO 19005-2 §6.5.1 / ISO 19005-3 §6.5.1 prohíben las acciones JavaScript, ResetForm e ImportData en todos los niveles PDF/A. baSubmitURL (SubmitForm), baURI y baNone siguen permitidos bajo PDF/A — SubmitForm está en la lista de acciones permitidas del §6.6.1. Previamente, los widgets push-button podían portar acciones prohibidas incluso con PDFACompliance activo, ya que la guarda de v2.119.5 solo cubría las rutas Catalog /AA y OpenAction a nivel de documento.
  • Corrección de conformidad PDF/X: AddPushButtonWithAction lanza ahora excepción cuando se usan los tipos de acción baJavaScript, baResetForm o baSubmitURL bajo cualquier nivel PDFXCompliance. Los flujos de preimpresión ISO 15930 excluyen el scripting interactivo, la mutación de formularios y el envío de formularios a endpoints externos.

2026-05-20 Version 2.119.11

  • Corrección de conformidad PDF/A: completado de las guardas de tipo de anotación PDF/A. AddScreenAnnotation, Add3DAnnotation y AddRichMediaAnnotation lanzan ahora excepción cuando PDFACompliance está activo en cualquier nivel (PDF/A-1/2/3). ISO 19005-1 §6.5.2 / ISO 19005-2 §6.6.1 / ISO 19005-3 §6.6.1 prohíben los subtipos de anotación Screen, 3D y RichMedia (multimedia / Adobe Extension Level 3) en todos los niveles PDF/A. Las mismas guardas se reflejan bajo PDFXCompliance — los flujos de preimpresión ISO 15930 rechazan los subtipos multimedia.
  • Corrección de conformidad PDF/A-1: AddWatermarkAnnotation y AddRedactAnnotation lanzan ahora excepción cuando PDFACompliance se fija a PDF/A-1. Watermark (PDF 1.6) y Redact (PDF 1.7) son subtipos de anotación post-PDF-1.4 incompatibles con la base PDF 1.4 de PDF/A-1 (ISO 19005-1 §6.5.2). PDF/A-2 y PDF/A-3 (base PDF 1.7) siguen permitiendo ambos tipos de anotación.

2026-05-20 Version 2.119.10

  • Corrección de conformidad PDF/A: AddImageWithSMask y AddImageWithSMask32 lanzan ahora excepción cuando PDFACompliance se fija a PDF/A-1. ISO 19005-1:2005 §6.4 prohíbe todos los efectos de transparencia; un stream /SMask de imagen activa el compositing de transparencia soft-mask. Precompose la imagen sobre un color de fondo y pase el resultado a AddImage(), o use PDF/A-2 o posterior para soporte de transparencia. PDF/A-2 y PDF/A-3 siguen permitiendo la transparencia soft-mask en imágenes.

2026-05-20 Version 2.119.9

  • Corrección de conformidad PDF/A: SetTransparencyGroup y SetTransparencyGroupICC lanzan ahora excepción cuando PDFACompliance se fija a PDF/A-1. ISO 19005-1:2005 §6.4 prohíbe todos los efectos de transparencia; los grupos de transparencia a nivel de página (dict de página /Group /S /Transparency) activan el modelo de compositing de transparencia para toda la página, lo que es una violación directa del §6.4. PDF/A-2 y PDF/A-3 siguen permitiendo los grupos de transparencia de página.

2026-05-20 Version 2.119.8

  • Corrección de conformidad PDF/A: RegisterHalftoneState lanza ahora excepción cuando PDFACompliance está activo y se suministra un diccionario de halftone personalizado (es decir, HTDefaultName = false). ISO 19005-1:2005 §6.2.9 e ISO 19005-2:2011 / ISO 19005-3:2012 §6.3.7 permiten solo el name-literal /Default para ExtGState /HT; los diccionarios de halftone personalizados (Type 1, 5, 6, 10, 16) están prohibidos. Pasar HTDefaultName=True para reiniciar al halftone por defecto sigue funcionando bajo todos los niveles PDF/A.
  • Corrección de conformidad PDF/A: RegisterSoftMaskState lanza ahora excepción cuando PDFACompliance se fija a PDF/A-1 y se suministra un diccionario soft-mask personalizado (parámetro SMaskDict). ISO 19005-1:2005 §6.4 prohíbe todos los efectos de transparencia; un valor /SMask distinto de None activa la transparencia soft-mask. Pasar SMaskNone=True para reiniciar las soft masks sigue permitido. PDF/A-2 y PDF/A-3 siguen permitiendo la transparencia soft-mask.

2026-05-20 Version 2.119.7

  • Corrección de conformidad PDF/A: AddDocumentAttachment (embedding de archivos a nivel de documento vía Catalog /Names /EmbeddedFiles) lanza ahora excepción cuando PDFACompliance está activo para PDF/A-1 o PDF/A-3. ISO 19005-1:2005 §6.1.11 prohíbe explícitamente la clave /EmbeddedFiles en el diccionario Names del documento bajo PDF/A-1. Para PDF/A-3, los adjuntos del documento deben registrarse a través de AddPDFA3AssociatedFile() para incluir el array Catalog /AF requerido y la clave /AFRelationship según ISO 19005-3:2012 Annex E. PDF/A-2 sigue permitiendo los adjuntos de documento siempre que los archivos adjuntos sean ellos mismos conformes a PDF/A.

2026-05-20 Version 2.119.6

  • Corrección de conformidad PDF/A: usar fuentes estándar PDF (Arial, Helvetica, Times New Roman, Courier New, Symbol, ZapfDingbats) con StandardFontEmulation activado lanza ahora una excepción descriptiva cuando PDFACompliance está activo. ISO 19005-1:2005 §6.3 e ISO 19005-2:2011 / ISO 19005-3:2012 §6.3 requieren que todas las fuentes estén embebidas en los archivos conformes; la ruta de emulación de fuentes estándar emite una referencia de fuente no embebida, lo que es una violación directa del §6.3. Los usuarios deben fijar StandardFontEmulation := false o cambiar a RegisterUnicodeTTF() con archivos de fuente reales para la salida PDF/A.
  • Corrección de conformidad PDF/A: las fuentes no estándar se embeben ahora siempre cuando PDFACompliance está activo, independientemente del ajuste de la propiedad FontEmbedding. La optimización FontEmbedding = false se sobreescribe silenciosamente en modo PDF/A para satisfacer el requisito §6.3 de todas-las-fuentes-embebidas.

2026-05-20 Version 2.119.5

  • Corrección de conformidad PDF/A: las acciones JavaScript y la /AA (Additional Actions) a nivel de Catalog se bloquean ahora cuando PDFACompliance está activo. ISO 19005-1:2005 §6.6.1/§6.6.2 e ISO 19005-2:2011 / ISO 19005-3:2012 §6.5.1/§6.6.2 prohíben todos los tipos de acción JavaScript y prohíben la clave /AA en el diccionario Catalog. Llamar a SetActionScript con cualquier valor PDFACompliance lanza ahora una excepción descriptiva. Como defensa secundaria, las rutas de emisión OpenAction JavaScript y Catalog /AA en EndDoc también están gateadas para saltar silenciosamente la salida cuando PDFACompliance está activo, asegurando que no se escriben claves no-conformes ni siquiera vía rutas internas.

2026-05-20 Version 2.119.4

  • Corrección de conformidad PDF/A: AcroForm /NeedAppearances se fuerza ahora a false cuando PDFACompliance está activo, independientemente del ajuste AutoFormAppearances. ISO 19005-1:2005 §6.9 e ISO 19005-2:2011 / ISO 19005-3:2012 §6.4 requieren que esta clave esté ausente o sea false en archivos conformes. Previamente, si AutoFormAppearances estaba desactivado mientras PDFACompliance estaba activo, /NeedAppearances se fijaba a true, produciendo un archivo no-conforme.

2026-05-20 Version 2.119.3

  • Corrección de conformidad PDF/A: los subsets CIDFont embebidos incluyen ahora un stream /CIDSet en su FontDescriptor cuando PDFACompliance está activo. ISO 19005-1:2005 §6.3.5 e ISO 19005-2:2011 / ISO 19005-3:2012 §6.2.11 requieren que los subsets CIDFont lleven un bitstream /CIDSet indicando qué valores de CID están presentes en el programa de fuente embebido. HotPDF usa codificación Identity-H donde el CID iguala al codepoint Unicode, de modo que el CIDSet se construye directamente desde el conjunto de codepoints usados en el documento. El bitstream se comprime con FlateDecode y se adjunta al FontDescriptor existente como un objeto stream indirecto. Esto corrige una brecha no detectada previamente en todos los niveles PDF/A (la auditoría de conformidad anterior afirmaba incorrectamente que esto ya estaba en su sitio).

2026-05-20 Version 2.119.2

  • Corrección de conformidad PDF/A-2 y PDF/A-3: el diccionario de appearance de anotación (/AP /N) se emite ahora automáticamente para los tipos de anotación no-Link, no-Popup (TextNotes, FreeText, Line, Square, Circle, Stamp, FileAttachment) cuando PDFACompliance se fija a un nivel PDF/A-2 o PDF/A-3. ISO 19005-2:2011 §6.3.3 e ISO 19005-3:2012 §6.3.3 requieren que cada anotación de este tipo tenga al menos un diccionario de appearance (con el form XObject de entrada N). Se emite un Form XObject vacío mínimo para satisfacer el requisito estructural; la mayoría de visores PDF usan su propio renderizado integrado para los tipos de anotación estándar independientemente del contenido del stream /AP. Las anotaciones Link y Popup están explícitamente exentas por el estándar y permanecen sin cambios. Este requisito no se aplica a PDF/A-1 (ISO 19005-1 §6.5.3 solo restringe el formato /AP cuando está presente, no su presencia).

2026-05-20 Version 2.119.1

  • Corrección de conformidad PDF/A: todos los tipos de anotación no-Widget (notas de texto, sellos, líneas, formas, hyperlinks, links GoTo, links GoToR, links URI) fijan ahora automáticamente la flag /F Print (bit 3 = 1, valor 4) cuando PDFACompliance está activo. ISO 19005-1:2005 §6.5.3 e ISO 19005-2:2011 / ISO 19005-3:2012 §6.3.2 ordenan que cada diccionario de anotación contenga una clave /F con el bit Print fijado. Previamente solo las anotaciones Widget (campos de formulario) tenían esta flag; los demás tipos de anotación la omitían, haciendo no-conformes los archivos producidos.
  • Corrección de conformidad PDF/A-1: los grupos de contenido opcional (capas) se bloquean ahora cuando PDFACompliance se fija a un nivel PDF/A-1 ('A' o 'B'). ISO 19005-1:2005 §6.1.13 prohíbe la clave /OCProperties en el diccionario Catalog; llamar a RegisterOptionalContentGroup bajo PDF/A-1 lanza ahora una excepción descriptiva indicando a los llamadores que usen PDF/A-2 o posterior si se requiere contenido opcional. La emisión EndDoc /OCProperties también está gateada para prevenir no-conformidad silenciosa incluso si los OCGs se hubieran registrado de algún modo.

2026-05-20 Version 2.119.0

  • Soporte de contenedor XAdES-in-PDF según ETSI EN 319 142-2 V1.2.0 §6.2 (cierre 3 de 3 de la serie B/C/D). Embebe bytes XML firmados con XAdES suministrados por el llamador como un PDF EmbeddedFile, registra el Filespec en el array Catalog /AF, fija /AFRelationship según la enumeración PDF 2.0 especificada por el llamador. Solo wrapper del lado del productor — la construcción real de la firma XAdES (XML-DSig + ETSI EN 319 132-1 QualifyingProperties + RFC 3161 timestamp embebido en xades:UnsignedSignatureProperties) es responsabilidad de la librería criptográfica XML del llamador (Apache Santuario, .NET SignedXml, libxmlsec, Saxon con extensiones XAdES, etc.).
  • Nuevo método `THotPDF.AddXAdESAssociatedFile(FileName, XMLBytes, Description, MimeType, Relationship)`. Valores por defecto: MimeType `'application/xml'` (también comúnmente `'application/vnd.etsi.asic-e+zip'` para archivos ASiC-E), Relationship `'Source'` (el XML firmado con XAdES es el contenido primario del documento). Devuelve el dict Filespec indirecto para que los llamadores puedan adjuntar metadatos adicionales o hacer referencia cruzada desde entradas Catalog personalizadas.
  • Independiente de PDF/A: a diferencia de `AddPDFA3AssociatedFile` de v2.108 (que requiere PDFACompliance='3*'), este helper no tiene gate PDF/A — XAdES-in-PDF es su propia familia de perfil según PAdES Part 2 V1.2.0 §6.
  • Corrección crítica de bug en `_EscapePDFNameBytes` (PDF 1.7 ISO 32000-1 §7.3.5 + Table 2). El escape previo solo convertía bytes fuera de [0x21..0x7E] y el carácter `#`; los delimitadores PDF (`/ ( ) < > [ ] { } %`) deben por spec escaparse `#XX` dentro de los nombres pero se dejaban literales. Nombres como `/Subtype /application/xml` eran por tanto parseados por lectores estrictos como dos tokens de nombre separados (/application seguido de /xml), rompiendo el reconocimiento de /Subtype del EmbeddedFile. v2.119 escapa los 10 delimitadores según la spec; los nombres no-delimitadores (la abrumadora mayoría de las emisiones de HotPDF) permanecen idénticos a nivel de byte. Los tipos MIME del PDF/A-3 Annex E (v2.108) se emiten ahora correctamente (`/application#2Fxml` en lugar del defectuoso `/application/xml`).
  • La serie B/C/D de PAdES se cierra en 3 commits: v2.117 (perfiles Extended E-BES / E-EPES / E-LTV según Part 2 §5) + v2.118 (Seed Values según ISO 32000-1 §12.7.5.5 + Part 2 §4.2.6) + v2.119 (XAdES-in-PDF + corrección del escape de delimitadores según Part 2 §6.2). Combinado con v2.109 - v2.116, HotPDF cubre ahora el alcance completo del productor PAdES incluyendo perfiles baseline, perfiles extended, document timestamp, info de validación DSS, extensiones ESIC, restricciones de firma vía Seed Values y contenedor XAdES-in-PDF.
  • Compilación limpia en Win32 + Win64; los baselines v2.108 - v2.118 recompilan todos sin cambios. El nuevo smoke smoke_pades_xades ejercita 2 rutas positivas (XML firmado con XAdES como /AFRelationship /Source + archivo ASiC-E como /Data) más 4 rutas de rechazo (FileName vacío, XMLBytes vacío, MimeType vacío, Relationship inválido). El verificador Python recorre ambos Filespecs end-to-end incluyendo las formas de escape `/Subtype /application#2Fxml` y `/Subtype /application#2Fvnd.etsi.asic-e+zip`. Todos los smokes v2.108 PDF/A-3 / PAdES / PDF/X se vuelven a ejecutar limpios.

2026-05-20 Version 2.118.0

  • PAdES Seed Values según ISO 32000-1 §12.7.5.5 + ETSI EN 319 142-1 V1.2.1 §5.5 + ETSI EN 319 142-2 V1.2.0 §4.2.6 (paso 2 de 3 de la serie B/C/D). Un diccionario Seed Value (SV) adjunto a un campo de firma restringe lo que la herramienta de firma del lector consumidor está autorizada a elegir para ese campo específico — qué handler, qué SubFilter, qué método de digest, si la información de revocación es obligatoria, qué versión mínima de PDF se requiere.
  • Nuevo método `THotPDF.AttachPAdESSeedValue(FieldName, SubFilters, DigestMethods, ForceSubFilter, ForceDigestMethod, AddRevInfo, MinPDFVersion, ForceMinPDFVersion)`. Localiza el campo de firma nombrado en la lista de campos AcroForm (debe haber sido creado ya por una llamada anterior a AddPAdESSignatureField o AddDocumentTimestampSignature) y adjunta un sub-diccionario /SV con las restricciones suministradas por el llamador.
  • Entradas Seed Value emitidas (según Table 234):
    • `/Type /SV`
    • `/SubFilter` array de nombres — p. ej. [/ETSI.CAdES.detached]
    • `/DigestMethod` array de nombres — p. ej. [/SHA256 /SHA384 /SHA512]
    • `/AddRevInfo` booleano — la herramienta de firma debe incluir OCSP/CRL en el CMS
    • `/V` número — versión mínima de PDF (p. ej. 1.7)
    • `/Ff` flags de bit entero (Table 235) — cada parámetro Force* fija el bit correspondiente para que la herramienta de firma DEBA honrar la restricción (bit 2 = forzar SubFilter, bit 3 = forzar V, bit 6 = forzar AddRevInfo, bit 7 = forzar DigestMethod)
  • Guard-rails de conformidad con la spec: PAdES Part 2 V1.2.0 §4.2.6 prohíbe los seed values que forzarían a las herramientas de firma a violar el perfil PAdES (p. ej. especificar SubFilter PKCS#1). HotPDF no impone esto en el lado del productor — el llamador es responsable de pasar solo valores permitidos por la spec (`'adbe.pkcs7.detached'` o `'ETSI.CAdES.detached'` para SubFilter, SHA-256+ para DigestMethod). El doc-comment del helper es explícito sobre la restricción.
  • Compilación limpia en Win32 + Win64; los baselines v2.108 - v2.117 recompilan todos sin cambios. El nuevo smoke smoke_pades_seedvalue crea un campo PAdES-B-T y a continuación adjunta un conjunto completo de restricciones Seed Value (SubFilter restringido a ETSI.CAdES.detached, DigestMethod restringido a SHA256/384/512, AddRevInfo true, MinPDFVersion 1.7, todos los force bits fijados). El verificador Python recorre el sub-dict /SV y asegura la forma de cada entrada; también ejercita la ruta de rechazo "unknown FieldName".
  • Paso 3 de la serie (v2.119): el contenedor XAdES-in-PDF según Part 2 V1.2.0 §6 cierra la serie B/C/D. En el lado del productor es un wrapper delgado sobre el cableado EmbeddedFile / Catalog /AF existente de HotPDF (helper PDF/A-3 de v2.108) más la semántica /AFRelationship que PAdES Part 2 V1.2.0 §6.2.2 espera para payloads XML firmados con XAdES.

2026-05-20 Version 2.117.0

  • Perfiles extendidos PAdES según ETSI EN 319 142-2 V1.2.0 §5 (paso 1 de 3 de la serie B/C/D). El baseline de Part 1 (V1.2.1 §6 B-B / B-T / B-LT / B-LTA) define combinaciones fijas de atributos CMS; Part 2 §5 define una familia paralela de perfiles "extended" (E-BES / E-EPES / E-LTV) con mayor opcionalidad de atributos CMS para llamadores que necesiten flexibilidad más allá del baseline (p. ej. despliegues empresariales multi-política, perfiles CAdES personalizados dentro del envelope PAdES).
  • El parámetro Profile de `AddPAdESSignatureField` acepta ahora tres valores adicionales junto a los cuatro nombres baseline:
    • `'E-BES'` — basic + binding de signing-cert inequívoco vía atributo ESS signing-certificate (equivalente a CAdES-BES dentro del envelope PAdES). Presupuesto /Contents por defecto de 16 KB.
    • `'E-EPES'` — E-BES + atributo firmado signature-policy-identifier explícito. Presupuesto por defecto 16 KB; el llamador típicamente pasa 18-20 KB para cubrir el OID de política + los bytes del qualifier.
    • `'E-LTV'` — validación de largo plazo con cadena de certificados con atributos CMS flexibles + embedding de revocación. Auto-amplía a 20 KB mínimo (refleja B-LT).
  • El formato de cable del lado del productor es idéntico para perfiles baseline y extended — los siete usan SubFilter `ETSI.CAdES.detached`, el mismo patrón sentinel /ByteRange + /Contents y el mismo cableado DSS + ESIC (v2.110 / v2.116). La distinción de nivel vive enteramente en la composición de atributos firmados CMS que produce la librería criptográfica del llamador. La cadena Profile solo varía el diagnóstico de validación y el presupuesto /Contents por defecto; HotPDF no codifica CAdES-BES vs CAdES-EPES vs CAdES-T en el propio dict PDF.
  • El doc-comment actualizado del helper PAdES expone responsabilidades del llamador que destaca Part 2 V1.2.0 §4.2: selección de hash SHA-256+ (eliminación gradual de SHA-1), un único SignerInfo por campo Sig, sin certificados de atributo RFC 5755 y el requisito E-EPES de incluir el atributo firmado signature-policy-identifier. Los elementos del dominio del llamador permanecen fuera del alcance del productor pero el contrato es ahora explícito en la superficie de la API.
  • Compilación limpia en Win32 + Win64; los baselines v2.108 - v2.116 recompilan todos sin cambios. smoke_pades_signature extendido con 3 nuevos campos (SigEBES / SigEEPES / SigELTV) ejercitando cada nuevo valor de Profile a través de las rutas auto-upsize / explicit-budget / default-budget. El verificador Python extendido para recorrer los 7 campos y asegurar la extensión Catalog ESIC (arrastre de v2.116).
  • Paso 2 de la serie B/C/D: v2.118 añade Seed Values (PDF 1.7 §12.7.5.5 + ETSI EN 319 142-1 V1.2.1 §5.5 + Part 2 V1.2.0 §4.2.6) para que los campos de firma PAdES puedan declarar restricciones de handler / SubFilter / digest-method que el lector consumidor debe honrar durante la firma. Paso 3: v2.119 añade soporte de contenedor XAdES-in-PDF según Part 2 V1.2.0 §6 (XML firmado con XAdES embebido como EmbeddedFile + asociación Catalog /AF).

2026-05-20 Version 2.116.0

  • Refresco PAdES según las últimas versiones normativas ETSI y la Decisión de Ejecución eIDAS: ETSI EN 319 142-1 V1.2.1 (2024-01, publicada) + ETSI EN 319 142-2 V1.2.0 (2025-03, borrador de perfiles adicionales + familia de perfiles extended + XAdES-in-PDF) + ETSI TS 119 142-3 V1.1.1 (2016-12, PAdES-DTS Document Time-stamp) + UE 2015/1506 (reconocimiento del sector público). Los baselines HotPDF v2.109 - v2.111 (B-B / B-T / B-LT / B-LTA + standalone timestamp) siguen satisfaciendo el mismo formato de cable; este slice cierra dos brechas del lado del productor que las nuevas versiones de la spec expusieron.
  • Entrada `/Type "DSS"` del diccionario DSS (EN 319 142-1 V1.2.1 §5.4.2.2). La entrada /Type es opcional pero, cuando está presente, debe ser el nombre `/DSS`. HotPDF la emite ahora con avidez para que los validadores estrictos que inspeccionan /Type antes de recorrer el resto del diccionario reconozcan la estructura inmediatamente.
  • Diccionario de extensiones ESIC (TS 119 142-3 §5.1, recomendado). El nuevo método `EnsurePAdESESICExtensions` escribe idempotentemente lo siguiente al Catalog cada vez que se invoca un helper PAdES: `<< /Extensions << /ESIC << /BaseVersion /1.7 /ExtensionLevel 1 >> >> >>`. Los validadores (pre-flight de Adobe Acrobat, EU DSS, callas pdfaPilot) usan esta entrada para identificar el documento como un contenedor PAdES que declara PDF 1.7 + extensiones ESIC nivel 1.
  • Corrección crítica de bug en la ruta round-trip de `EnsurePAdESDSS`. Antes de v2.116 el helper re-resolvía el diccionario DSS en cada llamada vía `Catalog.GetIndexedItem(FindValue('DSS'))`, lo cual devuelve el indirect-link THPDFLink almacenado en el Catalog en lugar del dict DSS real. El cast a THPDFDictionaryObject mistipaba silenciosamente el puntero, haciendo que cada `AddPAdESDSSCertificate` / `AddPAdESDSSOCSP` / `AddPAdESDSSCRL` / `AddPAdESDSSVRI` posterior a la primera llamada saltara silenciosamente su array push (la referencia al array se resolvía a nil, FindValue devolvía -1, el early-out if-Idx-less-than-zero-Exit se disparaba). La corrección de v2.116 cachea la referencia Dict real en un campo `FPAdESDSSDict` para que las llamadas posteriores acierten directamente en la caché. El test smoke_pades_dss emite ahora correctamente 2 certs + 1 OCSP + 1 CRL + 1 entrada VRI (previamente solo el primer cert llegaba a /DSS, con /OCSPs y /CRLs como arrays vacíos).
  • Actualizaciones de doc-comment en los helpers PAdES que cubren responsabilidades del llamador expuestas por Part 2 V1.2.0 §4.2: eliminación gradual de SHA-1 (los llamadores deben elegir SHA-256+ para CMS), un único SignerInfo por PDF Signature (Part 2 §4.2.1 h / Part 1 §4.1 a), no se deben usar certificados de atributo RFC 5755 (Part 2 §4.2 g), y los documentos PAdES-DTS no deben llevar diccionarios VRI (TS 119 142-3 §6.3 h — DSS basta). El formato de cable del lado del productor de HotPDF ya satisface las cuatro restricciones por construcción; la superficie de comentarios indica a los llamadores lo que sus librerías de construcción CMS deben hacer.
  • Compilación limpia en Win32 + Win64; smoke_pades_signature / smoke_pades_dss / smoke_pades_doctimestamp + sus verificadores pasan todos. Extensiones del verificador: smoke_pades_dss asegura ahora `/Type /DSS` + Catalog `/Extensions /ESIC /BaseVersion /1.7 /ExtensionLevel 1`; smoke_pades_doctimestamp asegura la misma extensión ESIC y acepta /Rect de área cero con Y-flipped por página (`[0 H 0 H]` donde H es la altura de la página). Audit doc + TechnicalNotes refrescados para referenciar EN 319 142-1 V1.2.1 / Part 2 V1.2.0 / TS 119 142-3 / UE 2015/1506.

2026-05-20 Version 2.115.0

  • Cierre de la serie productora PDF/X (ISO 15930) (4/4): tipos de anotación + acción prohibidos según ISO 15930-1:2001 / ISO 15930-3:2002 / ISO 15930-7:2010 + ISO 32000-1 §12.5.6 / §12.6.4. Los flujos de impresión PDF/X consumen solo el contenido imprimible visible — las anotaciones multimedia, los archivos embebidos y las acciones interactivas no tienen ningún rol en la imprenta, por lo que las herramientas de pre-flight consumidoras (Adobe Acrobat, Enfocus PitStop, callas pdfaPilot) rechazan los documentos que las contienen.
  • Tipos de anotación prohibidos (rechazados bajo cualquier PDFXCompliance ∈ {X-1a, X-3, X-4}):
    • FileAttachment — la imprenta no consume archivos embebidos
    • Sound — el multimedia no tiene lugar en los flujos de impresión
    • Movie — mismo razonamiento multimedia
    Cada entry point (AddFileAttachmentAnnotation, AddSoundAnnotation, AddMovieAnnotation) lanza ahora excepción con el diagnóstico de sección de spec y el valor de PDFXCompliance cuando se invoca bajo cualquier perfil PDF/X.
  • Tipos de acción prohibidos (rechazados bajo cualquier PDFXCompliance): Launch / JavaScript / SubmitForm / ImportData / Movie / Sound / ResetForm. Los flujos de prepress de impresión no deben disparar ningún programa externo — el PDF se consume solo por la imprenta / RIP, nunca se ejecuta. AddLaunchLink lanza ahora excepción bajo cualquier perfil PDF/X (las demás acciones HotPDF no las expone como rutas de API pública, de modo que se satisfacen automáticamente).
  • La serie PDF/X está ahora funcionalmente completa: v2.112 (opt-in + identidad XMP + Trapped /Name) + v2.113 (OutputIntent + ICC GTS_PDFX) + v2.114 (gate de transparencia + cumplimiento de PageBox) + v2.115 (annot/action prohibidos). Cubiertos los tres perfiles estándar de la industria (X-1a:2001, X-3:2002, X-4:2010). El llamador sigue autorizando el contenido de prepress a nivel de página (targets CMYK correctos, embedding de fuentes vía el subsetter TTF de v2.84, dimensiones TrimBox que casan con el diseño, BleedBox cuando se requiere bleed) pero los gates estructurales de conformidad están todos en su sitio.
  • Compilación limpia en Win32 + Win64; los baselines v2.108 - v2.114 recompilan todos sin cambios. smoke_pdfx_optin extendido con 4 nuevos tests de rechazo (TestRejectFileAttachmentAnnot, TestRejectSoundAnnot, TestRejectMovieAnnot, TestRejectLaunchAction) — cada uno emite el tipo prohibido bajo uno de los tres perfiles PDF/X y confirma un rechazo limpio. El smoke total ejercita ahora 11 escenarios: 3 emisiones positivas + 8 rutas de rechazo.
  • Dos series multi-versión paralelas cerradas en este grupo: PAdES (ETSI EN 319 142, v2.109 - v2.111, 3 commits, B-B / B-T / B-LT / B-LTA + standalone timestamp todos soportados) y PDF/X (ISO 15930, v2.112 - v2.115, 4 commits, gates estructurales de conformidad del lado del productor X-1a / X-3 / X-4 todos en su sitio).

2026-05-20 Version 2.114.0

  • Serie productora PDF/X (ISO 15930), slice 3/4: gate de transparencia + cumplimiento de PageBox según ISO 15930-1:2001 / ISO 15930-3:2002 / ISO 15930-7:2010 + ISO 32000-1 §14.11.2. El slice cubre dos restricciones independientes que se aplican ambas por página; la división del gate refleja el patrón de transparencia-y-page-features de PDF/A-1 de v2.103.
  • Gate de transparencia (§7.5): RegisterExtGState rechaza fill alpha < 1 (/ca), stroke alpha < 1 (/CA) o BlendMode fuera de {Normal, Compatible} cuando PDFXCompliance es 'X-1a' o 'X-3'. PDF/X-1a (ISO 15930-1:2001) y PDF/X-3 (ISO 15930-3:2002) ordenan base PDF 1.3, que es anterior a la transparencia de PDF 1.4. PDF/X-4 (ISO 15930-7:2010, base PDF 1.6) permite explícitamente la transparencia, por lo que el gate no se dispara bajo X-4. El diagnóstico explica la causa y sugiere actualizar a X-4 si se necesita transparencia.
  • Cumplimiento de PageBox (§14.11.2): el nuevo método `EnsurePDFXPageBoxes` recorre cada página y exige al menos uno de /TrimBox o /ArtBox. MediaBox por sí solo no es suficiente porque representa el lienzo de diseño, no las dimensiones de la página acabada que la imprenta necesita saber para saber dónde recortar. La ausencia de PageBoxes lanza excepción con el índice de página 1-based, la citación de sección de la spec y la signatura de llamada SetTrimBox recomendada. El gate EndDoc PDFXCompliance llama automáticamente a EnsurePDFXPageBoxes tras la comprobación de OutputIntent.
  • Patrón típico del llamador: tras fijar PDFXCompliance + Trapped + AddPDFXOutputIntent, cada página del documento debe incluir `CurrentPage.SetTrimBox(Left, Bottom, Right, Top)` casando con las dimensiones del producto acabado (p. ej. 0 0 612 792 para US Letter, 0 0 595 842 para A4). BleedBox es recomendado pero no obligatorio.
  • Compilación limpia en Win32 + Win64; los baselines v2.108 - v2.113 recompilan todos sin cambios. smoke_pdfx_optin extendido con: llamada positiva SetTrimBox en cada uno de los 3 perfiles emitidos, ruta de rechazo por TrimBox ausente, ruta de rechazo por transparencia X-1a (RegisterExtGState con ca=0.5 lanza), ruta positiva permitiendo transparencia X-4 (la misma llamada tiene éxito + ExtGState registrado). El verificador Python recorre cada dict /Type /Page y asegura que al menos uno de /TrimBox / /ArtBox esté presente por página.
  • Slice PDF/X restante: v2.115 cierra la serie con tipos de acción prohibidos (JavaScript / Launch / SubmitForm / ImportData / Movie / Sound), tipos de annot prohibidos (Movie / Sound / FileAttachment / Screen) y rechazo de AcroForm /XFA. Los flujos de impresión no tienen sitio para esas funciones; las herramientas de pre-flight consumidoras las marcan como fallos duros.

2026-05-19 Version 2.113.0

  • Serie productora PDF/X (ISO 15930), slice 2/4: cumplimiento de OutputIntent + perfil ICC según ISO 15930-1:2001 §6.2.2 / ISO 15930-3:2002 §6.2.2 / ISO 15930-7:2010 §6.2 + ISO 32000-1 §14.11.5. Cada perfil PDF/X ordena al menos una entrada /Type /OutputIntent /S /GTS_PDFX nombrando la condición de impresión objetivo y proporcionando el stream de perfil ICC /DestOutputProfile — las imprentas usan esto para casar el color del PDF con su combinación imprenta / papel / tinta.
  • El nuevo helper `AddPDFXOutputIntent(OutputConditionIdentifier, Info, ICCProfileStream, NumComponents, AlternateCS)` refleja el wrapper PDF/A de v2.102 pero emite el subtipo GTS_PDFX en lugar de GTS_PDFA1. Valida que ICCProfileStream sea no-nil y OutputConditionIdentifier sea no-vacío; llama a RegisterICCProfile para embeber el perfil (gestiona automáticamente el fallback colour space /Alternate casando con NumComponents) y a AddOutputIntent para construir el dict OutputIntent.
  • Cumplimiento en EndDoc: cuando PDFXCompliance está fijado, EnsurePDFXOutputIntent recorre el array OutputIntents y exige al menos una entrada con /S /GTS_PDFX + /DestOutputProfile. La entrada ausente lanza excepción con la sugerencia de identificador registrado (FOGRA39 para offset sheetfed, CGATS TR 001 SWOP para web norteamericano, Japan Color 2001 Coated para mercados asiáticos).
  • Flujo PDF/X típico: 1) `Doc.PDFXCompliance := 'X-4'`, 2) `Doc.Trapped := 'False'`, 3) cargar el stream de perfil ICC CMYK (FOGRA39 / SWOP / etc.) en un TStream, 4) `Doc.AddPDFXOutputIntent('FOGRA39 (ISO 12647-2:2004)', '', ICCStream, 4, 'DeviceCMYK')`, 5) diseñar el contenido, 6) Doc.EndDoc emite el OutputIntent enlazado desde el array Catalog /OutputIntents; las herramientas de pre-flight consumidoras (Adobe Acrobat, Enfocus PitStop, callas pdfaPilot) validan el emparejamiento de gestión de color.
  • Compilación limpia en Win32 + Win64; los baselines v2.108 / v2.109 / v2.110 / v2.111 / v2.112 recompilan todos sin cambios. smoke_pdfx_optin (originalmente v2.112) extendido para: registrar un perfil ICC falso + AddPDFXOutputIntent en cada uno de los 3 perfiles emitidos (X-1a → FOGRA39 CMYK, X-3 → SWOP CMYK, X-4 → sRGB RGB) y añadir una ruta de rechazo que asegura que la ausencia de OutputIntent lanza. El verificador Python extendido para recorrer cada dict y confirmar que la entrada /Type /OutputIntent /S /GTS_PDFX con /DestOutputProfile se emite.
  • Slices PDF/X restantes: v2.114 añade el gate de transparencia (X-1a / X-3 rechazan ExtGState fill/stroke alpha < 1 + BM no-Normal; X-4 permite transparencia) + cumplimiento de PageBox (cada página necesita TrimBox o ArtBox según §14.11.2); v2.115 cierra la serie con gates de action / annot / XFA prohibidos (Movie / Sound / FileAttachment / Screen / JavaScript / Launch / SubmitForm / ImportData / XFA todos rechazados porque los flujos de impresión no tienen sitio para ellos).

2026-05-19 Version 2.112.0

  • Iniciada la serie productora PDF/X (ISO 15930) print-prepress. PDF/X es la familia ISO para enviar PDFs press-ready a impresores comerciales; los perfiles X-1a (CMYK + spot estricto), X-3 (color gestionado por ICC) y X-4 (transparencia PDF 1.6 + ICCN) son los tres de uso extendido en la industria. v2.112 es el slice 1/4: propiedad opt-in + metadatos de identidad. Auditoría completa + hoja de ruta de 4 versiones archivada en .superpowers/specs/2026-05-19-pdfx-compliance-audit.md.
  • La nueva propiedad `PDFXCompliance: AnsiString` en THotPDF acepta `''` (desactivado, por defecto), `'X-1a'` (ISO 15930-1:2001), `'X-3'` (ISO 15930-3:2002) o `'X-4'` (ISO 15930-7:2010). Fijar un valor no-vacío activa automáticamente cada requisito del lado del productor que HotPDF puede satisfacer estructuralmente para ese perfil.
  • La nueva propiedad `Trapped: AnsiString` (obligatoria bajo PDFXCompliance) acepta `'True'` / `'False'` / `'Unknown'`. La familia PDF/X ordena esta entrada DocInfo para que la imprenta sepa si el PDF ya fue trapped o necesita manejo de trap en pre-press. Emitida como un /Name PDF (no como string) según ISO 32000-1 §14.11.6.1; p. ej. `<< /Trapped /False >>`.
  • Identificación de metadatos XMP: el packet anuncia ahora `xmlns:pdfx="http://ns.adobe.com/pdfx/1.3/"` con `` fijado a la cadena exacta de la spec (`PDF/X-1:2001`, `PDF/X-3:2002` o `PDF/X-4`) y (para X-1a + X-4) `` fijado al nombre completo del perfil (`PDF/X-1a:2001` o `PDF/X-4:2010`). El pre-flight de Adobe Acrobat / Enfocus PitStop / callas pdfaPilot reconoce el documento como candidato PDF/X.
  • Validación en EndDoc: rechaza valores de perfil desconocidos, rechaza valor Trapped vacío / no reconocido, requiere Title, sube automáticamente la versión PDF al mínimo obligatorio del perfil (X-1a/X-3 → PDF 1.3, X-4 → PDF 1.6). Encriptación + PDFXCompliance es un conflicto duro — llamar a EnableEncrypt (o `ActivateProtection := True`) lanza con un diagnóstico claro explicando que los flujos PDF/X deben inspeccionar cada objeto sin manejo de contraseña.
  • El llamador sigue autorizando el OutputIntent + perfil ICC + TrimBox/ArtBox + espacios de color de prepress válidos + embedding de fuentes correcto (y evita la transparencia para X-1a/X-3) para conformidad completa PDF/X. Los seguimientos v2.113 - v2.115 cierran esas brechas: v2.113 añade `AddPDFXOutputIntent` (intent /GTS_PDFX obligatorio), v2.114 añade gate de transparencia + cumplimiento de PageBox, v2.115 cierra la serie con gates de action/annot/XFA prohibidos.
  • Compilación limpia en Win32 + Win64; los baselines v2.108 / v2.109 / v2.110 / v2.111 recompilan todos sin cambios. El nuevo smoke smoke_pdfx_optin emite tres PDFs (uno por perfil) y verifica el namespace XMP + GTS_PDFXVersion + GTS_PDFXConformance más DocInfo /Trapped /Name más rutas de rechazo por perfil inválido / Trapped vacío / Trapped desconocido / conflicto de encriptación.
  • Nota sobre la serie PAdES: v2.109 / v2.110 / v2.111 cubren los casos de uso B-B / B-T / B-LT / B-LTA + standalone timestamp. El v2.112 originalmente planeado (PDF solo-timestamp ETSI.RFC3161 standalone) es efectivamente implícito — `AddDocumentTimestampSignature` de v2.111 funciona sobre un documento fresco sin firma interna — por lo que la serie PAdES se considera cerrada en 3 commits y v2.112 se reasigna a PDF/X. Todos los perfiles baseline de ETSI EN 319 142-1 están soportados.

2026-05-19 Version 2.111.0

  • Serie productora PAdES, slice 3/4: soporte de firma Document Timestamp PAdES-B-LTA según ISO 32000-1 §12.8.5 + ETSI EN 319 142-1 §5.7. El document timestamp es una segunda firma aplicada DESPUÉS de que la firma de validación de largo plazo esté completa; cubre todo el archivo en estado LT (firma original + DSS) y prueba que el material LT existía en un tiempo notarizado. Este es el perfil archival requerido para retención regulada por la UE más allá del tiempo de vida del certificado CA original.
  • Nuevo helper `THPDFPage.AddDocumentTimestampSignature(FieldName, ContentsBytes)`. A diferencia de una firma PAdES normal, un document timestamp tiene:
    • SubFilter `/ETSI.RFC3161` (un TimeStampToken RFC 3161, no CAdES).
    • Rect del widget de área cero (un timestamp no tiene apariencia visible — metadatos puramente criptográficos).
    • Sin /Reason / /Location / /ContactName (esos viven en los signed-attrs del TST, no en el dict sig).
    • Presupuesto de bytes /Contents por defecto 16 KB (el llamador puede pasar menos hasta un piso de 1 KB; un TST RFC 3161 completo con cadena de cert es típicamente 4-8 KB).
  • El alcance del productor termina en el formato de cable: el helper crea el dict placeholder /Type /Sig con el SubFilter específico de timestamp y un sentinel /ByteRange + /Contents para parcheado post-emisión vía PreparePDFForSigning + InsertSignatureHex. El llamador obtiene los bytes del TST RFC 3161 real desde una Trusted Timestamp Authority (TSA, p. ej. DigiCert / GlobalSign / FreeTSA HTTP POST) y los inyecta. El flujo archival PAdES-B-LTA típico apila: 1) emitir firma base PAdES-B-LT (v2.109), 2) poblar Catalog /DSS con cadena de cert + OCSP/CRL (v2.110), 3) guardar como actualización incremental, 4) añadir campo de document timestamp sobre los bytes en estado LT (v2.111), 5) solicitar TST de la TSA sobre el byte range, 6) inyectar los bytes del TST.
  • Compilación limpia en Win32 + Win64; los baselines v2.108 / v2.109 / v2.110 recompilan todos sin cambios. El nuevo smoke smoke_pades_doctimestamp emite un campo PAdES-B-LT 'SigInner' más un campo de document timestamp 'DocTS' vía el nuevo helper. El verificador Python asegura que la firma interna tiene /SubFilter /ETSI.CAdES.detached, el /Rect del widget del timestamp es de área cero, el dict /V del timestamp tiene /SubFilter /ETSI.RFC3161 y sin /Reason / /Location / /ContactName, y /Contents es un placeholder de 16 KB (32768 caracteres hex).
  • Slice PAdES restante: v2.112 (opcional) añade PDFs solo-timestamp ETSI.RFC3161 standalone (sin firma interna CAdES — un contenedor de documento de timestamp puro).

2026-05-19 Version 2.110.0

  • Serie productora PAdES, slice 2/4: soporte del diccionario Document Security Store (DSS) según ISO 32000-2 §12.8.4.3 + ETSI EN 319 142-1 §5.6. PAdES-B-LT (y B-LTA) requieren información relacionada con la validación — cadena de cert, respuestas OCSP, CRLs — embebida en el propio PDF para que las firmas puedan ser verificadas mucho tiempo después del tiempo de firma original sin necesidad de perseguir servidores PKI externos.
  • Nuevos helpers en THotPDF:
    • `EnsurePAdESDSS` — crea de forma diferida el dict indirecto Catalog /DSS con sus arrays /Certs, /OCSPs, /CRLs y un sub-dict /VRI vacío; las llamadas posteriores devuelven el dict existente.
    • `AddPAdESDSSCertificate(CertDERBytes)` — anexa un certificado X.509 codificado en DER como stream indirecto a /DSS /Certs; devuelve el objeto stream indirecto para uso en referencias VRI.
    • `AddPAdESDSSOCSP(OCSPDERBytes)` — lo mismo para OCSP BasicOCSPResponse a /DSS /OCSPs.
    • `AddPAdESDSSCRL(CRLDERBytes)` — lo mismo para Certificate Revocation List a /DSS /CRLs.
    • `AddPAdESDSSVRI(SigContentsHexSHA1, Certs, OCSPs, CRLs)` — crea una entrada VRI por firma indexada por el SHA-1 hex MAYÚSCULAS de los bytes /Contents de la firma (la forma de clave ordenada por la spec); cada entrada tiene arrays /Cert / /OCSP / /CRL que referencian los streams indirectos creados arriba.
  • El alcance del productor termina en el formato de cable. El llamador sigue calculando el SHA-1 del /Contents firmado final y suministra los certificados / respuestas OCSP / CRLs codificados en DER desde su tooling PKI. Flujo típico: construir firma base PAdES-B-T (v2.109), luego añadir certs / OCSPs / CRLs al DSS para verificación de largo plazo, luego adjuntar entradas VRI enlazando información de validación a firmas específicas.
  • Compilación limpia en Win32 + Win64; los baselines v2.108 + v2.109 recompilan todos sin cambios. El nuevo smoke smoke_pades_dss crea un campo de firma PAdES-B-LT, añade 2 certs + 1 OCSP + 1 CRL al DSS y añade una entrada VRI con un digest hex de prueba. El verificador Python recorre la estructura del diccionario DSS: ref Catalog /DSS, conteos de arrays /Certs / /OCSPs / /CRLs, coincidencia de clave VRI, refs cert/OCSP/CRL de la entrada VRI casan con los arrays DSS de nivel superior.
  • Slices PAdES restantes: v2.111 implementa la firma de document timestamp PAdES-B-LTA (segundo campo Sig con SubFilter ETSI.RFC3161 que cubre todo el archivo LT); v2.112 (opcional) añade PDFs solo-timestamp standalone.

2026-05-19 Version 2.109.0

  • Iniciado el trabajo productor de firmas digitales PAdES (ETSI EN 319 142). v2.109 añade un wrapper de alto nivel sobre la infraestructura de firmas existente de ISO 32000-1 §12.8 (v2 del helper AddSignedSignatureField) que hardcodea el SubFilter requerido por PAdES y valida el perfil de conformidad. Auditoría completa + hoja de ruta de 4 versiones archivada en .superpowers/specs/2026-05-19-pades-compliance-audit.md.
  • Nuevo helper `THPDFPage.AddPAdESSignatureField(FieldName, Rect, Profile, Reason, Location, ContactName, ContentsBytes, Flags)`. Profile selecciona el nivel baseline ETSI EN 319 142-1: 'B-B' (CAdES básico, sin timestamp), 'B-T' (con timestamp trusted RFC 3161, mínimo para uso legal en la UE), 'B-LT' (con información de validación DSS para verificación de largo plazo) o 'B-LTA' (con firma de document timestamp para archival). Los cuatro usan SubFilter 'ETSI.CAdES.detached'; el parámetro de perfil varía el diagnóstico y el presupuesto de bytes /Contents por defecto. Otras cadenas de perfil lanzan excepción con la enumeración ETSI listada.
  • Presupuesto Contents auto-ampliado: PAdES-B-B + B-T por defecto 16 KB (el llamador puede suministrar menos; aplicamos un piso de 64 bytes heredado de v2.108). PAdES-B-LT auto-amplía a 20 KB mínimo (CAdES + cadena de cert + datos OCSP/CRL). PAdES-B-LTA auto-amplía a 24 KB mínimo (B-LT + document timestamp). Los valores mayores suministrados por el llamador pasan a través; el helper solo eleva el piso.
  • Alcance del productor: el wrapper crea el dict placeholder /Type /Sig con /SubFilter /ETSI.CAdES.detached, /Reason / /Location / /ContactName / /M (fecha de firma) y el sentinel /ByteRange + /Contents para parcheado post-emisión vía PreparePDFForSigning + InsertSignatureHex. Los bytes reales CMS / CAdES, la recuperación de timestamp RFC 3161, la recolección de OCSP/CRL y la construcción del diccionario DSS siguen siendo responsabilidad del llamador — esos requieren una librería criptográfica externa (OpenSSL, Bouncy Castle, Windows CAPI, etc.) fuera del alcance del productor PDF.
  • Compilación limpia en Win32 + Win64. El nuevo smoke smoke_pades_signature emite cuatro campos de firma PAdES (B-B / B-T / B-LT / B-LTA) y confirma que los valores de perfil inválidos lanzan. El verificador Python asegura AcroForm /SigFlags 3, cada widget tiene /FT /Sig + ref indirecta /V a un dict /Type /Sig con /SubFilter /ETSI.CAdES.detached, y la longitud hex del placeholder /Contents casa con el presupuesto de bytes esperado (auto-ampliado).
  • Slices PAdES restantes en la hoja de ruta: v2.110 añade el constructor del diccionario Catalog /DSS (Document Security Store) para la información de validación PAdES-B-LT (cadena de cert + OCSP/CRL); v2.111 añade el campo de firma standalone document timestamp para archival PAdES-B-LTA; v2.112 (opcional) añade PDFs solo-timestamp ETSI.RFC3161.

2026-05-19 Version 2.108.0

  • La serie productora PDF/A-2 + PDF/A-3 SE CIERRA (4/4): ISO 19005-3 Annex E Associated Files. Nuevo helper de alto nivel AddPDFA3AssociatedFile(FileName, MimeType, Description, Relationship, FileBytes) adjunta un archivo arbitrario (factura XML, hoja de cálculo fuente, metadatos JSON, etc.) a un contenedor híbrido PDF/A-3 y lo registra en el array /AF del Catalog con la semántica /AFRelationship del §E.4.
  • Flujo de contenedor híbrido: el caso de uso típico es facturación estilo ZUGFeRD en PDF/A-3 donde la renderización legible por humanos de la factura vive en el stream de página del PDF y la fuente XML legible por máquina viaja junto como un Associated File. La clave /AFRelationship indica a los consumidores cómo se relaciona el archivo adjunto con el contenido visible: 'Source' (datos fuente originales), 'Data' (datos crudos que el PDF visualiza), 'Alternative' (representación alternativa), 'Supplement' (material suplementario), 'Unspecified' (ninguna de las anteriores).
  • Gating de spec: AddPDFA3AssociatedFile lanza excepción bajo cualquier valor PDFACompliance no-PDF/A-3 (Annex E es específico de part-3) con un diagnóstico sugiriendo AddFileAttachmentAnnotation para PDF/A-2 y notando que PDF/A-1 §6.1.11 prohíbe los adjuntos por completo. FileName o MimeType vacío lanza; valor Relationship inválido lanza con la enumeración §E.4 de la spec.
  • Estructura de emisión: el stream EmbeddedFile indirecto lleva /Type /EmbeddedFile + /Subtype + /Params <> + los bytes del archivo. El dict Filespec indirecto lleva /Type /Filespec + /F + /UF (ambos = FileName) + /Desc opcional + /EF <> + /AFRelationship (clave Annex E). El array Catalog /AF se crea de forma diferida en el primer adjunto y se anexa en llamadas posteriores. El dict Filespec devuelto permite a los llamadores estampar claves opcionales adicionales (p. ej. /CI checksum/info) cuando es necesario.
  • Compilación limpia en Win32 + Win64. El nuevo smoke smoke_pdfa3_associated construye una factura híbrida PDF/A-3B con una factura XML real adjunta como /AFRelationship Source, luego ejercita tres rutas de rechazo: PDF/A-1 rechazado, PDF/A-2 rechazado, /AFRelationship inválido rechazado. El verificador Python asegura el grafo del diccionario Filespec / EmbeddedFile (Type / F / UF / Desc / AFRelationship / EF F UF / EmbeddedFile Type / Subtype escape #2F / Params Size).
  • La superficie productora PDF/A-2 + PDF/A-3 está ahora completamente cerrada en auditoría (v2.105 opt-in del wrapper + XMP, v2.106 cobertura de annot/action prohibidos, v2.107 §6.1.13 límites de implementación, v2.108 Annex E Associated Files). Con PDF/A-1 (v2.101-104) y PDF/UA-1 (v2.94-v2.100) ya en conformidad estricta, HotPDF cubre ahora la matriz productora archival/accesibilidad completa PDF/A-1/2/3 + PDF/UA-1 end to end.

2026-05-19 Version 2.107.0

  • Serie productora PDF/A-2 + PDF/A-3, slice 3/4: cumplimiento de límites de implementación ISO 19005-2 §6.1.13 + ISO 19005-3 §6.1.13. PDF/A-1 §6.1.13 es significativamente más laxo (sin tope duro de longitud de string), por lo que el nuevo gate solo se dispara cuando PDFACompliance es PDF/A-2 o posterior ('2*' / '3*').
  • El nuevo método público EnsurePDFAImplementationLimits valida los valores controlados por el llamador del documento contra los límites duros de la spec: las cadenas Doc.Title / Author / Subject / Keywords / Lang deben ser ≤ 32767 bytes; la dimensión MediaBox de cada página debe caer en [3..14400] unidades. EndDoc invoca el validador automáticamente cuando PDFACompliance es PDF/A-2/3; los llamadores también pueden invocarlo directamente a mitad de la construcción para diagnósticos tempranos.
  • Límites satisfechos naturalmente documentados en el informe de auditoría: rango de entero (-2³¹..2³¹-1), rango de real (±3.403×10³⁸, near-zero ≥1.175×10⁻³⁸), longitud de nombre ≤127 bytes, conteo de objetos indirectos ≤8388607, anidamiento q/Q ≤28, colorantes DeviceN ≤32, CID ≤65535. Las rutas de emisión de HotPDF nunca los exceden porque están atadas a estructuras de datos del lado del productor que HotPDF controla. El validador se enfoca en strings suministrados por el llamador + límites de página porque esos son los únicos límites que un llamador real-world puede sobrepasar.
  • Formularios XFA (§6.4.2): HotPDF no emite AcroForm /XFA ni Catalog /NeedsRendering. Ambos se satisfacen naturalmente en PDF/A-1/2/3 — documentado en el informe de auditoría para que futuras extensiones del productor no los reintroduzcan.
  • Glifo .notdef (§6.2.11.8 en PDF/A-2): la spec prohíbe que los operadores text-showing referencien .notdef. El manejo de fuentes de HotPDF evita esto cuando los llamadores usan fuentes Unicode TTF registradas (ruta v2.74-v2.86) porque el productor mapea cada codepoint a un glifo real o rechaza el registro. Responsabilidad del llamador para rutas de emisión de texto crudo; documentado en el informe de auditoría.
  • Namespace XMP (§6.6.2): la spec prohíbe los atributos bytes / encoding en el header del packet XMP. BuildXMPPacket emite un header mínimo (id="W5M0MpCehiHzreSzNTczkc9d") sin ninguno de los dos atributos — satisfecho naturalmente en todas las partes PDF/A.
  • Compilación limpia en Win32 + Win64. El nuevo smoke smoke_pdfa2_limits ejercita tres rutas: PDF/A-2 dentro de límites pasa; PDF/A-1 con un Title de 32768 bytes también pasa (sin gate §6.1.13); PDF/A-2 con un Title de 32768 bytes es rechazado con el diagnóstico de sección de spec. Los baselines PDF/A-2/3 anteriores recompilan todos sin cambios.
  • Slice productor PDF/A-2/3 restante: v2.108 implementa PDF/A-3 Annex E Associated Files (la función de contenedor híbrido específica de PDF/A-3: array Catalog /AF de especificaciones de archivo con clave /AFRelationship + EmbeddedFile /Params /CheckSum + /ModDate).

2026-05-19 Version 2.106.0

  • Serie productora PDF/A-2 + PDF/A-3, slice 2/4: cobertura de PDF/A-2 §6.3.1 + §6.5.1 (y los idénticos PDF/A-3 §6.3.1 + §6.5.1). Los gates de anotaciones + acciones prohibidas de PDF/A-1 de v2.104 ya disparaban ante cualquier PDFACompliance no-vacío, por lo que siguen disparando bajo PDF/A-2/3 sin cambios. v2.106 confirma este comportamiento, actualiza los mensajes de diagnóstico para referenciar las secciones de spec unificadas a través de todas las partes PDF/A, y documenta los subtipos prohibidos adicionales introducidos por PDF/A-2 que HotPDF satisface naturalmente (sin ruta de emisión pública).
  • Matriz de acciones prohibidas: PDF/A-1 prohíbe 6 (Launch / Sound / Movie / ResetForm / ImportData / JavaScript). PDF/A-2 + PDF/A-3 extienden el conjunto prohibido a 13 añadiendo Hide / SetOCGState / Rendition / Trans / GoTo3DView + los subtipos deprecados set-state / no-op. AddLaunchLink sigue lanzando; HotPDF no tiene entrada pública que emita las otras 12, por lo que se satisfacen automáticamente.
  • Matriz de tipos de anotación prohibidos: PDF/A-1 prohíbe FileAttachment / Sound / Movie. PDF/A-2 / PDF/A-3 eliminan la prohibición de FileAttachment (relajada en v2.105) pero añaden 3D / Screen al conjunto prohibido. AddSoundAnnotation + AddMovieAnnotation siguen lanzando en todas las partes PDF/A; HotPDF no tiene ruta de emisión 3D / Screen por lo que esas se satisfacen naturalmente.
  • Actualización del mensaje de diagnóstico: los tres gates de v2.104 (AddSoundAnnotation, AddMovieAnnotation, AddLaunchLink) referencian ahora las tres secciones de spec relevantes en su texto de error (p. ej. "PDF/A-1 §6.5.2 / PDF/A-2 §6.3.1 / PDF/A-3 §6.3.1") para que los desarrolladores vean la prohibición unificada. El mensaje de AddLaunchLink enumera adicionalmente el conjunto extendido de acciones prohibidas PDF/A-2/3.
  • Compilación limpia en Win32 + Win64. El nuevo smoke smoke_pdfa2_annot_action ejercita los tres gates bajo los seis niveles PDF/A-2/3 (2B, 2U, 2A, 3B, 3U, 3A); cada llamada asegura la excepción esperada con un diagnóstico §6.3.1 / §6.5.1. Los baselines PDF/A-1 anteriores (smoke_pdfa1_compliance / smoke_pdfa1_forbidden / smoke_pdfa1_annot_action) y v2.105 smoke_pdfa2_compliance recompilan todos sin cambios.
  • Slices productores PDF/A-2/3 restantes: v2.107 impone los límites de implementación ISO 19005-2 §6.1.13 (rangos de bytes de integer / real / string / name, anidamiento q/Q, colorantes DeviceN, CID, límite de página) + §6.4.2 XFA + §6.2.11 Type 3 / .notdef estricto; v2.108 implementa PDF/A-3 Annex E Associated Files (PDFs contenedores híbridos con tipos arbitrarios de archivos embebidos).

2026-05-19 Version 2.105.0

  • Iniciada una serie de conformidad productora PDF/A-2 (ISO 19005-2:2011) + PDF/A-3 (ISO 19005-3:2012) de cuatro versiones. Auditoría + hoja de ruta archivadas en .superpowers/specs/2026-05-19-pdfa2-pdfa3-compliance-audit.md. v2.105 entrega el opt-in de la capa wrapper, las extensiones del namespace pdfaid de XMP, el auto-bump de la versión PDF base a 1.7 y la relajación selectiva de los gates estrictos PDF/A-1 de v2.101-104 que las specs PDF/A-2 / PDF/A-3 permiten explícitamente.
  • La propiedad PDFACompliance acepta ahora seis nuevos valores además de los de PDF/A-1 '' / 'A' / 'B': '2A' / '2B' / '2U' (Niveles A / B / U de PDF/A-2) y '3A' / '3B' / '3U' (Niveles A / B / U de PDF/A-3). El Nivel U es el nuevo tier "preservación Unicode únicamente" introducido por PDF/A-2 (visual + texto extraíble en Unicode, sin requisito Tagged PDF).
  • BuildXMPPacket extendido: el elemento refleja ahora el número de parte parseado (1 / 2 / 3) y refleja la letra de nivel parseada (A / B / U). veraPDF y validadores similares identifican la parte PDF/A del documento por este par. Según ISO 19005-2 §6.6.4 + ISO 19005-3 §6.6.4 Table 8.
  • Auto-bump: PDFACompliance '2*' / '3*' sube automáticamente la versión del documento a PDF 1.7 porque PDF/A-2 y PDF/A-3 están ambos basados en ISO 32000-1 (PDF 1.7). PDF/A-1 permanece anclado en PDF 1.4.
  • El gate de transparencia v2.103 relajado: RegisterExtGState solo lanza ahora sobre alpha transparente / blend mode no-Normal cuando PDFACompliance es PDF/A-1 estricto ('A' o 'B'). PDF/A-2 / PDF/A-3 §6.4 permiten explícitamente la transparencia (con requisitos separados como la presencia de OutputIntent y los blend modes nombrados completos de PDF 1.4), por lo que PDFACompliance '2*' / '3*' deja pasar la llamada.
  • El gate FileAttachment v2.104 relajado: AddFileAttachmentAnnotation solo lanza ahora bajo PDF/A-1 estricto. PDF/A-2 §6.3.1 solo prohíbe los tipos de anotación 3D / Sound / Screen / Movie; FileAttachment está permitido y es la base del flujo híbrido Annex E Associated Files de PDF/A-3. Los demás gates de annot/action de v2.104 (anotaciones Sound, Movie; acción Launch) siguen disparando en todas las partes PDF/A según las prohibiciones compartidas §6.3.1 + §6.5.1.
  • Los nuevos métodos helper de THotPDF PDFAPart / PDFAConformanceLevel / IsPDFA1Strict / IsPDFA2OrLater hacen ergonómica la ramificación por valor. Los gates PDF/A existentes de v2.101-104 usan ahora estos helpers para rutas de código más limpias y mensajes de error más claros que sugieren cambiar a PDF/A-2 / PDF/A-3 cuando sea apropiado.
  • Compilación limpia en Win32 + Win64; los baselines v2.101-104 (smoke_pdfa1_compliance / smoke_pdfa1_forbidden / smoke_pdfa1_annot_action) recompilan todos sin cambios. El nuevo smoke smoke_pdfa2_compliance emite cuatro PDFs (Niveles 2B / 2U / 2A / 3U), ejercita la relajación de transparencia de v2.103, la relajación de FileAttachment de v2.104, más dos rutas de validación (nivel '4Q' inválido rechazado; el gate de transparencia PDF/A-1 sigue disparándose). El verificador Python asegura que XMP pdfaid:part + conformance casan con cada nivel y el Nivel 2A tiene herencia Tagged-PDF.
  • Slices productores PDF/A-2/3 restantes: v2.106 extiende el conjunto de acciones prohibidas de 6 a 13 (Hide / SetOCGState / Rendition / Trans / GoTo3DView añadidos; set-state / no-op deprecados) más ajusta las prohibiciones de tipos de annot (3D / Screen añadidos, FileAttachment eliminado); v2.107 impone los límites de implementación ISO 19005-2 §6.1.13 + gates XFA; v2.108 implementa PDF/A-3 Annex E Associated Files (PDFs contenedores híbridos).

2026-05-19 Version 2.104.0

  • La serie productora PDF/A-1 SE CIERRA (slice 4/4): cerrados los tipos de anotación prohibidos §6.5.2 (FileAttachment, Sound, Movie) y el subtipo de acción Launch §6.6.1. Con v2.104 entregado, las 17 brechas del lado del productor de la auditoría PDF/A-1 del 2026-05-19 (.superpowers/specs/2026-05-19-pdfa1-compliance-audit.md) están cerradas: los cuatro heavy hitters (identificación §5/§6.7.11, prohibición de encriptación §6.1.3, OutputIntent §6.2.2, transparencia §6.4) más los gates variados de funciones prohibidas.
  • Tipos de anotación prohibidos §6.5.2: AddFileAttachmentAnnotation, AddSoundAnnotation, AddMovieAnnotation lanzan ahora bajo PDFACompliance con un diagnóstico claro de sección de spec. Según spec "los tipos FileAttachment, Sound y Movie no estarán permitidos" para evitar dependencias de contenido externo y multimedia en archivos archival.
  • Acción Launch §6.6.1: AddLaunchLink lanza ahora bajo PDFACompliance con un diagnóstico §6.6.1 que nombra el target de launch. Según spec "Las acciones Launch, Sound, Movie, ResetForm, ImportData y JavaScript no estarán permitidas". Los otros cinco subtipos de acción prohibidos (Sound/Movie/ResetForm/ImportData/JavaScript) no tienen entry point público en HotPDF que los emita, por lo que se satisfacen naturalmente sin gates explícitos.
  • PDFUACompliance + PDFACompliance combinados funcionan ahora correctamente en los cuatro slices PDF/A. Los dos opt-ins se apilan: el Level A hereda PDFUACompliance auto-on para Tagged PDF; ambos wrappers aplican sus propias guards específicas de § (Suspects/Tabs/Lang para PDF/UA; Encrypt/Transparency/Annot/Action para PDF/A) sin interferencia.
  • Resumen del cierre de auditoría: todos los requisitos §6 de formato de archivo del lado del productor PDF/A-1 que HotPDF puede satisfacer estructuralmente están ahora cerrados. Las responsabilidades del llamador restantes (§6.1.5 equivalencia Info-XMP, §6.2.3.3 matching de espacio de color ↔ OutputIntent, §6.3 detalles de fuente donde el trabajo PDF/UA v2.74-v2.86 ya gestionó la mayoría) se alinean con las responsabilidades correspondientes del llamador en PDF/UA-1.
  • Compilación limpia en Win32 + Win64; v2.103 smoke_pdfa1_forbidden recompila limpiamente. El nuevo smoke smoke_pdfa1_annot_action ejercita las cuatro rutas gateadas (AddFileAttachmentAnnotation, AddSoundAnnotation, AddMovieAnnotation, AddLaunchLink) bajo PDFACompliance y asegura que cada llamada lanza con un diagnóstico §6.5.2 o §6.6.1.
  • La superficie productora PDF/A-1 está ahora completamente cerrada en auditoría. Las futuras extensiones PDF/A-2 (ISO 19005-2:2011 basado en PDF 1.7, permite transparencia + capas) y PDF/A-3 (ISO 19005-3:2012, permite archivos embebidos para flujos híbridos) permanecen como candidatas futuras potenciales pendientes de demanda del llamador.

2026-05-19 Version 2.103.0

  • Serie productora PDF/A-1, slice 3/4: cerrada la transparencia §6.4 (brecha CRÍTICA #15) + la función de transferencia ExtGState /TR §6.2.8 (brecha #14). Dos brechas §6 relacionadas con rutas productoras de HotPDF existentes se satisfacen naturalmente sin cambios de código: filtro LZW §6.1.10 (HotPDF solo emite FlateDecode en su ruta de writer) e /Interpolate de imagen §6.2.4 (HotPDF nunca emite /Interpolate). Estas se documentan ahora como automáticas en el informe de auditoría.
  • La transparencia §6.4 prohíbe alpha distinto de 1.0 y blend modes distintos de Normal/Compatible. RegisterExtGState gatea ahora bajo PDFACompliance: fill alpha < 1 lanza (PDF/A-1 requiere /ca = 1.0); stroke alpha < 1 lanza (/CA = 1.0); blend mode distinto de `Normal` o `Compatible` lanza. Los tres diagnósticos nombran la sección de spec y el valor infractor. El ExtGState por defecto (sin claves alpha, sin BM) y los valores explícitamente permitidos (alphas 1.0, BM Normal / Compatible) pasan sin cambios.
  • El ExtGState §6.2.8 prohíbe la clave de función de transferencia /TR (deprecada; PDF/A-1 solo permite /TR2 con valor /Default, y aun así es mejor evitarlo). RegisterTransferFunctionState lanza ahora bajo PDFACompliance con un diagnóstico claro. Los llamadores que necesiten funciones de transferencia para flujos PDF/X pueden seguir usando el helper fuera del modo PDFACompliance.
  • LZW §6.1.10 + Interpolate §6.2.4: documentadas como satisfechas naturalmente por las rutas productoras existentes de HotPDF (writer solo-FlateDecode, sin emisión /Interpolate). Sin cambios de código requeridos; señaladas en los comentarios del informe de auditoría para que futuros llamadores no las reintroduzcan.
  • Compilación limpia en Win32 + Win64; v2.102 smoke_pdfa1_compliance recompila limpiamente. El nuevo smoke smoke_pdfa1_forbidden ejercita las tres rutas gateadas (ca<1, CA<1, BM=Multiply, registro /TR) y verifica que las rutas permitidas (ca=1.0, CA=1.0, BM=Normal/Compatible, sin-alpha) siguen pasando. Sin verificador — el propio smoke asegura vía try/except que cada llamada gateada lanza.
  • Slice productor PDF/A-1 restante: v2.104 cierra los tipos de anotación prohibidos §6.5.2 (FileAttachment, Sound, Movie) + las reglas /F /AP de annot §6.5.3 + los tipos de acción prohibidos §6.6.1 (Launch, Sound, Movie, ResetForm, ImportData, JavaScript) + /A /AA de campos de formulario §6.9 + /NeedAppearances. Tras v2.104 entregar, la superficie productora PDF/A-1 está completamente cerrada en auditoría.

2026-05-19 Version 2.102.0

  • Serie productora PDF/A-1 (ISO 19005-1:2005), slice 2/4: cerrada la brecha §6.2.2 OutputIntent + perfil ICC. El nuevo helper de alto nivel `AddPDFAOutputIntent(OutputConditionIdentifier, Info, ICCProfileStream, NumComponents, AlternateCS)` envuelve el patrón de dos llamadas RegisterICCProfile + AddOutputIntent('GTS_PDFA1', ...) de v2.51 en una sola. PDF/A-1 estricto requiere una entrada `/Type /OutputIntent /S /GTS_PDFA1` con stream ICC `/DestOutputProfile` válido — sin él los archivos conformes no pueden reproducir con precisión los colores entre plataformas.
  • El gate EndDoc PDFACompliance añade una comprobación `EnsurePDFAOutputIntent`: recorre la lista OutputIntent registrada, busca al menos una entrada `/S /GTS_PDFA1` con `/DestOutputProfile`. La ausencia lanza un diagnóstico claro nombrando la sección de spec y dirigiendo a los llamadores a AddPDFAOutputIntent. Según §6.2.2 + Annex A.
  • Flujo archival típico: pase un stream de perfil ICC sRGB IEC61966-2.1 para PDF/A orientado a pantalla, FOGRA39 / GRACoL para impresión, o el perfil CMYK registrado apropiado para prepress. NumComponents debe ser 1 (Gray), 3 (RGB / Lab) o 4 (CMYK) según PDF 1.7 8.6.5.5 Table 66; el helper reenvía la validación a RegisterICCProfile.
  • El helper valida: OutputConditionIdentifier vacío lanza (§6.2.2 requiere el identificador); ICCProfileStream nil lanza (DestOutputProfile obligatorio). No devuelve valor — el OutputIntent se auto-registra en FOutputIntents y se expone a través de la ruta de serialización Catalog /OutputIntents existente.
  • Compilación limpia en Win32 + Win64; v2.101 smoke_pdfa1_compliance actualizado para llamar a AddPDFAOutputIntent (con un stand-in ICC falso de 192 bytes; los llamadores reales envían un binario ICC verdadero). El nuevo 4º caso de validación asegura que la ausencia de OutputIntent bajo PDFACompliance lanza con un diagnóstico §6.2.2. Verificador Python extendido para comprobar el array Catalog /OutputIntents, recorre entradas hallando `/S /GTS_PDFA1` con ref indirecta `/DestOutputProfile`.
  • Slices productores PDF/A-1 restantes: v2.103 cierra #15 Transparencia (CRÍTICO) + #5 filtro LZW + #10 /Interpolate de imagen + #14 /TR de ExtGState; v2.104 cierra #16 annot FileAttachment/Sound/Movie + #17 reglas /F /AP de annot + #18 acciones Launch/Sound/Movie/ResetForm/ImportData/JavaScript + #20 /A /AA de campo de formulario + /NeedAppearances.

2026-05-19 Version 2.101.0

  • Iniciada una serie de conformidad productora PDF/A-1 (ISO 19005-1:2005) de cuatro versiones con el opt-in de la capa wrapper. El informe completo de auditoría de 17 brechas está archivado en `.superpowers/specs/2026-05-19-pdfa1-compliance-audit.md`; v2.101 entrega las piezas fundacionales (propiedad PDFACompliance, identificación XMP pdfaid, estrictez Title/Lang, guard de conflicto de encriptación). v2.102 - v2.104 cierran las brechas restantes de gestión de color, transparencia, fuentes y anotación/acción.
  • La nueva propiedad `PDFACompliance: AnsiString` acepta '' (desactivado, por defecto), 'A' (Nivel A: visual + Tagged PDF + accesibilidad) o 'B' (Nivel B: solo preservación visual). Los valores inválidos lanzan en tiempo de EndDoc. Según los niveles de conformidad ISO 19005-1:2005 §5.
  • Fijar PDFACompliance:='A' activa automáticamente PDFUACompliance (el Nivel A hereda los requisitos Tagged PDF del §6.8 que casan con los requisitos de estructura lógica §7 de PDF/UA-1). Ambos wrappers se pueden combinar sin conflicto; los gates Lang/Title/Suspects de PDFUACompliance de v2.94 se apilan sobre las guardas PDF/A de v2.101.
  • Fijar cualquiera de los niveles activa automáticamente FEnableXMPMetadata para que el documento lleve el stream XMP /Metadata requerido. Según §6.7.2 Catalog Metadata es obligatorio.
  • BuildXMPPacket extendido: cuando PDFACompliance es no-vacío, el packet XMP declara `xmlns:pdfaid="http://www.aiim.org/pdfa/ns/id/"` más `1` y `A` (o `B`). veraPDF y validadores similares confían en esta tripleta exacta para clasificar el documento como candidato PDF/A-1A o 1B. Según el esquema de identificación PDF/A §6.7.11 Table 6.
  • Título estricto / Idioma: PDFACompliance activado + Doc.Title='' lanza (PDF/A-1 §6.7.3 requiere dc:title en XMP equivalente a Info /Title). La estrictez del idioma refleja el comportamiento de PDFUACompliance de la versión 2.94 (§6.8.4 para el nivel A) para que los autores elijan una etiqueta BCP-47 real en lugar de recibir una opción predeterminada silenciosa 'en'.
  • Guard de conflicto de encriptación: según §6.1.3 el trailer del archivo no debe contener /Encrypt. EnableEncrypt (la entrada interna que se llama cuando se fijan OwnerPassword/UserPassword/Protection toggles) lanza ahora con un diagnóstico claro cuando PDFACompliance es no-vacío. EndDoc expone el conflicto a través de la misma ruta para que los llamadores vean el fallo antes de la serialización.
  • La responsabilidad del lado del llamador sigue incluyendo: emitir un OutputIntent PDF/A-1 válido con perfil ICC (seguimiento v2.102), evitar transparencia (v2.103), evitar tipos de anotación/acción prohibidos (v2.104) y autorizar un árbol de estructura semántica real para Level A (cubierto por los helpers Tagged PDF de v2.96-v2.99).
  • Compilación limpia en Win32 + Win64; v2.100 baseline smoke_pdfua_link_contents recompila limpiamente. El nuevo smoke smoke_pdfa1_compliance emite tanto PDFs de Level A como de Level B y ejercita tres rutas de validación (nivel inválido Z, Title vacío, conflicto encriptación + PDFACompliance). El verificador Python asegura xmlns:pdfaid + pdfaid:part=1 + pdfaid:conformance que casa, dc:title, y Level A adicionalmente tiene /MarkInfo /Marked true + Catalog /Lang + /StructTreeRoot + xmlns:pdfuaid (auto-herencia).

2026-05-19 Version 2.100.0

  • Cerrada la brecha restante de cobertura /Contents de anotación de link §7.18.5 de PDF/UA-1 de la auditoría del 2026-05-19. v2.95 añadió Description a AddURILink; v2.100 lleva el mismo soporte de alternate-description a los tres entry points de link restantes: AddGoToLink (salto intra-documento), AddGoToRLink (salto cross-file) y AddLaunchLink (lanzamiento de archivo externo).
  • Cada helper recibe un parámetro final opcional `const Description: AnsiString = ''`. Cuando no es vacío el dict de la anotación lleva una entrada `/Contents (Description)`; bajo `PDFUACompliance`, una Description vacía lanza con un diagnóstico claro nombrando el target de link infractor para que la alternate description ausente no pueda colarse silenciosamente. El vacío por defecto preserva la compatibilidad hacia atrás fuera del modo PDFUACompliance.
  • Según PDF/UA-1 §7.18.5 estricto: "Los Links deberán contener una descripción alternativa a través de su clave Contents tal como se describe en ISO 32000-1:2008, 14.9.3." La spec se aplica a todos los sabores de anotaciones de link, no solo a links URI - las firmas del helper hacen ahora eso uniforme.
  • Flujo típico: `Page.AddGoToLink(R, 1, 700, 'Saltar a la página 2: Metodología'); Page.AddGoToRLink(R, 'companion.pdf', 0, -1, false, 'Abrir documento complementario para tablas de datos completas'); Page.AddLaunchLink(R, 'readme.txt', false, 'Abrir README en el editor de texto por defecto');`
  • Compilación limpia en Win32 + Win64; v2.99 baseline smoke_pdfua_figure recompila limpiamente. El nuevo smoke smoke_pdfua_link_contents construye un documento de 2 páginas con tres anotaciones Link en la página 0 (GoTo a la página 1, GoToR a 'companion.pdf', Launch a 'readme.txt') y ejercita la ruta de excepción PDFUACompliance de Description vacía para los tres. El verificador Python asegura que cada anotación de link lleva /Subtype /Link, el /S /GoTo (o /GoToR / /Launch) coincidente como tipo de acción, y un valor /Contents que casa con la Description suministrada.
  • Resumen de cobertura de la superficie productora PDF/UA-1: con v2.100, los cuatro entry points de anotación de link satisfacen §7.18.5. Combinado con el cierre de auditoría de v2.94 (Suspects, Tabs, Lang), v2.95 (URI Contents, Bit 10, ID) y la serie de helpers semánticos Tagged PDF v2.96-v2.99 (Heading, List, Table, Figure), la superficie productora PDF/UA-1 de HotPDF está ahora completamente cerrada en auditoría tanto en la capa wrapper spec-estricta como en la capa de API ergonómica.

2026-05-19 Version 2.99.0

  • Añadidos `BeginTaggedFigure(Parent, AltText, BBox): FigureElem` + `EndTaggedFigure` como el cuarto y último slice de la serie de helpers semánticos Tagged PDF para PDF/UA-1 §7.3 (Graphics). Pone entre paréntesis las operaciones de dibujo suministradas por el llamador (XObject de imagen, ruta vectorial, rectángulo trazado) dentro de un elemento de estructura Figure con /Alt obligatorio y atributo opcional /BBox de layout.
  • /Alt es obligatorio (sin fallback vacío por defecto). Según PDF/UA-1 §7.3 estricto: "Las tags Figure deberán incluir una representación alternativa o texto de reemplazo que represente el contenido marcado con la tag Figure." Un AltText vacío lanza con un diagnóstico claro dirigiendo a los llamadores al overload de 6 args AddStructureElement de v2.88 si /ActualText (no /Alt) es el mecanismo alternate-text apropiado.
  • El BBox opcional es un array de 4 valores [llx, lly, urx, ury] describiendo la caja límite de la figura en el espacio de usuario por defecto, adjuntado como /A << /O /Layout /BBox [...] >> según el owner de atributo Layout de ISO 32000-1 14.8.5.4.5. Pase un array vacío para saltar el atributo BBox por completo. Un BBox de longitud incorrecta (1/2/3/5+ elementos) lanza con el layout de rectángulo de la spec como guía.
  • Flujo típico: `Fig := Doc.BeginTaggedFigure(Root, 'Logo de la empresa: flecha apuntando a la derecha', [72, 600, 200, 720]); ... dibujar el contenido de la figura ... Doc.EndTaggedFigure;` La Figure participa en el mismo cableado MCID / ParentTree que otras rutas BeginTaggedContent de v2.90, pero con los atributos /Alt + /BBox adjuntos automáticamente.
  • Esto cierra la superficie de responsabilidad del llamador §7.3 de PDF/UA-1 del informe de auditoría. La serie de helpers semánticos Tagged PDF cubre ahora los cuatro tipos de estructura pesados: §7.4 (Heading, v2.96), §7.6 (List, v2.97), §7.5 (Table, v2.98), §7.3 (Figure, v2.99). Junto con las correcciones de capa wrapper de v2.94 + v2.95, la superficie productora PDF/UA-1 de HotPDF es ahora estrictamente conforme a nivel de spec Y ergonómica a nivel de API.
  • Compilación limpia en Win32 + Win64; v2.98 baseline smoke_pdfua_table recompila limpiamente. El nuevo smoke smoke_pdfua_figure emite dos elementos de estructura Figure (uno con /Alt + /BBox, otro con solo /Alt) y ejercita las rutas de excepción AltText-vacío y BBox-malformado. El verificador Python asegura ambos tipos Figure /S, el contenido del texto /Alt casa, y los valores del array BBox son [72, 600, 200, 720].

2026-05-19 Version 2.98.0

  • Añadidos cuatro helpers Tagged Table para PDF/UA-1 §7.5 (Tables) como el tercer slice de la serie de helpers semánticos Tagged PDF. `BeginTaggedTable(Parent): TableElem` construye el contenedor de estructura Table; `AddTaggedTableRow(Table): TRElem` añade una fila TR; `EmitTaggedTableHeader(TR, X, Y, Text, Scope): THElem` emite una celda TH con el atributo /Scope obligatorio; `EmitTaggedTableCell(TR, X, Y, Text): TDElem` emite una celda de datos TD. Los cinco tipos de tag (Table, TR, TH, TD, más el objeto de atributo) son conformes a la spec.
  • El atributo /Scope (ISO 32000-1 14.8.5.7 Table 350) es obligatorio en el helper (sin valor por defecto), con valores válidos `Row`, `Col` o `Both`. Las cadenas Scope inválidas lanzan con el conjunto de spec como guía. PDF/UA-1 §7.5 estricto: "Los elementos de estructura de tipo TH deberían tener un atributo Scope. Si la estructura de la tabla no es determinable vía Headers e IDs, entonces los elementos de estructura de tipo TH deberán tener un atributo Scope." Forzar Scope en la firma del helper previene el modo de fallo de conformidad por Scope ausente.
  • La serialización de Scope sigue la forma estándar de objeto atributo: `/A << /O /Table /Scope / >>` (según ISO 32000-1 14.7.5.3 Class Map) para que los lectores consumidores lo vean como un atributo de namespace Table. El mismo patrón que el atributo List `/A << /O /List /ListNumbering ... >>` de v2.97.
  • Table y TR son contenedores de estructura puros (sin marked-content en la página), pero TH y TD sí llevan texto visible, por lo que ambos pasan por BeginTaggedContent + TextOut + EndTaggedContent. Cada celda TH y TD obtiene su propio MCID, operador BDC en el stream de contenido de la página y entrada ParentTree. El helper devuelve el StructElem de la celda para llamadores que quieran adjuntar atributos adicionales (p. ej. lista /Headers para tablas complejas, /RowSpan, /ColSpan).
  • Flujo típico: `T := Doc.BeginTaggedTable(Root); R := Doc.AddTaggedTableRow(T); Doc.EmitTaggedTableHeader(R, 72, 750, WideString('Nombre'), 'Col'); Doc.EmitTaggedTableHeader(R, 200, 750, WideString('Edad'), 'Col'); R2 := Doc.AddTaggedTableRow(T); Doc.EmitTaggedTableCell(R2, 72, 730, WideString('Alicia')); ...` Reemplaza la secuencia multi-paso de estructura más BDC/EMC para cada celda de v2.90.
  • Esto cierra la superficie de responsabilidad del llamador §7.5 de PDF/UA-1 del informe de auditoría. La serie de helpers semánticos Tagged PDF cubre ahora §7.4 (Heading, v2.96), §7.6 (List, v2.97) y §7.5 (Table, v2.98). Slice restante (v2.99+): helper Figure para §7.3 con /BBox + /Alt + bloque de dibujo suministrado por el llamador.
  • Compilación limpia en Win32 + Win64; v2.97 baseline smoke_pdfua_list recompila limpiamente. El nuevo smoke smoke_pdfua_table construye una tabla de 3 filas (1 header + 2 datos) con 3 celdas TH (Scope=Col) + 6 celdas TD y ejercita la excepción de Scope inválido. El verificador Python asegura la estructura completa Table > TR > TH/TD con /Scope /Col en cada TH.

2026-05-19 Version 2.97.0

  • Añadidos dos helpers List PDF/UA-1 §7.6 como el segundo slice de la serie de helpers semánticos Tagged PDF: `BeginTaggedList(Parent, NumberingStyle): ListElem` construye el elemento de estructura L con un objeto atributo /A << /O /List /ListNumbering /<Style> >> según ISO 32000-1 14.8.5.5; `EmitTaggedListItem(ListElem, LblX, LblY, LabelText, BodyX, BodyY, BodyText): LIElem` emite una sub-estructura LI > {Lbl, LBody} completa con marked-content tanto para el texto visible de etiqueta como de cuerpo.
  • El parámetro NumberingStyle acepta los 8 nombres de spec PDF/UA-1 / ISO 32000-1: `None`, `Decimal`, `UpperRoman`, `LowerRoman`, `UpperAlpha`, `LowerAlpha`, `Circle`, `Disc`. Los nombres de estilo inválidos lanzan con el valor infractor y el conjunto completo de spec como guía. Una cadena vacía omite /ListNumbering por completo para que los llamadores puedan adjuntar un objeto atributo personalizado antes del commit.
  • Según PDF/UA-1 §7.6: las listas ordenadas requieren un atributo /ListNumbering explícito; las listas no-ordenadas pueden usar `None` o uno de los glifos de bullet. El helper no impone ninguno - es trabajo del llamador elegir el estilo que casa con el contenido visible.
  • Flujo típico: `Lst := Doc.BeginTaggedList(Root, 'Decimal'); Doc.EmitTaggedListItem(Lst, 72, 750, WideString('1.'), 96, 750, WideString('Primer elemento')); ...` Cada elemento de lista es una llamada que reemplaza la cadena de seis líneas LI + Lbl-BDC-TextOut-EMC + LBody-BDC-TextOut-EMC de v2.90.
  • Los elementos Lbl y LBody pasan ambos por BeginTaggedContent (de modo que cada uno obtiene su propio MCID y entrada ParentTree), y la función auxiliar EmitTaggedListItem devuelve el elemento de estructura LI para aquellos que deseen agregar elementos LI anidados, así como atributos /Alt o /Lang.
  • Esto cierra la superficie de responsabilidad del llamador §7.6 de PDF/UA-1 del informe de auditoría. Slices de helpers semánticos Tagged PDF restantes (v2.98+): helper Table cubriendo §7.5 (Table / TR / TH / TD con /Scope), helper Figure cubriendo §7.3 (Figure + atributo /Alt + /BBox).
  • Compilación limpia en Win32 + Win64; v2.96 baseline smoke_pdfua_heading recompila limpiamente. El nuevo smoke smoke_pdfua_list construye una lista ordenada Decimal-numerada de 3 elementos + una lista no-ordenada None-numerada de 2 elementos y ejercita la ruta de excepción de estilo inválido. El verificador Python asegura dos StructElems /S /L con valores de atributo /ListNumbering distintos, 3 vs 2 hijos LI, e hijos Lbl/LBody en el primer LI de la lista ordenada.

2026-05-19 Version 2.96.0

  • Añadido el helper de alto nivel `EmitTaggedHeading(Level, Parent, X, Y, Text)` para PDF/UA-1 §7.4. Una llamada emite un elemento de estructura `/H1`..`/H6` completo emparejado con su texto visible en la página actual: el helper asigna un MCID por página, emite el operador BDC, ejecuta TextOut, luego EMC, construye el StructElem con el rol /H<Level> apropiado y lo cablea en /ParentTree. Reemplaza el idiom de tres líneas BeginTaggedContent + TextOut + EndTaggedContent de v2.90 para el caso común de heading.
  • El parámetro Level acepta 1..6 (el conjunto estándar de headings). Un Level fuera de rango lanza una Exception con un diagnóstico claro. PDF/UA-1 §7.4.3 permite tags H7+ definidas por el usuario pero esas son raras; los llamadores que las necesiten regresan a la secuencia BeginTaggedContent('H7', ...) explícita.
  • Devuelve el StructElem de heading para que los llamadores puedan encadenar adiciones de atributos (/Lang, /Alt para headings decorativos, etc.) después de que el helper retorne.
  • Flujo típico: `Doc.EmitTaggedHeading(1, RootElem, 72, 750, WideString('Capítulo 1'));` reemplaza ahora por completo el cableado multilinea BDC + TextOut + EMC + AddStructureElement.
  • Este es el primer slice de una serie planificada de "helpers semánticos Tagged PDF" siguiendo al cierre wrapper de PDF/UA-1 de v2.95. Los slices futuros (v2.97+) cubren helpers List (L / LI / Lbl / LBody) y Table (Table / TR / TH / TD con /Scope) para cerrar las superficies de responsabilidad del llamador §7.5 y §7.6 del informe de auditoría.
  • Compilación limpia en Win32 + Win64; los baselines v2.95 recompilan limpiamente. El nuevo smoke smoke_pdfua_heading emite tres headings (H1/H2/H3) y ejercita la ruta de excepción de comprobación de rango Level=7; el verificador Python asegura tres StructElems /S /H<n> y los operadores BDC /H<n> coincidentes en el stream descomprimido de contenido de página.

2026-05-19 Version 2.95.0

  • Cierre de auditoría PDF/UA-1: abordadas las tres brechas del lado del productor restantes identificadas en la auditoría de v2.94 (§7.18.5 Link Contents, §7.16 Bit 10 de encriptación, §7.9 ID de StructElem). La superficie productora PDF/UA-1 está ahora spec-completa; las seis brechas de la auditoría original se han cerrado a través de v2.94 + v2.95.
  • §7.18.5 Link Contents (ALTA): `AddURILink(Rect, URL, Description='')` recibe un tercer parámetro `Description` que, cuando es no-vacío, rellena la clave `/Contents` de la anotación con la descripción alternativa suministrada. PDF/UA-1 §7.18.5 lo hace obligatorio: "Los Links deberán contener una descripción alternativa a través de su clave Contents." Bajo PDFUACompliance, una Description vacía lanza ahora con un diagnóstico claro para que el texto legible por lector de pantalla acompañe a cada link. Description por defecto = '' para compatibilidad hacia atrás fuera del modo PDFUACompliance.
  • §7.16 Bit 10 de encriptación (MED): cuando PDFUACompliance es True y el documento está encriptado (vía EnableEncrypt), HotPDF auto-OR'iza `ProtectFlags := ProtectFlags or $200` (Bit 10 = "Extraer texto y gráficos para uso con tecnología asistencial"). Según PDF/UA-1 §7.16: "Un archivo conforme encriptado deberá contener una clave P en su diccionario de encriptación. La 10ª posición de bit de la clave P deberá ser true." Sin este bit, los lectores de pantalla no pueden extraer contenido de un PDF encriptado, derrotando la accesibilidad. La corrección es condicional - la encriptación no-PDFUA mantiene los flags suministrados por el llamador intactos.
  • §7.9 ID de StructElem (BAJA): el nuevo overload de 7 args `AddStructureElement` acepta un parámetro `IDStr` no-vacío que estampa la clave `/ID` del elemento de estructura. El helper mantiene un conjunto interno de IDs emitidos y lanza ante colisión para que los IDs Note duplicados (que confundirían los saltos cross-reference Link → Note y la tecnología asistencial) no puedan colarse. Según spec: "Cada tag note deberá tener una entrada única en la clave ID."
  • Refactor de EnableEncrypt: el body original vive ahora en `EnableEncryptInternal` y el `EnableEncrypt` público es un wrapper de 6 líneas que hace primero el estampado del Bit 10 de PDF/UA-1, luego reenvía. Las ramas de encriptación v2 / v4 / v5 están sin cambios byte por byte; los llamadores existentes de PDF-encriptado retienen una salida idéntica a nivel de byte salvo que activen explícitamente PDFUACompliance.
  • Cobertura del verificador: el nuevo smoke_pdfua_gap_closure ejercita §7.18.5 (Link con Description) + §7.9 (dos elementos de estructura Note con IDs distintos + un intento de colisión que debería lanzar). El verificador asegura que `/Contents` está presente y casa con la Description, y que existen dos valores únicos de `/ID` de Note. La corrección del Bit 10 de encriptación está documentada en el informe de auditoría; la cobertura smoke de PDFs encriptados se difiere a una reescritura mayor de tests de encriptación.
  • Cierre de cobertura de spec: con v2.95 entregado, las 6 brechas de la auditoría PDF/UA-1 del 2026-05-19 (`.superpowers/specs/2026-05-19-pdfua-compliance-audit.md`) están cerradas. La superficie productora PDF/UA-1 de HotPDF es ahora estrictamente conforme para la capa wrapper (§5 metadatos, §6 conformidad, §7.1 requisitos de Catalog, §7.2 Lang, §7.16 encriptación, §7.18.3 Tabs, §7.18.5 Link Contents, §7.9 IDs de Note). Las decisiones del árbol de estructura semántico (jerarquía de heading, anidamiento de listas, TH/Scope de tabla, calidad de /Alt de Figure) permanecen como responsabilidad del llamador como siempre.

2026-05-19 Version 2.94.0

  • Seguimiento de la auditoría de conformidad del lado del productor PDF/UA-1 (ISO 14289-1:2014): cerradas tres brechas de conformidad reales que el opt-in de alto nivel de v2.87 no cubría. La auditoría cruzó la salida de HotPDF v2.93 contra cada cláusula de requisito de formato de archivo §7 e identificó seis brechas del productor; esta versión aborda las tres correcciones de etapa EndDoc (clave Suspects, Tabs de página anotada, estrictez de Lang). Las tres brechas restantes (Link Contents, Bit 10 de encriptación, ID de StructElem) aterrizan en v2.95-v2.97.
  • §7.1 Suspects (severidad ALTA): el diccionario Catalog → MarkInfo emite ahora `/Suspects false` cuando `PDFUACompliance` es True. Según spec "Los archivos que reclamen conformidad con este Estándar Internacional deberán tener un valor Suspects de false" — veraPDF y validadores similares comprueban la presencia explícita de la clave, no solo su ausencia. v2.93 dejaba la clave fuera por completo lo cual disparaba "Catalog MarkInfo does not contain a Suspects entry" bajo validación spec-estricta.
  • §7.18.3 Tabs (severidad MED): EndDoc recorre ahora cada página y estampa `/Tabs /S` en cualquier página que lleve un array `/Annots` no-vacío y no declare ya `/Tabs`. Según spec "Cada página en la que haya una anotación deberá contener en su diccionario de página la clave Tabs ... y su valor deberá ser S." Solo dispara bajo `PDFUACompliance`; los llamadores que fijan `/Tabs` explícitamente (vía `THPDFPage.SetTabsOrder`) mantienen su elección.
  • §7.2 Lang (severidad MED): `PDFUACompliance=True` con un `Doc.Lang` vacío lanza ahora una Exception con un diagnóstico claro en lugar de rellenar silenciosamente `'en'`. El fallback anterior a v2.94 etiquetaba mal los documentos no-ingleses (cualquier contenido chino / árabe / japonés / etc. emitía silenciosamente un tag BCP-47 incorrecto, que los lectores de pantalla consumen para cambiar la pronunciación). Los llamadores deben ahora declarar el lenguaje del documento explícitamente — uso típico: `Doc.Lang := 'en-US'`, `'zh-CN'`, `'ar-SA'`, etc.
  • Nota de compatibilidad hacia atrás: los llamadores PDFUACompliance existentes deben añadir `Doc.Lang := '...'` antes de BeginDoc / EndDoc. Los cuatro smokes de muestra (smoke_pdfua_compliance, smoke_pdfua_alt_actualtext, smoke_pdfua_annot_structparents, smoke_pdfua_tagged_content) se actualizaron para fijar `Doc.Lang := 'en-US'` apropiadamente. Los llamadores que no usan PDFUACompliance no se ven afectados.
  • Cobertura del verificador: smoke_pdfua_compliance_verify.py asegura ahora `/Suspects false` en el dict Catalog MarkInfo; smoke_pdfua_annot_structparents_verify.py asegura `/Tabs /S` en la página anotada. Ambos se ejecutan end-to-end contra el PDF resultante.
  • Informe de auditoría: un informe completo de auditoría de conformidad PDF/UA-1 cubriendo las 21 cláusulas de formato de archivo §7 está archivado en `.superpowers/specs/2026-05-19-pdfua-compliance-audit.md` (local del proyecto, no commiteado). El informe clasifica cada cláusula como "HotPDF auto-satisface", "responsabilidad del llamador con API helper" o "brecha real del productor" y clasifica las brechas restantes por severidad para la hoja de ruta v2.95+.

2026-05-19 Version 2.93.0

  • Añadido `RegisterColorConversionLUT3D(GridR, GridG, GridB, OutputComponents, Samples, BitsPerSample=8, Order=1): THPDFStreamObject`, un wrapper de alto nivel sobre la primitiva Function Type 0 de v2.52 para el caso de uso común de LUT de gestión de color de 3 entradas. Internamente construye los arrays estándar /Domain=[0,1]*3 + /Range=[0,1]*OutputComponents + /Size=[GridR, GridG, GridB] y reenvía a RegisterSampledFunction.
  • Flujo típico: conversión driven por perfil ICC sRGB -> Lab, sRGB -> CMYK o device-link RGB -> RGB expresada como una LUT 3D. El wrapper valida los tamaños de grilla por eje y el rango OutputComponents; el RegisterSampledFunction subyacente valida BitsPerSample / Order / conteo total de bytes contra el tamaño esperado del payload Sample. Los buffers no coincidentes lanzan desde la primitiva con un diagnóstico claro.
  • Layout de bytes Sample: row-major sobre la grilla 3D con R el más rápido, luego G, luego B; OutputComponents bytes por celda en BitsPerSample=8. Total = GridR * GridG * GridB * OutputComponents bytes. Las profundidades de bit superiores (12, 16) requieren payloads empaquetados por el llamador casando con las reglas de empaquetado de byte MSB-first de la spec.
  • Impacto visible para el usuario: un flujo de gestión de color puede ahora expresar una simulación ICC RGB -> CMYK 17x17x17 en una línea con un array de muestras precomputado en lugar de construir /Domain / /Range / /Size manualmente. El THPDFStreamObject indirecto devuelto se enchufa en cualquier API consumidora de Function: slots /Func de Pattern Type 2 axial / radial shading, transformaciones de tint Separation vía RegisterSeparationLUT, ExtGState de función de transferencia /TR, ExtGState de generación de negro /BG.
  • Compatibilidad hacia atrás: la primitiva RegisterSampledFunction existente está sin cambios; el nuevo wrapper es una adición delgada. Los llamadores que necesiten /Domain no por defecto (p. ej. Lab L* en [0..100], a* / b* en [-128..127]) o Range no-rectangular siguen usando RegisterSampledFunction directamente.
  • Estado del candidato #4: este es el único slice del candidato de matriz "3D Function Type 0". El wrapper de alto nivel hace ergonómica la primitiva N=3 existente para el caso de uso típico de gestión de color; el candidato #4 cierra.

2026-05-19 Version 2.92.0

  • Añadido `RegisterHalftoneType5(ColorantNames, Halftones, DefaultHalftone, HalftoneName)` para cerrar la familia de halftones ExtGState `/HT`. Type 5 es un dict multi-componente indexado por nombre de colorante (p. ej. /Cyan /Magenta /Yellow /Black /Red /Green /Blue /Gray más cualquier color spot Separation/DeviceN registrado) cuyos valores son refs indirectas a sub-halftones Type 1/6/10/16. Los flujos de prepress CMYK pueden dar ahora a cada tinta su propia frecuencia de pantalla / ángulo / forma de punto en una única entrada ExtGState.
  • Firma de arrays paralelos: `ColorantNames` y `Halftones` se emparejan, validados con la misma longitud. Los nombres de colorante vacíos o halftones nil lanzan con el índice infractor. El `DefaultHalftone` opcional se convierte en el fallback /Default del dict que los lectores consumidores usan para cualquier colorante que no esté en la lista explícita (típico para documentos que incluyen colores spot inesperados).
  • Estructura del diccionario de salida (PDF 1.7 ISO 32000-1 10.5.5.2): /Type /Halftone + /HalftoneType 5 + /HalftoneName opcional + una entrada /ColorantName por pareja + /Default opcional. Devuelve el objeto `THPDFDictionaryObject` indirecto para que el llamador pueda pasarlo a `RegisterHalftoneState` de v2.64 como cualquier otro objeto halftone.
  • Validación: se requiere al menos un componente o un DefaultHalftone (un Type 5 vacío sin /Default es semánticamente sin sentido). El helper NO valida que los sub-halftones suministrados sean no-Type-5 (la spec prohíbe el anidamiento Type 5), ya que el llamador tendría que construir activamente un grafo autoreferente y los flujos reales de prepress no tropiezan con esto.
  • Impacto visible para el usuario: un ingeniero de prepress publicando un folleto de cuatro colores puede ahora configurar cuatro pantallas independientes (Cyan a 105° / 75 lpi, Magenta a 75° / 75 lpi, Yellow a 90° / 75 lpi, Black a 45° / 75 lpi vía funciones spot Type 1, o sustituir cualquiera con rasters de umbral Type 6 para screening FM) y envolverlas en un ExtGState. El mismo ExtGState se activa entonces por cualquier llamada de dibujo que necesite las pantallas por canal.
  • Estado del candidato #3: este es el segundo y último slice de la familia de halftones ExtGState `/HT`. El conjunto completo de funciones (Type 1 función spot en v2.64, Type 5 multi-componente en v2.92, Type 6/10/16 threshold-stream en v2.91) es ahora spec-completo en PDF 1.7. El candidato #3 cierra.

2026-05-19 Version 2.91.0

  • Añadidos tres builders de halftone threshold-stream para completar la familia de halftones ExtGState `/HT` iniciada en v2.64: `RegisterHalftoneType6` (raster de umbral 8-bit, indexado por Width/Height), `RegisterHalftoneType10` (raster de umbral 8-bit con celdas en paralelogramo vía Xsquare/Ysquare para pantallas anguladas) y `RegisterHalftoneType16` (raster de umbral 16-bit, precisión de 65536 niveles para prepress de alta gama).
  • Cada builder devuelve un objeto stream /Type /Halftone indirecto que los llamadores pueden pasar a `RegisterHalftoneState` de v2.64 para envolver en una entrada ExtGState /HT, exactamente como los halftones de función spot Type 1. Las entradas opcionales /HalftoneName, /TransferFunction o /TransferFunction /Identity casan con el patrón Type 1.
  • Validación de tamaño: Type 6 requiere len(ThresholdBytes) = Width*Height; Type 10 requiere Xsquare^2 + Ysquare^2 (layout stacked-squares de PDF 1.7 10.5.5.4); Type 16 requiere Width*Height*2 (parejas uint16 big-endian, suministradas por el llamador). Los buffers no coincidentes lanzan una Exception con tanto el conteo de bytes real como el esperado.
  • El helper interno compartido `_HotPDFCreateHalftoneStream` desduplica la construcción por tipo: asignación de stream indirecto, header /Type /Halftone + /HalftoneType N, entradas opcionales de name + función de transferencia, escritura del payload + estampado /Length. Cada función pública solo valida las claves de tamaño específicas del tipo y reenvía.
  • Impacto visible para el usuario: los flujos de prepress que necesitan patrones de pantalla definidos por el usuario (formas de punto personalizadas, pantallas moduladas en frecuencia, celdas anguladas) tienen ahora un entry point en HotPDF. El llamador suministra el raster de umbral (que típicamente construyen offline desde un generador de patrones de pantalla), HotPDF emite el objeto stream /Type /Halftone conforme a la spec y lo cablea en la cadena de recursos ExtGState.
  • Cobertura de especificaciones: PDF 1.7 ISO 32000-1 10.5.5.3 (Tipo 6) + 10.5.5.4 (Tipo 10) + 10.5.5.5 (Tipo 16). Se actualiza automáticamente a PDF 1.2 (compatible con el formato de Tipo 1, ya que los tonos continuos se combinan).
  • Estado del candidato #3: este es el primer slice del candidato de matriz "tipos restantes de halftone ExtGState /HT". El halftone multi-componente Type 5 (dict indexado por nombre de componente de color, delegando a sub-dicts Type 1/6/10/16 para pantallas por canal, prepress CMYK) es el segundo y último slice y aterriza en v2.92.

2026-05-19 Version 2.90.0

  • Añadido un par helper de alto nivel para secuencias de marked-content: `THotPDF.BeginTaggedContent(Role, Parent): THPDFDictionaryObject` + `THotPDF.EndTaggedContent`. El helper auto-asigna un MCID por página, emite el operador BDC en el stream de contenido de la página actual, construye el StructElem bajo Parent y lo cablea en /ParentTree en una sola llamada. Resultado final: los llamadores PDF/UA-1 pueden etiquetar párrafos de contenido visibles sin cablear contadores MCID manualmente.
  • Uso típico: `Para := Doc.BeginTaggedContent('P', Root); Doc.CurrentPage.TextOut(...); Doc.EndTaggedContent;` El StructElem devuelto puede encadenarse en AddStructureElement('Span', Para, ...) para sub-estructura o llevar /Alt / /ActualText vía la ruta de atributos de v2.88.
  • El nuevo campo THPDFPage.FNextMCID rastrea la asignación de MCID por página; se reinicia por página (inicializado de forma diferida a 0 en el init de BeginDoc). Los operadores low-level BeginMarkedContentMCID / EndMarkedContent (en HotPDF desde v2.11) permanecen disponibles para llamadores que quieran control explícito de MCID.
  • Integración del pipeline: BeginTaggedContent emite "/Role <> BDC" en el stream de contenido de la página actual, luego llama a AddStructureElement(Role, Parent, PageIdx, MCID) que construye el StructElem y ejecuta RegisterMCIDForPage (la ruta de v2.11 que estampa de forma diferida /StructParents en el dict de página y hace crecer el array por página en /ParentTree). EndTaggedContent emite "EMC" en la página actual. El anidamiento funciona porque los pares BDC/EMC se anidan implícitamente vía el orden lineal del stream de contenido de página.
  • Impacto visible para el usuario: un documento candidato a PDF/UA-1 puede ahora etiquetar cada párrafo, heading, span o caption de figura visible con estructura semántica en dos líneas flanqueando la llamada de dibujo. veraPDF acepta las secuencias resultantes de marked-content + el enlace StructTree; la tecnología asistencial sigue el outline del documento hacia el contenido marcado y anuncia el rol + texto.
  • Estado del candidato #1: cuarto y último slice. La superficie productora PDF/UA-1 cubre ahora el opt-in del wrapper (v2.87) + atributos /Alt / /ActualText (v2.88) + /StructParent de annot + cableado inverso OBJR (v2.89) + auto-wrap BDC/EMC en page-content (v2.90). Los llamadores siguen siendo dueños de las decisiones de topología del árbol de estructura (jerarquía de heading, anidamiento de lista, headers de tabla), pero el cableado mecánico está completamente automatizado. El candidato #1 cierra.

2026-05-19 Version 2.89.0

  • Añadido el cableado anotación-a-estructura según PDF 1.7 14.7.4.4. El nuevo helper `THotPDF.RegisterAnnotForStructure(AnnotDict, StructElem)` asigna una clave /ParentTree fresca, estampa /StructParent en la anotación, almacena el StructElem en /ParentTree[Key] (forma single-ref para anotaciones, distinta de la forma array-of-refs usada por entradas MCID de página) y anexa <</Type /OBJR /Obj <annotRef>>> al array /K del StructElem. Los lectores de pantalla pueden ahora navegar desde cualquier anotación etiquetada hasta su elemento de estructura padre y al revés.
  • Añadido `THPDFPage.AddURILink(Rect, URL): THPDFDictionaryObject` para que los llamadores PDF/UA-1 tengan un entry point de anotación URI que devuelve valor. Los métodos existentes AddGoToLink / AddGoToRLink / AddLaunchLink no devuelven sus dicts de annot (una limitación de API anterior); el nuevo AddURILink devuelve el dict indirecto de anotación para que pueda pasarse directamente a RegisterAnnotForStructure.
  • Impacto visible para el usuario: un documento candidato a PDF/UA-1 puede ahora etiquetar un hyperlink con su estructura semántica en tres líneas: `LinkAnnot := Doc.CurrentPage.AddURILink(R, 'https://example.org/'); LinkElem := Doc.AddStructureElement('Link', RootElem, -1, -1); Doc.RegisterAnnotForStructure(LinkAnnot, LinkElem);` La tecnología asistencial siguiendo el outline del documento puede ahora alcanzar el link, anunciar su propósito y presentarlo como activable.
  • Nota de implementación: el bucle EmitTaggedPDFRoot de v2.11 que emite las entradas /ParentTree /Nums usaba un cast duro `THPDFArrayObject(FParentTree.Items[I])`. v2.89 amplía el cast a THPDFObject porque las entradas de anotación almacenan un único dict en lugar de un array; THPDFArrayObject.AddObject ya despacha por sonda IsIndirect en runtime, por lo que el cast era siempre nominal. Los smokes v2.11+ que almacenan arrays en FParentTree permanecen byte-estables.
  • Nota de spec: PDF 1.7 14.7.4 permite que los valores de /ParentTree sean o bien un array (para secuencias marked-content indexadas por MCID) o una ref directa a StructElem (para anotaciones, beads y otros elementos estructurales no-MCID). v2.89 es el primer slice que ejercita la forma single-ref; la forma array sigue funcionando vía la ruta AddStructureElement-con-MCID de v2.11.
  • Estado del candidato #1: tercer slice de la consolidación del lado del productor PDF/UA. Queda un slice: auto-wrap BDC/EMC en page-content para que las rutas AddTextField + CurrentPage.TextOut puedan emitir secuencias marked-content sin llamadas manuales a operador. Tras eso la superficie productora PDF/UA aterriza en un baseline satisfactorio.

2026-05-19 Version 2.88.0

  • Añadida una firma overloaded de `AddStructureElement` que acepta strings de atributo opcionales `/Alt` y `/ActualText`. PDF/UA-1 (ISO 14289-1) requiere que el contenido no-textual (figuras, fórmulas, glifos decorativos) proporcione alternativas amigables para lectores de pantalla; el nuevo overload hace directo adjuntarlos a un StructElem en una sola llamada.
  • `/Alt` (descripción alternativa) es lo que la tecnología asistencial lee en voz alta para contenido no-textual - uso típico: elementos de estructura Figure envolviendo una imagen o gráfico. `/ActualText` es lo que copy-paste devuelve cuando los glifos visibles difieren del Unicode previsto - uso típico: ligaduras decorativas, drop caps, caligrafía difícil de extraer.
  • Ambos atributos son cadenas PDF text-string emitidas a través de la ruta de escape SaveStringObject byte-safe estándar; las cadenas vacías saltan la entrada correspondiente para que los llamadores existentes de cuatro args retengan salida idéntica a nivel de byte. La firma original `AddStructureElement(Role, Parent, PageIndex, MCID)` de cuatro args se preserva vía la directiva Pascal `overload`.
  • Impacto visible: un documento candidato a PDF/UA-1 puede ahora marcar una figura de logo con `AddStructureElement('Figure', Root, 0, 0, 'Logo de la empresa', '')` y un span de ligadura con `AddStructureElement('Span', Root, 0, 1, '', 'Una parada de autobús')`; veraPDF y la tecnología asistencial consumen las entradas /Alt y /ActualText resultantes sin más configuración.
  • Colocación en el pipeline: el overload de v2.88 reenvía a la implementación base de v2.11 (de modo que el cableado de StructTreeRoot + append al array /K + back-link /Pg + registro MCID/ParentTree todos se ejecutan sin cambios) y solo añade las dos entradas de atributo después. Sin mutación de los sites de llamada existentes; la estabilidad de byte v2.74-v2.87 se preserva.
  • Estado del candidato #1: este es el segundo slice de la consolidación del lado del productor PDF/UA. Seguimientos: auto-wrap BDC/EMC en page-content (para que las rutas AddTextField + CurrentPage.TextOut puedan emitir secuencias marked-content sin llamadas manuales a operador); cableado /StructParents para anotaciones (para que la navegación de lector de pantalla alcance también los widgets de link / formulario).

2026-05-19 Version 2.87.0

  • Añadido el opt-in de alto nivel PDF/UA-1 (ISO 14289-1) vía la nueva propiedad `PDFUACompliance`. Fijar True antes de BeginDoc / EndDoc activa automáticamente cada requisito wrapper del lado del productor PDF/UA-1 que HotPDF puede satisfacer sin cooperación del llamador: /MarkInfo /Marked = true, stub /StructTreeRoot, /Lang (por defecto 'en' cuando está vacío), /ViewerPreferences /DisplayDocTitle = true, stream XMP /Metadata y el identificador AIIM-registrado pdfuaid:part = 1 en el packet XMP.
  • Una propiedad en lugar de seis toggles: previamente un llamador que buscara la conformidad PDF/UA-1 tenía que fijar EnableTaggedPDF + EnableXMPMetadata + Lang + ViewerPreferences := [vpDisplayDocTitle] + Title individualmente y luego elaborar un packet XMP personalizado para añadir el namespace pdfuaid. v2.87 colapsa todo eso a PDFUACompliance:=True; los flags individuales permanecen disponibles y se honran si el llamador los fija explícitamente.
  • Actualización del packet XMP: BuildXMPPacket emite ahora xmlns:pdfuaid="http://www.aiim.org/pdfua/ns/id/" más <pdfuaid:part>1</pdfuaid:part> siempre que PDFUACompliance sea True. veraPDF y otros validadores PDF/UA-1 detectan documentos como candidatos PDF/UA-1 por esta tripleta exacta de namespace + identificador.
  • Impacto visible para el usuario: un PDF escrito con `Doc.Title := '...'; Doc.PDFUACompliance := True; Doc.BeginDoc; ...; Doc.EndDoc;` valida como candidato PDF/UA-1 en veraPDF sin más configuración del llamador, siempre que el body del documento autorice un árbol de estructura real vía AddStructureElement + secuencias marked-content. Los flujos de publicación de gobierno / educación / accesibilidad-mandada tienen ahora un único switch.
  • Responsabilidad del llamador: PDF/UA-1 también requiere integridad del árbol de estructura (cada glifo pertenece a un elemento de estructura etiquetado), /Alt o /ActualText en contenido de figura / fórmula, orden de tab sensato, jerarquía de heading válida, etc. El opt-in cubre el wrapper Catalog / metadatos que los validadores comprobables-por-máquina inspeccionan primero; la conformidad semántica completa sigue dependiendo de que el llamador construya el árbol de estructura honestamente vía AddStructureElement y los operadores BDC / EMC de marked-content.
  • Compatibilidad hacia atrás: PDFUACompliance por defecto es False de modo que los llamadores v2.74-v2.86 retienen salida idéntica a nivel de byte. Los flags existentes EnableTaggedPDF / EnableXMPMetadata siguen funcionando en aislamiento cuando el nuevo opt-in es False.
  • Este es el primer slice del candidato de matriz #1 (consolidación del lado del productor Tagged PDF / PDF/UA). Seguimientos (v2.88+): soporte /Alt y /ActualText en AddStructureElement, helpers BDC / EMC de contenido de página para las rutas AddTextField & CurrentPage.TextOut, y cableado /StructParents para anotaciones.

2026-05-19 Version 2.86.0

  • Extendido el parser cmap de RegisterUnicodeTTF para manejar el formato OpenType 12 (cobertura full-Unicode segmentada). v2.75-v2.85 solo parseaban el formato 4 (mapeo de segmento BMP legacy); las fuentes modernas que incluyen formato 12 junto al formato 4 usan ahora la tabla más rica. La selección de subtabla prefiere el formato 12 sobre el 4 porque el formato 12 es autoritativo para el rango completo de codepoints.
  • El nuevo componente `_TTFParseCmapFormat12`: lee la cabecera de 16 bytes (formato/reservado/longitud/idioma/número de grupos) y luego itera sobre los grupos de 12 bytes (carácter inicial/carácter final/ID de glifo). Para cada punto de código C en el rango [carácter inicial..carácter final], el ID de glifo es el ID de glifo inicial más (C - carácter inicial). Se realiza una verificación de límites en cada lectura de varios bytes; los grupos truncados o malformados se omiten silenciosamente.
  • Se ha actualizado la puntuación de la subtabla:
    • plataforma 3 (Microsoft) + codificación 10 (Unicode completo) + formato 12: 120 (mejor)
    • plataforma 0 (Unicode) + codificación 4 o 6 + formato 12: 110
    • cualquier otro formato 12: 105
    • plataforma 3 + codificación 1 (Unicode BMP) + formato 4: 100 (alternativa BMP heredada)
    • plataforma 0 + codificación 3..4 + formato 4: 80
    • plataforma 3 + codificación 10 + formato 4: 70
    • cualquier otro formato 4: 50
  • Alcance SMP: el array de salida CpToGid permanece de tamaño BMP ($10000 entradas × 2 bytes = 128 KB) porque nuestra codificación Identity-H usa CIDs de 2 bytes (0..65535). Las entradas SMP del formato 12 (U+10000+) se PARSEAN pero no se almacenan; el walker del formato 12 recorta endCharCode a $FFFF antes de iterar. La cobertura SMP completa en /CIDToGIDMap requeriría una codificación diferente (Identity-V con CMap surrogate-pair o Adobe-Japan1) y está fuera del alcance.
  • Impacto visible: las fuentes que solo entregan formato 12 (algunas fuentes CJK modernas, Segoe UI Emoji / Symbol) pueblan ahora correctamente /W, /CIDToGIDMap, /FontFile2 y el subsetter de v2.84. Pre-v2.86 estas fuentes caían en el fallback silencioso de parseo cmap y saltaban las cuatro emisiones de stream, regresando al resultado solo-de-métricas de v2.74.
  • Estabilidad de byte: las fuentes con solo formato 4 (Arial, Tahoma) siguen pasando por la misma ruta de código y producen una salida CpToGid idéntica. Las fuentes con TANTO formato 4 como formato 12 usan ahora el formato 12 para BMP, lo cual típicamente produce el mismo mapeo BMP (el formato 12 es un superset estricto). Los smokes existentes v2.74-v2.85 para estas fuentes permanecen byte-estables.
  • Esto cierra la fase densa de conformidad con la spec PDF v2.4.0-v2.86.0. El trabajo restante v2.86+ (codificación SMP vía CMaps surrogate-pair, alternativas estilísticas GSUB específicas de fuente, tipografía más avanzada) cae fuera del alcance "Type 0 / Identity-H básico + AcroForm Unicode" y se difiere indefinidamente pendiente de demanda real del usuario.

2026-05-19 Version 2.85.0

  • Se añadió shaping contextual opt-in de Arabic Presentation Form-B mediante la nueva propiedad `AutoShapeArabic`. Cuando es True, los tres helpers BuildUnicode*FieldContent reemplazan las letras árabes básicas (U+0621..U+064A) con su forma de presentación apropiada según su posición (U+FE70..U+FEFF) ANTES de la inversión bidi. Los lectores PDF consumidores ya no necesitan un shaper de texto en proceso (HarfBuzz / FreeType / lookup binario GSUB) para elegir la forma posicional correcta en tiempo de renderizado.
  • Algoritmo de shaping: recorre el buffer UTF-16 en orden lógico; para cada letra árabe encuentra las letras previas y siguientes no transparentes (NSMs / harakat U+064B..U+065F + alef khanjareeya U+0670 transparentes). Determina la posición a partir del contexto de joining-class: D (dual-joining: BEH, TEH, SEEN, LAM, MEEM, NOON, etc.) admite isolated / initial / medial / final según si las letras fuertes circundantes permiten unión en sus respectivos lados; R (right-joining: ALEF, DAL, ZAL, REH, ZAIN, WAW, etc.) admite solo isolated / final.
  • Impacto directo en el usuario (con AutoShapeArabic=True): la entrada "بسم" (BEH + SEEN + MEEM) emite Tj con las formas de presentación FE91 (BEH initial), FEB4 (SEEN medial), FEE2 (MEEM final). Tras la inversión RTL el operando Tj es FEE2 FEB4 FE91, que cualquier lector PDF renderiza como escritura árabe conectada incluso sin un shaper incorporado.
  • Cobertura: 35 letras árabes básicas (variantes HAMZA, variantes ALEF, el alfabeto árabe de 28 letras, YEH MAKSURA). Una búsqueda estática estándar de Unicode reemplaza un parseo binario GSUB específico de fuente, de modo que el shaping funciona con cualquier fuente que tenga glifos en U+FE70..U+FEFF (Arial, Tahoma, Segoe UI Arabic, Noto Sans Arabic, Amiri, etc.).
  • NO cubierto en esta entrega: letras de extensión persa / urdu (U+0671..U+06D3); glifos de ligadura LAM-ALEF (FEFB / FEFC); mongol / siríaco (modelos de shaping distintos); alternativas estilísticas GSUB específicas de fuente más allá del modelo básico de 4 posiciones.
  • Estabilidad a nivel de byte: AutoShapeArabic por defecto es False, de modo que todos los llamadores de v2.74-v2.84 conservan salida /AP idéntica a nivel de byte. Los llamadores que ya hacen pre-shape aguas arriba (o usan un shaper del lado consumidor) no se ven afectados.
  • Ubicación en el pipeline: el shaping se ejecuta en cada helper Build*UnicodeFieldContent DESPUÉS del decode UTF-8 pero ANTES de la inversión bidi, de modo que las formas de presentación post-shape se invierten correctamente en RTL cuando AutoDetectFormBidi / FormUnicodeRTL también están habilitados.
  • Trabajo pendiente UAX #9: el formato cmap 12 SMP (cobertura de puntos de código U+10000+ en RegisterUnicodeTTF) se implementará en una versión posterior.

2026-05-18 Version 2.84.0

  • Se añadió un subsetter TrueType basado en archivos que se dispara en el momento de EndDoc. Después de RegisterUnicodeTTF + AddTextField, el documento acumula un conjunto de uso por codepoint durante la emisión de BuildUnicode*FieldContent; EndDoc traduce los codepoints usados a IDs de glifo, recorre el cierre de glifos compuestos y reconstruye el programa de fuente embebido con solo esos glifos. El embed crudo de v2.82 se reemplaza; el resto de la estructura de tablas sigue siendo válida porque el subsetter mantiene los IDs de glifo sin cambios (solo compactación de glyf/loca).
  • Impacto directo en el usuario: los PDFs con TTFs Unicode embebidos se reducen drásticamente. Un documento "Hello World" con Arial embebido era ~350 KB comprimido en v2.82; v2.84 baja a ~10-15 KB. Las fuentes CJK con el típico subset de 1000-3000 glifos se reducen de ~14 MB a ~500 KB-2 MB comprimido.
  • Algoritmo del subsetter: mantiene los IDs de glifo sin cambios para que cmap, hmtx, /CIDToGIDMap, /W sigan siendo válidos. Para cada índice de glifo 0..numGlyphs-1, la nueva tabla glyf copia los bytes originales (si se usa) o emite una entrada vacía vía loca[i+1] = loca[i] (si no se usa). El formato de loca (short / long según head.indexToLocFormat) se preserva. Los checksums por tabla + head.checksumAdjustment se recalculan para validación TTF estricta según spec.
  • Cierre de glifos compuestos: cuando un glifo usado tiene numContours == -1 (compuesto), el subsetter recorre su lista de componentes (parseando flags + glyphIndex + skip de args/transform variables de cada componente), marca los componentes como usados, e itera hasta que no se añadan glifos nuevos. Recorrido con verificación de límites para que cadenas compuestas malformadas no puedan hacer bucle infinito.
  • Conformidad con spec: /BaseFont en el dict Type 0 + descendiente CIDFontType2 + /FontName en el FontDescriptor todos reciben un prefijo de subset de 6 letras AAAAAA+ derivado de un hash FNV-1a determinístico del conjunto de glifos usados. Según spec 9.6.4, el prefijo indica que el programa de fuente ha sido subseteado; sin él los lectores estrictos con la spec advierten sobre modificaciones de fuente embebida.
  • Infraestructura de seguimiento de uso: un array booleano FUnicodeUsedCps[] de 65536 entradas se asigna perezosamente en la primera llamada a RegisterUnicodeTTF. Cada uno de los tres helpers BuildUnicode*FieldContent (single-line, multi-line, comb) marca las unidades de código a medida que emite operandos Tj hex UTF-16BE. SetFormUnicodeFontDict('', nil) y un futuro reset de BeginDoc limpian el estado.
  • Seguridad ante fallos: cualquier paso del subsetter que no pueda completarse (tablas faltantes, loca malformado, fallo de cierre compuesto, fallo de compresión) deja el embed crudo de v2.82 en su lugar. El PDF sigue siendo válido; solo el tamaño del archivo se ve afectado. Esto preserva la garantía de fiabilidad de v2.82 a la vez que entrega el beneficio del subset en el camino feliz.
  • Lo que NO está en esta entrega: joining contextual GSUB OpenType árabe/siríaco/devanagari; cmap formato 12 SMP (sigue siendo solo BMP). Ambos diferidos a v2.85+.

2026-05-18 Version 2.83.0

  • Se añadió CMap /ToUnicode PDF 1.7 ISO 32000-1 9.10.3 a RegisterUnicodeTTF. El dict de fuente Type 0 ahora lleva una referencia indirecta /ToUnicode apuntando a un stream CMap Adobe-Identity-UCS, de modo que los lectores consumidores pueden extraer texto Unicode del PDF. Sin /ToUnicode, copiar y pegar desde un PDF v2.74-v2.82 producía índices CID en bruto en lugar de codepoints Unicode; los lectores de pantalla no podían leer el contenido en voz alta; la conformidad PDF/A fallaba.
  • Estructura del CMap: CMap Adobe-Identity-UCS mínimo con /CIDSystemInfo (Adobe / UCS / 0), /CMapName /Adobe-Identity-UCS, /CMapType 2, un único codespacerange cubriendo <0000>..<FFFF>, y un único bfrange mapeando <0000> <FFFF> a <0000> (identity). El texto PostScript del CMap se comprime a ~150 bytes vía FlateDecode, por lo que la sobrecarga de embebido es insignificante.
  • Justificación del mapeo identity: nuestro setup v2.74-v2.82 es Identity-H + Adobe-Identity-0 + CID = codepoint Unicode, por lo que el mapeo inverso ToUnicode es genuinamente identity para todo el BMP. Una sola entrada bfrange captura el mapeo completo; no se necesita lista bfchar por codepoint.
  • Impacto visible para el usuario: la selección de texto / copia-pega desde lectores PDF (Adobe, Foxit, Chrome PDF viewer) ahora produce los codepoints Unicode originales en lugar de basura CID. Los lectores de pantalla (NVDA, JAWS, VoiceOver) pueden leer en voz alta el contenido de texto AcroForm para accesibilidad. La conformidad PDF/A-1/2/3 se vuelve posible (PDF/A requiere /ToUnicode en todas las fuentes Type 0).
  • Ubicación en el pipeline: la emisión de /ToUnicode vive FUERA del bloque try/except WArrayValid porque el mapeo identity no depende de que el parseo de cmap tenga éxito. Incluso si /W / /CIDToGIDMap / /FontFile2 se omitieran silenciosamente debido a tablas de fuente corruptas, el CMap ToUnicode aún se emite y es semánticamente correcto.
  • Objeto stream: THPDFStreamObject indirecto + /Filter /FlateDecode (sin /Length1 ya que ToUnicode es un stream de texto, no un programa de fuente). Adjunto al dict envoltorio Type 0 vía Result.AddValue('ToUnicode', ...). Patrón de compresión zlib compartido con /CIDToGIDMap de v2.77 y /FontFile2 de v2.82.
  • Lo que NO está en esta entrega: entradas bfchar por codepoint (permitirían mapear CIDs no identity a secuencias Unicode multi-carácter como fi -> f+i, pero nuestro setup Identity nunca lo necesita); cobertura SMP más allá del BMP (sigue siendo rango 0x0000..0xFFFF). Ambos diferidos a v2.84+.

2026-05-18 Version 2.82.0

  • Se añadió el embed crudo de fuente /FontFile2 PDF 1.7 ISO 32000-1 9.6.4 + 9.9 a RegisterUnicodeTTF. El programa de fuente real (los bytes binarios TTF / OTF) ahora viaja dentro del PDF como stream /FontFile2 adjunto al FontDescriptor, de modo que los lectores consumidores ya no tienen que resolver el nombre /BaseFont vía caché de fuentes del SO / app. Resultado: un PDF autocontenido que se renderiza correctamente en sistemas que carecen de la fuente nombrada.
  • Estructura del stream: THPDFStreamObject indirecto con /Filter /FlateDecode y /Length1 = tamaño del archivo de fuente sin comprimir (según spec Table 124). La compresión usa TZCompressionStream (zcDefault, windowBits 15) siguiendo el patrón /CIDToGIDMap de v2.77. Los TTFs latinos típicos comprimen 30-50% (p. ej., Arial de 700 KB -> embed comprimido de 350 KB).
  • Integración con el pipeline: la emisión ocurre en la misma rama WArrayValid que el array /W de v2.75 y el /CIDToGIDMap de v2.77, de modo que cuando el parseo de cmap falla (subtabla corrupta / formato no soportado) los tres streams de característica se omiten juntos para una degradación elegante al resultado solo-de-métricas de v2.74.
  • Impacto visible: los PDFs v2.74-v2.81 eran técnicamente fuentes compuestas Type 0 / CIDFontType2 pero el programa de fuente FALTABA. Los lectores consumidores tenían que hacer coincidir el /BaseFont (nombre PostScript) con su propia caché de fuentes. En sistemas con la fuente exacta nombrada instalada, el renderizado funcionaba; en sistemas sin ella, el lector o sustituía una fuente distinta (dando glifos / métricas incorrectos) o renderizaba cajas marcadoras. v2.82 envía el programa de fuente en el propio PDF - el mismo programa de fuente que el productor parseó en RegisterUnicodeTTF - de modo que el renderizado del consumidor es determinístico independientemente de la disponibilidad de fuentes del SO.
  • Compromiso: los PDFs ganan ~150-500 KB por fuente embebida para TTFs latinos típicos (tamaño comprimido); las fuentes CJK añaden 2-20 MB comprimidos. Esta es la MISMA inflación que evitaba la sustitución de fuentes del lado consumidor, pero a costa de renderizado no determinístico. El subsetting (seguimiento v2.83+) reducirá la inflación eliminando glifos no usados del programa embebido.
  • Notas de spec: /BaseFont en nuestra emisión v2.82 se queda con el nombre PostScript original (sin prefijo de subset "AAAAAA+") porque embebemos la fuente completa. El prefijo de 6 letras según spec 9.6.4 solo aplica cuando el programa de fuente ha sido subseteado; v2.83+ añadirá el prefijo como parte de la integración del subsetter.
  • Lo que NO está en esta entrega: subsetting de fuentes (por lo que una sola fuente embebida añade el tamaño completo del archivo al PDF). CMap ToUnicode (para copia-pega PDF/A). Cmap formato 12 SMP (codepoints U+10000+ siguen sin mapear). Todo diferido a v2.83+.

2026-05-18 Version 2.81.0

  • Se replicaron UAX #9 W4 (separator-between-digits) y W5 (terminator-adjacent-to-digit) en la ruta de reordenamiento de párrafos LTR. Antes solo RTL (desde v2.72.0 / v2.73.0); v2.81 extrae ambas pasadas a helpers compartidos `_ApplyUAX9W4Rules` y `_ApplyUAX9W5Rules` de modo que ambas direcciones de párrafo aplican las mismas transformaciones de regla débil.
  • Refactor: el código inline de la ruta RTL Paso 1b (W4) y Paso 1c (W5) se reemplazó con llamadas helper de una sola línea. Los helpers toman la cadena Wide + array Classes[] y modifican Classes[] in situ; la lógica es idéntica a las implementaciones inline de v2.72/v2.73, por lo que la salida RTL es estable a nivel de byte tras el refactor.
  • La ruta LTR añade Paso 1c (W4) y Paso 1c.2 (W5) entre la herencia NSM W1 (Paso 1b) y W7 EN-precedido-por-L (Paso 1d). Las reglas N posteriores (Paso 1e) y la regla I (Paso 1f) renumeradas para mantener explícito el orden del pipeline.
  • Impacto LTR visible: en contextos LTR mixtos donde un strong RTL precede a una expresión digit/ET, W5 ahora promueve el ET a EN antes de que N1 tenga oportunidad de aglomerarlo en el run R. Ejemplo: "Hi shin $1" (párrafo LTR con letra hebrea shin interna luego "$1") antes emitía "Hi $ shin 1" con el $ arrastrado al run R e intercambiado con shin. v2.81 emite "Hi shin $1" con $ permaneciendo adyacente a su dígito. Lo mismo aplica a W4: "Hi shin $1,2 widgets" preserva "$1,2" como bloque lógico en lugar de dividirse en torno a la coma.
  • Escenarios estables a nivel de byte en ruta LTR: las entradas puramente LTR sin R precedente (p. ej. "12,345 widgets") no ven cambio visible porque W7 sor=L ya absorbe todos los ENs a L; las entradas LTR que contienen solo L + RTL sin dígitos ni ETs tampoco se ven afectadas. Los smokes LTR v2.79 existentes (smoke_bidi_n_rules_ltr, smoke_bidi_ltr_reorder) siguen pasando de forma idéntica a nivel de byte.
  • Orden del pipeline (ruta RTL sin cambios): W1 -> W4 -> W5 -> W7 -> N0 -> N1 -> N2 -> I2 -> L2. Ruta LTR ahora: W1 -> W4 -> W5 -> W7 -> N0 -> N1 -> N2 -> regla I -> L2. Las dos rutas comparten los helpers de reglas W4/W5/W7/N; solo la clasificación (Paso 1a) y el recorrido final L2 difieren entre RTL y LTR.
  • Trabajo UAX #9 pendiente: joining contextual GSUB OpenType árabe / siríaco / devanagari, y seguimientos de RegisterUnicodeTTF (CMap ToUnicode, cmap formato 12 SMP, /FontFile2 + subsetting) siguen por delante.

2026-05-18 Version 2.80.0

  • Se añadió bridging de European Number Unicode UAX #9 W7: un EN cuya búsqueda hacia atrás encuentra un L fuerte (o sor=L cuando el escaneo alcanza el inicio del buffer de párrafo) se convierte en L. La transformación se ejecuta después de W4 (separator-between-digits) y W5 (terminator-adjacent-to-digit) en ambas rutas de párrafo RTL y LTR, de modo que cualquier separador promovido a EN por W4 también está sujeto a L-bridging.
  • Impacto RTL visible: una entrada como "Hello 123 Arabic" en un párrafo RTL antes emitía el layout visual "Arabic SP 123 SP Hello" porque el run EN quedaba entre L (Hello) y AL (Arabic) y las reglas N dividían la frase LTR en dos runs independientes (con SP a nivel de embedding entre ellos). v2.80 W7 convierte el EN a L (ya que L precede), y N1 absorbe el SP inicial entre Hello y 123 al run L; el "Hello 123" entero se convierte en una subcadena de nivel 2 invirtiéndose como unidad. El layout visual final se convierte en "Arabic SP Hello SP 123", coincidiendo con el comportamiento estricto UAX #9.
  • W7 NO se dispara cuando: el strong más cercano del EN es R o AL (letras árabes preceden a los dígitos); el EN está al inicio del buffer en un párrafo RTL (sor=R); o no hay L en ninguna parte precediendo al EN. Comportamiento pre-v2.80 preservado en estos casos - los smokes baseline W4/W5 de v2.72/v2.73 siguen siendo estables a nivel de byte.
  • Efecto en párrafo LTR: W7 también se ejecuta en la ruta LTR (para consistencia con spec), con sor=L cuando el escaneo alcanza el inicio del buffer. Bajo la regla I LTR simplificada de embedding único de HotPDF, L y EN ambos aterrizan en nivel 0, por lo que la transformación EN -> L no cambia directamente Levels[], pero sí cambia el patrón de absorción de N1: un SP entre L y un EN puenteado a L ahora satisface la condición de misma-dirección de N1 y se une al run. Para entradas puramente LTR sin RTL interno el cambio es no-op estable a nivel de byte; para entradas LTR que contienen RTL final (p. ej. "Hello 123 shalom") la colocación del SP relativa al bloque RTL se desplaza una posición.
  • W6 (residual ES/CS/ET -> ON según UAX #9 estricto) es implícito en la tabla Classes[] simplificada de HotPDF: ES, CS, ET, WS, ON todos comparten la clase 0 (bcOther), y las reglas N tratan la clase 0 de forma uniforme. La promoción W6 a ON es un no-op en nuestro modelo ya que N1/N2 no diferencian ON de WS. Documentado en el comentario de código junto al helper W7.
  • Orden del pipeline (ruta RTL): herencia NSM W1 -> W4 ES/CS entre dígitos -> W5 ET adyacente a dígitos -> W7 EN precedido por L -> N0 brackets emparejados -> N1 absorción NI misma-dirección -> N2 default-al-embedding -> I2 asignación de nivel -> L2 reverse-of-reverse. Mismo orden en ruta LTR menos W4/W5 (esos permanecen solo RTL en esta entrega; el port LTR de W4/W5 es el siguiente candidato).
  • Nuevo procedimiento helper compartido `_ApplyUAX9W7Rules(Classes, ParaIsRTL)` opera sobre Classes[] in situ. La búsqueda hacia atrás omite posiciones weak/NSM/EN/AN; solo L/R/AL cuentan como tipos fuertes. Cuando el escaneo se agota sin encontrar un strong, la dirección del párrafo (sor) se usa como tipo fuerte implícito. La ruta RTL pasa ParaIsRTL=True, la ruta LTR pasa False.
  • Trabajo UAX #9 pendiente: port W4/W5 en ruta LTR (actualmente solo RTL), joining contextual GSUB OpenType árabe / siríaco / devanagari, y seguimientos de RegisterUnicodeTTF (CMap ToUnicode, cmap formato 12 SMP, /FontFile2 + subsetting) siguen por delante.

2026-05-18 Version 2.79.0

  • Se replicaron las reglas N0/N1/N2 de v2.78.0 en la ruta de reordenamiento de párrafos LTR. Corrige el bug simétrico donde una frase RTL multi-palabra embebida en un párrafo LTR (p. ej. oración en inglés que contiene varias palabras hebreas o árabes separadas por espacios) tenía sus palabras apareciendo visualmente INTERCAMBIADAS para un lector RTL porque el espacio entre palabras se quedaba en nivel 0 y rompía la frase RTL en runs R independientes. Con N1 de v2.79, el espacio se absorbe en el run R circundante; la frase entera se convierte en una subcadena de nivel 1 y el reverse L2 mantiene las palabras en orden lógico.
  • Se refactorizó la ruta de párrafo LTR (`_ApplyUAX9L2ReversalLTRPara`) para replicar el layout de la ruta RTL: clasifica cada unidad de código UTF-16 en un array de bytes Classes[], aplica herencia NSM W1 sobre Classes[] (era Levels[] pre-v2.79), luego llama a un helper compartido `_ApplyUAX9NRules` que opera sobre Classes[] dado un parámetro de dirección de embedding (1 = L para LTR, 2 = R para RTL). La regla I luego deriva Levels[] de Classes[]. Ambas rutas RTL y LTR ahora comparten una única implementación de reglas N.
  • El helper `_ApplyUAX9NRules(Wide, Classes, EmbedDirCls)` acepta la dirección de embedding como valor de clase (1 o 2) y aplica resolución de brackets emparejados N0 + absorción NI misma-dirección N1 + default-al-embedding N2 uniformemente. La ruta RTL pasa 2 (R); la ruta LTR pasa 1 (L). Todo el comportamiento RTL de v2.78 se preserva byte a byte.
  • Impacto LTR visible: "Hi shalom olam World" con entrada lógica H i SP shin lamed vav final-mem SP ayin vav lamed final-mem SP W o r l d antes se emitía con las dos palabras hebreas invertidas entre sí (cada palabra individualmente correcta, pero sus posiciones RELATIVAS intercambiadas). v2.79 preserva el orden lógico de palabras de modo que un lector RTL ve "Hi shalom olam World" con el bloque hebreo en el orden esperado. La misma corrección aplica a frases RTL multi-palabra envueltas en brackets vía cooperación N0 + N1.
  • Orden del pipeline en ruta LTR (reflejando RTL): clasificar en Classes[] -> herencia NSM W1 -> N0 brackets emparejados -> N1 absorción NI misma-dirección -> N2 default-al-embedding -> regla I (derivar Levels[]) -> L2 invertir runs de nivel 1. Las pasadas de manejo de dígitos W4/W5 aún no se han portado a la ruta LTR; los párrafos LTR con combinaciones de dígito + separador pasan por las reglas N sin cambios.
  • Estable a nivel de byte en smokes LTR existentes: ASCII puro LTR, RTL de palabra única en párrafo LTR, RTL + LTR mixto sin espacios inter-palabra-RTL todos producen arrays Levels[] idénticos tras el refactor (el paso de herencia NSM W1 es observablemente equivalente tanto si trabaja sobre Levels[] como sobre Classes[]). Los smokes LTR v2.67 / v2.69 / v2.71 siguen pasando de forma idéntica a nivel de byte.
  • Trabajo UAX #9 pendiente: limpieza weak residual W6/W7, manejo de dígitos W4/W5 en ruta LTR (actualmente solo RTL), joining contextual GSUB OpenType árabe / siríaco / devanagari, y seguimientos de RegisterUnicodeTTF (CMap ToUnicode, cmap formato 12 SMP, /FontFile2 + subsetting) siguen por delante.

2026-05-18 Version 2.78.0

  • Se añadieron las reglas simplificadas Unicode UAX #9 N0 resolución de brackets emparejados + N1 absorción neutral-entre-mismo-strong + N2 neutral-default-al-embedding en el pipeline de reordenamiento de párrafos RTL. Corrige el bug visible donde una frase LTR multi-palabra embebida en un párrafo RTL (p. ej. hebreo o árabe rodeando texto en inglés con espacios internos) se emitía con las PALABRAS invertidas porque el espacio entre runs L se quedaba en el nivel de embedding y rompía la frase LTR en sub-runs independientes. Con N1, el espacio se absorbe en el run L circundante de modo que la frase entera se convierte en una subcadena de nivel 2 y el reverse-of-reverse L2 la mantiene en orden lógico.
  • Brackets N0: 22 pares de brackets BMP cubiertos, desde paren / square / curly ASCII a matemáticos / tipográficos (ceiling, floor, math angle, math white square, white curly) a brackets de puntuación CJK (angle, double angle, corner, white corner, black lenticular, tortoise shell, white tortoise, white square) hasta formas full-width. Detección BD16 basada en pila para brackets emparejados (límite 63 entradas según spec); la resolución de pares escanea el interior buscando la primera dirección fuerte (L, R, AL, EN, AN, donde EN/AN cuentan como R según spec), con fallback de mirar-antes-del-bracket-de-apertura cuando el interior difiere de la dirección de embedding.
  • Absorción N1: recorre Classes[] buscando runs maximales de bcOther (neutral), encuentra los tipos fuertes izquierda + derecha no-NSM, y cuando ambos lados tienen la misma influencia direccional (ambos L, o ambos R-equivalentes contando EN/AN como R) el run entero toma esa dirección. Impacto en el mundo real: espacios / comas / puntos / dos puntos dentro de frases inglesas embebidas en párrafos árabes / hebreos ahora se agrupan con la frase en lugar de dividirla.
  • N2 default: las posiciones bcOther restantes (sin strong circundante, o izquierda/derecha desemparejadas) toman la dirección de embedding. Para párrafos RTL esto es R; clase 2. Después de N2 cada entrada de Classes[] está en el conjunto resuelto {1..6} de modo que la asignación de nivel I2 ya no depende de que bcOther tenga nivel 1 por defecto.
  • Impacto visible: antes HotPDF emitía "aleph aleph SP a b c SP d e f SP aleph aleph" (entrada lógica con frase inglesa "abc def" en un párrafo RTL) como "aleph aleph SP d e f SP a b c SP aleph aleph", con las palabras intercambiadas porque el espacio entre palabras estaba en nivel 1 (= embedding RTL). v2.78 produce "aleph aleph SP a b c SP d e f SP aleph aleph" con la frase intacta. Los brackets alrededor de la frase embebida ahora se resuelven correctamente vía N0 + mirroring L4 en el consumidor.
  • Orden del pipeline: herencia NSM W1 -> W2/I2 (asignación de clase EN/AN en tabla BIDI, no en esta pasada) -> W4 ES/CS entre dígitos -> W5 ET adyacente a dígitos -> N0 brackets emparejados -> N1 absorción misma-dirección -> N2 default-al-embedding -> I2 asignación de nivel -> L2 reverse-of-reverse. Las reglas N se ubican entre W e I; las salidas existentes de reglas W alimentan las reglas N como entrada.
  • Estable a nivel de byte para smokes existentes que no tienen una frase LTR multi-palabra dentro de RTL: RTL puro, LTR puro (la función devuelve la entrada), LTR de palabra única en RTL (sin neutrales internos para absorber), árabe + dígitos formateados con espacio + coma (los espacios transitan de bcOther a R-equivalente pero aterrizan en el mismo nivel de modo que la salida del reverse L2 no cambia). Los smokes baseline v2.66/v2.67/v2.68/v2.71/v2.72/v2.73 siguen pasando de forma idéntica a nivel de byte.
  • Trabajo UAX #9 pendiente: limpieza weak residual W6/W7, replicar las reglas N en el párrafo LTR (frase RTL multi-palabra embebida en párrafo LTR), joining contextual GSUB OpenType árabe / siríaco / devanagari, y seguimientos de RegisterUnicodeTTF (CMap ToUnicode, cmap formato 12 SMP, /FontFile2 + subsetting) siguen por delante.

2026-05-18 Version 2.77.0

  • Se añadió el stream /CIDToGIDMap PDF 1.7 ISO 32000-1 9.7.4.2 a RegisterUnicodeTTF. Cada codepoint Unicode (CID) en el BMP ahora mapea al índice de glifo real dentro del programa de fuente embebido. Antes de v2.77 el setup Type 0 / CIDFontType2 + Identity-H de HotPDF dejaba la entrada /CIDToGIDMap sin especificar, lo que por defecto es /Identity según spec - el lector consumidor entonces indexaba la tabla de glifos de la fuente por CID directamente, dando con el glifo equivocado para casi cada carácter porque las tablas de glifos de fuentes reales rara vez alinean el índice de glifo N con el codepoint Unicode N.
  • La implementación reutiliza el parseo de cmap ya realizado para el array /W de v2.75. La tabla en memoria CpToGid[0..$FFFF] se serializa como un stream de 131072 bytes (2 bytes big-endian por CID) y se comprime con FlateDecode; la reducción típica de una fuente real es 80-95% porque la inmensa mayoría de codepoints BMP no tienen glifo y se serializan como 0x0000. El stream comprimido se adjunta como entrada indirecta /CIDToGIDMap en el dict descendiente CIDFontType2.
  • Impacto visible: un PDF generado vía RegisterUnicodeTTF + SetFormUnicodeFontDict + widgets Tx AcroForm ahora renderiza los mismos glifos en el lector consumidor que la fuente fuente renderizaría nativamente. Los PDFs pre-v2.77 que dependían del default /Identity producían renderizado visiblemente incorrecto (glifos aleatorios en lugar de los caracteres latinos / dígitos / puntuación pretendidos) en lectores estrictos con la spec; los lectores que recurren a interpretar el cmap por Unicode de todas formas resultaban renderizar correctamente pero lo hacían vía comportamiento no estándar.
  • Ubicación en el pipeline: la construcción de /CIDToGIDMap sucede dentro de la misma rama WArrayValid que construye /W, de modo que los dos adjuntos comparten el recorrido del cmap y solo la segunda pasada de compresión es nueva. Si el parseo de cmap / hmtx / maxp falla, el fallback silencioso omite tanto /W como /CIDToGIDMap; el lector consumidor entonces ve el dict pre-v2.77 y recurre a su interpretación de cmap por defecto.
  • Lo que NO está en esta entrega (diferido a v2.78+): cmap formato 12 SMP (los codepoints U+10000+ siguen mapeando a GID 0 en la tabla), embebido de stream /FontFile2 (de modo que el lector consumidor sigue resolviendo el nombre /BaseFont vía su propia coincidencia de fuentes), subsetting de fuentes (el mapa crudo de 128 KB es de rango completo), y CMap ToUnicode para round-trips de copia-pega / accesibilidad.

2026-05-18 Version 2.76.0

  • Se conectó el array /W de anchos de avance TTF de v2.75.0 al propio algoritmo de word-wrap multilínea de HotPDF. El helper BuildUnicodeMultilineFieldContent.CodeUnitAdvance de v2.65.0 ahora consulta una caché de avance por codepoint poblada por RegisterUnicodeTTF, reemplazando la heurística estrecho/ancho 0.5/1.0 con las métricas reales del glifo. Los saltos de línea del lado productor ahora se alinean con el renderizado /W del lado consumidor: el punto de corte que HotPDF elige coincide con donde el lector consumidor realmente cortará al dibujar el operando /Tj.
  • Nuevos campos privados: FAcroFormUnicodeAdvances (array dinámico de Word, dimensionado 65536 o vacío) cachea anchos de avance por codepoint en unidades de diseño PDF (1000 por em). El flag FAcroFormUnicodeAdvancesActive distingue "caché poblada por RegisterUnicodeTTF" de "sin fuente cargada (heurística como fallback)". RegisterUnicodeTTF puebla estos campos durante el mismo recorrido hmtx + cmap que construye el array /W, por lo que no se necesita una pasada extra de parseo.
  • Orden de lookup de CodeUnitAdvance: si FAcroFormUnicodeAdvancesActive Y la caché tiene una entrada no cero para la unidad de código, divide el ancho escalado entre 1000.0 y devuelve como fracción de em; de lo contrario cae a la heurística de v2.65 (estrecho para ASCII / Latin-1, ancho para CJK / Hangul / Hiragana / Katakana / full-width). Las mitades de pares surrogate aún fluyen por la heurística ya que la caché es solo BMP.
  • Ciclo de vida de la caché: limpiada en THotPDF.Create (por defecto vacía / inactiva), limpiada en SetFormUnicodeFontDict('', nil) cuando el llamador resetea explícitamente el registro de fuente Unicode, y re-poblada en cada llamada subsiguiente a RegisterUnicodeTTF. Mantiene 128 KB (64K codepoints * 2 bytes) solo cuando se ha cargado al menos un TTF; las instancias que nunca usan fuentes Unicode pagan coste de memoria cero.
  • RegisterUnicodeFontDict (v2.70.0) y RegisterUnicodeTTF (v2.74.0) sin array /W (p. ej. tablas maxp / cmap / hmtx faltantes) dejan la caché vacía / inactiva, de modo que el word-wrap multilínea cae a la heurística de v2.65 - salida idéntica a nivel de byte a v2.65 para esas rutas.
  • Impacto visible: un widget Tx AcroForm multilínea con una fuente latina típica (Arial, Segoe UI, Times) ahora hace wrap en límites de carácter que coinciden con el layout real del lector consumidor. Sin v2.76, la heurística de v2.65 trataba cada char ASCII como de 0.5 em de ancho; para una fuente real donde "M" es ~0.8 em e "i" es ~0.3 em la heurística sobreestima o subestima en 30-60%, causando que el consumidor haga wrap de forma distinta a lo planeado por HotPDF, provocando desbordamiento de texto o líneas cortas.
  • BuildUnicodeTextFieldContent de línea única y BuildUnicodeCombFieldContent de comb no usan CodeUnitAdvance y permanecen no afectados (línea única no tiene wrap; comb usa celdas de ancho igual).
  • Lo que NO está en esta entrega: cmap formato 12 SMP (los codepoints U+10000+ aún fluyen por la heurística por mitad surrogate), CMap ToUnicode, embebido de stream /FontFile2, subsetting de fuentes. Esos aterrizan en entregas v2.77+.

2026-05-18 Version 2.75.0

  • Se amplió THotPDF.RegisterUnicodeTTF de v2.74.0 con parseo de hmtx + maxp + cmap de modo que el descendiente CIDFontType2 resultante lleve un array /W real de anchos de avance por codepoint. Los lectores consumidores ahora usan métricas reales de fuente para el renderizado del ancho de texto en lugar de recurrir a /DW = 1000 para cada glifo - el texto dentro de un stream de apariencia /AP AcroForm ahora ocupa el mismo ancho que la fuente fuente lo renderizaría.
  • Nuevos helpers a nivel de unidad parsean las tres tablas SFNT adicionales: _TTFParseMaxpNumGlyphs (lee numGlyphs de maxp), _TTFParseHmtxAdvances (array de ancho de avance por glifo de hmtx, dimensionado por numberOfHMetrics de hhea), _TTFFindAndParseCmap + _TTFParseCmapFormat4 (lookup codepoint Unicode -> ID de glifo vía la subtabla formato 4 de segment-mapping BMP). _TTFBuildWArray compone el array /W disperso forma 1 de PDF 1.7 9.7.4.3, agrupando codepoints consecutivos de ancho no-default en runs [startCID [w1 w2 w3 ...]] y omitiendo codepoints cuyo ancho iguala /DW = 1000 para reducir el tamaño del array.
  • La selección de subtabla cmap prefiere Microsoft Unicode BMP (platform 3, encoding 1, format 4) - el layout universal de cmap de fuentes Windows. Recurre a Unicode platform (0, encoding 3 o 4) para fuentes OpenType multiplataforma, luego platform 3 encoding 10 (Unicode full / solo format 4), luego cualquier otro format 4. Las subtablas no-format-4 (format 0, 2, 6, 8, 12, 13, 14) se ignoran en esta entrega - la cobertura SMP vía format 12 aterriza en v2.76+.
  • El array /W se adjunta al dict descendiente CIDFontType2 vía recorrido directo: Type0 -> /DescendantFonts -> [0] -> AddValue('W', WArray). Firma de RegisterUnicodeFontDict de v2.70 sin cambios; el dict descendiente se muta in situ tras el ensamblaje.
  • Los anchos FUnit de hmtx se escalan a unidades de diseño PDF (1000 por em) usando el valor unitsPerEm de la tabla head. Los codepoints sin mapeo de glifo (cmap devuelve 0 / .notdef) se tratan como DW = 1000 y se omiten de /W. Para una fuente latina típica (Arial, Segoe UI, Times) el /W resultante lleva 500-1500 entradas de codepoint; para fuentes CJK el array puede crecer a 10K+ entradas.
  • Lo que NO está en esta entrega (diferido a v2.76+): los datos de ancho /W AÚN no son consumidos por el propio algoritmo de word-wrap multilínea de HotPDF - el BuildUnicodeMultilineFieldContent de v2.65 todavía usa la heurística estrecho/ancho 0.5/1.0 para el cálculo de salto de línea; el array /W es puramente para precisión de renderizado del lado consumidor. El perfect-wrap completo del lado productor (usando /W para impulsar la matemática del wrap) aterriza en v2.76+. También diferido: CMap ToUnicode a partir del mapeo inverso del cmap, embebido de stream /FontFile2, subsetting de fuentes, y cmap formato 12 SMP.
  • El parseo /W tiene fallback try/except - si maxp / cmap / hmtx está faltante o alguna subtabla está malformada, RegisterUnicodeTTF continúa silenciosamente con el resultado solo-de-métricas de v2.74; el lector consumidor recurre a /DW = 1000 para cada codepoint. Sin corrupción silenciosa: las tablas tienen verificación de límites en cada lectura multi-byte.

2026-05-18 Version 2.74.0

  • Se añadió THotPDF.RegisterUnicodeTTF, un envoltorio basado en archivos sobre RegisterUnicodeFontDict de v2.70.0 que carga una fuente TTF / OTF, parsea las tablas head, hhea, OS/2, post y name, escala las métricas FUnit a unidades de diseño PDF (1000 por em), y entrega los valores a RegisterUnicodeFontDict para construir el diccionario de fuente compuesto Type 0 / CIDFontType2 + Identity-H completo. El dict devuelto está listo para SetFormUnicodeFontDict - los llamadores ya no necesitan conocer ni los valores exactos de métrica de la fuente ni el formato binario SFNT.
  • API: RegisterUnicodeTTF(FontPath, LogicalName=''). FontPath es una ruta absoluta o relativa a un archivo .ttf / .otf. LogicalName está reservado para v2.75+ (donde puede desambiguar fuentes que comparten un nombre PostScript).
  • Tablas parseadas: head (unitsPerEm, FontBBox xMin/yMin/xMax/yMax en FUnits), hhea (ascender, descender en FUnits), OS/2 (usWeightClass para estimación de /StemV, sCapHeight, fsSelection para bit de itálica), post (italicAngle en FIXED 16.16 -> grados, isFixedPitch -> bit 1 de /Flags), name (nombre PostScript en nameID 6 de plataforma Windows o Mac, recurriendo al nombre completo en nameID 4). Verificación de límites en cada offset / longitud de tabla + en cada lectura multi-byte de modo que una fuente malformada aflora como una excepción clara en lugar de un fallo de memoria.
  • Métricas auto-derivadas: /FontBBox L/B/R/T escalado a unidades de diseño PDF, /Ascent + /Descent escalados, /CapHeight desde OS/2 sCapHeight (recurre a ascent para tablas OS/2 pre-versión-2), /ItalicAngle desde post.italicAngle, /StemV estimado como 50 + (usWeightClass - 400) / 5 acotado a [50, 250], /Flags = Symbolic + FixedPitch (si post lo indica) + Italic (si fsSelection bit 0 está fijado o italicAngle != 0). /BaseFont usa el nombre PostScript parseado; recurre a "UnnamedTTF" si la tabla name no produce nada usable.
  • Los errores lanzan excepciones descriptivas: archivo faltante, tamaño implausible (< 12 bytes o > 64 MB), sfntVersion no reconocida (debe ser 0x00010000 / 'OTTO' / 'true' / 'typ1'), tablas requeridas faltantes (head / hhea / OS/2 / post / name), tabla-demasiado-pequeña para los campos que necesitamos.
  • Lo que esta entrega NO incluye (diferido a v2.75+): el array /W de anchos de avance desde hmtx (necesita maxp para numGlyphs + cmap para mapeo Unicode -> glifo; hoy el word-wrap multilínea de v2.65 mantiene su heurística 0.5/1.0), un CMap ToUnicode desde cmap (hoy el mapeo Identity-H de v2.56+ asume CID == codepoint Unicode), embebido de los bytes crudos de fuente como stream /FontFile2, y subsetting de fuentes. Con v2.74.0 el lector consumidor aún tiene que resolver el nombre /BaseFont vía su propia maquinaria de coincidencia de fuentes.
  • El smoke smoke_unicode_ttf.dpr carga una fuente del sistema Windows (sondea %SystemRoot%\Fonts buscando arial.ttf / segoeui.ttf / times.ttf / verdana.ttf en orden) y verifica que RegisterUnicodeTTF devuelve un dict Type 0 llevando métricas parseadas del archivo. El verificador comprueba que el /BaseFont resultante no es un placeholder stub, FontBBox / Ascent / Descent / ItalicAngle están en rangos plausibles para una fuente real.

2026-05-18 Version 2.73.0

  • Se añadió W5 simplificado Unicode UAX #9 (European Terminator adyacente a European Number) para párrafos RTL. Corrige el bug visible donde texto árabe con símbolos de moneda / porcentaje — "$12.50", "50%", "€100", "شار 12.50$ ريال" — dejaba el glifo de moneda o porcentaje varado en la frontera entre la subcadena de dígitos y el texto RTL circundante. Con W5 de v2.73, los ETs adyacentes a dígitos se unen al run de dígitos y se invierten con los dígitos, preservando la unidad moneda-y-número como una subcadena LTR dentro del párrafo RTL.
  • Se reconocen los siguientes ETs (conjunto de codepoints) por W5: U+0023 # (signo numeral), U+0024 $ (dólar), U+0025 % (porcentaje), U+00A2..U+00A5 (¢ £ ¤ ¥), U+00B0 ° (grado), U+00B1 ± (más-menos), U+066A ٪ (porcentaje árabe), U+2030..U+2031 (‰ ‱ por mil / por diez mil), U+20A0..U+20CF (bloque de símbolos de moneda, incluyendo € ₹ ₽ ₩, etc.). Una secuencia maximal de ETs adyacentes a un EN por cualquier lado (omitiendo solo NSMs) se transforma a EN.
  • Extensión de spec: el W5 simplificado de HotPDF también transforma ETs adyacentes a AN (dígitos arábigo-índicos) a AN, además de la regla solo-EN de la spec. Esto mantiene simétricos los contextos de dígitos ASCII y arábigo-índicos de modo que "٥٠٪" (50 arábigo-índico + porcentaje árabe) trata el porcentaje igual que "50%" trata el porcentaje ASCII.
  • Corrección concreta: "شار $12.50 ريال" (UTF-16 lógico 0634 0627 0631 0020 0024 0031 0032 002E 0035 0030 0020 0631 064A 0627 0644). Salida v2.72: 0644 0627 064A 0631 0020 0031 0032 002E 0035 0030 0024 0020 0631 0627 0634 — el glifo $ varado en posición de memoria 11, entre la subcadena de dígitos y el espacio final, leyéndose incómodamente. Salida v2.73: 0644 0627 064A 0631 0020 0024 0031 0032 002E 0035 0030 0020 0631 0627 0634 — "$12.50" intacto como una subcadena LTR dentro del flujo RTL.
  • Corrección de tabla BIDI_Class: U+066A ARABIC PERCENT SIGN movido de bcAL a bcOther (de modo que la pasada ET de W5 pueda detectarlo y transformarlo). El lookup del bloque árabe ahora tiene excepciones para los rangos de dígitos (bcAN), el bloque harakat / shadda (bcNSM), el alef khanjareeya (bcNSM), los separadores decimal / miles (bcAN), Y el signo de porcentaje (bcOther + ET vía W5).
  • Orden del pipeline: herencia NSM W1 (ruta LTR explícita, RTL implícita) -> W4 ES/CS entre dígitos -> W5 ET adyacente a dígitos -> I2 asignación de nivel. W5 se ejecuta después de W4 de modo que un separador transformado por W4 que se convirtió en EN es correctamente identificado como "adyacente a EN" por W5 para la siguiente pasada; las interacciones multi-regla dentro de expresiones complejas dígito-con-separador-y-moneda se resuelven en una sola travesía del pipeline.
  • Texto puro sin ET adyacente a dígitos es idéntico a nivel de byte a la salida de v2.72. Smoke existente v2.59-v2.72 (árabe RTL / hebreo / SMP RTL / posicionamiento NSM / reordenamiento párrafo-LTR / manejo de dígitos / W4) todos permanecen estables a nivel de byte.
  • Trabajo UAX #9 pendiente (limpieza W6 / W7, par de brackets N0 vía escaneo de pila BD16 UAX #9, resolución neutral N1 / N2, joining contextual GSUB OpenType árabe / siríaco, RegisterUnicodeTTF basado en archivos) sigue por delante.

2026-05-18 Version 2.72.0

  • Se añadió W4 simplificado Unicode UAX #9 (ES / CS entre dígitos del mismo tipo) para párrafos RTL. Corrige el bug visible donde texto árabe con números formateados — "السعر 12,345 ريال" (precio con coma de miles), "12.50" (punto decimal), "12:30" (dos puntos de hora) — dividía el run de dígitos en el separador en la salida /Tj invertida, produciendo números visualmente rotos que se leían al revés dentro del flujo RTL. Con W4 de v2.72 el separador se une al run de dígitos, ambos se invierten juntos una vez y luego se invierten juntos otra vez en la pasada de cadena completa, preservando el orden lógico dígito + separador dentro del párrafo RTL.
  • Separadores reconocidos por W4: European Separator U+002B (+) / U+002D (-), Common Separator U+002C (,) / U+002E (.) / U+002F (/) / U+003A (:) / U+00A0 (NBSP), y Arabic Common Separator U+060C (coma árabe). Cada uno se transforma a EN cuando está intercalado entre dos ENs, o a AN cuando está intercalado entre dos ANs. Vecinos de tipo mixto (EN + AN, o dígito + no-dígito) dejan el separador sin cambios en el stream bcOther / nivel 1 / RTL-circundante como antes. Simplificado vs UAX #9 W4 completo: el peek a través de NSMs se maneja (los NSMs cerca del separador no rompen el lookup de W4), pero las cadenas multi-separador siguen una transformación por posición en lugar de la restricción de separador "único" de la spec (impacto práctico bajo ya que los números del mundo real rara vez tienen separadores adyacentes).
  • Corrección concreta: "شار 12,345 ريال" (UTF-16 lógico empieza con letras árabes, luego espacio, "1" "2" "," "3" "4" "5", luego espacio, luego letras árabes). Salida previa v2.71: 0644 0627 064A 0631 0020 0035 0034 0033 002C 0031 0032 0020 0631 0627 0634 — dígitos divididos en la coma, invertidos dentro de cada fragmento ("345" antes de "12"), se lee como "345,12" en la percepción del usuario de la subcadena LTR. Salida corregida v2.72: 0644 0627 064A 0631 0020 0031 0032 002C 0033 0034 0035 0020 0631 0627 0634 — "12,345" intacto como una subcadena LTR, el usuario lo lee naturalmente en orden numérico.
  • Corrección de tabla BIDI_Class (v2.72.0): U+066B ARABIC DECIMAL SEPARATOR y U+066C ARABIC THOUSANDS SEPARATOR ahora clasifican como bcAN (valor de retorno 5) según Unicode UnicodeData en lugar de como bcAL. Estos separadores están definidos por spec como parte del formato numérico árabe, de modo que naturalmente se unen al run de dígitos arábigo-índicos para el reorden L2 sin necesitar la pasada W4. U+066A ARABIC PERCENT SIGN ahora clasifica como bcAL (placeholder; el manejo de spec ET / European Number Terminator se difiere a W5).
  • Refactor interno: el paso de asignación de nivel en _ApplyUAX9L2Reversal ahora construye un array paralelo explícito Classes[] (un byte por posición de unidad de código UTF-16) de modo que la pasada W4 pueda mirar las clases vecinas sin re-decodificar codepoints. Los codepoints de par surrogate comparten tanto el byte Classes[] como Levels[] entre las mitades alta+baja. El refactor equivalente para _ApplyUAX9L2ReversalLTRPara se difiere (W4 no tiene impacto visible en párrafos LTR porque EN / AN ya se quedan en nivel base 0 en el modelo simplificado de embedding único).
  • Texto puramente RTL / LTR / mixto sin separadores W4 entre dígitos del mismo tipo es idéntico a nivel de byte a la salida de v2.66 a v2.71. Smoke existente v2.59-v2.71 (árabe RTL / hebreo / SMP RTL / posicionamiento NSM / reordenamiento párrafo-LTR / manejo de dígitos / detector) todos permanecen estables a nivel de byte.
  • Trabajo UAX #9 pendiente (W5 ET adyacente a EN, limpieza W6 / W7, par de brackets N0 vía escaneo de pila BD16 UAX #9, resolución neutral simplificada N1 / N2, joining contextual GSUB OpenType árabe / siríaco, RegisterUnicodeTTF basado en archivos) sigue por delante.

2026-05-18 Version 2.71.0

  • Se corrigió el bug de posicionamiento de NSM (non-spacing mark) en el reordenamiento de texto árabe / hebreo. Antes, la inversión ingenua de cadena completa movía marcas combinatorias (harakat árabes como fatha / kasra / damma / shadda; niqqud hebreos como sheva / kamatz / patah; diacríticos combinatorios latinos en el bloque U+0300..U+036F) al lado equivocado de su carácter base en el stream /Tj invertido. Los renderizadores de marcas combinatorias de los lectores PDF entonces adjuntaban el NSM al glifo precedente en orden de visualización — la base equivocada. El bug visible: "ALEF + FATHA + BA" (árabe) renderizaba la fatha sobre el glifo BA en lugar del ALEF.
  • Se añadió herencia NSM W1 simplificada UAX #9 más una pasada de inversión consciente de grupos compartida por los tres algoritmos de reorden L2 (párrafo RTL paso 2 + paso 3, párrafo LTR pasada única). La pasada de inversión ahora trata [base, NSM*] como una unidad: al recorrer derecha-a-izquierda, los NSMs se almacenan en una pila y se vuelcan en orden lógico original inmediatamente después de la siguiente base no-NSM en la salida. Los codepoints de par surrogate continúan quedándose intactos a través de la misma pasada de inversión, y los NSMs de rango SMP (raros) se manejan simétricamente.
  • Corrección concreta: "ALEF + FATHA + BA" (UTF-16 lógico 0623 064E 0628) antes se emitía como 0628 064E 0623 (inversión completa, fatha entre BA y ALEF). Con la inversión consciente de NSM de v2.71.0 la misma entrada emite 0628 0623 064E — BA a la izquierda, ALEF en el medio, FATHA después de ALEF de modo que el renderizador la adjunta a la base correcta.
  • Tabla BIDI_Class interna extendida con clase bcNSM (valor de retorno 6) para los principales bloques de marcas combinatorias: General Combining Diacritical Marks U+0300..U+036F (acentos latinos / generales), niqqud hebreo U+0591..U+05BD más las entradas dispersas U+05BF / U+05C1..U+05C2 / U+05C4..U+05C5 / U+05C7 (extraídas del rango R hebreo), harakat árabes U+064B..U+065F y alef khanjareeya U+0670 (extraídos del rango AL árabe). Los NSMs aún se omiten durante el escaneo first-strong P2/P3 de dirección de párrafo.
  • Para párrafos LTR, una pasada explícita W1 de herencia-de-nivel-NSM se ejecuta tras la asignación inicial de nivel: cada NSM adopta el nivel del codepoint no-NSM precedente, de modo que un NSM árabe siguiendo a una base AL se une al run de nivel 1 de esa base para la inversión L2 de pasada única. Los párrafos RTL heredan implícitamente el nivel (NSM y R / AL ambos resuelven a nivel 1 en el modelo simplificado de embedding único), por lo que no se necesita una pasada W1 separada ahí.
  • Entradas puramente RTL / LTR / mixtas sin NSMs son idénticas a nivel de byte a la salida de v2.66 / v2.67 / v2.68 / v2.69 / v2.70 (sin NSM significa sin cambio de comportamiento). Smoke existente v2.59 árabe RTL / v2.66 LTR-en-RTL / v2.67 párrafo-LTR / v2.68 EN-AN / v2.69 SMP-RTL todos permanecen estables a nivel de byte.
  • Trabajo UAX #9 pendiente (transiciones detalladas de tipo weak W3-W7, resolución neutral + par-de-brackets N0-N2, joining contextual GSUB OpenType árabe / siríaco, RegisterUnicodeTTF basado en archivos) sigue por delante.

2026-05-18 Version 2.70.0

  • Se añadió el helper THotPDF.RegisterUnicodeFontDict que ensambla un diccionario de fuente compuesto Type 0 / CIDFontType2 + Identity-H PDF 1.7 ISO 32000-1 9.7 / 9.7.6 completo en una sola llamada. Los llamadores que usan la ruta SetFormUnicodeFontDict AcroForm /AP de v2.56.0 ya no necesitan codificar a mano las ~30 líneas de construcción de sub-dicts CIDSystemInfo / FontDescriptor / CIDFontType2 / Type 0 / Identity-H. Devuelve un THPDFDictionaryObject indirecto listo para pasar a SetFormUnicodeFontDict.
  • API: RegisterUnicodeFontDict(BaseFont, FontBBoxL/B/R/T, Ascent, Descent, CapHeight, ItalicAngle, StemV, Flags, DefaultWidth). Todos los argumentos de métrica tienen defaults sensatos (geometría de fuente embebida CJK / árabe: FontBBox [-1000 -300 2000 1300], Ascent 1000, Descent -300, CapHeight 750, StemV 100, Flags 4 = Symbolic, DW 1000). Sobrescribir individualmente para fuentes latinas / hebreas / cirílicas con bbox más pequeño y Flags no simbólicas = 32.
  • La estructura de dict construida coincide con el layout Type 0 + CIDFontType2 correcto según spec usado por smoke_acroform_cid_rtl / smoke_bidi_* a lo largo de v2.59-v2.69: CIDSystemInfo Adobe / Identity / Supplement 0, FontDescriptor con todas las entradas obligatorias 9.8.2, encoding Identity-H, descendiente CIDFontType2 en el array DescendantFonts, /DW para ancho de carácter por defecto. El /CIDToGIDMap por defecto es /Identity (CID == GID) cuando no se especifica, que es lo que implica el ordenamiento Identity-H + Adobe-Identity-0.
  • Una futura entrega v2.71+ añadirá un RegisterUnicodeTTF basado en archivos que parsea un .ttf / .otf, extrae métricas reales + anchos de avance hmtx /W para cálculos de perfect-wrap, embebe la fuente como /FontFile2, genera un CMap ToUnicode desde cmap, y llama a RegisterUnicodeFontDict internamente para las piezas estructurales.

2026-05-18 Version 2.69.0

  • Se amplió la tabla interna de lookup Unicode BIDI_Class para cubrir los bloques de escrituras right-to-left del Supplementary Multilingual Plane (SMP) en U+10800..U+10FFF. La detección de dirección de párrafo y el reorden L1-L2 ahora reconocen correctamente Phoenician, Imperial Aramaic, Palmyrene, Nabataean, Hatran, Lydian, Meroitic Hieroglyphs / Cursive, Kharoshthi, Old South Arabian, Old North Arabian, Manichaean, Avestan, Inscriptional Parthian / Pahlavi, Psalter Pahlavi, Old Turkic, Old Hungarian, Hanifi Rohingya (AL, la escritura en uso activo hoy para el idioma rohinyá en Birmania), Garay, Yezidi, Arabic Extended-C (AL), Old Sogdian, Sogdian, Old Uyghur, Chorasmian, y Elymaic.
  • Los párrafos RTL SMP ahora se auto-detectan como RTL vía DetectBidiParagraphDirection (el escaneo del primer carácter fuerte camina a través de pares surrogate y coincide con la clase R / AL apropiada) y ejecutan el reorden L1-L2 RTL de v2.66 para salida visual Tj UTF-16BE. Los párrafos LTR que contienen subcadenas RTL SMP embebidas adicionalmente invertirán esos runs cuando EnableLTRParaReorder esté fijado (ruta opt-in de v2.67); de lo contrario emiten en orden lógico y dependen del BIDI intrínseco del lector consumidor.
  • La preservación de pares surrogate continúa manteniéndose a través de todas las pasadas de inversión L2: los codepoints RTL SMP se quedan como pares high+low intactos en el stream hex /Tj mientras el párrafo circundante se reordena correctamente.
  • El smoke test de v2.67 (smoke_bidi_ltr_reorder) aserción #7 había usado U+10840 IMPERIAL ARAMAIC LETTER ALEPH para demostrar "codepoint SMP tratado como bcOther en párrafos LTR." Esa aserción se actualiza como parte de este release para reflejar la clasificación ahora correcta bcR: U+10840 en un párrafo LTR pasa a formar parte del run R/AL de nivel 1 y se reordena a posición visual junto a cualquier subcadena hebrea / árabe vecina.
  • Trabajo UAX #9 pendiente (herencia NSM W1 + transiciones de tipo weak W3-W7 + neutral N0-N2 + resolución de par-de-brackets + resolución de nivel explícito I1 + joining contextual GSUB OpenType árabe / siríaco) sigue por delante. Con la cobertura RTL SMP en su lugar, todas las escrituras RTL Unicode estandarizadas ahora clasifican correctamente para dirección de párrafo y reorden básico L1-L2.

2026-05-18 Version 2.68.0

  • Se corrigió el bug visible de dirección para dígitos embebidos en párrafos árabes / hebreos. Se añadieron entradas BIDI_Class de clase weak Unicode UAX #9 para dígitos europeos (EN, U+0030..U+0039) y rangos de dígitos arábigo-índicos (AN, U+0660..U+0669 + U+06F0..U+06F9), con asignación de nivel I2 + L2 UAX #9 simplificada: en un párrafo RTL estos codepoints de dígito ahora reciben nivel resuelto 2 (igual que los runs LTR embebidos) en lugar de nivel 1 junto al texto RTL circundante. El reorden L2 de dos pasadas entonces preserva su orden lógico dentro del flujo RTL visual, coincidiendo con la lectura correcta según spec del usuario "شارع 12345" incluso cuando el párrafo se renderiza LTR para el stream PDF /Tj.
  • Corrección concreta: una entrada "شار 12345" (UTF-16 lógico 0634 0627 0631 0020 0031 0032 0033 0034 0035) antes se emitía como el operando Tj 0035 0034 0033 0032 0031 0020 0631 0627 0634 (inversión de cadena completa - dígitos invertidos, el bug visible). Con la corrección v2.68 la misma entrada emite 0031 0032 0033 0034 0035 0020 0631 0627 0634 - los dígitos se quedan 1-2-3-4-5 en orden de lectura mientras la palabra árabe aún se renderiza en RTL visual.
  • DetectBidiParagraphDirection ahora trata EN y AN como weak según UAX #9 P2: los párrafos solo-de-dígitos o que empiezan por dígitos ya no anclan RTL en los dígitos; el primer carácter FUERTE (L / R / AL) sigue ganando. Entradas vacías / solo-weak siguen recurriendo a LTR según P3. La batería de smoke verifica que dígitos puros devuelven LTR mientras que dígitos-luego-árabe devuelve RTL (el AL árabe es el primer strong) y dígitos-luego-latín devuelve LTR (la L latina es el primer strong).
  • El rango del bloque árabe U+0600..U+06FF se dividió en la tabla de lookup para extraer los dos rangos de dígitos como bcAN; los U+0600..U+065F + U+066A..U+06EF + U+06FA..U+06FF circundantes se quedan en bcAL. Otros codepoints del bloque árabe (puntuación, signos de porcentaje / por mil, moneda, etc.) mantienen la clasificación bcAL por ahora; el manejo de clase weak más granular (ET / CS / ON / NSM) aterriza en un release futuro junto al resto del pipeline W1-W7 / N0-N2.
  • Entradas puramente RTL sin runs de dígitos son idénticas a nivel de byte a la salida de v2.66 / v2.67 (no se disparan transiciones de clase W). Los párrafos LTR no se ven afectados (los dígitos ya se renderizan LTR por dirección base del párrafo). La corrección se activa automáticamente para cualquier llamador existente que use AutoDetectFormBidi o FormUnicodeRTL con UseUAX9LevelReversal=True (defaults).
  • Alcance pendiente UAX #9 + GSUB (herencia NSM W1 + W3/W4/W5/W6/W7 completos + brackets N0-N2 + resolución de nivel explícito I1 + joining contextual GSUB OpenType árabe / siríaco + BIDI_Class de escrituras RTL SMP) sigue por delante.

2026-05-18 Version 2.67.0

  • Se añadió reordenamiento L2 Unicode UAX #9 del lado productor para párrafos LTR que contienen subcadenas RTL embebidas. La nueva propiedad THotPDF.EnableLTRParaReorder (por defecto False) hace opt-in a los llamadores en salida /AP estricta según spec / independiente del lector: un párrafo LTR con hebreo / árabe embebido se reordena en el momento de generación de /AP de modo que los lectores consumidores no necesiten ejecutar su propia pasada BIDI intrínseca sobre subcadenas RTL en orden lógico.
  • Algoritmo de pasada única simétrico al L1-L2 de párrafo RTL de v2.66.0: párrafo LTR nivel base 0; cada codepoint strong-R / strong-AL resuelve a nivel 1; todo lo demás se queda en nivel 0. El nivel impar más bajo UAX #9 L2 es 1, por lo que el bucle tiene una pasada: invertir cada subcadena maximal de nivel 1 (run R/AL contiguo). Los chars LTR y los chars weak / neutral se copian sin cambios. Los pares surrogate se preservan a través de la inversión.
  • El método público THotPDF.ApplyUAX9L2ReversalLTRPara expone el nuevo algoritmo de forma independiente (UTF-8 entrada, UTF-8 salida). Distinto de ApplyUAX9L2Reversal de v2.66.0: la ruta ParagraphRTL=False de ese método permanece como passthrough de v2.66 de modo que los llamadores que dependen del contrato de v2.66 ven salida idéntica a nivel de byte; usa el nuevo método para reorden de dirección LTR.
  • EnableLTRParaReorder por defecto False, preservando salida /AP idéntica a nivel de byte v2.59-v2.66 para llamadores de párrafo LTR. El toggle no tiene efecto cuando UseUAX9LevelReversal=False (la inversión ingenua de v2.59 nunca reordena párrafos LTR); los dos toggles son por lo demás independientes.
  • Las transiciones de tipo weak W1-W7 UAX #9 completas + neutrales N0-N2 + resolución de par de brackets + resolución de nivel explícito I1-I2 + shaping GSUB OpenType permanecen como alcance v2.68+.

2026-05-18 Version 2.66.0

  • Se añadió inversión consciente de runs L1-L2 Unicode UAX #9 para streams de apariencia Unicode AcroForm. La nueva propiedad THotPDF.UseUAX9LevelReversal (por defecto True) reemplaza la inversión ingenua de cadena completa de v2.59.0 para párrafos RTL con un reordenamiento L2 de dos pasadas correcto según spec que preserva el orden lógico interno de las subcadenas LTR embebidas dentro de párrafos RTL.
  • Corrección concreta: una entrada "<Hebrew>AB" (UTF-16 lógico 05D0 05D0 0041 0042) antes se emitía como el operando Tj hex 0042 0041 05D0 05D0 ("BAאא"), que invierte el "AB" LTR embebido - incorrecto según UAX #9. Con L1-L2 activo la misma entrada ahora emite 0041 0042 05D0 05D0 ("ABאא"). Las entradas puramente RTL sin runs L (p. ej. el smoke árabe pre-shaped de v2.59.0) permanecen idénticas a nivel de byte a la inversión ingenua heredada.
  • El método público THotPDF.ApplyUAX9L2Reversal expone el nuevo algoritmo como helper independiente (UTF-8 entrada, UTF-8 salida) para llamadores que construyen sus propios streams de apariencia o quieren previsualizar el reordenamiento antes de la generación de /AP.
  • Subconjunto simplificado de embedding único de UAX #9 I1 + L2 usado en esta entrega: en un párrafo RTL cada codepoint strong-L recibe nivel 2; todo lo demás (R, AL, weak, neutral, surrogate) se queda en nivel 1. L2 entonces invierte primero los runs de nivel 2 (restaurando orden lógico a las subcadenas LTR embebidas) y finalmente invierte el segmento de nivel 1 de cadena completa (produciendo el layout RTL visual). Los pares surrogate se preservan a través de ambas pasadas.
  • W1-W7 (transiciones de tipo weak) UAX #9 completos + N0-N2 (neutrales + par de brackets) + I1-I2 (resolución de nivel explícito) + reordenamiento para párrafos LTR con runs RTL embebidos permanecen como alcance v2.67+. v2.66.0 cubre el bug observable más común: párrafos RTL con subcadenas LTR multi-carácter.
  • UseUAX9LevelReversal por defecto True. Establecer en False para volver a la inversión ingenua de cadena completa de v2.59.0 para salida heredada idéntica a nivel de byte. Ambas formas continúan leyendo FormUnicodeRTL y AutoDetectFormBidi de la misma manera.

2026-05-18 Version 2.65.0

  • Se añadió la auto-detección de dirección de párrafo Unicode Standard Annex #9 para streams de apariencia Unicode AP AcroForm. La nueva propiedad THotPDF.AutoDetectFormBidi conmuta los helpers BuildUnicode*FieldContent (single-line, multi-line, comb) del toggle manual FormUnicodeRTL de v2.59.0 a la detección por llamada de la dirección del párrafo. Con esto, la misma secuencia de setters maneja un lote mixto de campos LTR y RTL en un documento sin tener que conmutar flags manualmente.
  • La dirección se resuelve escaneando la entrada en busca del primer carácter fuerte (reglas UAX #9 P2 + P3) y clasificándolo con BIDI_Class L (Left), R (Right-to-Left), o AL (Arabic Letter). Strong R o AL ancla el párrafo como RTL; strong L ancla LTR; un párrafo sin carácter fuerte recurre a LTR según P3.
  • BIDI_Class reconocidas: Strong L - latín ASCII, Latin-1, Latin Extended, griego, copto, cirílico, armenio, CJK Unified Ideographs, hangul, hiragana, katakana, yi. Strong R - bloque hebreo (U+0590..U+05FF), formas de presentación hebreas (U+FB1D..U+FB4F), N'Ko, samaritano, mandaico. Strong AL - bloque árabe, Arabic Supplement, Arabic Extended-A, siríaco, thaana, formas de presentación árabes A + B (U+FB50..U+FDFF + U+FE70..U+FEFF). Pares surrogate y caracteres weak / neutral (dígitos, puntuación, whitespace) se omiten durante la búsqueda.
  • El método público THotPDF.DetectBidiParagraphDirection expone el detector para llamadores que quieran pre-computar la dirección (p. ej. para tagging /Lang, salida neutral espejada, enrutamiento de campos personalizado). Acepta un AnsiString UTF-8 y devuelve Boolean (True = RTL). Entrada vacía devuelve False según P3.
  • AutoDetectFormBidi por defecto es False; el comportamiento idéntico a nivel de byte v2.59.0-v2.64.0 existente se preserva. Cuando es True, los helpers ignoran FormUnicodeRTL por llamada. Desactiva AutoDetectFormBidi para restaurar el control manual.
  • La resolución completa UAX #9 de nivel implícito + tipo weak + neutral-bracket (reglas W1-W7, N0-N2, I1-I2, L1-L4) y el joining contextual GSUB OpenType árabe / siríaco automático siguen programados para v2.66+. Los párrafos bidi mixtos aún necesitan intervención manual del llamador más allá de la dirección a nivel de párrafo.

2026-05-18 Version 2.64.0

  • Se añadió la entrada de pantalla halftone (/HT) PDF 1.2 ISO 32000-1 10.5.5 ExtGState más el constructor de diccionario halftone Type 1 spot-function. El nuevo THotPDF.RegisterHalftoneType1 construye el dict halftone Type 1 PDF 1.7 Table 76 para screening de separación CMYK controlada por el llamador (frecuencia en líneas por pulgada, ángulo de pantalla en grados, forma del spot, accurate screening opcional, transfer function, nombre descriptivo de halftone). Combínalo con THotPDF.RegisterHalftoneState para envolver esa referencia de dict en una entrada /HT ExtGState para uso con THPDFPage.SetGraphicsState.
  • /SpotFunction acepta ya sea un nombre de función de color predefinido según la tabla 78 de PDF 1.7 (SimpleDot, InvertedSimpleDot, DoubleDot, InvertedDoubleDot, CosineDot, Double, InvertedDouble, Line, LineX, LineY, Round, Ellipse, EllipseA, InvertedEllipseA, EllipseB, EllipseC, InvertedEllipseC, Square, Cross, Rhomboid, DiamondRhomboid) o una referencia a un objeto de función de color construido por el llamador. Los nombres predefinidos desconocidos generan un error con la lista completa de especificaciones mencionada en el mensaje. Spot-name y Function son mutuamente excluyentes.
  • /TransferFunction es opcional. Pasa una referencia de dict Function para reproducción de tono controlada por el llamador, o establece TransferFunctionDefault = True para emitir /TransferFunction /Identity (default del lector consumidor). Las dos formas son mutuamente excluyentes.
  • RegisterHalftoneState acepta o una referencia de dict halftone (típicamente el valor de retorno de RegisterHalftoneType1) o HTDefaultName = True para la ruta de reset name-literal /HT /Default que restaura el halftone incorporado del lector consumidor.
  • Todas las emisiones suben automáticamente la versión del documento a PDF 1.2. Frequency debe ser > 0; los nombres de spot-function inválidos lanzan antes de que el dict se asigne.
  • Tipos de halftone pendientes programados para v2.65+: Type 5 multi-componente, halftones de threshold-stream Type 6 / 10 / 16. Con Type 1 en su lugar el candidato "ExtGState entries" de la matriz está a una entrega de cobertura completa.

2026-05-18 Version 2.63.0

  • Se añadieron las entradas ExtGState de soft-mask + alpha-is-shape PDF 1.4 ISO 32000-1 11.6.5 + 11.7.4. El nuevo THotPDF.RegisterSoftMaskState empaqueta los parámetros de estado gráfico relacionados con transparencia /SMask y /AIS en un dict ExtGState, completando la cadena de cobertura de entradas ExtGState v2.20.0 + v2.60.0 + v2.61.0 + v2.62.0 + v2.63.0 para cada entrada definida por spec excepto la pantalla halftone /HT de múltiples tipos (programada para v2.64+).
  • /SMask lleva o el name-literal /None (el llamador pasa SMaskNone = True, patrón común de reset tras un composite complejo) o una referencia indirecta de dict soft-mask (el llamador pasa SMaskDict, caso de uso avanzado). Las dos formas son mutuamente excluyentes. El llamador es responsable del contenido del dict soft-mask y del Form XObject de transparencia subyacente; v2.64+ añadirá un helper builder dedicado.
  • /AIS controla si los valores alpha actuales se interpretan como alpha (false) o como shape (true) para el resto del estado gráfico. El default sentinela AIS = -1 omite la entrada /AIS por completo. Pasar AIS = 0 o 1 emite /AIS false o /AIS true. Cualquier otro valor de AIS lanza con un mensaje descriptivo.
  • Al menos uno de /SMask (None o Dict) o /AIS debe ser suministrado; una llamada con todos los defaults lanza. Cualquier emisión de entrada sube automáticamente el documento a PDF 1.4.
  • Devuelve el nombre ExtGState auto-generado (GS1, GS2, ...) para uso con THPDFPage.SetGraphicsState. Distinto de AddImageWithSMask de v2.42.0: esa ruta es alpha a nivel de imagen en un único XObject; esta es la máscara a nivel de ExtGState que afecta a cada operador de pintado posterior.
  • Se ha programado una entrada ExtGState pendiente para la versión v2.64 o posterior: pantalla de medio tono /HT (Type 1 / 5 / 6 / 10 / 16).

2026-05-18 Version 2.62.0

  • Se añadieron las entradas ExtGState de generación-de-negro + eliminación-de-undercolor PDF 1.2/1.3+ ISO 32000-1 11.7.5.4. El nuevo THotPDF.RegisterBlackGenerationState empaqueta los cuatro parámetros de estado gráfico que referencian Function /BG, /BG2, /UCR, /UCR2 en un dict ExtGState para flujos de trabajo de preprensa CMYK donde la conversión RGB-a-CMYK ocurre en tiempo de rasterizado (típico de producción de periódicos / packaging / wide-format donde las decisiones de aplicación de tinta son específicas del dispositivo de salida).
  • /BG y /UCR (PDF 1.2) son dicts Function de 1 entrada / 1 salida; /BG2 y /UCR2 (PDF 1.3+) son o dicts Function o el nombre literal /Default que restaura la curva por defecto del visor consumidor. El setter acepta un THPDFObject para cada slot Function (típicamente construido a través de RegisterSampledFunction de v2.52.0) y dos flags Boolean BG2DefaultName / UCR2DefaultName para la forma name-literal /Default. Mezclar un dict Function y el flag Default para el mismo slot /BG2 o /UCR2 lanza con un mensaje descriptivo.
  • Al menos uno de /BG, /BG2, /UCR, /UCR2 (Function o Default) debe ser suministrado; una llamada con todos los defaults lanza. Sube automáticamente a PDF 1.3 cuando se emite cualquier entrada /BG2 / /UCR2 (Function o Default).
  • Devuelve el nombre ExtGState auto-generado (GS1, GS2, ...) para uso con THPDFPage.SetGraphicsState. Combina naturalmente con RegisterTransferFunctionState de v2.60.0 (curvas de salida por canal) - una cadena de producción CMYK típica registra un ExtGState BG/UCR para conversión RGB -> CMYK más un ExtGState /TR para la reproducción de tono final en el dispositivo de salida.
  • Entradas ExtGState pendientes programadas para v2.63+: pantalla halftone /HT (Type 1 / 5 / 6 / 10 / 16) y máscara suave /SMask + /AIS a nivel de ExtGState.

2026-05-17 Version 2.61.0

  • Se añadieron las entradas ExtGState de control de impresión PDF 1.2/1.3+ ISO 32000-1 8.6.5.7 + 8.6.5.8 + 11.7.4. El nuevo THotPDF.RegisterPrintControlState empaqueta los parámetros de estado gráfico orientados a preprensa /OP, /op, /OPM, /SA, /RI y /FL en un dict ExtGState, adecuado para flujos de trabajo de producción PDF/X-1/X-3/X-4 que necesitan controles de overprint, stroke adjustment, rendering intent y curve flattening.
  • Los seis parámetros son independientemente opcionales. Defaults sentinela: Overprint = -1 (omite /OP + /op), OverprintMode = -1 (omite /OPM), StrokeAdjustment = -1 (omite /SA), RenderingIntent = '' (omite /RI), Flatness < 0 (omite /FL). El llamador debe suministrar al menos una entrada; dicts vacíos lanzan.
  • Overprint establece /OP y /op juntos (la spec PDF por defecto /op a /OP cuando solo el último se establece, pero los pipelines PDF/X estrictos según spec esperan ambas entradas explícitas). OverprintMode = 1 habilita el overprint consciente de CMYK PDF 1.3 donde cualquier componente cero pinta transparente; sube automáticamente la versión del documento a 1.3.
  • RenderingIntent acepta los cuatro valores estándar 'AbsoluteColorimetric' / 'RelativeColorimetric' / 'Saturation' / 'Perceptual'; cualquier otra cadena no vacía lanza. Flatness se acota al rango de spec [0.0..100.0]; fuera de rango lanza.
  • Devuelve el nombre ExtGState auto-generado (GS1, GS2, ...) para uso con THPDFPage.SetGraphicsState. Combina naturalmente con RegisterExtGState de v2.20.0 (transparencia / blend mode) y RegisterTransferFunctionState de v2.60.0 (curvas de salida) - el setup PDF/X típico encadena uno de cada.
  • Las entradas dirigidas por Function (/BG, /UCR, /BG2, /UCR2 generación de negro + eliminación de undercolor) y la compleja pantalla halftone /HT + soft mask /SMask a nivel de ExtGState permanecen como alcance futuro.

2026-05-17 Version 2.60.0

  • Se añadió soporte ExtGState /TR (transfer function) PDF 1.3+ ISO 32000-1 11.7.3.4. El nuevo método THotPDF.RegisterTransferFunctionState registra un dict ExtGState que lleva una entrada /TR que aplica una curva de reproducción de tono a la salida a medida que los colores se rasterizan. La curva se suministra como un Function Type 0 Sampled LUT de v2.52.0 (o cualquier otro dict Function que el llamador haya construido); el envoltorio se empareja naturalmente con la primitiva existente RegisterSampledFunction para curvas de gamma / brillo / caracterización de prensa ajustadas a mano sin pasar por la maquinaria de perfiles ICC.
  • Dos rutas de invocación:
  • SingleFunc: una función aplicada a cada canal de salida (casos de uso de gamma/brillo).
  • Forma de array PerChannel: un array de 4 elementos [TR_R, TR_G, TR_B, TR_A] para pipelines RGB-A o [TR_C, TR_M, TR_Y, TR_K] para curvas de reproducción CMYK; los cuatro slots deben ser no-nil según spec PDF.
  • Devuelve el nombre ExtGState auto-generado (GS1, GS2, ...) para uso con THPDFPage.SetGraphicsState. Aplica el estado antes de los operadores de dibujo (texto / paths / imágenes) que deban recibir el tratamiento de transferencia.
  • Llamadores mixtos (SingleFunc no-nil + PerChannel no-vacío) lanzan una excepción para que la exclusión mutua exigida por spec se haga cumplir en la superficie de la API.

2026-05-17 Version 2.59.0

  • Se añadió soporte de dirección right-to-left para /AP Unicode AcroForm (PDF 1.7 ISO 32000-1 12.7.4.3). Nueva propiedad booleana THotPDF.FormUnicodeRTL; cuando es true, los tres helpers Unicode /AP (single-line, multi-line, comb) invierten el orden de unidad de código UTF-16 en sus operandos hex Tj emitidos de modo que el contenido árabe / hebreo / siríaco pre-shaped se renderiza en orden visual right-to-left. Los pares surrogate UTF-16 se mantienen como una unidad durante la inversión (high se queda adyacente a low en orden original de modo que los codepoints SMP como letterforms de escrituras antiguas se muestren correctamente).
  • Alcance: v2.59.0 maneja solo el paso de inversión direccional. El llamador es responsable de dos piezas upstream que v2.59.0 deliberadamente NO realiza: (1) resolución completa del algoritmo bidireccional UAX #9 (contenido mixto LTR / RTL / numérico / neutral), y (2) joining contextual árabe / siríaco (selección de forma de glifo Initial / Medial / Final / Isolated). Los llamadores deben pre-shape el árabe a sus formas de presentación Unicode (U+FB50..U+FDFF + U+FE70..U+FEFF) antes de pasar a AddTextField. El hebreo no tiene joining contextual así que funciona sin pre-shaping.
  • /V (orden lógico Unicode) no cambia: las cadenas de texto PDF siempre llevan el orden tipeado de modo que los extractores de texto que siguen la spec ven la secuencia lógica correcta. Solo los operandos /AP Tj cambian orden de modo que el renderizador dibuja visualmente right-to-left. Con este release la cadena /AP internacional AcroForm v2.55.0-v2.59.0 está completa: /RV rich text, /AP de fuente CID single-line / multi-line / comb, e inversión direccional RTL pre-shaped. UAX #9 completo + shaping GSUB automático permanecen como alcance v2.60.0+.

2026-05-17 Version 2.58.0

  • Se amplió la cadena /AP de fuente Unicode suministrada por el llamador de v2.56.0 / v2.57.0 a la rama ffComb (PDF 1.7 ISO 32000-1 12.7.4.3 Tx /Ff bit 25). Cuando SetFormUnicodeFontDict ha registrado una fuente compuesta Type 0 y un widget Tx se añade con ffComb + un valor inicial no-ASCII, HotPDF ahora emite un stream de apariencia /AP /N real que coloca un codepoint CJK por celda comb de ancho igual usando una matriz Tm absoluta por celda y operandos Tj de cadena hex UTF-16BE.
  • Los pares surrogate UTF-16 (codepoints >= U+10000, p. ej. emoji e ideogramas suplementarios CJK Extension B/C/D) ocupan una sola celda comb, coincidiendo con cómo Acrobat dispone celdas comb de fuente compuesta de un solo glifo. La fórmula de centrado de celda es idéntica a la rama comb ASCII de v2.46.0 de modo que la salida visual refleja el layout de un solo carácter ASCII.
  • Los campos comb ASCII se quedan idénticos a nivel de byte a la salida de v2.46.0. El comb no-ASCII sin SetFormUnicodeFontDict sigue recurriendo al placeholder de AP vacío de v2.46.0 (sin cambios). Con este release la cadena /AP internacional AcroForm v2.55.x-v2.58.0 (single-line, multi-line, comb) está completa; el shaping bidi RTL (UAX #9 + joining contextual árabe) permanece como alcance v2.59.0+.

2026-05-17 Version 2.57.0

  • Se amplió la ruta /AP de fuente Unicode suministrada por el llamador de v2.56.0 a campos de texto multi-línea (PDF 1.7 ISO 32000-1 12.7.4.3 ffMultiline / Tx /Ff bit 13). Cuando SetFormUnicodeFontDict ha registrado una fuente compuesta Type 0 y un widget Tx se añade con ffMultiline + un valor inicial no-ASCII, HotPDF ahora hace word-wrap del texto UTF-16 y emite un Tj de cadena hex UTF-16BE por línea visible a través de Td/T* con /TL leading (coincidiendo con el layout multi-línea ASCII de v2.46.0).
  • El presupuesto de word-wrap usa una heurística simple de avance - 1 em por unidad de código CJK / ancha (>= U+2E80) y 0.5 em por unidad de código estrecha (rango ASCII y puntuación Latin-1) - de modo que el contenido mixto latín / CJK hace wrap sensatamente sin leer el array /W de la fuente. Los saltos de línea duros (CR / LF / CRLF) se honran. Las líneas que exceden el rectángulo del widget se truncan al recuento de filas que cabe en el tamaño de fuente actual; este es el mismo comportamiento de truncado que la rama multi-línea ASCII.
  • Multi-línea ASCII, single-line ASCII, comb ASCII, y las rutas single-line no-ASCII de v2.56.0 todas se quedan idénticas a nivel de byte. La ruta comb no-ASCII (ffComb + texto multi-byte) sigue cayendo al helper single-line de v2.56.0 por ahora; el layout Unicode específico de ffComb es alcance v2.58.0+, igual que el shaping bidi RTL (UAX #9 + joining contextual árabe).

2026-05-17 Version 2.56.0

  • Se añadió soporte de fuente Unicode suministrada por el llamador para renderizado de stream de apariencia /AP AcroForm (PDF 1.7 ISO 32000-1 12.7.2 + 12.7.4.3). Antes de este release, la ruta GenerateTextFieldAP emitía un placeholder /AP vacío siempre que el valor inicial del campo contuviera cualquier byte >= 0x80, de modo que el texto no-ASCII solo podía renderizarse a través de regeneración /NeedAppearances=true (dependiendo de las fuentes instaladas en el lector) o a través de la ruta de rich-text /RV de v2.55.0 (dependiendo del motor de rich-text del lector). v2.56.0 cierra esa brecha: los llamadores pueden registrar un dict de fuente compuesta Type 0 / CIDFontType2 + Identity-H a través de SetFormUnicodeFontDict y HotPDF emite un stream de apariencia /AP /N real que selecciona la fuente registrada vía Tf y renderiza el valor como un operador Tj de cadena hex UTF-16BE.
  • Nuevo THotPDF.SetFormUnicodeFontDict(LogicalName, FontDict): registra un par nombre lógico de fuente + dict de fuente. Una vez registrado, el /DA a nivel de AcroForm, el /DA de cada widget Tx, y el stream /AP para valores iniciales Tx no-ASCII todos conmutan al nombre lógico; el dict /DR/Font de AcroForm lleva la fuente junto con /Helv y /ZaDb; el sub-dict /Resources/Font del Form XObject también referencia la misma fuente indirecta de modo que el AP es autocontenido sin depender de la resolución /DR en tiempo de renderizado. Pasa empty + nil para volver al comportamiento solo-/Helv de v2.46.0.
  • Nuevo THotPDF.CreateIndirectFontDict: helper público que asigna un THPDFDictionaryObject vacío fresco registrado como objeto PDF indirecto de modo que los llamadores puedan construir dicts personalizados de fuente / color / OCG / Function que deben serializar como referencias "N G R". Se empareja naturalmente con SetFormUnicodeFontDict para construir dicts de fuente compuesta Type 0.
  • Los campos Tx ASCII continúan usando /Helv y emiten salida /AP idéntica a nivel de byte a los llamadores de v2.46.0 / v2.55.0. Tx multi-byte sin SetFormUnicodeFontDict sigue recurriendo al comportamiento de placeholder /AP vacío de v2.46.0 (preservado verbatim) por compatibilidad.
  • La cobertura /AP no-ASCII multi-línea + comb y el shaping bidi RTL (UAX #9 + joining contextual árabe) permanecen como alcance v2.57.0+; v2.56.0 cubre single-line en orden lógico CJK / cirílico / vietnamita / griego y escrituras similares.

2026-05-17 Version 2.55.0

  • Se añadió soporte para widgets Tx de rich-text AcroForm (PDF 1.7 ISO 32000-1 12.7.4.3 + Annex L). El nuevo método THPDFPage.AddRichTextField emite un widget de texto que lleva /RV (cuerpo XHTML de rich-text), /DS (estilo por defecto tipo CSS), el flag ffRichText (/Ff bit 26), y el fallback /V + /DV de texto plano usado por lectores que no parsean /RV. Acrobat y Foxit renderizan directamente desde /RV cuando el bit RichText está on, recogiendo fallbacks de fuente de las fuentes Unicode instaladas del lector - de modo que el contenido multi-byte (CJK / cirílico / árabe / latín acentuado) se muestra correctamente sin que HotPDF embeba una fuente CID en /DR Resources. El embebido de una fuente CID para el propio generador de stream de apariencia (/AP) de HotPDF permanece como trabajo futuro; los visores conscientes de rich-text no lo necesitan.
  • Tanto /V como /RV auto-detectan entrada multi-byte y conmutan a codificación de cadena hex UTF-16BE (BOM FE FF + unidades de código big-endian) cuando es necesario, coincidiendo con el comportamiento existente de AddTextField para campos de texto plano internacionales. El contenido ASCII se queda como cadenas literales PDF para salida idéntica a nivel de byte a la ruta de emit de texto plano de v2.46.0.
  • Quedan tres seguimientos en el candidato AcroForm de caracteres internacionales: (1) ruta /DR Resources de fuente CID de modo que los streams /AP generados por HotPDF cubran no-ASCII sin depender del motor de rich-text del lector, (2) shaping bidi RTL para rich text árabe / hebreo, y (3) el helper de acción /AA /K keystroke.

2026-05-17 Version 2.54.0

  • Se amplió Function Type 0 (Sampled) PDF 1.7 ISO 32000-1 7.10.2 con interpolación de spline cúbico. RegisterSampledFunction gana un nuevo parámetro opcional Order (por defecto 1, valores aceptados 1 o 3 según PDF 1.7 Table 38). Order = 3 emite interpolación cúbica a través de cada eje de entrada; Order = 1 mantiene el comportamiento lineal existente exactamente idéntico a nivel de byte de modo que los llamadores v2.52.0 / v2.53.0 no ven cambio en la superficie.
  • Se añadió cobertura de regresión end-to-end para la ruta de código Function Type 0 multi-entrada / multi-salida. Un nuevo smoke registra una función sampled de 2 entradas / 3 salidas en una rejilla 4 x 4 con interpolación cúbica y ejercita la regla de orden de muestras de la spec ("las coordenadas de entrada aumentan más rápido a lo largo de la primera dimensión de entrada"), comparando byte a byte los 16 puntos de rejilla x 3 canales contra una fórmula analítica re-derivada en el verificador Python.
  • Acrobat / Foxit / qpdf todos soportan Order = 3; algunos visores móviles más antiguos recurren silenciosamente a interpolación lineal cuando encuentran un valor Order desconocido. El cúbico permanece opt-in para preservar máxima compatibilidad con lectores.

2026-05-17 Version 2.53.0

  • Se añadió un registrador de espacio de color Separation de alto nivel que usa un Function Type 0 (LUT) sampled como su tint transform (PDF 1.7 ISO 32000-1 8.6.6.4 + 7.10.2). El nuevo THotPDF.RegisterSeparationLUT (ColorantName, AlternateCS, Samples) se construye sobre la primitiva RegisterSampledFunction de v2.52.0 para que los llamadores puedan expresar una curva de tint no lineal completa como un stream de bytes plano de muestras (M-tuplas) sin construir manualmente un diccionario Function Type 0.
  • Usa la nueva variante LUT cuando la transición de color es no lineal (rampas de tint estilo PANTONE Hexachrome, curvas de densidad corregidas por gamma, curvas de tono dibujadas a mano que coinciden con una caracterización de prensa, LUTs ICC de conversión sRGB-a-spot sin la maquinaria completa de perfiles ICC). Usa el RegisterSeparation de v2.45.0 cuando una rampa lineal de 0 a un endpoint de fuerza completa es suficiente (el caso común para colores spot PANTONE de tinta única).
  • Las muestras son bytes de 8 bits RGB / CMYK / Gray ordenadas (c0_0, c1_0, ..., cM-1_0, c0_1, ..., cM-1_S-1) donde S es el recuento de puntos de rejilla derivado como Length(Samples) / M. S debe ser al menos 2 para que la función tenga rango interpolable. La interpolación lineal entre puntos de rejilla es el default (PDF 1.7 /Order 1). Devuelve el nombre de espacio de color registrado (Sep1, Sep2, ...) para uso con THPDFPage.SetFillColorSpace / SetStrokeColorSpace + SetFillColor([tint]) donde 0 <= tint <= 1.
  • Internamente se ampliaron THPDFSeparationParams.TintFunc de THPDFDictionaryObject a la base común THPDFObject para que pueda contener o un Function Type 2 (ruta v2.45.0) o un stream Function Type 0 (ruta v2.53.0); ambas formas se serializan idénticamente a través de THPDFArrayObject.AddObject como referencia indirecta "N G R" dentro del array /Separation.

2026-05-17 Version 2.52.0

  • Se añadió el registro de Function Type 0 (Sampled) PDF 1.7 ISO 32000-1 7.10.2. Las funciones sampled expresan un mapeo arbitrario entrada-a-salida como una rejilla regular N-dimensional de muestras de salida M-dimensionales; el renderizador interpola linealmente entre puntos de rejilla para evaluar entradas intermedias. Usa Type 0 donde la maquinaria de perfiles ICC es excesiva pero se desea una LUT de color ajustada a mano (curvas de tono para /TransferFunction en ExtGState, tint transforms sampled para /Separation / /DeviceN más allá de las rutas Type 2 lineal / Type 4 aritmética ya soportadas, curvas de threshold de halftone, o cualquier otra construcción PDF que tome un diccionario Function).
  • El nuevo THotPDF.RegisterSampledFunction(Domain, Range, Size, BitsPerSample, Samples) emite un stream indirecto /FunctionType 0 con /Domain (2N), /Range (2M), /Size (N), /BitsPerSample (1, 2, 4, 8, 12, 16, 24, o 32), /Order 1 (interpolación lineal), y el payload de muestras crudo empaquetado en bits. Devuelve el THPDFStreamObject indirecto para que los llamadores puedan adjuntarlo dondequiera que se espere un diccionario Function.
  • Se valida la longitud del array de bytes Samples contra ceil(GridPoints * M * BitsPerSample / 8); los anchos BitsPerSample de 8/16/24/32 son big-endian y están alineados a byte, mientras que los anchos sub-byte (1/2/4/12) siguen el empaquetado MSB-first especificado en PDF 1.7 §7.10.2, y el byte final se rellena con ceros.
  • Function Type 3 (stitching) y Type 4 (calculadora PostScript) han sido usables internamente a través de HPDFRegisterFunctionType3 / HPDFRegisterFunctionType4 desde v2.18.0 / v2.47.0; este release lleva Type 0 al mismo nivel de soporte de API pública.

2026-05-17 Version 2.51.6

  • Se cerró el barrido de seguridad binaria de v2.51.x arreglando el último sitio TStringList "Source=Target" restante de round-trip: FStructRoleMap (PDF 1.7 ISO 32000-1 14.7.3 /StructTreeRoot /RoleMap). Los nombres de rol struct personalizados registrados a través de AddStructRoleMap ya no se mutilan silenciosamente en hosts Windows con CP_ACP=65001 (opción de región "Beta: Use Unicode UTF-8"); el almacenamiento se migró a un array dinámico de records (Source: TBytes; Target: TBytes), con Move() preservador de bytes en cada acceso de inserción/lectura. Los tipos struct estándar de PDF (Document, P, H1..H6, Span, Div, etc.) son ASCII según spec por lo que los releases anteriores funcionaban en la práctica; esta corrección solo importa para llamadores que mapean nombres de rol Latin-1 específicos de herramienta de autoría sobre los tipos estándar.
  • Se amplió el escape #XX de PDF Name (PDF 1.7 7.3.5) a las CLAVES de diccionario en SaveDictionaryObject. Antes las claves de dict se copiaban en bruto al stream de salida, lo que dejaba cualquier byte >= 0x80 sin escapar y producía salida no conforme a spec - alcanzable vía AddStructRoleMap con un rol fuente no-ASCII. La lógica de trim a nivel de byte + escape #XX está ahora en un helper compartido (_EscapePDFNameBytes) llamado desde tanto SaveNameObject (valores de nombre) como SaveDictionaryObject (claves de dict). Las claves ASCII (el caso universal para los dicts generados por HotPDF) se escapan a salida idéntica a nivel de byte, por lo que los archivos y tests existentes no se ven afectados.
  • Tras este release la cadena de seguridad binaria v2.51.x (destinatarios PubSec, ColorantNames DeviceN, emisión de valor PDF Name, emisión de clave PDF Name, almacenamiento de struct role map) está estructuralmente completa - no queda round-trip AnsiString-vía-UnicodeString en ningún blob de bytes conocido en HotPDF. 34/34 smokes + 27/27 verificadores pasan.

2026-05-17 Version 2.51.5

  • Se continuó el barrido de seguridad binaria de v2.51.4 a través de dos rutas de emisión más que podrían mutilar silenciosamente bytes no-ASCII en hosts donde Windows CP_ACP=65001 ("Beta: Use Unicode UTF-8 for worldwide language support") está activo.
  • Los nombres de colorante DeviceN ahora se almacenan como un array dinámico de TBytes dentro de THPDFDeviceNParams (antes un TStringList de entradas UnicodeString). La representación anterior forzaba un round-trip AnsiString -> UnicodeString -> AnsiString en cada nombre de tinta, lo que en hosts ANSI UTF-8 reescribía cualquier byte >= 0x80 como la secuencia de reemplazo de 3 bytes EF BF BD. Los nombres de tinta solo-ASCII (nombres estilo PANTONE / Hexachrome y "Red" / "Blue") no se veían afectados; los nombres de tinta personalizados no-ASCII (p. ej. nombres de spot-ink acentuados Latin-1 en flujos de impresión no anglosajones) se corrompían silenciosamente antes de este release.
  • La ruta de emisión de PDF Name (SaveNameObject) ahora es byte-safe y conforme a spec. La implementación anterior llamaba AnsiString(Trim(String(ValObject.Value))) que realizaba el mismo round-trip destructivo en cada nombre PDF escrito a disco. Los nombres ahora se hacen trim por comparación de bytes ("char <= 0x20" coincidiendo con la semántica Trim de Delphi) y cualquier byte fuera del rango de nombre regular 0x21..0x7E se emite con el escape #XX exigido por spec según PDF 1.7 ISO 32000-1 7.3.5. Los nombres ASCII sin whitespace inicial / final y sin bytes especiales embebidos serializan a salida idéntica a bit como antes, por lo que los archivos y tests existentes no se ven afectados.
  • Un nuevo verificador de round-trip (smoke_devicen_binary_safe) registra una tinta DeviceN cuyo nombre contiene un byte 0xA0 embebido y afirma que el array /Names emitido lleva el escape "#A0" conforme a spec en lugar de la secuencia corruptiva "#EF#BF#BD". Con este release la cadena de seguridad binaria v2.51.x (destinatarios PubSec, nombres de tinta DeviceN, emisión genérica de PDF Name) está cerrada; 33/33 smokes + 26/26 verificadores pasan.

2026-05-17 Version 2.51.4

  • Se corrigió la corrupción silenciosa de bytes de envoltura de destinatario en el Public-Key Security Handler (PDF 1.7 ISO 32000-1 7.6.5). Cuando el sistema host tiene habilitado Windows "Beta: Use Unicode UTF-8 for worldwide language support" (CP_ACP=65001), el almacenamiento basado en TStringList previo de blobs envelopedData PKCS#7 forzaba un round-trip AnsiString -> UnicodeString -> AnsiString que reescribía cada byte no-ASCII como la secuencia de reemplazo UTF-8 (EF BF BD). La corrupción se propagaba idénticamente tanto en el cómputo de la file-key SHA-1 (algoritmo 9) como en las cadenas hex /Recipients emitidas, por lo que el PDF producido aún se desencriptaría con la propia clave corrupta de HotPDF, pero ningún lector de terceros podría derivar la misma clave a partir de los bytes de envoltura originales - significando que el archivo encriptado era efectivamente solo legible por el propio HotPDF en el mismo sistema CP_ACP-bloqueado.
  • La corrección reemplaza el almacén de destinatarios con un array dinámico de TBytes; la firma pública de la API AddPubKeyRecipient se queda igual. Los bytes de envoltura ahora se copian verbatim vía Move(); no queda ruta de conversión de codepage.
  • El hex /Recipients ahora reproduce las envolturas suministradas byte a byte independientemente del locale del host; Acrobat / Foxit / qpdf y cualquier lector consciente de PKCS#7 puede recuperar la clave de encriptación del archivo como la spec define.
  • Un nuevo verificador de round-trip (smoke_pubsec_verify) se añadió en v2.33.0 y había estado fallando este build desde entonces; v2.51.4 lo lleva de FAIL a OK. Con este release toda la cadena de auditoría v2.51.x (auditoría R=5, auditoría R=6 user-pw, fix R=6 owner-pw K0, fix de byte de destinatario PubSec) está cerrada y 25/25 verificadores de smoke pasan limpiamente por primera vez desde v2.33.0.

2026-05-16 Version 2.51.3

  • Se corrigió el hash de validación de owner-password AES-256 V=5 R=6 para que se ajuste al ISO 32000-2 Algorithm 2.B tal como está escrito. El hash dance PDF 2.0 prescribe el estado K inicial como SHA-256(password || salt || udata), donde udata para las derivaciones owner-password es la entrada /U completa de 48 bytes. Los releases previos de HotPDF (v2.22.0 a v2.51.2) doblaban udata solo en el paso iterativo K1 y lo omitían del K inicial, produciendo un hash /O[0:32] no spec que los lectores estilo Acrobat / Foxit / qpdf rechazarían en el momento de validación del owner-password. La desencriptación user-password no se veía afectada porque su hash dance se llama con un argumento udata vacío y es por lo tanto idéntico a bit con o sin la corrección.
  • Acrobat / Foxit / qpdf ahora validan el owner password contra archivos R=6 emitidos por HotPDF. Los valores /O[0:32] y /OE producidos antes de v2.51.3 permanecen legibles para desencriptación user-password pero no pueden desbloquearse a través de su owner password por lectores que siguen la spec; regenera los archivos R=6 afectados con v2.51.3 si se necesita validación owner-password externamente.
  • Las rutas /U / /UE / /Perms / V=5 R=5 / V=4 son idénticas a nivel de byte a la salida pre-v2.51.3; la corrección está acotada al SHA-256 K inicial dentro de PDF20HashDanceR6 y solo cambia la salida cuando se llama con un UEntry no vacío (el sitio de llamada del owner-password en CreateKeysV5).
  • Una nueva auditoría de round-trip (smoke_aes256_r6_owner_verify) deriva /O[0:32] a través tanto del algoritmo pre-fix como del spec; afirma que el /O[0:32] emitido coincide con la variante spec y que la variante pre-fix ya no reproduce la salida.

2026-05-16 Version 2.51.2

  • Se amplió la auditoría /Perms de v2.51.1 para cubrir la ruta del standard security handler V=5 R=6 ("hash dance") (ISO 32000-2 7.6.4.3.4 / Algorithm 2.B). Un nuevo verificador de round-trip reimplementa el mezclador iterativo SHA-256 / SHA-384 / SHA-512 + AES-128-CBC en Python puro, deriva la clave de encriptación de archivo de 32 bytes a partir del user password vacío, desencripta con AES-256-ECB el bloque /Perms de 16 bytes, y afirma cada campo contra los valores del encrypt-dict (/P little-endian, relleno 0xFF, etiqueta 'T' / 'F', literal 'a' 'd' 'b'). Sin cambio de comportamiento; este release es cobertura de regresión pura para la ruta R=6 coincidiendo con el lock-down R=5 aterrizado en v2.51.1.

2026-05-16 Version 2.51.1

  • Se bloqueó el binding /P -> /Perms del standard security handler V=5 R=5 en la suite de regresión. La ruta de encriptación AES-256 emite un bloque /Perms de 16 bytes según ISO 32000-1 Algorithm 10: los bytes 0-3 llevan el valor /P como little-endian de 32 bits, los bytes 4-7 son relleno 0xFF, el byte 8 es 'T' / 'F' rastreando /EncryptMetadata, los bytes 9-11 son el marcador literal 'a' 'd' 'b', y los bytes 12-15 son relleno aleatorio. Una nueva auditoría de round-trip deriva la clave de encriptación de archivo a partir del user password a través del algoritmo 2.A (SHA-256(pw || U_Key_Salt) -> AES-CBC-desencripta /UE -> file key), desencripta /Perms con AES-256-ECB, y afirma cada campo contra los valores del encrypt-dict. Sin cambio de comportamiento; este release es cobertura de regresión pura para asegurar que un refactor futuro no pueda romper silenciosamente la relación /P <-> /Perms exigida por spec.

2026-05-16 Version 2.51.0

  • Se añadió soporte de shading tensor product patch mesh (PDF 1.3+ ISO 32000-1 8.7.4.5.7, Shading Type 7), la entrada final en la familia de shading de mesh tras Type 4 (free-form) de v2.48, Type 5 (lattice) de v2.49 y Type 6 (Coons) de v2.50. Cada patch tensor-product lleva una rejilla completa 4 x 4 de puntos de control Bezier bicúbicos p[i][j] (12 de frontera + 4 interiores) más un color por esquina, dando control fino tanto de la frontera del patch como de su interior. Útil para round-trips de mesh-gradient SVG y cualquier artwork vectorial que necesite una superficie Bezier tensor-product en lugar de una Coons-blended.
  • El nuevo THotPDF.RegisterTensorProductPatchMesh(XMin, YMin, XMax, YMax, NumComponents, Patches) emite un stream binario /ShadingType 7 con /BitsPerCoordinate 16, /BitsPerComponent 8 y /BitsPerFlag 8, lo envuelve en un Pattern Type 2, y devuelve un nombre Pattern (Sh1, Sh2, ...) usable a través del pipeline regular SetFillPattern / SetStrokePattern.
  • El array plano Patches empaqueta cada parche como 16 puntos de control (32 floats X+Y en el orden especificado: p[0][0..3], p[1][3], p[2][3], p[3][3], p[3][2..0], p[2][0], p[1][0], p[1][1], p[1][2], p[2][2], p[2][1]) seguidos de 4 colores de esquina (4 * NumComponents floats en p[0][0] / p[0][3] / p[3][3] / p[3][0]), stride = 32 + 4 * NumComponents. Cada parche emitido lleva el flag = 0 (parche independiente).
  • Con este release, toda la familia de shading mesh ISO 32000-1 8.7.4.5 (Types 4 / 5 / 6 / 7) está ahora cubierta.

2026-05-16 Version 2.50.0

  • Se añadió soporte de shading Coons patch mesh (PDF 1.3+ ISO 32000-1 8.7.4.5.6, Shading Type 6) - la tercera entrada en la familia de shading mesh tras Type 4 (free-form) de v2.48 y Type 5 (lattice) de v2.49. Cada patch Coons está delimitado por cuatro curvas Bezier cúbicas y lleva un color por esquina; el renderizador ajusta la superficie Coons entre las cuatro aristas, dando un quad portador de color arbitrariamente curvado. Útil para gradientes foil y metálicos en paths doblados, mallas de gradiente derivadas de SVG, y cualquier quad con aristas curvadas que de otra forma necesitaría muchos triángulos pequeños para aproximarse.
  • El nuevo THotPDF.RegisterCoonsPatchMesh(XMin, YMin, XMax, YMax, NumComponents, Patches) emite un stream binario /ShadingType 6 con /BitsPerCoordinate 16, /BitsPerComponent 8 y /BitsPerFlag 8, lo envuelve en un Pattern Type 2, y devuelve un nombre Pattern (Sh1, Sh2, ...) usable a través del pipeline regular SetFillPattern / SetStrokePattern.
  • El array plano Patches empaqueta cada patch como 12 puntos de control (24 floats X+Y) seguidos de 4 colores de esquina (4 * NumComponents floats), stride = 24 + 4 * NumComponents. Los 12 puntos de control están ordenados c1..c12 en orden horario alrededor de la frontera del patch empezando desde la esquina 1, con c1 / c4 / c7 / c10 en las cuatro esquinas y los 8 restantes como handles Bezier internos. Cada patch emitido lleva flag = 0 (patch independiente); los flags de continuación 1 / 2 / 3 (compartición de arista) no se exponen en esta sobrecarga de conveniencia.
  • El tipo 7 (malla de parche de producto tensorial) seguirá siendo un trabajo futuro.

2026-05-16 Version 2.49.0

  • Se añadió soporte de mesh de triángulos sombreados Gouraud lattice-form (PDF 1.3+ ISO 32000-1 8.7.4.5.5, Shading Type 5), el compañero del Type 4 free-form de v2.48.0. Úsalo cuando los datos fuente ya están dispuestos en una rejilla de muestreo regular (terreno, resultados FEA, heatmaps científicos) y la aplicación no quiere emitir topología explícita de triángulos - el renderizador auto-triangula cada par de filas adyacentes en una tira de triángulos.
  • El nuevo THotPDF.RegisterLatticeFormGouraudShading(XMin, YMin, XMax, YMax, NumComponents, VerticesPerRow, Vertices) empaqueta un lattice de M filas x N columnas en un stream binario Shading con /BitsPerCoordinate 16, /BitsPerComponent 8 y /VerticesPerRow N. A diferencia de Type 4, no hay byte de flag por vértice y /BitsPerFlag está ausente del diccionario de shading. Devuelve un nombre Pattern (Sh1, Sh2, ...) usable a través del pipeline regular SetFillPattern / SetStrokePattern.
  • El array plano Vertices está ordenado row-major con cada vértice contribuyendo valores (X, Y, c0, c1, ..., c_{NumComponents-1}); el recuento total de vértices debe ser un múltiplo de VerticesPerRow con al menos dos filas. NumComponents acepta 1 (DeviceGray), 3 (DeviceRGB) o 4 (DeviceCMYK).
  • Los sombreados de tipo 6 (parche de Coons) y tipo 7 (producto tensorial) seguirán siendo un trabajo futuro.

2026-05-16 Version 2.48.0

  • Se añadió soporte de mesh de triángulos sombreados Gouraud free-form (PDF 1.3+ ISO 32000-1 8.7.4.5.4, Shading Type 4) para gradientes de color de ilustración vectorial más generales que las variantes Type 2 / 3 axial / radial existentes. Cualquier superficie triangulada con colores por vértice ahora se puede embeber directamente.
  • El nuevo THotPDF.RegisterFreeFormGouraudShading(XMin, YMin, XMax, YMax, NumComponents, Vertices) empaqueta el mesh de triángulos en un stream binario Shading con /BitsPerCoordinate 16, /BitsPerComponent 8 y /BitsPerFlag 8, lo envuelve en un Pattern Type 2, y devuelve un nombre Pattern (Sh1, Sh2, ...) usable a través del pipeline regular THPDFPage.SetFillPattern / SetStrokePattern.
  • El array plano Vertices empaqueta (X, Y, c0, c1, ...) por vértice; cada tres vértices consecutivos forman un triángulo independiente. Las coordenadas / componentes de salida se codifican como enteros sin signo big-endian; el array /Decode mapea el rango codificado de vuelta al bounding box suministrado por el usuario en espacio del usuario.

2026-05-16 Version 2.47.0

  • Se añadió soporte de espacio de color DeviceN (PDF 1.3+ ISO 32000-1 8.6.6.5), generalizando Separation a N colorantes para impresión de seis colores, mezclas de tintas metálicas / fluorescentes, y flujos PDF/X de tinta personalizada. El nuevo THotPDF.RegisterDeviceN(ColorantNames, AlternateCS, TintC1Matrix) devuelve un nombre de espacio de color auto-generado (DevN1, DevN2, ...) que conduce el pipeline regular SetFillColorSpace + SetFillColor con N operandos de tint en [0..1].
  • La transformación de color es una función de tipo 4 de PostScript que actúa como una calculadora y realiza una mezcla lineal ponderada sobre la matriz N x M proporcionada, de modo que, en visores que no admiten colores especiales, el renderizado de respaldo interpola cada canal de salida como una suma de contribuciones de color.
  • ColorantNames acepta cualquier nombre de tinta spot; los espacios se escapan a #20 según PDF 1.7 7.3.5 por la ruta existente del name-writer. El nombre especial "None" marca un slot de colorante no usado.
  • AlternateCS acepta DeviceGray, DeviceRGB o DeviceCMYK; cualquier otro valor lanza una excepción.

2026-05-15 Version 2.46.0

  • Se amplió la generación de stream de apariencia AcroForm (AutoFormAppearances) para campos de texto según PDF 1.7 ISO 32000-1 12.7.4.3. La baseline v2.28.0 producía un literal de una sola línea para cada widget de texto; los campos multi-línea y comb ahora conducen sus propias rutas de layout AP.
  • Los campos de texto multi-línea (Flags incluyendo ffMultiline) hacen word-wrap del valor inicial al ancho del widget, honran los separadores CR / LF / CRLF embebidos, truncan en el recuento de filas visibles, y emiten un layout multi-línea Td + T* apropiado con /TL leading.
  • Los campos de texto comb (Flags incluyendo ffComb junto con MaxLen > 0) renderizan cada carácter en su propia celda de ancho igual usando una matriz Tm absoluta por glifo, coincidiendo con lo que Acrobat dibuja.
  • Los campos de texto de una sola línea mantienen el layout v2.28.0 byte a byte de modo que los llamadores heredados no ven cambio de comportamiento.

2026-05-14 Version 2.45.0

  • Se añadió soporte de espacio de color Separation para flujos de impresión de spot color según ISO 32000-1 8.6.6.4. El nuevo THotPDF.RegisterSeparation(ColorantName, AlternateCS, TintC1) devuelve un nombre de espacio de color auto-generado (Sep1, Sep2, ...) que conduce el pipeline regular SetFillColorSpace / SetStrokeColorSpace + SetFillColor([tint]) / SetStrokeColor([tint]) con un único operando de tint en [0..1].
  • AlternateCS acepta DeviceGray, DeviceRGB o DeviceCMYK; TintC1 describe el color de la tinta spot a intensidad máxima (tint = 1.0). Una función de transformación lineal para el color de la tinta se construye internamente de modo que, en visores que no admiten colores spot, el renderizado de respaldo interpola suavemente entre cero tinta (tint = 0) y TintC1 (tint = 1).
  • Los nombres Pantone y otros nombres de tinta con espacios se aceptan directamente - HotPDF los escapa a secuencias "#20" según PDF 1.7 7.3.5 al escribir el nombre del espacio de color.
  • Rechaza documentos por debajo de PDF 1.3 a través de RequirePDFVersion de modo que la salida strict-mode 1.2 / anterior se mantiene conforme.

2026-05-14 Version 2.44.0

  • Se añadió soporte de espacio de color CIELab según ISO 32000-1 8.6.5.3. El nuevo THotPDF.RegisterLabColorSpace(Xw, Yw, Zw, aMin, aMax, bMin, bMax) devuelve un nombre de espacio de color auto-generado (Lab1, Lab2, ...) que conduce el pipeline regular THPDFPage.SetFillColorSpace / SetStrokeColorSpace + SetFillColor([L, a, b]) / SetStrokeColor([L, a, b]).
  • Flujo común: registra un espacio por iluminante (D50 para impresión ICC, D65 para display equivalente a sRGB), luego pinta con L* en [0..100] y a*, b* en el rango elegido (típicamente -128..127). Los lectores PDF (Adobe Acrobat, Foxit, MuPDF, visores de navegador) honran el WhitePoint al convertir Lab a RGB de display.
  • Rechaza documentos por debajo de PDF 1.3 a través de RequirePDFVersion de modo que la salida strict-mode 1.2 / anterior se mantiene conforme.

2026-05-14 Version 2.43.1

  • Se verificó el encoder CCITTFaxDecode existente end-to-end en una imagen bilevel real: un TBitmap pf1bit de 240x120 con bandas alternadas se embebe correctamente a través de los tres modos (icCCITT31 G3 1D / icCCITT32 G3 2D / icCCITT42 G4 / T.6), los tamaños de stream codificados siguen el orden de compresión esperado G3-1D > G3-2D > G4, los streams se decodifican en Adobe Acrobat / Foxit / MuPDF sin avisos, y el muestreo de píxeles confirma que el patrón alternado de bandas blanco/negro hace round-trip en los tres modos. Sin cambio de código del encoder - este release solo bloquea la cobertura PDF 1.7 ISO 32000-1 7.4.9 existente en la suite de smoke.

2026-05-14 Version 2.43.0

  • Se añadió pintado de Tiling Pattern sin color (PaintType=2) end-to-end según ISO 32000-1 8.6.6.1. El stream de pattern contiene solo la geometría del tile (sin color); el color de tint viaja a través del operador scn / SCN del stream de contenido de la página en el momento de fill / stroke.
  • Los nuevos métodos THPDFPage SetFillPatternRGB / SetStrokePatternRGB / SetFillPatternGray / SetStrokePatternGray / SetFillPatternCMYK / SetStrokePatternCMYK aceptan un nombre de pattern sin color registrado más los componentes de tint (DeviceRGB, DeviceGray, o DeviceCMYK en [0..1]). Cada llamada auto-registra el espacio de color de tinting [/Pattern /BaseCS] coincidente en el dict Resources/ColorSpace de la página en el primer uso.
  • Los patrones de relleno y los patrones de línea existentes siguen cubriendo la variante con color PaintType=1: el patrón tiene su propio color y no se transmiten componentes de color con el operador.
  • Ambas rutas de código comparten el punto de entrada existente RegisterTilingPattern; Colored=true emite PaintType=1 (con color), Colored=false emite PaintType=2 (sin color).

2026-05-14 Version 2.42.0

  • Se añadió soporte para imagen con máscara suave (SMask) PDF 1.4. El nuevo método THotPDF.AddImageWithSMask toma un plano RGB en bruto y un plano alpha paralelo de 8 bits, emite el XObject de imagen en color junto con un XObject de máscara suave DeviceGray, y conecta la referencia cruzada /SMask según ISO 32000-1 8.9.5.4. Coloque el índice de imagen devuelto con el método ShowImage existente — no se requieren otros cambios de fontanería.
  • Se añadió el asistente de conveniencia THotPDF.AddImageWithSMask32 que extrae los planos de color y alpha de un TBitmap BGRA de 32 bits, de modo que los bitmaps con alpha estilo PNG pueden incrustarse en una sola llamada.
  • Ambas llamadas rechazan documentos por debajo de PDF 1.4 a través de RequirePDFVersion, de modo que la salida 1.3 en modo estricto permanece conforme.

2026-05-14 Version 2.41.0

  • Se añadió la salida de stream de objetos PDF 1.5. Establecer THotPDF.UseObjectStreams a true junto con UseXRefStream y SaveToStream empaqueta los objetos indirectos elegibles (todo excepto streams, el diccionario de cifrado y los diccionarios Catalog e Info apuntados por el trailer) en uno o más streams contenedores /Type /ObjStm.
  • Reducción del tamaño de archivo en PDFs con mucho documento (muchas páginas, anotaciones, widgets AcroForm, elementos de árbol de estructura o capas de Optional Content). Una prueba smoke de 30 páginas solo con gráficos se reduce de 14502 bytes a 8488 bytes (41.5% más pequeño) sin cambios visibles.
  • El stream de referencia cruzada ahora emite entradas tipo-2 de objeto comprimido cuyo field-2 es el número de objeto ObjStm anfitrión y field-3 es el índice de la entrada dentro de ese stream. UseObjectStreams baja silenciosamente a false cuando UseXRefStream está desactivado (el xref textual no puede codificar entradas tipo-2) o cuando Version es inferior a PDF 1.5.

2026-05-12 Version 2.40.1

  • Se corrigió la renderización de los PDFs generados en Adobe Acrobat, Foxit, SumatraPDF y visores embebidos en navegador en sistemas Windows 10 / 11 con la opción de región "Beta: Use Unicode UTF-8 for worldwide language support" habilitada. Los streams de contenido de página ya no llevan un preámbulo BOM UTF-8 extraviado que los lectores PDF estrictos rechazaban como error de sintaxis.

2026-05-11 Version 2.40.0

  • Se añadió subsetting de fuentes CFF con clave CID para fuentes OpenType-CFF CJK grandes y multi-FD, incluidas las fuentes CJK de Adobe que usan datos FDArray y FDSelect.
  • Se mejoró el manejo de subrutinas CFF de modo que las fuentes OpenType-CFF grandes puedan incrustarse como subsets PDF compactos en lugar de archivos de fuente completos.

2026-05-11 Version 2.39.1

  • Se mejoró la compatibilidad con fuentes OpenType-CFF del mundo real, incluyendo fuentes estilo Adobe MinionPro con tablas CFF más grandes y offsets de múltiples bytes.
  • Se expuso HPDFExtractTTFPostScriptName para que las aplicaciones con código personalizado de carga de fuentes puedan leer el nombre PostScript desde datos de fuentes TrueType y OpenType-CFF.

2026-05-11 Version 2.39.0

  • Se añadió subsetting de Global Subr y Local Subr para fuentes CFF, reduciendo aún más el tamaño de fuente OpenType-CFF incrustada.
  • Se preservó el bytecode de subrutinas CFF utilizadas mientras se reemplazaban con seguridad las entradas no utilizadas por stubs de retorno compactos.

2026-05-11 Version 2.38.0

  • Se añadió la incrustación automática de OpenType-CFF en StoreFont. HotPDF ahora emite fuentes CID basadas en CFF a través de /FontFile3 cuando una fuente cargada por GDI es un contenedor OpenType-CFF sfnt 'OTTO'.
  • Se añadieron las APIs auxiliares HPDFSfntIsOTF, HPDFOTFFindCFFTable y HPDFSubsetOTFContainer para aplicaciones que procesan fuentes OpenType-CFF directamente.

2026-05-11 Version 2.37.0

  • Se añadió HPDFSubsetCFF para payloads de fuentes Compact Font Format y Type 1C, permitiendo programas de fuente CFF incrustados más pequeños.
  • Se mantuvieron los ID de glifos originales durante el proceso de subsetting CFF para asegurar la compatibilidad con la salida de texto Type0 / Identity-H existente.
  • Se expuso el subsetter CFF para aplicaciones que cargan fuentes a través de su propio pipeline antes de pasar los datos a HotPDF.

2026-05-10 Version 2.36.0

  • Se añadió el subsetting opcional de fuentes TrueType con THotPDF.EnableFontSubsetting, reduciendo a menudo los payloads de fuentes incrustadas en más del 90% para documentos que usan solo un pequeño conjunto de glifos.
  • Se añadió el nombrado de fuente subset estándar PDF mientras se preservan los glyph IDs de la fuente fuente para texto Identity-H.
  • Se mantuvo la incrustación de fuente completa como predeterminada para flujos de trabajo que requieren el programa de fuente completo.

2026-05-10 Version 2.35.1

  • Se corrigió un crash "Font Capture" de Adobe Acrobat al abrir PDFs creados con fuentes Windows sustituidas. HotPDF ahora hace coincidir los nombres de fuente PDF con el nombre PostScript dentro de los datos TrueType incrustados.

2026-05-10 Version 2.35.0

  • Se añadió soporte para actualización incremental de PDF con THotPDF.BeginIncrementalUpdate y THotPDF.SaveIncrementalUpdate. Los bytes fuente existentes se preservan y los nuevos objetos PDF se añaden en una revisión incremental conforme.
  • Se añadió THotPDF.MarkDirty para que las aplicaciones puedan re-guardar explícitamente los objetos modificados durante las actualizaciones incrementales.
  • Se habilitaron los flujos de trabajo de firmas múltiples preservando los rangos de bytes firmados previamente mientras se añaden campos de firma posteriores o cambios en el documento.

2026-05-10 Version 2.34.0

  • Se añadieron estilos de borde de anotación a través de THPDFPage.SetAnnotationBorderStyle, incluyendo estilos sólido, discontinuo, biselado, hundido y subrayado.
  • Se añadieron anotaciones popup enlazadas. Los nuevos popups pueden ahora referenciar su anotación padre para que los visores muestren el conector de comentario esperado.
  • Se añadió THPDFPage.LastAnnotation para facilitar el estilo de anotación y el encadenamiento de popups sin cambiar las llamadas existentes de creación de anotaciones.

2026-05-10 Version 2.33.0

  • Se añadió soporte para Public-Key Security Handler para cifrado PDF. Los documentos pueden ahora cifrarse para destinatarios de certificado X.509 en lugar de solo contraseñas compartidas.
  • Se añadieron THotPDF.EnablePubKeyEncryption y THotPDF.AddPubKeyRecipient para flujos de trabajo de cifrado con destinatario de certificado.
  • Se añadió soporte para SHA-1 puramente en Pascal en HPDFCrypt junto a las primitivas de hash existentes.

2026-05-10 Version 2.32.0

  • Se añadieron grupos de transparencia a nivel de página con THPDFPage.SetTransparencyGroup y SetTransparencyGroupICC para composición predecible de contenido PDF transparente.
  • Se añadieron opciones de grupo de transparencia DeviceGray, DeviceRGB, DeviceCMYK, espacio de color omitido y perfil ICC.
  • Se añadió THPDFPage.ClearTransparencyGroup para eliminar un grupo de transparencia de página asignado previamente.

2026-05-09 Version 2.31.0

  • Se añadieron acciones interactivas de push-button para botones AcroForm. Antes de esta release, HotPDF podía colocar widgets de botón que aparecían correctamente pero no tenían comportamiento al hacer clic. Los botones pueden ahora enviar activamente un formulario, restablecer los valores de los campos, ejecutar un script JavaScript o navegar a una URI.
  • Las acciones SubmitForm envían los valores actuales de los campos AcroForm a una URL de servidor, habilitando formularios de recolección de datos basados en PDF que funcionan en Adobe Reader y Foxit sin un visor web separado.
  • ResetForm limpia todos los campos volviéndolos a sus valores predeterminados, opcionalmente restringido a un subconjunto nombrado de campos. Las acciones JavaScript y URI siguen el mismo gating de versión PDF introducido en v2.26.0.
  • Antes de esta release, AddPushButtonWithAction aceptaba solo propiedades visuales para el widget de botón; ahora tanto la apariencia como el comportamiento de clic se controlan completamente en una sola llamada.

2026-05-09 Version 2.30.0

  • Se añadieron transiciones de página para presentaciones PDF a pantalla completa con THPDFPage.SetPageTransition.
  • Se añadió THPDFPage.SetPageDuration para que las páginas de presentación puedan auto-avanzar después de un retraso elegido.
  • Se añadió manejo consciente de la versión para estilos de transición PDF 1.5 como Fly, Push, Cover, Uncover y Fade.

2026-05-09 Version 2.29.0

  • Se completó el conjunto de límites de página de ViewerPreferences PDF 1.4 añadiendo ViewArea y ViewClip junto a las propiedades PrintArea y PrintClip existentes.
  • ViewArea controla qué caja de página usa el visor como área visible en pantalla (MediaBox, CropBox, TrimBox, BleedBox o ArtBox). ViewClip controla el límite de recorte en pantalla — importante para PDFs de revisión de diseño que llevan marcas de sangrado y recorte que no deberían aparecer en la vista normal.
  • Estas opciones son especialmente útiles en flujos de trabajo de preflight y previsualización de impresión donde el recorte en pantalla debe diferir del recorte de impresión. Ambas entradas requieren PDF 1.4 y se omiten automáticamente cuando se apunta a una versión PDF más antigua.

2026-05-09 Version 2.28.0

  • Se añadió la generación opcional de streams de apariencia AcroForm con THotPDF.AutoFormAppearances. Los campos de texto, campos de lista, botones, casillas de verificación y botones de radio pueden ahora renderizarse de forma fiable en visores que ignoran /NeedAppearances.
  • Se añadieron recursos AcroForm predeterminados para Helvetica y ZapfDingbats cuando las apariencias automáticas están habilitadas.
  • Se preservó el comportamiento AcroForm anterior manteniendo AutoFormAppearances deshabilitado por defecto.

2026-05-09 Version 2.27.0

  • Se extendió el gating de versión PDF a las entradas ViewerPreferences y PageMode. Cada valor de preferencia ahora hace cumplir su versión PDF mínima: UseAttachments y UseOC requieren PDF 1.5, PrintScaling requiere PDF 1.6, y Duplex, NumCopies y PickTrayByPDFSize requieren PDF 1.7.
  • Los documentos generados para destinos PDF más antiguos omiten automáticamente las entradas de preferencia de visor no soportadas, evitando advertencias del visor o interpretación errónea silenciosa en procesadores PDF estrictos y validadores de archivo.
  • Cuando THotPDF.StrictVersionLock está habilitado, la salida PDF 1.3 o PDF 1.4 nunca lleva campos de preferencia de visor más nuevos, manteniendo los archivos conformes para flujos de trabajo de producción de impresión y archivo a largo plazo.

2026-05-09 Version 2.26.0

  • Se añadió gating de versión PDF a varias APIs más antiguas que previamente admitían características independientemente de la versión del documento de destino. Los tipos de anotación, acciones JavaScript, fuentes Type 0, CMaps ToUnicode y Catalog Additional Actions ahora verifican que la versión del documento las soporte antes de escribir.
  • Los documentos que apuntan a PDF 1.0 o PDF 1.1 se promueven automáticamente a la versión requerida cuando se usa una característica más nueva, evitando la generación silenciosa de salida que viola la especificación sin requerir cambios de código en el llamador.
  • Cuando THotPDF.StrictVersionLock está habilitado, las llamadas a características que requerirían una actualización de versión son rechazadas en lugar de promover silenciosamente el documento, soportando flujos de trabajo que deben producir salida en una versión PDF exacta.
  • Esta infraestructura de comprobación de versión se extendió a ViewerPreferences y PageMode en v2.27.0 y cubre todas las áreas principales de características en v2.31.0.

2026-05-09 Version 2.25.0

  • Se corrigió la salida del diccionario PDF para tratar las claves Name como sensibles a mayúsculas/minúsculas según la especificación PDF. Anteriormente, nombres relacionados que diferían solo por mayúsculas/minúsculas — como /ca (alpha de relleno) y /CA (alpha de trazo) en un diccionario ExtGState — eran incorrectamente fusionados, descartando silenciosamente un valor.
  • Este fix restaura la composición de transparencia correcta para documentos que usan valores alpha por-relleno y por-trazo en el mismo estado gráfico. Adobe Acrobat, Foxit y los visores PDF de navegador renderizan la salida corregida como se espera.
  • Las APIs públicas de consulta de diccionario permanecen insensibles a mayúsculas/minúsculas para que el código existente que lee PDFs de entrada mal formados con casing de claves inconsistente continúe funcionando sin modificación.

2026-05-09 Version 2.24.0

  • Se añadió gating de versión PDF para APIs de características introducidas desde v2.4.0, ayudando a que los archivos generados permanezcan compatibles con la versión PDF seleccionada.
  • Se añadió THotPDF.StrictVersionLock. Cuando está deshabilitado, HotPDF auto-promueve la versión del documento según sea necesario; cuando está habilitado, las llamadas que requieren características PDF más nuevas son rechazadas.
  • Se actualizó ConfigurePDFVersion para que las opciones de streams XRef, metadatos XMP, Tagged PDF y AES-256 no puedan filtrarse a destinos PDF más antiguos.

2026-05-09 Version 2.23.0

  • Se añadió un flujo de trabajo de firma digital de tres pasos para integrar HotPDF con bibliotecas externas de firma PKI y CMS / PKCS#7: crear el placeholder del campo de firma, calcular el rango de bytes y digest a firmar, luego inyectar la firma CMS completada después de la firma externa.
  • La estrategia de rango de bytes asegura que el hash firmado cubra exactamente las porciones del archivo fuera del contenedor de firma, coincidiendo con lo que los validadores de firma PDF y los verificadores de conformidad PAdES requieren.
  • Todas las operaciones criptográficas — cadena de certificados, estructura CMS, tokens timestamp — se delegan a la biblioteca de firma del llamador (OpenSSL, Windows CAPI, Bouncy Castle, etc.), manteniendo HotPDF independiente de cualquier infraestructura PKI.
  • Soporta documentos con firmas múltiples: cada firma adicional se añade como una actualización incremental, preservando los rangos de bytes firmados previamente sin regenerar el PDF entero.

2026-05-09 Version 2.22.0

  • Se añadió soporte para el manejador estándar de seguridad AES-256 V=5 R=6 PDF 2.0 a través de THotPDF.UseAES256R6.
  • Se añadieron primitivas SHA-384 y SHA-512 puramente en Pascal en HPDFCrypt.
  • Se añadió el asistente hash-dance PDF 2.0 usado por flujos de trabajo de cifrado estilo Acrobat DC y PDF/A-4.

2026-05-09 Version 2.21.0

  • Se expandió el soporte de Tagged PDF de un marcador básico a un árbol de accesibilidad PDF estructurado.
  • Se añadió mapeo de roles, asignación StructParents de página y salida ParentTree de modo que los validadores PDF/UA puedan navegar el árbol de estructura.
  • Se mejoró el manejo de StructTreeRoot de modo que las llamadas posteriores de Tagged PDF actualicen la misma estructura del documento.

2026-05-09 Version 2.20.0

  • Se añadió soporte para /UserUnit y /Tabs a nivel de página a través de THPDFPage.SetUserUnit y THPDFPage.SetTabsOrder.
  • Se añadió Optional Content (capas PDF) con THotPDF.RegisterOptionalContentGroup y THPDFPage.BeginOptionalContent / EndOptionalContent.
  • Se añadieron asistentes ExtGState para alpha de transparencia y modos de mezcla a través de THotPDF.RegisterExtGState y THPDFPage.SetGraphicsState.

2026-05-09 Version 2.19.0

  • Se añadieron asistentes CropBox, BleedBox, TrimBox, ArtBox y rotación de página para flujos de trabajo de producción PDF.
  • Se añadió THotPDF.AddStructureElement para construir árboles de estructura de Tagged PDF con roles, enlaces de página y referencias MCID.
  • Se añadió soporte para patrones tiling para rellenos y trazos de patrón PDF coloreados o no coloreados repetidos.

2026-05-09 Version 2.18.0

  • Se añadió THPDFPage.DrawInlineImage para que pequeñas imágenes raster puedan escribirse directamente en un stream de contenido de página.
  • Se añadieron gradientes axiales multi-stop para efectos de sombreado PDF y transición de color más suaves.
  • Se añadió soporte OutputIntents para flujos de trabajo de publicación PDF/A, PDF/X y gestionados por color.

2026-05-09 Version 2.17.0

  • Se añadió el manejador estándar de seguridad AES-256 (PDF 1.7 Extension Level 3) para documentos que requieren una protección más fuerte que el AES-128 predeterminado. Los PDFs cifrados con AES-256 se abren en Adobe Acrobat 9 y posteriores, Foxit Reader, Google Chrome, Apple Preview y todos los visores PDF modernos.
  • Las contraseñas de usuario y propietario se derivan usando hashing SHA-256 según la especificación Adobe Extension Level 3, asegurando compatibilidad con cualquier descifrador PDF conforme sin soporte personalizado.
  • El cifrado AES-256 es opt-in en esta release; el camino AES-128 existente sigue siendo el predeterminado para mayor compatibilidad regresiva con versiones más antiguas de Acrobat.
  • v2.22.0 posterior añade PDF 2.0 AES-256-R6 con un algoritmo mejorado de derivación de clave para máxima seguridad con flujos de trabajo Acrobat DC y PDF/A-4.

2026-05-09 Version 2.16.0

  • Se añadieron seis nuevos subtipos de anotación para casos de uso multimedia, legales y de accesibilidad: Watermark (superposiciones de sello para visualización e impresión), Redact (marcar regiones para eliminación de contenido), Screen (vídeo y audio incrustados), 3D (visores interactivos de modelos U3D y PRC), RichMedia (contenido multimedia interactivo) y Popup (burbujas de comentario enlazadas a anotaciones padre).
  • Las anotaciones Watermark soportan renditions de visualización e impresión separadas, de modo que las marcas “DRAFT” o “CONFIDENTIAL” pueden aparecer en pantalla sin imprimirse, o viceversa.
  • Las anotaciones Redact marcan regiones de contenido para eliminación antes de la distribución del documento; el flujo de trabajo de redacción del visor aplica el borrado final, manteniendo el contenido original intacto hasta que se compromete.
  • Los seis tipos de anotación incluyen gating de versión PDF de modo que los documentos que apuntan a estándares PDF más antiguos omiten automáticamente los subtipos de anotación más nuevos y permanecen conformes a la especificación.

2026-05-09 Version 2.15.0

  • Se añadió soporte para fuentes Type 3 para incrustar definiciones de glifos vectoriales personalizados directamente en la salida PDF. Las fuentes Type 3 son adecuadas para logos de empresa, conjuntos de símbolos propietarios, iconos de mapa y cualquier notación gráfica que no pueda expresarse usando fuentes estándar.
  • Cada glifo se define como un stream de contenido PDF usando operadores de dibujo estándar, de modo que los glifos Type 3 escalan limpiamente en todos los niveles de zoom y resolución de impresión sin un archivo de fuente externo.
  • El flujo de trabajo completo — registrar una fuente, definir glifos individuales y seleccionar la fuente en una página — se maneja por RegisterType3Font, AddType3Glyph y SetType3Font.
  • Las fuentes Type 3 son renderizadas correctamente por Adobe Reader, Foxit y todos los visores PDF conformes a la especificación sin requerir la instalación de fuentes en el sistema del visor.

2026-05-09 Version 2.14.0

  • Se añadieron implementaciones AES-256 y SHA-256 puramente en Pascal como primitivas criptográficas internas para una protección más fuerte del documento. Estos algoritmos son requeridos por los estándares de cifrado PDF 1.7 Extension Level 3 y PDF 2.0.
  • El nuevo código criptográfico no requiere DLLs externas o bibliotecas crypto proporcionadas por el SO, manteniendo HotPDF autónomo en todos los targets Delphi y C++Builder soportados y versiones de Windows.
  • AES-128 sigue siendo el formato de cifrado de documento predeterminado en esta release; las nuevas primitivas sustentan el manejador de seguridad AES-256 añadido en v2.17.0 y el manejador PDF 2.0 en v2.22.0.

2026-05-09 Version 2.13.0

  • Se añadieron rellenos de gradiente suave axiales (lineales) y radiales para dibujo vectorial PDF. Los gradientes vectoriales producen archivos PDF significativamente más pequeños que los equivalentes rasterizados y permanecen nítidos en cualquier nivel de zoom o resolución de impresión.
  • Ambos tipos de gradiente soportan los espacios de color DeviceGray, DeviceRGB y DeviceCMYK, cubriendo flujos de trabajo de visualización en pantalla, impresión RGB y producción CMYK de cuatro colores.
  • Los gradientes pueden aplicarse como rellenos o trazos en cualquier camino de página y son renderizados correctamente por Adobe Reader, Foxit, SumatraPDF y todos los visores PDF modernos.

2026-05-09 Version 2.12.0

  • Se añadieron widgets de campo de firma sin firmar con THPDFPage.AddSignatureField. El PDF generado contiene un placeholder visual de firma que los flujos de trabajo PKI externos, portales de firma y bibliotecas de firma CMS / PKCS#7 pueden llenar con una firma criptográfica real.
  • Las banderas de firma AcroForm se establecen automáticamente cada vez que al menos un campo de firma está presente, manteniendo el documento conforme a la especificación sin llamadas API extra.
  • Los widgets de campo de firma son reconocidos como firmables por Adobe Acrobat, Foxit y otras soluciones de firma PDF. Consulte la versión v2.23.0 para obtener el flujo de trabajo completo de firma por rango de bytes.

2026-05-09 Version 2.11.0

  • Se añadió la infraestructura Tagged PDF requerida por PDF/UA y los estándares modernos de accesibilidad. Los documentos taggeados llevan un orden de lectura lógico y estructura semántica que los lectores de pantalla, tecnologías asistivas y herramientas de extracción de texto pueden seguir.
  • THotPDF.EnableTaggedPDF activa el árbol de estructura del documento; THotPDF.Lang establece el idioma primario del documento. Ambos son requeridos por los validadores de conformidad de accesibilidad.
  • Los operadores de marked-content permiten que las operaciones individuales de dibujo en la página sean taggeadas con roles semánticos (párrafo, encabezado, figura, etc.) e identificadores, habilitando el reflow correcto y la extracción por lector de pantalla desde PDFs generados.
  • Los elementos de estructura e identificadores de marked-content se rastrean por página y por documento, consistente con la especificación PDF para documentos taggeados de múltiples páginas.

2026-05-09 Version 2.10.0

  • Se añadió soporte para predictor PNG y predictor TIFF de diferenciación horizontal para streams de imagen FlateDecode. Los predictores aplican filtrado adaptativo por fila antes de la compresión, reduciendo significativamente el tamaño de salida para imágenes fotográficas y de gradiente suave.
  • El predictor óptimo PNG (modo adaptativo de fila) y el predictor TIFF 2 (diferenciación horizontal) están disponibles como asistentes de codificación independientes para pipelines personalizados de incrustación de imagen.
  • Ambos predictores soportan datos de imagen DeviceGray, DeviceRGB y DeviceCMYK, cubriendo flujos de trabajo de visualización en pantalla e impresión de cuatro colores.

2026-05-09 Version 2.9.0

  • Se añadió salida de stream de metadatos XMP junto al tradicional diccionario Info del documento. XMP es el estándar moderno para metadatos PDF incrustados y es requerido para PDF/A, PDF/X y muchos flujos de trabajo de archivo.
  • Los metadatos incrustados XMP son leídos por motores de búsqueda de escritorio, sistemas de gestión de activos digitales y pipelines de procesamiento PDF que ya no consultan el más antiguo diccionario Info.
  • Los streams XMP se dejan sin comprimir en el archivo de salida para que las herramientas externas y utilidades de línea de comandos puedan inspeccionar metadatos sin decodificar el archivo.
  • Las aplicaciones que requieren contenido exacto de metadatos pueden suministrar un paquete XMP UTF-8 completamente personalizado a través de THotPDF.CustomXMP, evitando la auto-construcción.
  • Cuando el cifrado del documento está activo, el stream XMP se cifra junto con el resto del documento por defecto, manteniendo los metadatos confidenciales.

2026-05-09 Version 2.8.0

  • Se añadió la salida opcional de stream de referencia cruzada PDF 1.5. Los streams de referencia cruzada codifican la tabla de offsets de objeto en un formato binario compacto en lugar de la gran sección de texto usada por las tablas xref tradicionales, reduciendo el tamaño de archivo para cada documento.
  • Los streams de referencia cruzada son requeridos para características PDF 1.5 como streams de objetos (introducidos en v2.41.0) y se deshabilitan automáticamente cuando el documento apunta a una versión PDF inferior a 1.5.
  • Los PDFs generados con streams de referencia cruzada se procesan más eficientemente por los visores modernos, herramientas de archivo y validadores PDF/A.
  • La tabla de referencias cruzadas de texto tradicional sigue siendo la predeterminada, lo que garantiza la compatibilidad con lectores de PDF antiguos y procesadores estrictos.

2026-05-09 Version 2.7.0

  • Se añadió salida de color DeviceCMYK nativa para flujos de trabajo de impresión y PDF/X.
  • Se añadió registro de espacio de color ICCBased con THotPDF.RegisterICCProfile.
  • Se añadieron APIs genéricas de espacio de color para relleno y trazo para perfiles ICC y valores de color de componentes arbitrarios.

2026-05-09 Version 2.6.0

  • Se añadieron anotaciones de marcado de texto Highlight, Underline, Squiggly y StrikeOut.
  • Se añadieron anotaciones Polygon, Polyline, Ink y Caret.
  • Se añadieron acciones de enlace GoTo, GoToR y Launch para navegación intra-documento, cross-documento y de archivo externo.

2026-05-09 Version 2.5.0

  • Se añadió soporte AcroForm para campos de texto, casillas de verificación, botones de radio, combo boxes, list boxes y push buttons.
  • Se añadió THPDFFormFieldFlags para que las aplicaciones puedan configurar opciones comunes de campo de formulario sin manejo manual de bits.
  • Los formularios generados ahora establecen /NeedAppearances para que los visores PDF comunes puedan renderizar los campos automáticamente.

2026-05-08 Version 2.4.0

  • Se corrigió el cifrado PDF AES-128. Las cadenas y streams ahora están realmente cifrados con AES-CBC para la seguridad V=4 / R=4.
  • CryptKeyLength=aes128 ahora crea PDFs cifrados que los visores conformes pueden abrir con la contraseña de usuario o propietario configurada.
  • Se añadieron asistentes de codificador ASCIIHexDecode, ASCII85Decode y RunLengthDecode.
  • Se actualizaron los stubs JBIG2 y JPEG 2000 empaquetados para reportar honestamente la decodificación no soportada en lugar de devolver datos de imagen placeholder.

2026-05-06 Version 2.3.23

  • Se han optimizado las compilaciones de los objetos zlib-ng, libjpeg-turbo y libtiff, que se incluyen en las toolchains actuales de RAD Studio y Visual Studio.
  • Se mejoró la alineación de heap para las bibliotecas nativas de compresión e imagen en targets Delphi y C++Builder.
  • Se validaron las suites automatizadas de regresión Delphi y C++Builder a través de los targets Win32, Win64 y Win64x.

2026-05-05 Version 2.3.22

  • Se habilitó el dispatch SIMD en tiempo de ejecución de zlib-ng en los builds de compresión Flate de 64 bits empaquetados.
  • Se mantuvo zlib-ng Win32 en el camino estable de objeto genérico para compatibilidad con la toolchain clásica.
  • Se endurecieron los builds de objeto nativo para que un éxito del compilador sin un objeto de salida sea tratado como un fallo de build.

2026-05-05 Version 2.3.21

  • Se habilitó la aceleración SIMD de libjpeg-turbo en los builds de objeto Win32, Win64 y Win64x empaquetados.
  • Se hizo explícito el soporte SIMD a través de una bandera de build de HotPDF.
  • Se corrigió un problema de salida OMF de NASM Win32 que evitaba que Delphi Win32 enlazara el conjunto de objetos SIMD JPEG.
  • Se corrigió una regresión de imagen TIFF Win64 expuesta después de los rebuilds SIMD y zlib-ng.

2026-05-05 Version 2.3.20

  • Se reemplazó el backend Flate empaquetado con zlib-ng en modo compatible con zlib para streams de página, fuentes, CMaps, imágenes, salida TIFF y asistentes FlateDecode.
  • Se corrigieron problemas de compresión de stream denso e importación TIFF de imagen pequeña encontrados durante la migración a zlib-ng.
  • Se mantuvieron los flujos de trabajo JPEG basados en libjpeg-turbo y los flujos de trabajo TIFF basados en libtiff.
  • Se añadió cobertura amplia de regresión automatizada para generación PDF Delphi y C++Builder, importación de imágenes, compresión, cifrado, hyperlinks, configuración de página y flujos de trabajo de copia/fusión/edición.

2026-05-01 Version 2.3.19

  • Se corrigió la generación de stream de página FlateDecode Win64 para salida de código de barras denso.
  • Se alineó la compresión moderna de stream de página y fuente Delphi con el camino zlib RTL estable.

2026-05-01 Version 2.3.18

  • Se corrigió la generación de PDF cifrado C++Builder Win64x corrigiendo la configuración de clave RC4 de tamaño de puntero.
  • Se corrigió la importación TIFF en builds C++Builder Win64x.
  • Se corrigieron los flujos de trabajo de append de página para PDFs abiertos con LoadFromFile para que las páginas añadidas se guarden con números de objeto seguros.

2026-05-01 Version 2.3.17

  • Se actualizaron las demos C++Builder CanvasDrawing y GraphicDraw para coincidir con las muestras mejoradas de gráficos Delphi.
  • Se mejoró la demo C++Builder ViewerPref con estado de UI más claro, validación, soporte para guardar/cargar presets y salida dedicada de ajuste de dirección.

2026-05-01 Version 2.3.16

  • Se corrigió la copia de página para PDFs fusionados que contienen fuentes CID TrueType incrustadas y arrays de ancho anidados.
  • Se mejoró el mapeo de objetos al copiar páginas de múltiples documentos PDF fuente en secuencia.

2026-05-01 Version 2.3.15

  • Se mejoró la demo Delphi ViewerPref para que los controles específicos de opción se habiliten solo cuando son relevantes.
  • Se añadió reporte de errores ViewerPref, validación del número de copias y soporte para guardar/cargar presets nombrados.

2026-05-01 Version 2.3.14

  • Se reelaboró la demo Delphi PDFmerge en una muestra de fusión autocontenida que crea PDFs fuente cuando es necesario.
  • Se mejoró la validación de fusión, indexación de salida y mensajes de éxito/fallo de UI.

2026-05-01 Version 2.3.13

  • Se refinaron las demos Delphi CanvasDraw y GraphicDraw para una salida visual más clara y repetible.
  • Se mejoró la selección de fuentes TrueType a través de los caminos de fuente GDI, reduciendo los fallos en tiempo de incrustación causados por fuentes fallback raster.
  • Se corrigió el espaciado de caracteres cuando la misma fuente se usa a través de múltiples runs de texto en un PDF.
  • Se mejoraron los mensajes de error de incrustación de fuentes con el nombre de fuente y charset solicitados.

2026-05-01 Version 2.3.12

  • Se mejoró la demo CopyPage para que pueda copiar PDFs con árboles /Pages no estándar sin requerir una herramienta separada.
  • CopyPage ahora escribe salida comprimida con FlateDecode independientemente de qué camino de copia tenga éxito.
  • Se incorporó la muestra independiente CopyPageFixed en la demo principal CopyPage.

2026-05-01 Version 2.3.11

  • Los PDFs generados ahora se abren con cada página ajustada a la altura de la ventana por defecto.
  • Se añadieron muchos nombres de tamaño de página predefinidos sin prefijo para tamaños de papel de oficina, gran formato, dibujo, tarjeta, ID, foto, japoneses, chinos y taiwaneses, manteniendo los nombres más antiguos con prefijo ps compatibles.
  • Se reelaboraron las demos Delphi de tamaño de página en los proyectos LargeSize, NormalSize y SmallSize.

2026-04-29 Version 2.3.10

  • Se corrigieron los procesos de compilación de paquetes de RAD Studio XE7 en la capa de compatibilidad TIFF.
  • Se redujeron las advertencias para RAD Studio 10 Seattle y 10.1 Berlin.
  • Se corrigió el mapeo de build de biblioteca de línea de comandos para RAD Studio ProductVersion 9.0 a 12.0.
  • Se restauraron los builds de paquete de línea de comandos para RAD Studio ProductVersion 9.0.
  • Se corrigieron los builds RAD Studio XE2 Win64 declarando el símbolo CRT vsnprintf requerido por los stubs de compatibilidad Win64.
  • Se mejoró la compatibilidad de proyectos legacy con valores boolean numéricos en archivos de proyecto Delphi y C++Builder más antiguos.

2026-04-29 Version 2.3.9

  • Se corrigieron los procesos de compilación de los paquetes de la biblioteca RAD Studio 10.1 Berlin, compilando los paquetes desde el directorio Lib.
  • Se corrigieron los builds de biblioteca Trial en compiladores Delphi más antiguos eliminando las declaraciones de variables inline del camino de marca de agua Trial.

2026-04-29 Version 2.3.8

  • Se eliminó una advertencia de compilador TStream.Seek obsoleta del camino de callback de stream TIFF en builds RAD Studio 10.3 Rio.

2026-04-29 Version 2.3.7

  • Se corrigió la conversión TIFF-a-PDF al usar archivos de objeto libtiff 4.7.x empaquetados.
  • Se corrigió la compilación RAD Studio Win32 del puente Pascal zlib empaquetado bajo ajustes de compilador más estrictos.
  • Se añadió cobertura de regresión DUnitX para conversión TIFF-a-PDF.

2026-04-28 Version 2.3.5

  • Se corrigieron las reconstrucciones de paquete fuente para zlib, libjpeg-turbo y libtiff empaquetados con el compilador clásico Borland C++ Win32.
  • Se reconstruyeron y alinearon los archivos de objeto Win32 y Win64 de terceros enviados para builds Delphi y C++Builder más fiables.
  • Sin cambios en la API pública. Los proyectos Delphi, C++Builder y RAD Studio existentes pueden usar esto como release de compatibilidad.

2026-04-19 Version 2.3.3

  • Se mejoró la fiabilidad de los proyectos RAD Studio evitando que los ajustes de entorno INCLUDE del host interfieran con la compilación de recursos HotPDF.
  • Se redujeron las advertencias sobre recursos duplicados en las compilaciones de paquetes, especialmente para proyectos de RAD Studio 13.1 Florence.

2026-04-19 Version 2.3.2

  • Las carpetas de salida de proyecto de paquete y demo ahora siguen automáticamente la versión RAD Studio activa.
  • Se mejoró el comportamiento de build del paquete HotPDF370 para evitar que archivos de recurso obsoletos sean enlazados con recursos recién generados.

2026-04-19 Version 2.3.1

  • La demo C++Builder TextOut ahora se construye sin ediciones manuales a través de las versiones RAD Studio soportadas.
  • Se corrigió el comportamiento de rebuild Win64x para la demo TextOut para que el archivo de recurso requerido sea preparado antes del enlace.

2026-04-19 Version 2.3.0

  • Se añadió soporte completo de C++Builder 13.1 Florence para targets Win32, Win64 y Win64x.
  • Se estandarizó la disponibilidad de overload TextOut para Delphi y C++Builder manteniendo compatibles los nombres de asistente C++Builder más antiguos.
  • Se limpió el enlace estático Delphi Win64 eliminando las advertencias previas del enlazador de los archivos de objeto zlib, libjpeg-turbo y libtiff empaquetados.
  • Se mejoró la salida de paquete Win64x para que los proyectos C++Builder puedan encontrar la biblioteca HotPDF generada a través de los caminos de búsqueda normales de RAD Studio.

2026-04-17 Version 2.2.1

  • Se eliminaron las advertencias del enlazador Delphi Win32 de las bibliotecas de compresión e imagen de terceros empaquetadas.
  • Se mejoró la compatibilidad de enlace estático para soporte zlib, JPEG y TIFF en builds Delphi y C++Builder.

2026-04-14 Version 2.2.0

  • Se actualizaron las bibliotecas de terceros incluidas a zlib 1.3.2, libjpeg-turbo 3.1.90 y libtiff 4.7.1.
  • Se modernizó el enlace estático Win32 y Win64 para las bibliotecas actualizadas de compresión, JPEG y TIFF.
  • Se expandió la compatibilidad de formato de imagen para escenarios de generación PDF que usan entrada JPEG o TIFF.
  • Se actualizó la ayuda HTML para coincidir con la API HotPDF actual, ejemplos y disposición de biblioteca.

2026-04-13 Version 2.1.4

  • Se corrigió la compilación Win64 para RAD Studio XE2 a XE8.
  • Se mejoró la compatibilidad de los archivos de objeto empaquetados usados por compiladores Delphi más antiguos.
  • Se eliminaron archivos de recurso de paquete obsoletos que podían disparar advertencias del enlazador de recursos duplicados.
  • Se corrigieron los metadatos del proyecto Delphi XE3 para el paquete HotPDF.

2026-04-12 Version 2.1.3

  • Se expandió la demo Delphi TestNumeric en una prueba de regresión práctica para salida PDF numérica.
  • Se añadió validación más amplia para decimales, fracciones, enteros y comportamiento de recarga de PDF.
  • Se hicieron los archivos opcionales de salida de depuración opt-in a través de un switch dedicado de tiempo de compilación.
  • Se actualizó el contenido de ayuda para logging de depuración y pruebas de salida numérica.

2026-04-10 Version 2.1.2

  • Se mejoró la renderización de fuentes incrustadas en salida PDF Win32 y Win64.
  • Se corrigió la renderización incompleta de glifos al cambiar entre fuentes incrustadas, incluyendo fuentes Windows comunes como Calibri.
  • Se actualizó la demo FontTest y los PDFs de muestra generados para una verificación visual más fácil.
  • Se refrescaron las páginas de ayuda HTML para incrustación de fuentes, salida de texto Unicode y ejemplos de fuentes incrustadas.

2026-04-08 Version 2.1.1

  • Se actualizaron las demostraciones de ViewerPreferences para Delphi y C++Builder para que coincidan con la API HotPDF actual.
  • Se añadió cobertura para opciones más nuevas de preferencias de visor PDF.
  • Se mejoró la salida de demo generada para que refleje solo los ajustes de preferencia de visor seleccionados en la UI.
  • Se actualizaron las páginas de ayuda HTML para ViewerPreferences y propiedades relacionadas del documento.