Table of Contents

1. Dictionaries

Dictionaries are associative arrays that store key-value pairs. They are fundamental to PostScript’s name resolution system, variable scoping, and object-oriented features. Understanding dictionaries is essential for advanced PostScript programming.

1.1. Overview

A dictionary is a composite object that maps keys to values. Keys are typically names, but can be any object. Dictionaries provide fast lookup, dynamic binding, and hierarchical scoping through the dictionary stack.

1.2. Dictionary Characteristics

1.2.1. Associative Array

Dictionaries store pairs of keys and values:

Dictionary structure
<< /name (John) /age 30 >>      % Dictionary with two entries
<< /x 42 /y 100 >>              % Coordinate dictionary
<< >>                            % Empty dictionary

1.2.2. Key-Value Pairs

Each entry in a dictionary associates a key with a value:

Key-value associations
% Key: /myname → Value: 42
/myname 42 def

% Key: /proc → Value: { 1 add }
/proc { 1 add } def

% Any object can be a value
/data [1 2 3] def

1.2.3. Composite Object

Dictionaries are composite objects with:

  • Capacity: Maximum number of entries it can hold

  • Length: Current number of entries

  • Access: Read/write permissions

1.3. Dictionary Creation

1.3.1. Using dict Operator

The dict operator creates a dictionary with specified capacity:

Creating with dict
10 dict             % Create dictionary with capacity 10
0 dict              % Empty dictionary (minimal capacity)
100 dict            % Large dictionary

% Capacity vs length
10 dict
dup /a 1 put
dup /b 2 put
dup maxlength       % Returns 10 (capacity)
length              % Returns 2 (current entries)

1.3.2. Level 2 Dictionary Syntax

Level 2+ supports inline dictionary creation with << >>:

Inline dictionary syntax
% Basic syntax
<< /key value >>

% Multiple entries
<< /x 10 /y 20 /z 30 >>

% Mixed types
<<
    /name (PostScript)
    /version 3
    /active true
    /data [1 2 3]
>>

% Nested dictionaries
<<
    /outer <<
        /inner 42
    >>
>>

1.3.3. Building Dictionaries Step by Step

Programmatic dictionary construction
% Create and populate
10 dict begin
    /x 100 def
    /y 200 def
    /color (red) def
currentdict end     % Returns populated dictionary

% Or using put
10 dict
dup /x 100 put
dup /y 200 put
dup /color (red) put

1.4. Dictionary Operations

1.4.1. Basic Operations

Operator Stack Effect Description

dict

int → dict

Create dictionary

length

dict → int

Get entry count

maxlength

dict → int

Get capacity

begin

dict → -

Push onto dict stack

end

- → -

Pop from dict stack

def

key value → -

Define in current dict

load

key → value

Look up value

store

key value → -

Store in dict on stack

get

dict key → value

Get value from dict

put

dict key value → -

Put value in dict

known

dict key → bool

Test if key exists

undef

dict key → -

Remove key from dict

Basic operation examples
% Create and populate
5 dict dup
dup /x 10 put
dup /y 20 put

% Access
dup /x get          % Returns 10
dup /x known        % Returns true
dup /z known        % Returns false

% Modify
dup /x 99 put       % Change x to 99

% Remove
dup /x undef        % Delete x entry

1.4.2. Dictionary Stack Operations

Operator Stack Effect Description

begin

dict → -

Push dict onto dict stack

end

- → -

Pop dict from dict stack

countdictstack

- → int

Count dict stack depth

dictstack

array → subarray

Copy dict stack to array

currentdict

- → dict

Get topmost dict

Dictionary stack examples
% Stack depth
countdictstack      % Returns initial depth

% Push dictionary
10 dict begin
countdictstack      % Depth increased by 1

% Get current dict
currentdict /x 42 put

% Pop dictionary
end
countdictstack      % Back to initial depth

1.5. The Dictionary Stack

1.5.1. Stack Structure

PostScript maintains a stack of dictionaries for name resolution:

Dictionary stack hierarchy
Dictionary Stack (top to bottom):
┌─────────────────────┐
│  User Dictionary    │ ← Most recent begin
├─────────────────────┤
│  Local Dictionary   │
├─────────────────────┤
│  Global Dictionary  │
├─────────────────────┤
│  User Dict          │
├─────────────────────┤
│  System Dict        │ ← Bottom (always present)
└─────────────────────┘

1.5.2. Name Resolution

When a name is executed, PostScript searches the dictionary stack top to bottom:

Name lookup process
% systemdict has: /add → <operator>
% userdict is empty

% Lookup finds add in systemdict
add                 % Uses built-in operator

% Override in userdict
/add { mul } def    % Define in current dict

% Now lookup finds add in userdict first
add                 % Uses redefined version (multiply)

% Original still in systemdict
systemdict /add get exec  % Uses original add

1.5.3. Scope Management

Creating scoped contexts
% Global scope
/x 100 def

% Local scope
10 dict begin
    /x 42 def       % Local x shadows global
    x               % Returns 42 (local)
end

% Back to global scope
x                   % Returns 100 (global)

1.6. Standard Dictionaries

1.6.1. systemdict

Contains all built-in operators and PostScript language features:

systemdict contents
% Access systemdict
systemdict /add known       % true
systemdict /moveto known    % true
systemdict length           % Large number (all operators)

% Get operator
systemdict /add get         % Returns add operator

1.6.2. userdict

The default dictionary for user definitions:

userdict usage
% Definitions go in userdict by default
/myvar 42 def

% Equivalent to:
userdict /myvar 42 put

% Access
userdict /myvar get         % Returns 42

1.6.3. globaldict (Level 2+)

Persistent dictionary across jobs:

globaldict in Level 2+
% Check VM mode
currentglobal               % false (local VM)

% Switch to global VM
true setglobal
globaldict /persistent 42 put
false setglobal

% Value persists across jobs

1.6.4. $error Dictionary

Contains error handling information:

Error information
% After an error occurs
$error /newerror get        % true if error occurred
$error /errorname get       % Name of error
$error /command get         % Command that caused error

1.6.5. statusdict

Device and system status information:

Status queries
statusdict /product get     % Device name
statusdict /version get     % Version info
statusdict /resolution get  % Device resolution

1.7. Dictionary Patterns

1.7.1. Local Variables

Dictionary-based locals
/MyProc {
    % in: x y z

    % Create local scope
    3 dict begin
        /z exch def
        /y exch def
        /x exch def

        % Use local variables
        x y add z mul
    end
    % out: result
} def

1 2 3 MyProc                % Returns (1+2)*3 = 9

1.7.2. Configuration Dictionary

Storing configuration
/Config <<
    /PageWidth 612
    /PageHeight 792
    /Margin 72
    /Font /Times-Roman
    /FontSize 12
    /Color [0 0 0]
>> def

% Access configuration
Config /PageWidth get       % Returns 612
Config /Font get           % Returns /Times-Roman

1.7.3. Object-Like Structures

Dictionary as object
/MakePoint {
    % in: x y
    % out: point-dict

    <<
        /x 3 -1 roll
        /y 3 -1 roll
        /distance {
            % Closure over x and y
            x dup mul y dup mul add sqrt
        } bind
    >>
} def

% Create point
10 20 MakePoint /pt exch def

% Access fields
pt /x get                   % Returns 10
pt /y get                   % Returns 20

% Call method
pt /distance get exec       % Returns distance from origin

1.7.4. Namespace Management

Module pattern
% Create module dictionary
/MyModule 50 dict def
MyModule begin
    % Private helpers (not exported)
    /_helper { 2 mul } def

    % Public interface
    /PublicFunc {
        _helper 1 add
    } def

    /PublicVar 42 def
end

% Use module
MyModule /PublicFunc get exec
MyModule /PublicVar get
% MyModule /_helper get        % Can access but not intended

1.8. Dictionary Iteration

1.8.1. Using forall

Iterating dictionary entries
% Print all entries
<< /a 1 /b 2 /c 3 >> {
    % Stack: key value
    exch =only ( : ) print =
} forall

% Count entries (alternative to length)
0 << /a 1 /b 2 /c 3 >> {
    pop pop 1 add
} forall                    % Returns 3

1.8.2. Filtering Entries

Selecting dictionary entries
% Copy entries matching condition
<<
    /a 10
    /b 20
    /c 30
    /d 40
>>
10 dict exch {
    % Copy only entries with values > 15
    1 index 15 gt {
        3 copy pop put
    } {
        pop pop
    } ifelse
} forall
% Result: << /b 20 /c 30 /d 40 >>

1.9. Dictionary Copying

1.9.1. Shallow Copy

Copying dictionary
/source << /a 1 /b 2 /c 3 >> def

% Method 1: Copy entries
source dup length dict exch {
    3 copy pop put
} forall

% Method 2: Using copy (Level 2+)
source dup length dict copy

1.9.2. Merging Dictionaries

Combining dictionaries
/Merge {
    % in: dict1 dict2
    % out: merged-dict

    % Create result dict
    2 copy length exch length add dict

    % Copy first dictionary
    3 1 roll {
        3 copy pop put
    } forall

    % Copy second dictionary (may override)
    exch {
        3 copy pop put
    } forall
} def

<< /a 1 /b 2 >> << /c 3 /d 4 >> Merge
% Result: << /a 1 /b 2 /c 3 /d 4 >>

1.10. Dictionary Lookup Optimization

1.10.1. where Operator

Find which dictionary contains a key:

Finding definitions
% where: key → dict true | false

/add where {
    % Found, dict is on stack
    /add get type =         % /operatortype
} {
    % Not found
    (Undefined) =
} ifelse

% Check custom definition
/myvar where {
    pop /myvar get
} {
    0                       % Default value
} ifelse

1.10.2. Cached Lookups

Cache frequently used values
% SLOW - repeated lookup
/MyProc {
    /Config where { pop Config /Value get } if
    % ... use Value multiple times ...
    /Config where { pop Config /Value get } if
    /Config where { pop Config /Value get } if
} def

% FAST - cache the value
/MyProc {
    /Config where {
        pop Config /Value get
        dup dup                 % Use cached value
    } if
} def

1.11. Dictionary Scoping Patterns

1.11.1. Save/Restore Pattern

Temporary definitions
% Save state
save

% Make temporary changes
/temp-dict 20 dict def
temp-dict begin
    /x 100 def
    /y 200 def
    % ... use x and y ...
end

% Restore state (removes temp-dict)
restore

1.11.2. Begin/End Pattern

Scoped execution
/WithContext {
    % in: dict proc

    exch begin
        exec
    end
} def

% Usage
<< /x 10 /y 20 >> {
    % x and y available here
    x y add
} WithContext               % Returns 30

1.11.3. Isolated Scope

Creating isolated context
/IsolatedExec {
    % in: proc

    % Save current dict stack depth
    countdictstack exch

    % Create isolated environment
    10 dict begin
        % Execute procedure
        stopped {
            % Handle errors
        } if
    end

    % Restore dict stack depth
    countdictstack exch sub {
        end
    } repeat
} def

1.12. Dictionary as Data Structure

1.12.1. Hash Table Behavior

Dictionaries provide O(1) average lookup time:

Fast lookup
% Create large lookup table
1000 dict begin
    0 1 999 {
        dup dup 100 string cvs
        exch def
    } for
currentdict end /lookup exch def

% Fast access
lookup (500) load           % Quick lookup

1.12.2. Set Implementation

Using dictionary as set
/MakeSet {
    % in: array
    % out: set-dict

    dup length dict exch {
        1 index exch null put
    } forall
} def

/Contains {
    % in: set key
    % out: bool

    exch known
} def

% Usage
[1 2 3 4 5] MakeSet /myset exch def
myset 3 Contains            % true
myset 10 Contains           % false

1.12.3. Sparse Array

Dictionary as sparse array
/SparseArray 100 dict def

% Set values at arbitrary indices
SparseArray 1000 (value1000) put
SparseArray 5000 (value5000) put
SparseArray 10000 (value10000) put

% Retrieve
SparseArray 5000 get        % (value5000)
SparseArray 2000 known      % false

1.13. Dictionary Performance

1.13.1. Capacity Planning

Choosing initial capacity
% POOR - will need to grow
5 dict
% ... add 100 entries (slow)

% GOOD - right-sized from start
100 dict
% ... add 100 entries (fast)

% Size to expected entries + ~20%
/expected-entries 80 def
expected-entries 1.2 mul cvi dict

1.13.2. Growth Behavior

Dictionaries automatically grow when full:

Automatic growth
% Small initial capacity
5 dict dup
dup /a 1 put
dup /b 2 put
dup /c 3 put
dup /d 4 put
dup /e 5 put
dup maxlength =         % 5
dup /f 6 put           % Causes growth
maxlength =             % Larger (implementation-dependent)

1.14. Dictionary Best Practices

1.14.1. Naming Conventions

Key naming standards
% Use descriptive names
/PageWidth 612 def          % GOOD
/pw 612 def                 % POOR

% Prefix private entries
/_internalHelper { } def    % Private
/PublicAPI { } def          % Public

% Use namespaces for modules
/MyModule.Init { } def
/MyModule.Process { } def

1.14.2. Scope Management

Clean scope handling
% GOOD - explicit scope
10 dict begin
    % ... definitions ...
end

% BAD - leaked scope
10 dict begin
    % ... definitions ...
% Missing end - dict stays on stack!

% BETTER - guaranteed cleanup
10 dict begin
    {
        % ... work ...
    } stopped pop
end

1.14.3. Resource Management

Dictionary cleanup
% Clear large dictionary when done
/bigdict 1000 dict def
% ... use bigdict ...
bigdict begin
    { currentdict { pop undef } forall } stopped pop
end
% or
/bigdict null def          % Release reference

1.15. Common Pitfalls

1.15.1. Dictionary Stack Imbalance

Unmatched begin/end
% WRONG - missing end
10 dict begin
    /x 42 def
    % ... more code ...
% Dictionary stays on stack!

% RIGHT - balanced
10 dict begin
    /x 42 def
end                         % Properly removed

1.15.2. Key Type Confusion

Name vs string keys
% These are DIFFERENT:
<< /name 42 >> /name get    % Works: 42
<< /name 42 >> (name) get   % Error: string ≠ name

% Keys must match exactly
/dict 10 dict def
dict /key 1 put
dict /key get               % OK
dict (key) cvn get         % OK (converted to name)
dict (key) get             % ERROR

1.15.3. Scope Leakage

Variables in wrong scope
% WRONG - pollutes current dict
/GlobalProc {
    /x 42 def              % Defines in current dict
} def

% RIGHT - uses local dict
/LocalProc {
    1 dict begin
        /x 42 def          % Local to this dict
        x                   % Use it
    end
} def

1.15.4. Dictionary Modification During Iteration

Iterator invalidation
% DANGEROUS
<< /a 1 /b 2 /c 3 >> {
    pop                     % Key
    dup (remove) eq {
        currentdict exch undef  % Modifying during iteration!
    } if
} forall

% SAFE - collect keys first
<< /a 1 /b 2 /c 3 >>
[ exch { pop } forall ]     % Collect all keys
{ ... } forall              % Now safe to modify

1.16. Advanced Dictionary Techniques

1.16.1. Method Dispatch

Dynamic dispatch table
/Object <<
    /type /MyObject
    /data [1 2 3]

    /Methods <<
        /print {
            (Object: ) print
            Object /data get =
        } bind

        /size {
            Object /data get length
        } bind
    >>
>> def

% Invoke method
Object /Methods get /print get exec
Object /Methods get /size get exec

1.16.2. Prototype-Based Inheritance

Prototype chain
/Prototype <<
    /sharedMethod { (From prototype) = } bind
>> def

/Instance <<
    /prototype Prototype
    /instanceData 42
>> def

% Lookup with fallback
/Lookup {
    % in: object key
    % out: value

    2 copy known {
        get
    } {
        exch /prototype get
        exch Lookup         % Recursive lookup
    } ifelse
} def

Instance /sharedMethod Lookup exec

1.16.3. Memoization

Caching computed values
/FibonacciMemo << >> def

/Fibonacci {
    % in: n
    % out: fib(n)

    % Check cache
    FibonacciMemo 1 index known {
        FibonacciMemo exch get
    } {
        % Compute
        dup 2 lt {
            % Base case
        } {
            dup 1 sub Fibonacci
            exch 2 sub Fibonacci
            add
        } ifelse

        % Store in cache
        dup
        FibonacciMemo 3 1 roll put
    } ifelse
} def

1.17. See Also


Back to top

Copyright © 2025 Ribose. PostScript is a trademark of Adobe. Distributed under the MIT License.