use crate::document::HeaderVariables;
use crate::io::dwg::crc::{crc16, CRC16_SEED};
use crate::io::dwg::dwg_reference_type::DwgReferenceType;
use crate::io::dwg::dwg_stream_writers::DwgBitWriter;
use crate::io::dwg::dwg_stream_writers::DwgMergedWriter;
use crate::io::dwg::dwg_version::DwgVersion;
use crate::io::dwg::file_headers::section_definition::{end_sentinels, start_sentinels};
use crate::types::{Color, DxfVersion, Handle, Vector2, Vector3};
enum SectionWriterInner {
BitWriter(DwgBitWriter),
MergedWriter(DwgMergedWriter),
}
struct SectionWriter {
inner: SectionWriterInner,
}
impl SectionWriter {
fn new(version: DxfVersion) -> Self {
let dwg = DwgVersion::from_dxf_version(version).unwrap_or(DwgVersion::AC15);
let inner = if version >= DxfVersion::AC1021 {
let mut writer = DwgMergedWriter::new(dwg, version);
writer.save_position_for_size(); SectionWriterInner::MergedWriter(writer)
} else {
let writer = DwgBitWriter::new(dwg, version);
SectionWriterInner::BitWriter(writer)
};
SectionWriter { inner }
}
fn write_bit(&mut self, value: bool) {
match &mut self.inner {
SectionWriterInner::BitWriter(w) => w.write_bit(value),
SectionWriterInner::MergedWriter(w) => w.write_bit(value),
}
}
fn write_byte(&mut self, value: u8) {
match &mut self.inner {
SectionWriterInner::BitWriter(w) => w.write_byte(value),
SectionWriterInner::MergedWriter(w) => w.write_byte(value),
}
}
fn write_bit_short(&mut self, value: i16) {
match &mut self.inner {
SectionWriterInner::BitWriter(w) => w.write_bit_short(value),
SectionWriterInner::MergedWriter(w) => w.write_bit_short(value),
}
}
fn write_bit_long(&mut self, value: i32) {
match &mut self.inner {
SectionWriterInner::BitWriter(w) => w.write_bit_long(value),
SectionWriterInner::MergedWriter(w) => w.write_bit_long(value),
}
}
fn write_bit_long_long(&mut self, value: i64) {
match &mut self.inner {
SectionWriterInner::BitWriter(w) => w.write_bit_long_long(value),
SectionWriterInner::MergedWriter(w) => w.write_bit_long_long(value),
}
}
fn write_bit_double(&mut self, value: f64) {
match &mut self.inner {
SectionWriterInner::BitWriter(w) => w.write_bit_double(value),
SectionWriterInner::MergedWriter(w) => w.write_bit_double(value),
}
}
fn write_3bit_double(&mut self, value: Vector3) {
match &mut self.inner {
SectionWriterInner::BitWriter(w) => w.write_3bit_double(value),
SectionWriterInner::MergedWriter(w) => w.write_3bit_double(value),
}
}
fn write_2raw_double(&mut self, value: Vector2) {
match &mut self.inner {
SectionWriterInner::BitWriter(w) => w.write_2raw_double(value),
SectionWriterInner::MergedWriter(w) => w.write_2raw_double(value),
}
}
fn write_cm_color(&mut self, color: &Color) {
match &mut self.inner {
SectionWriterInner::BitWriter(w) => w.write_cm_color(color),
SectionWriterInner::MergedWriter(w) => w.write_cm_color(color),
}
}
fn write_datetime(&mut self, day: i32, ms: i32) {
match &mut self.inner {
SectionWriterInner::BitWriter(w) => w.write_datetime(day, ms),
SectionWriterInner::MergedWriter(w) => w.write_datetime(day, ms),
}
}
fn write_timespan(&mut self, days: i32, ms: i32) {
match &mut self.inner {
SectionWriterInner::BitWriter(w) => w.write_timespan(days, ms),
SectionWriterInner::MergedWriter(w) => w.write_timespan(days, ms),
}
}
fn write_variable_text(&mut self, value: &str) {
match &mut self.inner {
SectionWriterInner::BitWriter(w) => w.write_variable_text(value),
SectionWriterInner::MergedWriter(w) => w.write_variable_text(value),
}
}
fn write_handle_ref(&mut self, ref_type: DwgReferenceType, handle: Handle) {
match &mut self.inner {
SectionWriterInner::BitWriter(w) => w.write_handle(ref_type, handle.value()),
SectionWriterInner::MergedWriter(w) => w.write_handle(ref_type, handle.value()),
}
}
fn write_handle_seed(&mut self, handle_seed: u64) {
match &mut self.inner {
SectionWriterInner::BitWriter(w) => w.write_handle_undefined(handle_seed),
SectionWriterInner::MergedWriter(w) => {
w.main_mut().write_handle_undefined(handle_seed);
}
}
}
fn finalize(self) -> Vec<u8> {
match self.inner {
SectionWriterInner::BitWriter(mut w) => {
w.write_spear_shift();
w.into_bytes()
}
SectionWriterInner::MergedWriter(mut w) => {
w.merge()
}
}
}
}
pub fn write_header(version: DxfVersion, header: &HeaderVariables, maintenance_version: u8) -> Vec<u8> {
let mut w = SectionWriter::new(version);
write_header_fields(&mut w, version, header);
let section_data = w.finalize();
wrap_with_sentinels_and_crc(version, maintenance_version, §ion_data)
}
fn wrap_with_sentinels_and_crc(version: DxfVersion, maintenance_version: u8, section_data: &[u8]) -> Vec<u8> {
let mut output = Vec::with_capacity(16 + 4 + section_data.len() + 2 + 16 + 8);
output.extend_from_slice(&start_sentinels::HEADER);
let mut crc_content = Vec::with_capacity(4 + section_data.len());
crc_content.extend_from_slice(&(section_data.len() as i32).to_le_bytes());
if DwgVersion::has_section_extra_rl(version, maintenance_version) {
crc_content.extend_from_slice(&0i32.to_le_bytes());
}
crc_content.extend_from_slice(section_data);
let crc = crc16(CRC16_SEED, &crc_content);
output.extend_from_slice(&crc_content);
output.extend_from_slice(&crc.to_le_bytes());
output.extend_from_slice(&end_sentinels::HEADER);
if version >= DxfVersion::AC1018 {
output.extend_from_slice(&[0u8; 8]);
}
output
}
#[inline]
fn r13_14_only(v: DxfVersion) -> bool {
v >= DxfVersion::AC1012 && v <= DxfVersion::AC1014
}
#[inline]
fn r13_15_only(v: DxfVersion) -> bool {
v >= DxfVersion::AC1012 && v <= DxfVersion::AC1015
}
#[inline]
fn r2000_plus(v: DxfVersion) -> bool {
v >= DxfVersion::AC1015
}
#[inline]
fn r2004_plus(v: DxfVersion) -> bool {
v >= DxfVersion::AC1018
}
#[inline]
fn r2007_plus(v: DxfVersion) -> bool {
v >= DxfVersion::AC1021
}
#[inline]
fn r2010_plus(v: DxfVersion) -> bool {
v >= DxfVersion::AC1024
}
#[inline]
fn r2013_plus(v: DxfVersion) -> bool {
v >= DxfVersion::AC1027
}
fn julian_to_day_ms(julian: f64) -> (i32, i32) {
let day = julian as i32;
let fraction = julian - day as f64;
let ms = (fraction * 86_400_000.0) as i32;
(day, ms)
}
fn timespan_to_day_ms(days_fraction: f64) -> (i32, i32) {
let days = days_fraction as i32;
let fraction = days_fraction - days as f64;
let ms = (fraction * 86_400_000.0) as i32;
(days, ms)
}
fn write_header_fields(w: &mut SectionWriter, v: DxfVersion, h: &HeaderVariables) {
if r2013_plus(v) {
w.write_bit_long_long(h.required_versions);
}
w.write_bit_double(412148564080.0);
w.write_bit_double(1.0);
w.write_bit_double(1.0);
w.write_bit_double(1.0);
w.write_variable_text("m");
w.write_variable_text("");
w.write_variable_text("");
w.write_variable_text("");
w.write_bit_long(24);
w.write_bit_long(0);
if r13_14_only(v) {
w.write_bit_short(0);
}
if v < DxfVersion::AC1018 {
w.write_handle_ref(DwgReferenceType::HardPointer, Handle::NULL);
}
w.write_bit(h.associate_dimensions);
w.write_bit(h.update_dimensions_while_dragging);
if r13_14_only(v) {
w.write_bit(false); }
w.write_bit(h.polyline_linetype_generation);
w.write_bit(h.ortho_mode);
w.write_bit(h.regen_mode);
w.write_bit(h.fill_mode);
w.write_bit(h.quick_text_mode);
w.write_bit(h.paper_space_linetype_scaling);
w.write_bit(h.limit_check);
if r13_14_only(v) {
w.write_bit(h.blip_mode);
}
if r2004_plus(v) {
w.write_bit(false); }
w.write_bit(h.user_timer);
w.write_bit(false); w.write_bit(h.angle_direction != 0); w.write_bit(h.spline_frame);
if r13_14_only(v) {
w.write_bit(h.attribute_request);
w.write_bit(h.attribute_dialog);
}
w.write_bit(h.mirror_text);
w.write_bit(h.world_view);
if r13_14_only(v) {
w.write_bit(false); }
w.write_bit(h.show_model_space);
w.write_bit(h.paper_space_limit_check);
w.write_bit(h.retain_xref_visibility);
if r13_14_only(v) {
w.write_bit(h.delete_objects);
}
w.write_bit(h.display_silhouette);
w.write_bit(false); w.write_bit_short(h.proxy_graphics);
if r13_14_only(v) {
w.write_bit_short(h.drag_mode);
}
w.write_bit_short(h.tree_depth);
w.write_bit_short(h.linear_unit_format);
w.write_bit_short(h.linear_unit_precision);
w.write_bit_short(h.angular_unit_format);
w.write_bit_short(h.angular_unit_precision);
if r13_14_only(v) {
w.write_bit_short(h.object_snap_mode as i16);
}
w.write_bit_short(h.attribute_visibility);
if r13_14_only(v) {
w.write_bit_short(h.coords_mode);
}
w.write_bit_short(h.point_display_mode);
if r13_14_only(v) {
w.write_bit_short(h.pick_style);
}
if r2004_plus(v) {
w.write_bit_long(0); w.write_bit_long(0); w.write_bit_long(0); }
w.write_bit_short(h.user_int1);
w.write_bit_short(h.user_int2);
w.write_bit_short(h.user_int3);
w.write_bit_short(h.user_int4);
w.write_bit_short(h.user_int5);
w.write_bit_short(h.spline_segments);
w.write_bit_short(h.surface_u_density);
w.write_bit_short(h.surface_v_density);
w.write_bit_short(h.surface_type);
w.write_bit_short(h.surface_tab1);
w.write_bit_short(h.surface_tab2);
w.write_bit_short(h.spline_type);
w.write_bit_short(h.shade_edge);
w.write_bit_short(h.shade_diffuse);
w.write_bit_short(0); w.write_bit_short(h.max_active_viewports);
w.write_bit_short(h.isolines);
w.write_bit_short(h.multiline_justification);
w.write_bit_short(h.text_quality);
w.write_bit_double(h.linetype_scale);
w.write_bit_double(h.text_height);
w.write_bit_double(h.trace_width);
w.write_bit_double(h.sketch_increment);
w.write_bit_double(h.fillet_radius);
w.write_bit_double(h.thickness);
w.write_bit_double(h.angle_base);
w.write_bit_double(h.point_display_size);
w.write_bit_double(h.polyline_width);
w.write_bit_double(h.user_real1);
w.write_bit_double(h.user_real2);
w.write_bit_double(h.user_real3);
w.write_bit_double(h.user_real4);
w.write_bit_double(h.user_real5);
w.write_bit_double(h.chamfer_distance_a);
w.write_bit_double(h.chamfer_distance_b);
w.write_bit_double(h.chamfer_length);
w.write_bit_double(h.chamfer_angle);
w.write_bit_double(h.facet_resolution);
w.write_bit_double(h.multiline_scale);
w.write_bit_double(h.current_entity_linetype_scale);
w.write_variable_text(&h.menu_name);
let (cd, cms) = julian_to_day_ms(h.create_date_julian);
w.write_datetime(cd, cms);
let (ud, ums) = julian_to_day_ms(h.update_date_julian);
w.write_datetime(ud, ums);
if r2004_plus(v) {
w.write_bit_long(0); w.write_bit_long(0); w.write_bit_long(0); }
let (ted, tems) = timespan_to_day_ms(h.total_editing_time);
w.write_timespan(ted, tems);
let (ued, uems) = timespan_to_day_ms(h.user_elapsed_time);
w.write_timespan(ued, uems);
w.write_cm_color(&h.current_entity_color);
w.write_handle_seed(h.handle_seed);
w.write_handle_ref(DwgReferenceType::HardPointer, h.current_layer_handle);
w.write_handle_ref(DwgReferenceType::HardPointer, h.current_text_style_handle);
w.write_handle_ref(DwgReferenceType::HardPointer, h.current_linetype_handle);
if r2007_plus(v) {
w.write_handle_ref(DwgReferenceType::HardPointer, h.current_material_handle);
}
w.write_handle_ref(DwgReferenceType::HardPointer, h.current_dimstyle_handle);
w.write_handle_ref(DwgReferenceType::HardPointer, h.current_multiline_style_handle);
if r2000_plus(v) {
w.write_bit_double(h.viewport_scale_factor);
}
w.write_3bit_double(h.paper_space_insertion_base);
w.write_3bit_double(h.paper_space_extents_min);
w.write_3bit_double(h.paper_space_extents_max);
w.write_2raw_double(h.paper_space_limits_min);
w.write_2raw_double(h.paper_space_limits_max);
w.write_bit_double(h.paper_elevation);
w.write_3bit_double(h.paper_space_ucs_origin);
w.write_3bit_double(h.paper_space_ucs_x_axis);
w.write_3bit_double(h.paper_space_ucs_y_axis);
w.write_handle_ref(DwgReferenceType::HardPointer, Handle::NULL);
if r2000_plus(v) {
w.write_handle_ref(DwgReferenceType::HardPointer, h.paper_ucs_ortho_ref);
w.write_bit_short(h.paper_ucs_ortho_view);
w.write_handle_ref(DwgReferenceType::HardPointer, Handle::NULL);
w.write_3bit_double(Vector3::ZERO); w.write_3bit_double(Vector3::ZERO); w.write_3bit_double(Vector3::ZERO); w.write_3bit_double(Vector3::ZERO); w.write_3bit_double(Vector3::ZERO); w.write_3bit_double(Vector3::ZERO); }
w.write_3bit_double(h.model_space_insertion_base);
w.write_3bit_double(h.model_space_extents_min);
w.write_3bit_double(h.model_space_extents_max);
w.write_2raw_double(h.model_space_limits_min);
w.write_2raw_double(h.model_space_limits_max);
w.write_bit_double(h.elevation);
w.write_3bit_double(h.model_space_ucs_origin);
w.write_3bit_double(h.model_space_ucs_x_axis);
w.write_3bit_double(h.model_space_ucs_y_axis);
w.write_handle_ref(DwgReferenceType::HardPointer, Handle::NULL);
if r2000_plus(v) {
w.write_handle_ref(DwgReferenceType::HardPointer, h.ucs_ortho_ref);
w.write_bit_short(h.ucs_ortho_view);
w.write_handle_ref(DwgReferenceType::HardPointer, Handle::NULL);
w.write_3bit_double(Vector3::ZERO); w.write_3bit_double(Vector3::ZERO); w.write_3bit_double(Vector3::ZERO); w.write_3bit_double(Vector3::ZERO); w.write_3bit_double(Vector3::ZERO); w.write_3bit_double(Vector3::ZERO);
w.write_variable_text(&h.dim_post);
w.write_variable_text(&h.dim_alt_post);
}
if r13_14_only(v) {
w.write_bit(h.dim_tolerance);
w.write_bit(h.dim_limits);
w.write_bit(h.dim_text_inside_horizontal);
w.write_bit(h.dim_text_outside_horizontal);
w.write_bit(h.dim_suppress_ext1);
w.write_bit(h.dim_suppress_ext2);
w.write_bit(h.dim_alternate_units);
w.write_bit(h.dim_force_line_inside);
w.write_bit(h.dim_separate_arrows);
w.write_bit(h.dim_force_text_inside);
w.write_bit(h.dim_suppress_outside_ext);
w.write_byte(h.dim_alt_decimal_places as u8);
w.write_byte(h.dim_zero_suppression as u8);
w.write_bit(h.dim_suppress_line1);
w.write_bit(h.dim_suppress_line2);
w.write_byte(h.dim_tolerance_justification as u8);
w.write_byte(h.dim_horizontal_justification as u8);
w.write_byte(h.dim_fit as u8);
w.write_bit(h.dim_user_positioned_text);
w.write_byte(h.dim_tolerance_zero_suppression as u8);
w.write_byte(h.dim_alt_tolerance_zero_suppression as u8);
w.write_byte(h.dim_alt_tolerance_zero_tight as u8);
w.write_byte(h.dim_text_above as u8);
w.write_bit_short(0); w.write_bit_short(h.dim_angular_decimal_places);
w.write_bit_short(h.dim_decimal_places);
w.write_bit_short(h.dim_tolerance_decimal_places);
w.write_bit_short(h.dim_alt_units_format);
w.write_bit_short(h.dim_alt_tolerance_decimal_places);
w.write_handle_ref(DwgReferenceType::HardPointer, h.dim_text_style_handle);
}
w.write_bit_double(h.dim_scale);
w.write_bit_double(h.dim_arrow_size);
w.write_bit_double(h.dim_ext_line_offset);
w.write_bit_double(h.dim_line_increment);
w.write_bit_double(h.dim_ext_line_extension);
w.write_bit_double(h.dim_rounding);
w.write_bit_double(h.dim_line_extension);
w.write_bit_double(h.dim_tolerance_plus);
w.write_bit_double(h.dim_tolerance_minus);
if r2007_plus(v) {
w.write_bit_double(0.0); w.write_bit_double(0.7854); w.write_bit_short(0); w.write_cm_color(&Color::ByBlock); }
if r2000_plus(v) {
w.write_bit(h.dim_tolerance);
w.write_bit(h.dim_limits);
w.write_bit(h.dim_text_inside_horizontal);
w.write_bit(h.dim_text_outside_horizontal);
w.write_bit(h.dim_suppress_ext1);
w.write_bit(h.dim_suppress_ext2);
w.write_bit_short(h.dim_text_above);
w.write_bit_short(h.dim_zero_suppression);
w.write_bit_short(h.dim_alt_zero_suppression);
}
if r2007_plus(v) {
w.write_bit_short(0); }
w.write_bit_double(h.dim_text_height);
w.write_bit_double(h.dim_center_mark);
w.write_bit_double(h.dim_tick_size);
w.write_bit_double(h.dim_alt_scale);
w.write_bit_double(h.dim_linear_scale);
w.write_bit_double(h.dim_text_vertical_pos);
w.write_bit_double(h.dim_tolerance_scale);
w.write_bit_double(h.dim_line_gap);
if r13_14_only(v) {
w.write_variable_text(&h.dim_post);
w.write_variable_text(&h.dim_alt_post);
w.write_variable_text(&h.dim_arrow_block);
w.write_variable_text(&h.dim_arrow_block1);
w.write_variable_text(&h.dim_arrow_block2);
}
if r2000_plus(v) {
w.write_bit_double(h.dim_alt_rounding);
w.write_bit(h.dim_alternate_units);
w.write_bit_short(h.dim_alt_decimal_places);
w.write_bit(h.dim_force_line_inside);
w.write_bit(h.dim_separate_arrows);
w.write_bit(h.dim_force_text_inside);
w.write_bit(h.dim_suppress_outside_ext);
}
w.write_cm_color(&h.dim_line_color);
w.write_cm_color(&h.dim_ext_line_color);
w.write_cm_color(&h.dim_text_color);
if r2000_plus(v) {
w.write_bit_short(h.dim_angular_decimal_places);
w.write_bit_short(h.dim_decimal_places);
w.write_bit_short(h.dim_tolerance_decimal_places);
w.write_bit_short(h.dim_alt_units_format);
w.write_bit_short(h.dim_alt_tolerance_decimal_places);
w.write_bit_short(h.dim_angular_units);
w.write_bit_short(h.dim_fraction_format);
w.write_bit_short(h.dim_linear_unit_format);
w.write_bit_short(h.dim_decimal_separator as i16);
w.write_bit_short(h.dim_text_movement);
w.write_bit_short(h.dim_horizontal_justification);
w.write_bit(h.dim_suppress_line1);
w.write_bit(h.dim_suppress_line2);
w.write_bit_short(h.dim_tolerance_justification);
w.write_bit_short(h.dim_tolerance_zero_suppression);
w.write_bit_short(h.dim_alt_tolerance_zero_suppression);
w.write_bit_short(h.dim_alt_tolerance_zero_tight);
w.write_bit(h.dim_user_positioned_text);
w.write_bit_short(h.dim_fit);
}
if r2007_plus(v) {
w.write_bit(false); }
if r2010_plus(v) {
w.write_bit(false); w.write_bit_double(0.0); w.write_variable_text(""); w.write_bit_double(0.0); w.write_variable_text(""); }
if r2000_plus(v) {
w.write_handle_ref(DwgReferenceType::HardPointer, h.dim_text_style_handle);
w.write_handle_ref(DwgReferenceType::HardPointer, Handle::NULL); w.write_handle_ref(DwgReferenceType::HardPointer, Handle::NULL); w.write_handle_ref(DwgReferenceType::HardPointer, Handle::NULL); w.write_handle_ref(DwgReferenceType::HardPointer, Handle::NULL); }
if r2007_plus(v) {
w.write_handle_ref(DwgReferenceType::HardPointer, h.dim_linetype_handle);
w.write_handle_ref(DwgReferenceType::HardPointer, h.dim_linetype1_handle);
w.write_handle_ref(DwgReferenceType::HardPointer, h.dim_linetype2_handle);
}
if r2000_plus(v) {
w.write_bit_short(h.dim_line_weight);
w.write_bit_short(h.dim_ext_line_weight);
}
w.write_handle_ref(DwgReferenceType::HardOwnership, h.block_control_handle);
w.write_handle_ref(DwgReferenceType::HardOwnership, h.layer_control_handle);
w.write_handle_ref(DwgReferenceType::HardOwnership, h.style_control_handle);
w.write_handle_ref(DwgReferenceType::HardOwnership, h.linetype_control_handle);
w.write_handle_ref(DwgReferenceType::HardOwnership, h.view_control_handle);
w.write_handle_ref(DwgReferenceType::HardOwnership, h.ucs_control_handle);
w.write_handle_ref(DwgReferenceType::HardOwnership, h.vport_control_handle);
w.write_handle_ref(DwgReferenceType::HardOwnership, h.appid_control_handle);
w.write_handle_ref(DwgReferenceType::HardOwnership, h.dimstyle_control_handle);
if r13_15_only(v) {
w.write_handle_ref(DwgReferenceType::HardOwnership, h.vpent_hdr_control_handle);
}
w.write_handle_ref(DwgReferenceType::HardPointer, h.acad_group_dict_handle);
w.write_handle_ref(DwgReferenceType::HardPointer, h.acad_mlinestyle_dict_handle);
w.write_handle_ref(DwgReferenceType::HardOwnership, h.named_objects_dict_handle);
if r2000_plus(v) {
w.write_bit_short(1); w.write_bit_short(70);
w.write_variable_text(&h.hyperlink_base);
w.write_variable_text(&h.stylesheet);
w.write_handle_ref(DwgReferenceType::HardPointer, h.acad_layout_dict_handle);
w.write_handle_ref(DwgReferenceType::HardPointer, h.acad_plotsettings_dict_handle);
w.write_handle_ref(DwgReferenceType::HardPointer, h.acad_plotstylename_dict_handle);
}
if r2004_plus(v) {
w.write_handle_ref(DwgReferenceType::HardPointer, h.acad_material_dict_handle);
w.write_handle_ref(DwgReferenceType::HardPointer, h.acad_color_dict_handle);
}
if r2007_plus(v) {
w.write_handle_ref(DwgReferenceType::HardPointer, h.acad_visualstyle_dict_handle);
if r2013_plus(v) {
w.write_handle_ref(DwgReferenceType::HardPointer, Handle::NULL); }
}
if r2000_plus(v) {
let mut flags: i32 = (h.current_line_weight as i32) & 0x1F;
flags |= (h.end_caps as i32) << 5;
flags |= (h.join_style as i32) << 7;
if !h.lineweight_display {
flags |= 0x200;
}
if !h.xedit {
flags |= 0x400;
}
if h.extended_names {
flags |= 0x800;
}
if h.plotstyle_mode {
flags |= 0x2000;
}
if h.ole_startup {
flags |= 0x4000;
}
w.write_bit_long(flags);
w.write_bit_short(h.insertion_units);
w.write_bit_short(h.current_plotstyle_type);
if h.current_plotstyle_type == 3 {
w.write_handle_ref(DwgReferenceType::HardPointer, Handle::NULL);
}
w.write_variable_text(&h.fingerprint_guid);
w.write_variable_text(&h.version_guid);
}
if r2004_plus(v) {
w.write_byte(h.sort_entities as u8);
w.write_byte(h.index_control as u8);
w.write_byte(h.hide_text as u8);
w.write_byte(h.xclip_frame as u8);
w.write_byte(h.dimension_associativity as u8);
w.write_byte(h.halo_gap as u8);
w.write_bit_short(h.obscured_color);
w.write_bit_short(h.intersection_color);
w.write_byte(h.obscured_linetype as u8);
w.write_byte(h.intersection_display as u8);
w.write_variable_text(&h.project_name);
}
w.write_handle_ref(DwgReferenceType::HardPointer, h.paper_space_block_handle);
w.write_handle_ref(DwgReferenceType::HardPointer, h.model_space_block_handle);
w.write_handle_ref(DwgReferenceType::HardPointer, h.bylayer_linetype_handle);
w.write_handle_ref(DwgReferenceType::HardPointer, h.byblock_linetype_handle);
w.write_handle_ref(DwgReferenceType::HardPointer, h.continuous_linetype_handle);
if r2007_plus(v) {
w.write_bit(h.camera_display);
w.write_bit_long(0); w.write_bit_long(0); w.write_bit_double(0.0);
w.write_bit_double(h.steps_per_second);
w.write_bit_double(h.step_size);
w.write_bit_double(2.0); w.write_bit_double(h.lens_length);
w.write_bit_double(h.camera_height);
w.write_byte(0); w.write_byte(0); w.write_bit_double(0.25); w.write_bit_double(0.25); w.write_bit_double(h.loft_angle1);
w.write_bit_double(h.loft_angle2);
w.write_bit_double(h.loft_magnitude1);
w.write_bit_double(h.loft_magnitude2);
w.write_bit_short(h.loft_param);
w.write_byte(h.loft_normals as u8);
w.write_bit_double(h.latitude);
w.write_bit_double(h.longitude);
w.write_bit_double(h.north_direction);
w.write_bit_long(h.timezone);
w.write_byte(0); w.write_byte(1); w.write_byte(0); w.write_byte(0);
w.write_bit(false);
w.write_cm_color(&Color::from_index(h.intersection_color));
w.write_handle_ref(DwgReferenceType::HardPointer, Handle::NULL); w.write_handle_ref(DwgReferenceType::HardPointer, Handle::NULL); w.write_handle_ref(DwgReferenceType::HardPointer, Handle::NULL);
w.write_byte(0); w.write_bit_double(h.shadow_plane_location);
}
if v >= DxfVersion::AC1014 {
w.write_bit_short(-1);
w.write_bit_short(-1);
w.write_bit_short(-1);
w.write_bit_short(-1);
if r2004_plus(v) {
w.write_bit_long(0);
w.write_bit_long(0);
w.write_bit(false);
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_write_header_r2000_has_sentinels() {
let h = HeaderVariables::default();
let data = write_header(DxfVersion::AC1015, &h, 0);
assert_eq!(&data[..16], &start_sentinels::HEADER);
let end_start = data.len() - 16;
assert_eq!(&data[end_start..], &end_sentinels::HEADER);
}
#[test]
fn test_write_header_size_field() {
let h = HeaderVariables::default();
let data = write_header(DxfVersion::AC1015, &h, 0);
let size = i32::from_le_bytes([data[16], data[17], data[18], data[19]]);
assert!(size > 0, "Header section size should be > 0: got {}", size);
}
#[test]
fn test_write_header_crc_valid() {
let h = HeaderVariables::default();
let data = write_header(DxfVersion::AC1015, &h, 0);
let end_sentinel_start = data.len() - 16;
let crc_bytes = &data[end_sentinel_start - 2..end_sentinel_start];
let size_plus_data = &data[16..end_sentinel_start - 2];
let expected_crc = crc16(CRC16_SEED, size_plus_data);
let actual_crc = u16::from_le_bytes([crc_bytes[0], crc_bytes[1]]);
assert_eq!(
actual_crc, expected_crc,
"Header CRC mismatch: got 0x{:04X}, expected 0x{:04X}",
actual_crc, expected_crc
);
}
#[test]
fn test_write_header_r2004_larger_than_r2000() {
let h = HeaderVariables::default();
let data_2000 = write_header(DxfVersion::AC1015, &h, 0);
let data_2004 = write_header(DxfVersion::AC1018, &h, 0);
assert!(
data_2004.len() > data_2000.len(),
"R2004 header ({} bytes) should be larger than R2000 ({} bytes)",
data_2004.len(),
data_2000.len()
);
}
#[test]
fn test_write_header_r2007_uses_merged_format() {
let h = HeaderVariables::default();
let data_2007 = write_header(DxfVersion::AC1021, &h, 0);
assert_eq!(&data_2007[..16], &start_sentinels::HEADER);
assert!(data_2007.len() > 200, "R2007 header should be substantial");
}
#[test]
fn test_write_header_r14_smaller_than_r2000() {
let h = HeaderVariables::default();
let data_r14 = write_header(DxfVersion::AC1014, &h, 0);
let data_2000 = write_header(DxfVersion::AC1015, &h, 0);
assert!(data_r14.len() > 100);
assert!(data_2000.len() > 100);
}
#[test]
fn test_julian_conversion() {
let (day, ms) = julian_to_day_ms(2451544.5);
assert_eq!(day, 2451544);
assert!(ms > 0);
let (d, m) = julian_to_day_ms(0.0);
assert_eq!(d, 0);
assert_eq!(m, 0);
}
#[test]
fn test_timespan_conversion() {
let (days, ms) = timespan_to_day_ms(1.5);
assert_eq!(days, 1);
assert_eq!(ms, 43_200_000);
}
}