Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revision Previous revision
Next revision
Previous revision
Last revision Both sides next revision
postscript [2020/02/11 08:27]
christian [Get the code]
postscript [2020/02/23 10:24]
christian [Motivation]
Line 10: Line 10:
  
 Load the package **[PostScript]** from the [[storeaccess|Cincom Public Store]] into your [[http://www.cincomsmalltalk.com/main/products/visualworks/|VisualWorks]] image. Load the package **[PostScript]** from the [[storeaccess|Cincom Public Store]] into your [[http://www.cincomsmalltalk.com/main/products/visualworks/|VisualWorks]] image.
 +Tests are in package **[PostScript Testing]**. The package can be used stand-alone without PDFtalk. It only depends on the **[Values]** package.
  
-Tests are in package **[PostScript Testing]**.+The bundle **{PDFtalk}** now depends on this package and loads PostScript automatically. 
 + 
 +===== Use it ===== 
 + 
 +<code smalltalk> 
 +| ps | 
 +ps := PostScript.Interpreter run: '3 4 add'
 +</code> 
 + 
 +returns an instance of the interpreter which consumed and processed the PostScript language string. 
 + 
 +<code smalltalk> 
 +ps pop 
 +</code> 
 +returns and removes the top of the operand stack - in this case **7**. 
 + 
 +You can also run successive pieces of PostScript code: 
 +<code smalltalk> 
 +| ps | 
 +ps := PostScript.Interpreter run: '3 4 add'
 +ps run: 'dup mul'
 +ps stack top"is 49" 
 +</code>
  
-PostScript is now a prerequisite of PDFtalk. 
 ===== Motivation ===== ===== Motivation =====
  
-  * need it for PDF, especially CMaps +PDF is the successor of PostScript and still depends on it in some places: 
-  * beauty of the language +  * CMaps in fonts 
-  * used to programm UIs with Display PostScript on a Sun Solaris NeWS workstation+  * Type1 fonts 
 +  * PostScript calculator functions 
 +  * PostScript XObjects (deprecated) 
 + 
 +Ultimately I want to extract text from PDFs for which I need CMaps. 
 + 
 +Also, I like PostScript. I used it quite a bit to write UIs with Display PostScript on a Sun NeWS(([[https://en.wikipedia.org/wiki/NeWS|NeWS]] Wikipedia article)) workstation. The language allows for some nice programming patterns.
 ===== PostScript programming introduction ===== ===== PostScript programming introduction =====
  
Line 42: Line 70:
 /seven { 3 4 add } def /seven { 3 4 add } def
 </code> </code>
-This stores the procedure ''{3 4 add}'' under the name ''/seven'' in the top dictionary of the dictionary stack.+This stores the procedure ''{3 4 add}'' under the name ''/seven'' in the top dictionary of the dictionary stack. The new operator can be used to put a 7 onto the stack: 
 +<code PostScript> 
 +seven dup mul 
 +</code> 
 +leaves 49 on the stack.
  
 At the start of a PostScript interpreter, the dictionary stack contains 3 dictionaries: At the start of a PostScript interpreter, the dictionary stack contains 3 dictionaries:
Line 48: Line 80:
   * **global dict** "globally accessible definitions. Initially empty"   * **global dict** "globally accessible definitions. Initially empty"
   * **system dict** "bottom of the stack. Read only with build-in operators"   * **system dict** "bottom of the stack. Read only with build-in operators"
 +
 +New definitions are put into the topmost dictionary of the stack. This allows programs to override system definitions. The NeWS system from Sun used this to implement an object oriented Display PostScript with the dictionaries as classes. Another view is to see the dictionaries as namespaces.
 +
 +The dictionaries can be named with an interesting trick: the dictionary itself is stored under a key which is interpreted as its name. The system dict for example is constructed like:
 +<code smalltalk>
 +| dict |
 +dict := PSDictionary new.
 +dict at: #systemdict put: dict.
 +^dict
 +</code>
 +
 +This creates a recursive structure which is not handled well by the standard Smalltalk dictionary, hence ''PSDictionary'' as my implementation of it.
 +
 +  Interestingly, Gemstone has exactly the same dictionaries implementing method lookup.
 +  Including the naming scheme for dictionaries!
 +
 +(more will be written on demand...)
  
 ===== Implementation notes ===== ===== Implementation notes =====
  
-  * no global / local differenciation +This implementation does not have any graphics operators; it is just about the programming language. 
-  * no graphics operators + 
-  * exception handling example+There is a distinction about local and global VM, which, in my view, is an optimization not important for the language. Therefore, I did not implement this distiction (and hope that it really does not have any importance...). 
 + 
 +==== Exception handling example ==== 
 + 
 +When looking at CMaps in the wild, I encountered one where the PostScript was wrong. Instead of 
 +<code postscript> 
 +/CIDInit /ProcSet findresource begin 12 dict begin begincmap    % ... 
 +</code> 
 +the program started with 
 +<code postscript> 
 +/CIDInit /ProcSet find begin 12 dict begin begincmap    % ... 
 +</code> 
 +Instead of ''findresource'' the non existing operator ''find'' was used. 
 + 
 +To fix this I tried two approaches: the PostScript and the Smalltalk way. 
 + 
 +In PostScript, you can just define the missing ''find'' operator and read it again: 
 +<code smalltalk> 
 +| source ps resources | 
 +source := self exampleWrongSource. 
 +ps := Interpreter new. 
 +resources := [(ps run: source) resources] on: KeyNotFoundError do: [:ex | 
 + (ex selector = #valueAt: and: [ 
 + ex parameter = #find]) 
 + ifTrue: [ 
 + | ps1 | 
 + ps1 := Interpreter run: '/find /findresource cvx def'
 + ex return: (ps1 run: source) resources] 
 + ifFalse: [ 
 + ex pass]]. 
 +^self newWith: ((resources at: #CMap) at: #F1) 
 +</code> 
 + 
 +In Smalltalk you could just call the correct operator and resume: 
 + 
 +<code smalltalk> 
 +| source ps resources | 
 +source := self exampleWrongSource. 
 +ps := Interpreter new. 
 +resources := [(ps run: source) resources] on: KeyNotFoundError do: [:ex | 
 + (ex selector = #valueAt: and: [ 
 + ex parameter = #find]) 
 + ifTrue: [ex resume: (ex receiver valueAt: #findresource)] 
 + ifFalse: [ex pass]]. 
 +^self newWith: ((resources at: #CMap) at: #F1) 
 +</code> 
 + 
 +I think that both approaches are very elegant.
  
  • postscript.txt
  • Last modified: 2020/02/23 15:53
  • by christian