use crate::io::dwg::crc;
use crate::io::dwg::dwg_reference_type::DwgReferenceType;
use crate::types::{Color, Handle, Transparency};
use super::DwgObjectWriter;
pub const OBJ_TEXT: i16 = 1;
pub const OBJ_ATTRIB: i16 = 2;
pub const OBJ_ATTDEF: i16 = 3;
pub const OBJ_BLOCK: i16 = 4;
pub const OBJ_ENDBLK: i16 = 5;
pub const OBJ_SEQEND: i16 = 6;
pub const OBJ_INSERT: i16 = 7;
pub const OBJ_MINSERT: i16 = 8;
pub const OBJ_VERTEX_2D: i16 = 10;
pub const OBJ_VERTEX_3D: i16 = 11;
pub const OBJ_VERTEX_MESH: i16 = 12;
pub const OBJ_VERTEX_PFACE: i16 = 13;
pub const OBJ_VERTEX_PFACE_FACE: i16 = 14;
pub const OBJ_POLYLINE_2D: i16 = 15;
pub const OBJ_POLYLINE_3D: i16 = 16;
pub const OBJ_ARC: i16 = 17;
pub const OBJ_CIRCLE: i16 = 18;
pub const OBJ_LINE: i16 = 19;
pub const OBJ_DIMENSION_ORDINATE: i16 = 20;
pub const OBJ_DIMENSION_LINEAR: i16 = 21;
pub const OBJ_DIMENSION_ALIGNED: i16 = 22;
pub const OBJ_DIMENSION_ANG_3PT: i16 = 23;
pub const OBJ_DIMENSION_ANG_2LN: i16 = 24;
pub const OBJ_DIMENSION_RADIUS: i16 = 25;
pub const OBJ_DIMENSION_DIAMETER: i16 = 26;
pub const OBJ_POINT: i16 = 27;
pub const OBJ_3DFACE: i16 = 28;
pub const OBJ_POLYLINE_PFACE: i16 = 29;
pub const OBJ_POLYLINE_MESH: i16 = 30;
pub const OBJ_SOLID: i16 = 31;
pub const OBJ_TRACE: i16 = 32;
pub const OBJ_TABLECONTENT: i16 = 105;
pub const OBJ_SHAPE: i16 = 33;
pub const OBJ_VIEWPORT: i16 = 34;
pub const OBJ_ELLIPSE: i16 = 35;
pub const OBJ_SPLINE: i16 = 36;
pub const OBJ_REGION: i16 = 37;
pub const OBJ_3DSOLID: i16 = 38;
pub const OBJ_BODY: i16 = 39;
pub const OBJ_RAY: i16 = 40;
pub const OBJ_XLINE: i16 = 41;
pub const OBJ_DICTIONARY: i16 = 42;
pub const OBJ_OLEFRAME: i16 = 43;
pub const OBJ_MTEXT: i16 = 44;
pub const OBJ_LEADER: i16 = 45;
pub const OBJ_TOLERANCE: i16 = 46;
pub const OBJ_MLINE: i16 = 47;
pub const OBJ_BLOCK_CONTROL: i16 = 48;
pub const OBJ_BLOCK_HEADER: i16 = 49;
pub const OBJ_LAYER_CONTROL: i16 = 50;
pub const OBJ_LAYER: i16 = 51;
pub const OBJ_STYLE_CONTROL: i16 = 52;
pub const OBJ_STYLE: i16 = 53;
pub const OBJ_LTYPE_CONTROL: i16 = 56;
pub const OBJ_LTYPE: i16 = 57;
pub const OBJ_VIEW_CONTROL: i16 = 60;
pub const OBJ_VIEW: i16 = 61;
pub const OBJ_UCS_CONTROL: i16 = 62;
pub const OBJ_UCS: i16 = 63;
pub const OBJ_VPORT_CONTROL: i16 = 64;
pub const OBJ_VPORT: i16 = 65;
pub const OBJ_APPID_CONTROL: i16 = 66;
pub const OBJ_APPID: i16 = 67;
pub const OBJ_DIMSTYLE_CONTROL: i16 = 68;
pub const OBJ_DIMSTYLE: i16 = 69;
pub const OBJ_VPENT_HDR_CONTROL: i16 = 70;
pub const OBJ_VPENT_HDR: i16 = 71;
pub const OBJ_GROUP: i16 = 72;
pub const OBJ_MLINESTYLE: i16 = 73;
pub const OBJ_OLE2FRAME: i16 = 74;
pub const OBJ_DUMMY: i16 = 75;
pub const OBJ_LONG_TRANSACTION: i16 = 76;
pub const OBJ_LWPOLYLINE: i16 = 77; pub const OBJ_HATCH: i16 = 78;
pub const OBJ_IMAGE: i16 = -1; pub const OBJ_MESH: i16 = -2; pub const OBJ_MULTILEADER: i16 = -3; pub const OBJ_SURFACE: i16 = -5; pub const OBJ_PDFUNDERLAY: i16 = -12; pub const OBJ_DWFUNDERLAY: i16 = -13; pub const OBJ_DGNUNDERLAY: i16 = -14; pub const OBJ_HELIX: i16 = -15; pub const OBJ_TABLE: i16 = -16; pub const OBJ_LIGHT: i16 = -17; pub const OBJ_ARC_DIMENSION: i16 = -18;
pub const OBJ_LARGE_RADIAL_DIMENSION: i16 = -19;
pub const OBJ_CAMERA: i16 = -20;
pub const OBJ_SECTIONOBJECT: i16 = -21;
pub const OBJ_ARCALIGNEDTEXT: i16 = -22;
pub const OBJ_RTEXT: i16 = -23;
pub const OBJ_GEOPOSITIONMARKER: i16 = -24;
pub const OBJ_NAVISWORKSMODEL: i16 = -25;
pub const OBJ_POINTCLOUD: i16 = -26;
pub const OBJ_POINTCLOUDEX: i16 = -27;
pub const OBJ_MPOLYGON: i16 = -28;
pub const OBJ_PROXY_ENTITY: i16 = 498;
pub const OBJ_PROXY_OBJECT: i16 = 499;
pub const OBJ_BLOCKVISIBILITYPARAMETER: i16 = 0x84;
pub const OBJ_XRECORD: i16 = 79; pub const OBJ_PLACEHOLDER: i16 = 80; pub const OBJ_LAYOUT: i16 = 82;
pub const OBJ_DICTIONARYWDFLT: i16 = 0x78; pub const OBJ_DICTIONARYVAR: i16 = 0x79; pub const OBJ_PLOTSETTINGS: i16 = 0x7A; pub const OBJ_MLEADERSTYLE: i16 = 0x7B; pub const OBJ_IMAGEDEF: i16 = 0x7C; pub const OBJ_IMAGEDEFREACTOR: i16 = 0x7D; pub const OBJ_SCALE: i16 = 0x7E; pub const OBJ_SORTENTSTABLE: i16 = 0x7F; pub const OBJ_RASTERVARIABLES: i16 = 0x80; pub const OBJ_DBCOLOR: i16 = 0x81; pub const OBJ_WIPEOUTVARIABLES: i16 = 0x82; pub const OBJ_GEODATA: i16 = 0x83; pub const OBJ_SPATIALFILTER: i16 = 0x86; pub const OBJ_PDFDEFINITION: i16 = 0x87; pub const OBJ_DWFDEFINITION: i16 = 0x88; pub const OBJ_DGNDEFINITION: i16 = 0x89;
impl<'a> DwgObjectWriter<'a> {
pub fn register_object(&mut self, handle: Handle) {
if !handle.is_null() && !self.registered_handles.insert(handle.value()) {
self.writer.reset();
self.pending_type_code = None;
return;
}
let data = self.writer.merge();
debug_assert!(
!data.is_empty() || handle.is_null(),
"register_object: merged data is empty for handle {:#X}",
handle.value()
);
let handle_bits = if self.version.r2010_plus() {
let total_bits = (data.len() as i64) * 8;
let hstart = self.writer.handle_start_bits();
debug_assert!(
hstart >= 0 && hstart <= total_bits,
"handle_start_bits ({}) out of range [0, {}]",
hstart,
total_bits
);
total_bits - hstart
} else {
0
};
let pos = self.output.len() as u32;
write_modular_short_bytes(&mut self.output, data.len());
if self.version.r2010_plus() {
write_modular_char_bytes(&mut self.output, handle_bits as usize);
}
self.output.extend_from_slice(&data);
let crc_val = crc::crc16(crc::CRC16_SEED, &self.output[pos as usize..]);
self.output.extend_from_slice(&crc_val.to_le_bytes());
if !handle.is_null() {
self.handle_map.push((handle.value(), pos));
}
if let Some(type_code) = self.pending_type_code.take().filter(|code| *code >= 500) {
*self.class_instance_counts.entry(type_code).or_default() += 1;
}
self.writer.reset();
}
pub fn register_raw_object(&mut self, handle: Handle, raw_data: &[u8], handle_bits: i64) {
self.class_counts_complete = false;
if !handle.is_null() && !self.registered_handles.insert(handle.value()) {
return;
}
let pos = self.output.len() as u32;
write_modular_short_bytes(&mut self.output, raw_data.len());
if self.version.r2010_plus() {
write_modular_char_bytes(&mut self.output, handle_bits as usize);
}
self.output.extend_from_slice(raw_data);
let crc_val = crc::crc16(crc::CRC16_SEED, &self.output[pos as usize..]);
self.output.extend_from_slice(&crc_val.to_le_bytes());
if !handle.is_null() {
self.handle_map.push((handle.value(), pos));
}
}
pub fn write_common_data(
&mut self,
type_code: i16,
handle: Handle,
xdata: &crate::xdata::ExtendedData,
) {
self.pending_type_code = Some(type_code);
self.writer.write_object_type(type_code);
if self.version.r2000_plus() && !self.version.r2010_plus() {
self.writer.save_position_for_size();
}
self.writer
.main_mut()
.write_handle_undefined(handle.value());
self.write_extended_data(xdata);
}
fn entity_layer_handle(&self, layer: &str) -> Handle {
if let Some(entry) = self.document.layers.get(layer) {
if !entry.handle.is_null() {
return entry.handle;
}
}
self.document
.layers
.get("0")
.map(|entry| entry.handle)
.unwrap_or(Handle::NULL)
}
pub fn write_common_entity_data(
&mut self,
type_code: i16,
handle: Handle,
owner_handle: Handle,
layer: &str,
color: &Color,
line_weight: &crate::types::LineWeight,
transparency: &Transparency,
invisible: bool,
linetype_scale: f64,
linetype: &str,
linetype_handle: &Option<Handle>,
xdata: &crate::xdata::ExtendedData,
reactors: &[Handle],
xdictionary_handle: &Option<Handle>,
graphic_data: Option<&[u8]>,
entity_mode_override: Option<u8>,
material_flags: u8,
material_handle: &Option<Handle>,
shadow_flags: u8,
plotstyle_flags: u8,
plotstyle_handle: &Option<Handle>,
color_book_handle: &Option<Handle>,
full_visual_style_handle: &Option<Handle>,
face_visual_style_handle: &Option<Handle>,
edge_visual_style_handle: &Option<Handle>,
) {
self.write_common_data(type_code, handle, xdata);
let has_graphic = graphic_data.is_some();
self.writer.write_bit(has_graphic);
if let Some(gdata) = graphic_data {
let gsize = gdata.len() as i64;
if self.version.r2010_plus() {
self.writer.write_bit_long_long(gsize);
} else {
self.writer.write_raw_long(gsize as i32);
}
for &b in gdata {
self.writer.write_byte(b);
}
}
if self.version.r13_14_only() {
self.writer.save_position_for_size();
}
let entmode = match entity_mode_override {
Some(0) => 0,
Some(mode) if owner_handle.is_null() => mode,
_ => self.get_entity_mode(&owner_handle),
};
self.writer.write_2bits(entmode);
if entmode == 0 {
self.writer
.write_handle(DwgReferenceType::SoftPointer, owner_handle.value());
}
self.writer.write_bit_long(reactors.len() as i32);
for r in reactors {
self.writer
.write_handle(DwgReferenceType::SoftPointer, r.value());
}
let effective_entity_xdic =
xdictionary_handle.filter(|xdic| !xdic.is_null() && self.is_writable_object(xdic));
if self.version.r2004_plus() {
self.writer.write_bit(effective_entity_xdic.is_none());
if let Some(xdic) = effective_entity_xdic {
self.writer
.write_handle(DwgReferenceType::HardOwnership, xdic.value());
}
} else {
let xdic_val = effective_entity_xdic.map(|h| h.value()).unwrap_or(0);
self.writer
.write_handle(DwgReferenceType::HardOwnership, xdic_val);
}
if let Some(xdic) = effective_entity_xdic {
if !xdic.is_null() {
self.object_queue.push_back(xdic);
}
}
if self.version.r2013_plus(self.dxf_version) {
let has_ds = self.pending_has_ds_data;
self.writer.write_bit(has_ds);
}
self.pending_has_ds_data = false;
if self.version.r13_14_only() {
let layer_h = self.entity_layer_handle(layer);
self.writer
.write_handle(DwgReferenceType::HardPointer, layer_h.value());
let lt_lower = linetype.to_ascii_lowercase();
let resolved_linetype = self
.document
.line_types
.get(linetype)
.map(|lt| lt.handle)
.or(*linetype_handle)
.filter(|handle| !handle.is_null());
let has_preserved_handle = linetype_handle.filter(|handle| !handle.is_null()).is_some();
let is_bylayer =
(lt_lower == "bylayer" || lt_lower.is_empty()) && !has_preserved_handle;
self.writer.write_bit(is_bylayer);
if !is_bylayer {
self.writer.write_handle(
DwgReferenceType::HardPointer,
resolved_linetype.unwrap_or(Handle::NULL).value(),
);
}
}
if !self.version.r2004_plus() {
let prev_h = self.prev_handle.unwrap_or(Handle::NULL);
let next_h = self.next_handle.unwrap_or(Handle::NULL);
let has_links = !prev_h.is_null()
&& prev_h.value() == handle.value().wrapping_sub(1)
&& !next_h.is_null()
&& next_h.value() == handle.value().wrapping_add(1);
self.writer.write_bit(has_links);
if !has_links {
self.writer
.write_handle(DwgReferenceType::SoftPointer, prev_h.value());
self.writer
.write_handle(DwgReferenceType::SoftPointer, next_h.value());
}
}
if self.version.r2000_plus() {
let color_book_handle = if self.version.r2004_plus() {
color_book_handle
.filter(|handle| !handle.is_null() && self.is_writable_object(handle))
} else {
None
};
self.writer
.write_en_color_with_book(color, transparency, color_book_handle.is_some());
if let Some(handle) = color_book_handle {
self.writer
.write_handle(DwgReferenceType::HardPointer, handle.value());
self.object_queue.push_back(handle);
}
} else {
self.writer.write_cm_color(color);
}
self.writer.write_bit_double(linetype_scale);
if self.version.r13_14_only() {
self.writer.write_bit_short(if invisible { 1 } else { 0 });
return;
}
let layer_h = self.entity_layer_handle(layer);
self.writer
.write_handle(DwgReferenceType::HardPointer, layer_h.value());
let lt_lower = linetype.to_ascii_lowercase();
let resolved_linetype = self
.document
.line_types
.get(linetype)
.map(|lt| lt.handle)
.or(*linetype_handle)
.filter(|handle| !handle.is_null());
let has_preserved_handle = linetype_handle.filter(|handle| !handle.is_null()).is_some();
let is_builtin = lt_lower == "bylayer"
|| lt_lower.is_empty()
|| lt_lower == "byblock"
|| lt_lower == "continuous";
if resolved_linetype.is_some() && (has_preserved_handle || !is_builtin) {
self.writer.write_2bits(0b11);
self.writer.write_handle(
DwgReferenceType::HardPointer,
resolved_linetype.unwrap().value(),
);
} else if lt_lower == "bylayer" || lt_lower.is_empty() {
self.writer.write_2bits(0b00);
} else if lt_lower == "byblock" {
self.writer.write_2bits(0b01);
} else if lt_lower == "continuous" {
self.writer.write_2bits(0b10);
} else {
self.writer.write_2bits(0b11);
let lt_handle = resolved_linetype.unwrap_or(Handle::NULL);
self.writer
.write_handle(DwgReferenceType::HardPointer, lt_handle.value());
}
if self.version.r2007_plus() {
let material_handle = material_handle.filter(|handle| self.is_writable_object(handle));
let material_flags = if material_flags == 0b11 && material_handle.is_none() {
0b00
} else {
material_flags
};
self.writer.write_2bits(material_flags);
if material_flags == 0b11 {
if let Some(mh) = material_handle {
self.writer
.write_handle(DwgReferenceType::HardPointer, mh.value());
} else {
self.writer.write_handle(DwgReferenceType::HardPointer, 0);
}
}
self.writer.write_byte(shadow_flags);
}
self.writer.write_2bits(plotstyle_flags);
if plotstyle_flags == 0b11 {
if let Some(ph) = plotstyle_handle {
self.writer
.write_handle(DwgReferenceType::HardPointer, ph.value());
} else {
self.writer.write_handle(DwgReferenceType::HardPointer, 0);
}
}
if self.version.r2010_plus() {
let full = full_visual_style_handle
.filter(|handle| !handle.is_null() && self.is_writable_object(handle));
let face = face_visual_style_handle
.filter(|handle| !handle.is_null() && self.is_writable_object(handle));
let edge = edge_visual_style_handle
.filter(|handle| !handle.is_null() && self.is_writable_object(handle));
for handle in [full, face, edge] {
self.writer.write_bit(handle.is_some());
if let Some(handle) = handle {
self.writer
.write_handle(DwgReferenceType::HardPointer, handle.value());
self.object_queue.push_back(handle);
}
}
}
self.writer.write_bit_short(if invisible { 1 } else { 0 });
self.writer.write_byte(line_weight.to_dwg_index());
}
pub fn write_common_non_entity_data(
&mut self,
type_code: i16,
handle: Handle,
owner_handle: Handle,
reactors: &[Handle],
xdictionary_handle: &Option<Handle>,
) {
self.write_common_non_entity_data_eed(
type_code,
handle,
owner_handle,
reactors,
xdictionary_handle,
Vec::new(),
);
}
pub fn annotative_eed_block(&self, annotative: bool) -> Option<(u64, Vec<u8>)> {
if !annotative {
return None;
}
let app = self.document.app_ids.get("AcadAnnotative")?;
let bytes = crate::io::dwg::annotative_eed::encode(self.version.r2007_plus(), annotative);
Some((app.handle.value(), bytes))
}
pub(super) fn annotative_eed_block_for_handle(
&self,
handle: Handle,
annotative: bool,
) -> Option<(u64, Vec<u8>)> {
let had_marker = self
.existing_xdata_values(handle, "AcadAnnotative")
.is_some_and(|values| {
values.iter().any(|value| {
matches!(value, crate::xdata::XDataValue::String(label) if label == "AnnotativeData")
})
});
if !annotative && !had_marker {
return None;
}
let app = self.document.app_ids.get("AcadAnnotative")?;
let bytes = crate::io::dwg::annotative_eed::encode(self.version.r2007_plus(), annotative);
Some((app.handle.value(), bytes))
}
pub(super) fn existing_xdata_values(
&self,
handle: Handle,
application: &str,
) -> Option<Vec<crate::xdata::XDataValue>> {
if let Some(record) = self
.document
.object_xdata
.get(&handle)
.and_then(|data| data.get_record(application))
{
return Some(record.values.clone());
}
let app = self.document.app_ids.get(application)?;
let (_, raw) = self
.document
.eed_by_handle
.get(&handle)?
.iter()
.find(|(app_handle, _)| *app_handle == app.handle.value())?;
let source_version = self.document.dwg_source_version.unwrap_or(self.dxf_version);
crate::io::dwg::eed_codec::decode_values(
raw,
source_version >= crate::types::DxfVersion::AC1021,
|handle| {
self.document
.layers
.iter()
.find(|layer| layer.handle.value() == handle)
.map(|layer| layer.name.clone())
},
)
}
pub fn write_common_non_entity_data_eed(
&mut self,
type_code: i16,
handle: Handle,
owner_handle: Handle,
reactors: &[Handle],
xdictionary_handle: &Option<Handle>,
extra_eed: Vec<(u64, Vec<u8>)>,
) {
self.write_common_non_entity_data_eed_internal(
type_code,
handle,
owner_handle,
reactors,
xdictionary_handle,
extra_eed,
false,
false,
None,
);
}
pub(super) fn write_common_non_entity_data_relative_owner(
&mut self,
type_code: i16,
handle: Handle,
owner_handle: Handle,
reactors: &[Handle],
xdictionary_handle: &Option<Handle>,
) {
self.write_common_non_entity_data_eed_internal(
type_code,
handle,
owner_handle,
reactors,
xdictionary_handle,
Vec::new(),
true,
false,
None,
);
}
pub(super) fn write_complete_non_entity_data(
&mut self,
type_code: i16,
handle: Handle,
owner_handle: Handle,
reactors: &[Handle],
xdictionary_handle: &Option<Handle>,
extra_eed: Vec<(u64, Vec<u8>)>,
has_ds_data: bool,
) {
self.write_common_non_entity_data_eed_internal(
type_code,
handle,
owner_handle,
reactors,
xdictionary_handle,
extra_eed,
false,
true,
Some(has_ds_data),
);
}
fn write_common_non_entity_data_eed_internal(
&mut self,
type_code: i16,
handle: Handle,
owner_handle: Handle,
reactors: &[Handle],
xdictionary_handle: &Option<Handle>,
extra_eed: Vec<(u64, Vec<u8>)>,
relative_owner: bool,
complete_common: bool,
has_ds_data_override: Option<bool>,
) {
self.pending_type_code = Some(type_code);
self.writer.write_object_type(type_code);
if self.version.r2000_plus() && !self.version.r2010_plus() {
self.writer.save_position_for_size();
}
self.writer
.main_mut()
.write_handle_undefined(handle.value());
let mut eed = crate::xdata::ExtendedData::default();
if let Some(raw) = self.document.eed_by_handle.get(&handle) {
eed.raw_dwg_eed = raw.clone();
}
if let Some(xdata) = self.document.object_xdata.get(&handle) {
for record in xdata.records() {
eed.add_record(record.clone());
}
}
let mut opaque_extra = Vec::new();
for (app_handle, bytes) in extra_eed {
let application = self
.document
.app_ids
.iter()
.find(|app| app.handle.value() == app_handle);
let values = crate::io::dwg::eed_codec::decode_values(
&bytes,
self.version.r2007_plus(),
|handle| {
self.document
.layers
.iter()
.find(|layer| layer.handle.value() == handle)
.map(|layer| layer.name.clone())
},
);
if let (Some(application), Some(values)) = (application, values) {
if eed.get_record(&application.name).is_none() {
if let Some(values) = self.existing_xdata_values(handle, &application.name) {
let mut source = crate::xdata::ExtendedDataRecord::new(&application.name);
source.values = values;
eed.add_record(source);
}
}
let mut replacement = crate::xdata::ExtendedDataRecord::new(&application.name);
replacement.values = values;
eed.merge_modeled_record(&replacement);
eed.raw_dwg_eed.retain(|(app, _)| *app != app_handle);
} else {
eed.raw_dwg_eed.retain(|(app, _)| *app != app_handle);
opaque_extra.push((app_handle, bytes));
}
}
self.write_extended_data_with(&eed, &opaque_extra);
if self.version.r13_14_only() {
self.writer.save_position_for_size();
}
let effective_owner = self
.owner_overrides
.get(&handle)
.copied()
.unwrap_or(owner_handle);
if relative_owner && !effective_owner.is_null() {
self.writer
.write_handle_relative(handle.value(), effective_owner.value());
} else {
self.writer
.write_handle(DwgReferenceType::SoftPointer, effective_owner.value());
}
let effective_reactors: Vec<Handle>;
let reactors_to_write: &[Handle] = if reactors.is_empty() && !complete_common {
if let Some(saved) = self.document.reactors_by_handle.get(&handle) {
effective_reactors = saved.clone();
&effective_reactors
} else {
reactors
}
} else {
reactors
};
self.writer.write_bit_long(reactors_to_write.len() as i32);
for r in reactors_to_write {
self.writer
.write_handle(DwgReferenceType::SoftPointer, r.value());
}
let effective_xdic = if xdictionary_handle.is_none() && !complete_common {
self.extension_dictionary_handle(handle)
.filter(|xdic| self.document.objects.contains_key(xdic))
} else {
*xdictionary_handle
};
let no_xdic = effective_xdic.is_none();
if self.version.r2004_plus() {
self.writer.write_bit(no_xdic);
if !no_xdic {
self.writer.write_handle(
DwgReferenceType::HardOwnership,
effective_xdic.unwrap().value(),
);
}
} else {
let xdic_val = effective_xdic.map(|h| h.value()).unwrap_or(0);
self.writer
.write_handle(DwgReferenceType::HardOwnership, xdic_val);
}
if let Some(xdic) = effective_xdic {
if !xdic.is_null() {
self.object_queue.push_back(xdic);
}
}
if self.version.r2013_plus(self.dxf_version) {
let has_ds_data = has_ds_data_override.unwrap_or_else(|| {
self.document.dwg_source_version == Some(self.dxf_version)
&& self.document.dwg_data_store_handles.contains(&handle)
});
self.writer.write_bit(has_ds_data);
}
}
pub fn write_xref_dependant_bit(&mut self) {
self.write_xref_dependant_bit_value(false);
}
pub fn write_xref_dependant_bit_value(&mut self, xref_dep: bool) {
self.write_xref_table_flags(false, false, xref_dep);
}
pub fn write_xref_table_flags(
&mut self,
xref_reference: bool,
xref_resolved: bool,
xref_dependent: bool,
) {
if self.version.r2007_plus() {
let combined: i16 = if xref_resolved || xref_dependent {
0x100
} else {
0
};
self.writer.write_bit_short(combined);
} else {
self.writer.write_bit(xref_reference);
self.writer
.write_bit_short(if xref_resolved { 0x100 } else { 0 });
self.writer.write_bit(xref_dependent);
}
}
pub fn encode_xdata_record(
&self,
rec: &crate::xdata::ExtendedDataRecord,
) -> Option<(u64, Vec<u8>)> {
let app = self.document.app_ids.get(&rec.application_name)?;
let code_page =
crate::io::dxf::code_page::dwg_code_page_index(&self.document.header.code_page);
let encoding = crate::io::dxf::code_page::encoding_from_dwg_code_page(code_page);
let bytes = crate::io::dwg::eed_codec::encode_values_with_encoding(
self.version.r2007_plus(),
&rec.values,
encoding,
code_page,
|name| {
self.document
.layers
.get(name)
.map(|l| l.handle.value())
.unwrap_or(0)
},
);
(bytes.len() <= i16::MAX as usize).then_some((app.handle.value(), bytes))
}
pub fn write_extended_data(&mut self, xdata: &crate::xdata::ExtendedData) {
self.write_extended_data_with(xdata, &[]);
}
pub fn write_extended_data_with(
&mut self,
xdata: &crate::xdata::ExtendedData,
extra: &[(u64, Vec<u8>)],
) {
let eed_compatible = match self.document.dwg_source_version {
None => true,
Some(src) => {
(src >= crate::types::DxfVersion::AC1021)
== (self.dxf_version >= crate::types::DxfVersion::AC1021)
}
};
let registered = |app: u64| {
self.document
.app_ids
.iter()
.any(|a| a.handle.value() == app)
};
let raw_blocks: Vec<(u64, Vec<u8>)> = if eed_compatible {
xdata
.raw_dwg_eed
.iter()
.filter(|(app, _)| registered(*app))
.cloned()
.collect()
} else {
Vec::new()
};
let record_blocks: Vec<(u64, Vec<u8>)> = xdata
.records()
.iter()
.filter_map(|rec| self.encode_xdata_record(rec))
.filter(|(app, _)| !raw_blocks.iter().chain(extra).any(|(a, _)| a == app))
.chain(extra.iter().cloned())
.collect();
if raw_blocks.is_empty() && record_blocks.is_empty() {
self.writer.write_bit_short(0);
return;
}
for (app_handle, raw_bytes) in raw_blocks.iter().chain(record_blocks.iter()) {
self.writer.write_bit_short(raw_bytes.len() as i16);
self.writer.main_mut().write_handle(
crate::io::dwg::dwg_reference_type::DwgReferenceType::HardPointer,
*app_handle,
);
for &b in raw_bytes {
self.writer.write_byte(b);
}
}
self.writer.write_bit_short(0);
}
pub fn alloc_handle(&mut self) -> Handle {
let h = self.next_alloc_handle;
self.next_alloc_handle += 1;
Handle::new(h)
}
pub fn class_type_code(&self, dxf_name: &str, fallback: i16) -> i16 {
self.document
.classes
.get_by_name(dxf_name)
.map(|c| c.class_number)
.unwrap_or(fallback)
}
fn get_entity_mode(&self, owner_handle: &Handle) -> u8 {
self.entity_modes.get(owner_handle).copied().unwrap_or(0)
}
pub(super) fn build_entity_modes(
document: &crate::document::CadDocument,
paper_space_block_handle: Handle,
) -> std::collections::HashMap<Handle, u8> {
let mut modes = std::collections::HashMap::with_capacity(document.block_records.len());
for br in document.block_records.iter() {
let mode = if br.name.eq_ignore_ascii_case("*MODEL_SPACE") {
2 } else if br.handle == paper_space_block_handle {
1 } else {
0 };
modes.entry(br.handle).or_insert(mode);
}
modes
}
}
pub(crate) fn write_modular_short_bytes(output: &mut Vec<u8>, value: usize) {
let mut remaining = value;
loop {
let word = (remaining & 0x7FFF) as u16;
remaining >>= 15;
if remaining > 0 {
output.extend_from_slice(&(word | 0x8000).to_le_bytes());
} else {
output.extend_from_slice(&word.to_le_bytes());
break;
}
}
}
pub(crate) fn write_modular_char_bytes(output: &mut Vec<u8>, value: usize) {
if value == 0 {
output.push(0);
return;
}
let mut remaining = value;
while remaining > 0 {
let b = (remaining & 0x7F) as u8;
remaining >>= 7;
if remaining > 0 {
output.push(b | 0x80);
} else {
output.push(b);
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn modular_short_small() {
let mut buf = Vec::new();
write_modular_short_bytes(&mut buf, 100);
assert_eq!(buf.len(), 2);
assert_eq!(u16::from_le_bytes([buf[0], buf[1]]), 100);
}
#[test]
fn modular_short_large() {
let mut buf = Vec::new();
write_modular_short_bytes(&mut buf, 0x8000);
assert_eq!(buf.len(), 4);
let w0 = u16::from_le_bytes([buf[0], buf[1]]);
assert_ne!(w0 & 0x8000, 0);
let w1 = u16::from_le_bytes([buf[2], buf[3]]);
assert_eq!(w1 & 0x8000, 0);
let lo = (w0 & 0x7FFF) as usize;
let hi = (w1 & 0x7FFF) as usize;
assert_eq!(lo | (hi << 15), 0x8000);
}
#[test]
fn modular_short_zero() {
let mut buf = Vec::new();
write_modular_short_bytes(&mut buf, 0);
assert_eq!(buf.len(), 2);
assert_eq!(u16::from_le_bytes([buf[0], buf[1]]), 0);
}
#[test]
fn modular_short_max_single_word() {
let mut buf = Vec::new();
write_modular_short_bytes(&mut buf, 0x7FFF);
assert_eq!(buf.len(), 2);
assert_eq!(u16::from_le_bytes([buf[0], buf[1]]), 0x7FFF);
}
}