1use std::io::Write;
8
9use crate::device::OutputDevice;
10use crate::dict::DictKey;
11use crate::display_list::{DisplayList, GroupParams, SoftMaskParams};
12use crate::dual_array_store::DualArrayStore;
13use crate::dual_dict_store::DualDictStore;
14use crate::dual_string_store::DualStringStore;
15use crate::error::PsError;
16use crate::file_store::FileStore;
17use crate::graphics_state::{GraphicsState, Matrix, PathSegment, PatternData};
18use crate::name::NameTable;
19use crate::object::{EntityId, NameId, ObjFlags, PsObject, PsValue, SaveLevel};
20use crate::save_stack::{SaveRecord, SaveStack, StoreType};
21use crate::stack::Stack;
22
23pub struct OpEntry {
25 pub func: fn(&mut Context) -> Result<(), PsError>,
26 pub name: NameId,
27}
28
29pub struct NameCache {
31 pub n_def: NameId,
32 pub n_true: NameId,
33 pub n_false: NameId,
34 pub n_null: NameId,
35 pub n_mark: NameId,
36 pub n_font_name: NameId,
38 pub n_font_type: NameId,
39 pub n_font_matrix: NameId,
40 pub n_font_bbox: NameId,
41 pub n_encoding: NameId,
42 pub n_char_strings: NameId,
43 pub n_private: NameId,
44 pub n_fid: NameId,
45 pub n_paint_type: NameId,
46 pub n_subrs: NameId,
47 pub n_len_iv: NameId,
48 pub n_notdef: NameId,
49 pub n_metrics: NameId,
50 pub n_font_directory: NameId,
51 pub n_find_resource: NameId,
53 pub n_define_resource: NameId,
54 pub n_undef_resource: NameId,
55 pub n_resource_status: NameId,
56 pub n_resource_for_all: NameId,
57 pub n_category: NameId,
58 pub n_instance_type: NameId,
59 pub n_resource_dir: NameId,
60 pub n_resource_ext: NameId,
61 pub n_build_char: NameId,
63 pub n_build_glyph: NameId,
64 pub n_stroke_width: NameId,
66 pub n_wmode: NameId,
67}
68
69pub struct LoopState {
71 pub loop_type: LoopType,
72 pub proc_entity: EntityId,
73 pub proc_start: u32,
74 pub proc_len: u32,
75
76 pub counter: f64,
78 pub increment: f64,
79 pub limit: f64,
80 pub use_int: bool,
81
82 pub source: PsObject,
84 pub index: u32,
85 pub dict_keys: Option<Vec<DictKey>>,
87
88 pub path_segments: Option<Vec<PathSegment>>,
90 pub path_procs: Option<[PsObject; 4]>, pub path_ictm: Option<Matrix>,
92}
93
94pub enum LoopType {
96 For,
97 Repeat,
98 Loop,
99 Forall,
100 PathForall,
101}
102
103pub type ExecSyncFn = fn(&mut Context, PsObject) -> Result<(), PsError>;
106
107pub struct Context {
108 pub o_stack: Stack,
110 pub e_stack: Stack,
111 pub d_stack: Vec<EntityId>,
112
113 pub strings: DualStringStore,
115 pub arrays: DualArrayStore,
116 pub dicts: DualDictStore,
117 pub names: NameTable,
118 pub files: FileStore,
119
120 pub loops: Vec<LoopState>,
122
123 pub operators: Vec<OpEntry>,
125
126 pub systemdict: EntityId,
128 pub globaldict: EntityId,
129 pub userdict: EntityId,
130 pub errordict: EntityId,
131 pub dollar_error: EntityId,
132
133 pub rand_state: u64,
135 pub rand_seed: i32,
136 pub current_source_line: u32,
138 pub packing_mode: bool,
140 pub echo: bool,
142
143 pub name_cache: NameCache,
145
146 pub stdout: Box<dyn Write>,
148
149 pub save_stack: SaveStack,
151 pub job_start_save_depth: usize,
153
154 pub vm_alloc_mode: bool,
156
157 pub object_format: i32,
159
160 pub current_operator: Option<NameId>,
162 pub in_error_handler: bool,
163 pub initializing: bool,
165 pub allow_ps_resolution: bool,
168
169 pub exit_code: Option<i32>,
174
175 pub gstate: GraphicsState,
177 pub gstate_stack: Vec<crate::graphics_state::GstateEntry>,
178 pub gstate_store: Vec<GraphicsState>,
180 pub device: Option<Box<dyn OutputDevice>>,
181 pub display_list: DisplayList,
182 pub group_stack: Vec<GroupFrame>,
189 pub save_group_depths: rustc_hash::FxHashMap<u32, usize>,
193 pub ocg_registry: rustc_hash::FxHashMap<NameId, OcgRecord>,
200 pub next_ocg_id: u32,
203 pub doc_structure: stet_graphics::document_structure::DocumentStructure,
210 pub capture_display_lists: Option<Vec<(DisplayList, f64)>>,
214 pub display_list_sender: Option<
221 std::sync::mpsc::Sender<(
222 DisplayList,
223 f64,
224 u32,
225 u32,
226 Option<std::sync::Arc<Vec<u8>>>,
227 bool,
228 )>,
229 >,
230 pub page_width: u32,
231 pub page_height: u32,
232 pub output_path: Option<String>,
233 pub page_filter: Option<std::collections::HashSet<i32>>,
235 #[allow(clippy::type_complexity)]
237 pub device_factory: Option<Box<dyn Fn(u32, u32) -> Box<dyn OutputDevice>>>,
238
239 pub font_directory: EntityId,
241 pub font_resource_path: Option<String>,
242 pub next_fid: i32,
243
244 pub global_resources: EntityId,
246 pub local_resources: EntityId,
247 pub category_registry: EntityId,
248 pub resource_base_path: Option<String>,
249
250 pub user_params: EntityId,
252 pub system_params: EntityId,
253
254 pub internaldict: Option<EntityId>,
256
257 pub icc_cache: crate::icc::IccCache,
259
260 pub exec_sync_fn: Option<ExecSyncFn>,
262
263 pub char_width: Option<(f64, f64)>,
265 pub char_width_mode1: Option<((f64, f64), (f64, f64))>,
267
268 pub glyph_caches: rustc_hash::FxHashMap<EntityId, crate::glyph_cache::GlyphCache>,
270 pub char_cache_mode: Option<crate::glyph_cache::Type3CacheMode>,
272
273 pub cshow_pending_cid: Option<i32>,
275
276 pub pattern_store: Vec<PatternData>,
279 pub form_cache: rustc_hash::FxHashMap<EntityId, DisplayList>,
281
282 pub start_time: Option<std::time::Instant>,
284
285 pub dict_version: u64,
287 pub name_resolve_cache: Vec<(u64, PsObject)>,
290
291 pub interrupt_flag: Option<std::sync::Arc<std::sync::atomic::AtomicBool>>,
295
296 pub yield_after_showpage: bool,
303}
304
305pub struct GroupFrame {
310 pub display_list: DisplayList,
313 pub kind: GroupKind,
316 pub saved_clip_path_version: u32,
320 pub saved_gsave_depth: usize,
324}
325
326pub enum GroupKind {
329 Transparency { params: GroupParams },
333 SoftMask { params: SoftMaskParams },
337 Masked {
343 mask: DisplayList,
344 params: SoftMaskParams,
345 },
346 OptionalContent { ocg_id: u32, default_visible: bool },
351}
352
353#[derive(Clone, Debug)]
358pub struct OcgRecord {
359 pub ocg_id: u32,
362 pub default_visible: bool,
365}
366
367impl Context {
368 pub fn exec_sync(&mut self, proc_obj: PsObject) -> Result<(), PsError> {
370 let f = self.exec_sync_fn.expect("exec_sync not initialized");
371 f(self, proc_obj)
372 }
373
374 pub fn new() -> Self {
377 let mut names = NameTable::new();
378
379 let name_cache = NameCache {
380 n_def: names.intern(b"def"),
381 n_true: names.intern(b"true"),
382 n_false: names.intern(b"false"),
383 n_null: names.intern(b"null"),
384 n_mark: names.intern(b"mark"),
385 n_font_name: names.intern(b"FontName"),
386 n_font_type: names.intern(b"FontType"),
387 n_font_matrix: names.intern(b"FontMatrix"),
388 n_font_bbox: names.intern(b"FontBBox"),
389 n_encoding: names.intern(b"Encoding"),
390 n_char_strings: names.intern(b"CharStrings"),
391 n_private: names.intern(b"Private"),
392 n_fid: names.intern(b"FID"),
393 n_paint_type: names.intern(b"PaintType"),
394 n_subrs: names.intern(b"Subrs"),
395 n_len_iv: names.intern(b"lenIV"),
396 n_notdef: names.intern(b".notdef"),
397 n_metrics: names.intern(b"Metrics"),
398 n_font_directory: names.intern(b"FontDirectory"),
399 n_find_resource: names.intern(b"FindResource"),
401 n_define_resource: names.intern(b"DefineResource"),
402 n_undef_resource: names.intern(b"UndefineResource"),
403 n_resource_status: names.intern(b"ResourceStatus"),
404 n_resource_for_all: names.intern(b"ResourceForAll"),
405 n_category: names.intern(b"Category"),
406 n_instance_type: names.intern(b"InstanceType"),
407 n_resource_dir: names.intern(b"ResourceDir"),
408 n_resource_ext: names.intern(b"ResourceExtension"),
409 n_build_char: names.intern(b"BuildChar"),
410 n_build_glyph: names.intern(b"BuildGlyph"),
411 n_stroke_width: names.intern(b"StrokeWidth"),
412 n_wmode: names.intern(b"WMode"),
413 };
414
415 let mut strings = DualStringStore::new();
416 let mut dicts = DualDictStore::new();
417
418 let systemdict = dicts.allocate_with(400, b"systemdict", 0, true, 0);
422 let globaldict = dicts.allocate_with(100, b"globaldict", 0, true, 0);
423 let userdict = dicts.allocate(200, b"userdict");
424 let errordict = dicts.allocate(50, b"errordict");
425 let dollar_error = dicts.allocate(20, b"$error");
426 let font_directory = dicts.allocate(50, b"FontDirectory");
427
428 let global_resources = dicts.allocate_with(20, b"GlobalResources", 0, true, 0);
430 let local_resources = dicts.allocate(20, b"LocalResources");
431 let category_registry = dicts.allocate_with(30, b"CategoryRegistry", 0, true, 0);
432
433 let user_params = dicts.allocate(25, b"UserParams");
437 for key_name in [
438 "MaxDictStack",
439 "MaxExecStack",
440 "MaxOpStack",
441 "MaxFontItem",
442 "MaxFormItem",
443 "MaxPatternItem",
444 "MaxUPathItem",
445 "MaxScreenItem",
446 "MaxSuperScreen",
447 "MinFontCompress",
448 "MaxLocalVM",
449 "VMReclaim",
450 "VMThreshold",
451 "UCacheBLimit",
452 ] {
453 dicts.put(
454 user_params,
455 DictKey::Name(names.intern(key_name.as_bytes())),
456 PsObject::int(0),
457 );
458 }
459 dicts.put(
460 user_params,
461 DictKey::Name(names.intern(b"JobName")),
462 PsObject::string(strings.allocate_from(b""), 0),
463 );
464 dicts.put(
465 user_params,
466 DictKey::Name(names.intern(b"ExecutionHistory")),
467 PsObject::bool(false),
468 );
469 dicts.put(
470 user_params,
471 DictKey::Name(names.intern(b"ExecutionHistorySize")),
472 PsObject::int(20),
473 );
474 dicts.put(
475 user_params,
476 DictKey::Name(names.intern(b"IdiomRecognition")),
477 PsObject::bool(true),
478 );
479 dicts.put(
480 user_params,
481 DictKey::Name(names.intern(b"AccurateScreens")),
482 PsObject::bool(false),
483 );
484 dicts.put(
485 user_params,
486 DictKey::Name(names.intern(b"HalftoneMode")),
487 PsObject::int(0),
488 );
489
490 let system_params = dicts.allocate(30, b"SystemParams");
491 for (key, val) in [
493 ("MaxFontCache", 67108864),
494 ("MaxFormCache", 131072),
495 ("MaxPatternCache", 131072),
496 ("MaxUPathCache", 131072),
497 ("MaxScreenStorage", 524288),
498 ("MaxDisplayList", 2097152),
499 ("MaxDisplayAndSourceList", 4194304),
500 ("MaxSourceList", 2097152),
501 ("MaxImageBuffer", 524288),
502 ("MaxOutlineCache", 65536),
503 ("MaxStoredScreenCache", 0),
504 ("CurFontCache", 0),
506 ("CurFormCache", 0),
507 ("CurPatternCache", 0),
508 ("CurUPathCache", 0),
509 ("CurScreenStorage", 0),
510 ("CurSourceList", 0),
511 ("CurStoredScreenCache", 0),
512 ("CurOutlineCache", 0),
513 ("PageCount", 0),
514 ("Revision", 1),
515 ] {
516 dicts.put(
517 system_params,
518 DictKey::Name(names.intern(key.as_bytes())),
519 PsObject::int(val),
520 );
521 }
522 let printer_str = strings.allocate_from(b"stet");
523 dicts.put(
524 system_params,
525 DictKey::Name(names.intern(b"PrinterName")),
526 PsObject::string(printer_str, 6),
527 );
528 let realfmt_str = strings.allocate_from(b"IEE");
529 dicts.put(
530 system_params,
531 DictKey::Name(names.intern(b"RealFormat")),
532 PsObject::string(realfmt_str, 3),
533 );
534 let pw_str = strings.allocate_from(b"0");
535 dicts.put(
536 system_params,
537 DictKey::Name(names.intern(b"SystemParamsPassword")),
538 PsObject::string(pw_str, 1),
539 );
540 let pw_str2 = strings.allocate_from(b"0");
541 dicts.put(
542 system_params,
543 DictKey::Name(names.intern(b"StartJobPassword")),
544 PsObject::string(pw_str2, 1),
545 );
546 dicts.put(
547 system_params,
548 DictKey::Name(names.intern(b"LicenseID")),
549 PsObject::int(0),
550 );
551
552 let sd_obj = PsObject::dict(systemdict);
554 dicts.put(
555 systemdict,
556 DictKey::Name(names.intern(b"systemdict")),
557 sd_obj,
558 );
559
560 let ud_obj = PsObject::dict(userdict);
561 dicts.put(systemdict, DictKey::Name(names.intern(b"userdict")), ud_obj);
562
563 let gd_obj = PsObject::dict(globaldict);
564 dicts.put(
565 systemdict,
566 DictKey::Name(names.intern(b"globaldict")),
567 gd_obj,
568 );
569
570 let ed_obj = PsObject::dict(errordict);
571 dicts.put(
572 systemdict,
573 DictKey::Name(names.intern(b"errordict")),
574 ed_obj,
575 );
576
577 let de_obj = PsObject::dict(dollar_error);
578 dicts.put(systemdict, DictKey::Name(names.intern(b"$error")), de_obj);
579
580 let fd_obj = PsObject::dict(font_directory);
581 dicts.put(
582 systemdict,
583 DictKey::Name(name_cache.n_font_directory),
584 fd_obj,
585 );
586
587 dicts.put(
589 systemdict,
590 DictKey::Name(names.intern(b"true")),
591 PsObject::bool(true),
592 );
593 dicts.put(
594 systemdict,
595 DictKey::Name(names.intern(b"false")),
596 PsObject::bool(false),
597 );
598 dicts.put(
599 systemdict,
600 DictKey::Name(names.intern(b"null")),
601 PsObject::null(),
602 );
603
604 dicts.put(
606 systemdict,
607 DictKey::Name(names.intern(b"mark")),
608 PsObject::mark(),
609 );
610
611 dicts.put(
613 systemdict,
614 DictKey::Name(names.intern(b"[")),
615 PsObject::mark(),
616 );
617
618 dicts.put(
620 systemdict,
621 DictKey::Name(names.intern(b"<<")),
622 PsObject::dict_mark(),
623 );
624
625 dicts.put(
627 systemdict,
628 DictKey::Name(names.intern(b"languagelevel")),
629 PsObject::int(3),
630 );
631
632 let d_stack = vec![systemdict, globaldict, userdict];
634
635 Self {
636 o_stack: Stack::new(500),
637 e_stack: Stack::new(250),
638 d_stack,
639 strings,
640 arrays: DualArrayStore::new(),
641 dicts,
642 names,
643 files: FileStore::new(),
644 loops: Vec::new(),
645 operators: Vec::new(),
646 systemdict,
647 globaldict,
648 userdict,
649 errordict,
650 dollar_error,
651 rand_state: 0,
652 rand_seed: 0,
653 current_source_line: 1,
654 packing_mode: false,
655 echo: false,
656 name_cache,
657 stdout: Box::new(std::io::stdout()),
658 save_stack: SaveStack::new(),
659 job_start_save_depth: 0,
660 vm_alloc_mode: false,
661 object_format: 0,
662 current_operator: None,
663 in_error_handler: false,
664 initializing: true,
665 allow_ps_resolution: false,
666 exit_code: None,
667 gstate: GraphicsState::new(),
668 gstate_stack: Vec::new(),
669 gstate_store: Vec::new(),
670 device: None,
671 display_list: DisplayList::new(),
672 group_stack: Vec::new(),
673 save_group_depths: rustc_hash::FxHashMap::default(),
674 ocg_registry: rustc_hash::FxHashMap::default(),
675 next_ocg_id: 0,
676 doc_structure: stet_graphics::document_structure::DocumentStructure::new(),
677 capture_display_lists: None,
678 display_list_sender: None,
679 page_width: 612,
680 page_height: 792,
681 output_path: None,
682 page_filter: None,
683 device_factory: None,
684 font_directory,
685 font_resource_path: None,
686 next_fid: 0,
687 global_resources,
688 local_resources,
689 category_registry,
690 resource_base_path: None,
691 user_params,
692 system_params,
693 internaldict: None,
694 icc_cache: crate::icc::IccCache::new(),
695 exec_sync_fn: None,
696 char_width: None,
697 char_width_mode1: None,
698 glyph_caches: rustc_hash::FxHashMap::default(),
699 char_cache_mode: None,
700 cshow_pending_cid: None,
701 pattern_store: Vec::new(),
702 form_cache: rustc_hash::FxHashMap::default(),
703 #[cfg(not(target_arch = "wasm32"))]
704 start_time: Some(std::time::Instant::now()),
705 #[cfg(target_arch = "wasm32")]
706 start_time: None,
707 dict_version: 0,
708 name_resolve_cache: Vec::new(),
709 interrupt_flag: None,
710 yield_after_showpage: false,
711 }
712 }
713
714 pub fn new_with_output(output: Box<dyn Write>) -> Self {
716 let mut ctx = Self::new();
717 ctx.stdout = output;
718 ctx
719 }
720
721 #[inline]
725 pub fn dict_load(&mut self, key: &DictKey) -> Option<PsObject> {
726 if let DictKey::Name(name_id) = key {
728 let idx = name_id.0 as usize;
729 if idx < self.name_resolve_cache.len() {
730 let (ver, obj) = self.name_resolve_cache[idx];
731 if ver == self.dict_version {
732 return Some(obj);
733 }
734 }
735 }
736
737 for &dict_id in self.d_stack.iter().rev() {
739 if let Some(val) = self.dicts.get(dict_id, key) {
740 if let DictKey::Name(name_id) = key {
742 let idx = name_id.0 as usize;
743 if idx >= self.name_resolve_cache.len() {
744 self.name_resolve_cache
745 .resize(idx + 64, (u64::MAX, PsObject::null()));
746 }
747 self.name_resolve_cache[idx] = (self.dict_version, val);
748 }
749 return Some(val);
750 }
751 }
752 None
753 }
754
755 #[inline]
757 pub fn invalidate_name_cache(&mut self) {
758 self.dict_version = self.dict_version.wrapping_add(1);
759 }
760
761 pub fn dict_where(&self, key: &DictKey) -> Option<(EntityId, PsObject)> {
763 for &dict_id in self.d_stack.iter().rev() {
764 if let Some(val) = self.dicts.get(dict_id, key) {
765 return Some((dict_id, val));
766 }
767 }
768 None
769 }
770
771 pub fn dict_def(&mut self, key: DictKey, value: PsObject) -> Result<(), PsError> {
773 let current = *self.d_stack.last().ok_or(PsError::DictStackUnderflow)?;
774 self.cow_check_dict(current);
775 self.invalidate_name_cache();
776 self.dicts.put(current, key, value);
777 Ok(())
778 }
779
780 pub fn dict_store(&mut self, key: DictKey, value: PsObject) -> Result<(), PsError> {
782 self.invalidate_name_cache();
783 for &dict_id in self.d_stack.iter().rev() {
784 if self.dicts.known(dict_id, &key) {
785 self.cow_check_dict(dict_id);
786 self.dicts.put(dict_id, key, value);
787 return Ok(());
788 }
789 }
790 self.dict_def(key, value)
792 }
793
794 pub fn make_dict_key(&mut self, obj: &PsObject) -> Result<DictKey, PsError> {
796 match obj.value {
797 PsValue::Name(id) => Ok(DictKey::Name(id)),
798 PsValue::Int(v) => Ok(DictKey::Int(v)),
799 PsValue::Real(v) => Ok(DictKey::Real(v.to_bits())),
800 PsValue::Bool(v) => Ok(DictKey::Bool(v)),
801 PsValue::String { entity, start, len } => {
802 let bytes = self.strings.get(entity, start, len).to_vec();
805 let name_id = self.names.intern(&bytes);
806 Ok(DictKey::Name(name_id))
807 }
808 PsValue::Operator(op) => Ok(DictKey::Operator(op.0)),
809 PsValue::Array { entity, start, len } | PsValue::PackedArray { entity, start, len } => {
810 Ok(DictKey::Identity(entity.0, start, len))
811 }
812 PsValue::Dict(entity) => Ok(DictKey::Identity(entity.0, 0, 0)),
813 PsValue::Null => Err(PsError::TypeCheck),
814 _ => Err(PsError::TypeCheck),
815 }
816 }
817
818 pub fn alloc_loop(&mut self, state: LoopState) -> EntityId {
820 let id = EntityId(self.loops.len() as u32);
821 self.loops.push(state);
822 id
823 }
824
825 pub fn get_loop(&self, entity: EntityId) -> &LoopState {
827 &self.loops[entity.0 as usize]
828 }
829
830 pub fn get_loop_mut(&mut self, entity: EntityId) -> &mut LoopState {
832 &mut self.loops[entity.0 as usize]
833 }
834
835 #[inline]
843 pub fn current_display_list_mut(&mut self) -> &mut DisplayList {
844 if let Some(frame) = self.group_stack.last_mut() {
845 &mut frame.display_list
846 } else {
847 &mut self.display_list
848 }
849 }
850
851 #[inline]
853 pub fn current_display_list(&self) -> &DisplayList {
854 if let Some(frame) = self.group_stack.last() {
855 &frame.display_list
856 } else {
857 &self.display_list
858 }
859 }
860
861 pub fn take_display_list(&mut self) -> DisplayList {
867 if self.capture_display_lists.is_some() {
868 let dpi = self.current_page_dpi();
869 if let Some(ref mut captures) = self.capture_display_lists {
870 captures.push((self.display_list.clone(), dpi));
871 }
872 }
873 if let Some(ref sender) = self.display_list_sender {
874 let dpi = self.current_page_dpi();
875 let (w, h) = self
878 .device
879 .as_ref()
880 .map(|d| d.page_size())
881 .unwrap_or((self.page_width, self.page_height));
882 let _ = sender.send((self.display_list.clone(), dpi, w, h, None, false));
886 }
887 if self.yield_after_showpage
891 && let Some(ref flag) = self.interrupt_flag
892 {
893 flag.store(true, std::sync::atomic::Ordering::Relaxed);
894 }
895 std::mem::take(&mut self.display_list)
896 }
897
898 pub fn current_page_dpi(&self) -> f64 {
900 use crate::dict::DictKey;
901 if let Some(pd) = self.gstate.page_device
902 && let Some(name_id) = self.names.find(b"HWResolution")
903 && let Some(obj) = self.dicts.get(pd, &DictKey::Name(name_id))
904 && let PsValue::Array { entity, .. } = obj.value
905 {
906 let first = self.arrays.get_element(entity, 0);
907 return match first.value {
908 PsValue::Real(r) => r,
909 PsValue::Int(i) => i as f64,
910 _ => 72.0,
911 };
912 }
913 72.0
914 }
915
916 pub fn vm_save(&mut self) -> PsObject {
921 let d_depth = self.d_stack.len();
922 let gstate_snapshot = self.gstate.clone();
923 let gstate_stack_snapshot = self.gstate_stack.clone();
924 let (_level, save_id) = self.save_stack.save(
925 d_depth,
926 self.packing_mode,
927 self.vm_alloc_mode,
928 self.object_format,
929 gstate_snapshot,
930 gstate_stack_snapshot,
931 );
932
933 self.gstate_stack.push(crate::graphics_state::GstateEntry {
936 state: self.gstate.clone(),
937 saved_by_save: true,
938 });
939
940 PsObject {
941 value: PsValue::Save(SaveLevel(save_id)),
942 flags: crate::object::ObjFlags::literal(),
943 }
944 }
945
946 pub fn vm_restore(&mut self, save_id: u32) -> Result<(), PsError> {
948 if !self.save_stack.is_valid(save_id) {
950 return Err(PsError::InvalidRestore);
951 }
952
953 let levels = self
958 .save_stack
959 .restore_to(save_id)
960 .ok_or(PsError::InvalidRestore)?;
961
962 for level in levels.iter().rev() {
967 for record in level.records.iter().rev() {
968 match record.store_type {
969 StoreType::String => {
970 self.strings.swap_offsets(record.src, record.copy);
971 self.strings.entity_meta_mut(record.src).save_level = 0;
972 }
973 StoreType::Array => {
974 self.arrays.swap_offsets(record.src, record.copy);
975 self.arrays.entity_meta_mut(record.src).save_level = 0;
976 }
977 StoreType::Dict => {
978 self.dicts.swap_offsets(record.src, record.copy);
979 self.dicts.entity_meta_mut(record.src).save_level = 0;
980 }
981 }
982 }
983 }
984
985 let target = &levels[0];
987 self.packing_mode = target.packing_mode;
988 self.vm_alloc_mode = target.vm_alloc_mode;
989 self.object_format = target.object_format;
990
991 self.gstate = target.gstate.clone();
993 self.gstate_stack = target.gstate_stack.clone();
994
995 self.d_stack.truncate(target.d_stack_depth);
997
998 self.invalidate_name_cache();
999 Ok(())
1000 }
1001
1002 pub fn cow_check_string(&mut self, entity: EntityId) {
1007 let current_level = self.save_stack.current_level();
1008 if current_level == 0 {
1009 return; }
1011
1012 if entity.is_global() {
1013 return; }
1015 let meta = self.strings.entity_meta(entity);
1016 if meta.save_level >= current_level {
1017 return; }
1019
1020 let copy_id = self.strings.cow_copy(entity);
1022 self.strings.entity_meta_mut(entity).save_level = current_level;
1023
1024 self.save_stack.add_record(SaveRecord {
1025 src: entity,
1026 copy: copy_id,
1027 store_type: StoreType::String,
1028 });
1029 }
1030
1031 pub fn cow_check_array(&mut self, entity: EntityId) {
1033 let current_level = self.save_stack.current_level();
1034 if current_level == 0 {
1035 return;
1036 }
1037
1038 if entity.is_global() {
1039 return;
1040 }
1041 let meta = self.arrays.entity_meta(entity);
1042 if meta.save_level >= current_level {
1043 return;
1044 }
1045
1046 let copy_id = self.arrays.cow_copy(entity);
1047 self.arrays.entity_meta_mut(entity).save_level = current_level;
1048
1049 self.save_stack.add_record(SaveRecord {
1050 src: entity,
1051 copy: copy_id,
1052 store_type: StoreType::Array,
1053 });
1054 }
1055
1056 pub fn cow_check_dict(&mut self, entity: EntityId) {
1058 let current_level = self.save_stack.current_level();
1059 if current_level == 0 {
1060 return;
1061 }
1062
1063 if entity.is_global() {
1064 return;
1065 }
1066 let meta = self.dicts.entity_meta(entity);
1067 if meta.save_level >= current_level {
1068 return;
1069 }
1070
1071 let copy_id = self.dicts.cow_copy(entity);
1072 self.dicts.entity_meta_mut(entity).save_level = current_level;
1073
1074 self.save_stack.add_record(SaveRecord {
1075 src: entity,
1076 copy: copy_id,
1077 store_type: StoreType::Dict,
1078 });
1079 }
1080
1081 pub fn token_to_object(&mut self, token: crate::tokenizer::Token) -> Result<PsObject, PsError> {
1085 use crate::tokenizer::Token;
1086 match token {
1087 Token::Int(v) => Ok(PsObject::int(v)),
1088 Token::Real(v) => Ok(PsObject::real(v)),
1089 Token::String(bytes) => {
1090 let save_level = self.save_stack.current_level();
1091 let global = self.vm_alloc_mode;
1092 let created = self.save_stack.last_save_id();
1093 let entity = self
1094 .strings
1095 .allocate_with(bytes.len(), save_level, global, created);
1096 self.strings
1097 .get_mut(entity, 0, bytes.len() as u32)
1098 .copy_from_slice(&bytes);
1099 let mut obj = PsObject::string(entity, bytes.len() as u32);
1100 if global {
1101 obj.flags = ObjFlags::new(ObjFlags::ACCESS_UNLIMITED, false, true, true);
1102 }
1103 Ok(obj)
1104 }
1105 Token::Name(bytes, is_exec) => {
1106 let id = self.names.intern(&bytes);
1107 if is_exec {
1108 Ok(PsObject::name_exec(id))
1109 } else {
1110 Ok(PsObject::name_lit(id))
1111 }
1112 }
1113 Token::LiteralName(bytes) => {
1114 let id = self.names.intern(&bytes);
1115 Ok(PsObject::name_lit(id))
1116 }
1117 Token::ImmediateName(bytes) => {
1118 let id = self.names.intern(&bytes);
1119 let key = DictKey::Name(id);
1120 self.dict_load(&key).ok_or(PsError::Undefined)
1121 }
1122 Token::ArrayBegin => {
1123 let id = self.names.intern(b"[");
1124 Ok(PsObject::name_exec(id))
1125 }
1126 Token::ArrayEnd => {
1127 let id = self.names.intern(b"]");
1128 Ok(PsObject::name_exec(id))
1129 }
1130 Token::DictBegin => {
1131 let id = self.names.intern(b"<<");
1132 Ok(PsObject::name_exec(id))
1133 }
1134 Token::DictEnd => {
1135 let id = self.names.intern(b">>");
1136 Ok(PsObject::name_exec(id))
1137 }
1138 Token::ProcBegin | Token::ProcEnd | Token::Eof | Token::BinaryTokenByte(_) => {
1139 Err(PsError::SyntaxError)
1140 }
1141 }
1142 }
1143
1144 pub fn reset_local_vm(&mut self) {
1147 self.strings.reset_local();
1148 self.arrays.reset_local();
1149 self.dicts.reset_local();
1150 }
1151}
1152
1153impl Default for Context {
1154 fn default() -> Self {
1155 Self::new()
1156 }
1157}
1158
1159#[cfg(test)]
1160mod tests {
1161 use super::*;
1162
1163 #[test]
1164 fn test_context_creation() {
1165 let ctx = Context::new();
1166 assert!(ctx.o_stack.is_empty());
1167 assert!(ctx.e_stack.is_empty());
1168 assert_eq!(ctx.d_stack.len(), 3); }
1170
1171 #[test]
1172 fn test_dict_def_and_load() {
1173 let mut ctx = Context::new();
1174 let key = DictKey::Name(ctx.names.intern(b"foo"));
1175 ctx.dict_def(key.clone(), PsObject::int(42)).unwrap();
1176
1177 let val = ctx.dict_load(&key).unwrap();
1178 assert_eq!(val.as_i32(), Some(42));
1179 }
1180
1181 #[test]
1182 fn test_dict_where() {
1183 let mut ctx = Context::new();
1184 let key = DictKey::Name(ctx.names.intern(b"true"));
1185 let result = ctx.dict_where(&key);
1186 assert!(result.is_some());
1187 let (dict_id, val) = result.unwrap();
1188 assert_eq!(dict_id, ctx.systemdict);
1189 assert!(matches!(val.value, PsValue::Bool(true)));
1190 }
1191
1192 #[test]
1193 fn test_dict_store_existing() {
1194 let mut ctx = Context::new();
1195 let key = DictKey::Name(ctx.names.intern(b"myvar"));
1196
1197 ctx.dict_def(key.clone(), PsObject::int(1)).unwrap();
1199
1200 ctx.dict_store(key.clone(), PsObject::int(2)).unwrap();
1202
1203 let val = ctx.dict_load(&key).unwrap();
1204 assert_eq!(val.as_i32(), Some(2));
1205 }
1206
1207 #[test]
1208 fn test_save_restore_basic() {
1209 let mut ctx = Context::new();
1210 let key = DictKey::Name(ctx.names.intern(b"testvar"));
1211
1212 ctx.dict_def(key.clone(), PsObject::int(1)).unwrap();
1214
1215 let save_obj = ctx.vm_save();
1217 let save_id = match save_obj.value {
1218 PsValue::Save(SaveLevel(id)) => id,
1219 _ => panic!("Expected Save"),
1220 };
1221
1222 ctx.dict_def(key.clone(), PsObject::int(2)).unwrap();
1224 assert_eq!(ctx.dict_load(&key).unwrap().as_i32(), Some(2));
1225
1226 ctx.vm_restore(save_id).unwrap();
1228 assert_eq!(ctx.dict_load(&key).unwrap().as_i32(), Some(1));
1229 }
1230
1231 #[test]
1232 fn test_save_restore_string() {
1233 let mut ctx = Context::new();
1234
1235 let entity = ctx.strings.allocate_from(b"hello");
1236
1237 let save_obj = ctx.vm_save();
1239 let save_id = match save_obj.value {
1240 PsValue::Save(SaveLevel(id)) => id,
1241 _ => panic!("Expected Save"),
1242 };
1243
1244 ctx.cow_check_string(entity);
1246 ctx.strings.put_byte(entity, 0, b'H');
1247 assert_eq!(ctx.strings.get(entity, 0, 5), b"Hello");
1248
1249 ctx.vm_restore(save_id).unwrap();
1251 assert_eq!(ctx.strings.get(entity, 0, 5), b"hello");
1252 }
1253
1254 #[test]
1255 fn test_save_restore_array() {
1256 let mut ctx = Context::new();
1257
1258 let items = [PsObject::int(1), PsObject::int(2), PsObject::int(3)];
1259 let entity = ctx.arrays.allocate_from(&items);
1260
1261 let save_obj = ctx.vm_save();
1262 let save_id = match save_obj.value {
1263 PsValue::Save(SaveLevel(id)) => id,
1264 _ => panic!("Expected Save"),
1265 };
1266
1267 ctx.cow_check_array(entity);
1268 ctx.arrays.set_element(entity, 1, PsObject::int(99));
1269 assert_eq!(ctx.arrays.get_element(entity, 1).as_i32(), Some(99));
1270
1271 ctx.vm_restore(save_id).unwrap();
1272 assert_eq!(ctx.arrays.get_element(entity, 1).as_i32(), Some(2));
1273 }
1274
1275 #[test]
1276 fn test_invalid_restore() {
1277 let mut ctx = Context::new();
1278 assert_eq!(ctx.vm_restore(999), Err(PsError::InvalidRestore));
1280 }
1281}