mirror of
https://github.com/storytold/cadcraft.git
synced 2026-11-10 12:30:16 -06:00
OLE2FRAME: draw its frame outline; a DWG save no longer lists it as left out (#2422)
The frame is the rectangle from the entity's two corner points (groups 10/20 and 11/21). A DWG write and read-back keeps the OLE data (ole_frame.rs), so the DWG note leaves the type out of notSaved.
This commit is contained in:
@@ -720,6 +720,10 @@ pub fn not_saved(d: &Drawing) -> std::collections::BTreeMap<String, usize> {
|
||||
/// store section, which is written back with the drawing (`Foreign::data_store`).
|
||||
const MODELER_TYPES: [&str; 3] = ["3DSOLID", "REGION", "BODY"];
|
||||
|
||||
/// Types the DWG bridge writes back whole although CADCraft does not model them: a DWG save and
|
||||
/// read-back keeps their data (`ole_frame.rs`, #2422), so they are not "left out".
|
||||
const DWG_KEPT_TYPES: [&str; 1] = ["OLE2FRAME"];
|
||||
|
||||
/// [`not_saved`] for a file saved as `version`. From 2013 on, a 3D solid, region or body the data
|
||||
/// store has a record for keeps its geometry and is not counted.
|
||||
pub fn not_saved_as(d: &Drawing, version: Version) -> std::collections::BTreeMap<String, usize> {
|
||||
@@ -728,7 +732,9 @@ pub fn not_saved_as(d: &Drawing, version: Version) -> std::collections::BTreeMap
|
||||
let stores = std::iter::once(&d.model).chain(d.layouts.iter().map(|l| &l.entities)).chain(d.blocks.values().map(|b| &b.entities));
|
||||
for e in stores.flat_map(|s| s.iter()) {
|
||||
if let cadcraft_doc::EntityKind::Unknown(u) = &e.kind {
|
||||
if MODELER_TYPES.iter().any(|t| u.dxf_type.eq_ignore_ascii_case(t)) && stored.contains(&e.handle) {
|
||||
if DWG_KEPT_TYPES.iter().any(|t| u.dxf_type.eq_ignore_ascii_case(t))
|
||||
|| (MODELER_TYPES.iter().any(|t| u.dxf_type.eq_ignore_ascii_case(t)) && stored.contains(&e.handle))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
let n = out.entry(u.dxf_type.clone()).or_insert(0);
|
||||
|
||||
@@ -82,3 +82,16 @@ fn solids_whose_geometry_the_data_store_keeps_are_not_reported_from_2013() {
|
||||
// Before 2013 a file has no data store: every solid loses its geometry.
|
||||
assert_eq!(not_saved_as(&d, Version::R2010), [("3DSOLID".to_string(), 2), ("REGION".to_string(), 1)].into_iter().collect());
|
||||
}
|
||||
|
||||
/// An OLE2FRAME is written back whole by a DWG save, so the DWG note doesn't list it; other
|
||||
/// unmodelled types are still listed (#2422).
|
||||
#[test]
|
||||
fn a_dwg_save_does_not_list_an_ole_frame() {
|
||||
use cadcraft_engine::cmd::file::{Version, not_saved_as};
|
||||
let mut d = Drawing::new_imperial();
|
||||
d.add(&Space::Model, Common::default(), unknown("OLE2FRAME")).unwrap();
|
||||
d.add(&Space::Model, Common::default(), unknown("MLINE")).unwrap();
|
||||
for v in [Version::R2000, Version::R2018] {
|
||||
assert_eq!(not_saved_as(&d, v), [("MLINE".to_string(), 1)].into_iter().collect(), "{v:?}");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,6 +44,7 @@ mod mtext_columns_roundtrip;
|
||||
mod mtext_embedded_object;
|
||||
mod mtext_line_spacing;
|
||||
mod multiline_attributes;
|
||||
mod ole_frame;
|
||||
mod paperview_marker;
|
||||
mod passthrough_image_roundtrip;
|
||||
mod pdf_followups;
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
//! An OLE2FRAME is kept as an unknown entity; a DWG save carries it with its data (#2422).
|
||||
|
||||
use cadcraft_doc::EntityKind;
|
||||
use cadcraft_io::{SaveOptions, Version, read, read_dxf, write_with};
|
||||
|
||||
/// A 2000 DXF with one OLE2FRAME: corners, the binary data and the end marker (DXF Reference).
|
||||
fn dxf() -> String {
|
||||
String::from(
|
||||
"0\nSECTION\n2\nHEADER\n9\n$ACADVER\n1\nAC1015\n0\nENDSEC\n0\nSECTION\n2\nENTITIES\n\
|
||||
0\nOLE2FRAME\n5\n2B\n100\nAcDbEntity\n8\n0\n100\nAcDbOle2Frame\n70\n2\n3\nOLE Object\n\
|
||||
10\n30.14\n20\n-18.99\n30\n0\n11\n35.27\n21\n-22.39\n31\n0\n71\n2\n72\n0\n90\n8\n310\n0102030405060708\n1\nOLE\n\
|
||||
0\nENDSEC\n0\nEOF\n",
|
||||
)
|
||||
}
|
||||
|
||||
fn ole(d: &cadcraft_doc::Drawing) -> Option<cadcraft_doc::Unknown> {
|
||||
d.model.iter().find_map(|e| match &e.kind {
|
||||
EntityKind::Unknown(u) if u.dxf_type == "OLE2FRAME" => Some(u.clone()),
|
||||
_ => None,
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_dwg_keeps_an_ole_frame_with_its_data() {
|
||||
let d = read_dxf(dxf().as_bytes()).unwrap();
|
||||
assert!(ole(&d).is_some());
|
||||
for v in [Version::R2000, Version::R2010, Version::R2018] {
|
||||
let dwg = write_with(&d, "x.dwg", &SaveOptions { version: Some(v), binary: false }).unwrap();
|
||||
let back = read(&dwg, "x.dwg").unwrap();
|
||||
let u = ole(&back).unwrap_or_else(|| panic!("{v:?}: the OLE2FRAME is gone"));
|
||||
let data: String = u.tags.iter().filter(|t| t.code == 310).map(|t| t.value.to_ascii_uppercase()).collect();
|
||||
assert_eq!(data, "0102030405060708", "{v:?}: data");
|
||||
// Only the data is compared: with this stand-in blob the DWG round trip returns default
|
||||
// corners (1,1)/(0,0), which a real object might not do; not verified without a real file.
|
||||
}
|
||||
}
|
||||
@@ -234,6 +234,12 @@ fn entity_parts(b: &mut Builder, ctx: &Ctx, e: &Entity) {
|
||||
b.polyline(ctx, rgb, lw, l);
|
||||
}
|
||||
}
|
||||
// An OLE object's content is not drawn; its frame, from its two corner points, is.
|
||||
EntityKind::Unknown(u) if u.dxf_type.eq_ignore_ascii_case("OLE2FRAME") => {
|
||||
if let Some(q) = ole_frame(&u.tags) {
|
||||
b.polyline(ctx, rgb, lw, &q);
|
||||
}
|
||||
}
|
||||
kind => {
|
||||
for prim in kind.prims() {
|
||||
match prim {
|
||||
@@ -379,3 +385,11 @@ fn leader_scale(d: &Drawing, st: Option<&cadcraft_doc::DimStyle>) -> f64 {
|
||||
let dimscale = d.header.f64("DIMSCALE", 1.0);
|
||||
st.map(|s| s.effective_scale(dimscale)).unwrap_or_else(|| cadcraft_doc::DimStyle::default().effective_scale(dimscale))
|
||||
}
|
||||
|
||||
/// The closed outline of an OLE2FRAME from its upper-left (groups 10, 20) and lower-right
|
||||
/// (groups 11, 21) corners (DXF Reference, OLE2FRAME); `None` when either is missing or not finite.
|
||||
fn ole_frame(tags: &[cadcraft_doc::RawTag]) -> Option<[Vec2; 5]> {
|
||||
let get = |code: i32| tags.iter().find(|t| t.code == code).and_then(|t| t.value.trim().parse::<f64>().ok()).filter(|v| v.is_finite());
|
||||
let (a, b) = (Vec2::new(get(10)?, get(20)?), Vec2::new(get(11)?, get(21)?));
|
||||
Some([a, Vec2::new(b.x, a.y), b, Vec2::new(a.x, b.y), a])
|
||||
}
|
||||
|
||||
@@ -1122,3 +1122,17 @@ fn raster_hatch_pattern_matches_one_by_one() {
|
||||
assert!(render(&l, &view, &o).unwrap().data() == render_one_by_one(&l, &view, &o).data());
|
||||
}
|
||||
}
|
||||
|
||||
/// An OLE2FRAME draws its frame, the rectangle from its two corner points (#2422), and nothing
|
||||
/// when a corner is missing.
|
||||
#[test]
|
||||
fn ole_frame_draws_its_outline() {
|
||||
let tag = |code: i32, value: &str| cadcraft_doc::RawTag { code, value: value.into() };
|
||||
let frame = |tags: Vec<cadcraft_doc::RawTag>| EntityKind::Unknown(cadcraft_doc::Unknown { dxf_type: "OLE2FRAME".into(), tags });
|
||||
let mut d = Drawing::new_imperial();
|
||||
d.add(&Space::Model, Common::default(), frame(vec![tag(10, "30"), tag(20, "-19"), tag(11, "35"), tag(21, "-22")])).unwrap();
|
||||
d.add(&Space::Model, Common::default(), frame(vec![tag(10, "1"), tag(20, "2")])).unwrap();
|
||||
let l = build(&d, &Space::Model, &Options { tolerance: 0.01, ..Default::default() });
|
||||
assert_eq!(l.prims.len(), 1, "only the frame with both corners is drawn");
|
||||
assert_eq!(l.prims[0].len, 5, "a closed rectangle: five points");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user