Differences
This shows you the differences between two versions of the page.
Both sides previous revision Previous revision Next revision | Previous revision Next revision Both sides next revision | ||
postscript [2020/02/10 13:36] christian [Get the code] |
postscript [2020/02/23 10:20] christian [Get the code] |
||
---|---|---|---|
Line 9: | Line 9: | ||
===== Get the code ===== | ===== Get the code ===== | ||
- | The code is in the package [PostScript] | + | Load the package |
+ | Tests are in package **[PostScript Testing]**. The package can be used stand-alone without PDFtalk. It only depends on the **[Values]** package. | ||
+ | |||
+ | The bundle **{PDFtalk}** now depends on this package and loads PostScript automatically. | ||
+ | |||
+ | ===== Use it ===== | ||
+ | |||
+ | <code smalltalk> | ||
+ | | ps | | ||
+ | ps := PostScript.Interpreter run: '3 4 add' | ||
+ | </ | ||
+ | |||
+ | returns an instance of the interpreter which consumed and processed the PostScript language string. | ||
+ | |||
+ | <code smalltalk> | ||
+ | ps pop | ||
+ | </ | ||
+ | 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" | ||
+ | </ | ||
===== Motivation ===== | ===== Motivation ===== | ||
- | * need it for PDF, especially | + | PDF is the successor of PostScript and still depends on it in some places: |
- | * beauty of the language | + | * CMaps in fonts |
- | * used to programm | + | * 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 Solaris NeWS workstation. The language allows for some nice programming patterns. | ||
===== PostScript programming introduction ===== | ===== PostScript programming introduction ===== | ||
Line 39: | Line 70: | ||
/seven { 3 4 add } def | /seven { 3 4 add } def | ||
</ | </ | ||
- | This stores the procedure '' | + | This stores the procedure '' |
+ | <code PostScript> | ||
+ | seven dup mul | ||
+ | </ | ||
+ | leaves 49 on the stack. | ||
At the start of a PostScript interpreter, | At the start of a PostScript interpreter, | ||
Line 45: | Line 80: | ||
* **global dict** " | * **global dict** " | ||
* **system dict** " | * **system dict** " | ||
+ | |||
+ | New definitions are put into the topmost dictionary of the stack. This allows programs to override system definitions. The NeWS(([[https:// | ||
+ | |||
+ | 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 | ||
+ | </ | ||
+ | |||
+ | This creates a recursive structure which is not handled well by the standard Smalltalk dictionary, hence '' | ||
+ | |||
+ | Interestingly, | ||
+ | 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 | + | 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 | ||
+ | |||
+ | 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 | ||
+ | </ | ||
+ | the program started with | ||
+ | <code postscript> | ||
+ | /CIDInit /ProcSet find begin 12 dict begin begincmap | ||
+ | </ | ||
+ | Instead of '' | ||
+ | |||
+ | To fix this I tried two approaches: the PostScript and the Smalltalk way. | ||
+ | |||
+ | In PostScript, you can just define the missing '' | ||
+ | <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 / | ||
+ | ex return: (ps1 run: source) resources] | ||
+ | ifFalse: [ | ||
+ | ex pass]]. | ||
+ | ^self newWith: ((resources at: #CMap) at: #F1) | ||
+ | </ | ||
+ | |||
+ | 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: # | ||
+ | ifFalse: [ex pass]]. | ||
+ | ^self newWith: ((resources at: #CMap) at: #F1) | ||
+ | </ | ||
+ | |||
+ | I think that both approaches are very elegant. | ||