Skip to content

Commit

Permalink
improve compat when writing BLOCK/INSERT on R12
Browse files Browse the repository at this point in the history
  • Loading branch information
brettfo committed Nov 29, 2021
1 parent 3f40cec commit 944f6e4
Show file tree
Hide file tree
Showing 9 changed files with 180 additions and 14 deletions.
3 changes: 3 additions & 0 deletions build/entity_generator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -674,6 +674,9 @@ fn generate_write_code_pairs_for_write_order(
attr(&write_command, "DontWriteIfValueIs")
));
}
if !attr(&write_command, "WriteCondition").is_empty() {
predicates.push(attr(&write_command, "WriteCondition"));
}
let code = code(&write_command);
let expected_type = ExpectedType::get_expected_type(code).unwrap();
let typ = get_code_pair_type(&expected_type);
Expand Down
50 changes: 50 additions & 0 deletions examples/src/block_examples.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
use dxf::entities::*;
use dxf::enums::AcadVersion;
use dxf::{Block, Drawing, Point};

pub fn all() -> dxf::DxfResult<()> {
basic_block_and_insert()?;
Ok(())
}

fn basic_block_and_insert() -> dxf::DxfResult<()> {
let mut drawing = Drawing::new();
drawing.header.version = AcadVersion::R12; // this example only tested on R12

//
// create a block with a unique name...
//
let mut block = Block::default();
block.name = "my-block-name".to_string();

//
// ...and populate it with entities
//
block.entities.push(Entity {
common: Default::default(),
specific: EntityType::Line(Line::new(
// line from (0,0) to (1,1)
Point::new(0.0, 0.0, 0.0),
Point::new(1.0, 1.0, 0.0),
)),
});

//
// add the block to the drawing
//
drawing.add_block(block);

//
// add a reference to the block with an `INSERT` entity
//
let mut insert = Insert::default();
insert.name = "my-block-name".to_string(); // use the same name as the block defined above
insert.location = Point::new(3.0, 3.0, 0.0); // select the base-point of the insertion
drawing.add_entity(Entity {
common: Default::default(),
specific: EntityType::Insert(insert),
}); // the end result is a line from (3,3) to (4,4)

drawing.save_file("basic_block_and_insert.dxf")?;
Ok(())
}
2 changes: 2 additions & 0 deletions examples/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
extern crate dxf;

mod block_examples;
mod line_type_examples;

fn main() -> dxf::DxfResult<()> {
block_examples::all()?;
line_type_examples::all()?;
Ok(())
}
2 changes: 1 addition & 1 deletion spec/EntitiesSpec.xml
Original file line number Diff line number Diff line change
Expand Up @@ -486,7 +486,7 @@
<Field Name="__attributes_and_handles" Code="10" Type="(Attribute, Handle)" DefaultValue="vec![]" AllowMultiples="true" GenerateReader="false" GenerateWriter="false" />
<WriteOrder>
<WriteSpecificValue Code="100" Value='&amp;String::from("AcDbBlockReference")' MinVersion="R13" />
<WriteSpecificValue Code="66" Value="as_i16(ent.attributes().count() > 0)" />
<WriteSpecificValue Code="66" Value="as_i16(ent.attributes().count() > 0)" WriteCondition="ent.attributes().count() > 0" />
<WriteField Field="name" />
<WriteField Field="location" />
<WriteField Field="x_scale_factor" />
Expand Down
47 changes: 46 additions & 1 deletion src/block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,7 @@ impl Block {
write_handles: bool,
) {
pairs.push(CodePair::new_str(0, "BLOCK"));
if write_handles {
if write_handles && version >= AcadVersion::R13 {
pairs.push(CodePair::new_string(5, &self.handle.as_string()));
}

Expand Down Expand Up @@ -705,4 +705,49 @@ mod tests {
_ => panic!("expected a circle"),
}
}

/// Test case derived from https://ezdxf.readthedocs.io/en/stable/dxfinternals/block_management.html
#[test]
fn write_block_r12_compat() {
let mut drawing = Drawing::new();
drawing.header.version = AcadVersion::R12;
let mut block = Block::default();
block.name = "block-name".to_string();
block.entities.push(Entity {
common: Default::default(),
specific: EntityType::Line(Line::new(
Point::new(0.0, 0.0, 0.0),
Point::new(1.0, 1.0, 0.0),
)),
});
drawing.add_block(block);
assert_contains_pairs(
&drawing,
vec![
CodePair::new_str(0, "SECTION"),
CodePair::new_str(2, "BLOCKS"),
CodePair::new_str(0, "BLOCK"),
// no handle
CodePair::new_str(8, "0"), // layer
CodePair::new_str(2, "block-name"), // name
CodePair::new_i16(70, 0), // flags
CodePair::new_f64(10, 0.0), // insertion point
CodePair::new_f64(20, 0.0),
CodePair::new_f64(30, 0.0),
CodePair::new_str(3, "block-name"), // name again
CodePair::new_str(1, ""), // x-ref name; empty = external
CodePair::new_str(0, "LINE"), // first entity
CodePair::new_str(5, "12"), // entity handle
],
);
assert_contains_pairs(
&drawing,
vec![
CodePair::new_str(0, "ENDBLK"),
CodePair::new_str(5, "10"), // endblk got handle, original block didn't
CodePair::new_str(8, "0"), // layer
CodePair::new_str(0, "ENDSEC"), // end of block
],
);
}
}
56 changes: 49 additions & 7 deletions src/drawing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -666,9 +666,11 @@ impl Drawing {
None
}
}
pub(crate) fn add_block_no_handle_set(&mut self, block: Block) -> &Block {
pub(crate) fn add_block_no_handle_set(&mut self, mut block: Block) -> &Block {
self.ensure_layer_is_present_for_block(&block);
self.ensure_line_type_is_present_for_block(&block);
self.ensure_block_record_is_present_for_block(&mut block);
self.ensure_block_entity_handles_are_set(&mut block);
self.__blocks.push(block);
self.__blocks.last().unwrap()
}
Expand Down Expand Up @@ -826,6 +828,17 @@ impl Drawing {
self.ensure_line_type_is_present(&ent.common.line_type_name);
}
}
fn ensure_block_record_is_present_for_block(&mut self, block: &mut Block) {
self.add_block_record(BlockRecord {
name: String::from(&block.name),
..Default::default()
});
}
fn ensure_block_entity_handles_are_set(&mut self, block: &mut Block) {
for ent in &mut block.entities {
ent.common.handle = self.next_handle();
}
}
fn ensure_line_type_is_present_for_object(&mut self, obj: &Object) {
if let ObjectType::MLineStyle(ref style) = &obj.specific {
self.ensure_line_type_is_present(&style.style_name);
Expand Down Expand Up @@ -933,13 +946,15 @@ impl Drawing {
pairs.push(CodePair::new_str(0, "ENDSEC"));
}
pub(crate) fn add_objects_pairs(&self, pairs: &mut Vec<CodePair>) {
pairs.push(CodePair::new_str(0, "SECTION"));
pairs.push(CodePair::new_str(2, "OBJECTS"));
for o in &self.__objects {
o.add_code_pairs(pairs, self.header.version);
}
if self.header.version >= AcadVersion::R13 {
pairs.push(CodePair::new_str(0, "SECTION"));
pairs.push(CodePair::new_str(2, "OBJECTS"));
for o in &self.__objects {
o.add_code_pairs(pairs, self.header.version);
}

pairs.push(CodePair::new_str(0, "ENDSEC"));
pairs.push(CodePair::new_str(0, "ENDSEC"));
}
}
pub(crate) fn add_thumbnail_pairs(&self, pairs: &mut Vec<CodePair>) -> DxfResult<()> {
if self.header.version >= AcadVersion::R2000 {
Expand Down Expand Up @@ -1227,6 +1242,7 @@ impl Drawing {
#[cfg(test)]
mod tests {
use crate::entities::*;
use crate::enums::AcadVersion;
use crate::helper_functions::tests::*;
use crate::objects::*;
use crate::tables::*;
Expand Down Expand Up @@ -1315,6 +1331,32 @@ mod tests {
assert_ne!(Handle(0), layer.handle);
}

#[test]
fn objects_section_is_not_written_on_r12() {
let mut drawing = Drawing::new();
drawing.header.version = AcadVersion::R12;
assert_not_contains_pairs(
&drawing,
vec![
CodePair::new_str(0, "SECTION"),
CodePair::new_str(2, "OBJECTS"),
],
);
}

#[test]
fn objects_section_is_written_on_r13() {
let mut drawing = Drawing::new();
drawing.header.version = AcadVersion::R13;
assert_contains_pairs(
&drawing,
vec![
CodePair::new_str(0, "SECTION"),
CodePair::new_str(2, "OBJECTS"),
],
);
}

#[test]
fn block_handle_is_set_during_read_if_not_specified() {
let drawing = drawing_from_pairs(vec![
Expand Down
23 changes: 20 additions & 3 deletions src/entity.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1436,7 +1436,9 @@ impl Entity {
} else {
"AcDb2dPolyline"
};
pairs.push(CodePair::new_str(100, subclass_marker));
if version >= AcadVersion::R13 {
pairs.push(CodePair::new_str(100, subclass_marker));
}
if version <= AcadVersion::R13 {
pairs.push(CodePair::new_i16(66, as_i16(poly.contains_vertices)));
}
Expand Down Expand Up @@ -1496,7 +1498,9 @@ impl Entity {
} else {
"AcDb2dVertex"
};
pairs.push(CodePair::new_str(100, subclass_marker));
if version >= AcadVersion::R13 {
pairs.push(CodePair::new_str(100, subclass_marker));
}
pairs.push(CodePair::new_f64(10, v.location.x));
pairs.push(CodePair::new_f64(20, v.location.y));
pairs.push(CodePair::new_f64(30, v.location.z));
Expand Down Expand Up @@ -1773,6 +1777,7 @@ mod tests {
#[test]
fn write_specific_entity_fields() {
let mut drawing = Drawing::new();
drawing.header.version = AcadVersion::R13;
let line = Line {
p1: Point::new(1.1, 2.2, 3.3),
p2: Point::new(4.4, 5.5, 6.6),
Expand Down Expand Up @@ -2393,6 +2398,7 @@ mod tests {
#[test]
fn write_2d_polyline() {
let mut drawing = Drawing::new();
drawing.header.version = AcadVersion::R13;
let mut poly = Polyline::default();
poly.add_vertex(
&mut drawing,
Expand All @@ -2419,6 +2425,7 @@ mod tests {
common: Default::default(),
specific: EntityType::Polyline(poly),
});
// TODO
assert_contains_pairs(
&drawing,
vec![
Expand Down Expand Up @@ -2472,6 +2479,7 @@ mod tests {
#[test]
fn write_3d_polyline() {
let mut drawing = Drawing::new();
drawing.header.version = AcadVersion::R13;
let mut poly = Polyline::default();
poly.add_vertex(
&mut drawing,
Expand Down Expand Up @@ -2775,15 +2783,24 @@ mod tests {
#[test]
fn write_insert_no_embedded_attributes() {
let mut drawing = Drawing::new();
let ins = Insert::default();
let mut ins = Insert::default();
ins.name = "insert-name".to_string();
let ent = Entity::new(EntityType::Insert(ins));
drawing.add_entity(ent);
assert_not_contains_pairs(
&drawing,
vec![
CodePair::new_i16(66, 0), // contains no attributes
CodePair::new_str(2, "insert-name"), // sentinel to ensure we're reading at the correct location
],
);
assert_not_contains_pairs(&drawing, vec![CodePair::new_str(0, "SEQEND")]);
}

#[test]
fn write_insert_with_embedded_attributes() {
let mut drawing = Drawing::new();
drawing.header.version = AcadVersion::R13;
let mut ins = Insert::default();
ins.add_attribute(&mut drawing, Attribute::default());
let ent = Entity::new(EntityType::Insert(ins));
Expand Down
10 changes: 8 additions & 2 deletions src/helper_functions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -626,7 +626,10 @@ pub mod tests {

pub fn assert_contains_pairs(drawing: &Drawing, expected: Vec<CodePair>) {
let actual = drawing.get_code_pairs().ok().unwrap();
println!("checking pairs: {:?}", actual);
println!("checking pairs:");
for pair in &actual {
println!("{:?}", pair);
}
let actual_index = try_find_index(&actual, &expected);
assert!(actual_index.is_some());
}
Expand All @@ -638,7 +641,10 @@ pub mod tests {

pub fn assert_not_contains_pairs(drawing: &Drawing, not_expected: Vec<CodePair>) {
let actual = drawing.get_code_pairs().ok().unwrap();
println!("checking pairs: {:?}", actual);
println!("checking pairs:");
for pair in &actual {
println!("{:?}", pair);
}
let actual_index = try_find_index(&actual, &not_expected);
assert!(actual_index.is_none());
}
Expand Down
1 change: 1 addition & 0 deletions src/object.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2016,6 +2016,7 @@ mod tests {
dict.value_handles
.insert(String::from("key2"), Handle(0xBBBB));
let mut drawing = Drawing::new();
drawing.header.version = AcadVersion::R13; // OBJECTS section only written on R13+
drawing.add_object(Object {
common: Default::default(),
specific: ObjectType::Dictionary(dict),
Expand Down

0 comments on commit 944f6e4

Please sign in to comment.