From caeaac5cd44d653612ba4fbfeef9076b7f317516 Mon Sep 17 00:00:00 2001 From: aiongg Date: Thu, 22 Oct 2026 04:15:17 +0000 Subject: [PATCH 1/3] =?UTF-8?q?Integration:=20render=20test=20expects=20th?= =?UTF-8?q?e=20RGB=20PNG=20of=20an=20opaque=20page=20(#2825=20=C3=97=20#24?= =?UTF-8?q?25)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #2425 writes opaque rasters as 8-bit RGB; the owner-password render test was written against the RGBA encoder. A rendered page is always opaque, so the test asserts RGB and still counts inked pixels. --- apps/pdfcraft-cli/tests/owner_password.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/pdfcraft-cli/tests/owner_password.rs b/apps/pdfcraft-cli/tests/owner_password.rs index 3542a7d4c..679698098 100644 --- a/apps/pdfcraft-cli/tests/owner_password.rs +++ b/apps/pdfcraft-cli/tests/owner_password.rs @@ -108,10 +108,10 @@ fn assert_rendered_page(data: &[u8], dpi: u32, what: &str) { let mut buf = vec![0; reader.output_buffer_size().unwrap()]; let frame = reader.next_frame(&mut buf).unwrap(); assert_eq!((frame.width, frame.height), (200 * dpi / 72, 300 * dpi / 72), "{what}"); - // `render` writes 8-bit RGBA (`pdfcraft_engine::export::encode_png`). - assert_eq!((frame.color_type, frame.bit_depth), (png::ColorType::Rgba, png::BitDepth::Eight), "{what}"); - // Opaque pixels with every colour channel below half intensity: ink, not white paper. - let inked = buf[..frame.buffer_size()].as_chunks::<4>().0.iter().filter(|[r, g, b, a]| [r, g, b].iter().all(|c| **c < 128) && *a > 128).count(); + // A rendered page is opaque, so `render` writes it as 8-bit RGB (`pdfcraft_engine::export::encode_png`). + assert_eq!((frame.color_type, frame.bit_depth), (png::ColorType::Rgb, png::BitDepth::Eight), "{what}"); + // Pixels with every colour channel below half intensity: ink, not white paper. + let inked = buf[..frame.buffer_size()].as_chunks::<3>().0.iter().filter(|px| px.iter().all(|c| *c < 128)).count(); assert!(inked > 100, "{what}: only {inked} inked pixels, the page is blank"); } From 55e32759b1a363a34fffaa3c50680bcaa4624fa1 Mon Sep 17 00:00:00 2001 From: aiongg Date: Thu, 22 Oct 2026 04:15:17 +0000 Subject: [PATCH 2/3] =?UTF-8?q?Integration:=20subset=20fallback=20test=20n?= =?UTF-8?q?ames=20the=20face=20the=20build=20leads=20with=20(#2609=20?= =?UTF-8?q?=C3=97=20#1760)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #2609 put the Japanese faces ahead of the bundled Inter face and rewrote the #1760 test to expect Inter, which holds only without craft-fonts. With CRAFT_FONTS_DIR, BIZ UDPGothic has ő and draws it. The test now expects the Japanese sans face when the build has one, Inter otherwise. --- crates/edit/src/tests.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/crates/edit/src/tests.rs b/crates/edit/src/tests.rs index 5b76060c5..cf3860237 100644 --- a/crates/edit/src/tests.rs +++ b/crates/edit/src/tests.rs @@ -5342,10 +5342,12 @@ fn a_shaped_run_is_one_text_object_with_each_glyph_where_shaping_put_it() { #[test] fn a_subset_program_is_not_trusted_for_new_characters_and_the_error_names_the_character() { let mut doc = embedded_program_page("BT /F1 12 Tf 72 700 Td (Stable text) Tj ET", "ABCDEF+Inter-Regular"); - // The subset's own program is not trusted for a character: the bundled Inter face draws it - // through a generated font instead (the subset may map a code whose outline was dropped). + // The subset's own program is not trusted for a character: a fallback face draws it through a + // generated font instead (the subset may map a code whose outline was dropped). The Japanese + // sans face leads the fallbacks when built with craft-fonts; the bundled Inter face otherwise. let edit = text::rewrite_block(&mut doc, 0, 0, Some("Stable tőxt"), &text::BlockStyle::default()).unwrap(); - assert_eq!(edit.substituted.as_deref(), Some("Inter Type3")); + let face = pdfcraft_fonts::document_japanese_font_for_style(false, false).map_or("Inter", |f| f.family); + assert_eq!(edit.substituted, Some(format!("{face} Type3"))); assert_eq!(text::text_blocks(&reopen(&doc), 0).unwrap()[0].editor_text, "Stable tőxt"); // A character no fallback face has is refused, named, and left as it was. let mut doc = embedded_program_page("BT /F1 12 Tf 72 700 Td (Stable text) Tj ET", "ABCDEF+Inter-Regular"); From f84eeaed7ace48fba7752dd09714c64d57b0b247 Mon Sep 17 00:00:00 2001 From: aiongg Date: Thu, 22 Oct 2026 04:15:17 +0000 Subject: [PATCH 3/3] =?UTF-8?q?Integration:=20a=20non-interactive=20XFA=20?= =?UTF-8?q?field=20is=20measured=20at=20the=20width=20it=20is=20drawn=20(#?= =?UTF-8?q?3515=20=C3=97=20#3521)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #3515 measures a growing non-interactive field at its text's scale but with a widget's 4pt of padding, while the layout draws its text without it. Once #3521 fits lines by their ink, the narrower measuring width wrapped to one more line than the drawn text, leaving a blank line below it. The measurement leaves out the padding the draw doesn't have. --- crates/xfa/src/layout.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/crates/xfa/src/layout.rs b/crates/xfa/src/layout.rs index b2c17a059..326e7f741 100644 --- a/crates/xfa/src/layout.rs +++ b/crates/xfa/src/layout.rs @@ -1148,12 +1148,15 @@ impl Layouter<'_> { } let value = self.data.and_then(|d| d.value_at(&data_path(self.tpl, &ctx.som))).filter(|t| !t.trim().is_empty()).or_else(|| f.value.plain()); let Some(value) = value.filter(|t| !t.trim().is_empty()) else { return 0.0 }; - // Beside a caption the text has what the caption leaves; 2pt of padding on each side. + // Beside a caption the text has what the caption leaves; a widget's text has 2pt of + // padding on each side, a non-interactive field's (drawn as text) none, so it wraps + // where it is drawn. let beside = match self.caption_reserve(f, inner_w, ctx) { Some((Placement::Top | Placement::Bottom, _)) | None => 0.0, Some((_, r)) => r, }; - let w = (inner_w - beside - f.ui_margin.horizontal() - 4.0).max(1.0); + let padding = if f.access == Access::NonInteractive { 0.0 } else { 4.0 }; + let w = (inner_w - beside - f.ui_margin.horizontal() - padding).max(1.0); let font = ctx.font.apply(&f.common.font); text::measure_height(&text::plain(value.trim_end()), w, &ctx.para, &Self::field_text_face(f, &font), &|_| String::new()) }