1use std::ops::{BitOr, BitOrAssign};
9
10use crate::objects::{
11 Dictionary, DictionaryCloningFlags, ObjectType, XRecord, XRecordEntry, XRecordValue,
12};
13use crate::tables::normalize_name;
14use crate::types::{Color, Handle, LineWeight, Transparency};
15use crate::CadDocument;
16
17const LAYER_STATES_DICTIONARY: &str = "ACAD_LAYERSTATES";
18
19#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
21#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
22pub struct LayerStateMask(u32);
23
24impl LayerStateMask {
25 pub const NONE: Self = Self(0);
26 pub const ON: Self = Self(0x0001);
27 pub const FROZEN: Self = Self(0x0002);
28 pub const LOCKED: Self = Self(0x0004);
29 pub const PLOT: Self = Self(0x0008);
30 pub const NEW_VIEWPORT: Self = Self(0x0010);
31 pub const COLOR: Self = Self(0x0020);
32 pub const LINE_TYPE: Self = Self(0x0040);
33 pub const LINE_WEIGHT: Self = Self(0x0080);
34 pub const PLOT_STYLE: Self = Self(0x0100);
35 pub const CURRENT_VIEWPORT: Self = Self(0x0200);
36 pub const TRANSPARENCY: Self = Self(0x0400);
37 pub const LAST_RESTORED: Self = Self(0x1_0000);
38
39 pub const SUPPORTED: Self = Self(
41 Self::ON.0
42 | Self::FROZEN.0
43 | Self::LOCKED.0
44 | Self::PLOT.0
45 | Self::NEW_VIEWPORT.0
46 | Self::COLOR.0
47 | Self::LINE_TYPE.0
48 | Self::LINE_WEIGHT.0
49 | Self::PLOT_STYLE.0
50 | Self::TRANSPARENCY.0,
51 );
52
53 pub const ALL: Self = Self(0x07ff);
55
56 pub const fn from_bits(bits: u32) -> Self {
57 Self(bits)
58 }
59
60 pub const fn bits(self) -> u32 {
61 self.0
62 }
63
64 pub const fn contains(self, other: Self) -> bool {
65 (self.0 & other.0) == other.0
66 }
67}
68
69impl BitOr for LayerStateMask {
70 type Output = Self;
71
72 fn bitor(self, rhs: Self) -> Self::Output {
73 Self(self.0 | rhs.0)
74 }
75}
76
77impl BitOrAssign for LayerStateMask {
78 fn bitor_assign(&mut self, rhs: Self) {
79 self.0 |= rhs.0;
80 }
81}
82
83#[derive(Debug, Clone, PartialEq)]
85#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
86pub struct LayerStateLayer {
87 pub layer_handle: Handle,
88 pub layer_name: String,
89 pub off: bool,
90 pub frozen: bool,
91 pub locked: bool,
92 pub plottable: bool,
93 pub new_viewport_frozen: bool,
94 pub color: Color,
95 #[cfg_attr(feature = "serde", serde(default))]
96 pub color_name: Option<String>,
97 #[cfg_attr(feature = "serde", serde(default))]
98 pub book_name: Option<String>,
99 pub line_type: String,
100 pub line_weight: LineWeight,
101 pub plot_style: String,
102 pub transparency: Option<Transparency>,
103}
104
105#[derive(Debug, Clone, PartialEq)]
107#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
108pub struct LayerState {
109 pub name: String,
110 pub description: String,
111 pub mask: LayerStateMask,
112 pub hidden: bool,
113 pub current_layer: String,
114 pub layers: Vec<LayerStateLayer>,
115}
116
117impl CadDocument {
118 pub(crate) fn rename_layer_state_references(&mut self, old_key: &str, new_name: &str) {
119 let Some(dictionary_handle) = self.layer_states_dictionary_handle() else {
120 return;
121 };
122 let handles = match self.objects.get(&dictionary_handle) {
123 Some(ObjectType::Dictionary(dictionary)) => dictionary
124 .entries
125 .iter()
126 .map(|(_, handle)| *handle)
127 .collect::<Vec<_>>(),
128 _ => return,
129 };
130 for handle in handles {
131 let Some(ObjectType::XRecord(xrecord)) = self.objects.get_mut(&handle) else {
132 continue;
133 };
134 let header_end = xrecord
135 .entries
136 .iter()
137 .position(|entry| entry.code == 330)
138 .unwrap_or(xrecord.entries.len());
139 let Some(entry) = xrecord.entries[..header_end].iter_mut().find(|entry| {
140 entry.code == 302
141 && entry
142 .value
143 .as_string()
144 .is_some_and(|name| normalize_name(name) == old_key)
145 }) else {
146 continue;
147 };
148 entry.value = XRecordValue::String(new_name.to_string());
149 xrecord.raw_data.clear();
150 xrecord.raw_dwg_data = None;
151 }
152 }
153
154 pub fn layer_states(&self) -> Vec<LayerState> {
156 let Some(dictionary_handle) = self.layer_states_dictionary_handle() else {
157 return Vec::new();
158 };
159 let Some(ObjectType::Dictionary(dictionary)) = self.objects.get(&dictionary_handle) else {
160 return Vec::new();
161 };
162
163 dictionary
164 .entries
165 .iter()
166 .filter_map(|(name, handle)| {
167 let ObjectType::XRecord(xrecord) = self.objects.get(handle)? else {
168 return None;
169 };
170 Some(self.decode_layer_state(name, xrecord))
171 })
172 .collect()
173 }
174
175 pub fn layer_state(&self, name: &str) -> Option<LayerState> {
177 self.layer_states()
178 .into_iter()
179 .find(|state| state.name.eq_ignore_ascii_case(name))
180 }
181
182 pub fn capture_layer_state(
186 &mut self,
187 name: impl Into<String>,
188 description: impl Into<String>,
189 ) -> Handle {
190 let name = name.into();
191 let current_layer = if self.header.current_layer_name.is_empty() {
192 self.layers
193 .iter()
194 .find(|layer| layer.handle == self.header.current_layer_handle)
195 .map(|layer| layer.name.clone())
196 .unwrap_or_else(|| "0".to_string())
197 } else {
198 self.header.current_layer_name.clone()
199 };
200 let layers = self
201 .layers
202 .iter()
203 .map(|layer| LayerStateLayer {
204 layer_handle: layer.handle,
205 layer_name: layer.name.clone(),
206 off: layer.flags.off,
207 frozen: layer.flags.frozen,
208 locked: layer.flags.locked,
209 plottable: layer.is_plottable,
210 new_viewport_frozen: layer.flags.frozen_in_new_viewport,
211 color: layer.color,
212 color_name: layer.color_name.clone(),
213 book_name: layer.book_name.clone(),
214 line_type: layer.line_type.clone(),
215 line_weight: layer.line_weight,
216 plot_style: if layer.plot_style.is_empty() {
217 format!("Color_{}", layer.color.approximate_index())
218 } else {
219 layer.plot_style.clone()
220 },
221 transparency: Some(layer.transparency),
222 })
223 .collect();
224
225 self.store_layer_state(LayerState {
226 name,
227 description: description.into(),
228 mask: LayerStateMask::SUPPORTED,
229 hidden: false,
230 current_layer,
231 layers,
232 })
233 }
234
235 pub fn store_layer_state(&mut self, state: LayerState) -> Handle {
239 let dictionary_handle = self.ensure_layer_states_dictionary();
240 let existing_handle = self
241 .dictionary_entry(dictionary_handle, &state.name)
242 .map(|(_, handle)| handle);
243 let xrecord_handle = existing_handle.unwrap_or_else(|| self.allocate_handle());
244 let entries = self.encode_layer_state(&state);
245
246 let mut xrecord = match self.objects.remove(&xrecord_handle) {
247 Some(ObjectType::XRecord(xrecord)) => xrecord,
248 _ => XRecord::named(&state.name),
249 };
250 xrecord.handle = xrecord_handle;
251 xrecord.owner = dictionary_handle;
252 xrecord.reactors = vec![dictionary_handle];
253 xrecord.name = state.name.clone();
254 xrecord.cloning_flags = DictionaryCloningFlags::KeepExisting;
255 xrecord.entries = entries;
256 xrecord.raw_data.clear();
257 xrecord.raw_dwg_data = None;
258 xrecord.raw_dwg_handle_bits = 0;
259 xrecord.raw_dwg_version = None;
260 self.objects
261 .insert(xrecord_handle, ObjectType::XRecord(xrecord));
262
263 if let Some(ObjectType::Dictionary(dictionary)) = self.objects.get_mut(&dictionary_handle) {
264 if let Some((key, handle)) = dictionary
265 .entries
266 .iter_mut()
267 .find(|(key, _)| key.eq_ignore_ascii_case(&state.name))
268 {
269 *key = state.name;
270 *handle = xrecord_handle;
271 } else {
272 dictionary.entries.push((state.name, xrecord_handle));
273 }
274 }
275
276 xrecord_handle
277 }
278
279 pub fn restore_layer_state(&mut self, name: &str) -> Option<usize> {
281 let state = self.layer_state(name)?;
282 let mut restored = 0usize;
283
284 for saved in &state.layers {
285 let layer_name = if !saved.layer_handle.is_null() {
286 self.layers
287 .iter()
288 .find(|layer| layer.handle == saved.layer_handle)
289 .map(|layer| layer.name.clone())
290 } else {
291 None
292 }
293 .unwrap_or_else(|| saved.layer_name.clone());
294 let layer = self.layers.get_mut(&layer_name);
295
296 let Some(layer) = layer else {
297 continue;
298 };
299 if state.mask.contains(LayerStateMask::ON) {
300 layer.flags.off = saved.off;
301 }
302 if state.mask.contains(LayerStateMask::FROZEN) {
303 layer.flags.frozen = saved.frozen;
304 }
305 if state.mask.contains(LayerStateMask::LOCKED) {
306 layer.flags.locked = saved.locked;
307 }
308 if state.mask.contains(LayerStateMask::PLOT) {
309 layer.is_plottable = saved.plottable;
310 }
311 if state.mask.contains(LayerStateMask::NEW_VIEWPORT) {
312 layer.flags.frozen_in_new_viewport = saved.new_viewport_frozen;
313 }
314 if state.mask.contains(LayerStateMask::COLOR) {
315 layer.color = saved.color;
316 layer.color_name.clone_from(&saved.color_name);
317 layer.book_name.clone_from(&saved.book_name);
318 }
319 if state.mask.contains(LayerStateMask::LINE_TYPE) && !saved.line_type.is_empty() {
320 layer.line_type.clone_from(&saved.line_type);
321 }
322 if state.mask.contains(LayerStateMask::LINE_WEIGHT) {
323 layer.line_weight = saved.line_weight;
324 }
325 if state.mask.contains(LayerStateMask::PLOT_STYLE) {
326 layer.plot_style.clone_from(&saved.plot_style);
327 }
328 if state.mask.contains(LayerStateMask::TRANSPARENCY) {
329 if let Some(transparency) = saved.transparency {
330 layer.transparency = transparency;
331 }
332 }
333 restored += 1;
334 }
335
336 if let Some(layer) = self.layers.get(&state.current_layer) {
337 self.header.current_layer_handle = layer.handle;
338 self.header.current_layer_name.clone_from(&layer.name);
339 }
340
341 Some(restored)
342 }
343
344 pub fn rename_layer_state(&mut self, old_name: &str, new_name: &str) -> bool {
347 let Some(dictionary_handle) = self.layer_states_dictionary_handle() else {
348 return false;
349 };
350 if self.dictionary_entry(dictionary_handle, new_name).is_some()
351 && !old_name.eq_ignore_ascii_case(new_name)
352 {
353 return false;
354 }
355 let Some(ObjectType::Dictionary(dictionary)) = self.objects.get_mut(&dictionary_handle)
356 else {
357 return false;
358 };
359 let Some((key, handle)) = dictionary
360 .entries
361 .iter_mut()
362 .find(|(key, _)| key.eq_ignore_ascii_case(old_name))
363 else {
364 return false;
365 };
366 *key = new_name.to_string();
367 let xrecord_handle = *handle;
368 if let Some(ObjectType::XRecord(xrecord)) = self.objects.get_mut(&xrecord_handle) {
369 xrecord.name = new_name.to_string();
370 }
371 true
372 }
373
374 pub fn delete_layer_state(&mut self, name: &str) -> bool {
376 let Some(dictionary_handle) = self.layer_states_dictionary_handle() else {
377 return false;
378 };
379 let Some(ObjectType::Dictionary(dictionary)) = self.objects.get_mut(&dictionary_handle)
380 else {
381 return false;
382 };
383 let Some(index) = dictionary
384 .entries
385 .iter()
386 .position(|(key, _)| key.eq_ignore_ascii_case(name))
387 else {
388 return false;
389 };
390 let (_, handle) = dictionary.entries.remove(index);
391 self.objects.remove(&handle);
392 true
393 }
394
395 fn layer_extension_dictionary_handle(&self) -> Option<Handle> {
396 let table_handle = self.layers.handle();
397 self.xdic_by_handle
398 .get(&table_handle)
399 .copied()
400 .filter(|handle| self.objects.contains_key(handle))
401 .or_else(|| {
402 self.objects
403 .iter()
404 .find_map(|(handle, object)| match object {
405 ObjectType::Dictionary(dictionary) if dictionary.owner == table_handle => {
406 Some(*handle)
407 }
408 _ => None,
409 })
410 })
411 }
412
413 fn layer_states_dictionary_handle(&self) -> Option<Handle> {
414 let extension_handle = self.layer_extension_dictionary_handle()?;
415 let Some(ObjectType::Dictionary(extension)) = self.objects.get(&extension_handle) else {
416 return None;
417 };
418 extension
419 .entries
420 .iter()
421 .find(|(key, _)| {
422 key.eq_ignore_ascii_case(LAYER_STATES_DICTIONARY)
423 || key.eq_ignore_ascii_case("ACAD_LAYERSTATE")
424 })
425 .map(|(_, handle)| *handle)
426 }
427
428 fn ensure_layer_states_dictionary(&mut self) -> Handle {
429 let table_handle = self.layers.handle();
430 let extension_handle = self.layer_extension_dictionary_handle().unwrap_or_else(|| {
431 let handle = self.allocate_handle();
432 let mut dictionary = Dictionary::new();
433 dictionary.handle = handle;
434 dictionary.owner = table_handle;
435 dictionary.hard_owner = true;
436 self.objects
437 .insert(handle, ObjectType::Dictionary(dictionary));
438 handle
439 });
440 self.xdic_by_handle.insert(table_handle, extension_handle);
441
442 if let Some(handle) = self.layer_states_dictionary_handle() {
443 if matches!(self.objects.get(&handle), Some(ObjectType::Dictionary(_))) {
444 return handle;
445 }
446 }
447
448 let dictionary_handle = self.allocate_handle();
449 let mut dictionary = Dictionary::new();
450 dictionary.handle = dictionary_handle;
451 dictionary.owner = extension_handle;
452 dictionary.reactors = vec![extension_handle];
453 self.objects
454 .insert(dictionary_handle, ObjectType::Dictionary(dictionary));
455
456 if let Some(ObjectType::Dictionary(extension)) = self.objects.get_mut(&extension_handle) {
457 if let Some((key, handle)) = extension.entries.iter_mut().find(|(key, _)| {
458 key.eq_ignore_ascii_case(LAYER_STATES_DICTIONARY)
459 || key.eq_ignore_ascii_case("ACAD_LAYERSTATE")
460 }) {
461 *key = LAYER_STATES_DICTIONARY.to_string();
462 *handle = dictionary_handle;
463 } else {
464 extension
465 .entries
466 .push((LAYER_STATES_DICTIONARY.to_string(), dictionary_handle));
467 }
468 }
469 dictionary_handle
470 }
471
472 fn dictionary_entry(&self, dictionary_handle: Handle, name: &str) -> Option<(String, Handle)> {
473 let Some(ObjectType::Dictionary(dictionary)) = self.objects.get(&dictionary_handle) else {
474 return None;
475 };
476 dictionary
477 .entries
478 .iter()
479 .find(|(key, _)| key.eq_ignore_ascii_case(name))
480 .cloned()
481 }
482
483 fn decode_layer_state(&self, name: &str, xrecord: &XRecord) -> LayerState {
484 let first_layer = xrecord
485 .entries
486 .iter()
487 .position(|entry| entry.code == 330)
488 .unwrap_or(xrecord.entries.len());
489 let header = &xrecord.entries[..first_layer];
490 let mask = header
491 .iter()
492 .find(|entry| entry.code == 91)
493 .and_then(|entry| entry.value.as_i32())
494 .map(|value| LayerStateMask::from_bits(value as u32))
495 .unwrap_or(LayerStateMask::ALL);
496 let description = header
497 .iter()
498 .find(|entry| entry.code == 301)
499 .and_then(|entry| entry.value.as_string())
500 .unwrap_or_default()
501 .to_string();
502 let hidden = header
503 .iter()
504 .find(|entry| entry.code == 290)
505 .and_then(|entry| entry.value.as_bool())
506 .unwrap_or(false);
507 let current_layer = header
508 .iter()
509 .find(|entry| entry.code == 302)
510 .and_then(|entry| entry.value.as_string())
511 .unwrap_or_default()
512 .to_string();
513
514 let mut layers = Vec::new();
515 let mut start = first_layer;
516 while start < xrecord.entries.len() {
517 let end = xrecord.entries[start + 1..]
518 .iter()
519 .position(|entry| entry.code == 330)
520 .map(|offset| start + 1 + offset)
521 .unwrap_or(xrecord.entries.len());
522 let group = &xrecord.entries[start..end];
523 if let Some(layer) = self.decode_layer_state_layer(group) {
524 layers.push(layer);
525 }
526 start = end;
527 }
528
529 LayerState {
530 name: name.to_string(),
531 description,
532 mask,
533 hidden,
534 current_layer,
535 layers,
536 }
537 }
538
539 fn decode_layer_state_layer(&self, entries: &[XRecordEntry]) -> Option<LayerStateLayer> {
540 let layer_handle = entries
541 .first()
542 .filter(|entry| entry.code == 330)?
543 .value
544 .as_handle()?;
545 let flags = entry_i32(entries, 90).unwrap_or(0);
546 let color = entry_i32(entries, 92)
547 .or_else(|| entry_i32(entries, 420))
548 .map(Color::from_true_color_value)
549 .unwrap_or_else(|| Color::from_index(entry_i32(entries, 62).unwrap_or(7) as i16));
550 let (book_name, color_name) = entry_string(entries, 300)
551 .map(crate::io::dxf::split_color_book_name)
552 .unwrap_or((None, None));
553 let line_type_handle = entry_handle(entries, 331).unwrap_or(Handle::NULL);
554 let layer_name = self
555 .layers
556 .iter()
557 .find(|layer| layer.handle == layer_handle)
558 .map(|layer| layer.name.clone())
559 .unwrap_or_default();
560 let line_type = self
561 .line_types
562 .iter()
563 .find(|line_type| line_type.handle == line_type_handle)
564 .map(|line_type| line_type.name.clone())
565 .unwrap_or_default();
566
567 Some(LayerStateLayer {
568 layer_handle,
569 layer_name,
570 off: flags & 0x01 != 0,
571 frozen: flags & 0x02 != 0,
572 locked: flags & 0x04 != 0,
573 plottable: flags & 0x08 != 0,
574 new_viewport_frozen: flags & 0x10 != 0,
575 color,
576 color_name,
577 book_name,
578 line_type,
579 line_weight: LineWeight::from_value(entry_i32(entries, 370).unwrap_or(-3) as i16),
580 plot_style: entry_string(entries, 1).unwrap_or_default().to_string(),
581 transparency: entry_i32(entries, 440)
582 .and_then(|value| Transparency::from_layer_alpha_value(value as u32)),
583 })
584 }
585
586 fn encode_layer_state(&self, state: &LayerState) -> Vec<XRecordEntry> {
587 let mut entries = vec![
588 XRecordEntry::int32(91, state.mask.bits() as i32),
589 XRecordEntry::string(301, &state.description),
590 XRecordEntry::bool(290, state.hidden),
591 XRecordEntry::string(302, &state.current_layer),
592 ];
593
594 for layer in &state.layers {
595 let layer_handle = if layer.layer_handle.is_null() {
596 self.layers
597 .get(&layer.layer_name)
598 .map(|layer| layer.handle)
599 .unwrap_or(Handle::NULL)
600 } else {
601 layer.layer_handle
602 };
603 if layer_handle.is_null() {
604 continue;
605 }
606
607 let mut flags = 0i32;
608 if layer.off {
609 flags |= 0x01;
610 }
611 if layer.frozen {
612 flags |= 0x02;
613 }
614 if layer.locked {
615 flags |= 0x04;
616 }
617 if layer.plottable {
618 flags |= 0x08;
619 }
620 if layer.new_viewport_frozen {
621 flags |= 0x10;
622 }
623
624 let line_type_handle = self
625 .line_types
626 .iter()
627 .find(|line_type| line_type.name.eq_ignore_ascii_case(&layer.line_type))
628 .map(|line_type| line_type.handle)
629 .or_else(|| {
630 self.line_types
631 .get("Continuous")
632 .map(|line_type| line_type.handle)
633 })
634 .unwrap_or(Handle::NULL);
635 entries.push(XRecordEntry::handle(330, layer_handle));
636 entries.push(XRecordEntry::int32(90, flags));
637 entries.push(XRecordEntry::int16(62, layer.color.approximate_index()));
638 if let Some(true_color) = layer.color.to_true_color_value() {
639 entries.push(XRecordEntry::int32(
640 92,
641 (0xC200_0000u32 | true_color as u32) as i32,
642 ));
643 }
644 entries.push(XRecordEntry::int16(370, layer.line_weight.value()));
645 entries.push(XRecordEntry::handle(331, line_type_handle));
646 entries.push(XRecordEntry::string(1, &layer.plot_style));
647 if let Some(transparency) = layer.transparency {
648 entries.push(XRecordEntry::int32(440, transparency.to_dxf_value()));
649 }
650 if let Some(name) = crate::io::dxf::join_color_book_name(
651 layer.book_name.as_deref(),
652 layer.color_name.as_deref(),
653 ) {
654 entries.push(XRecordEntry::string(300, &name));
655 }
656 }
657 entries
658 }
659}
660
661fn entry_i32(entries: &[XRecordEntry], code: i32) -> Option<i32> {
662 entries
663 .iter()
664 .find(|entry| entry.code == code)
665 .and_then(|entry| entry.value.as_i32())
666}
667
668fn entry_handle(entries: &[XRecordEntry], code: i32) -> Option<Handle> {
669 entries
670 .iter()
671 .find(|entry| entry.code == code)
672 .and_then(|entry| entry.value.as_handle())
673}
674
675fn entry_string(entries: &[XRecordEntry], code: i32) -> Option<&str> {
676 entries
677 .iter()
678 .find(|entry| entry.code == code)
679 .and_then(|entry| entry.value.as_string())
680}