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 pdfmark_buffer: crate::pdfmark::PdfMarkBuffer,
208 pub capture_display_lists: Option<Vec<(DisplayList, f64)>>,
212 pub display_list_sender: Option<
219 std::sync::mpsc::Sender<(
220 DisplayList,
221 f64,
222 u32,
223 u32,
224 Option<std::sync::Arc<Vec<u8>>>,
225 bool,
226 )>,
227 >,
228 pub page_width: u32,
229 pub page_height: u32,
230 pub output_path: Option<String>,
231 pub page_filter: Option<std::collections::HashSet<i32>>,
233 #[allow(clippy::type_complexity)]
235 pub device_factory: Option<Box<dyn Fn(u32, u32) -> Box<dyn OutputDevice>>>,
236
237 pub font_directory: EntityId,
239 pub font_resource_path: Option<String>,
240 pub next_fid: i32,
241
242 pub global_resources: EntityId,
244 pub local_resources: EntityId,
245 pub category_registry: EntityId,
246 pub resource_base_path: Option<String>,
247
248 pub user_params: EntityId,
250 pub system_params: EntityId,
251
252 pub internaldict: Option<EntityId>,
254
255 pub icc_cache: crate::icc::IccCache,
257
258 pub exec_sync_fn: Option<ExecSyncFn>,
260
261 pub char_width: Option<(f64, f64)>,
263 pub char_width_mode1: Option<((f64, f64), (f64, f64))>,
265
266 pub glyph_caches: rustc_hash::FxHashMap<EntityId, crate::glyph_cache::GlyphCache>,
268 pub char_cache_mode: Option<crate::glyph_cache::Type3CacheMode>,
270
271 pub cshow_pending_cid: Option<i32>,
273
274 pub pattern_store: Vec<PatternData>,
277 pub form_cache: rustc_hash::FxHashMap<EntityId, DisplayList>,
279
280 pub start_time: Option<std::time::Instant>,
282
283 pub dict_version: u64,
285 pub name_resolve_cache: Vec<(u64, PsObject)>,
288
289 pub interrupt_flag: Option<std::sync::Arc<std::sync::atomic::AtomicBool>>,
293
294 pub yield_after_showpage: bool,
301}
302
303pub struct GroupFrame {
308 pub display_list: DisplayList,
311 pub kind: GroupKind,
314 pub saved_clip_path_version: u32,
318 pub saved_gsave_depth: usize,
322}
323
324pub enum GroupKind {
327 Transparency { params: GroupParams },
331 SoftMask { params: SoftMaskParams },
335 Masked {
341 mask: DisplayList,
342 params: SoftMaskParams,
343 },
344 OptionalContent { ocg_id: u32, default_visible: bool },
349}
350
351#[derive(Clone, Debug)]
356pub struct OcgRecord {
357 pub ocg_id: u32,
360 pub default_visible: bool,
363}
364
365impl Context {
366 pub fn exec_sync(&mut self, proc_obj: PsObject) -> Result<(), PsError> {
368 let f = self.exec_sync_fn.expect("exec_sync not initialized");
369 f(self, proc_obj)
370 }
371
372 pub fn new() -> Self {
375 let mut names = NameTable::new();
376
377 let name_cache = NameCache {
378 n_def: names.intern(b"def"),
379 n_true: names.intern(b"true"),
380 n_false: names.intern(b"false"),
381 n_null: names.intern(b"null"),
382 n_mark: names.intern(b"mark"),
383 n_font_name: names.intern(b"FontName"),
384 n_font_type: names.intern(b"FontType"),
385 n_font_matrix: names.intern(b"FontMatrix"),
386 n_font_bbox: names.intern(b"FontBBox"),
387 n_encoding: names.intern(b"Encoding"),
388 n_char_strings: names.intern(b"CharStrings"),
389 n_private: names.intern(b"Private"),
390 n_fid: names.intern(b"FID"),
391 n_paint_type: names.intern(b"PaintType"),
392 n_subrs: names.intern(b"Subrs"),
393 n_len_iv: names.intern(b"lenIV"),
394 n_notdef: names.intern(b".notdef"),
395 n_metrics: names.intern(b"Metrics"),
396 n_font_directory: names.intern(b"FontDirectory"),
397 n_find_resource: names.intern(b"FindResource"),
399 n_define_resource: names.intern(b"DefineResource"),
400 n_undef_resource: names.intern(b"UndefineResource"),
401 n_resource_status: names.intern(b"ResourceStatus"),
402 n_resource_for_all: names.intern(b"ResourceForAll"),
403 n_category: names.intern(b"Category"),
404 n_instance_type: names.intern(b"InstanceType"),
405 n_resource_dir: names.intern(b"ResourceDir"),
406 n_resource_ext: names.intern(b"ResourceExtension"),
407 n_build_char: names.intern(b"BuildChar"),
408 n_build_glyph: names.intern(b"BuildGlyph"),
409 n_stroke_width: names.intern(b"StrokeWidth"),
410 n_wmode: names.intern(b"WMode"),
411 };
412
413 let mut strings = DualStringStore::new();
414 let mut dicts = DualDictStore::new();
415
416 let systemdict = dicts.allocate_with(400, b"systemdict", 0, true, 0);
420 let globaldict = dicts.allocate_with(100, b"globaldict", 0, true, 0);
421 let userdict = dicts.allocate(200, b"userdict");
422 let errordict = dicts.allocate(50, b"errordict");
423 let dollar_error = dicts.allocate(20, b"$error");
424 let font_directory = dicts.allocate(50, b"FontDirectory");
425
426 let global_resources = dicts.allocate_with(20, b"GlobalResources", 0, true, 0);
428 let local_resources = dicts.allocate(20, b"LocalResources");
429 let category_registry = dicts.allocate_with(30, b"CategoryRegistry", 0, true, 0);
430
431 let user_params = dicts.allocate(25, b"UserParams");
435 for key_name in [
436 "MaxDictStack",
437 "MaxExecStack",
438 "MaxOpStack",
439 "MaxFontItem",
440 "MaxFormItem",
441 "MaxPatternItem",
442 "MaxUPathItem",
443 "MaxScreenItem",
444 "MaxSuperScreen",
445 "MinFontCompress",
446 "MaxLocalVM",
447 "VMReclaim",
448 "VMThreshold",
449 "UCacheBLimit",
450 ] {
451 dicts.put(
452 user_params,
453 DictKey::Name(names.intern(key_name.as_bytes())),
454 PsObject::int(0),
455 );
456 }
457 dicts.put(
458 user_params,
459 DictKey::Name(names.intern(b"JobName")),
460 PsObject::string(strings.allocate_from(b""), 0),
461 );
462 dicts.put(
463 user_params,
464 DictKey::Name(names.intern(b"ExecutionHistory")),
465 PsObject::bool(false),
466 );
467 dicts.put(
468 user_params,
469 DictKey::Name(names.intern(b"ExecutionHistorySize")),
470 PsObject::int(20),
471 );
472 dicts.put(
473 user_params,
474 DictKey::Name(names.intern(b"IdiomRecognition")),
475 PsObject::bool(true),
476 );
477 dicts.put(
478 user_params,
479 DictKey::Name(names.intern(b"AccurateScreens")),
480 PsObject::bool(false),
481 );
482 dicts.put(
483 user_params,
484 DictKey::Name(names.intern(b"HalftoneMode")),
485 PsObject::int(0),
486 );
487
488 let system_params = dicts.allocate(30, b"SystemParams");
489 for (key, val) in [
491 ("MaxFontCache", 67108864),
492 ("MaxFormCache", 131072),
493 ("MaxPatternCache", 131072),
494 ("MaxUPathCache", 131072),
495 ("MaxScreenStorage", 524288),
496 ("MaxDisplayList", 2097152),
497 ("MaxDisplayAndSourceList", 4194304),
498 ("MaxSourceList", 2097152),
499 ("MaxImageBuffer", 524288),
500 ("MaxOutlineCache", 65536),
501 ("MaxStoredScreenCache", 0),
502 ("CurFontCache", 0),
504 ("CurFormCache", 0),
505 ("CurPatternCache", 0),
506 ("CurUPathCache", 0),
507 ("CurScreenStorage", 0),
508 ("CurSourceList", 0),
509 ("CurStoredScreenCache", 0),
510 ("CurOutlineCache", 0),
511 ("PageCount", 0),
512 ("Revision", 1),
513 ] {
514 dicts.put(
515 system_params,
516 DictKey::Name(names.intern(key.as_bytes())),
517 PsObject::int(val),
518 );
519 }
520 let printer_str = strings.allocate_from(b"stet");
521 dicts.put(
522 system_params,
523 DictKey::Name(names.intern(b"PrinterName")),
524 PsObject::string(printer_str, 6),
525 );
526 let realfmt_str = strings.allocate_from(b"IEE");
527 dicts.put(
528 system_params,
529 DictKey::Name(names.intern(b"RealFormat")),
530 PsObject::string(realfmt_str, 3),
531 );
532 let pw_str = strings.allocate_from(b"0");
533 dicts.put(
534 system_params,
535 DictKey::Name(names.intern(b"SystemParamsPassword")),
536 PsObject::string(pw_str, 1),
537 );
538 let pw_str2 = strings.allocate_from(b"0");
539 dicts.put(
540 system_params,
541 DictKey::Name(names.intern(b"StartJobPassword")),
542 PsObject::string(pw_str2, 1),
543 );
544 dicts.put(
545 system_params,
546 DictKey::Name(names.intern(b"LicenseID")),
547 PsObject::int(0),
548 );
549
550 let sd_obj = PsObject::dict(systemdict);
552 dicts.put(
553 systemdict,
554 DictKey::Name(names.intern(b"systemdict")),
555 sd_obj,
556 );
557
558 let ud_obj = PsObject::dict(userdict);
559 dicts.put(systemdict, DictKey::Name(names.intern(b"userdict")), ud_obj);
560
561 let gd_obj = PsObject::dict(globaldict);
562 dicts.put(
563 systemdict,
564 DictKey::Name(names.intern(b"globaldict")),
565 gd_obj,
566 );
567
568 let ed_obj = PsObject::dict(errordict);
569 dicts.put(
570 systemdict,
571 DictKey::Name(names.intern(b"errordict")),
572 ed_obj,
573 );
574
575 let de_obj = PsObject::dict(dollar_error);
576 dicts.put(systemdict, DictKey::Name(names.intern(b"$error")), de_obj);
577
578 let fd_obj = PsObject::dict(font_directory);
579 dicts.put(
580 systemdict,
581 DictKey::Name(name_cache.n_font_directory),
582 fd_obj,
583 );
584
585 dicts.put(
587 systemdict,
588 DictKey::Name(names.intern(b"true")),
589 PsObject::bool(true),
590 );
591 dicts.put(
592 systemdict,
593 DictKey::Name(names.intern(b"false")),
594 PsObject::bool(false),
595 );
596 dicts.put(
597 systemdict,
598 DictKey::Name(names.intern(b"null")),
599 PsObject::null(),
600 );
601
602 dicts.put(
604 systemdict,
605 DictKey::Name(names.intern(b"mark")),
606 PsObject::mark(),
607 );
608
609 dicts.put(
611 systemdict,
612 DictKey::Name(names.intern(b"[")),
613 PsObject::mark(),
614 );
615
616 dicts.put(
618 systemdict,
619 DictKey::Name(names.intern(b"<<")),
620 PsObject::dict_mark(),
621 );
622
623 dicts.put(
625 systemdict,
626 DictKey::Name(names.intern(b"languagelevel")),
627 PsObject::int(3),
628 );
629
630 let d_stack = vec![systemdict, globaldict, userdict];
632
633 Self {
634 o_stack: Stack::new(500),
635 e_stack: Stack::new(250),
636 d_stack,
637 strings,
638 arrays: DualArrayStore::new(),
639 dicts,
640 names,
641 files: FileStore::new(),
642 loops: Vec::new(),
643 operators: Vec::new(),
644 systemdict,
645 globaldict,
646 userdict,
647 errordict,
648 dollar_error,
649 rand_state: 0,
650 rand_seed: 0,
651 current_source_line: 1,
652 packing_mode: false,
653 echo: false,
654 name_cache,
655 stdout: Box::new(std::io::stdout()),
656 save_stack: SaveStack::new(),
657 job_start_save_depth: 0,
658 vm_alloc_mode: false,
659 object_format: 0,
660 current_operator: None,
661 in_error_handler: false,
662 initializing: true,
663 allow_ps_resolution: false,
664 exit_code: None,
665 gstate: GraphicsState::new(),
666 gstate_stack: Vec::new(),
667 gstate_store: Vec::new(),
668 device: None,
669 display_list: DisplayList::new(),
670 group_stack: Vec::new(),
671 save_group_depths: rustc_hash::FxHashMap::default(),
672 ocg_registry: rustc_hash::FxHashMap::default(),
673 next_ocg_id: 0,
674 pdfmark_buffer: crate::pdfmark::PdfMarkBuffer::new(),
675 capture_display_lists: None,
676 display_list_sender: None,
677 page_width: 612,
678 page_height: 792,
679 output_path: None,
680 page_filter: None,
681 device_factory: None,
682 font_directory,
683 font_resource_path: None,
684 next_fid: 0,
685 global_resources,
686 local_resources,
687 category_registry,
688 resource_base_path: None,
689 user_params,
690 system_params,
691 internaldict: None,
692 icc_cache: crate::icc::IccCache::new(),
693 exec_sync_fn: None,
694 char_width: None,
695 char_width_mode1: None,
696 glyph_caches: rustc_hash::FxHashMap::default(),
697 char_cache_mode: None,
698 cshow_pending_cid: None,
699 pattern_store: Vec::new(),
700 form_cache: rustc_hash::FxHashMap::default(),
701 #[cfg(not(target_arch = "wasm32"))]
702 start_time: Some(std::time::Instant::now()),
703 #[cfg(target_arch = "wasm32")]
704 start_time: None,
705 dict_version: 0,
706 name_resolve_cache: Vec::new(),
707 interrupt_flag: None,
708 yield_after_showpage: false,
709 }
710 }
711
712 pub fn new_with_output(output: Box<dyn Write>) -> Self {
714 let mut ctx = Self::new();
715 ctx.stdout = output;
716 ctx
717 }
718
719 #[inline]
723 pub fn dict_load(&mut self, key: &DictKey) -> Option<PsObject> {
724 if let DictKey::Name(name_id) = key {
726 let idx = name_id.0 as usize;
727 if idx < self.name_resolve_cache.len() {
728 let (ver, obj) = self.name_resolve_cache[idx];
729 if ver == self.dict_version {
730 return Some(obj);
731 }
732 }
733 }
734
735 for &dict_id in self.d_stack.iter().rev() {
737 if let Some(val) = self.dicts.get(dict_id, key) {
738 if let DictKey::Name(name_id) = key {
740 let idx = name_id.0 as usize;
741 if idx >= self.name_resolve_cache.len() {
742 self.name_resolve_cache
743 .resize(idx + 64, (u64::MAX, PsObject::null()));
744 }
745 self.name_resolve_cache[idx] = (self.dict_version, val);
746 }
747 return Some(val);
748 }
749 }
750 None
751 }
752
753 #[inline]
755 pub fn invalidate_name_cache(&mut self) {
756 self.dict_version = self.dict_version.wrapping_add(1);
757 }
758
759 pub fn dict_where(&self, key: &DictKey) -> Option<(EntityId, PsObject)> {
761 for &dict_id in self.d_stack.iter().rev() {
762 if let Some(val) = self.dicts.get(dict_id, key) {
763 return Some((dict_id, val));
764 }
765 }
766 None
767 }
768
769 pub fn dict_def(&mut self, key: DictKey, value: PsObject) -> Result<(), PsError> {
771 let current = *self.d_stack.last().ok_or(PsError::DictStackUnderflow)?;
772 self.cow_check_dict(current);
773 self.invalidate_name_cache();
774 self.dicts.put(current, key, value);
775 Ok(())
776 }
777
778 pub fn dict_store(&mut self, key: DictKey, value: PsObject) -> Result<(), PsError> {
780 self.invalidate_name_cache();
781 for &dict_id in self.d_stack.iter().rev() {
782 if self.dicts.known(dict_id, &key) {
783 self.cow_check_dict(dict_id);
784 self.dicts.put(dict_id, key, value);
785 return Ok(());
786 }
787 }
788 self.dict_def(key, value)
790 }
791
792 pub fn make_dict_key(&mut self, obj: &PsObject) -> Result<DictKey, PsError> {
794 match obj.value {
795 PsValue::Name(id) => Ok(DictKey::Name(id)),
796 PsValue::Int(v) => Ok(DictKey::Int(v)),
797 PsValue::Real(v) => Ok(DictKey::Real(v.to_bits())),
798 PsValue::Bool(v) => Ok(DictKey::Bool(v)),
799 PsValue::String { entity, start, len } => {
800 let bytes = self.strings.get(entity, start, len).to_vec();
803 let name_id = self.names.intern(&bytes);
804 Ok(DictKey::Name(name_id))
805 }
806 PsValue::Operator(op) => Ok(DictKey::Operator(op.0)),
807 PsValue::Array { entity, start, len } | PsValue::PackedArray { entity, start, len } => {
808 Ok(DictKey::Identity(entity.0, start, len))
809 }
810 PsValue::Dict(entity) => Ok(DictKey::Identity(entity.0, 0, 0)),
811 PsValue::Null => Err(PsError::TypeCheck),
812 _ => Err(PsError::TypeCheck),
813 }
814 }
815
816 pub fn alloc_loop(&mut self, state: LoopState) -> EntityId {
818 let id = EntityId(self.loops.len() as u32);
819 self.loops.push(state);
820 id
821 }
822
823 pub fn get_loop(&self, entity: EntityId) -> &LoopState {
825 &self.loops[entity.0 as usize]
826 }
827
828 pub fn get_loop_mut(&mut self, entity: EntityId) -> &mut LoopState {
830 &mut self.loops[entity.0 as usize]
831 }
832
833 #[inline]
841 pub fn current_display_list_mut(&mut self) -> &mut DisplayList {
842 if let Some(frame) = self.group_stack.last_mut() {
843 &mut frame.display_list
844 } else {
845 &mut self.display_list
846 }
847 }
848
849 #[inline]
851 pub fn current_display_list(&self) -> &DisplayList {
852 if let Some(frame) = self.group_stack.last() {
853 &frame.display_list
854 } else {
855 &self.display_list
856 }
857 }
858
859 pub fn take_display_list(&mut self) -> DisplayList {
865 if self.capture_display_lists.is_some() {
866 let dpi = self.current_page_dpi();
867 if let Some(ref mut captures) = self.capture_display_lists {
868 captures.push((self.display_list.clone(), dpi));
869 }
870 }
871 if let Some(ref sender) = self.display_list_sender {
872 let dpi = self.current_page_dpi();
873 let (w, h) = self
876 .device
877 .as_ref()
878 .map(|d| d.page_size())
879 .unwrap_or((self.page_width, self.page_height));
880 let _ = sender.send((self.display_list.clone(), dpi, w, h, None, false));
884 }
885 if self.yield_after_showpage
889 && let Some(ref flag) = self.interrupt_flag
890 {
891 flag.store(true, std::sync::atomic::Ordering::Relaxed);
892 }
893 std::mem::take(&mut self.display_list)
894 }
895
896 pub fn current_page_dpi(&self) -> f64 {
898 use crate::dict::DictKey;
899 if let Some(pd) = self.gstate.page_device
900 && let Some(name_id) = self.names.find(b"HWResolution")
901 && let Some(obj) = self.dicts.get(pd, &DictKey::Name(name_id))
902 && let PsValue::Array { entity, .. } = obj.value
903 {
904 let first = self.arrays.get_element(entity, 0);
905 return match first.value {
906 PsValue::Real(r) => r,
907 PsValue::Int(i) => i as f64,
908 _ => 72.0,
909 };
910 }
911 72.0
912 }
913
914 pub fn vm_save(&mut self) -> PsObject {
919 let d_depth = self.d_stack.len();
920 let gstate_snapshot = self.gstate.clone();
921 let gstate_stack_snapshot = self.gstate_stack.clone();
922 let (_level, save_id) = self.save_stack.save(
923 d_depth,
924 self.packing_mode,
925 self.vm_alloc_mode,
926 self.object_format,
927 gstate_snapshot,
928 gstate_stack_snapshot,
929 );
930
931 self.gstate_stack.push(crate::graphics_state::GstateEntry {
934 state: self.gstate.clone(),
935 saved_by_save: true,
936 });
937
938 PsObject {
939 value: PsValue::Save(SaveLevel(save_id)),
940 flags: crate::object::ObjFlags::literal(),
941 }
942 }
943
944 pub fn vm_restore(&mut self, save_id: u32) -> Result<(), PsError> {
946 if !self.save_stack.is_valid(save_id) {
948 return Err(PsError::InvalidRestore);
949 }
950
951 let levels = self
956 .save_stack
957 .restore_to(save_id)
958 .ok_or(PsError::InvalidRestore)?;
959
960 for level in levels.iter().rev() {
965 for record in level.records.iter().rev() {
966 match record.store_type {
967 StoreType::String => {
968 self.strings.swap_offsets(record.src, record.copy);
969 self.strings.entity_meta_mut(record.src).save_level = 0;
970 }
971 StoreType::Array => {
972 self.arrays.swap_offsets(record.src, record.copy);
973 self.arrays.entity_meta_mut(record.src).save_level = 0;
974 }
975 StoreType::Dict => {
976 self.dicts.swap_offsets(record.src, record.copy);
977 self.dicts.entity_meta_mut(record.src).save_level = 0;
978 }
979 }
980 }
981 }
982
983 let target = &levels[0];
985 self.packing_mode = target.packing_mode;
986 self.vm_alloc_mode = target.vm_alloc_mode;
987 self.object_format = target.object_format;
988
989 self.gstate = target.gstate.clone();
991 self.gstate_stack = target.gstate_stack.clone();
992
993 self.d_stack.truncate(target.d_stack_depth);
995
996 self.invalidate_name_cache();
997 Ok(())
998 }
999
1000 pub fn cow_check_string(&mut self, entity: EntityId) {
1005 let current_level = self.save_stack.current_level();
1006 if current_level == 0 {
1007 return; }
1009
1010 if entity.is_global() {
1011 return; }
1013 let meta = self.strings.entity_meta(entity);
1014 if meta.save_level >= current_level {
1015 return; }
1017
1018 let copy_id = self.strings.cow_copy(entity);
1020 self.strings.entity_meta_mut(entity).save_level = current_level;
1021
1022 self.save_stack.add_record(SaveRecord {
1023 src: entity,
1024 copy: copy_id,
1025 store_type: StoreType::String,
1026 });
1027 }
1028
1029 pub fn cow_check_array(&mut self, entity: EntityId) {
1031 let current_level = self.save_stack.current_level();
1032 if current_level == 0 {
1033 return;
1034 }
1035
1036 if entity.is_global() {
1037 return;
1038 }
1039 let meta = self.arrays.entity_meta(entity);
1040 if meta.save_level >= current_level {
1041 return;
1042 }
1043
1044 let copy_id = self.arrays.cow_copy(entity);
1045 self.arrays.entity_meta_mut(entity).save_level = current_level;
1046
1047 self.save_stack.add_record(SaveRecord {
1048 src: entity,
1049 copy: copy_id,
1050 store_type: StoreType::Array,
1051 });
1052 }
1053
1054 pub fn cow_check_dict(&mut self, entity: EntityId) {
1056 let current_level = self.save_stack.current_level();
1057 if current_level == 0 {
1058 return;
1059 }
1060
1061 if entity.is_global() {
1062 return;
1063 }
1064 let meta = self.dicts.entity_meta(entity);
1065 if meta.save_level >= current_level {
1066 return;
1067 }
1068
1069 let copy_id = self.dicts.cow_copy(entity);
1070 self.dicts.entity_meta_mut(entity).save_level = current_level;
1071
1072 self.save_stack.add_record(SaveRecord {
1073 src: entity,
1074 copy: copy_id,
1075 store_type: StoreType::Dict,
1076 });
1077 }
1078
1079 pub fn token_to_object(&mut self, token: crate::tokenizer::Token) -> Result<PsObject, PsError> {
1083 use crate::tokenizer::Token;
1084 match token {
1085 Token::Int(v) => Ok(PsObject::int(v)),
1086 Token::Real(v) => Ok(PsObject::real(v)),
1087 Token::String(bytes) => {
1088 let save_level = self.save_stack.current_level();
1089 let global = self.vm_alloc_mode;
1090 let created = self.save_stack.last_save_id();
1091 let entity = self
1092 .strings
1093 .allocate_with(bytes.len(), save_level, global, created);
1094 self.strings
1095 .get_mut(entity, 0, bytes.len() as u32)
1096 .copy_from_slice(&bytes);
1097 let mut obj = PsObject::string(entity, bytes.len() as u32);
1098 if global {
1099 obj.flags = ObjFlags::new(ObjFlags::ACCESS_UNLIMITED, false, true, true);
1100 }
1101 Ok(obj)
1102 }
1103 Token::Name(bytes, is_exec) => {
1104 let id = self.names.intern(&bytes);
1105 if is_exec {
1106 Ok(PsObject::name_exec(id))
1107 } else {
1108 Ok(PsObject::name_lit(id))
1109 }
1110 }
1111 Token::LiteralName(bytes) => {
1112 let id = self.names.intern(&bytes);
1113 Ok(PsObject::name_lit(id))
1114 }
1115 Token::ImmediateName(bytes) => {
1116 let id = self.names.intern(&bytes);
1117 let key = DictKey::Name(id);
1118 self.dict_load(&key).ok_or(PsError::Undefined)
1119 }
1120 Token::ArrayBegin => {
1121 let id = self.names.intern(b"[");
1122 Ok(PsObject::name_exec(id))
1123 }
1124 Token::ArrayEnd => {
1125 let id = self.names.intern(b"]");
1126 Ok(PsObject::name_exec(id))
1127 }
1128 Token::DictBegin => {
1129 let id = self.names.intern(b"<<");
1130 Ok(PsObject::name_exec(id))
1131 }
1132 Token::DictEnd => {
1133 let id = self.names.intern(b">>");
1134 Ok(PsObject::name_exec(id))
1135 }
1136 Token::ProcBegin | Token::ProcEnd | Token::Eof | Token::BinaryTokenByte(_) => {
1137 Err(PsError::SyntaxError)
1138 }
1139 }
1140 }
1141
1142 pub fn reset_local_vm(&mut self) {
1145 self.strings.reset_local();
1146 self.arrays.reset_local();
1147 self.dicts.reset_local();
1148 }
1149}
1150
1151impl Default for Context {
1152 fn default() -> Self {
1153 Self::new()
1154 }
1155}
1156
1157#[cfg(test)]
1158mod tests {
1159 use super::*;
1160
1161 #[test]
1162 fn test_context_creation() {
1163 let ctx = Context::new();
1164 assert!(ctx.o_stack.is_empty());
1165 assert!(ctx.e_stack.is_empty());
1166 assert_eq!(ctx.d_stack.len(), 3); }
1168
1169 #[test]
1170 fn test_dict_def_and_load() {
1171 let mut ctx = Context::new();
1172 let key = DictKey::Name(ctx.names.intern(b"foo"));
1173 ctx.dict_def(key.clone(), PsObject::int(42)).unwrap();
1174
1175 let val = ctx.dict_load(&key).unwrap();
1176 assert_eq!(val.as_i32(), Some(42));
1177 }
1178
1179 #[test]
1180 fn test_dict_where() {
1181 let mut ctx = Context::new();
1182 let key = DictKey::Name(ctx.names.intern(b"true"));
1183 let result = ctx.dict_where(&key);
1184 assert!(result.is_some());
1185 let (dict_id, val) = result.unwrap();
1186 assert_eq!(dict_id, ctx.systemdict);
1187 assert!(matches!(val.value, PsValue::Bool(true)));
1188 }
1189
1190 #[test]
1191 fn test_dict_store_existing() {
1192 let mut ctx = Context::new();
1193 let key = DictKey::Name(ctx.names.intern(b"myvar"));
1194
1195 ctx.dict_def(key.clone(), PsObject::int(1)).unwrap();
1197
1198 ctx.dict_store(key.clone(), PsObject::int(2)).unwrap();
1200
1201 let val = ctx.dict_load(&key).unwrap();
1202 assert_eq!(val.as_i32(), Some(2));
1203 }
1204
1205 #[test]
1206 fn test_save_restore_basic() {
1207 let mut ctx = Context::new();
1208 let key = DictKey::Name(ctx.names.intern(b"testvar"));
1209
1210 ctx.dict_def(key.clone(), PsObject::int(1)).unwrap();
1212
1213 let save_obj = ctx.vm_save();
1215 let save_id = match save_obj.value {
1216 PsValue::Save(SaveLevel(id)) => id,
1217 _ => panic!("Expected Save"),
1218 };
1219
1220 ctx.dict_def(key.clone(), PsObject::int(2)).unwrap();
1222 assert_eq!(ctx.dict_load(&key).unwrap().as_i32(), Some(2));
1223
1224 ctx.vm_restore(save_id).unwrap();
1226 assert_eq!(ctx.dict_load(&key).unwrap().as_i32(), Some(1));
1227 }
1228
1229 #[test]
1230 fn test_save_restore_string() {
1231 let mut ctx = Context::new();
1232
1233 let entity = ctx.strings.allocate_from(b"hello");
1234
1235 let save_obj = ctx.vm_save();
1237 let save_id = match save_obj.value {
1238 PsValue::Save(SaveLevel(id)) => id,
1239 _ => panic!("Expected Save"),
1240 };
1241
1242 ctx.cow_check_string(entity);
1244 ctx.strings.put_byte(entity, 0, b'H');
1245 assert_eq!(ctx.strings.get(entity, 0, 5), b"Hello");
1246
1247 ctx.vm_restore(save_id).unwrap();
1249 assert_eq!(ctx.strings.get(entity, 0, 5), b"hello");
1250 }
1251
1252 #[test]
1253 fn test_save_restore_array() {
1254 let mut ctx = Context::new();
1255
1256 let items = [PsObject::int(1), PsObject::int(2), PsObject::int(3)];
1257 let entity = ctx.arrays.allocate_from(&items);
1258
1259 let save_obj = ctx.vm_save();
1260 let save_id = match save_obj.value {
1261 PsValue::Save(SaveLevel(id)) => id,
1262 _ => panic!("Expected Save"),
1263 };
1264
1265 ctx.cow_check_array(entity);
1266 ctx.arrays.set_element(entity, 1, PsObject::int(99));
1267 assert_eq!(ctx.arrays.get_element(entity, 1).as_i32(), Some(99));
1268
1269 ctx.vm_restore(save_id).unwrap();
1270 assert_eq!(ctx.arrays.get_element(entity, 1).as_i32(), Some(2));
1271 }
1272
1273 #[test]
1274 fn test_invalid_restore() {
1275 let mut ctx = Context::new();
1276 assert_eq!(ctx.vm_restore(999), Err(PsError::InvalidRestore));
1278 }
1279}