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_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_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_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;
impl<'a> DwgObjectWriter<'a> {
pub fn register_object(&mut self, handle: Handle) {
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));
}
self.writer.reset();
}
pub fn register_raw_object(&mut self, handle: Handle, raw_data: &[u8], handle_bits: i64) {
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.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);
}
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,
xdata: &crate::xdata::ExtendedData,
reactors: &[Handle],
xdictionary_handle: &Option<Handle>,
) {
self.write_common_data(type_code, handle, xdata);
self.writer.write_bit(false);
if self.version.r13_14_only() {
self.writer.save_position_for_size();
}
let entmode = 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());
}
if self.version.r2004_plus() {
self.writer.write_bit(xdictionary_handle.is_none());
if let Some(xdic) = xdictionary_handle {
self.writer
.write_handle(DwgReferenceType::HardOwnership, xdic.value());
}
} else {
let xdic_val = xdictionary_handle
.map(|h| h.value())
.unwrap_or(0);
self.writer
.write_handle(DwgReferenceType::HardOwnership, xdic_val);
}
if let Some(xdic) = xdictionary_handle {
if !xdic.is_null() {
self.object_queue.push_back(*xdic);
}
}
if self.version.r2013_plus(self.dxf_version) {
self.writer.write_bit(false);
}
if self.version.r13_14_only() {
let layer_h = self
.document
.layers
.get(layer)
.map(|l| l.handle)
.unwrap_or(Handle::NULL);
self.writer
.write_handle(DwgReferenceType::HardPointer, layer_h.value());
self.writer.write_bit(true); }
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() {
self.writer.write_en_color(color, transparency);
} 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
.document
.layers
.get(layer)
.map(|l| l.handle)
.unwrap_or(Handle::NULL);
self.writer
.write_handle(DwgReferenceType::HardPointer, layer_h.value());
let lt_lower = linetype.to_ascii_lowercase();
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 = self
.document
.line_types
.get(linetype)
.map(|lt| lt.handle)
.unwrap_or(Handle::NULL);
self.writer
.write_handle(DwgReferenceType::HardPointer, lt_handle.value());
}
if self.version.r2007_plus() {
self.writer.write_2bits(0b00);
self.writer.write_byte(0);
}
self.writer.write_2bits(0b00);
if self.version.r2010_plus() {
self.writer.write_bit(false); self.writer.write_bit(false); self.writer.write_bit(false); }
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.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 empty = crate::xdata::ExtendedData::default();
self.write_extended_data(&empty);
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);
self.writer
.write_handle(DwgReferenceType::SoftPointer, effective_owner.value());
self.writer.write_bit_long(reactors.len() as i32);
for r in reactors {
self.writer
.write_handle(DwgReferenceType::SoftPointer, r.value());
}
let no_xdic = xdictionary_handle.is_none();
if self.version.r2004_plus() {
self.writer.write_bit(no_xdic);
if !no_xdic {
self.writer.write_handle(
DwgReferenceType::HardOwnership,
xdictionary_handle.unwrap().value(),
);
}
} else {
let xdic_val = xdictionary_handle
.map(|h| h.value())
.unwrap_or(0);
self.writer
.write_handle(DwgReferenceType::HardOwnership, xdic_val);
}
if let Some(xdic) = xdictionary_handle {
if !xdic.is_null() {
self.object_queue.push_back(*xdic);
}
}
if self.version.r2013_plus(self.dxf_version) {
self.writer.write_bit(false);
}
}
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) {
if self.version.r2007_plus() {
let combined: i16 = if xref_dep { 0x10 } else { 0 };
self.writer.write_bit_short(combined);
} else {
self.writer.write_bit(false); self.writer.write_bit_short(0); self.writer.write_bit(xref_dep); }
}
pub fn write_extended_data(&mut self, _xdata: &crate::xdata::ExtendedData) {
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 {
for br in self.document.block_records.iter() {
if br.handle == *owner_handle {
let upper = br.name.to_ascii_uppercase();
if upper == "*MODEL_SPACE" {
return 2; }
if upper == "*PAPER_SPACE"
|| (upper.starts_with("*PAPER_SPACE")
&& upper.len() > 12
&& upper[12..].bytes().all(|b| b.is_ascii_digit()))
{
return 1; }
return 0; }
}
0
}
}
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);
}
}