use crate::io::dwg::dwg_reference_type::DwgReferenceType;
use crate::objects::*;
use crate::types::{Color, DxfVersion, Handle};
use super::common;
use super::DwgObjectWriter;
#[derive(PartialEq)]
enum XdVt {
Str,
Real,
Int16,
Int32,
Int64,
Int8,
Point3d,
Binary,
Handle,
Invalid,
}
fn xdata_value_type(gc: i16) -> XdVt {
use XdVt::*;
match gc {
g if g < 0 => Handle,
0..=4 => Str,
5 => Handle,
6..=9 => Str,
10..=37 => Point3d,
38..=59 => Real,
60..=79 => Int16,
80..=99 => Int32,
100..=102 => Str,
105 => Handle,
110..=139 => Point3d,
140..=149 => Real,
150..=169 => Int64,
170..=179 => Int16,
210..=269 => Point3d,
270..=279 => Int16,
280..=289 => Int8,
290..=299 => Int8, 300..=309 => Str,
310..=319 => Binary,
320..=329 => Handle,
330..=369 => Handle,
370..=389 => Int16,
390..=399 => Handle,
400..=409 => Int16,
410..=419 => Str,
420..=429 => Int32,
430..=439 => Str,
440..=459 => Int32,
460..=469 => Real,
470..=479 => Str,
480..=481 => Handle,
999 => Str,
1004 => Binary,
1000..=1003 => Str,
1005 => Handle,
1010..=1039 => Point3d,
1040..=1042 => Real,
1043..=1069 => Point3d,
1070 => Int16,
1071 => Int32,
_ => Invalid,
}
}
fn xrecord_reference_type(kind: ProxyReferenceKind) -> DwgReferenceType {
match kind {
ProxyReferenceKind::Undefined => DwgReferenceType::Undefined,
ProxyReferenceKind::SoftOwnership => DwgReferenceType::SoftOwnership,
ProxyReferenceKind::HardOwnership => DwgReferenceType::HardOwnership,
ProxyReferenceKind::SoftPointer => DwgReferenceType::SoftPointer,
ProxyReferenceKind::HardPointer => DwgReferenceType::HardPointer,
}
}
fn transcode_xrecord_xdata(
raw: &[u8],
src_unicode: bool,
tgt_unicode: bool,
target_encoding: crate::io::dxf::code_page::LegacyEncoding,
target_code_page: u8,
) -> Vec<u8> {
if src_unicode == tgt_unicode {
return raw.to_vec();
}
let rd_u16 = |b: &[u8], i: usize| (b[i] as u16) | ((b[i + 1] as u16) << 8);
let mut out = Vec::with_capacity(raw.len() + raw.len() / 2);
let mut p = 0usize;
while p + 2 <= raw.len() {
let item_start = p;
let output_start = out.len();
macro_rules! preserve_tail {
() => {{
out.truncate(output_start);
out.extend_from_slice(&raw[item_start..]);
return out;
}};
}
let gc = rd_u16(raw, p) as i16;
let vt = xdata_value_type(gc);
if vt == XdVt::Invalid {
preserve_tail!();
}
out.extend_from_slice(&raw[p..p + 2]); p += 2;
let fixed = match vt {
XdVt::Real | XdVt::Int64 | XdVt::Handle => Some(8usize),
XdVt::Point3d => Some(24),
XdVt::Int32 => Some(4),
XdVt::Int16 => Some(2),
XdVt::Int8 => Some(1),
_ => None,
};
if let Some(n) = fixed {
if p + n > raw.len() {
preserve_tail!();
}
out.extend_from_slice(&raw[p..p + n]);
p += n;
continue;
}
if vt == XdVt::Binary {
if p >= raw.len() {
preserve_tail!();
}
let size = raw[p] as usize;
if p + 1 + size > raw.len() {
preserve_tail!();
}
out.extend_from_slice(&raw[p..p + 1 + size]);
p += 1 + size;
continue;
}
if p + 2 > raw.len() {
preserve_tail!();
}
let len = rd_u16(raw, p) as usize;
p += 2;
let text: String = if src_unicode {
if p + len * 2 > raw.len() {
preserve_tail!();
}
let units: Vec<u16> = (0..len).map(|i| rd_u16(raw, p + i * 2)).collect();
p += len * 2;
String::from_utf16_lossy(&units)
} else {
if p + 1 + len > raw.len() {
preserve_tail!();
}
let source_code_page = raw[p] as u16;
p += 1;
let s = crate::io::dxf::code_page::encoding_from_dwg_code_page(source_code_page)
.decode(&raw[p..p + len])
.0
.into_owned();
p += len;
crate::io::dxf::code_page::decode_legacy_escapes(&s)
};
if tgt_unicode {
let utf16: Vec<u16> = text.encode_utf16().take(u16::MAX as usize).collect();
out.extend_from_slice(&(utf16.len() as u16).to_le_bytes());
for u in utf16 {
out.extend_from_slice(&u.to_le_bytes());
}
} else {
let encoded = crate::io::dxf::code_page::encode_legacy_string(&text, target_encoding);
let bytes = &encoded[..encoded.len().min(u16::MAX as usize)];
out.extend_from_slice(&(bytes.len() as u16).to_le_bytes());
out.push(target_code_page);
out.extend_from_slice(&bytes);
}
}
out.extend_from_slice(&raw[p..]);
out
}
fn encode_xrecord_entries(
entries: &[XRecordEntry],
unicode: bool,
encoding: crate::io::dxf::code_page::LegacyEncoding,
code_page: u8,
) -> Vec<u8> {
let mut output = Vec::new();
for entry in entries {
output.extend_from_slice(&(entry.code as i16 as u16).to_le_bytes());
match &entry.value {
XRecordValue::String(value) => {
if unicode {
let units: Vec<u16> = value.encode_utf16().collect();
output.extend_from_slice(
&(units.len().min(u16::MAX as usize) as u16).to_le_bytes(),
);
for unit in units.iter().take(u16::MAX as usize) {
output.extend_from_slice(&unit.to_le_bytes());
}
} else {
let encoded = crate::io::dxf::code_page::encode_legacy_string(value, encoding);
let bytes: &[u8] = encoded.as_ref();
output.extend_from_slice(
&(bytes.len().min(u16::MAX as usize) as u16).to_le_bytes(),
);
output.push(code_page);
output.extend_from_slice(&bytes[..bytes.len().min(u16::MAX as usize)]);
}
}
XRecordValue::Double(value) => {
output.extend_from_slice(&value.to_le_bytes());
}
XRecordValue::Int16(value) => {
output.extend_from_slice(&value.to_le_bytes());
}
XRecordValue::Int32(value) => {
output.extend_from_slice(&value.to_le_bytes());
}
XRecordValue::Int64(value) => {
output.extend_from_slice(&value.to_le_bytes());
}
XRecordValue::Byte(value) => output.push(*value),
XRecordValue::Bool(value) => output.push(*value as u8),
XRecordValue::Handle(value) => {
output.extend_from_slice(&value.value().to_le_bytes());
}
XRecordValue::Point3D(x, y, z) => {
output.extend_from_slice(&x.to_le_bytes());
output.extend_from_slice(&y.to_le_bytes());
output.extend_from_slice(&z.to_le_bytes());
}
XRecordValue::Chunk(value) => {
let length = value.len().min(u8::MAX as usize);
output.push(length as u8);
output.extend_from_slice(&value[..length]);
}
}
}
output
}
fn matrix_to_dxf_4x3(m: &crate::types::Matrix4) -> [f64; 12] {
let mut out = [0.0; 12];
let mut i = 0;
for row in 0..3 {
for col in 0..4 {
out[i] = m.m[row][col];
i += 1;
}
}
out
}
pub(crate) fn object_class_names(object: &ObjectType) -> Vec<std::borrow::Cow<'_, str>> {
use std::borrow::Cow;
let name: &str = match object {
ObjectType::MultiLeaderStyle(_) => "MLEADERSTYLE",
ObjectType::ImageDefinition(_) => "IMAGEDEF",
ObjectType::UnderlayDefinition(value) => value.entity_name(),
ObjectType::ImageDefinitionReactor(_) => "IMAGEDEF_REACTOR",
ObjectType::PlotSettings(_) => "PLOTSETTINGS",
ObjectType::Scale(_) => "SCALE",
ObjectType::ObjectContextData(value) => value.class_name(),
ObjectType::SortEntitiesTable(_) => "SORTENTSTABLE",
ObjectType::DictionaryVariable(_) => "DICTIONARYVAR",
ObjectType::RasterVariables(_) => "RASTERVARIABLES",
ObjectType::DictionaryWithDefault(_) => "ACDBDICTIONARYWDFLT",
ObjectType::BookColor(_) => "DBCOLOR",
ObjectType::WipeoutVariables(_) => "WIPEOUTVARIABLES",
ObjectType::SpatialFilter(_) => "SPATIAL_FILTER",
ObjectType::GeoData(_) => "GEODATA",
ObjectType::BlockVisibilityParameter(_) => "BLOCKVISIBILITYPARAMETER",
ObjectType::TableContent(_) => "TABLECONTENT",
ObjectType::VisualStyle(_) => "VISUALSTYLE",
ObjectType::Material(_) => "MATERIAL",
ObjectType::TableStyle(_) => "TABLESTYLE",
ObjectType::Field(_) => "FIELD",
ObjectType::FieldList(_) => "FIELDLIST",
ObjectType::DynamicBlock(value) => &value.dxf_name,
ObjectType::DgnLineStyle(value) => value.dxf_name(),
ObjectType::ClassObject(value) if !matches!(value.data, ClassObjectData::VbaProject(_)) => {
value.dxf_name()
}
ObjectType::DataObject(value)
if !matches!(
value.data,
DataObjectData::Dummy | DataObjectData::LongTransaction
) =>
{
value.dxf_name()
}
ObjectType::RegisteredClass(value) if value.properties.is_empty() => &value.dxf_name,
ObjectType::Associative(value) => {
let canonical = associative_canonical_name(&value.dxf_name);
return vec![
Cow::Borrowed(value.dxf_name.as_str()),
Cow::Owned(format!("ACDB{canonical}")),
];
}
_ => return Vec::new(),
};
vec![Cow::Borrowed(name)]
}
const STALE_TABLE_STYLE_FLAG: i16 = 8;
impl<'a> DwgObjectWriter<'a> {
pub(super) fn lacks_object_class(&self, object: &ObjectType) -> bool {
let class_only = matches!(
object,
ObjectType::ObjectContextData(_)
| ObjectType::DynamicBlock(_)
| ObjectType::Associative(_)
| ObjectType::DgnLineStyle(_)
| ObjectType::Field(_)
| ObjectType::FieldList(_)
| ObjectType::ClassObject(_)
| ObjectType::DataObject(_)
| ObjectType::RegisteredClass(_)
);
let names = object_class_names(object);
class_only
&& !names.is_empty()
&& !names
.iter()
.any(|name| self.document.classes.contains(name))
}
pub(super) fn write_object(&mut self, obj: &ObjectType) {
if self.lacks_object_class(obj) {
return;
}
let preserved_record = match obj {
ObjectType::DynamicBlock(value) => Some((value.handle, value.xdictionary_handle)),
ObjectType::Associative(value) => Some((value.handle, value.xdictionary_handle)),
_ => None,
};
if let Some((handle, source_dictionary)) = preserved_record {
let effective_dictionary = source_dictionary.or_else(|| {
self.extension_dictionary_handle(handle)
.filter(|dictionary| self.document.objects.contains_key(dictionary))
});
if self.document.original_objects.get(&handle) == Some(obj)
&& effective_dictionary == source_dictionary
{
if let Some((_, raw)) = self.document.raw_records.get(&handle.value()) {
if raw.version == self.dxf_version {
let raw = raw.clone();
self.register_raw_object(handle, &raw.data, raw.handle_bits);
return;
}
}
}
}
match obj {
ObjectType::Dictionary(d) => self.write_dictionary(d),
ObjectType::Layout(l) => self.write_layout(l),
ObjectType::XRecord(x) => {
if !x.entries_complete && !self.raw_excluded_handles.contains(&x.handle.value()) {
if let Some(raw) = &x.raw_dwg_data {
if self.raw_passthrough_compatible(x.raw_dwg_version) {
for reference in &x.object_references {
if self.document.objects.contains_key(&reference.handle) {
self.object_queue.push_back(reference.handle);
}
}
if let Some(xdictionary) = x.xdictionary_handle {
if self.document.objects.contains_key(&xdictionary) {
self.object_queue.push_back(xdictionary);
}
}
self.register_raw_object(x.handle, raw, x.raw_dwg_handle_bits);
return;
}
}
}
self.write_xrecord(x)
}
ObjectType::Group(g) => self.write_group(g),
ObjectType::MLineStyle(m) => self.write_mlinestyle(m),
ObjectType::MultiLeaderStyle(m) => self.write_multileader_style(m),
ObjectType::ImageDefinition(d) => self.write_image_definition(d),
ObjectType::UnderlayDefinition(d) => self.write_underlay_definition(d),
ObjectType::ImageDefinitionReactor(r) => self.write_image_definition_reactor(r),
ObjectType::PlotSettings(p) => self.write_plot_settings_obj(p),
ObjectType::Scale(s) => self.write_scale(s),
ObjectType::ObjectContextData(c) => self.write_object_context_data(c),
ObjectType::SortEntitiesTable(s) => self.write_sort_entities_table(s),
ObjectType::DictionaryVariable(d) => self.write_dictionary_variable(d),
ObjectType::RasterVariables(r) => self.write_raster_variables(r),
ObjectType::DictionaryWithDefault(d) => self.write_dictionary_with_default(d),
ObjectType::PlaceHolder(p) => self.write_placeholder(p),
ObjectType::BookColor(b) => self.write_book_color(b),
ObjectType::WipeoutVariables(w) => self.write_wipeout_variables(w),
ObjectType::SpatialFilter(s) => self.write_spatial_filter(s),
ObjectType::GeoData(g) => self.write_geodata(g),
ObjectType::BlockVisibilityParameter(p) => self.write_block_visibility_parameter(p),
ObjectType::DynamicBlock(value) => self.write_dynamic_block(value),
ObjectType::Associative(value) => self.write_associative_object(value),
ObjectType::ClassObject(value) => {
if let ClassObjectData::CsacDocumentOptions(data) = &value.data {
if let Some(raw) = &data.raw_dwg_data {
if self.raw_passthrough_compatible(data.raw_dwg_version)
&& !self.raw_excluded_handles.contains(&value.handle.value())
{
self.register_raw_object(value.handle, raw, data.raw_dwg_handle_bits);
return;
}
}
}
self.write_class_object(value)
}
ObjectType::DataObject(value) => self.write_data_object(value),
ObjectType::Field(value) => self.write_field_object(value),
ObjectType::FieldList(value) => self.write_field_list(value),
ObjectType::RegisteredClass(value) => {
if value.properties.is_empty()
&& !self.raw_excluded_handles.contains(&value.handle.value())
{
if let Some(raw) = &value.raw_dwg_data {
if self.raw_passthrough_compatible(value.raw_dwg_version) {
self.register_raw_object(value.handle, raw, value.raw_dwg_handle_bits);
return;
}
}
}
self.write_registered_class_object(value)
}
ObjectType::DgnLineStyle(value) => self.write_dgn_line_style_object(value),
ObjectType::ProxyObject(value) => self.write_proxy_object(value),
ObjectType::Arch(value) => {
self.write_architecture_object(value);
}
ObjectType::VisualStyle(v) => self.write_visual_style(v),
ObjectType::Material(m) => self.write_material(m),
ObjectType::TableContent(t) => self.write_table_content_object(t),
ObjectType::TableStyle(t) => self.write_table_style(t),
ObjectType::Unknown {
handle,
raw_dwg_data,
raw_dwg_handle_bits,
raw_dwg_version,
..
} => {
if let Some(ref raw) = raw_dwg_data {
if self.raw_passthrough_compatible(*raw_dwg_version) {
self.register_raw_object(*handle, raw, *raw_dwg_handle_bits);
}
}
}
}
}
fn write_registered_class_object(&mut self, value: &RegisteredClassObject) {
if !value.properties.is_empty() {
let payload = crate::objects::semantic_property::encode_registered_class_envelope(
&value.dxf_name,
&value.cpp_class_name,
&value.properties,
&value.payload,
);
self.write_common_non_entity_data(
common::OBJ_PROXY_OBJECT,
value.handle,
value.owner,
&value.reactors,
&value.xdictionary_handle,
);
self.writer.write_bit_long(499);
if self.dxf_version > crate::types::DxfVersion::AC1015 {
self.writer.write_variable_text(&value.dxf_name);
}
if self.version.r2018_plus(self.dxf_version) {
self.writer.write_bit_long(0);
self.writer.write_bit_long(0);
} else {
self.writer.write_bit_long(0);
}
if self.version.r2000_plus() {
self.writer.write_bit(true);
}
self.write_registered_payload(&payload, &value.object_ids);
self.register_object(value.handle);
return;
}
let type_code = self.class_type_code(&value.dxf_name, 0);
self.write_common_non_entity_data(
type_code,
value.handle,
value.owner,
&value.reactors,
&value.xdictionary_handle,
);
self.write_registered_payload(&value.payload, &value.object_ids);
self.register_object(value.handle);
}
fn write_dgn_line_style_object(&mut self, value: &DgnLineStyleObject) {
let type_code = self.class_type_code(value.dxf_name(), 0);
self.write_common_non_entity_data(
type_code,
value.handle,
value.owner,
&value.reactors,
&value.xdictionary_handle,
);
match &value.data {
DgnLineStyleData::Definition {
description,
version,
style_number,
component_uid,
is_continuous,
unit_definition,
unit_scale,
units_type,
is_element,
is_physical,
is_scale_independent,
is_snappable,
root_component,
..
} => {
self.writer.write_variable_text(description);
self.writer.write_bit_long(*version);
self.writer.write_bit_long(*style_number);
self.writer.write_bytes(component_uid);
self.writer.write_bit(*is_continuous);
self.writer.write_bit_double(*unit_definition);
self.writer.write_bit_double(*unit_scale);
self.writer.write_bit_long(*units_type);
self.writer.write_bit(*is_element);
self.writer.write_bit(*is_physical);
self.writer.write_bit(*is_scale_independent);
self.writer.write_bit(*is_snappable);
self.writer
.write_handle(DwgReferenceType::HardPointer, root_component.value());
}
DgnLineStyleData::Component {
kind,
description,
version,
component_uid,
scale,
property_flags,
component,
..
} => {
self.writer.write_variable_text(description);
self.writer.write_bit_long(*version);
self.writer.write_bit_long(kind.code());
self.writer.write_bytes(component_uid);
self.writer.write_bit_double(*scale);
self.writer.write_bytes(&[*property_flags]);
self.write_dgn_line_style_component(component);
}
DgnLineStyleData::Registered {
payload,
object_ids,
..
} => self.write_registered_payload(payload, object_ids),
}
self.register_object(value.handle);
}
fn write_dgn_line_style_component(&mut self, component: &DgnLsComponentData) {
match component {
DgnLsComponentData::Symbol(value) => {
self.writer.write_bit_double(value.stored_unit_scale);
self.writer.write_bit_double(value.unit_scale);
self.writer.write_bit(value.has_unit_scale);
self.writer.write_bit(value.is_3d);
self.writer
.write_handle(DwgReferenceType::HardPointer, value.block.value());
}
DgnLsComponentData::Compound(value) => {
self.writer.write_bit_long(value.entries.len() as i32);
for entry in &value.entries {
self.writer.write_bit_double(entry.offset);
}
for entry in &value.entries {
self.writer
.write_handle(DwgReferenceType::HardPointer, entry.component.value());
}
}
DgnLsComponentData::Stroke(value) => {
self.write_dgn_stroke_pattern(value);
}
DgnLsComponentData::Point(value) => {
self.writer.write_bit_long(value.symbols.len() as i32);
for symbol in &value.symbols {
self.writer.write_bit(symbol.partial_strokes);
self.writer.write_bit(symbol.clip_partial);
self.writer.write_bit(symbol.allow_stretch);
self.writer.write_bit(symbol.partial_projected);
self.writer.write_bit(symbol.use_symbol_color);
self.writer.write_bit(symbol.use_symbol_lineweight);
self.writer.write_bit_long(symbol.justify);
self.writer.write_bit_long(symbol.rotation_type);
self.writer.write_bit_long(symbol.vertex_mask);
self.writer.write_bit_double(symbol.x_offset);
self.writer.write_bit_double(symbol.y_offset);
self.writer.write_bit_double(symbol.angle);
self.writer.write_bit_long(symbol.stroke_number);
}
self.writer.write_handle(
DwgReferenceType::HardPointer,
value.stroke_component.value(),
);
for symbol in &value.symbols {
self.writer.write_handle(
DwgReferenceType::HardPointer,
symbol.symbol_component.value(),
);
}
}
DgnLsComponentData::Internal(value) => {
self.write_dgn_stroke_pattern(&value.pattern);
self.writer.write_bit_long(value.internal_version);
self.writer.write_bit_long(value.hardware_style);
self.writer.write_bit(value.is_hardware_style);
self.writer.write_bit_long(value.line_code);
}
}
}
fn write_dgn_stroke_pattern(&mut self, pattern: &DgnLsStrokePattern) {
self.writer.write_bit(pattern.has_iteration_limit);
self.writer.write_bit(pattern.is_single_segment);
self.writer.write_bit_long(pattern.iteration_limit);
self.writer.write_bit_double(pattern.auto_phase);
self.writer.write_bit_double(pattern.phase);
let phase_mode = pattern.phase_mode.code();
self.writer.write_bit((phase_mode & 2) != 0);
self.writer.write_bit((phase_mode & 1) != 0);
self.writer.write_bit_long(pattern.strokes.len() as i32);
for stroke in &pattern.strokes {
self.writer.write_bit(stroke.is_dash);
self.writer.write_bit(stroke.bypass_corner);
self.writer.write_bit(stroke.can_be_scaled);
self.writer.write_bit(stroke.invert_at_origin);
self.writer.write_bit(stroke.invert_at_end);
self.writer.write_bit_double(stroke.length);
self.writer.write_bit_double(stroke.start_width);
self.writer.write_bit_double(stroke.end_width);
self.writer.write_bit_long(stroke.width_mode);
self.writer.write_bit_long(stroke.cap_mode);
}
}
pub(super) fn write_registered_payload(
&mut self,
payload: &crate::objects::ProxyPayload,
object_ids: &[crate::objects::ProxyObjectReference],
) {
let data = payload.data();
for bit_index in 0..payload.bit_count as usize {
let byte = data.get(bit_index / 8).copied().unwrap_or(0);
self.writer
.write_bit((byte & (0x80 >> (bit_index % 8))) != 0);
}
for object_id in object_ids {
let reference_type = match object_id.kind {
crate::objects::ProxyReferenceKind::Undefined => DwgReferenceType::Undefined,
crate::objects::ProxyReferenceKind::SoftOwnership => {
DwgReferenceType::SoftOwnership
}
crate::objects::ProxyReferenceKind::HardOwnership => {
DwgReferenceType::HardOwnership
}
crate::objects::ProxyReferenceKind::SoftPointer => DwgReferenceType::SoftPointer,
crate::objects::ProxyReferenceKind::HardPointer => DwgReferenceType::HardPointer,
};
self.writer
.write_handle(reference_type, object_id.handle.value());
}
}
fn write_proxy_object(&mut self, value: &ProxyObject) {
self.write_common_non_entity_data(
common::OBJ_PROXY_OBJECT,
value.handle,
value.owner,
&value.reactors,
&value.xdictionary_handle,
);
self.writer.write_bit_long(value.class_id);
if self.dxf_version > crate::types::DxfVersion::AC1015 {
let dxf_subclass = if value.dxf_subclass.is_empty() {
self.document
.classes
.iter()
.find(|class| i32::from(class.class_number) == value.class_id)
.map(|class| class.dxf_name.as_str())
.unwrap_or("")
} else {
&value.dxf_subclass
};
self.writer.write_variable_text(dxf_subclass);
}
if self.version.r2018_plus(self.dxf_version) {
self.writer.write_bit_long(value.dwg_version);
self.writer.write_bit_long(value.maintenance_version);
} else {
self.writer
.write_bit_long((value.maintenance_version << 16) | (value.dwg_version & 0xffff));
}
if self.version.r2000_plus() {
self.writer.write_bit(value.from_dxf);
}
let payload = value.payload.data();
for bit_index in 0..value.payload.bit_count as usize {
let byte = payload.get(bit_index / 8).copied().unwrap_or(0);
self.writer
.write_bit((byte & (0x80 >> (bit_index % 8))) != 0);
}
let text_payload = value.text_payload.data();
for bit_index in 0..value.text_payload.bit_count as usize {
let byte = text_payload.get(bit_index / 8).copied().unwrap_or(0);
self.writer
.write_text_bit((byte & (0x80 >> (bit_index % 8))) != 0);
}
for object_id in &value.object_ids {
let reference_type = match object_id.kind {
crate::objects::ProxyReferenceKind::Undefined => DwgReferenceType::Undefined,
crate::objects::ProxyReferenceKind::SoftOwnership => {
DwgReferenceType::SoftOwnership
}
crate::objects::ProxyReferenceKind::HardOwnership => {
DwgReferenceType::HardOwnership
}
crate::objects::ProxyReferenceKind::SoftPointer => DwgReferenceType::SoftPointer,
crate::objects::ProxyReferenceKind::HardPointer => DwgReferenceType::HardPointer,
};
self.writer
.write_handle(reference_type, object_id.handle.value());
}
self.register_object(value.handle);
}
fn write_block_visibility_parameter(&mut self, value: &BlockVisibilityParameter) {
let type_code = self.class_type_code(
"BLOCKVISIBILITYPARAMETER",
common::OBJ_BLOCKVISIBILITYPARAMETER,
);
self.write_common_non_entity_data(type_code, value.handle, value.owner, &[], &None);
self.writer.write_bit_long(value.eval_parent_id);
self.writer.write_bit_long(value.eval_major);
self.writer.write_bit_long(value.eval_minor);
self.writer.write_bit_short(value.eval_value_code);
match &value.eval_value {
BlockEvalValue::Real(v) => self.writer.write_bit_double(*v),
BlockEvalValue::Point(v) => self
.writer
.write_2raw_double(crate::types::Vector2::new(v[0], v[1])),
BlockEvalValue::Text(v) => self.writer.write_variable_text(v),
BlockEvalValue::Long(v) => self.writer.write_bit_long(*v),
BlockEvalValue::Handle(v) => self
.writer
.write_handle(DwgReferenceType::HardPointer, v.value()),
BlockEvalValue::Short(v) => self.writer.write_bit_short(*v),
BlockEvalValue::None => {}
}
self.writer.write_bit_long(value.eval_node_id);
self.writer.write_variable_text(&value.element_name);
self.writer.write_bit_long(value.element_major);
self.writer.write_bit_long(value.element_minor);
self.writer.write_bit_long(value.element_eed_1071);
self.writer.write_bit(value.show_properties);
self.writer.write_bit(value.chain_actions);
self.writer.write_3bit_double(value.def_point);
for property in &value.property_info {
self.writer
.write_bit_long(property.connections.len() as i32);
for connection in &property.connections {
self.writer.write_bit_long(connection.code);
self.writer.write_variable_text(&connection.name);
}
}
self.writer.write_bit_long(value.property_info_count);
self.writer.write_bit(value.is_initialized);
self.writer.write_variable_text(&value.name);
self.writer.write_variable_text(&value.description);
self.writer.write_bit(value.unknown_bool);
self.writer.write_bit_long(value.all_blocks.len() as i32);
for handle in &value.all_blocks {
self.writer
.write_handle(DwgReferenceType::SoftPointer, handle.value());
}
self.writer.write_bit_long(value.states.len() as i32);
for state in &value.states {
self.writer.write_variable_text(&state.name);
self.writer
.write_bit_long(state.visible_blocks.len() as i32);
for handle in &state.visible_blocks {
self.writer
.write_handle(DwgReferenceType::SoftPointer, handle.value());
}
self.writer
.write_bit_long(state.visible_params.len() as i32);
for handle in &state.visible_params {
self.writer
.write_handle(DwgReferenceType::SoftPointer, handle.value());
}
}
self.register_object(value.handle);
}
fn write_visual_style(&mut self, value: &VisualStyle) {
let Some(type_code) = self
.document
.classes
.get_by_name("VISUALSTYLE")
.map(|class| class.class_number)
else {
return;
};
let extra: Vec<(u64, Vec<u8>)> = if self.version.r2010_plus() {
Vec::new()
} else {
let mut marker = crate::xdata::ExtendedDataRecord::new("ACAD");
marker.add_value(crate::xdata::XDataValue::String(
"AcDbSavedByObjectVersion".to_string(),
));
marker.add_value(crate::xdata::XDataValue::Integer16(0));
self.encode_xdata_record(&marker).into_iter().collect()
};
self.write_common_non_entity_data_eed(
type_code,
value.handle,
value.owner,
&value.reactors,
&value.xdictionary_handle,
extra,
);
self.writer.write_variable_text(&value.description);
self.writer.write_bit_long(value.style_type as i32);
if !self.version.r2010_plus() {
self.writer.write_bit_long(value.face_lighting_model as i32);
self.writer
.write_bit_long(value.face_lighting_quality as i32);
self.writer.write_bit_long(value.face_color_mode as i32);
let properties = value.legacy_properties();
self.writer
.write_bit_double(Self::visual_style_double(&properties[0]));
self.writer
.write_bit_double(Self::visual_style_double(&properties[1]));
self.writer
.write_cm_color(&Self::visual_style_color(&properties[2]));
self.writer.write_bit_long(value.face_modifier);
self.writer.write_bit_long(value.edge_model);
self.writer.write_bit_long(value.edge_style);
self.writer
.write_cm_color(&Self::visual_style_color(&properties[3]));
self.writer
.write_cm_color(&Self::visual_style_color(&properties[4]));
self.writer
.write_bit_long(Self::visual_style_long(&properties[5]));
self.writer
.write_bit_double(Self::visual_style_double(&properties[6]));
self.writer
.write_bit_long(Self::visual_style_long(&properties[7]));
self.writer
.write_cm_color(&Self::visual_style_color(&properties[8]));
self.writer
.write_bit_double(Self::visual_style_double(&properties[9]));
self.writer
.write_bit_short(Self::visual_style_long(&properties[10]) as i16);
self.writer
.write_bit_short(Self::visual_style_long(&properties[11]) as i16);
self.writer
.write_bit_long(Self::visual_style_long(&properties[12]));
self.writer
.write_cm_color(&Self::visual_style_color(&properties[13]));
self.writer
.write_bit_short(Self::visual_style_long(&properties[14]) as i16);
self.writer
.write_byte(Self::visual_style_long(&properties[15]) as u8);
self.writer
.write_bit_short(Self::visual_style_long(&properties[16]) as i16);
self.writer
.write_bit(Self::visual_style_bool(&properties[17]));
self.writer
.write_bit_short(Self::visual_style_long(&properties[18]) as i16);
self.writer
.write_bit_short(Self::visual_style_long(&properties[19]) as i16);
self.writer
.write_bit_long(Self::visual_style_long(&properties[20]));
self.writer
.write_bit_long(Self::visual_style_long(&properties[21]));
self.writer
.write_bit_long(Self::visual_style_long(&properties[22]));
if self.version.r2007_plus() {
self.writer
.write_bit_double(Self::visual_style_double(&properties[23]));
}
self.writer.write_bit(value.internal_use_only);
} else {
self.writer.write_bit_short(value.extended_lighting_model);
self.writer.write_bit(value.internal_use_only);
for property in value.core_properties() {
self.write_visual_style_property(&property);
}
if self.version.r2013_plus(self.dxf_version) {
for property in value.extended_properties() {
self.write_visual_style_property(&property);
}
}
}
self.register_object(value.handle);
}
fn visual_style_long(property: &VisualStyleProperty) -> i32 {
match &property.value {
VisualStylePropertyValue::Short(value) => *value as i32,
VisualStylePropertyValue::Long(value) => *value,
VisualStylePropertyValue::Double(value) => *value as i32,
VisualStylePropertyValue::Bool(value) => *value as i32,
_ => 0,
}
}
fn visual_style_double(property: &VisualStyleProperty) -> f64 {
match &property.value {
VisualStylePropertyValue::Short(value) => *value as f64,
VisualStylePropertyValue::Long(value) => *value as f64,
VisualStylePropertyValue::Double(value) => *value,
VisualStylePropertyValue::Bool(value) => *value as u8 as f64,
_ => 0.0,
}
}
fn visual_style_bool(property: &VisualStyleProperty) -> bool {
match &property.value {
VisualStylePropertyValue::Short(value) => *value != 0,
VisualStylePropertyValue::Long(value) => *value != 0,
VisualStylePropertyValue::Double(value) => *value != 0.0,
VisualStylePropertyValue::Bool(value) => *value,
_ => false,
}
}
fn visual_style_color(property: &VisualStyleProperty) -> crate::types::Color {
match &property.value {
VisualStylePropertyValue::Color(value) => *value,
_ => crate::types::Color::ByLayer,
}
}
fn write_visual_style_value(&mut self, value: &VisualStylePropertyValue) {
match value {
VisualStylePropertyValue::Short(value) => {
self.writer.write_bit_short(*value);
}
VisualStylePropertyValue::Long(value) => {
self.writer.write_bit_long(*value);
}
VisualStylePropertyValue::Double(value) => {
self.writer.write_bit_double(*value);
}
VisualStylePropertyValue::Bool(value) => {
self.writer.write_bit(*value);
}
VisualStylePropertyValue::Color(value) => {
self.writer.write_cm_color(value);
}
VisualStylePropertyValue::Text(value) => {
self.writer.write_variable_text(value);
}
}
}
fn write_visual_style_property(&mut self, property: &VisualStyleProperty) {
self.write_visual_style_value(&property.value);
self.writer.write_bit_short(property.enabled);
}
fn write_material(&mut self, value: &Material) {
let Some(type_code) = self
.document
.classes
.get_by_name("MATERIAL")
.map(|class| class.class_number)
else {
return;
};
self.write_common_non_entity_data(
type_code,
value.handle,
value.owner,
&value.reactors,
&value.xdictionary_handle,
);
self.writer.write_variable_text(&value.name);
self.writer.write_variable_text(&value.description);
self.write_material_color(&value.ambient_color);
self.write_material_color(&value.diffuse_color);
self.write_material_map(&value.diffuse_map);
self.write_material_color(&value.specular_color);
self.write_material_map(&value.specular_map);
self.writer.write_bit_double(value.specular_gloss_factor);
self.write_material_map(&value.reflection_map);
self.writer.write_bit_double(value.opacity_percent);
self.write_material_map(&value.opacity_map);
self.write_material_map(&value.bump_map);
self.writer.write_bit_double(value.refraction_index);
self.write_material_map(&value.refraction_map);
if self.version.r2007_plus() {
self.writer.write_bit_double(value.translucence);
self.writer.write_bit_double(value.self_illumination);
self.writer.write_bit_double(value.reflectivity);
self.writer.write_bit_long(value.illumination_model);
self.writer.write_bit_long(value.channel_flags);
self.writer.write_bit_long(value.mode);
}
self.register_object(value.handle);
}
fn write_material_color(&mut self, value: &MaterialColor) {
self.writer.write_byte(value.flag);
self.writer.write_bit_double(value.factor);
if value.flag == 1 {
self.writer.write_bit_long(value.rgb.unwrap_or_default());
}
}
fn write_material_texture(&mut self, value: &MaterialTexture, depth: usize) {
self.writer.write_bit_short(value.mode);
if value.mode == 0 || value.mode == 1 {
self.write_material_color(&value.color1);
self.write_material_color(&value.color2);
} else if value.mode == 2 {
match value.procedural.as_ref() {
Some(MaterialProceduralValue::Bool(item)) => {
self.writer.write_bit_short(1);
self.writer.write_bit(*item);
}
Some(MaterialProceduralValue::Integer(item)) => {
self.writer.write_bit_short(2);
self.writer.write_bit_short(*item);
}
Some(MaterialProceduralValue::Real(item)) => {
self.writer.write_bit_short(3);
self.writer.write_bit_double(*item);
}
Some(MaterialProceduralValue::Color(item)) => {
self.writer.write_bit_short(4);
self.writer.write_cm_color(item);
}
Some(MaterialProceduralValue::Text(item)) => {
self.writer.write_bit_short(5);
self.writer.write_variable_text(item);
}
Some(MaterialProceduralValue::Table(items)) if depth < 8 => {
self.writer.write_bit_short(6);
self.writer
.write_bit_short(items.len().min(i16::MAX as usize) as i16);
for (name, texture) in items.iter().take(i16::MAX as usize) {
self.writer.write_variable_text(name);
self.write_material_texture(texture, depth + 1);
}
self.writer.write_bit(value.table_end);
}
_ => {
self.writer.write_bit_short(0);
}
}
}
}
fn write_material_map(&mut self, value: &MaterialMap) {
self.writer.write_bit_double(value.blend_factor);
self.writer.write_byte(value.projection);
self.writer.write_byte(value.tiling);
self.writer.write_byte(value.auto_transform);
for item in value.transform {
self.writer.write_bit_double(item);
}
self.writer.write_byte(value.source);
if value.source == 1 {
self.writer.write_variable_text(&value.file_name);
} else if value.source == 2 {
if let Some(texture) = &value.texture {
self.write_material_texture(texture, 0);
} else {
self.write_material_texture(&MaterialTexture::default(), 0);
}
}
}
fn write_table_content_object(&mut self, value: &crate::entities::Table) {
let type_code = self.class_type_code("TABLECONTENT", common::OBJ_TABLECONTENT);
self.write_common_non_entity_data(
type_code,
value.common.handle,
value.common.owner_handle,
&value.common.reactors,
&value.common.xdictionary_handle,
);
self.write_table_content(value);
self.register_object(value.common.handle);
}
fn write_table_style(&mut self, value: &TableStyle) {
let Some(type_code) = self
.document
.classes
.get_by_name("TABLESTYLE")
.map(|class| class.class_number)
else {
return;
};
self.write_common_non_entity_data(type_code, value.handle, value.owner_handle, &[], &None);
if !self.version.r2010_plus() {
self.writer.write_variable_text(&value.name);
self.writer.write_bit_short(value.flow_direction as i16);
self.writer
.write_bit_short(value.effective_flags().bits() & !STALE_TABLE_STYLE_FLAG);
self.writer.write_bit_double(value.horizontal_margin);
self.writer.write_bit_double(value.vertical_margin);
self.writer.write_bit(!value.has_title_row());
self.writer.write_bit(!value.has_header_row());
self.write_legacy_table_row_style(&value.data_row_style);
self.write_legacy_table_row_style(&value.title_row_style);
self.write_legacy_table_row_style(&value.header_row_style);
} else {
self.writer.write_byte(value.modern_unknown_byte);
self.writer.write_variable_text(&value.name);
self.writer
.write_bit_long((value.effective_flags().bits() & !STALE_TABLE_STYLE_FLAG) as i32);
self.writer.write_bit_long(value.modern_unknown_long2);
self.writer.write_handle(
DwgReferenceType::HardOwnership,
value.modern_cell_style_handle.value(),
);
if let Some(style) = &value.modern_style {
if style.cell_style.content_format.text_style.is_null() {
self.write_named_table_cell_style(style);
} else {
self.write_table_style_named_cell_style(style);
}
} else {
self.write_default_modern_table_cell_style(value);
}
if value.modern_overrides.is_empty() {
self.write_default_modern_table_row_styles(value);
} else {
self.writer
.write_bit_long(value.modern_overrides.len().min(i32::MAX as usize) as i32);
for (key, style) in value.modern_overrides.iter().take(i32::MAX as usize) {
self.writer.write_bit_long(*key);
self.write_table_style_named_cell_style(style);
}
}
}
self.register_object(value.handle);
}
fn write_legacy_table_row_style(&mut self, value: &RowCellStyle) {
self.writer.write_handle(
DwgReferenceType::HardPointer,
value
.text_style_handle
.map(|handle| handle.value())
.unwrap_or(0),
);
self.writer.write_bit_double(value.text_height);
self.writer.write_bit_short(value.alignment as i16);
self.writer.write_cm_true_color(&value.text_color);
self.writer.write_cm_true_color(&value.fill_color);
self.writer.write_bit(value.fill_enabled);
for border in [
&value.top_border,
&value.horizontal_inside_border,
&value.bottom_border,
&value.left_border,
&value.vertical_inside_border,
&value.right_border,
] {
self.writer.write_bit_short(border.line_weight.as_i16());
self.writer.write_bit(!border.is_invisible);
self.writer.write_cm_true_color(&border.color);
}
if self.version == crate::io::dwg::DwgVersion::AC21 {
self.writer.write_bit_long(value.data_type);
self.writer.write_bit_long(value.unit_type);
self.writer.write_variable_text(&value.format_string);
}
}
fn write_object_table_content_format(&mut self, value: &TableContentFormat) {
self.writer.write_bit_long(value.property_override_flags);
self.writer.write_bit_long(value.property_flags);
self.writer.write_bit_long(value.value_data_type);
self.writer.write_bit_long(value.value_unit_type);
self.writer.write_variable_text(&value.value_format_string);
self.writer.write_bit_double(value.rotation);
self.writer.write_bit_double(value.block_scale);
self.writer.write_bit_long(value.cell_alignment);
self.writer.write_cm_true_color(&value.content_color);
self.writer
.write_handle(DwgReferenceType::HardPointer, value.text_style.value());
self.writer.write_bit_double(value.text_height);
}
pub(super) fn write_table_cell_style_data(&mut self, value: &TableCellStyleData) {
self.writer.write_bit_long(value.style_type);
self.writer.write_bit_short(value.data_flags);
if value.data_flags == 0 {
return;
}
self.writer.write_bit_long(value.property_override_flags);
self.writer.write_bit_long(value.merge_flags);
self.writer.write_cm_true_color(&value.background_color);
self.writer.write_bit_long(value.content_layout);
self.write_object_table_content_format(&value.content_format);
self.writer.write_bit_short(value.margin_override_flags);
if value.margin_override_flags != 0 {
self.writer.write_bit_double(value.vertical_margin);
self.writer.write_bit_double(value.horizontal_margin);
self.writer.write_bit_double(value.bottom_margin);
self.writer.write_bit_double(value.right_margin);
self.writer.write_bit_double(value.horizontal_spacing);
self.writer.write_bit_double(value.vertical_spacing);
}
self.writer
.write_bit_long(value.borders.len().min(6) as i32);
for grid in value.borders.iter().take(6) {
self.writer.write_bit_long(grid.index_mask);
if grid.index_mask == 0 {
continue;
}
self.writer
.write_bit_long(grid.border.property_flags.bits());
self.writer.write_bit_long(grid.border.border_type as i32);
self.writer.write_cm_true_color(&grid.border.color);
self.writer
.write_bit_long(grid.border.line_weight.as_i16() as i32);
self.writer
.write_handle(DwgReferenceType::HardPointer, grid.line_type.value());
self.writer
.write_bit_long((!grid.border.is_invisible) as i32);
self.writer
.write_bit_double(grid.border.double_line_spacing);
}
}
pub(super) fn write_named_table_cell_style(&mut self, value: &NamedTableCellStyle) {
self.write_table_cell_style_data(&value.cell_style);
self.writer.write_bit_long(value.id);
self.writer.write_bit_long(value.style_type);
self.writer.write_variable_text(&value.name);
}
fn write_table_style_named_cell_style(&mut self, value: &NamedTableCellStyle) {
let resolved = self.resolve_table_text_style(value.cell_style.content_format.text_style);
if resolved == value.cell_style.content_format.text_style {
self.write_named_table_cell_style(value);
return;
}
let mut value = value.clone();
value.cell_style.content_format.text_style = resolved;
self.write_named_table_cell_style(&value);
}
fn write_default_modern_table_cell_style(&mut self, value: &TableStyle) {
let cell_style = TableCellStyleData {
style_type: 5,
data_flags: 1,
background_color: Color::None,
content_layout: 1,
content_format: TableContentFormat {
value_data_type: 512,
block_scale: 1.0,
cell_alignment: 1,
content_color: Color::ByBlock,
text_height: 0.18,
..TableContentFormat::default()
},
margin_override_flags: 1,
vertical_margin: value.vertical_margin,
horizontal_margin: value.horizontal_margin,
bottom_margin: value.vertical_margin,
right_margin: value.horizontal_margin,
horizontal_spacing: 0.18,
vertical_spacing: 0.18,
..TableCellStyleData::default()
};
self.write_named_table_cell_style(&NamedTableCellStyle {
cell_style,
id: 4,
style_type: 2,
name: "Table".to_string(),
});
}
fn write_default_modern_table_row_styles(&mut self, value: &TableStyle) {
let rows = [
(1, &value.title_row_style, 1, 1, "_TITLE", 0x8000),
(2, &value.header_row_style, 2, 1, "_HEADER", 0),
(3, &value.data_row_style, 3, 2, "_DATA", 0),
];
self.writer.write_bit_long(rows.len() as i32);
for (key, row, id, style_type, name, merge_flags) in rows {
self.writer.write_bit_long(key);
self.write_default_modern_table_row_style(row, id, style_type, name, merge_flags);
}
}
fn write_default_modern_table_row_style(
&mut self,
row: &RowCellStyle,
id: i32,
style_type: i32,
name: &str,
merge_flags: i32,
) {
let borders = [
(1, &row.top_border),
(2, &row.horizontal_inside_border),
(4, &row.bottom_border),
(8, &row.left_border),
(16, &row.vertical_inside_border),
(32, &row.right_border),
]
.into_iter()
.map(|(index_mask, border)| TableGridFormat {
index_mask,
border: TableCellBorder {
is_invisible: !border.is_invisible,
..border.clone()
},
line_type: Handle::NULL,
})
.collect();
self.write_named_table_cell_style(&NamedTableCellStyle {
cell_style: TableCellStyleData {
style_type: 5,
data_flags: 1,
merge_flags,
background_color: if row.fill_enabled {
row.fill_color
} else {
Color::None
},
content_layout: 1,
content_format: TableContentFormat {
value_data_type: row.data_type,
value_unit_type: row.unit_type,
value_format_string: row.format_string.clone(),
block_scale: 1.0,
cell_alignment: row.alignment as i32,
content_color: row.text_color,
text_style: self.resolve_table_row_text_style(row),
text_height: row.text_height,
..TableContentFormat::default()
},
borders,
..TableCellStyleData::default()
},
id,
style_type,
name: name.to_string(),
});
}
fn resolve_table_row_text_style(&self, row: &RowCellStyle) -> Handle {
row.text_style_handle
.filter(|handle| self.is_text_style_handle(*handle))
.or_else(|| {
self.document
.text_styles
.get(&row.text_style_name)
.map(|style| style.handle)
})
.unwrap_or_else(|| self.resolve_table_text_style(Handle::NULL))
}
fn resolve_table_text_style(&self, handle: Handle) -> Handle {
if self.is_text_style_handle(handle) {
return handle;
}
self.document
.text_styles
.get("Standard")
.map(|style| style.handle)
.filter(|handle| !handle.is_null())
.or_else(|| {
self.document
.text_styles
.iter()
.map(|style| style.handle)
.find(|handle| !handle.is_null())
})
.unwrap_or(Handle::NULL)
}
fn is_text_style_handle(&self, handle: Handle) -> bool {
!handle.is_null()
&& self
.document
.text_styles
.iter()
.any(|style| style.handle == handle)
}
pub(super) fn raw_passthrough_compatible(&self, raw_dwg_version: Option<DxfVersion>) -> bool {
match raw_dwg_version {
None => true,
Some(src) => src == self.dxf_version,
}
}
pub(super) fn is_writable_object(&self, handle: &Handle) -> bool {
match self.document.objects.get(handle) {
None => match self.document.get_entity(*handle) {
Some(crate::entities::EntityType::Unknown(entity)) => entity
.raw_dwg_data
.as_ref()
.is_some_and(|_| self.raw_passthrough_compatible(entity.dwg_source_version)),
Some(_) => true,
None => false,
},
Some(obj) if self.lacks_object_class(obj) => false,
Some(obj) => match obj {
ObjectType::VisualStyle(_) => {
(self.version.r2007_plus()
|| self.document.dwg_source_version == Some(self.dxf_version))
&& self.document.classes.get_by_name("VISUALSTYLE").is_some()
}
ObjectType::Material(_) => {
(self.version.r2007_plus()
|| self.document.dwg_source_version == Some(self.dxf_version))
&& self.document.classes.get_by_name("MATERIAL").is_some()
}
ObjectType::TableStyle(_) => {
self.document.classes.get_by_name("TABLESTYLE").is_some()
}
ObjectType::Arch(value) => {
value.raw_dwg_data.is_some()
&& self.raw_passthrough_compatible(value.raw_dwg_version)
}
ObjectType::Unknown {
type_name,
raw_dwg_data,
raw_dwg_version,
..
} => {
if type_name.starts_with("DWG_OBJ_106") || type_name.starts_with("DWG_OBJ_105")
{
return false;
}
raw_dwg_data.is_some() && self.raw_passthrough_compatible(*raw_dwg_version)
}
_ => true,
},
}
}
fn write_dictionary(&mut self, dict: &Dictionary) {
let preserve_source_schema = self.version.r13_14_only()
&& self.document.dwg_source_version == Some(self.dxf_version);
let entries: Vec<&(String, Handle)> = if self.version.r2000_plus() || preserve_source_schema
{
dict.entries
.iter()
.filter(|(_, h)| !h.is_null() && self.is_writable_object(h))
.collect()
} else {
dict.entries
.iter()
.filter(|(name, h)| {
!matches!(
name.as_str(),
"ACAD_PLOTSTYLENAME"
| "ACAD_LAYOUT"
| "ACAD_PLOTSETTINGS"
| "ACAD_MATERIAL"
| "ACAD_COLOR"
| "ACAD_VISUALSTYLE"
) && !h.is_null()
&& self.is_writable_object(h)
})
.collect()
};
self.write_common_non_entity_data(
common::OBJ_DICTIONARY,
dict.handle,
dict.owner,
&dict.reactors,
&dict.xdictionary_handle,
);
self.writer.write_bit_long(entries.len() as i32);
if self.dxf_version == DxfVersion::AC1014 {
self.writer.write_byte(0);
}
if self.version.r2000_plus() {
self.writer.write_bit_short(dict.duplicate_cloning as i16);
self.writer.write_byte(if dict.hard_owner { 1 } else { 0 });
}
for (name, handle) in &entries {
self.writer.write_variable_text(name);
self.writer
.write_handle(DwgReferenceType::SoftOwnership, handle.value());
if !handle.is_null() {
self.object_queue.push_back(*handle);
}
}
self.register_object(dict.handle);
}
fn write_dictionary_with_default(&mut self, dict: &DictionaryWithDefault) {
let type_code = self.class_type_code("ACDBDICTIONARYWDFLT", common::OBJ_DICTIONARYWDFLT);
self.write_common_non_entity_data(type_code, dict.handle, dict.owner, &[], &None);
let entries: Vec<&(String, Handle)> = dict
.entries
.iter()
.filter(|(_, h)| h.is_null() || self.is_writable_object(h))
.collect();
self.writer.write_bit_long(entries.len() as i32);
self.writer.write_bit_short(dict.duplicate_cloning as i16);
self.writer.write_byte(if dict.hard_owner { 1 } else { 0 });
for (name, handle) in &entries {
self.writer.write_variable_text(name);
self.writer
.write_handle(DwgReferenceType::SoftOwnership, handle.value());
if !handle.is_null() {
self.object_queue.push_back(*handle);
}
}
self.writer
.write_handle(DwgReferenceType::HardPointer, dict.default_handle.value());
self.register_object(dict.handle);
}
fn write_dictionary_variable(&mut self, dv: &DictionaryVariable) {
let type_code = self.class_type_code("DICTIONARYVAR", common::OBJ_DICTIONARYVAR);
self.write_common_non_entity_data(type_code, dv.handle, dv.owner_handle, &[], &None);
self.writer.write_byte(0); self.writer.write_variable_text(&dv.value);
self.register_object(dv.handle);
}
fn write_layout(&mut self, layout: &Layout) {
let type_code = if self.version.r2004_pre() {
self.document
.classes
.get_by_name("LAYOUT")
.map(|c| c.class_number)
.unwrap_or(common::OBJ_LAYOUT)
} else {
common::OBJ_LAYOUT
};
self.write_common_non_entity_data(
type_code,
layout.handle,
layout.owner,
&layout.reactors,
&layout.xdictionary_handle,
);
let mut plot_flags = layout.plot_flags.to_bits();
if layout.name == "Model" {
plot_flags |= 0x400;
}
self.write_plot_settings_data(plot_flags, layout);
self.writer.write_variable_text(&layout.name);
self.writer.write_bit_short(layout.tab_order);
self.writer.write_bit_short(layout.flags);
self.writer.write_3bit_double(crate::types::Vector3::new(
layout.insertion_base.0,
layout.insertion_base.1,
layout.insertion_base.2,
));
self.writer.write_raw_double(layout.min_limits.0);
self.writer.write_raw_double(layout.min_limits.1);
self.writer.write_raw_double(layout.max_limits.0);
self.writer.write_raw_double(layout.max_limits.1);
self.writer.write_3bit_double(crate::types::Vector3::new(
layout.ucs_origin.0,
layout.ucs_origin.1,
layout.ucs_origin.2,
));
self.writer.write_3bit_double(crate::types::Vector3::new(
layout.ucs_x_axis.0,
layout.ucs_x_axis.1,
layout.ucs_x_axis.2,
));
self.writer.write_3bit_double(crate::types::Vector3::new(
layout.ucs_y_axis.0,
layout.ucs_y_axis.1,
layout.ucs_y_axis.2,
));
self.writer.write_bit_double(layout.elevation);
self.writer.write_bit_short(layout.ucs_ortho_type);
self.writer.write_3bit_double(crate::types::Vector3::new(
layout.min_extents.0,
layout.min_extents.1,
layout.min_extents.2,
));
self.writer.write_3bit_double(crate::types::Vector3::new(
layout.max_extents.0,
layout.max_extents.1,
layout.max_extents.2,
));
if self.version.r2004_plus() {
self.writer.write_bit_long(layout.viewports.len() as i32);
}
self.writer
.write_handle(DwgReferenceType::SoftPointer, layout.block_record.value());
self.writer
.write_handle(DwgReferenceType::SoftPointer, layout.viewport.value());
self.writer
.write_handle(DwgReferenceType::HardPointer, layout.base_ucs.value());
self.writer
.write_handle(DwgReferenceType::HardPointer, layout.named_ucs.value());
if self.version.r2004_plus() {
for viewport in &layout.viewports {
self.writer
.write_handle(DwgReferenceType::SoftPointer, viewport.value());
}
}
self.register_object(layout.handle);
}
fn write_plot_settings_data(&mut self, plot_flags: i32, layout: &crate::objects::Layout) {
self.writer.write_variable_text(&layout.plot_page_name);
self.writer.write_variable_text(&layout.plot_printer_name);
self.writer.write_bit_short(plot_flags as i16);
self.writer.write_bit_double(layout.plot_margin_left);
self.writer.write_bit_double(layout.plot_margin_bottom);
self.writer.write_bit_double(layout.plot_margin_right);
self.writer.write_bit_double(layout.plot_margin_top);
self.writer.write_bit_double(layout.paper_width);
self.writer.write_bit_double(layout.paper_height);
self.writer.write_variable_text(&layout.paper_size);
self.writer.write_bit_double(layout.plot_origin_x);
self.writer.write_bit_double(layout.plot_origin_y);
self.writer.write_bit_short(layout.plot_paper_units);
self.writer.write_bit_short(layout.plot_rotation);
self.writer.write_bit_short(layout.plot_type);
self.writer.write_bit_double(layout.plot_window_min_x);
self.writer.write_bit_double(layout.plot_window_min_y);
self.writer.write_bit_double(layout.plot_window_max_x);
self.writer.write_bit_double(layout.plot_window_max_y);
if self.version.r13_15_only() {
self.writer.write_variable_text(&layout.plot_view_name);
}
self.writer.write_bit_double(layout.plot_scale_numerator);
self.writer.write_bit_double(layout.plot_scale_denominator);
self.writer.write_variable_text(&layout.plot_style_sheet);
self.writer.write_bit_short(layout.plot_scale_type);
self.writer.write_bit_double(layout.plot_scale_factor);
self.writer.write_bit_double(layout.paper_image_origin_x);
self.writer.write_bit_double(layout.paper_image_origin_y);
if self.version.r2004_plus() {
self.writer.write_bit_short(layout.shade_plot_mode);
self.writer.write_bit_short(layout.shade_plot_resolution);
self.writer.write_bit_short(layout.shade_plot_dpi);
let plot_view_handle = if !layout.plot_view_handle.is_null() {
layout.plot_view_handle
} else {
self.document
.views
.get(&layout.plot_view_name)
.map(|view| view.handle)
.unwrap_or(Handle::NULL)
};
self.writer
.write_handle(DwgReferenceType::HardPointer, plot_view_handle.value());
}
if self.version.r2007_plus() {
self.writer.write_handle(
DwgReferenceType::SoftPointer,
layout.visual_style_handle.value(),
);
}
}
fn write_plot_settings_obj(&mut self, ps: &PlotSettings) {
let type_code = self.class_type_code("PLOTSETTINGS", common::OBJ_PLOTSETTINGS);
self.write_common_non_entity_data(
type_code,
ps.handle,
ps.owner,
&ps.reactors,
&ps.xdictionary_handle,
);
self.writer.write_variable_text(&ps.page_name);
self.writer.write_variable_text(&ps.printer_name);
self.writer.write_bit_short(ps.flags.to_bits() as i16);
self.writer.write_bit_double(ps.margins.left);
self.writer.write_bit_double(ps.margins.bottom);
self.writer.write_bit_double(ps.margins.right);
self.writer.write_bit_double(ps.margins.top);
self.writer.write_bit_double(ps.paper_width);
self.writer.write_bit_double(ps.paper_height);
self.writer.write_variable_text(&ps.paper_size);
self.writer.write_bit_double(ps.origin_x);
self.writer.write_bit_double(ps.origin_y);
self.writer.write_bit_short(ps.paper_units as i16);
self.writer.write_bit_short(ps.rotation as i16);
self.writer.write_bit_short(ps.plot_type as i16);
self.writer.write_bit_double(ps.plot_window.lower_left_x);
self.writer.write_bit_double(ps.plot_window.lower_left_y);
self.writer.write_bit_double(ps.plot_window.upper_right_x);
self.writer.write_bit_double(ps.plot_window.upper_right_y);
if self.version.r13_15_only() {
self.writer.write_variable_text(&ps.plot_view_name);
}
self.writer.write_bit_double(ps.scale_numerator);
self.writer.write_bit_double(ps.scale_denominator);
self.writer.write_variable_text(&ps.current_style_sheet);
self.writer.write_bit_short(ps.scale_type as i16);
self.writer.write_bit_double(ps.standard_scale_factor);
self.writer.write_bit_double(ps.paper_image_origin_x);
self.writer.write_bit_double(ps.paper_image_origin_y);
if self.version.r2004_plus() {
self.writer.write_bit_short(ps.shade_plot_mode as i16);
self.writer.write_bit_short(ps.shade_plot_resolution as i16);
self.writer.write_bit_short(ps.shade_plot_dpi);
let plot_view_handle = if !ps.plot_view_handle.is_null() {
ps.plot_view_handle
} else {
self.document
.views
.get(&ps.plot_view_name)
.map(|view| view.handle)
.unwrap_or(Handle::NULL)
};
self.writer
.write_handle(DwgReferenceType::HardPointer, plot_view_handle.value());
}
if self.version.r2007_plus() {
self.writer.write_handle(
DwgReferenceType::SoftPointer,
ps.visual_style_handle.value(),
);
}
self.register_object(ps.handle);
}
fn write_group(&mut self, group: &Group) {
self.write_common_non_entity_data(common::OBJ_GROUP, group.handle, group.owner, &[], &None);
self.writer.write_variable_text(&group.description);
self.writer
.write_bit_short(if group.unnamed { 1 } else { 0 });
self.writer
.write_bit_short(if group.selectable { 1 } else { 0 });
self.writer.write_bit_long(group.entities.len() as i32);
for h in &group.entities {
self.writer
.write_handle(DwgReferenceType::HardPointer, h.value());
}
self.register_object(group.handle);
}
fn write_mlinestyle(&mut self, style: &MLineStyle) {
self.write_common_non_entity_data(
common::OBJ_MLINESTYLE,
style.handle,
style.owner,
&[],
&None,
);
self.writer.write_variable_text(&style.name);
self.writer.write_variable_text(&style.description);
let mut flags: i16 = 0;
if style.flags.display_joints {
flags |= 1;
}
if style.flags.fill_on {
flags |= 2;
}
if style.flags.start_square_cap {
flags |= 16;
}
if style.flags.start_round_cap {
flags |= 0x20;
}
if style.flags.start_inner_arcs_cap {
flags |= 0x40;
}
if style.flags.end_square_cap {
flags |= 0x100;
}
if style.flags.end_round_cap {
flags |= 0x200;
}
if style.flags.end_inner_arcs_cap {
flags |= 0x400;
}
self.writer.write_bit_short(flags);
self.writer.write_cm_color(&style.fill_color);
self.writer.write_bit_double(style.start_angle);
self.writer.write_bit_double(style.end_angle);
self.writer.write_byte(style.elements.len() as u8);
for elem in &style.elements {
self.writer.write_bit_double(elem.offset);
self.writer.write_cm_color(&elem.color);
if self.version.r2018_plus(self.dxf_version) {
let lt_handle = self
.document
.line_types
.get(&elem.linetype)
.map(|lt| lt.handle)
.unwrap_or(Handle::NULL);
self.writer
.write_handle(DwgReferenceType::HardPointer, lt_handle.value());
} else {
let idx: i16 =
if elem.linetype.is_empty() || elem.linetype.eq_ignore_ascii_case("ByLayer") {
0x7FFF
} else {
self.document
.line_types
.iter()
.filter(|lt| {
!lt.name.eq_ignore_ascii_case("ByBlock")
&& !lt.name.eq_ignore_ascii_case("ByLayer")
})
.position(|lt| lt.name.eq_ignore_ascii_case(&elem.linetype))
.map(|p| p as i16)
.unwrap_or(0x7FFF)
};
self.writer.write_bit_short(idx);
}
}
self.register_object(style.handle);
}
fn write_multileader_style(&mut self, style: &MultiLeaderStyle) {
let type_code = self.class_type_code("MLEADERSTYLE", common::OBJ_MLEADERSTYLE);
self.write_common_non_entity_data(type_code, style.handle, style.owner_handle, &[], &None);
if self.version.r2010_plus() {
self.writer.write_bit_short(2);
}
self.writer.write_bit_short(style.content_type as i16);
self.writer
.write_bit_short(style.multileader_draw_order as i16);
self.writer.write_bit_short(style.leader_draw_order as i16);
self.writer.write_bit_long(style.max_leader_points);
self.writer.write_bit_double(style.first_segment_angle);
self.writer.write_bit_double(style.second_segment_angle);
self.writer.write_bit_short(style.path_type as i16);
self.writer.write_cm_color(&style.line_color);
let lt = style.line_type_handle.unwrap_or(Handle::NULL);
self.writer
.write_handle(DwgReferenceType::HardPointer, lt.value());
self.writer
.write_bit_long(style.line_weight.as_i16() as i32);
self.writer.write_bit(style.enable_landing);
self.writer.write_bit_double(style.landing_gap);
self.writer.write_bit(style.enable_dogleg);
self.writer.write_bit_double(style.landing_distance);
self.writer.write_variable_text(&style.description);
let ah = style.arrowhead_handle.unwrap_or(Handle::NULL);
self.writer
.write_handle(DwgReferenceType::HardPointer, ah.value());
self.writer.write_bit_double(style.arrowhead_size);
self.writer.write_variable_text(&style.default_text);
let ts = style.text_style_handle.unwrap_or(Handle::NULL);
self.writer
.write_handle(DwgReferenceType::HardPointer, ts.value());
self.writer
.write_bit_short(style.text_left_attachment as i16);
self.writer
.write_bit_short(style.text_right_attachment as i16);
self.writer.write_bit_short(style.text_angle_type as i16);
self.writer.write_bit_short(style.text_alignment as i16);
self.writer.write_cm_color(&style.text_color);
self.writer.write_bit_double(style.text_height);
self.writer.write_bit(style.text_frame);
self.writer.write_bit(style.text_always_left);
self.writer.write_bit_double(style.align_space);
let bc = style.block_content_handle.unwrap_or(Handle::NULL);
self.writer
.write_handle(DwgReferenceType::HardPointer, bc.value());
self.writer.write_cm_color(&style.block_content_color);
self.writer.write_bit_double(style.block_content_scale_x);
self.writer.write_bit_double(style.block_content_scale_y);
self.writer.write_bit_double(style.block_content_scale_z);
self.writer.write_bit(style.enable_block_scale);
self.writer.write_bit_double(style.block_content_rotation);
self.writer.write_bit(style.enable_block_rotation);
self.writer
.write_bit_short(style.block_content_connection as i16);
self.writer.write_bit_double(style.scale_factor);
self.writer.write_bit(style.property_changed);
self.writer.write_bit(style.is_annotative);
self.writer.write_bit_double(style.break_gap_size);
if self.version.r2010_plus() {
self.writer
.write_bit_short(style.text_attachment_direction as i16);
self.writer
.write_bit_short(style.text_top_attachment as i16);
self.writer
.write_bit_short(style.text_bottom_attachment as i16);
}
if self.version.r2013_plus(self.dxf_version) {
self.writer.write_bit(style.unknown_flag_298);
}
self.register_object(style.handle);
}
fn write_image_definition(&mut self, def: &ImageDefinition) {
let type_code = self.class_type_code("IMAGEDEF", common::OBJ_IMAGEDEF);
self.write_common_non_entity_data(type_code, def.handle, def.owner, &[], &None);
self.writer.write_bit_long(def.class_version);
self.writer.write_2raw_double(crate::types::Vector2::new(
def.size_in_pixels.0 as f64,
def.size_in_pixels.1 as f64,
));
self.writer.write_variable_text(&def.file_name);
self.writer.write_bit(def.is_loaded);
self.writer.write_byte(def.resolution_unit as u8);
self.writer.write_2raw_double(crate::types::Vector2::new(
def.pixel_size.0,
def.pixel_size.1,
));
self.register_object(def.handle);
}
fn write_underlay_definition(&mut self, def: &UnderlayDefinition) {
use crate::entities::underlay::UnderlayType;
let fallback = match def.underlay_type {
UnderlayType::Dwf => common::OBJ_DWFDEFINITION,
UnderlayType::Dgn => common::OBJ_DGNDEFINITION,
UnderlayType::Pdf => common::OBJ_PDFDEFINITION,
};
let type_code = self.class_type_code(def.entity_name(), fallback);
let mut extra = Vec::new();
if def.unloaded {
if let Some(app) = self.document.app_ids.get("ACAD") {
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_code_page(
&self.document.header.code_page,
)
.unwrap_or(encoding_rs::WINDOWS_1252.into());
let bytes = crate::io::dwg::eed_codec::encode_values_with_encoding(
self.version.r2007_plus(),
&[crate::xdata::XDataValue::String("NOLOAD".to_string())],
encoding,
code_page,
|_| 0,
);
extra.push((app.handle.value(), bytes));
}
}
self.write_common_non_entity_data_eed(
type_code,
def.handle,
def.owner_handle,
&def.reactors,
&None,
extra,
);
self.writer.write_variable_text(&def.file_path);
self.writer.write_variable_text(&def.page_name);
self.register_object(def.handle);
}
fn write_image_definition_reactor(&mut self, reactor: &ImageDefinitionReactor) {
let type_code = self.class_type_code("IMAGEDEF_REACTOR", common::OBJ_IMAGEDEFREACTOR);
self.write_common_non_entity_data(type_code, reactor.handle, reactor.owner, &[], &None);
self.writer.write_bit_long(0);
self.register_object(reactor.handle);
}
fn write_scale(&mut self, scale: &Scale) {
let type_code = self.class_type_code("SCALE", common::OBJ_SCALE);
self.write_common_non_entity_data(type_code, scale.handle, scale.owner_handle, &[], &None);
self.writer.write_bit_short(0); self.writer.write_variable_text(&scale.name);
self.writer.write_bit_double(scale.paper_units);
self.writer.write_bit_double(scale.drawing_units);
self.writer.write_bit(scale.is_unit_scale);
self.register_object(scale.handle);
}
fn write_object_context_data(&mut self, obj: &ObjectContextData) {
let type_code = self.class_type_code(obj.class_name(), 0);
self.write_common_non_entity_data(
type_code,
obj.handle,
obj.owner_handle,
&obj.reactors,
&obj.xdictionary_handle,
);
self.writer.write_bit_short(obj.class_version);
let legacy_mleader_base = obj.uses_legacy_mleader_context_base
&& matches!(obj.kind, ObjectContextKind::MLeader(_));
if self.version.r2010_plus() && !legacy_mleader_base {
self.writer.write_bit(obj.has_file_to_extension_dictionary);
}
self.writer.write_bit(obj.is_default);
self.writer
.write_handle(DwgReferenceType::HardPointer, obj.scale.value());
match &obj.kind {
ObjectContextKind::AnnotScale => {}
ObjectContextKind::MLeader(context) => {
self.write_multileader_annotation_context(context, false);
}
ObjectContextKind::BlkRef {
rotation,
insertion,
scale_factor,
} => {
self.writer.write_bit_double(*rotation);
self.writer.write_bit_double(insertion.x);
self.writer.write_bit_double(insertion.y);
self.writer.write_bit_double(insertion.z);
self.writer.write_bit_double(scale_factor.x);
self.writer.write_bit_double(scale_factor.y);
self.writer.write_bit_double(scale_factor.z);
}
ObjectContextKind::Text {
horizontal_mode,
rotation,
insertion,
alignment,
} => {
self.writer.write_bit_short(*horizontal_mode);
self.writer.write_bit_double(*rotation);
self.writer.write_raw_double(insertion.x);
self.writer.write_raw_double(insertion.y);
self.writer.write_raw_double(alignment.x);
self.writer.write_raw_double(alignment.y);
}
ObjectContextKind::MText(m) => {
self.writer.write_bit_long(m.attachment);
self.writer.write_bit_double(m.x_axis_dir.x);
self.writer.write_bit_double(m.x_axis_dir.y);
self.writer.write_bit_double(m.x_axis_dir.z);
self.writer.write_bit_double(m.insertion.x);
self.writer.write_bit_double(m.insertion.y);
self.writer.write_bit_double(m.insertion.z);
self.writer.write_bit_double(m.rect_width);
self.writer.write_bit_double(m.rect_height);
self.writer.write_bit_double(m.extents_width);
self.writer.write_bit_double(m.extents_height);
self.writer.write_bit_long(m.column_type);
if m.column_type != 0 {
if let Some(c) = &m.columns {
self.writer.write_bit_long(c.num_heights);
self.writer.write_bit_double(c.width);
self.writer.write_bit_double(c.gutter);
self.writer.write_bit(c.auto_height);
self.writer.write_bit(c.flow_reversed);
if !c.auto_height && m.column_type == 2 {
for h in &c.heights {
self.writer.write_bit_double(*h);
}
}
}
}
}
ObjectContextKind::Dim(d) => {
self.writer.write_raw_double(d.def_pt.x);
self.writer.write_raw_double(d.def_pt.y);
self.writer.write_bit(d.is_def_textloc);
self.writer.write_bit_double(d.text_rotation);
self.writer.write_bit(d.b293);
self.writer.write_bit(d.dimtofl);
self.writer.write_bit(d.dimosxd);
self.writer.write_bit(d.dimatfit);
self.writer.write_bit(d.dimtix);
self.writer.write_bit(d.dimtmove);
self.writer.write_byte(d.override_code);
self.writer.write_bit(d.has_arrow2);
self.writer.write_bit(d.flip_arrow2);
self.writer.write_bit(d.flip_arrow1);
let mut pt = |p: &crate::types::Vector3| {
self.writer.write_bit_double(p.x);
self.writer.write_bit_double(p.y);
self.writer.write_bit_double(p.z);
};
match &d.subtype {
DimSubtype::Aligned { dimline_pt } => pt(dimline_pt),
DimSubtype::Angular { arc_pt } => pt(arc_pt),
DimSubtype::Diametric {
first_arc_pt,
def_pt,
} => {
pt(first_arc_pt);
pt(def_pt);
}
DimSubtype::Radial { first_arc_pt } => pt(first_arc_pt),
DimSubtype::RadialLarge {
ovr_center,
jog_point,
} => {
pt(ovr_center);
pt(jog_point);
}
DimSubtype::Ordinate {
feature_location_pt,
leader_endpt,
} => {
pt(feature_location_pt);
pt(leader_endpt);
}
}
}
ObjectContextKind::MTextAttribute(value) => {
self.writer.write_bit_short(value.horizontal_mode);
self.writer.write_bit_double(value.rotation);
self.writer.write_raw_double(value.insertion.x);
self.writer.write_raw_double(value.insertion.y);
let has_context = value.enable_context && value.context.is_some();
self.writer.write_raw_double(value.alignment.x);
self.writer.write_raw_double(value.alignment.y);
self.writer.write_bit(has_context);
if let Some(context) = value.context.as_ref().filter(|_| has_context) {
self.writer.write_bit_long(context.reactors.len() as i32);
self.writer.write_bit(context.xdictionary_handle.is_none());
if self.version.r2013_plus(self.dxf_version) {
self.writer.write_bit(context.has_binary_data);
}
self.writer.write_bit_short(context.class_version);
if self.version.r2010_plus() {
self.writer
.write_bit(context.has_file_to_extension_dictionary);
}
self.writer.write_bit(context.is_default);
self.writer.write_bit_long(context.mtext.attachment);
self.writer.write_bit_double(context.mtext.x_axis_dir.x);
self.writer.write_bit_double(context.mtext.x_axis_dir.y);
self.writer.write_bit_double(context.mtext.x_axis_dir.z);
self.writer.write_bit_double(context.mtext.insertion.x);
self.writer.write_bit_double(context.mtext.insertion.y);
self.writer.write_bit_double(context.mtext.insertion.z);
self.writer.write_bit_double(context.mtext.rect_width);
self.writer.write_bit_double(context.mtext.rect_height);
self.writer.write_bit_double(context.mtext.extents_width);
self.writer.write_bit_double(context.mtext.extents_height);
self.writer.write_bit_long(context.mtext.column_type);
if context.mtext.column_type != 0 {
if let Some(columns) = &context.mtext.columns {
self.writer.write_bit_long(columns.num_heights);
self.writer.write_bit_double(columns.width);
self.writer.write_bit_double(columns.gutter);
self.writer.write_bit(columns.auto_height);
self.writer.write_bit(columns.flow_reversed);
if !columns.auto_height && context.mtext.column_type == 2 {
for height in &columns.heights {
self.writer.write_bit_double(*height);
}
}
}
}
}
}
ObjectContextKind::Leader(value) => {
self.writer.write_bit_long(value.points.len() as i32);
for point in &value.points {
self.writer.write_3bit_double(*point);
}
self.writer.write_3bit_double(value.x_direction);
self.writer.write_bit(value.annotation_enabled);
self.writer.write_3bit_double(value.insertion_offset);
self.writer.write_3bit_double(value.endpoint_projection);
}
ObjectContextKind::Fcf {
location,
horizontal_direction,
} => {
self.writer.write_bit_double(location.x);
self.writer.write_bit_double(location.y);
self.writer.write_bit_double(location.z);
self.writer.write_bit_double(horizontal_direction.x);
self.writer.write_bit_double(horizontal_direction.y);
self.writer.write_bit_double(horizontal_direction.z);
}
ObjectContextKind::HatchScale(value) => {
self.write_hatch_scale_context_data(value);
}
ObjectContextKind::HatchView(value) => {
self.write_hatch_scale_context_data(&value.hatch);
self.writer.write_bit_double(value.view_normal.x);
self.writer.write_bit_double(value.view_normal.y);
self.writer.write_bit_double(value.view_normal.z);
self.writer.write_bit_double(value.view_rotation);
self.writer.write_bit(value.evaluate_hatch);
}
ObjectContextKind::Opaque => {}
}
if let ObjectContextKind::Dim(d) = &obj.kind {
self.writer
.write_handle(DwgReferenceType::HardPointer, d.block.value());
}
if let ObjectContextKind::HatchView(value) = &obj.kind {
self.writer
.write_handle(DwgReferenceType::SoftOwnership, value.view.value());
}
if let ObjectContextKind::MTextAttribute(value) = &obj.kind {
if value.enable_context {
if let Some(context) = &value.context {
self.writer
.write_handle(DwgReferenceType::SoftPointer, context.owner_handle.value());
for reactor in &context.reactors {
self.writer
.write_handle(DwgReferenceType::SoftPointer, reactor.value());
}
if let Some(xdictionary_handle) = context.xdictionary_handle {
self.writer.write_handle(
DwgReferenceType::HardOwnership,
xdictionary_handle.value(),
);
}
self.writer
.write_handle(DwgReferenceType::HardPointer, context.scale.value());
}
}
}
self.register_object(obj.handle);
}
fn write_hatch_scale_context_data(&mut self, value: &crate::objects::HatchScaleContext) {
self.writer
.write_bit_short(value.pattern_lines.len() as i16);
for line in &value.pattern_lines {
self.writer.write_bit_double(line.angle);
self.writer.write_bit_double(line.base_point.x);
self.writer.write_bit_double(line.base_point.y);
self.writer.write_bit_double(line.offset.x);
self.writer.write_bit_double(line.offset.y);
self.writer.write_bit_short(line.dash_lengths.len() as i16);
for dash in &line.dash_lengths {
self.writer.write_bit_double(*dash);
}
}
self.writer.write_bit_double(value.pattern_scale);
self.writer.write_bit_double(value.pattern_base.x);
self.writer.write_bit_double(value.pattern_base.y);
self.writer.write_bit_double(value.pattern_base.z);
self.writer.write_bit_long(value.loops.len() as i32);
for loop_data in &value.loops {
self.writer.write_bit_long(loop_data.loop_type);
self.writer.write_bit(loop_data.supports_context);
if !loop_data.supports_context {
if let Some(boundary) = &loop_data.boundary {
self.write_hatch_boundary_path_contents(boundary, loop_data.loop_type, false);
} else if loop_data.loop_type & 2 != 0 {
self.writer.write_bit(false);
self.writer.write_bit(false);
self.writer.write_bit_long(0);
} else {
self.writer.write_bit_long(0);
}
}
}
}
fn write_sort_entities_table(&mut self, table: &SortEntitiesTable) {
let type_code = self.class_type_code("SORTENTSTABLE", common::OBJ_SORTENTSTABLE);
self.write_common_non_entity_data(type_code, table.handle, table.owner_handle, &[], &None);
let entries: Vec<_> = table.entries().collect();
self.writer.write_bit_long(entries.len() as i32);
for entry in &entries {
self.writer
.write_main_handle(DwgReferenceType::Undefined, entry.sort_handle.value());
}
self.writer.write_handle(
DwgReferenceType::SoftPointer,
table.block_owner_handle.value(),
);
for entry in &entries {
self.writer
.write_handle(DwgReferenceType::SoftPointer, entry.entity_handle.value());
}
self.register_object(table.handle);
}
fn write_xrecord(&mut self, xrec: &XRecord) {
let type_code = if self.dxf_version <= DxfVersion::AC1014 {
self.class_type_code("XRECORD", common::OBJ_XRECORD)
} else {
common::OBJ_XRECORD
};
self.write_common_non_entity_data(
type_code,
xrec.handle,
xrec.owner,
&xrec.reactors,
&xrec.xdictionary_handle,
);
let advanced_material_entries = match self.document.objects.get(&xrec.owner) {
Some(ObjectType::Dictionary(dictionary))
if dictionary.entries.iter().any(|(name, handle)| {
name.eq_ignore_ascii_case("ADVMATERIAL") && *handle == xrec.handle
}) =>
{
self.document
.objects
.values()
.find_map(|object| match object {
ObjectType::Material(material)
if material.xdictionary_handle == Some(xrec.owner)
&& material.has_advanced_data() =>
{
let values = vec![
XRecordEntry::double(460, material.color_bleed_scale * 100.0),
XRecordEntry::double(461, material.indirect_bump_scale * 100.0),
XRecordEntry::double(462, material.reflectance_scale * 100.0),
XRecordEntry::double(463, material.transmittance_scale * 100.0),
XRecordEntry::bool(290, material.two_sided_material),
XRecordEntry::int16(270, material.luminance_mode),
XRecordEntry::double(464, material.luminance),
XRecordEntry::bool(293, material.is_anonymous),
XRecordEntry::int16(272, material.global_illumination),
XRecordEntry::int16(273, material.final_gather),
];
let mut merged = xrec.entries.clone();
for value in values {
if let Some(existing) =
merged.iter_mut().find(|entry| entry.code == value.code)
{
existing.value = value.value;
} else {
merged.push(value);
}
}
Some(merged)
}
_ => None,
})
}
_ => None,
};
let xrecord_entries = advanced_material_entries
.as_deref()
.unwrap_or(&xrec.entries);
let code_page =
crate::io::dxf::code_page::dwg_code_page_index(&self.document.header.code_page)
.min(u8::MAX as u16) as u8;
let encoding = crate::io::dxf::code_page::encoding_from_dwg_code_page(code_page as u16);
let xdata = if !xrec.entries_complete && !xrec.raw_data.is_empty() {
let tgt_unicode = self.dxf_version >= DxfVersion::AC1021;
match self.document.dwg_source_version {
Some(src) if (src >= DxfVersion::AC1021) != tgt_unicode => transcode_xrecord_xdata(
&xrec.raw_data,
src >= DxfVersion::AC1021,
tgt_unicode,
encoding,
code_page,
),
_ => xrec.raw_data.clone(),
}
} else if !xrecord_entries.is_empty() {
encode_xrecord_entries(
xrecord_entries,
self.dxf_version >= DxfVersion::AC1021,
encoding,
code_page,
)
} else if xrec.raw_data.is_empty() {
Vec::new()
} else {
let tgt_unicode = self.dxf_version >= DxfVersion::AC1021;
match self.document.dwg_source_version {
Some(src) if (src >= DxfVersion::AC1021) != tgt_unicode => transcode_xrecord_xdata(
&xrec.raw_data,
src >= DxfVersion::AC1021,
tgt_unicode,
encoding,
code_page,
),
_ => xrec.raw_data.clone(),
}
};
if !xdata.is_empty() {
self.writer.write_bit_long(xdata.len() as i32);
for &b in &xdata {
self.writer.write_byte(b);
}
} else {
self.writer.write_bit_long(0);
}
if self.dxf_version >= DxfVersion::AC1015 {
self.writer.write_bit_short(xrec.cloning_flags.to_value());
}
if xrec.preserve_object_reference_stream {
for reference in &xrec.object_references {
self.writer.write_handle(
xrecord_reference_type(reference.kind),
reference.handle.value(),
);
if self.document.objects.contains_key(&reference.handle) {
self.object_queue.push_back(reference.handle);
}
}
} else {
let object_id_entries: Vec<_> = xrecord_entries
.iter()
.filter(|entry| (330..=369).contains(&entry.code))
.collect();
for (index, entry) in object_id_entries.iter().enumerate() {
let decoded = xrec.object_references.get(index);
let handle = entry
.value
.as_handle()
.or_else(|| decoded.map(|reference| reference.handle))
.unwrap_or(Handle::NULL);
let reference_type = decoded
.map(|reference| xrecord_reference_type(reference.kind))
.unwrap_or_else(|| match entry.code {
330..=339 => DwgReferenceType::SoftPointer,
340..=349 => DwgReferenceType::HardPointer,
350..=359 => DwgReferenceType::SoftOwnership,
360..=369 => DwgReferenceType::HardOwnership,
_ => DwgReferenceType::SoftPointer,
});
self.writer.write_handle(reference_type, handle.value());
if self.document.objects.contains_key(&handle) {
self.object_queue.push_back(handle);
}
}
for reference in xrec.object_references.iter().skip(object_id_entries.len()) {
self.writer.write_handle(
xrecord_reference_type(reference.kind),
reference.handle.value(),
);
if self.document.objects.contains_key(&reference.handle) {
self.object_queue.push_back(reference.handle);
}
}
}
self.register_object(xrec.handle);
}
fn write_raster_variables(&mut self, rv: &RasterVariables) {
let type_code = self.class_type_code("RASTERVARIABLES", common::OBJ_RASTERVARIABLES);
self.write_common_non_entity_data(type_code, rv.handle, rv.owner, &[], &None);
self.writer.write_bit_long(rv.class_version);
self.writer.write_bit_short(rv.display_image_frame);
self.writer.write_bit_short(rv.image_quality);
self.writer.write_bit_short(rv.units);
self.register_object(rv.handle);
}
fn write_spatial_filter(&mut self, sf: &SpatialFilter) {
let type_code = self.class_type_code("SPATIAL_FILTER", common::OBJ_SPATIALFILTER);
self.write_common_non_entity_data(type_code, sf.handle, sf.owner, &[], &None);
self.writer.write_bit_short(sf.boundary_points.len() as i16);
for p in &sf.boundary_points {
self.writer.write_2raw_double(*p);
}
self.writer.write_3bit_double(sf.normal);
self.writer.write_3bit_double(sf.origin);
self.writer.write_bit_short(sf.display_enabled as i16);
self.writer.write_bit_short(sf.front_clip.is_some() as i16);
if let Some(d) = sf.front_clip {
self.writer.write_bit_double(d);
}
self.writer.write_bit_short(sf.back_clip.is_some() as i16);
if let Some(d) = sf.back_clip {
self.writer.write_bit_double(d);
}
for v in matrix_to_dxf_4x3(&sf.inverse_block_transform) {
self.writer.write_bit_double(v);
}
for v in matrix_to_dxf_4x3(&sf.clip_bound_transform) {
self.writer.write_bit_double(v);
}
self.register_object(sf.handle);
}
fn write_geodata(&mut self, geo: &GeoData) {
let type_code = self.class_type_code("GEODATA", common::OBJ_GEODATA);
self.write_common_non_entity_data(
type_code,
geo.handle,
geo.owner,
&geo.reactors,
&geo.xdictionary_handle,
);
self.writer.write_bit_long(geo.version);
self.writer
.write_handle(DwgReferenceType::SoftPointer, geo.host_block.value());
self.writer.write_bit_short(geo.coordinate_type);
if geo.version == 1 {
self.writer.write_3bit_double(geo.reference_point);
self.writer.write_bit_long(geo.horizontal_units);
self.writer.write_3bit_double(geo.design_point);
self.writer
.write_3bit_double(geo.obsolete_observation_point);
self.writer.write_3bit_double(geo.up_direction);
let north_angle =
std::f64::consts::FRAC_PI_2 - geo.north_direction.y.atan2(geo.north_direction.x);
self.writer.write_bit_double(north_angle);
self.writer.write_3bit_double(geo.obsolete_scale_vector);
self.writer
.write_variable_text(&geo.coordinate_system_definition);
self.writer.write_variable_text(&geo.geo_rss_tag);
self.writer.write_bit_double(geo.horizontal_unit_scale);
self.writer
.write_variable_text(&geo.coordinate_system_datum);
self.writer.write_variable_text(&geo.coordinate_system_wkt);
} else {
self.writer.write_3bit_double(geo.design_point);
self.writer.write_3bit_double(geo.reference_point);
self.writer.write_bit_double(geo.horizontal_unit_scale);
self.writer.write_bit_long(geo.horizontal_units);
self.writer.write_bit_double(geo.vertical_unit_scale);
self.writer.write_bit_long(geo.vertical_units);
self.writer.write_3bit_double(geo.up_direction);
self.writer.write_2raw_double(geo.north_direction);
self.writer.write_bit_long(geo.scale_estimation_method);
self.writer.write_bit_double(geo.user_scale_factor);
self.writer.write_bit(geo.sea_level_correction);
self.writer.write_bit_double(geo.sea_level_elevation);
self.writer
.write_bit_double(geo.coordinate_projection_radius);
self.writer
.write_variable_text(&geo.coordinate_system_definition);
self.writer.write_variable_text(&geo.geo_rss_tag);
}
self.writer.write_variable_text(&geo.observation_from_tag);
self.writer.write_variable_text(&geo.observation_to_tag);
self.writer
.write_variable_text(&geo.observation_coverage_tag);
self.writer.write_bit_long(geo.mesh_points.len() as i32);
for point in &geo.mesh_points {
self.writer.write_2raw_double(point.source);
self.writer.write_2raw_double(point.destination);
}
self.writer.write_bit_long(geo.mesh_faces.len() as i32);
for face in &geo.mesh_faces {
self.writer.write_bit_long(face.first);
self.writer.write_bit_long(face.second);
self.writer.write_bit_long(face.third);
}
if geo.version == 1 {
self.writer.write_bit(geo.civil_data_present);
self.writer.write_bit(geo.civil_obsolete_flag);
self.writer.write_2raw_double(geo.civil_reference_point1);
self.writer.write_2raw_double(geo.civil_reference_point2);
self.writer.write_bit_long(geo.civil_unknown1);
self.writer.write_bit_long(geo.civil_unknown2);
self.writer.write_bit(geo.civil_unknown_flag1);
self.writer.write_2raw_double(geo.civil_zero_point1);
self.writer.write_2raw_double(geo.civil_zero_point2);
self.writer.write_bit(geo.civil_unknown_flag2);
self.writer.write_bit_double(geo.civil_north_angle_degrees);
self.writer.write_bit_double(geo.civil_north_angle_radians);
self.writer.write_bit_long(geo.scale_estimation_method);
self.writer.write_bit_double(geo.user_scale_factor);
self.writer.write_bit(geo.sea_level_correction);
self.writer.write_bit_double(geo.sea_level_elevation);
self.writer
.write_bit_double(geo.coordinate_projection_radius);
}
self.register_object(geo.handle);
}
fn write_placeholder(&mut self, ph: &PlaceHolder) {
self.write_common_non_entity_data(common::OBJ_PLACEHOLDER, ph.handle, ph.owner, &[], &None);
self.register_object(ph.handle);
}
fn write_book_color(&mut self, bc: &BookColor) {
let type_code = self.class_type_code("DBCOLOR", common::OBJ_DBCOLOR);
self.write_common_non_entity_data(type_code, bc.handle, bc.owner, &[], &None);
if self.version.r2004_plus() {
self.writer.write_bit_short(0);
let true_color = match bc.color {
crate::types::Color::ByLayer => 0xC000_0000,
crate::types::Color::ByBlock => 0xC100_0000,
crate::types::Color::Index(index) => 0xC300_0000 | u32::from(index),
crate::types::Color::None => 0xC800_0000,
crate::types::Color::Rgb { r, g, b } => {
0xC200_0000 | (u32::from(r) << 16) | (u32::from(g) << 8) | u32::from(b)
}
};
self.writer.write_bit_long(true_color as i32);
let mut flags = 0u8;
if !bc.color_name.is_empty() {
flags |= 1;
}
if !bc.book_name.is_empty() {
flags |= 2;
}
self.writer.write_byte(flags);
if flags & 1 != 0 {
self.writer.write_variable_text(&bc.color_name);
}
if flags & 2 != 0 {
self.writer.write_variable_text(&bc.book_name);
}
} else {
self.writer.write_bit_short(bc.color.approximate_index());
}
self.register_object(bc.handle);
}
fn write_wipeout_variables(&mut self, wv: &WipeoutVariables) {
let type_code = self.class_type_code("WIPEOUTVARIABLES", common::OBJ_WIPEOUTVARIABLES);
self.write_common_non_entity_data(type_code, wv.handle, wv.owner, &[], &None);
self.writer.write_bit_short(wv.display_frame);
self.register_object(wv.handle);
}
}