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, PsPath};
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 type CieDecodeKey = (u64, u64, u32, u64, u64);
114
115pub struct Context {
116 pub o_stack: Stack,
118 pub e_stack: Stack,
119 pub d_stack: Vec<EntityId>,
120
121 pub strings: DualStringStore,
123 pub arrays: DualArrayStore,
124 pub dicts: DualDictStore,
125 pub names: NameTable,
126 pub files: FileStore,
127
128 pub loops: Vec<LoopState>,
130
131 pub operators: Vec<OpEntry>,
133
134 pub systemdict: EntityId,
136 pub globaldict: EntityId,
137 pub userdict: EntityId,
138 pub errordict: EntityId,
139 pub dollar_error: EntityId,
140
141 pub rand_state: u64,
143 pub rand_seed: i32,
144 pub current_source_line: u32,
146 pub packing_mode: bool,
148 pub echo: bool,
150
151 pub name_cache: NameCache,
153
154 pub stdout: Box<dyn Write>,
156
157 pub save_stack: SaveStack,
159 pub job_start_save_depth: usize,
161
162 pub vm_alloc_mode: bool,
164
165 pub object_format: i32,
167
168 pub current_operator: Option<NameId>,
170 pub in_error_handler: bool,
171 pub initializing: bool,
173 pub allow_ps_resolution: bool,
176
177 pub exit_code: Option<i32>,
182
183 pub gstate: GraphicsState,
185 pub gstate_stack: Vec<crate::graphics_state::GstateEntry>,
186 pub gstate_store: Vec<GraphicsState>,
188 pub device: Option<Box<dyn OutputDevice>>,
189 pub display_list: DisplayList,
190 pub group_stack: Vec<GroupFrame>,
197 pub save_group_depths: rustc_hash::FxHashMap<u32, usize>,
201 pub ocg_registry: rustc_hash::FxHashMap<NameId, OcgRecord>,
208 pub next_ocg_id: u32,
211 pub doc_structure: stet_graphics::document_structure::DocumentStructure,
218 pub capture_display_lists: Option<Vec<(DisplayList, f64)>>,
222 pub display_list_sender: Option<
229 std::sync::mpsc::Sender<(
230 DisplayList,
231 f64,
232 u32,
233 u32,
234 Option<std::sync::Arc<Vec<u8>>>,
235 bool,
236 )>,
237 >,
238 pub page_width: u32,
239 pub page_height: u32,
240 pub output_path: Option<String>,
241 pub page_filter: Option<std::collections::HashSet<i32>>,
243 #[allow(clippy::type_complexity)]
245 pub device_factory: Option<Box<dyn Fn(u32, u32) -> Box<dyn OutputDevice>>>,
246
247 pub font_directory: EntityId,
249 pub font_resource_path: Option<String>,
250 pub next_fid: i32,
251
252 pub global_resources: EntityId,
254 pub local_resources: EntityId,
255 pub category_registry: EntityId,
256 pub resource_base_path: Option<String>,
257
258 pub user_params: EntityId,
260 pub system_params: EntityId,
261
262 pub internaldict: EntityId,
271
272 pub icc_cache: crate::icc::IccCache,
274
275 pub exec_sync_fn: Option<ExecSyncFn>,
277
278 pub char_width: Option<(f64, f64)>,
280 pub char_width_mode1: Option<((f64, f64), (f64, f64))>,
282
283 pub glyph_caches: rustc_hash::FxHashMap<EntityId, crate::glyph_cache::GlyphCache>,
285 pub char_cache_mode: Option<crate::glyph_cache::Type3CacheMode>,
287
288 pub cshow_pending_cid: Option<i32>,
290
291 pub charpath_capture: Option<PsPath>,
301
302 pub pattern_store: Vec<PatternData>,
305 pub form_cache: rustc_hash::FxHashMap<EntityId, DisplayList>,
307
308 pub cie_decode_cache: rustc_hash::FxHashMap<CieDecodeKey, Vec<f64>>,
324
325 pub start_time: Option<std::time::Instant>,
327
328 pub dict_version: u64,
330 pub name_resolve_cache: Vec<(u64, PsObject)>,
333
334 pub interrupt_flag: Option<std::sync::Arc<std::sync::atomic::AtomicBool>>,
338
339 pub yield_after_showpage: bool,
346}
347
348pub struct GroupFrame {
353 pub display_list: DisplayList,
356 pub kind: GroupKind,
359 pub saved_clip_path_version: u32,
363 pub saved_gsave_depth: usize,
367}
368
369pub enum GroupKind {
372 Transparency { params: GroupParams },
376 SoftMask { params: SoftMaskParams },
380 Masked {
386 mask: DisplayList,
387 params: SoftMaskParams,
388 },
389 OptionalContent { ocg_id: u32, default_visible: bool },
394}
395
396#[derive(Clone, Debug)]
401pub struct OcgRecord {
402 pub ocg_id: u32,
405 pub default_visible: bool,
408}
409
410fn is_flate_stream_complete(data: &[u8]) -> bool {
415 let mut decomp = flate2::Decompress::new(true);
416 let mut out = [0u8; 8192];
417 let mut pos = 0;
418 loop {
419 if pos >= data.len() {
420 return false;
421 }
422 match decomp.decompress(&data[pos..], &mut out, flate2::FlushDecompress::None) {
423 Ok(flate2::Status::StreamEnd) => return true,
424 Ok(_) => {
425 let new_pos = decomp.total_in() as usize;
426 if new_pos == pos {
427 return false;
428 }
429 pos = new_pos;
430 }
431 Err(_) => return false,
432 }
433 }
434}
435
436impl Context {
437 pub fn exec_sync(&mut self, proc_obj: PsObject) -> Result<(), PsError> {
439 let f = self.exec_sync_fn.expect("exec_sync not initialized");
440 f(self, proc_obj)
441 }
442
443 pub fn pump_proc_sources(&mut self, entity: EntityId) -> Result<(), PsError> {
470 while let Some((src, proc, flate_above)) = self.files.pending_proc_source(entity) {
475 let data = self.drain_proc_source(proc, flate_above)?;
476 self.files.install_proc_data(src, data);
477 }
478 Ok(())
479 }
480
481 fn drain_proc_source(
503 &mut self,
504 procedure: PsObject,
505 flate_above: bool,
506 ) -> Result<Vec<u8>, PsError> {
507 const MAX_PROC_BYTES: usize = 64 * 1024 * 1024;
509
510 let mut data = Vec::new();
511 loop {
512 let depth_before = self.o_stack.len();
513 self.exec_sync(procedure)?;
514
515 if self.o_stack.len() <= depth_before {
516 break;
517 }
518 let result = self.o_stack.peek(0)?;
519 match result.value {
520 PsValue::String { entity, start, len } => {
521 let bytes = self.strings.get(entity, start, len).to_vec();
522 self.o_stack.pop()?;
523 if bytes.is_empty() {
524 break; }
526 data.extend_from_slice(&bytes);
527 if flate_above && is_flate_stream_complete(&data) {
528 break;
529 }
530 if data.len() >= MAX_PROC_BYTES {
531 break;
532 }
533 }
534 _ => break,
536 }
537 }
538 Ok(data)
539 }
540
541 pub fn new() -> Self {
544 let mut names = NameTable::new();
545
546 let name_cache = NameCache {
547 n_def: names.intern(b"def"),
548 n_true: names.intern(b"true"),
549 n_false: names.intern(b"false"),
550 n_null: names.intern(b"null"),
551 n_mark: names.intern(b"mark"),
552 n_font_name: names.intern(b"FontName"),
553 n_font_type: names.intern(b"FontType"),
554 n_font_matrix: names.intern(b"FontMatrix"),
555 n_font_bbox: names.intern(b"FontBBox"),
556 n_encoding: names.intern(b"Encoding"),
557 n_char_strings: names.intern(b"CharStrings"),
558 n_private: names.intern(b"Private"),
559 n_fid: names.intern(b"FID"),
560 n_paint_type: names.intern(b"PaintType"),
561 n_subrs: names.intern(b"Subrs"),
562 n_len_iv: names.intern(b"lenIV"),
563 n_notdef: names.intern(b".notdef"),
564 n_metrics: names.intern(b"Metrics"),
565 n_font_directory: names.intern(b"FontDirectory"),
566 n_find_resource: names.intern(b"FindResource"),
568 n_define_resource: names.intern(b"DefineResource"),
569 n_undef_resource: names.intern(b"UndefineResource"),
570 n_resource_status: names.intern(b"ResourceStatus"),
571 n_resource_for_all: names.intern(b"ResourceForAll"),
572 n_category: names.intern(b"Category"),
573 n_instance_type: names.intern(b"InstanceType"),
574 n_resource_dir: names.intern(b"ResourceDir"),
575 n_resource_ext: names.intern(b"ResourceExtension"),
576 n_build_char: names.intern(b"BuildChar"),
577 n_build_glyph: names.intern(b"BuildGlyph"),
578 n_stroke_width: names.intern(b"StrokeWidth"),
579 n_wmode: names.intern(b"WMode"),
580 };
581
582 let mut strings = DualStringStore::new();
583 let mut dicts = DualDictStore::new();
584
585 let systemdict = dicts.allocate_with(400, b"systemdict", 0, true, 0);
595 let globaldict = dicts.allocate_with(100, b"globaldict", 0, true, 0);
596 let userdict = dicts.allocate_at_level_zero(200, b"userdict");
597 let errordict = dicts.allocate_at_level_zero(50, b"errordict");
598 let dollar_error = dicts.allocate_at_level_zero(20, b"$error");
599 let font_directory = dicts.allocate_at_level_zero(50, b"FontDirectory");
600
601 let global_resources = dicts.allocate_with(20, b"GlobalResources", 0, true, 0);
603 let local_resources = dicts.allocate_at_level_zero(20, b"LocalResources");
604 let internaldict = dicts.allocate_at_level_zero(50, b"internaldict");
605 let category_registry = dicts.allocate_with(30, b"CategoryRegistry", 0, true, 0);
606
607 let user_params = dicts.allocate_at_level_zero(25, b"UserParams");
611 for key_name in [
612 "MaxDictStack",
613 "MaxExecStack",
614 "MaxOpStack",
615 "MaxFontItem",
616 "MaxFormItem",
617 "MaxPatternItem",
618 "MaxUPathItem",
619 "MaxScreenItem",
620 "MaxSuperScreen",
621 "MinFontCompress",
622 "MaxLocalVM",
623 "VMReclaim",
624 "VMThreshold",
625 "UCacheBLimit",
626 ] {
627 dicts.put(
628 user_params,
629 DictKey::Name(names.intern(key_name.as_bytes())),
630 PsObject::int(0),
631 );
632 }
633 dicts.put(
634 user_params,
635 DictKey::Name(names.intern(b"JobName")),
636 PsObject::string(strings.allocate_from_at_level_zero(b""), 0),
637 );
638 dicts.put(
639 user_params,
640 DictKey::Name(names.intern(b"ExecutionHistory")),
641 PsObject::bool(false),
642 );
643 dicts.put(
644 user_params,
645 DictKey::Name(names.intern(b"ExecutionHistorySize")),
646 PsObject::int(20),
647 );
648 dicts.put(
649 user_params,
650 DictKey::Name(names.intern(b"IdiomRecognition")),
651 PsObject::bool(true),
652 );
653 dicts.put(
654 user_params,
655 DictKey::Name(names.intern(b"AccurateScreens")),
656 PsObject::bool(false),
657 );
658 dicts.put(
659 user_params,
660 DictKey::Name(names.intern(b"HalftoneMode")),
661 PsObject::int(0),
662 );
663
664 let system_params = dicts.allocate_at_level_zero(30, b"SystemParams");
665 for (key, val) in [
667 ("MaxFontCache", 67108864),
668 ("MaxFormCache", 131072),
669 ("MaxPatternCache", 131072),
670 ("MaxUPathCache", 131072),
671 ("MaxScreenStorage", 524288),
672 ("MaxDisplayList", 2097152),
673 ("MaxDisplayAndSourceList", 4194304),
674 ("MaxSourceList", 2097152),
675 ("MaxImageBuffer", 524288),
676 ("MaxOutlineCache", 65536),
677 ("MaxStoredScreenCache", 0),
678 ("CurFontCache", 0),
680 ("CurFormCache", 0),
681 ("CurPatternCache", 0),
682 ("CurUPathCache", 0),
683 ("CurScreenStorage", 0),
684 ("CurSourceList", 0),
685 ("CurStoredScreenCache", 0),
686 ("CurOutlineCache", 0),
687 ("PageCount", 0),
688 ("Revision", 1),
689 ] {
690 dicts.put(
691 system_params,
692 DictKey::Name(names.intern(key.as_bytes())),
693 PsObject::int(val),
694 );
695 }
696 let printer_name = b"stet";
697 let printer_str = strings.allocate_from_at_level_zero(printer_name);
698 dicts.put(
699 system_params,
700 DictKey::Name(names.intern(b"PrinterName")),
701 PsObject::string(printer_str, printer_name.len() as u32),
702 );
703 let real_format = b"IEEE";
705 let realfmt_str = strings.allocate_from_at_level_zero(real_format);
706 dicts.put(
707 system_params,
708 DictKey::Name(names.intern(b"RealFormat")),
709 PsObject::string(realfmt_str, real_format.len() as u32),
710 );
711 let pw_str = strings.allocate_from_at_level_zero(b"0");
712 dicts.put(
713 system_params,
714 DictKey::Name(names.intern(b"SystemParamsPassword")),
715 PsObject::string(pw_str, 1),
716 );
717 let pw_str2 = strings.allocate_from_at_level_zero(b"0");
718 dicts.put(
719 system_params,
720 DictKey::Name(names.intern(b"StartJobPassword")),
721 PsObject::string(pw_str2, 1),
722 );
723 dicts.put(
724 system_params,
725 DictKey::Name(names.intern(b"LicenseID")),
726 PsObject::int(0),
727 );
728
729 let sd_obj = PsObject::dict(systemdict);
731 dicts.put(
732 systemdict,
733 DictKey::Name(names.intern(b"systemdict")),
734 sd_obj,
735 );
736
737 let ud_obj = PsObject::dict(userdict);
738 dicts.put(systemdict, DictKey::Name(names.intern(b"userdict")), ud_obj);
739
740 let gd_obj = PsObject::dict(globaldict);
741 dicts.put(
742 systemdict,
743 DictKey::Name(names.intern(b"globaldict")),
744 gd_obj,
745 );
746
747 let ed_obj = PsObject::dict(errordict);
748 dicts.put(
749 systemdict,
750 DictKey::Name(names.intern(b"errordict")),
751 ed_obj,
752 );
753
754 let de_obj = PsObject::dict(dollar_error);
755 dicts.put(systemdict, DictKey::Name(names.intern(b"$error")), de_obj);
756
757 let fd_obj = PsObject::dict(font_directory);
758 dicts.put(
759 systemdict,
760 DictKey::Name(name_cache.n_font_directory),
761 fd_obj,
762 );
763
764 dicts.put(
766 systemdict,
767 DictKey::Name(names.intern(b"true")),
768 PsObject::bool(true),
769 );
770 dicts.put(
771 systemdict,
772 DictKey::Name(names.intern(b"false")),
773 PsObject::bool(false),
774 );
775 dicts.put(
776 systemdict,
777 DictKey::Name(names.intern(b"null")),
778 PsObject::null(),
779 );
780
781 dicts.put(
783 systemdict,
784 DictKey::Name(names.intern(b"mark")),
785 PsObject::mark(),
786 );
787
788 dicts.put(
790 systemdict,
791 DictKey::Name(names.intern(b"[")),
792 PsObject::mark(),
793 );
794
795 dicts.put(
797 systemdict,
798 DictKey::Name(names.intern(b"<<")),
799 PsObject::dict_mark(),
800 );
801
802 dicts.put(
804 systemdict,
805 DictKey::Name(names.intern(b"languagelevel")),
806 PsObject::int(3),
807 );
808
809 let d_stack = vec![systemdict, globaldict, userdict];
811
812 Self {
813 o_stack: Stack::new(500),
814 e_stack: Stack::new(250),
815 d_stack,
816 strings,
817 arrays: DualArrayStore::new(),
818 dicts,
819 names,
820 files: FileStore::new(),
821 loops: Vec::new(),
822 operators: Vec::new(),
823 systemdict,
824 globaldict,
825 userdict,
826 errordict,
827 dollar_error,
828 rand_state: 0,
829 rand_seed: 0,
830 current_source_line: 1,
831 packing_mode: false,
832 echo: false,
833 name_cache,
834 stdout: Box::new(std::io::stdout()),
835 save_stack: SaveStack::new(),
836 job_start_save_depth: 0,
837 vm_alloc_mode: false,
838 object_format: 0,
839 current_operator: None,
840 in_error_handler: false,
841 initializing: true,
842 allow_ps_resolution: false,
843 exit_code: None,
844 gstate: GraphicsState::new(),
845 gstate_stack: Vec::new(),
846 gstate_store: Vec::new(),
847 device: None,
848 display_list: DisplayList::new(),
849 group_stack: Vec::new(),
850 save_group_depths: rustc_hash::FxHashMap::default(),
851 ocg_registry: rustc_hash::FxHashMap::default(),
852 next_ocg_id: 0,
853 doc_structure: stet_graphics::document_structure::DocumentStructure::new(),
854 capture_display_lists: None,
855 display_list_sender: None,
856 page_width: 612,
857 page_height: 792,
858 output_path: None,
859 page_filter: None,
860 device_factory: None,
861 font_directory,
862 font_resource_path: None,
863 next_fid: 0,
864 global_resources,
865 local_resources,
866 category_registry,
867 resource_base_path: None,
868 user_params,
869 system_params,
870 internaldict,
871 icc_cache: crate::icc::IccCache::new(),
872 exec_sync_fn: None,
873 char_width: None,
874 char_width_mode1: None,
875 glyph_caches: rustc_hash::FxHashMap::default(),
876 char_cache_mode: None,
877 cshow_pending_cid: None,
878 charpath_capture: None,
879 pattern_store: Vec::new(),
880 form_cache: rustc_hash::FxHashMap::default(),
881 cie_decode_cache: rustc_hash::FxHashMap::default(),
882 #[cfg(not(target_arch = "wasm32"))]
883 start_time: Some(std::time::Instant::now()),
884 #[cfg(target_arch = "wasm32")]
885 start_time: None,
886 dict_version: 0,
887 name_resolve_cache: Vec::new(),
888 interrupt_flag: None,
889 yield_after_showpage: false,
890 }
891 }
892
893 pub fn new_with_output(output: Box<dyn Write>) -> Self {
895 let mut ctx = Self::new();
896 ctx.stdout = output;
897 ctx
898 }
899
900 #[inline]
904 pub fn dict_load(&mut self, key: &DictKey) -> Option<PsObject> {
905 if let DictKey::Name(name_id) = key {
907 let idx = name_id.0 as usize;
908 if idx < self.name_resolve_cache.len() {
909 let (ver, obj) = self.name_resolve_cache[idx];
910 if ver == self.dict_version {
911 return Some(obj);
912 }
913 }
914 }
915
916 for &dict_id in self.d_stack.iter().rev() {
918 if let Some(val) = self.dicts.get(dict_id, key) {
919 if let DictKey::Name(name_id) = key {
921 let idx = name_id.0 as usize;
922 if idx >= self.name_resolve_cache.len() {
923 self.name_resolve_cache
924 .resize(idx + 64, (u64::MAX, PsObject::null()));
925 }
926 self.name_resolve_cache[idx] = (self.dict_version, val);
927 }
928 return Some(val);
929 }
930 }
931 None
932 }
933
934 #[inline]
936 pub fn invalidate_name_cache(&mut self) {
937 self.dict_version = self.dict_version.wrapping_add(1);
938 }
939
940 pub fn dict_where(&self, key: &DictKey) -> Option<(EntityId, PsObject)> {
942 for &dict_id in self.d_stack.iter().rev() {
943 if let Some(val) = self.dicts.get(dict_id, key) {
944 return Some((dict_id, val));
945 }
946 }
947 None
948 }
949
950 pub fn dict_def(&mut self, key: DictKey, value: PsObject) -> Result<(), PsError> {
952 let current = *self.d_stack.last().ok_or(PsError::DictStackUnderflow)?;
953 self.cow_check_dict(current);
954 self.invalidate_name_cache();
955 self.dicts.put(current, key, value);
956 Ok(())
957 }
958
959 pub fn dict_store(&mut self, key: DictKey, value: PsObject) -> Result<(), PsError> {
961 self.invalidate_name_cache();
962 for &dict_id in self.d_stack.iter().rev() {
963 if self.dicts.known(dict_id, &key) {
964 self.cow_check_dict(dict_id);
965 self.dicts.put(dict_id, key, value);
966 return Ok(());
967 }
968 }
969 self.dict_def(key, value)
971 }
972
973 pub fn make_dict_key(&mut self, obj: &PsObject) -> Result<DictKey, PsError> {
975 match obj.value {
976 PsValue::Name(id) => Ok(DictKey::Name(id)),
977 PsValue::Int(v) => Ok(DictKey::Int(v)),
978 PsValue::Real(v) => Ok(DictKey::Real(v.to_bits())),
979 PsValue::Bool(v) => Ok(DictKey::Bool(v)),
980 PsValue::String { entity, start, len } => {
981 let bytes = self.strings.get(entity, start, len).to_vec();
984 let name_id = self.names.intern(&bytes);
985 Ok(DictKey::Name(name_id))
986 }
987 PsValue::Operator(op) => Ok(DictKey::Operator(op.0)),
988 PsValue::Array { entity, start, len } | PsValue::PackedArray { entity, start, len } => {
989 Ok(DictKey::Identity(entity.0, start, len))
990 }
991 PsValue::Dict(entity) => Ok(DictKey::Identity(entity.0, 0, 0)),
992 PsValue::Null => Err(PsError::TypeCheck),
993 _ => Err(PsError::TypeCheck),
994 }
995 }
996
997 pub fn alloc_loop(&mut self, state: LoopState) -> EntityId {
999 let id = EntityId(self.loops.len() as u32);
1000 self.loops.push(state);
1001 id
1002 }
1003
1004 pub fn get_loop(&self, entity: EntityId) -> &LoopState {
1006 &self.loops[entity.0 as usize]
1007 }
1008
1009 pub fn get_loop_mut(&mut self, entity: EntityId) -> &mut LoopState {
1011 &mut self.loops[entity.0 as usize]
1012 }
1013
1014 #[inline]
1022 pub fn current_display_list_mut(&mut self) -> &mut DisplayList {
1023 if let Some(frame) = self.group_stack.last_mut() {
1024 &mut frame.display_list
1025 } else {
1026 &mut self.display_list
1027 }
1028 }
1029
1030 #[inline]
1032 pub fn current_display_list(&self) -> &DisplayList {
1033 if let Some(frame) = self.group_stack.last() {
1034 &frame.display_list
1035 } else {
1036 &self.display_list
1037 }
1038 }
1039
1040 pub fn take_display_list(&mut self) -> DisplayList {
1046 if self.capture_display_lists.is_some() {
1047 let dpi = self.current_page_dpi();
1048 if let Some(ref mut captures) = self.capture_display_lists {
1049 captures.push((self.display_list.clone(), dpi));
1050 }
1051 }
1052 if let Some(ref sender) = self.display_list_sender {
1053 let dpi = self.current_page_dpi();
1054 let (w, h) = self
1057 .device
1058 .as_ref()
1059 .map(|d| d.page_size())
1060 .unwrap_or((self.page_width, self.page_height));
1061 let _ = sender.send((self.display_list.clone(), dpi, w, h, None, false));
1065 }
1066 if self.yield_after_showpage
1070 && let Some(ref flag) = self.interrupt_flag
1071 {
1072 flag.store(true, std::sync::atomic::Ordering::Relaxed);
1073 }
1074 std::mem::take(&mut self.display_list)
1075 }
1076
1077 pub fn current_page_dpi(&self) -> f64 {
1079 use crate::dict::DictKey;
1080 if let Some(pd) = self.gstate.page_device
1081 && let Some(name_id) = self.names.find(b"HWResolution")
1082 && let Some(obj) = self.dicts.get(pd, &DictKey::Name(name_id))
1083 && let PsValue::Array { entity, .. } = obj.value
1084 {
1085 let first = self.arrays.get_element(entity, 0);
1086 return match first.value {
1087 PsValue::Real(r) => r,
1088 PsValue::Int(i) => i as f64,
1089 _ => 72.0,
1090 };
1091 }
1092 72.0
1093 }
1094
1095 fn vm_marks(&self) -> crate::save_stack::VmMarks {
1101 crate::save_stack::VmMarks {
1102 string_data: self.strings.local.data_len(),
1103 string_entities: self.strings.local.entities.len(),
1104 array_data: self.arrays.local.allocated_objects(),
1105 array_entities: self.arrays.local.entities.len(),
1106 dict_slots: self.dicts.local.dict_slots(),
1107 dict_entities: self.dicts.local.entities.len(),
1108 }
1109 }
1110
1111 pub fn vm_save(&mut self) -> PsObject {
1112 let d_depth = self.d_stack.len();
1113 let gstate_snapshot = self.gstate.clone();
1114 let gstate_stack_snapshot = self.gstate_stack.clone();
1115 let marks = self.vm_marks();
1116 let (_level, save_id) = self.save_stack.save(crate::save_stack::SaveSnapshot {
1117 d_stack_depth: d_depth,
1118 packing_mode: self.packing_mode,
1119 vm_alloc_mode: self.vm_alloc_mode,
1120 object_format: self.object_format,
1121 gstate: gstate_snapshot,
1122 gstate_stack: gstate_stack_snapshot,
1123 gstate_store_len: self.gstate_store.len(),
1124 marks,
1125 });
1126
1127 self.gstate_stack.push(crate::graphics_state::GstateEntry {
1130 state: self.gstate.clone(),
1131 saved_by_save: true,
1132 });
1133
1134 PsObject {
1135 value: PsValue::Save(SaveLevel(save_id)),
1136 flags: crate::object::ObjFlags::literal(),
1137 }
1138 }
1139
1140 pub fn vm_restore(&mut self, save_id: u32) -> Result<(), PsError> {
1142 if !self.save_stack.is_valid(save_id) {
1144 return Err(PsError::InvalidRestore);
1145 }
1146
1147 let levels = self
1152 .save_stack
1153 .restore_to(save_id)
1154 .ok_or(PsError::InvalidRestore)?;
1155
1156 for level in levels.iter().rev() {
1161 for record in level.records.iter().rev() {
1162 match record.store_type {
1163 StoreType::String => {
1164 self.strings.swap_offsets(record.src, record.copy);
1165 self.strings.entity_meta_mut(record.src).save_level = 0;
1166 }
1167 StoreType::Array => {
1168 self.arrays.swap_offsets(record.src, record.copy);
1169 self.arrays.entity_meta_mut(record.src).save_level = 0;
1170 }
1171 StoreType::Dict => {
1172 self.dicts.swap_offsets(record.src, record.copy);
1173 self.dicts.entity_meta_mut(record.src).save_level = 0;
1174 }
1175 }
1176 }
1177 }
1178
1179 let target = &levels[0];
1181 self.packing_mode = target.packing_mode;
1182 self.vm_alloc_mode = target.vm_alloc_mode;
1183 self.object_format = target.object_format;
1184
1185 self.gstate = target.gstate.clone();
1187 self.gstate_stack = target.gstate_stack.clone();
1188
1189 self.gstate_store.truncate(target.gstate_store_len);
1193
1194 self.d_stack.truncate(target.d_stack_depth);
1196
1197 let marks = target.marks;
1207
1208 let dict_mark = marks.dict_entities;
1213 let live_dict = |e: &EntityId| e.is_global() || e.raw_index() < dict_mark;
1214 self.glyph_caches.retain(|entity, _| live_dict(entity));
1215 self.form_cache.retain(|entity, _| live_dict(entity));
1216
1217 self.strings
1218 .local
1219 .truncate_to(marks.string_data, marks.string_entities);
1220 self.arrays
1221 .local
1222 .truncate_to(marks.array_data, marks.array_entities);
1223 self.dicts
1224 .local
1225 .truncate_to(marks.dict_slots, marks.dict_entities);
1226
1227 self.invalidate_name_cache();
1228 self.close_restored_proc_sources();
1229 self.debug_assert_no_dangling_refs();
1230 Ok(())
1231 }
1232
1233 fn close_restored_proc_sources(&mut self) {
1242 for (entity, proc) in self.files.pending_proc_handles() {
1243 if !self.entity_is_live(&proc) {
1244 self.files.close_pending_proc(entity);
1245 }
1246 }
1247 }
1248
1249 fn entity_is_live(&self, obj: &PsObject) -> bool {
1251 match obj.value {
1252 PsValue::Array { entity, .. } | PsValue::PackedArray { entity, .. } => {
1253 let len = if entity.is_global() {
1254 self.arrays.global.entities.len()
1255 } else {
1256 self.arrays.local.entities.len()
1257 };
1258 entity.raw_index() < len
1259 }
1260 _ => true,
1261 }
1262 }
1263
1264 #[inline]
1280 fn debug_assert_no_dangling_refs(&self) {
1281 #[cfg(debug_assertions)]
1282 {
1283 let dangling = crate::vm_audit::audit_dangling_refs(self);
1284 assert!(
1285 dangling.is_empty(),
1286 "restore left {} dangling reference(s):\n {}",
1287 dangling.len(),
1288 dangling
1289 .iter()
1290 .map(|d| d.to_string())
1291 .collect::<Vec<_>>()
1292 .join("\n ")
1293 );
1294 }
1295 }
1296
1297 pub fn cow_check_string(&mut self, entity: EntityId) {
1302 let current_level = self.save_stack.current_level();
1303 if current_level == 0 {
1304 return; }
1306
1307 if entity.is_global() {
1308 return; }
1310 let meta = self.strings.entity_meta(entity);
1311 if meta.save_level >= current_level {
1312 return; }
1314
1315 let copy_id = self.strings.cow_copy(entity);
1317 self.strings.entity_meta_mut(entity).save_level = current_level;
1318
1319 self.save_stack.add_record(SaveRecord {
1320 src: entity,
1321 copy: copy_id,
1322 store_type: StoreType::String,
1323 });
1324 }
1325
1326 pub fn cow_check_array(&mut self, entity: EntityId) {
1328 let current_level = self.save_stack.current_level();
1329 if current_level == 0 {
1330 return;
1331 }
1332
1333 if entity.is_global() {
1334 return;
1335 }
1336 let meta = self.arrays.entity_meta(entity);
1337 if meta.save_level >= current_level {
1338 return;
1339 }
1340
1341 let copy_id = self.arrays.cow_copy(entity);
1342 self.arrays.entity_meta_mut(entity).save_level = current_level;
1343
1344 self.save_stack.add_record(SaveRecord {
1345 src: entity,
1346 copy: copy_id,
1347 store_type: StoreType::Array,
1348 });
1349 }
1350
1351 pub fn dict_put_cow(&mut self, entity: EntityId, key: DictKey, value: PsObject) {
1366 self.cow_check_dict(entity);
1367 self.dicts.put(entity, key, value);
1368 }
1369
1370 pub fn cow_check_dict(&mut self, entity: EntityId) {
1371 let current_level = self.save_stack.current_level();
1372 if current_level == 0 {
1373 return;
1374 }
1375
1376 if entity.is_global() {
1377 return;
1378 }
1379 let meta = self.dicts.entity_meta(entity);
1380 if meta.save_level >= current_level {
1381 return;
1382 }
1383
1384 let copy_id = self.dicts.cow_copy(entity);
1385 self.dicts.entity_meta_mut(entity).save_level = current_level;
1386
1387 self.save_stack.add_record(SaveRecord {
1388 src: entity,
1389 copy: copy_id,
1390 store_type: StoreType::Dict,
1391 });
1392 }
1393
1394 pub fn token_to_object(&mut self, token: crate::tokenizer::Token) -> Result<PsObject, PsError> {
1398 use crate::tokenizer::Token;
1399 match token {
1400 Token::Int(v) => Ok(PsObject::int(v)),
1401 Token::Real(v) => Ok(PsObject::real(v)),
1402 Token::String(bytes) => {
1403 let save_level = self.save_stack.current_level();
1404 let global = self.vm_alloc_mode;
1405 let created = self.save_stack.last_save_id();
1406 let entity = self
1407 .strings
1408 .allocate_with(bytes.len(), save_level, global, created);
1409 self.strings
1410 .get_mut(entity, 0, bytes.len() as u32)
1411 .copy_from_slice(&bytes);
1412 let mut obj = PsObject::string(entity, bytes.len() as u32);
1413 if global {
1414 obj.flags = ObjFlags::new(ObjFlags::ACCESS_UNLIMITED, false, true, true);
1415 }
1416 Ok(obj)
1417 }
1418 Token::Name(bytes, is_exec) => {
1419 let id = self.names.intern(&bytes);
1420 if is_exec {
1421 Ok(PsObject::name_exec(id))
1422 } else {
1423 Ok(PsObject::name_lit(id))
1424 }
1425 }
1426 Token::LiteralName(bytes) => {
1427 let id = self.names.intern(&bytes);
1428 Ok(PsObject::name_lit(id))
1429 }
1430 Token::ImmediateName(bytes) => {
1431 let id = self.names.intern(&bytes);
1432 let key = DictKey::Name(id);
1433 self.dict_load(&key).ok_or(PsError::Undefined)
1434 }
1435 Token::ArrayBegin => {
1436 let id = self.names.intern(b"[");
1437 Ok(PsObject::name_exec(id))
1438 }
1439 Token::ArrayEnd => {
1440 let id = self.names.intern(b"]");
1441 Ok(PsObject::name_exec(id))
1442 }
1443 Token::DictBegin => {
1444 let id = self.names.intern(b"<<");
1445 Ok(PsObject::name_exec(id))
1446 }
1447 Token::DictEnd => {
1448 let id = self.names.intern(b">>");
1449 Ok(PsObject::name_exec(id))
1450 }
1451 Token::ProcBegin | Token::ProcEnd | Token::Eof | Token::BinaryTokenByte(_) => {
1452 Err(PsError::SyntaxError)
1453 }
1454 }
1455 }
1456
1457 pub fn reset_local_vm(&mut self) {
1460 self.strings.reset_local();
1461 self.arrays.reset_local();
1462 self.dicts.reset_local();
1463 }
1464}
1465
1466impl Default for Context {
1467 fn default() -> Self {
1468 Self::new()
1469 }
1470}
1471
1472#[cfg(test)]
1473mod tests {
1474 use super::*;
1475
1476 #[test]
1477 fn test_context_creation() {
1478 let ctx = Context::new();
1479 assert!(ctx.o_stack.is_empty());
1480 assert!(ctx.e_stack.is_empty());
1481 assert_eq!(ctx.d_stack.len(), 3); }
1483
1484 #[test]
1485 fn test_dict_def_and_load() {
1486 let mut ctx = Context::new();
1487 let key = DictKey::Name(ctx.names.intern(b"foo"));
1488 ctx.dict_def(key.clone(), PsObject::int(42)).unwrap();
1489
1490 let val = ctx.dict_load(&key).unwrap();
1491 assert_eq!(val.as_i32(), Some(42));
1492 }
1493
1494 #[test]
1495 fn test_dict_where() {
1496 let mut ctx = Context::new();
1497 let key = DictKey::Name(ctx.names.intern(b"true"));
1498 let result = ctx.dict_where(&key);
1499 assert!(result.is_some());
1500 let (dict_id, val) = result.unwrap();
1501 assert_eq!(dict_id, ctx.systemdict);
1502 assert!(matches!(val.value, PsValue::Bool(true)));
1503 }
1504
1505 #[test]
1506 fn test_dict_store_existing() {
1507 let mut ctx = Context::new();
1508 let key = DictKey::Name(ctx.names.intern(b"myvar"));
1509
1510 ctx.dict_def(key.clone(), PsObject::int(1)).unwrap();
1512
1513 ctx.dict_store(key.clone(), PsObject::int(2)).unwrap();
1515
1516 let val = ctx.dict_load(&key).unwrap();
1517 assert_eq!(val.as_i32(), Some(2));
1518 }
1519
1520 #[test]
1521 fn test_save_restore_basic() {
1522 let mut ctx = Context::new();
1523 let key = DictKey::Name(ctx.names.intern(b"testvar"));
1524
1525 ctx.dict_def(key.clone(), PsObject::int(1)).unwrap();
1527
1528 let save_obj = ctx.vm_save();
1530 let save_id = match save_obj.value {
1531 PsValue::Save(SaveLevel(id)) => id,
1532 _ => panic!("Expected Save"),
1533 };
1534
1535 ctx.dict_def(key.clone(), PsObject::int(2)).unwrap();
1537 assert_eq!(ctx.dict_load(&key).unwrap().as_i32(), Some(2));
1538
1539 ctx.vm_restore(save_id).unwrap();
1541 assert_eq!(ctx.dict_load(&key).unwrap().as_i32(), Some(1));
1542 }
1543
1544 #[test]
1545 fn test_save_restore_string() {
1546 let mut ctx = Context::new();
1547
1548 let entity = ctx.strings.allocate_from_at_level_zero(b"hello");
1549
1550 let save_obj = ctx.vm_save();
1552 let save_id = match save_obj.value {
1553 PsValue::Save(SaveLevel(id)) => id,
1554 _ => panic!("Expected Save"),
1555 };
1556
1557 ctx.cow_check_string(entity);
1559 ctx.strings.put_byte(entity, 0, b'H');
1560 assert_eq!(ctx.strings.get(entity, 0, 5), b"Hello");
1561
1562 ctx.vm_restore(save_id).unwrap();
1564 assert_eq!(ctx.strings.get(entity, 0, 5), b"hello");
1565 }
1566
1567 #[test]
1568 fn test_save_restore_array() {
1569 let mut ctx = Context::new();
1570
1571 let items = [PsObject::int(1), PsObject::int(2), PsObject::int(3)];
1572 let entity = ctx.arrays.allocate_from_at_level_zero(&items);
1573
1574 let save_obj = ctx.vm_save();
1575 let save_id = match save_obj.value {
1576 PsValue::Save(SaveLevel(id)) => id,
1577 _ => panic!("Expected Save"),
1578 };
1579
1580 ctx.cow_check_array(entity);
1581 ctx.arrays.set_element(entity, 1, PsObject::int(99));
1582 assert_eq!(ctx.arrays.get_element(entity, 1).as_i32(), Some(99));
1583
1584 ctx.vm_restore(save_id).unwrap();
1585 assert_eq!(ctx.arrays.get_element(entity, 1).as_i32(), Some(2));
1586 }
1587
1588 #[test]
1589 fn test_invalid_restore() {
1590 let mut ctx = Context::new();
1591 assert_eq!(ctx.vm_restore(999), Err(PsError::InvalidRestore));
1593 }
1594
1595 #[test]
1603 #[cfg(debug_assertions)]
1604 #[should_panic(expected = "dangling reference")]
1605 fn vm_restore_rejects_a_dangling_reference() {
1606 let mut ctx = Context::new();
1607 let save_obj = ctx.vm_save();
1608 let save_id = match save_obj.value {
1609 PsValue::Save(SaveLevel(id)) => id,
1610 _ => panic!("Expected Save"),
1611 };
1612 let retired = EntityId(ctx.dicts.local.entities.len() as u32);
1613 let key = DictKey::Name(ctx.names.intern(b"stale"));
1614 ctx.dicts.put(ctx.userdict, key, PsObject::dict(retired));
1615 let _ = ctx.vm_restore(save_id);
1616 }
1617
1618 #[test]
1621 fn restore_rewinds_gstate_store() {
1622 let mut ctx = Context::new();
1623 ctx.gstate_store.push(ctx.gstate.clone());
1624 let save_obj = ctx.vm_save();
1625 let save_id = match save_obj.value {
1626 PsValue::Save(SaveLevel(id)) => id,
1627 _ => panic!("Expected Save"),
1628 };
1629 ctx.gstate_store.push(ctx.gstate.clone());
1630 assert_eq!(ctx.gstate_store.len(), 2);
1631 ctx.vm_restore(save_id).unwrap();
1632 assert_eq!(ctx.gstate_store.len(), 1);
1633 }
1634}