diff --git a/crates/engine/src/tests.rs b/crates/engine/src/tests.rs
index 94867f98e5..5e485799b4 100644
--- a/crates/engine/src/tests.rs
+++ b/crates/engine/src/tests.rs
@@ -3790,6 +3790,45 @@ fn printing_lays_out_sheets_that_render() {
assert_eq!(s4.print_pdf(id4, &print::Settings { pages: vec![0], ..Default::default() }), Err(EditError::NotPermitted("printing")));
}
+/// A dynamic XFA form's screen-only and print-only objects (`relevant`): on screen, the
+/// screen-only field and draw show and the print-only ones don't; printed, the screen-only field
+/// is left off and the print-only one is on paper. (Draws follow the layers' state on screen
+/// when printed until printing applies the layers' print usage; other viewers read it from the
+/// file.)
+#[test]
+fn xfa_screen_only_and_print_only_objects_show_and_print_where_the_form_says() {
+ let field = |name: &str, attrs: &str| {
+ format!(
+ r#"{name} value"#
+ )
+ };
+ let draw = |name: &str, attrs: &str| {
+ format!(r#"{name} text"#)
+ };
+ let xdp = format!(
+ r##"
+
+
+ {}{}{}{}
+"##,
+ field("screenField", r#"relevant="-print""#),
+ field("paperField", r#"presence="invisible" relevant="+print""#),
+ draw("screenDraw", r#"relevant="-print""#),
+ draw("paperDraw", r#"presence="invisible" relevant="+print""#),
+ );
+ let mut s = Session::new();
+ let id = s.open("form.pdf", None, Arc::new(pdfcraft_xfa::fixtures::shell(&xdp)), None).expect("opens");
+ let screen = page_texts(&s, id).join("\n");
+ assert!(screen.contains("screenField value") && screen.contains("screenDraw text"), "on screen: {screen}");
+ assert!(!screen.contains("paperField value") && !screen.contains("paperDraw text"), "on screen: {screen}");
+ let sheet = s.print_pdf(id, &print::Settings { pages: vec![0], ..Default::default() }).unwrap();
+ let mut out = Session::new();
+ let printed = out.open("print.pdf", None, Arc::new(sheet), None).unwrap();
+ let paper = page_texts(&out, printed).join("\n");
+ assert!(!paper.contains("screenField value"), "printed: {paper}");
+ assert!(paper.contains("paperField value"), "printed: {paper}");
+}
+
/// A one-page document whose red square is in a layer; `off` switches the layer off by default.
fn red_layer_pdf(off: bool) -> Vec {
let content = "/OC /L1 BDC 1 0 0 rg 0 0 100 100 re f EMC";
diff --git a/crates/xfa/src/layout.rs b/crates/xfa/src/layout.rs
index e98ca1f2df..93da598912 100644
--- a/crates/xfa/src/layout.rs
+++ b/crates/xfa/src/layout.rs
@@ -16,7 +16,7 @@ use crate::XfaError;
use crate::data::{DataNode, DataPath, data_date_letters, data_path, data_to_iso, date_letters, is_on, iso_to_pattern};
use crate::model::{
Access, Area, Border, Common, Draw, ExclGroup, Field, Font, HAlign, Layout, Margin, Node, Occur, PageArea, PageRole, Para, Placement, Presence,
- Rect, Script, SomIndices, Stroke, Subform, Template, Ui, VAlign, Value,
+ Rect, Script, SomIndices, Stroke, Subform, Template, Ui, VAlign, Value, Views,
};
use crate::text::{self, Block, Face, Span};
@@ -180,6 +180,8 @@ struct Layouter<'t> {
splitting: Option,
/// The items `draw_field` drew for the border of the field drawn last: (first, one past the last).
field_border: Option<(usize, usize)>,
+ /// Where what is being drawn shows, as the objects it is inside say ([`Common::views`]).
+ views: Views,
/// The flowed subforms being laid out, outermost first: a break between their children
/// closes their parts on one content area and opens new ones on the next.
frames: Vec,
@@ -230,6 +232,7 @@ pub fn layout(tpl: &Template, data: Option<&DataNode>) -> Result