use oxideav_core::vector::{Node, Paint};
use oxideav_pdf::read_pdf_to_scene;
fn build_shading_pattern_pdf() -> Vec<u8> {
let mut bytes: Vec<u8> = Vec::with_capacity(1024);
bytes.extend_from_slice(b"%PDF-1.5\n%\xE2\xE3\xCF\xD3\n");
let mut offsets: [u64; 5] = [0; 5];
offsets[1] = bytes.len() as u64;
bytes.extend_from_slice(b"1 0 obj\n<< /Type /Catalog /Pages 2 0 R >>\nendobj\n");
offsets[2] = bytes.len() as u64;
bytes.extend_from_slice(b"2 0 obj\n<< /Type /Pages /Kids [3 0 R] /Count 1 >>\nendobj\n");
let content: &[u8] = b"/Pattern cs /P0 scn 0 0 m 100 0 l 100 100 l h f\n";
offsets[3] = bytes.len() as u64;
bytes.extend_from_slice(
b"3 0 obj\n<< /Type /Page /Parent 2 0 R /MediaBox [0 0 100 100] \
/Contents 4 0 R \
/Resources << /Pattern << /P0 << \
/Type /Pattern /PatternType 2 \
/Shading << /ShadingType 2 /ColorSpace /DeviceRGB \
/Coords [0 0 100 0] \
/Function << /FunctionType 2 /Domain [0 1] \
/C0 [0 0 0] /C1 [1 1 1] /N 1 >> \
>> \
>> >> >> \
>>\nendobj\n",
);
offsets[4] = bytes.len() as u64;
bytes.extend_from_slice(
format!("4 0 obj\n<< /Length {} >>\nstream\n", content.len()).as_bytes(),
);
bytes.extend_from_slice(content);
bytes.extend_from_slice(b"\nendstream\nendobj\n");
let xref_off = bytes.len() as u64;
bytes.extend_from_slice(b"xref\n");
bytes.extend_from_slice(b"0 5\n");
bytes.extend_from_slice(b"0000000000 65535 f \n");
for off in offsets.iter().skip(1) {
bytes.extend_from_slice(format!("{:010} 00000 n \n", off).as_bytes());
}
bytes.extend_from_slice(b"trailer\n");
bytes.extend_from_slice(b"<< /Size 5 /Root 1 0 R >>\n");
bytes.extend_from_slice(format!("startxref\n{}\n%%EOF\n", xref_off).as_bytes());
bytes
}
fn first_fill(node: &Node) -> Option<&Paint> {
match node {
Node::Path(p) => p.fill.as_ref(),
Node::Group(g) => g.children.iter().find_map(first_fill),
_ => None,
}
}
#[test]
fn shading_pattern_fill_resolves_to_gradient() {
let pdf = build_shading_pattern_pdf();
assert!(pdf.len() <= 10 * 1024, "fixture under 10 KB");
let scene = read_pdf_to_scene(&pdf).expect("read PDF to scene");
let pages = scene.pages.as_ref().expect("scene has pages");
assert_eq!(pages.len(), 1);
let fill = pages[0]
.content
.root
.children
.iter()
.find_map(first_fill)
.expect("a painted path with a fill");
let Paint::LinearGradient(lg) = fill else {
panic!("expected a linear gradient fill, got {fill:?}");
};
assert!((lg.start.x - 0.0).abs() < 1e-2 && (lg.end.x - 100.0).abs() < 1e-2);
assert!(!lg.stops.is_empty());
assert_eq!(
(
lg.stops[0].color.r,
lg.stops[0].color.g,
lg.stops[0].color.b
),
(0, 0, 0)
);
let last = lg.stops.last().unwrap();
assert_eq!((last.color.r, last.color.g, last.color.b), (255, 255, 255));
}