Skip to content

Assorted PostScript snippets I wrote for fun.

License

Notifications You must be signed in to change notification settings

Alhadis/Stupid-Post-Tricks

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

35 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

sim.ps

PostScript snippets that I gone and dun.

Because who doesn't love a good gs -sDEVICE=txtwrite -sOutputFile=- -q -sBATCH -dNOPAUSE -dNOSAFER *.ps?


inspect.ps (moved to Alhadis/Inspect.ps)

A recursive inspection function that prints spiffy-looking colours to stdout. Basically, what == would be if it didn't suck at inspecting substructures:


Using ==
-dict-
Using ===
<<
	/FID => (FontID)
	/FontBBox => {
		-161.0
		-317.0
		761.0
		933.0
	}
	/.AGLprocessed~GS > true
	/PaintType => 0
	/FontName => /Courier
	/PathLoad => (/usr/share/ghostscript/9.53.3/Resource/Font/NimbusMonoPS-Regular)
	/FontInfo => <<
		/ItalicAngle => 0.0
		/Notice => ((URW)++,Copyright 2014 by (URW)++ Design & Development)
		/Weight => (Regular)
		/UnderlineThickness => 51
		/FamilyName => (Nimbus Mono PS)
		/version => (1.00)
		/FullName => (Nimbus Mono PS Regular)
		/isFixedPitch => true
		/UnderlinePosition => -91
	>>
>>

inspect.ps was originally part of this dumb repository, but later moved to a dedicated repository. I've left this entry intact as an explanation1 for any readers finding their way here from a link I posted to Groff's mailing list.

Various other shite that doesn't belong anywhere else. Even in a repository as eclectic as this one.

string bool =85 -
string =85 -
Print an Ascii85-encoded representation of a string:
(ABCD) =85 % => <~5sdq,~>

If the first operand is true, the input is treated as an /ASCIIHexEncoded bytestream, and is decoded as such first:

(ABCD)      true =85 % => <~X3C~>
(\000\200)  true =85 % => <~!.Y~>

% Whitespace is always ignored
( AB CD \n) true =85 % => <~X3C~>
< AB CD >   true =85 % => <~X3C~>
string name apply-filter string
Apply an arbitrary decoding filter to a string:
(48656C6C6F2C20776F726C642E)
/ASCIIHexDecode apply-filter % => (Hello, world.)

Currently, this doesn't work with encoding filters. See the red book for a discussion on filter types.

dict key default get? any
Retrieve the value of a dictionary entry, falling back to a default value if no such entry was found:
<< /Foo 1 >> /Foo null get? % => 1
<< /Foo 1 >> /Bar null get? % => null
any numeric? bool
Return a boolean indicating whether argument is an integer or real:
256 numeric? % => true
1.5 numeric? % => true
(1) numeric? % => false
- pagesize width height
Ascertain the physical dimensions of the device's printing area:
% When printing A4-sized pages:
pagesize % => 595.0 842.0
llx lly urx ury bboxsize width height
Resolve the dimensions of a bounding box returned from pathbbox or measuretext:
0 0 moveto
/Times-Roman findfont 10 scalefont setfont
(Hello, world.) measuretext bboxsize
% => 52.1602 8.21875
string measuretext llx lly urx ury
Compute the bounding box for the given string, expressed as the Cartesian coordinates for a rectangle's lower-left and upper-right corners.
0 0 moveto
/Times-Roman findfont 10 scalefont setfont
(Hello, world.) measuretext
% => 0.1875 -1.40625 52.3477 6.8125
array concatprocs proc
Construct a procedure by concatenating the bodies of smaller procedures. For example, to “monkey-patch” a procedure named callback, use:
SourceOutput
/callback {} def
[
	{[/foo /bar /baz] before}
	/callback load
	{after}
] concatprocs bind
{
	[/foo /bar /baz]
	before
	
	after
}

Procedures for plotting simple geometry.

width height rect -
Draw a rectangle centred at the current point:
100 100 moveto
50 80 rect
stroke
radius circle -
Draw a circle centred at the current point:
100 100 moveto
10 circle
fill
[xRadius YRadiussides polygon -
radius sides polygon -
Draw an 𝑁-sided polygon centred at the current point:
% Draw a 100×60-sized hexagon
100 100 moveto
[100 60] 6 polygon
stroke

% Draw a solid-filled triangle
100 100 moveto
50 3 polygon
fill

Despite being a stack-based language, PostScript has surprisingly limited functions for manipulating arrays and lists.2 Those I deem missing will eventually find their way into this file.

Array manipulation
array last any
Retrieve the last element of an array:
[1 2 3]
last % => 1
array rev array
Return a copy of an array in reverse order:
[1 2 3]
rev % => [3 2 1]
array value append array
Append a value to an array:
[ 1 2 3 ]
4 append % => 1 2 3 4
array value prepend array
Prepend a value to an array:
[ 1 2 3 ]
4 prepend % => 4 1 2 3

Dictionary manipulation
dict keys array
Return a dictionary's keys as an array:
<< /yes true /no false >>
keys % => [/yes /no]
dict values array
Return a dictionary's values as an array:
<< /yes true /no false >>
values % => [true false]
dict entries array
Return a dictionary's concatenated key/value pairs as an array:
<< /yes true /no false >>
entries % => [/yes true /no false]
dict flip dict
Return a copy of a dictionary with its keys and values swapped:
<< /width 640 /height 480 >>
flip % => << 640 /width 480 /height >>

Operand stack manipulation
any… index nth any
Return the 𝑁th operand if index is positive, and 𝑁th-last if index is negative:
% Positive indices
(A)(B)(C)(D)  0 nth % => (A) (B) (C) (D) (D)
(A)(B)(C)(D)  1 nth % => (A) (B) (C) (D) (C)

% Negative indices
(A)(B)(C)(D) -1 nth % => (A) (B) (C) (D) (A)
(A)(B)(C)(D) -2 nth % => (A) (B) (C) (D) (B)

This differs from PostScript’s index operator in that indices may be negative (similar to JavaScript’s Array.prototype.slice). Note that negative offsets are indexed from 1 instead of 0 (which otherwise refers to the first/right-most operand).3

Irrespective of which end is being measured from, offsets that fall outside the operand stack will trigger a rangecheck.

any shift -
Remove the last (left-most) operand from the stack:
1 2 3 4
shift % => 2 3 4

Not to be confused with pop, which removes the first operand instead.

any unshift -
Push an operand onto the end (left-side) of the stack:
1 2 3
4 unshift % => 4 1 2 3

Procedures for string conversion and manipulation.

array join string
string string join string
Concatenate a list of strings:
[ (Foo) (Bar) ] join % => (FooBar)
[ (A) (B) (C) ] join % => (ABC)

If only two strings are to be concatenated, they can be passed directly as operands:

(Foo) (Bar) join % => (FooBar)
array delimiter joind string
string string delimiter joind string
Wrapper for join that inserts delimiter between each pair of strings:
[ (A) (B) (C) ]    (, ) joind % => (A, B, C)
[ (X) (Y) (Z) () ] (;)  joind % => (X;Y;Z;)
(Foo) (Bar)        (+)  joind % => (Foo+Bar)
int chr string
Return a single-character string with the given codepoint:
4 chr % => (\004)
9 chr % => (\t)
string ord int
Return the codepoint of string's first character, or 0 if the string is empty:
(ABC) ord % => 65
()    ord % => 0
Totes unserious footnotes

Footnotes

  1. Also because I don't have the heart to delete that preview image's alt text.
  2. Yeah, okay, PostScript arrays are fixed-length records, but that still doesn't excuse the lack of operand helpers. Where's GhostScript's subr.el library, danggit?
  3. Confusing, I know. Signed zeroes would’ve been handy here, had PostScript implemented IEEE 754 like JavaScript did.

About

Assorted PostScript snippets I wrote for fun.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages