Files
cadcraft/crates/io/tests/plot_styles.rs
T
Valentin KleinandClaude Opus 5.5 ee34f423cc Plot styles: colour-dependent tables apply to PDF plots and layouts (part of #410) (#412)
A PlotStyleTable model in doc with our own built-in tables (default.ctb keeps
object properties, monochrome.ctb prints black, grayscale.ctb prints the grey of
each colour's luminance). The render builder resolves each object's style by its
index colour (true colours by the nearest index) and applies colour, screening,
linetype and lineweight. PLOT/EXPORTPDF use the page setup's table (or the
plotStyleTable option); layouts with Display plot styles (PAGESETUP
displayPlotStyles, DXF plot flag 2) show it on screen and in PNG/SVG exports.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 19:19:48 -04:00

63 lines
2.8 KiB
Rust

//! Plot style tables in plots and exports (issue #410): the PDF content stream carries the
//! table's colours and pen widths, layout SVG exports follow "Display plot styles", and the
//! page setup's table and flag survive a DXF round trip.
use cadcraft_color::Color;
use cadcraft_doc::{Common, Drawing, EntityKind, Line, Space};
use cadcraft_geom::Vec3;
use cadcraft_io::{read_dxf, svg, write_dxf};
use serde_json::json;
fn layout_with_red_line() -> Drawing {
let mut d = Drawing::new_metric();
let line = EntityKind::Line(Line { a: Vec3::new(10.0, 10.0, 0.0), b: Vec3::new(100.0, 10.0, 0.0) });
d.add(&Space::Paper("Layout1".into()), Common { color: Color::Index(1), ..Default::default() }, line).unwrap();
d
}
/// The uncompressed PDF of Layout1 with extra plot options.
fn pdf_text(d: &Drawing, extra: serde_json::Value) -> String {
let mut opts = json!({"compress": false});
if let (Some(o), Some(e)) = (opts.as_object_mut(), extra.as_object()) {
o.extend(e.clone());
}
let bytes = cadcraft_io::plot(d, &Space::Paper("Layout1".into()), &opts).unwrap();
String::from_utf8_lossy(&bytes).into_owned()
}
#[test]
fn pdf_uses_the_page_setup_table() {
let mut d = layout_with_red_line();
assert!(pdf_text(&d, json!({})).contains("1 0 0 RG"), "no table: red");
d.layouts[0].page.plot_style_table = "monochrome.ctb".into();
let mono = pdf_text(&d, json!({}));
assert!(mono.contains("0 0 0 RG") && !mono.contains("1 0 0 RG"), "{mono}");
// The plot's own option replaces the page setup's; "None" plots object colours.
assert!(pdf_text(&d, json!({"plotStyleTable": "None"})).contains("1 0 0 RG"));
let gray = pdf_text(&d, json!({"plotStyleTable": "grayscale.ctb"}));
assert!(!gray.contains("1 0 0 RG") && !gray.contains("0 0 0 RG"), "red prints mid grey: {gray}");
}
#[test]
fn layout_svg_follows_display_plot_styles() {
let mut d = layout_with_red_line();
d.layouts[0].page.plot_style_table = "monochrome.ctb".into();
let space = Space::Paper("Layout1".into());
assert!(svg::export(&d, &space).contains("#ff0000"));
d.layouts[0].page.show_plot_styles = true;
let s = svg::export(&d, &space);
assert!(!s.contains("#ff0000") && s.contains("#000000"), "{s}");
}
#[test]
fn page_setup_table_and_display_flag_round_trip() {
let mut d = layout_with_red_line();
d.layouts[0].page.plot_style_table = "grayscale.ctb".into();
d.layouts[0].page.show_plot_styles = true;
let back = read_dxf(write_dxf(&d).as_bytes()).unwrap();
let page = &back.layout("Layout1").unwrap().page;
assert_eq!(page.plot_style_table, "grayscale.ctb");
assert!(page.show_plot_styles);
assert!(!read_dxf(write_dxf(&layout_with_red_line()).as_bytes()).unwrap().layout("Layout1").unwrap().page.show_plot_styles);
}