mirror of
https://github.com/storytold/pdfcraft.git
synced 2026-10-27 19:48:14 -06:00
M6: a round XFA check box's field highlight is round, as a radio button's is (#4512)
This commit is contained in:
@@ -480,7 +480,7 @@ fn frame(doc: &Document, wd: &Dict, w: f64, h: f64) -> (String, f64) {
|
||||
|
||||
/// Whether `wd` is a check box or radio button PdfCraft laid out for a round XFA check button
|
||||
/// (`/PCXfaRound true`, written by the XFA layout).
|
||||
fn is_round(doc: &Document, wd: &Dict) -> bool {
|
||||
pub(crate) fn is_round(doc: &Document, wd: &Dict) -> bool {
|
||||
matches!(wd.get(b"PCXfaRound").map(|o| doc.resolve(o)).as_deref(), Some(Object::Bool(true)))
|
||||
}
|
||||
|
||||
|
||||
@@ -963,6 +963,7 @@ pub fn add_field(doc: &mut Document, page: usize, rect: [f64; 4], kind: &NewFiel
|
||||
rotation: 0,
|
||||
caption: None,
|
||||
list: None,
|
||||
round: true,
|
||||
};
|
||||
let ap = appearance::check_box_states(doc, &w, FieldKind::Radio, export, "/ZaDb 0 Tf 0 g");
|
||||
doc.update_dict(widget, |d| d.set(b"AP".to_vec(), Object::Dict(ap)))?;
|
||||
|
||||
@@ -118,6 +118,9 @@ pub struct Widget {
|
||||
pub caption: Option<String>,
|
||||
/// List boxes: where the options are drawn, so a click can pick the one under the pointer.
|
||||
pub list: Option<appearance::ListRows>,
|
||||
/// Drawn round: a radio button, or a check box laid out for a round XFA check button
|
||||
/// (`/PCXfaRound true`).
|
||||
pub round: bool,
|
||||
}
|
||||
|
||||
/// A terminal form field.
|
||||
@@ -882,6 +885,7 @@ fn walk(
|
||||
})
|
||||
.flatten(),
|
||||
list: (kind == FieldKind::List).then(|| appearance::list_rows_of(doc, inh.da.as_deref().unwrap_or("/Helv 0 Tf 0 g"), Some(d), wd)),
|
||||
round: kind == FieldKind::Radio || (kind == FieldKind::CheckBox && appearance::is_round(doc, wd)),
|
||||
})
|
||||
})
|
||||
.collect();
|
||||
|
||||
@@ -3860,20 +3860,19 @@ pub fn document_area(app: &mut PdfCraftApp, index: usize, ui: &mut egui::Ui) {
|
||||
}
|
||||
|
||||
// Form-field highlight (Acrobat's "Highlight existing fields"); required fields get a
|
||||
// red border. Radio buttons are round, and so is theirs (as in Acrobat). Only what can
|
||||
// be filled is marked: a read-only widget (its field's, or a radio button's own) is
|
||||
// left as it is drawn.
|
||||
// red border. Radio buttons and round XFA check boxes are round, and so is theirs (as
|
||||
// in Acrobat). Only what can be filled is marked: a read-only widget (its field's, or a
|
||||
// radio button's own) is left as it is drawn.
|
||||
if view.highlight_fields {
|
||||
for f in form.iter() {
|
||||
let required = f.has(pdfcraft_engine::field_flags::REQUIRED);
|
||||
let round = f.kind == pdfcraft_engine::FormFieldKind::Radio;
|
||||
for w in f.widgets.iter().filter(|w| w.page == Some(i) && !w.hidden && !w.read_only) {
|
||||
let r = w.rect;
|
||||
let sr = xf.user_rect(info, i, [r[0] as f32, r[1] as f32, r[2] as f32, r[3] as f32]);
|
||||
let tint = Color32::from_rgba_unmultiplied(0x6E, 0x8E, 0xF5, 48);
|
||||
let (width, color) =
|
||||
if required { (2.0, Color32::from_rgb(0xE3, 0x22, 0x22)) } else { (1.0, Color32::from_rgb(0x6E, 0x8E, 0xF5)) };
|
||||
if round {
|
||||
if w.round {
|
||||
let radius = sr.width().min(sr.height()) / 2.0;
|
||||
painter.circle_filled(sr.center(), radius, tint);
|
||||
painter.circle_stroke(sr.center(), radius - width / 2.0, Stroke::new(width, color));
|
||||
|
||||
@@ -1468,6 +1468,52 @@ fn required_radio_buttons_get_a_round_red_border() {
|
||||
assert!(!stroke(at(50.3, 339.7)), "the widget's corner, outside the circle, is not: {:?}", at(50.3, 339.7));
|
||||
}
|
||||
|
||||
/// Two required check boxes: the first laid out for a round XFA check button (`/PCXfaRound`), the
|
||||
/// second an ordinary square one.
|
||||
const ROUND_CHECKS: &[u8] = b"%PDF-1.7
|
||||
1 0 obj << /Type /Catalog /Pages 2 0 R /AcroForm << /Fields [4 0 R 5 0 R] >> >> endobj
|
||||
2 0 obj << /Type /Pages /Kids [3 0 R] /Count 1 /MediaBox [0 0 300 400] >> endobj
|
||||
3 0 obj << /Type /Page /Parent 2 0 R /Annots [4 0 R 5 0 R] >> endobj
|
||||
4 0 obj << /Type /Annot /Subtype /Widget /FT /Btn /Ff 2 /T (round) /V /Off /AS /Off /Rect [50 300 90 340] /PCXfaRound true /P 3 0 R >> endobj
|
||||
5 0 obj << /Type /Annot /Subtype /Widget /FT /Btn /Ff 2 /T (square) /V /Off /AS /Off /Rect [150 300 190 340] /P 3 0 R >> endobj
|
||||
trailer << /Root 1 0 R >>
|
||||
%%EOF";
|
||||
|
||||
/// With fields highlighted, a round XFA check box gets a round border, as a radio button does,
|
||||
/// while an ordinary check box keeps a square one.
|
||||
#[test]
|
||||
fn round_check_boxes_get_a_round_highlight() {
|
||||
let _gpu = gpu();
|
||||
let mut h = Harness::builder().with_size(egui::vec2(1400.0, 900.0)).build_eframe(|_cc| {
|
||||
let mut app = PdfCraftApp::new();
|
||||
app.set_option("language", "en").unwrap();
|
||||
app.open_bytes("checks.pdf", None, ROUND_CHECKS.to_vec()).expect("opens");
|
||||
app.set_option("left", "closed").unwrap();
|
||||
app.set_option("panel", "none").unwrap();
|
||||
app.set_option("fields", "on").unwrap();
|
||||
app
|
||||
});
|
||||
crate::support::settle(&mut h);
|
||||
let r = rect(&h, 0).expect("page 1");
|
||||
let ppp = h.ctx.pixels_per_point();
|
||||
let img = h.render().expect("renders");
|
||||
let at = |x: f32, y: f32| {
|
||||
let p = egui::pos2(r.min.x + r.width() * (x / 300.0), r.min.y + r.height() * (1.0 - y / 400.0));
|
||||
*img.get_pixel((p.x * ppp) as u32, (p.y * ppp) as u32)
|
||||
};
|
||||
// The red of the required border, or its antialiased edge over the field tint.
|
||||
let stroke = |p: image::Rgba<u8>| p[0] > 180 && p[2] < 140 && i16::from(p[0]) - i16::from(p[2]) > 80;
|
||||
assert!([0.05_f32, 0.2].into_iter().any(|dx| stroke(at(50.0 + dx, 320.0))), "the round box's left edge is red: {:?}", at(50.2, 320.0));
|
||||
// Near the top of the left edge: outside the circle, on a square's border.
|
||||
assert!(!stroke(at(50.25, 339.0)), "the round box's corner, outside the circle, is not: {:?}", at(50.25, 339.0));
|
||||
assert!(
|
||||
[0.1_f32, 0.25, 0.4].into_iter().any(|dx| stroke(at(150.0 + dx, 339.0))),
|
||||
"the square box's is: {:?} {:?}",
|
||||
at(150.1, 339.0),
|
||||
at(150.25, 339.0)
|
||||
);
|
||||
}
|
||||
|
||||
/// Acrobat's Highlight existing fields is a preference, not a per-document choice: turned off
|
||||
/// once, the next document (and the next session) opens with fields plain. New settings start
|
||||
/// with it on.
|
||||
|
||||
@@ -4463,8 +4463,8 @@ milestone = "M3"
|
||||
status = "shipped"
|
||||
commands = []
|
||||
tools = []
|
||||
evidence = ["crates/ui-egui/tests/it/view.rs::highlight_fields_tints_the_field_area", "crates/ui-egui/tests/it/view.rs::required_radio_buttons_get_a_round_red_border", "crates/ui-egui/tests/it/view.rs::field_highlighting_is_on_by_default_and_remembered", "crates/ui-egui/tests/it/view.rs::stored_field_highlight_choice_wins_over_the_default", "crates/ui-egui/tests/it/view.rs::highlight_fields_leaves_read_only_fields_alone", "crates/ui-egui/tests/it/editing.rs::a_secured_form_shows_its_form_notice_and_its_security"]
|
||||
notes = "only fields that can be filled are marked (a read-only field is left as drawn, so a form's locked boxes don't look fillable); offered on secured forms too; on by default for new settings (a stored choice is kept) and remembered for the next document and session; toggled from the form bar and from Preferences > Appearance, where it sets the default for documents opened afterwards; required fields get a red border (round for radio buttons); the tint is drawn over the field, where Acrobat paints the highlight colour behind it"
|
||||
evidence = ["crates/ui-egui/tests/it/view.rs::highlight_fields_tints_the_field_area", "crates/ui-egui/tests/it/view.rs::required_radio_buttons_get_a_round_red_border", "crates/ui-egui/tests/it/view.rs::field_highlighting_is_on_by_default_and_remembered", "crates/ui-egui/tests/it/view.rs::stored_field_highlight_choice_wins_over_the_default", "crates/ui-egui/tests/it/view.rs::highlight_fields_leaves_read_only_fields_alone", "crates/ui-egui/tests/it/editing.rs::a_secured_form_shows_its_form_notice_and_its_security", "crates/ui-egui/tests/it/view.rs::round_check_boxes_get_a_round_highlight"]
|
||||
notes = "only fields that can be filled are marked (a read-only field is left as drawn, so a form's locked boxes don't look fillable); offered on secured forms too; on by default for new settings (a stored choice is kept) and remembered for the next document and session; toggled from the form bar and from Preferences > Appearance, where it sets the default for documents opened afterwards; required fields get a red border (the tint and border round for radio buttons and for check boxes laid out for a round XFA check button); the tint is drawn over the field, where Acrobat paints the highlight colour behind it"
|
||||
|
||||
[[feature]]
|
||||
id = "form.field-appearance-display"
|
||||
|
||||
Reference in New Issue
Block a user