pdfcraft/crates/fonts
komo1iddin 6d843a8e8b
Preserve source fonts in text editing and resize clipped artwork (#1116)
* M0: remove upstream trademark artwork from contributor fork

Use the existing document icon and a plain project label. Remove trademark
asset entries and regenerate attribution as required for modified forks.

Co-authored-by: OpenAI Codex <codex@openai.com>

* M7: group compatible PDF words and hide source text during editing

Join compatible words across text objects and subset-font spaces, recognize
component colours, and reuse subset fonts with numeric space positioning.
Render a read-only background with the selected glyphs removed while the
inline editor is open. Keep one frame and source line breaks, and reset
the layout width when switching paragraphs. Expose the same preview through page_render.

Add synthetic extraction, save/reopen, preview and UI regressions. Record
the remaining mixed-style paragraph and installed-font limitations.

Co-authored-by: OpenAI Codex <codex@openai.com>

* M7: preserve rich PDF regions and preview text edits in place

Group mixed source runs into complete visual regions, retain run styles and source encodings, and use validated embedded source faces in the inline editor. Preserve source baselines and show a single live frame as text grows or shrinks, backed by a read-only draft geometry tool.

Validation: strict workspace Clippy, workspace tests, all 30 WASM crates, asset and parity gates, licensed synthetic edit/save/reopen and desktop checks, and an opt-in four-page private-document regression.

Co-authored-by: OpenAI Codex <codex@openai.com>

* M7: keep cropped artwork from blocking text editing

Track page/path clipping separately from full image placement, use visible selection bounds, and prioritize text hit testing over overlapping artwork. Preserve fixed clips during image transformations and expose visible bounds through the headless tools.

Validation: strict workspace Clippy, workspace tests, 30 WASM crates, asset/parity gates, synthetic engine/tool/UI and native desktop checks, both private-document UI regressions, and all-page local edit/save/reopen/undo on a four-page cropped-artwork PDF.

Co-authored-by: OpenAI Codex <codex@openai.com>

* M7: resize cropped images with their clipping paths

Keep the visible frame and image pixels under the same transform. Preserve the
opposite corner and respond to diagonal, horizontal-only and vertical-only
corner drags without jumping from an off-centre grip. Refuse shared clipping
scopes that would move unrelated content.

Add synthetic engine/tool/UI regressions and opt-in private-file coverage
for 80 corner gestures and 10 grow/shrink save/reopen/undo render checks.

Co-authored-by: OpenAI Codex <codex@openai.com>

* M7: complete source font subsets and preserve clipped editing

Add explicit document fonts, CFF embedding, range formatting and bounded contextual shaping; isolate shared image clips and verify private Chinese edits on all pages.

Co-authored-by: OpenAI Codex <codex@openai.com>

* Revert "M0: remove upstream trademark artwork from contributor fork"

This reverts commit 3aa539b5b69e97419bbab83e6cca3d38835b8de8.

* Test: give the text layer up to 4 s before copying (it flaked on a loaded machine)

---------

Co-authored-by: Kamoliddin Fatkhullaev <komoliddinfatxullayev@MacBook-Pro-Komoliddin.local>
Co-authored-by: OpenAI Codex <codex@openai.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 20:27:55 -04:00
..
src Preserve source fonts in text editing and resize clipped artwork (#1116) 2026-10-11 20:27:55 -04:00
Cargo.toml Preserve source fonts in text editing and resize clipped artwork (#1116) 2026-10-11 20:27:55 -04:00
README.md Preserve source fonts in text editing and resize clipped artwork (#1116) 2026-10-11 20:27:55 -04:00
build.rs M7: support shaped Thai text in Add Text and UI fallback fonts (#767) 2026-10-11 18:07:36 -04:00

README.md

pdfcraft-fonts

Layer L2. Font helpers for the appearance streams PdfCraft generates (comments, form fields).

Today it holds what generated appearances need without a font program:

  • helvetica_width: an approximation of Helvetica's proportions by character class. No metrics file or font program from any vendor is bundled; widths are PdfCraft's own estimates, good enough for line breaking and alignment, not for typesetting.
  • wrap: greedy line breaking with that measure (paragraphs on newlines, long words split).
  • win_ansi: Unicode → WinAnsiEncoding bytes (? for characters it can't represent), and literal to write bytes as a PDF literal string. Nothing writes that ? silently. Callers check first_undrawable (or UnicodeCMap::first_unencodable, for a character beyond U+FFFF in UCS-2), then either refuse with undrawable_message (one wording that names the character and what can be written instead) where the ? would be permanent page content, or, for a form value or a comment's text, which is stored as typed, draw the ? and mark the annotation with set_undrawn_mark (UNDRAWN_MARK, read back by undrawn_mark) so the edit is reported and flattening refuses. script_missing names what the typed-signature script font and its Inter fallback both lack.

It also holds the fonts of the optional craft-fonts build input. build.rs embeds them as CRAFT_FONTS when the build sets CRAFT_FONTS_DIR=<checkout> (and fails if it can't while CRAFT_FONTS_REQUIRED=1, as releases set). Without that variable CRAFT_FONTS is empty and everything below copes:

  • ui_japanese_fonts: the Jpan faces for the interface, BIZ UDPGothic first.
  • ui_chinese_fonts: the Hans faces for the interface, in manifest order (without one, Chinese characters the Japanese faces lack show the replacement glyph).
  • ui_cjk_fonts(prefer_hans): both in fallback order for the UI language (Chinese group first in Chinese mode, so one line never mixes faces with different vertical metrics).
  • ui_arabic_fonts: the Arab faces for Arabic-script interface text (file names, document titles), in manifest order.
  • ui_telugu_fonts: the Telu faces for Telugu interface text (the Telugu catalog, file names, document titles), in manifest order.
  • ui_hebrew_fonts / document_hebrew_font(bold): the Hebr faces (Noto Sans Hebrew) for Hebrew interface text, and the face whose outlines become the Type 3 fallback for Hebrew text written by Edit text (Bold when asked for and present, else Regular). None without a craft-fonts input that has one; the editor then reports a clear error.
  • document_japanese_font / japanese_glyph: the face (Shippori Mincho, then BIZ UDMincho) whose outlines become the Type 3 fallback font for Japanese text written into PDFs. Without it, japanese_glyph returns GlyphError::NoFont and the editor reports a clear error.
  • document_japanese_font_for_style / japanese_glyph_from: select and draw a real Japanese fallback face for edited text. Sans sources prefer BIZ UDPGothic Regular/Bold; serif sources keep Mincho. Type 3 paths and advances both come from that selected face. Missing weights fall back to a real Regular face, never synthetic bold; no italic face is supplied. The default document_japanese_font / japanese_glyph APIs still use regular document Mincho.
  • document_arabic_font / shape_arabic / arabic_glyph: the first Arab face, shaped with harfrust (joining forms, ligatures, mark positions) into clusters for Arabic text written into PDFs; the editor draws each cluster as one Type 3 glyph with a ToUnicode entry. Without the face they return GlyphError::NoFont and the editor reports a clear error.
  • document_japanese_fonts_for_style: every Jpan face in that order of preference, so the editor can use the first one that has all of the replacement's glyphs (the faces differ in coverage, e.g. of Cyrillic).
  • ui_thai_fonts / document_thai_font / shape_thai: the Thai faces for the interface and shaped Thai Add Text content. Harfrust positions stacked vowel/tone marks and decomposes Sara Am; cluster ToUnicode entries preserve the original text. The shared Type 3 writer embeds outlines, so reopening does not depend on installed fonts.
  • SHIPPORI_MINCHO: Shippori Mincho's bytes, or None.

Generated Japanese Type 3 fonts record the fallback face's family and style in an indirect FontDescriptor. pdf::Metrics::base_font uses its FontName when the Type 3 dictionary has no BaseFont, so text inspection and edit-tool results identify the fallback after save/reopen. The descriptor does not change glyph programs or replace the Type 3 FontBBox metrics when Ascent/Descent are omitted (PDF Reference 1.7, tables 5.9 and 5.19). The small web input has only Gothic Regular, so it cannot preserve bold. No new font assets or changes to that input are needed.

wasm32 builds embed only BIZ UDPGothic Regular and any Arab, Telu, Hebr or Thai face, to keep the web build small: the web build currently has no Chinese face, so Chinese there still shows the replacement glyph. Font files are never committed here (AGENTS.md §1.4; team members: craftrules standards/fonts.md, internal).

The full font subsystem (parsing, shaping, subsetting and embedding) arrives with M2.2/M7.

Document font inputs use embedding: bounded static TrueType/OpenType-CFF validation, exact program preservation, Unicode/CID/GID mapping and full-face subset completion. shaping uses the same program with harfrust and UAX #9; source UI fallback fonts are never exported. See existing-text editing.