mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-11-13 01:29:04 +00:00
Merge pull request #6797 from dotCooCoo/feat/dxf-leave-out-images-hatches
DXF: export options leave placed images and fill hatches out, and CNC / CAM turns both off
This commit is contained in:
@@ -21,7 +21,7 @@ pub fn specs() -> Vec<CommandSpec> {
|
||||
"Export DXF",
|
||||
[],
|
||||
None,
|
||||
"{path?, dxfPreset?: custom (default)|cnc (CNC / CAM: version 2000, the oldest version that has both lightweight polylines and lineweights; unit mm, scale 1; colors 256, all 2000 holds; preserve editability and alterPaths false, so strokes stay center lines; outlineText true, so type is paths; rasterFormat png; the options given override it), version?: R12|R13|R14|2000|2004|2007|2010|2013|2018 (default), unit?: mm (default; any unit name: pt, in, cm…), scale?: 1 (drawing units per unit), scaleLineweights?: false, colors?: 8|16|256|true (default; true colour needs 2004+, else 256), rasterFormat?: png (default)|jpeg (placed images, written next to the file), preserve?: appearance (default: type as outlines; strokes a CAD line can't draw as filled outlines; brush art)|editability (type as text; every stroke a line with its lineweight and dashes), alterPaths?: false (strokes a CAD line can't draw as their filled outlines, with editability too; also strokes wider than 2.11 mm, and every stroke in R13 and R14), outlineText?: false, selectedOnly?: false (the selected objects, in their layers), artboard?: 0 (the origin is its bottom-left corner), useArtboards?: true (one drawing per chosen artboard, artboards?/range?, holding the art over it: {stem}-{artboard}.dxf) | false (the origin at the art's bounds)} → {path, bytes, warnings, files?, linked?: [image files]}; no path → {dataBase64, …, linked?: [{name, dataBase64}]}. Layers become DXF layers (hidden: off; locked; non-printing: not plotted); paths become polylines and splines, fills their outlines (which laser and cutter software reads) and solid hatches. Same as document.export {format: dxf}. A key DXF doesn't take is refused",
|
||||
"{path?, dxfPreset?: custom (default)|cnc (CNC / CAM: version 2000, the oldest version that has both lightweight polylines and lineweights; unit mm, scale 1; colors 256, all 2000 holds; preserve editability and alterPaths false, so strokes stay center lines; outlineText true, so type is paths; includeImages and includeHatches false, so fills are their outlines and no image files are written; rasterFormat png; the options given override it), version?: R12|R13|R14|2000|2004|2007|2010|2013|2018 (default), unit?: mm (default; any unit name: pt, in, cm…), scale?: 1 (drawing units per unit), scaleLineweights?: false, colors?: 8|16|256|true (default; true colour needs 2004+, else 256), rasterFormat?: png (default)|jpeg (placed images, written next to the file), includeImages?: true (false: placed images and their files are left out), includeHatches?: true (false: fills are their outlines, without solid hatches), preserve?: appearance (default: type as outlines; strokes a CAD line can't draw as filled outlines; brush art)|editability (type as text; every stroke a line with its lineweight and dashes), alterPaths?: false (strokes a CAD line can't draw as their filled outlines, with editability too; also strokes wider than 2.11 mm, and every stroke in R13 and R14), outlineText?: false, selectedOnly?: false (the selected objects, in their layers), artboard?: 0 (the origin is its bottom-left corner), useArtboards?: true (one drawing per chosen artboard, artboards?/range?, holding the art over it: {stem}-{artboard}.dxf) | false (the origin at the art's bounds)} → {path, bytes, warnings, files?, linked?: [image files]}; no path → {dataBase64, …, linked?: [{name, dataBase64}]}. Layers become DXF layers (hidden: off; locked; non-printing: not plotted); paths become polylines and splines, fills their outlines (which laser and cutter software reads) and solid hatches. Same as document.export {format: dxf}. A key DXF doesn't take is refused",
|
||||
has_doc,
|
||||
export_dxf
|
||||
)]
|
||||
@@ -37,7 +37,7 @@ pub(crate) const OPTIONS: &[FormatOption] = &[
|
||||
name: "dxfPreset",
|
||||
ty: "string",
|
||||
default: "\"custom\"",
|
||||
description: "custom, or cnc (CNC / CAM: version 2000, mm, 256 colors, editability, type as outlines, PNG images); the options given override it",
|
||||
description: "custom, or cnc (CNC / CAM: version 2000, mm, 256 colors, editability, type as outlines, no images, no fill hatches); the options given override it",
|
||||
},
|
||||
FormatOption { name: "version", ty: "string", default: "\"2018\"", description: "R12, R13, R14, 2000, 2004, 2007, 2010, 2013 or 2018" },
|
||||
FormatOption {
|
||||
@@ -60,6 +60,18 @@ pub(crate) const OPTIONS: &[FormatOption] = &[
|
||||
default: "\"png\"",
|
||||
description: "png or jpeg: the files placed images are written to, next to the drawing",
|
||||
},
|
||||
FormatOption {
|
||||
name: "includeImages",
|
||||
ty: "boolean",
|
||||
default: "true",
|
||||
description: "placed images are written, linked to their files; false leaves them and their files out",
|
||||
},
|
||||
FormatOption {
|
||||
name: "includeHatches",
|
||||
ty: "boolean",
|
||||
default: "true",
|
||||
description: "filled areas get a solid hatch after their outlines; false writes the outlines alone",
|
||||
},
|
||||
FormatOption {
|
||||
name: "preserve",
|
||||
ty: "string",
|
||||
@@ -133,6 +145,8 @@ struct Params {
|
||||
scale_lineweights: Option<bool>,
|
||||
colors: Option<Value>,
|
||||
raster_format: Option<String>,
|
||||
include_images: Option<bool>,
|
||||
include_hatches: Option<bool>,
|
||||
preserve: Option<String>,
|
||||
alter_paths: Option<bool>,
|
||||
outline_text: Option<bool>,
|
||||
@@ -176,6 +190,8 @@ fn parse(p: &Value) -> Result<(DxfOptions, ArtboardPick)> {
|
||||
scale_lineweights: q.scale_lineweights.unwrap_or(d.scale_lineweights),
|
||||
colors: choice("colors", q.colors.as_ref().map(text), ColorDepth::from_id, d.colors, "8, 16, 256 or true")?,
|
||||
raster: choice("rasterFormat", q.raster_format, RasterFormat::from_id, d.raster, "png or jpeg")?,
|
||||
include_images: q.include_images.unwrap_or(d.include_images),
|
||||
include_hatches: q.include_hatches.unwrap_or(d.include_hatches),
|
||||
preserve: choice("preserve", q.preserve, Preserve::from_id, d.preserve, "appearance or editability")?,
|
||||
alter_paths: q.alter_paths.unwrap_or(d.alter_paths),
|
||||
outline_text: q.outline_text.unwrap_or(d.outline_text),
|
||||
|
||||
@@ -84,6 +84,11 @@ fn the_cnc_preset_sets_the_options_that_are_not_given() {
|
||||
true
|
||||
)
|
||||
);
|
||||
assert_eq!((o.include_images, o.include_hatches), (false, false), "CNC / CAM leaves out images and fill hatches");
|
||||
let given = dxf::options(&json!({"dxfPreset": "cnc", "includeImages": true, "includeHatches": true})).unwrap();
|
||||
assert_eq!((given.include_images, given.include_hatches), (true, true), "the options given override it");
|
||||
let custom = dxf::options(&json!({})).unwrap();
|
||||
assert_eq!((custom.include_images, custom.include_hatches), (true, true), "Custom keeps both");
|
||||
assert_eq!(dxf::options(&json!({})).unwrap().version, vectorcraft_cad::DxfVersion::R2018, "custom by default");
|
||||
let e = s.execute("document.exportDxf", &json!({"dxfPreset": "laser"})).unwrap_err().to_string();
|
||||
assert!(e.contains("custom or cnc"), "{e}");
|
||||
@@ -151,6 +156,44 @@ fn placed_images_come_back_as_linked_files() {
|
||||
let _ = std::fs::remove_dir_all(dir);
|
||||
}
|
||||
|
||||
/// `includeImages` and `includeHatches` leave out placed images (with their files) and fill
|
||||
/// hatches, each on its own, through both commands, with and without a path; CNC / CAM leaves
|
||||
/// out both, and the options given override it.
|
||||
#[test]
|
||||
fn placed_images_and_fill_hatches_can_be_left_out() {
|
||||
let mut s = super::tests_svg::image_session();
|
||||
s.execute("paint.setFill", &json!({"color": "#ff0000"})).unwrap();
|
||||
s.execute("paint.setStroke", &json!({"none": true})).unwrap();
|
||||
s.execute("shape.rectangle", &json!({"x": 0, "y": 0, "width": 1, "height": 1})).unwrap();
|
||||
let counts = |r: &Value| {
|
||||
let e = entities(&text(r));
|
||||
let n = |k: &str| e.iter().filter(|x| *x == k).count();
|
||||
(n("LWPOLYLINE"), n("HATCH"), n("IMAGE"), r.get("linked").and_then(Value::as_array).map_or(0, Vec::len))
|
||||
};
|
||||
let warned = |r: &Value, w: &str| r["warnings"].as_array().unwrap().iter().any(|x| x.as_str().is_some_and(|x| x.starts_with(w)));
|
||||
let all = s.execute("document.export", &json!({"format": "dxf"})).unwrap();
|
||||
assert_eq!(counts(&all), (1, 1, 1, 1));
|
||||
let r = s.execute("document.export", &json!({"format": "dxf", "includeImages": false})).unwrap();
|
||||
assert_eq!(counts(&r), (1, 1, 0, 0));
|
||||
assert!(warned(&r, "placed images are left out") && !warned(&r, "fill hatches"), "{r}");
|
||||
let r = s.execute("document.exportDxf", &json!({"includeHatches": false})).unwrap();
|
||||
assert_eq!(counts(&r), (1, 0, 1, 1));
|
||||
assert!(warned(&r, "fill hatches are left out") && !warned(&r, "placed images"), "{r}");
|
||||
let r = s.execute("document.exportDxf", &json!({"dxfPreset": "cnc"})).unwrap();
|
||||
assert_eq!(counts(&r), (1, 0, 0, 0));
|
||||
let r = s.execute("document.export", &json!({"format": "dxf", "dxfPreset": "cnc", "includeImages": true})).unwrap();
|
||||
assert_eq!(counts(&r), (1, 0, 1, 1));
|
||||
// With a path, no image file is written next to the drawing.
|
||||
let dir = std::env::temp_dir().join(format!("vc-dxf-out-{}", std::process::id()));
|
||||
std::fs::create_dir_all(&dir).unwrap();
|
||||
let path = dir.join("cut.dxf").to_string_lossy().to_string();
|
||||
let r = s.execute("document.exportDxf", &json!({"path": path, "dxfPreset": "cnc"})).unwrap();
|
||||
assert!(r.get("linked").is_none(), "{r}");
|
||||
let files: Vec<String> = std::fs::read_dir(&dir).unwrap().filter_map(|e| e.ok()).map(|e| e.file_name().to_string_lossy().to_string()).collect();
|
||||
assert_eq!(files, ["cut.dxf"]);
|
||||
let _ = std::fs::remove_dir_all(dir);
|
||||
}
|
||||
|
||||
/// Two placed images whose pixel data can't be decoded are left out of the drawing and get no
|
||||
/// linked file, and `warnings` holds the note raster exports give, once, without naming either
|
||||
/// image's key.
|
||||
@@ -181,6 +224,8 @@ fn bad_dxf_options_are_refused() {
|
||||
json!({"rasterFormat": "gif"}),
|
||||
json!({"preserve": "both"}),
|
||||
json!({"alterPaths": "yes"}),
|
||||
json!({"includeImages": "no"}),
|
||||
json!({"includeHatches": 0}),
|
||||
] {
|
||||
let e = s.execute("document.exportDxf", &p).unwrap_err().to_string();
|
||||
assert!(e.contains("DXF"), "{p}: {e}");
|
||||
@@ -203,6 +248,8 @@ fn formats_list_dxf_and_what_can_not_be_written() {
|
||||
"scaleLineweights",
|
||||
"colors",
|
||||
"rasterFormat",
|
||||
"includeImages",
|
||||
"includeHatches",
|
||||
"preserve",
|
||||
"alterPaths",
|
||||
"outlineText",
|
||||
|
||||
Reference in New Issue
Block a user