Skip to main content

Context

Struct Context 

Source
pub struct Context {
Show 74 fields pub o_stack: Stack, pub e_stack: Stack, pub d_stack: Vec<EntityId>, pub strings: DualStringStore, pub arrays: DualArrayStore, pub dicts: DualDictStore, pub names: NameTable, pub files: FileStore, pub loops: Vec<LoopState>, pub operators: Vec<OpEntry>, pub systemdict: EntityId, pub globaldict: EntityId, pub userdict: EntityId, pub errordict: EntityId, pub dollar_error: EntityId, pub rand_state: u64, pub rand_seed: i32, pub current_source_line: u32, pub packing_mode: bool, pub echo: bool, pub name_cache: NameCache, pub stdout: Box<dyn Write>, pub save_stack: SaveStack, pub job_start_save_depth: usize, pub vm_alloc_mode: bool, pub object_format: i32, pub current_operator: Option<NameId>, pub in_error_handler: bool, pub initializing: bool, pub allow_ps_resolution: bool, pub exit_code: Option<i32>, pub gstate: GraphicsState, pub gstate_stack: Vec<GstateEntry>, pub gstate_store: Vec<GraphicsState>, pub device: Option<Box<dyn OutputDevice>>, pub display_list: DisplayList, pub group_stack: Vec<GroupFrame>, pub save_group_depths: FxHashMap<u32, usize>, pub ocg_registry: FxHashMap<NameId, OcgRecord>, pub next_ocg_id: u32, pub doc_structure: DocumentStructure, pub capture_display_lists: Option<Vec<(DisplayList, f64)>>, pub display_list_sender: Option<Sender<(DisplayList, f64, u32, u32, Option<Arc<Vec<u8>>>, bool)>>, pub page_width: u32, pub page_height: u32, pub output_path: Option<String>, pub page_filter: Option<HashSet<i32>>, pub device_factory: Option<Box<dyn Fn(u32, u32) -> Box<dyn OutputDevice>>>, pub font_directory: EntityId, pub font_resource_path: Option<String>, pub next_fid: i32, pub global_resources: EntityId, pub local_resources: EntityId, pub category_registry: EntityId, pub resource_base_path: Option<String>, pub user_params: EntityId, pub system_params: EntityId, pub internaldict: EntityId, pub icc_cache: IccCache, pub exec_sync_fn: Option<ExecSyncFn>, pub char_width: Option<(f64, f64)>, pub char_width_mode1: Option<((f64, f64), (f64, f64))>, pub glyph_caches: FxHashMap<EntityId, GlyphCache>, pub char_cache_mode: Option<Type3CacheMode>, pub cshow_pending_cid: Option<i32>, pub charpath_capture: Option<PsPath>, pub pattern_store: Vec<PatternData>, pub form_cache: FxHashMap<EntityId, DisplayList>, pub cie_decode_cache: FxHashMap<CieDecodeKey, Vec<f64>>, pub start_time: Option<Instant>, pub dict_version: u64, pub name_resolve_cache: Vec<(u64, PsObject)>, pub interrupt_flag: Option<Arc<AtomicBool>>, pub yield_after_showpage: bool,
}

Fields§

§o_stack: Stack§e_stack: Stack§d_stack: Vec<EntityId>§strings: DualStringStore§arrays: DualArrayStore§dicts: DualDictStore§names: NameTable§files: FileStore§loops: Vec<LoopState>§operators: Vec<OpEntry>§systemdict: EntityId§globaldict: EntityId§userdict: EntityId§errordict: EntityId§dollar_error: EntityId§rand_state: u64§rand_seed: i32§current_source_line: u32

Current source line number (1-based), updated during scanning.

§packing_mode: bool

Packing mode for array/procedure creation (setpacking/currentpacking).

§echo: bool

Echo mode for %lineedit/%statementedit (PLRM echo operator).

§name_cache: NameCache§stdout: Box<dyn Write>§save_stack: SaveStack§job_start_save_depth: usize

Save stack depth when the current job started (for startjob condition 3).

§vm_alloc_mode: bool§object_format: i32

Binary object format (0-4). Default 0.

§current_operator: Option<NameId>§in_error_handler: bool§initializing: bool

True during init script execution — relaxes access checks.

§allow_ps_resolution: bool

When true, PS programs can change HWResolution via setpagedevice. Set by WASM frontend; CLI leaves false to keep DPI under user control.

§exit_code: Option<i32>

Process exit code requested by the running PS program via the .quitwithcode operator. None means “use the default” (0 on success). The CLI reads this on PsError::Quit and propagates to std::process::exit.

§gstate: GraphicsState§gstate_stack: Vec<GstateEntry>§gstate_store: Vec<GraphicsState>

Storage for gstate objects (PsValue::Gstate indexes into this).

§device: Option<Box<dyn OutputDevice>>§display_list: DisplayList§group_stack: Vec<GroupFrame>

Stack of active transparency-group capture frames. While non-empty, paint operators emit into the topmost frame’s display list instead of display_list. endtransparencygroup pops the top frame and emits a stet_graphics::display_list::DisplayElement::Group into the next-innermost target. See op_begintransparencygroup / op_endtransparencygroup in stet-ops::transparency_ops.

§save_group_depths: FxHashMap<u32, usize>

group_stack.len() recorded at each save. restore consults this to refuse a revert that would unwind across an unbalanced begintransparencygroup / endtransparencygroup pair.

§ocg_registry: FxHashMap<NameId, OcgRecord>

Registry of OCGs (PDF Optional Content Groups) declared via the defineocg operator. Keyed by the interned NameId of the human-readable layer name from the OCG dict’s /Name entry. beginoptionalcontent looks up an OCG by name to obtain the ocg_id and default_visible it embeds into the emitted stet_graphics::display_list::OcgVisibility::Single.

§next_ocg_id: u32

Monotonic counter feeding OcgRecord::ocg_id. Each call to defineocg increments this; ids never recycle.

§doc_structure: DocumentStructure

Document-level structural data — outline, annotations, metadata, page boxes, etc. — parallel IR to display_list. PostScript pdfmark operators populate this; the PDF output device drains it at end-of-job; non-PDF devices ignore it. Document-global: save / restore do not roll this back. See stet_graphics::document_structure.

§capture_display_lists: Option<Vec<(DisplayList, f64)>>

When Some, each showpage clones the display list here before consuming it. Used by the WASM frontend to retain display lists for viewport re-rendering. Each entry is (DisplayList, dpi) where dpi is from the pagedevice HWResolution.

§display_list_sender: Option<Sender<(DisplayList, f64, u32, u32, Option<Arc<Vec<u8>>>, bool)>>

When Some, each showpage sends a clone of the display list through this channel. Used by the CLI viewer for incremental display list delivery. Tuple: (DisplayList, dpi, page_width, page_height, effective_cmyk_bytes, cmyk_proofing). PostScript pages always pass None/false (no PDF/X concept); PDF pages may set these from the document’s OutputIntent context.

§page_width: u32§page_height: u32§output_path: Option<String>§page_filter: Option<HashSet<i32>>

Page filter: if set, only render pages in this set (1-based).

§device_factory: Option<Box<dyn Fn(u32, u32) -> Box<dyn OutputDevice>>>

Factory closure for creating raster devices (registered by CLI).

§font_directory: EntityId§font_resource_path: Option<String>§next_fid: i32§global_resources: EntityId§local_resources: EntityId§category_registry: EntityId§resource_base_path: Option<String>§user_params: EntityId§system_params: EntityId§internaldict: EntityId

Backing dict for the internaldict operator.

Created during bootstrap rather than on first use. Context holds the EntityId for the whole life of the interpreter, so the dict has to outlive every restore; creating it lazily inside a save bracket would leave this handle pointing at storage that restore reclaims. Entries written into it after a save are still reverted normally, by the dict’s own copy-on-write.

§icc_cache: IccCache§exec_sync_fn: Option<ExecSyncFn>§char_width: Option<(f64, f64)>§char_width_mode1: Option<((f64, f64), (f64, f64))>§glyph_caches: FxHashMap<EntityId, GlyphCache>§char_cache_mode: Option<Type3CacheMode>§cshow_pending_cid: Option<i32>§charpath_capture: Option<PsPath>

Set while a Type 3 glyph procedure runs under charpath.

PLRM: charpath “obtains the path for the glyph outlines that would result if string were shown”; for a Type 3 font that means running the glyph procedure without painting. While this is Some, fill, eofill and stroke contribute the path they were given here instead of marking the page — in particular stroke contributes the path as constructed, since charpath’s own boolean operand, not the glyph procedure, decides whether the result gets stroked.

§pattern_store: Vec<PatternData>

Storage for pattern instances created by makepattern.

§form_cache: FxHashMap<EntityId, DisplayList>

Cache of form display lists keyed by dict EntityId.

§cie_decode_cache: FxHashMap<CieDecodeKey, Vec<f64>>

Memo of sampled CIE decode tables, keyed by a structural fingerprint of the decode procedure together with the sampled range.

Sampling one table runs the procedure 256 times, and a CIE colour space installs up to six of them, so a file that re-installs the same space per page (what pdftops emits for every ICCBased space) pays thousands of exec_sync calls per page. Worse, decode procedures routinely contain inline array literals, and every evaluation allocates a fresh array in the non-reclaiming array arena — turning the repeated sampling into unbounded memory growth.

Memoising is sound because the PLRM already requires a CIE decode procedure to be a pure function of its single input: sampling it at 256 points and interpolating (which this code has always done) is the same assumption. See stet_ops::color_ops::eval_decode_table_range.

§start_time: Option<Instant>§dict_version: u64§name_resolve_cache: Vec<(u64, PsObject)>

Name resolution cache indexed by NameId. Each entry is (dict_version, resolved_object). Public for inline cache checks in the eval loop’s hot path.

§interrupt_flag: Option<Arc<AtomicBool>>

When set, the eval loop aborts with PsError::Quit on the next iteration. Used by the interactive viewer to cancel an in-flight parse when the user drops a new file.

§yield_after_showpage: bool

When true, each successful showpage / copypage sets interrupt_flag after capturing the display list, so the eval loop yields back to the caller one page at a time. The caller clears the flag and re-enters eval to drive the next page. Used by the WASM viewer to stream multi-page PostScript documents: page 1 renders while pages 2..N are still pending interpretation. Requires interrupt_flag to be set.

Implementations§

Source§

impl Context

Source

pub fn exec_sync(&mut self, proc_obj: PsObject) -> Result<(), PsError>

Execute a PostScript procedure synchronously and return.

Source

pub fn pump_proc_sources(&mut self, entity: EntityId) -> Result<(), PsError>

Run any not-yet-executed procedure data source underneath entity, replacing it with the bytes it produces.

A filter’s data source may be a procedure (PLRM 3.8.4), which the filter is supposed to call for more data as the consumer reads. Running it when filter is called instead is observably wrong: the procedure runs against whatever is on the operand stack at that moment. pdftops builds an inline image as

<< /ImageType 1 ... /DataSource { pdfImStr } /LZWDecode filter >> imagemask

so filter is reached while the enclosing << ... >> is still on the stack, and pdfImStr — which reads its array index state off the stack — would pick up the half-built dictionary instead.

So the procedure is run here, from the read path, when the consumer’s operands are the ones in place. Every entry point that reads from a file must call this first; see [FileHandle::PendingProc].

The drain is one-shot rather than incremental: the procedure is run to completion and the result installed as a plain byte source. That keeps the filters themselves unchanged — none of them has to cope with a source that is temporarily dry — while still running the procedure at the right moment, which is what the bug above is about.

Source

pub fn new() -> Self

Create a new context with empty stacks and stores. Call build_system_dict afterward to populate operators.

Source

pub fn new_with_output(output: Box<dyn Write>) -> Self

Create a context that captures stdout to a buffer (for testing).

Source

pub fn dict_load(&mut self, key: &DictKey) -> Option<PsObject>

Look up a name in the dictionary stack (top to bottom).

Source

pub fn invalidate_name_cache(&mut self)

Invalidate the name resolution cache (call on begin/end/def).

Source

pub fn dict_where(&self, key: &DictKey) -> Option<(EntityId, PsObject)>

Look up and return (dict_entity, value) pair.

Source

pub fn dict_def(&mut self, key: DictKey, value: PsObject) -> Result<(), PsError>

Store in current dict (top of d_stack).

Source

pub fn dict_store( &mut self, key: DictKey, value: PsObject, ) -> Result<(), PsError>

Store in first dict that contains key, or current dict if not found.

Source

pub fn make_dict_key(&mut self, obj: &PsObject) -> Result<DictKey, PsError>

Convert a PsObject to a DictKey.

Source

pub fn alloc_loop(&mut self, state: LoopState) -> EntityId

Allocate a new loop state, returning its EntityId.

Source

pub fn get_loop(&self, entity: EntityId) -> &LoopState

Get a loop state by EntityId.

Source

pub fn get_loop_mut(&mut self, entity: EntityId) -> &mut LoopState

Get a mutable loop state by EntityId.

Source

pub fn current_display_list_mut(&mut self) -> &mut DisplayList

Return the display list paint operators should currently append to.

While a transparency group is active (group_stack non-empty), the topmost frame’s display list is returned. Otherwise the page-level display_list is returned. Every paint-emitting operator must route through this helper to keep group capture correct.

Source

pub fn current_display_list(&self) -> &DisplayList

Read-only counterpart to Self::current_display_list_mut.

Source

pub fn take_display_list(&mut self) -> DisplayList

Take the display list, optionally capturing a clone for viewport re-rendering.

This replaces std::mem::take(&mut ctx.display_list) at showpage/copypage call sites. When capture_display_lists is active, a clone is saved along with the current page DPI from the pagedevice HWResolution.

Source

pub fn current_page_dpi(&self) -> f64

Read the current page DPI from the pagedevice HWResolution, defaulting to 72.

Source

pub fn vm_save(&mut self) -> PsObject

Source

pub fn vm_restore(&mut self, save_id: u32) -> Result<(), PsError>

Perform a restore: revert VM to the given save state.

Source

pub fn cow_check_string(&mut self, entity: EntityId)

Check if a string entity needs COW before mutation. If yes, creates a backup copy and records it.

Source

pub fn cow_check_array(&mut self, entity: EntityId)

Check if an array entity needs COW before mutation.

Source

pub fn dict_put_cow(&mut self, entity: EntityId, key: DictKey, value: PsObject)

Check if a dict entity needs COW before mutation. Store into a dictionary, copy-on-writing it first.

Prefer this over a bare ctx.dicts.put for any write into a dictionary the current operation did not itself allocate — FontDirectory, the resource dictionaries, userdict, a caller-supplied dict. Writing straight through bypasses save/restore: if the dictionary predates the current save, no backup is taken and restore will not revert the entry. That leaves the dictionary holding a value the restore released.

cow_check_dict is cheap and idempotent — it returns immediately at save level 0, for global entities, and for dictionaries already copied at this level — so there is no reason to skip it when in doubt.

Source

pub fn cow_check_dict(&mut self, entity: EntityId)

Source

pub fn token_to_object(&mut self, token: Token) -> Result<PsObject, PsError>

Convert a tokenizer token into a PsObject.

Source

pub fn reset_local_vm(&mut self)

Reset local VM stores (for job boundary cleanup). Full implementation deferred until job server loop is built.

Trait Implementations§

Source§

impl Default for Context

Source§

fn default() -> Self

Returns the “default value” for a type. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T, S> SimdFrom<T, S> for T
where S: Simd,

Source§

fn simd_from(value: T, _simd: S) -> T

Source§

impl<F, T, S> SimdInto<T, S> for F
where T: SimdFrom<F, S>, S: Simd,

Source§

fn simd_into(self, simd: S) -> T

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.