From 15eef03efc6b327b792d2c03dda69d0e40236630 Mon Sep 17 00:00:00 2001 From: Eric Kaden <608341+EricKaden@users.noreply.github.com> Date: Tue, 20 Oct 2026 09:23:32 -0400 Subject: [PATCH] M12: doc comments for the functions around this change --- crates/xfa/src/layout.rs | 15 +++++++++++++++ crates/xfa/src/parse.rs | 3 +++ crates/xfa/src/pdf.rs | 3 +++ crates/xfa/src/tests.rs | 7 +++++++ 4 files changed, 28 insertions(+) diff --git a/crates/xfa/src/layout.rs b/crates/xfa/src/layout.rs index feb8a6fde3..5571bf9934 100644 --- a/crates/xfa/src/layout.rs +++ b/crates/xfa/src/layout.rs @@ -1004,6 +1004,9 @@ impl Layouter<'_> { } } + /// The height a value takes at width `w` in the context's font and paragraph: its text as + /// measured, a picture scaled to `w` keeping its shape, nothing for a line, a rectangle or + /// no value. fn value_height(&self, v: &Value, w: f64, ctx: &Ctx) -> f64 { match v { Value::Text(t) => text::measure_height(&text::plain(t), w, &ctx.para, &Face::of(&ctx.font), &|id| self.embed(id)), @@ -1013,6 +1016,10 @@ impl Layouter<'_> { } } + /// Where a field's caption goes and the room it takes there: its reserve, else the height + /// of its text above or below, or beside it its text's width plus 2pt, at most 60% of + /// `inner_w`. An inline caption goes on the left. `None` for a button, a caption that takes + /// no space, or one with neither text nor a reserve. fn caption_reserve(&self, f: &Field, inner_w: f64, ctx: &Ctx) -> Option<(Placement, f64)> { let cap = f.caption.as_ref()?; if !cap.presence.occupies() || f.ui == Ui::Button { @@ -1037,6 +1044,9 @@ impl Layouter<'_> { Some((placement, reserve)) } + /// The height of a field at `inner_w`: its widget (a check box's size, or its lines of text, + /// grown to hold a long value, plus padding and the widget margin), plus a caption above or + /// below it. A caption beside it makes it at least as tall as the caption's text. fn field_height(&self, f: &Field, inner_w: f64, ctx: &Ctx) -> f64 { let font = ctx.font.apply(&f.common.font); let lines = if f.multiline { 3.0 } else { 1.0 }; @@ -1089,6 +1099,8 @@ impl Layouter<'_> { text::measure_height(&text::plain(value.trim_end()), w, &ctx.para, &Self::field_text_face(f, &font), &|_| String::new()) } + /// The height of an exclusion group's fields at `inner_w`, by the group's layout: stacked + /// top to bottom, at their own positions, or packed in rows. fn group_height(&self, g: &ExclGroup, inner_w: f64, ctx: &Ctx, depth: usize) -> f64 { let nodes: Vec = g.fields.iter().map(|f| Node::Field(Box::new(f.clone()))).collect(); match g.layout { @@ -1457,6 +1469,9 @@ impl Layouter<'_> { self.push(Item::Image { rect: fitted, data: Arc::new(data.to_vec()), content_type: content_type.to_string() }) } + /// The widget name for a field or group named `base`, cleaned (dots become underscores, + /// control characters go): the first use is the name itself, later ones `name_2`, `name_3` + /// and so on. fn unique_name(&mut self, base: &str) -> String { let base = clean_name(base); let n = self.names.entry(base.clone()).or_insert(0); diff --git a/crates/xfa/src/parse.rs b/crates/xfa/src/parse.rs index 3ebdc92c73..e11ce3060e 100644 --- a/crates/xfa/src/parse.rs +++ b/crates/xfa/src/parse.rs @@ -40,6 +40,8 @@ pub fn measure(s: &str) -> Option { Some((v * k).clamp(-100_000.0, 100_000.0)) } +/// The attribute `name` of `n` read as a measurement in points (see [`measure`]); `None` +/// when it is missing or unreadable. fn attr_measure(n: XmlNode, name: &str) -> Option { n.attribute(name).and_then(measure) } @@ -51,6 +53,7 @@ fn scale(v: &str) -> Option { (v.is_finite() && v > 0.0).then(|| (v / 100.0).clamp(0.01, 10.0)) } +/// The first child element of `n` named `name` (by local name, in any namespace). fn child<'a, 'd>(n: XmlNode<'a, 'd>, name: &str) -> Option> { n.children().find(|c| c.is_element() && c.tag_name().name() == name) } diff --git a/crates/xfa/src/pdf.rs b/crates/xfa/src/pdf.rs index 00d473f0da..bbde05ab0d 100644 --- a/crates/xfa/src/pdf.rs +++ b/crates/xfa/src/pdf.rs @@ -509,6 +509,9 @@ impl Emitter<'_> { Some(r) } + /// Write a new image XObject for a picture, tagged with its `hash`: a JPEG as it is, a PNG + /// or GIF decoded to samples with a soft mask for any transparency. `None`, with a warning, + /// when the picture can't be read or is another kind. fn new_image_xobject(&mut self, data: &[u8], content_type: &str, hash: &[u8; 32]) -> Option { let what = if content_type.is_empty() { "picture" } else { content_type }; let mut d = Dict::new(); diff --git a/crates/xfa/src/tests.rs b/crates/xfa/src/tests.rs index 3d01245abd..0645633f11 100644 --- a/crates/xfa/src/tests.rs +++ b/crates/xfa/src/tests.rs @@ -13,14 +13,18 @@ use crate::layout::WidgetKind; use crate::model::*; use crate::script::{NodeKind, Overrides, apply_overrides, fields_by_som, form_tree, overrides, rerender, set_overrides}; +/// The widgets laid out on `page`, in order. fn widgets(page: &Page) -> Vec<&Widget> { page.items.iter().filter_map(|i| if let Item::Widget(w) = i { Some(w.as_ref()) } else { None }).collect() } +/// The text of each text span laid out on `page`, in order. fn texts(page: &Page) -> Vec { page.items.iter().filter_map(|i| if let Item::Text(s) = i { Some(s.text.clone()) } else { None }).collect() } +/// Measurements in points, inches, millimeters and centimeters convert to points, a bare +/// number is points, and an `em` length, a non-number or a NaN is `None`. #[test] fn measurements_convert_to_points() { assert_eq!(measure("72pt"), Some(72.0)); @@ -137,6 +141,9 @@ fn horizontally_scaled_text_is_written_with_tz_inside_its_own_graphics_state() { assert!(ops.lines().filter(|l| l.contains(" Tj ") && !l.contains(" Tz ")).all(|l| l.starts_with("BT ")), "{ops}"); } +/// The sample template parses without warnings into what the layout uses: its page areas, +/// page-number draws, rich-text title, field font, caption and widget border, check box, +/// date field, radio group, table columns and page break all come through. #[test] fn the_template_parses_with_fonts_captions_and_rich_text() { let (tpl, warnings) = parse(&template(2)).unwrap();