//! 1:1 port of `dart-pdf/packages/pdf_graphics/test/interpreter_test.dart` (first tranche) //! Covers path ops (m, l, c, v, y, h, re) and aggregated device path. use nigig_pdf_graphics::content::parse_content_stream; use nigig_pdf_graphics::recording::{RecordingDevice, RenderCommand}; use nigig_pdf_graphics::PdfDevice; fn cmds(content: &[u8]) -> Vec { let ops = parse_content_stream(content).expect("parse"); RecordingDevice::from_ops(&ops) } #[test] fn path_ops_m_l_c_h_re() { // m 10 20, l 30 40, c 1 2 3 4 5 6, h, re let content = b"10 20 m\n30 40 l\n1 2 3 4 5 6 c\nh\n10 20 100 50 re\n"; let ops = parse_content_stream(content).expect("parse"); // m, l, c, h, re assert!(ops.iter().any(|op| matches!(op, nigig_pdf_graphics::content::PdfOp::MoveTo(10.0, 20.0)))); assert!(ops.iter().any(|op| matches!(op, nigig_pdf_graphics::content::PdfOp::LineTo(30.0, 40.0)))); assert!(ops.iter().any(|op| matches!(op, nigig_pdf_graphics::content::PdfOp::CurveTo(1.0, 2.0, 3.0, 4.0, 5.0, 6.0)))); assert!(ops.iter().any(|op| matches!(op, nigig_pdf_graphics::content::PdfOp::ClosePath))); assert!(ops.iter().any(|op| matches!(op, nigig_pdf_graphics::content::PdfOp::Rectangle(10.0, 20.0, 100.0, 50.0)))); let c = cmds(content); assert!(c.iter().any(|c| matches!(c, RenderCommand::MoveTo(10.0, 20.0)))); assert!(c.iter().any(|c| matches!(c, RenderCommand::LineTo(30.0, 40.0)))); assert!(c.iter().any(|c| matches!(c, RenderCommand::CurveTo(1.0, 2.0, 3.0, 4.0, 5.0, 6.0)))); assert!(c.iter().any(|c| matches!(c, RenderCommand::ClosePath))); assert!(c.iter().any(|c| matches!(c, RenderCommand::Rectangle(10.0, 20.0, 100.0, 50.0)))); } #[test] fn path_ops_v_and_y_use_current_point() { // v and y are the two short forms of c that reuse the current point let content_v = b"10 20 m\n30 40 50 60 v\n"; let c_v = cmds(content_v); // v with current (10,20) -> curve (10,20, 30,40, 50,60) assert!(c_v.iter().any(|c| matches!(c, RenderCommand::CurveTo(10.0, 20.0, 30.0, 40.0, 50.0, 60.0)))); let content_y = b"10 20 m\n30 40 50 60 y\n"; let c_y = cmds(content_y); // y with current (10,20) -> curve (30,40, 10,20, 50,60) assert!(c_y.iter().any(|c| matches!(c, RenderCommand::CurveTo(30.0, 40.0, 10.0, 20.0, 50.0, 60.0)))); } #[test] fn graphics_state_save_restore_and_transform() { let content = b"q\n1 0 0 1 10 20 cm\nQ\n"; let c = cmds(content); assert!(c.iter().any(|c| matches!(c, RenderCommand::Save))); assert!(c.iter().any(|c| matches!(c, RenderCommand::SetTransform(1.0, 0.0, 0.0, 1.0, 10.0, 20.0)))); assert!(c.iter().any(|c| matches!(c, RenderCommand::Restore))); } #[test] fn color_ops_produce_set_color() { let content = b"1 0 0 rg\n0.5 g\n1 0 0 RG\n0.5 G\n"; let c = cmds(content); // rg/g are fill (non-stroking), RG/G are stroke (stroking) assert!(c.iter().any(|c| matches!(c, RenderCommand::SetFillColor(_)))); assert!(c.iter().any(|c| matches!(c, RenderCommand::SetStrokeColor(_)))); // Specific values: rg 1 0 0 -> red fill, RG 1 0 0 -> red stroke assert!(c.iter().any(|c| matches!(c, RenderCommand::SetFillColor([r, g, b, _]) if *r==1.0 && *g==0.0 && *b==0.0))); assert!(c.iter().any(|c| matches!(c, RenderCommand::SetStrokeColor([r, g, b, _]) if *r==1.0 && *g==0.0 && *b==0.0))); } #[test] fn text_ops_produce_text_commands() { let content = b"BT\n/F1 12 Tf\n100 700 Td\n(Hello) Tj\nET\n"; let c = cmds(content); assert!(c.iter().any(|c| matches!(c, RenderCommand::BeginText))); assert!(c.iter().any(|c| matches!(c, RenderCommand::EndText))); assert!(c.iter().any(|c| matches!(c, RenderCommand::ShowTextWithMetrics { .. }))); } #[test] fn clipping_ops() { let content = b"10 20 m\n30 40 l\nh\nW\nW*\n"; let c = cmds(content); assert!(c.iter().any(|c| matches!(c, RenderCommand::ClipWinding))); assert!(c.iter().any(|c| matches!(c, RenderCommand::ClipEvenOdd))); } #[test] fn aggregated_path_via_device() { // Direct device aggregated call, as dart's test does with fillPath(path, color, rule, alpha) let mut device = RecordingDevice::new(); let mut builder = nigig_pdf_graphics::path::PdfPathBuilder::new(); builder.move_to(0.0, 0.0); builder.line_to(100.0, 0.0); builder.line_to(100.0, 100.0); builder.close(); let path = builder.take_path(); device.fill_path(path, [1.0, 0.0, 0.0, 1.0], nigig_pdf_graphics::path::PdfFillRule::NonZero, 1.0); assert!(device.commands.iter().any(|c| matches!(c, RenderCommand::Path(_)))); assert!(device.commands.iter().any(|c| matches!(c, RenderCommand::FillWinding))); }