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