From a52890ec0fcbd7c0d345f53de06fe91b27a1b327 Mon Sep 17 00:00:00 2001 From: ruchirguitar <113586074+ruchirguitar@users.noreply.github.com> Date: Fri, 13 Nov 2026 23:00:54 +0530 Subject: [PATCH] fix(pdf): blend mode inheritance for Type 3 fallback, CharProcs budget via decoded_within, and docs clarification --- crates/pdf/src/import/actual_text.rs | 13 ++----------- crates/pdf/src/import/builder.rs | 8 ++++---- crates/pdf/src/import/device.rs | 2 +- crates/pdf/src/import_scan.rs | 11 ++--------- docs/gaps.md | 6 +++--- 5 files changed, 12 insertions(+), 28 deletions(-) diff --git a/crates/pdf/src/import/actual_text.rs b/crates/pdf/src/import/actual_text.rs index 7f754d3e37..47e9167bf5 100644 --- a/crates/pdf/src/import/actual_text.rs +++ b/crates/pdf/src/import/actual_text.rs @@ -94,14 +94,7 @@ impl Builder<'_> { } /// A Type 3 glyph under an active ActualText span. - pub(super) fn actual_type3_glyph( - &mut self, - key: u128, - at: Placement, - look: Look, - opacity: f32, - advance: f64, - ) -> bool { + pub(super) fn actual_type3_glyph(&mut self, key: u128, at: Placement, look: Look, opacity: f32, advance: f64) -> bool { self.flush_glyphs(); self.flush_type3(); if let (Some(stroke), Some(line)) = (&look.stroke, &mut self.text) @@ -115,9 +108,7 @@ impl Builder<'_> { Some(text) if !text.is_empty() => TextContent::Actual(text), _ => TextContent::Pen, }; - let place = |line: &mut TextLine| { - line.push_content(&look, at, opacity, advance, content) - }; + let place = |line: &mut TextLine| line.push_content(&look, at, opacity, advance, content); if !self.text.as_mut().is_some_and(place) { self.flush_text(); let mut line = TextLine::new(at, opacity); diff --git a/crates/pdf/src/import/builder.rs b/crates/pdf/src/import/builder.rs index c7ceff4666..dfcd94c784 100644 --- a/crates/pdf/src/import/builder.rs +++ b/crates/pdf/src/import/builder.rs @@ -558,7 +558,7 @@ impl<'p> Builder<'p> { /// Read a soft mask, pattern cell or type 3 glyph (`read`) on its own: its art, in the /// coordinates `page` has. `None` when nested too deeply. - pub(super) fn nested(&mut self, page: Rect, read: impl FnOnce(&mut Self)) -> Option>> { + pub(super) fn nested(&mut self, page: Rect, blend: BlendMode, read: impl FnOnce(&mut Self)) -> Option>> { if self.nested >= MAX_NESTED { return None; } @@ -567,7 +567,7 @@ impl<'p> Builder<'p> { let saved = Saved { stack: std::mem::replace(&mut self.stack, vec![Frame::new(FrameKind::Root)]), root_groups: std::mem::take(&mut self.root_groups), - blend: std::mem::replace(&mut self.blend, BlendMode::Normal), + blend: std::mem::replace(&mut self.blend, blend), mask: self.mask.take(), page: std::mem::replace(&mut self.page, page), type3_run: outer_type3, @@ -589,7 +589,7 @@ impl<'p> Builder<'p> { } let page = self.page; let saved = std::mem::replace(&mut self.white_images, m.mask_type() == MaskType::Alpha); - let art = self.nested(page, |b| m.interpret(b)); + let art = self.nested(page, BlendMode::Normal, |b| m.interpret(b)); self.white_images = saved; let spec = match art { Some(art) => { @@ -653,7 +653,7 @@ impl<'p> Builder<'p> { // A swatch is shared with uses outside an alpha mask, and pattern paints keep their // colours there: its images are read as they are. let white = std::mem::take(&mut self.white_images); - let art = self.nested(Rect::ZERO, |b| { + let art = self.nested(Rect::ZERO, BlendMode::Normal, |b| { // A cell whose content can't be decoded is empty. let _ = t.interpret(b, Affine::IDENTITY, stroke); }); diff --git a/crates/pdf/src/import/device.rs b/crates/pdf/src/import/device.rs index b145f2d7f2..3fcefd7b8e 100644 --- a/crates/pdf/src/import/device.rs +++ b/crates/pdf/src/import/device.rs @@ -225,7 +225,7 @@ impl<'a> Device<'a> for Builder<'_> { } self.flush_glyphs(); self.flush_text(); - let art = self.nested(self.page, |b| { + let art = self.nested(self.page, self.blend, |b| { t3.interpret(b, transform, glyph_transform, paint); }); if let Some(nodes) = art { diff --git a/crates/pdf/src/import_scan.rs b/crates/pdf/src/import_scan.rs index 5e87fcf964..b65254eafa 100644 --- a/crates/pdf/src/import_scan.rs +++ b/crates/pdf/src/import_scan.rs @@ -678,7 +678,7 @@ impl Walker<'_, '_> { colour = true; break; } - if let Ok(data) = stream.decoded() { + if let Ok(data) = stream.decoded_within(MAX_CHARPROC_BYTES.saturating_sub(decoded_bytes)) { decoded_bytes = decoded_bytes.saturating_add(data.len()); let mut iter = TypedIter::new(data.as_ref()); while let Some(op) = iter.next() { @@ -704,14 +704,7 @@ impl Walker<'_, '_> { }; self.fonts.entry(f_key).or_insert_with(|| { let embedded = embeds_glyphs(&f); - FontNote { - name, - embedded, - vertical: vertical_metrics(&f, embedded), - is_type3, - to_unicode, - is_colour, - } + FontNote { name, embedded, vertical: vertical_metrics(&f, embedded), is_type3, to_unicode, is_colour } }); } } diff --git a/docs/gaps.md b/docs/gaps.md index d386be463f..d5b7d2456b 100644 --- a/docs/gaps.md +++ b/docs/gaps.md @@ -1,6 +1,6 @@ # Gaps -> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-11-13 · **Change:** minor (PDF import: preserve Type 3 font text runs as named groups , or live text if ToUnicode exists, #6740; scores and estimates unchanged; previously: icon-strip flyouts also resize from the top edge and the canvas-side top corner, keeping the moved top in workspaces (storytold/craft-libs#1094); scores and estimates unchanged; previously: #6865: oversized editing artboard import; Glyphs panel: Alternates for Current Selection applies an OpenType feature value (`salt=2`) to the selected letter alone, so an Arabic word stays joined, and Entire Font lists the unencoded glyphs, #5514; #6404: transform-aware Eyedropper character copying; scores and estimates unchanged; previously: G5: built-in Energy Beams brush library; estimate 5–15 → 4–14 h; previously minor: G5: built-in Skill Icons symbol library; estimate 6–16 → 5–15 h; previously minor: ruler double-click adds a guide at the pointer, #5239; G5: built-in Status Effects graphic style library; estimate 7–17 → 6–16 h; MCP docs catch-up for UI Frames and Map Markers; previously minor: G5: built-in Inventory Icons symbol library; estimate 8–18 → 7–17 h; previously minor: G9: `.ai` save names a variable font instance's design-space setting, #6388; previously minor: G5: built-in Game Trails brush library; estimate 9–19 → 8–18 h; previously minor: Edit › Edit Custom Dictionary…: its dialog adds, deletes and changes the custom dictionary's words, #4936; previously minor: docs tidy-up, no content change: stray notes at the top moved into the revision table; scores and estimates unchanged; previously minor: G5: built-in Game HUD graphic style library; estimate 10–20 → 9–19 h; previously minor: G5: built-in Map Markers symbol library; estimate 11–21 → 10–20 h; previously minor: right-click on type: Spelling › Auto Spell Check, with its check mark, #4936; previously minor: right-click on a word Auto Spell Check underlined: its suggestions, Ignore All and Add to Dictionary, #4936; previously minor: G2: Effect Gallery thumbnails and in-dialog preview; estimate 1–2 h → 0; Auto Spell Check: `edit.autoSpellCheck` underlines the unknown words, #4936; previously minor: PDF editable import preserves authored ActualText once, including empty/nested spans and authored ordering (#6165); cross-line/style layout remains approximate; scores and estimates unchanged; PDF marked-text export (#6309): compatible glyphs stay on one layout line with per-glyph offsets; scores and estimates unchanged; G5: built-in UI Frames symbol library with default 9-slice; estimate 11–21 h; previously minor: G5: dynamic symbol part on-canvas takes opacity, blend, gradient and pattern; docs Overlap/auto-corners sync; estimate 12–22 h; previously minor: interim macOS cursor delivery, #2292; native validation pending; scores and estimates unchanged; previously minor: Control bar › Snapping includes the existing Snap to Glyph and Alignment Guides toggles (#6164); scores and estimates unchanged; previously minor: G5 docs: Art brush Overlap, Pattern brush auto corners and Global Edit Shift-click leave-out moved from Missing to Done (code already shipped); previously minor: custom dictionary and Ignore All: `text.spelling.addWord`, `removeWord`, `changeWord`, `dictionary` and `ignoreAll`, #4936; previously minor: EPS import: embedded fonts (composite, Type 42, CID-keyed, Type 1, CFF FontSet) draw their glyphs; spell check engine: `text.spelling.check` and `text.spelling.suggest` on craft-proof, #4936; previously minor: floating tab groups collapse into the side icon rail and opened rail panels fold back into it, #5925 in part; G9: EPS gradients drawn as abutting strips import as one gradient, without seams; Tools stays beside the canvas with left-side panels; new gradients follow document color mode; Selection QoL: marquee modes and visible feedback, #5313; #3380 deletion-only review follow-up: independent survivors keep gradient placement, owned subpaths avoid discarded copies, and contracts/status/evidence are clarified; endpoint highlighting remains open; scores and estimates unchanged; Indian-language hyphenation review and license allowlist, #4947; opening a slow file shows a progress window, part of #5930; Hindi catalog and contributor loanword preferences; agent wording pass complete; wider native proofreading welcome; an image zoomed in on is separated, colorized or proofed only where it shows (#4155); scores and estimates unchanged; a document's first frame renders on the render worker, so opening a heavy file no longer freezes the window; scores and estimates unchanged; large images in CMYK documents are shrunk before they are separated into ink planes (#4155); scores and estimates unchanged; `.ai` paths with a Calligraphic brush open brushed, with the 6D Art Pen's channels, and Windows reads the pen's tilt and barrel rotation, #372; scores and estimates unchanged; Compound Shapes add-on, beyond Illustrator; scores and estimates unchanged; Contour add-on, beyond Illustrator; scores and estimates unchanged; a slow first frame is waited for instead of restarting without the graphics adapter, and the menu bar no longer waits for the installed fonts' scan; scores and estimates unchanged) · **Target:** Adobe Illustrator 2026 (30.x) +> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-11-13 · **Change:** minor (PDF import: preserve Type 3 font text runs as named groups , or as live text in TextAs::Text mode if ToUnicode exists on non-colour fonts (TextAs::Outlines produces grouped outlines), #6740; scores and estimates unchanged; previously: icon-strip flyouts also resize from the top edge and the canvas-side top corner, keeping the moved top in workspaces (storytold/craft-libs#1094); scores and estimates unchanged; previously: #6865: oversized editing artboard import; Glyphs panel: Alternates for Current Selection applies an OpenType feature value (`salt=2`) to the selected letter alone, so an Arabic word stays joined, and Entire Font lists the unencoded glyphs, #5514; #6404: transform-aware Eyedropper character copying; scores and estimates unchanged; previously: G5: built-in Energy Beams brush library; estimate 5–15 → 4–14 h; previously minor: G5: built-in Skill Icons symbol library; estimate 6–16 → 5–15 h; previously minor: ruler double-click adds a guide at the pointer, #5239; G5: built-in Status Effects graphic style library; estimate 7–17 → 6–16 h; MCP docs catch-up for UI Frames and Map Markers; previously minor: G5: built-in Inventory Icons symbol library; estimate 8–18 → 7–17 h; previously minor: G9: `.ai` save names a variable font instance's design-space setting, #6388; previously minor: G5: built-in Game Trails brush library; estimate 9–19 → 8–18 h; previously minor: Edit › Edit Custom Dictionary…: its dialog adds, deletes and changes the custom dictionary's words, #4936; previously minor: docs tidy-up, no content change: stray notes at the top moved into the revision table; scores and estimates unchanged; previously minor: G5: built-in Game HUD graphic style library; estimate 10–20 → 9–19 h; previously minor: G5: built-in Map Markers symbol library; estimate 11–21 → 10–20 h; previously minor: right-click on type: Spelling › Auto Spell Check, with its check mark, #4936; previously minor: right-click on a word Auto Spell Check underlined: its suggestions, Ignore All and Add to Dictionary, #4936; previously minor: G2: Effect Gallery thumbnails and in-dialog preview; estimate 1–2 h → 0; Auto Spell Check: `edit.autoSpellCheck` underlines the unknown words, #4936; previously minor: PDF editable import preserves authored ActualText once, including empty/nested spans and authored ordering (#6165); cross-line/style layout remains approximate; scores and estimates unchanged; PDF marked-text export (#6309): compatible glyphs stay on one layout line with per-glyph offsets; scores and estimates unchanged; G5: built-in UI Frames symbol library with default 9-slice; estimate 11–21 h; previously minor: G5: dynamic symbol part on-canvas takes opacity, blend, gradient and pattern; docs Overlap/auto-corners sync; estimate 12–22 h; previously minor: interim macOS cursor delivery, #2292; native validation pending; scores and estimates unchanged; previously minor: Control bar › Snapping includes the existing Snap to Glyph and Alignment Guides toggles (#6164); scores and estimates unchanged; previously minor: G5 docs: Art brush Overlap, Pattern brush auto corners and Global Edit Shift-click leave-out moved from Missing to Done (code already shipped); previously minor: custom dictionary and Ignore All: `text.spelling.addWord`, `removeWord`, `changeWord`, `dictionary` and `ignoreAll`, #4936; previously minor: EPS import: embedded fonts (composite, Type 42, CID-keyed, Type 1, CFF FontSet) draw their glyphs; spell check engine: `text.spelling.check` and `text.spelling.suggest` on craft-proof, #4936; previously minor: floating tab groups collapse into the side icon rail and opened rail panels fold back into it, #5925 in part; G9: EPS gradients drawn as abutting strips import as one gradient, without seams; Tools stays beside the canvas with left-side panels; new gradients follow document color mode; Selection QoL: marquee modes and visible feedback, #5313; #3380 deletion-only review follow-up: independent survivors keep gradient placement, owned subpaths avoid discarded copies, and contracts/status/evidence are clarified; endpoint highlighting remains open; scores and estimates unchanged; Indian-language hyphenation review and license allowlist, #4947; opening a slow file shows a progress window, part of #5930; Hindi catalog and contributor loanword preferences; agent wording pass complete; wider native proofreading welcome; an image zoomed in on is separated, colorized or proofed only where it shows (#4155); scores and estimates unchanged; a document's first frame renders on the render worker, so opening a heavy file no longer freezes the window; scores and estimates unchanged; large images in CMYK documents are shrunk before they are separated into ink planes (#4155); scores and estimates unchanged; `.ai` paths with a Calligraphic brush open brushed, with the 6D Art Pen's channels, and Windows reads the pen's tilt and barrel rotation, #372; scores and estimates unchanged; Compound Shapes add-on, beyond Illustrator; scores and estimates unchanged; Contour add-on, beyond Illustrator; scores and estimates unchanged; a slow first frame is waited for instead of restarting without the graphics adapter, and the menu bar no longer waits for the installed fonts' scan; scores and estimates unchanged) · **Target:** Adobe Illustrator 2026 (30.x) ## Feature submission evidence @@ -34,7 +34,7 @@ Artboard Options forwards the active Artboard tool's Scale Artwork with Artboard | Date | Change | Summary | |---|---|---| -| 2026-11-13 | minor | PDF import: preserve Type 3 font text runs as named groups ``, or as live text if `/ToUnicode` exists on non-colour fonts (#6740); scores and estimates unchanged. | +| 2026-11-13 | minor | PDF import: preserve Type 3 font text runs as named groups ``, or as live text in `TextAs::Text` mode if `/ToUnicode` exists on non-colour fonts (`TextAs::Outlines` produces grouped outlines) (#6740); scores and estimates unchanged. | | 2026-11-08 | minor | #3380, deletion only: preserve gradient placement and exact surviving geometry; consume owned subpaths with a shared original-node snapshot; consolidate status/evidence and document command/helper contracts. Compound behavior and the existing M2 status entry are retained. Endpoint highlighting remains open; scores and estimates unchanged. | | 2026-11-07 | minor | #3380 (deletion only): independent surviving path objects; transformed appearance, stacking, selection, cubic handles and Undo/Redo covered by `delete_path_pieces`; compound semantics retained. Endpoint highlighting remains open; scores and estimates unchanged. | | 2026-11-07 | minor | Indian-language hyphenation (#4947): eleven MIT-licensed pattern sets, with grapheme and shaping-cluster protection in both composers and existing paragraph limits. Assamese and Sanskrit are selectable; Sanskrit automatic patterns remain pending an allowlisted source. Scores and estimates unchanged. | @@ -193,7 +193,7 @@ macOS cursor delivery (#2292) is narrowed to a native host adapter using the exi ## G9 Illustrator `.ai` and EPS files from real users -- **Done (#6740, 2026-11-13):** PDF import preserves Type 3 font text runs as named groups `` when drawn as outlines or color graphics, and reconstructs live text objects when `/ToUnicode` exists and the font is not a colour font (`crates/pdf/src/tests/text_placement.rs`). Scores and estimates unchanged. +- **Done (#6740, 2026-11-13):** PDF import preserves Type 3 font text runs as named groups `` when drawn as outlines or color graphics, and reconstructs live text objects in `TextAs::Text` mode when `/ToUnicode` exists and the font is not a colour font (`TextAs::Outlines` produces grouped outlines; `crates/pdf/src/tests/text_placement.rs`). Scores and estimates unchanged. - **Done (#6388, 2026-11-10):** a saved `.ai`'s text document names, for type in a variable font, where in the font's design space it is set (each axis, the named instance's or the run's own setting) and the font's version, so Montserrat Bold no longer opens as the font's default instance (Thin); two settings of one font are two fonts of the set. Test `ai_editing::tests::variable_font_instances_name_their_design_space`. The PDF part's font naming (#4548) remains with craft-libs. Estimate unchanged.