// Regression for #385: text and dimension text must reach the PDF / print // export. From v0.8.2 text renders on-screen only as GPU SDF glyph quads // (`WireModel::text_verts`); the CPU PDF exporter used to draw only wire // stroke `points`, so all text — standalone TEXT/MTEXT and dimension text — // vanished from exported PDFs (present in v0.7.6). // // The op-level placement checks live in `pdf_export`'s own unit tests, which can // see `emit_text`'s ops. What only an integration test can cover is that the // real entity -> scene.entity_wires() -> export_pdf path carries text at all, // per entity kind — so that is what this asserts. use acadrust::entities::{Dimension, DimensionLinear, Text}; use acadrust::types::Vector3; use acadrust::EntityType; use OpenCADStudio::io::pdf_export::{ export_pdf, PdfPageInput, PdfPlotOptions, PlotContent, PlotWire, }; use OpenCADStudio::scene::Scene; #[test] fn text_and_dim_reach_pdf_export() { let mut scene = Scene::new(); let t = Text::with_value("HELLO", Vector3::new(10.0, 10.0, 0.0)).with_height(5.0); let text_h = scene.add_entity(EntityType::Text(t)); // A linear dimension — its measurement value is synthesized as SDF text too, // and dimension text is what #385 was actually filed about. let dim = DimensionLinear::new(Vector3::new(0.0, 0.0, 0.0), Vector3::new(40.0, 0.0, 0.0)); let dim_h = scene.add_entity(EntityType::Dimension(Dimension::Linear(dim))); let wires = scene.entity_wires(); // Assert per entity, not "some wire somewhere has text": both asserts below // would otherwise be satisfied by the TEXT alone, leaving a dimension-text // regression — the actual reported bug — green. // Tessellation names each wire after its entity handle (see the // `w.name.parse::()` lookup in the render pipeline). let has_text = |h: acadrust::types::Handle| { let want = h.value().to_string(); wires .iter() .any(|w| w.name == want && !w.text_verts.is_empty()) }; assert!(has_text(text_h), "TEXT entity emitted no text glyphs"); assert!( has_text(dim_h), "DIMENSION entity emitted no measurement-value text glyphs" ); // End-to-end: the same wire set with and without text. A private temp dir // keeps concurrent runs (and other users on a shared build host) from // colliding on a fixed name in the shared temp root. let dir = std::env::temp_dir().join(format!("ocs385-{}", std::process::id())); std::fs::create_dir_all(&dir).expect("temp dir"); let plot_wires: Vec = wires .iter() .map(|w| PlotWire { wire: w.clone(), draw_depth: 0.0, }) .collect(); let p_text = dir.join("with_text.pdf"); let mut page = PdfPageInput { content: PlotContent { wires: std::sync::Arc::new(plot_wires), ..Default::default() }, paper_w: 210.0, paper_h: 297.0, offset_x: 0.0, offset_y: 0.0, rotation_deg: 0, scale: 1.0, clip: None, options: PdfPlotOptions::default(), plot_style: None, }; export_pdf(&page, &p_text).expect("export with text"); let with_text = std::fs::read(&p_text).expect("read pdf"); assert!(with_text.starts_with(b"%PDF"), "not a PDF"); let stripped: Vec<_> = page .content .wires .iter() .cloned() .map(|mut w| { w.wire.text_verts.clear(); w }) .collect(); let p_bare = dir.join("no_text.pdf"); page.content.wires = std::sync::Arc::new(stripped); export_pdf(&page, &p_bare).expect("export without text"); let no_text = std::fs::read(&p_bare).expect("read pdf"); assert!( with_text.len() > no_text.len(), "text added no content to the PDF: {} !> {}", with_text.len(), no_text.len() ); let _ = std::fs::remove_dir_all(&dir); }