use rustyfi_backend::{
Closing, Color, GraphicsElem, Length, Page, PageGeometry, Path, PathSeg, PlacedLine,
PureHorzBox, Subpath,
};
fn rectangle_path() -> Path {
Path {
subpaths: vec![Subpath {
start: (Length::pt(0.0), Length::pt(0.0)),
segs: vec![
PathSeg::Line((Length::pt(20.0), Length::pt(0.0))),
PathSeg::Line((Length::pt(20.0), Length::pt(20.0))),
PathSeg::Line((Length::pt(0.0), Length::pt(20.0))),
],
closing: Closing::Line,
}],
}
}
fn page_with_graphics_box() -> Page {
let path = rectangle_path();
let elems = vec![
GraphicsElem::Fill(Color::Rgb(1.0, 0.0, 0.0), path.clone()),
GraphicsElem::Stroke(Length::pt(1.0), Color::Gray(0.0), path),
];
let gbox = PureHorzBox::Graphics {
width: Length::pt(20.0),
height: Length::pt(20.0),
depth: Length::pt(0.0),
elems,
origin_independent: false,
};
Page {
body_lines: usize::MAX,
lines: vec![PlacedLine {
x: Length::pt(50.0),
baseline_y: Length::pt(100.0),
contents: vec![(Length::ZERO, gbox)],
}],
}
}
#[test]
fn graphics_box_renders_path_operators_into_the_content_stream() {
let geometry = PageGeometry {
paper_width: Length::pt(200.0),
paper_height: Length::pt(300.0),
text_origin: (Length::pt(20.0), Length::pt(20.0)),
text_width: Length::pt(160.0),
text_height: Length::pt(260.0),
};
let page = page_with_graphics_box();
let bytes = rustyfi_pdf::render_pdf(&geometry, std::slice::from_ref(&page), &[])
.expect("PDF rendering must succeed");
assert!(bytes.starts_with(b"%PDF-"), "not a PDF header");
let hay = String::from_utf8_lossy(&bytes);
for op in ["0 0 m", "20 0 l", "20 20 l", "0 20 l", "\nh\n"] {
assert!(hay.contains(op), "content stream missing {op:?}:\n{hay}");
}
for op in ["1 0 0 rg", "f*", "1 w", "0 G", "\nS\n"] {
assert!(hay.contains(op), "content stream missing {op:?}:\n{hay}");
}
let expected_ty = geometry.paper_height.0 - 100.0;
let expected_cm = format!("1 0 0 1 50 {expected_ty} cm");
assert!(
hay.contains(&expected_cm),
"content stream missing the box's placement transform {expected_cm:?}:\n{hay}"
);
}
fn render_elems(elems: Vec<GraphicsElem>) -> String {
let geometry = PageGeometry {
paper_width: Length::pt(200.0),
paper_height: Length::pt(300.0),
text_origin: (Length::pt(20.0), Length::pt(20.0)),
text_width: Length::pt(160.0),
text_height: Length::pt(260.0),
};
let gbox = PureHorzBox::Graphics {
width: Length::pt(20.0),
height: Length::pt(20.0),
depth: Length::pt(0.0),
elems,
origin_independent: false,
};
let page = Page {
body_lines: usize::MAX,
lines: vec![PlacedLine {
x: Length::pt(50.0),
baseline_y: Length::pt(100.0),
contents: vec![(Length::ZERO, gbox)],
}],
};
let bytes = rustyfi_pdf::render_pdf(&geometry, std::slice::from_ref(&page), &[])
.expect("PDF rendering must succeed");
String::from_utf8_lossy(&bytes).into_owned()
}
#[test]
fn clip_emits_w_star_n_between_the_clip_path_and_its_contents() {
let clip_path = rectangle_path();
let fill = GraphicsElem::Fill(Color::Rgb(1.0, 0.0, 0.0), rectangle_path());
let hay = render_elems(vec![GraphicsElem::Clip(clip_path, vec![fill])]);
let path_pos = hay.find("0 0 m").expect("clip path move-to");
let w_pos = hay.find("\nW*\n").expect("W* clip op");
let n_pos = hay.find("\nn\n").expect("n end-path op");
let fill_pos = hay.find("f*").expect("fill paint op");
assert!(
path_pos < w_pos && w_pos < n_pos && n_pos < fill_pos,
"expected path < W* < n < fill, got positions {path_pos} < {w_pos} < {n_pos} < {fill_pos}:\n{hay}"
);
}
#[test]
fn group_renders_identically_to_its_flattened_element_sequence() {
let fill = GraphicsElem::Fill(Color::Rgb(1.0, 0.0, 0.0), rectangle_path());
let stroke = GraphicsElem::Stroke(Length::pt(1.0), Color::Gray(0.0), rectangle_path());
let grouped = render_elems(vec![GraphicsElem::Group(vec![fill.clone(), stroke.clone()])]);
let flat = render_elems(vec![fill, stroke]);
for op in ["1 0 0 rg", "f*", "1 w", "0 G", "\nS\n"] {
assert!(grouped.contains(op), "grouped stream missing {op:?}:\n{grouped}");
assert!(flat.contains(op), "flat stream missing {op:?}:\n{flat}");
}
}