This document has a standard, validated CSS2 stylesheet, which your browser does not seem to display properly. In a browser supporting web standards, this table of contents would be fixed at the side of the page for easy reference.
anastigmatix homeWhen Adobe Systems unveiled Level 2 of the PostScript language in 1990, they included a very flexible named resource facility as well as other features that simplify development and deployment of libraries of reusable PostScript resources. Judging by PostScript code and tutorials available on the web, few PostScript programmers are yet taking advantage of what these language features can offer for the packaging of their code.
This page describes how those features of PostScript can be effectively used in one approach—not the only one possible, but the one used for resources on this site—to packaging PostScript code that can be reusable and widely deployed. These guidelines and tools, with a few resources offered here as working examples, may persuade you that it would not be too hard to put the finishing touches on your own code.
So you've got a great library of PostScript procedures that the world
shouldn't be without. If it is like a lot of those available on the web,
it comes in a big file of PostScript, with instructions that say to paste
it in at the front of a program that uses it or, for people using ghostscript
or Distiller, save it in some handy spot on disk and add the file name with
a run
command at the top of a program that uses it.
The user of your library might have some questions. When I run this file, what
does it do? Does it push a new dictionary on the lookup stack, or does it add
definitions to userdict
? Does it define only the names I am
expected to see in using the library, or will there be a lot of other names it
only uses internally? Will some collide with names I use in my own code? Does
its dictionary need to be on my lookup stack for its procedures to work right?
If my program needs your library and another one from somewhere else, will
they trip over each other's definitions, or stomp on each other's assumptions?
Does running the file have other effects besides creating some definitions? Will it leave something on a stack, or change the graphics state, impose a new coordinate system or move the current point? Will it leave some interpreter setting in a different state than before?
Without packaging guidelines, there are a lot of questions to ask before using somebody else's PostScript code. If the code is packaged as a resource, Adobe's guidelines for a well-behaved resource guarantee that the answers to most of those questions will be no.
If your code is packaged as a resource, the first thing we know about it is
it has a name and a category. There are several standard
categories, and the most likely one is ProcSet
, a set of
procedure definitions conceptually belonging in the prolog of a file.
Now, if the name of your resource is com.example.helloworld
,
your users will get access to it by putting this command in the setup
section of a program:
/com.example.helloworld /ProcSet findresource
The findresource
will succeed if the PostScript interpreter has
been supplied with your resource file or knows where to get it, which can
happen at least four different ways, covering a lot of practical scenarios.
The program doesn't need to know which method was used.
When findresource
succeeds, it returns one object. In
the ProcSet
category, the object will be a dictionary. Because
your resource is well-behaved, that object is the only manifestation of your
resource there is (besides the memory it takes up, which could be noticed with
vmstatus
, but there's really nothing you can do about that).
Making your resource available did not have any other effect: no graphics
state updates, nothing left around in other dictionaries or on the stacks.
It only created one dictionary the user can get with
findresource
. And nothing else will happen, until the user,
either by get
or begin
, starts using procedures
from your dictionary.
What are the implications for writing your resource file? It should be a
PostScript program that ultimately produces one dictionary (or one object
of the expected type, for a category other than ProcSet
)
and passes it
to defineresource
. It can do anything reasonable in the
process, allocating objects, creating other intermediate dictionaries, and
so on, even calling findresource
on other resources you rely
on, as long as everything is left as it was found by the end. If your code
currently has some effect at the moment it is loaded, like setting up a page
size or graphics state, those effects should be moved to an
“init” procedure in your dictionary, so they will not happen
until the user's request.
I hinted above that findresource
will succeed if the PostScript
interpreter has been supplied with the resource file or knows where to get
it. Here are four ways to make findresource
succeed.
Method | Pro | Con |
---|---|---|
Direct inclusion | Not much to go wrong. The file with all needed resources included is self-contained and will print anywhere. | Storage space if lots of files have copies of the same resources pasted in. Bandwidth when files sent to printer. |
Simply paste the resource file into any program that uses it, in
the prolog section (before the findresource command, which
belongs in the setup section).
This is much like the way you might be deploying now, except by making the
file a well-behaved resource, you assure the user the only effect of
pasting it there is to make its dictionary available to
findresource and there will be no other effect until some
procedure from that dictionary is executed.
|
||
Download to initial VM | Works in any printer with enough memory. Easy and fast. |
Resource occupies printer memory until next power cycle, even for jobs
that do not use it. Must be sent again after a power cycle. No luck if
printer startjob password has been set and you don't know
what it is.
|
Download one copy of the resource file to the printer, with the linetrue () startjob added at the top, where () should match the printer's startjob password.
If the passwords match (or the printer had no password set, in which case
any password “matches”), the resource is loaded in virtual
memory that persists between print jobs. No need to paste resource in
other files; findresource will just work.
|
||
File available to interpreter |
Works in any printer with a disk or flash file system, and also for
host-based interpreters like ghostscript. Brought into memory
only when requested, transparently; findresource just
works. Lasts across power cycles.
|
Printers with disks are very handy but not common at the low end. |
Query the category dictionary in the printer (or other interpreter,
such as ghostscript) for the resource category and name, to find the
full file name the interpreter would use to look for the resource.
Save the resource file under exactly that name on the printer's disk
(or the computer where you're running ghostscript). No need to paste
resource in other files; findresource just works. More
details in an
appendix.
|
||
File available to document manager | Convenient if files are always sent through document manager and contain correct DSC comments. Flexible. | Requires document manager software that does resource management; I've read all about it in Adobe manuals, but never met anyone with experience using such software. I am not aware of any freely available software that does this (CUPS didn't seem to, last I checked), even though that surprises me. |
Document-manager software looks at files queued for printing and can
preprocess them in various ways. Resource management looks at the
DocumentNeededResources, DocumentSuppliedResources, BeginResource,
EndResource, and IncludeResource document-structure
convention (DSC) comments, and can transparently replace an
IncludeResource comment with the appropriate resource if
it believes the printer doesn't have that resource; can also do the
reverse, if it believes the printer does have the resource
already; can accumulate its own copies of often-used resources by
snarfing them out of files that way; and may decide on its own to
download certain resources to printer's initial VM or disk based on
frequency of use, and then snarf them out of files to save bandwidth.
|
The upshot is there are several ways your users can choose to make your resource available to their programs, depending on their own circumstances, and as a resource developer you'd just like to make sure the full range of choices will work. Two things fall out:
VMusage
comment is consulted by resourcestatus
in the file-available-to-interpreter scenario—but without correct
DSC comments you will handicap users with document managers.
It is possible to look at the life of a resource as a matter of staged programming, where a program in one stage generates a related program to be used in the next.
What is the ultimate form of your resource? It is an object obtained from
findresource
, plus any procedures and other objects in it,
which will be used by the program importing it. Those procedures
and objects should be in a form for efficient execution and reasonable use
of interpreter memory.
One stage earlier, your resource file contains a PostScript program that generates that object. The file should be in a form that is compact for disk storage and communication bandwidth, comfortable for users who have it pasted into files they have to look at, and tolerably efficient for something that will be executed one time and thrown away after building the resource dictionary. The file should also have reliable DSC comments.
Another stage back, and your source file may be a program that generates the resource file, including the DSC comments, which are part unchanging boilerplate and part details that redundantly appear in several places and are madness to keep in sync by hand. The best form for this file is what supports easy comprehension and maintenance: readable names, liberal whitespace and systematic indentation, and lots and lots of comments.
The staging view relaxes a tension often seen in PostScript programming, where
programmers try to strike some precarious compromise between readability,
compactness, and efficiency all at once, and wind up with something that is
terse, filled with made-up names like mt
where they meant
moveto
, and not as readable, compact, or efficient as it could
be. It is easier and more effective to get a compact resource file by an
automatic conversion from the source, and then the source does not need to
make any compromises on readability. As long as the conversion is well
tested and known to work, it does not really complicate development and
testing of your resource: you keep the source file open in a text editor, and
after saving a change, just run the conversion before reloading the
resource file in your PostScript interpreter.
For the most part, what I have called “staging”
here—generation of resource file from source, and resource from
resource file—can be done in simple PostScript.
In some cases, the job can
be simplified further by turning PostScript into an explicitly staged
programming language, as the ProcSet
resource
net.anastigmatix.MetaPre
does.
An example of an Anastigmatix resource file shows a number of required Document Structuring Convention comments, possibly a few ordinary comments briefly explaining what the resource is for, and a compact block of goo. The goo has no secretive or proprietary purpose; there is even a snippet of PostScript at the end of this page you can use to unpack it (the original source with indentation and comments is easier to read). It is just a form that answers well the several considerations above for the form of a resource file. It is compact for storage and transmission, and uses only encodings built into every Level 2 or later PostScript interpreter, which can very efficiently unpack it. It uses only seven-bit ASCII characters, so it can be shipped over all sorts of communication channels, and does not make a mess in a text editor for someone doing direct PostScript programming with the resource pasted into the file. Being a block of goo, it looks nothing like what the user is writing, so one can quite mindlessly scroll down to where the good stuff begins. The name of the resource, repeated occasionally in whitespace banner letters through the goo, helps to avoid disorientation in a large file that may have several different resources pasted in—when they all look like ordinary PostScript code, it can be hard to tell one from another without scrolling up to find where it started.
The goo transformation is straightforward. The source program is read using
PostScript's token
operator, with the natural effect of stripping
all white space and comments, and the tokens are written in “binary
token” form. This is supported by every Level 2 and later
PostScript interpreter, and requires no special arrangement: a PostScript
scanner can read binary and ordinary ASCII tokens interleaved in any mixture.
That means the binary token encoder doesn't need to be complete; some tokens
can be written in the ordinary ASCII form, if the binary form wouldn't be
shorter, or just out of laziness. Benefits of the binary token form include:
true
, false
, and 226 of
the most common system names—free of programmer effort or new
definitions; only familiar, standard names are used in the source.
The binary token form by itself would not be suitable for pasting
into text files, so all of that output is written through the
ASCII85Encode
filter, with an overall expansion of 25%, five
bytes from every four. The corresponding ASCII85Decode
filter
is again present in every Level 2 or later interpreter, and it ignores all
whitespace, so the banner text merged into the resource file is transparent to
it. Effectively, system names are encoded in 2.5 bytes, and integers in 1.25,
2.5, or 5, depending on their sizes.
A final benefit of the goo transformation could be a discipline it imposes on
the resource developer. While code is in development, it is very easy to leave
various configuration settings to be made by tweaking the code itself. But by
the time it is distributed as a resource, it isn't pretty to expect the user
to edit the resource code to make settings; it undermines the very idea of a
shared resource that can be pulled from storage by findresource
for any job that needs it. The distributed version should have arguments, or
configuration commands, or a parameter dictionary, for the anticipated
settings, and a resource file in goo form is a strong reminder that settings
are not made with code tweaks. (For debugging emergencies if the source isn't
handy, it is possible to unpack the file given only a PostScript interpreter,
and edit and use the unpacked version, but that should be rare.)
It is possible to insert the FlateEncode
filter between the
binary tokens and ASCII85Encode
to shrink the file still more
with the compression method of gzip. I do not do this for most resources
because FlateDecode
is only certain to be present in Level 3
interpreters, and most resources are tolerably small without it. I do it
for net.anastigmatix.hyphenpattern
resources, though, to get
Level 3 versions substantially smaller than the Flate
-free
versions usable on Level 2. Smaller for storage and transmission, that is;
of course they are just the same size once loaded into interpreter VM.
To use the resources on this site, you need nothing besides any Level 2 or later PostScript interpreter. For hacking on them, or your own resources, you need a Level 2 or later PostScript interpreter with access to a filesystem, such as ghostscript or Distiller running on a computer, or even a PostScript printer with a disk. The tools for packaging resources in this form are in the pure PostScript net.anastigmatix.Packager resource.
Some resources on this site have not yet been rebuilt with
Packager
and use an older build system that relied
on a collection of tools most easily assembled on Unix and further
described in
an appendix.
Those resources will be converted to Packager
style as time
permits, or when they need editing.
The definitions in your final resource dictionary should be the ones you expect to be used by programs importing your resource. In those procedures, you might refer to other definitions that belong only to your implementation. For instance, in everyday PostScript programming, you might have a procedure defined this way:
/foo { 42 add internalfoo } bind def
With this design, the name internalfoo
is looked up every time
foo
is executed. As a speed issue, that is surprisingly
unimportant: PostScript name
lookups are optimized and cached, and extremely fast. But if foo
is exported in a resource, it won't work unless the interpreter is able to
look up internalfoo
at the time of use. It will work if you
include internalfoo
in your resource dictionary and you
require the calling program to keep your resource dictionary on the lookup
stack—but both would be better avoided, to control the risk of name
conflicts with other code in the calling program.
A simple solution that covers most cases is to use immediately evaluated names:
/foo { 42 add //internalfoo exec } bind def
foo
no longer contains the name internalfoo
at all,
just a direct reference to the procedure. Notice that
the pre-resolved form of “internalfoo
” is not simply
“//internalfoo
” but “//internalfoo
exec
”—and, as one of PostScript's less consistent corners,
that is only true when internalfoo
names an array or
packed array that is executable (a procedure); if the value is not executable,
or has any other type whether executable or not, then you do not add
exec
. Thank the next-to-last paragraph of PLRM section 3.5.3.
Now you are able to leave internalfoo
out of your final resource
dictionary, and let the dictionary contain only the definitions you mean to
export. Even better, foo
will work whether the program using
your resource adds your dictionary to the lookup stack or simply uses
/foo get exec
on your dictionary directly. Some users of your
resource might prefer that, if they use, or other resources they import use,
definitions that might conflict with yours.
Immediately-evaluated names do not allow forward reference; you must order
definitions so internalfoo
has been defined before
foo
refers to it. One way to put it all together is to begin
with a private dictionary:
2 dict begin /internalfoo { 42 sub (Your number was:) = = } bind def /foo { 42 add //internalfoo exec } bind def
and then copy only the exported definitions into a new dictionary and complete defining the resource:
/foo dup load end 1 dict begin def /MyResource currentdict end /ProcSet defineresource pop
If you have imported MetaPre, there is a handy export that handles creating the final dictionary and discarding the original one, just given a list of the names that should be exported—though of course something so simple would not be a reason to import MetaPre if you were not using it for anything else:
/MyResource { /foo } export /ProcSet defineresource pop
If you have procedures with a mutual
dependency, or even just a single recursive procedure so some forward
reference can't be avoided, sometimes messy tricks are used.
You can leave a hole in a procedure and use put
to
fill the hole after what goes there is defined. Another option is
to keep your original dictionary around, with all the internal and exported
definitions, as a private dictionary:
2 dict begin /internalfoo { -|[currentdict]|- begin 84 lt { foo } if end } metabind def /foo { 42 add //internalfoo exec } bind def /MyResource { /foo } export /ProcSet defineresource pop
Here I have used
MetaPre
notation for splicing the current value of currentdict
into
internalfoo
. But to keep the whole private dictionary around
and do lots of internal begin
s and end
s is a fairly
heavy solution, unless you have a lot of mutual references to be dealt with.
If you use this technique, and your design involves callback procedures
supplied by user code, don't forget to end
the private dictionary
before calling them and reinstate it after.
A less messy trick for recursion can be to use
fix, which is also supplied by
MetaPre
.
If your resource relies on some other resource MegaQuux, you can
start with a findresource
to import it. If you know it doesn't
have any names that conflict with yours, go ahead and put it on the lookup
stack with begin
. Just remember an extra end
when
you are done so it is not left on the stack: if the program importing you also
uses MegaQuux, it'll have its own findresource
for it, and not
rely on your implementation dependencies.
/MegaQuux /ProcSet findresource begin 2 dict begin /internalfoo ... def /foo ... def ... defineresource pop end % MegaQuux
How you use the imported resource depends on how well it is packaged. If it, like your own resource, is packaged with resolved references so it will work without its dictionary on the lookup stack, you are in luck; just refer to its procedures the same way you refer to your own:
/internalfoo { currentpoint //triplequux exec } bind def
The calling program will not need to do anything special or be aware of
the other resource you are using internally. (Of course, the DSC comments
in your resource file will include
%%DocumentNeededResources: procset MegaQuux 0.0 0
and the calling program will have DocumentNeededResources
referring to you, so any document manager will know what files
it needs to supply.)
If MegaQuux is not so nicely packaged, and needs its own dictionary on the
lookup stack or its procedures don't work, things get more complicated. You
can stage your own procedures with begin
and end
of
the MegaQuux dictionary surrounding calls into it. You can provide an
init
procedure for your resource that does a begin
of the MegaQuux dictionary once and for all, though by doing that you may be
causing name conflicts for the calling program. That isn't such a problem in
the situation where MegaQuux is a resource that any program using your
resource would obviously also be using. In that case, you can also do nothing
about MegaQuux yourself, and rely on the calling program having imported it.
You can also send a note to the MegaQuux maintainers with a link to this page.
Depending on the method used to supply your resource file to the interpreter, your resource may be loaded into global VM. That means any data structures created at the time your resource is loaded will not allow stores of local data passed in later by the calling program. This can be an issue if you have default settings the program can change, or preallocated dictionaries for local variables in procedures.
A good solution can be to stage things so that structures that might have
to hold program input are created later, say in the setup section. The
resource can have an init
procedure that does that; it might
even have only an init
procedure in the dictionary
returned by findresource
, and init
returns a new
locally-allocated dictionary with the rest of the goods. There can even be
an init
that takes a parameter dictionary as an
argument, specifying various settings, and returns a new dictionary with
procedures specialized to those settings.
Although defineresource makes the dictionary (or other object) representing your resource read-only, nothing does that automatically for other objects contained in it. If your dictionary contains composite objects that are writable, client code will be able to alter them. That is also true of objects appearing within procedures. For example, if your resource contains the procedure
/foo { (myfile) (r) file } bind readonly def
then even though the procedure is read-only, client code
could use (w) //foo 1 get copy
to change the behavior of
your resource. Note that bind alone does not make the procedure
it is applied to read-only, though it does make any procedure references
within it read-only. Packed arrays, of course, are read-only with no
special attention.
Coding techniques that can help include:
/fnam (myfile) readonly def /facc (r) readonly def /foo { //fnam //fac file } bind def
MetaPre
to build readonly references into the procedure:
/foo { -|[(myfile) readonly (r) readonly]|- file } bind def
StreamIO
supplies procedures that help eliminate those strings from your code.
For example:
//r+file exec
rather than (r+) file
//stderr exec
rather than (%stderr) (w) file
The Document Structuring Conventions briefly mention
(Tech Note 5001, p. 35)
that names of ProcSet
resources should contain company and
application names with an eye to reducing naming conflicts, but does not
suggest any single convention. The example I used at the top of this document,
com.example.helloworld
, is an idea borrowed from Java, which
recommends basing package names on inverted domain names. It is a reasonable
idea, and perhaps Adobe might have suggested it too if the web had taken off a
few years earlier, or they had defined the structure conventions a few years
later.
It is possible not only to define resources within existing categories, but to
create new dedicated categories; an example is
net.anastigmatix.hyphenpattern
, where every resource in the
category is a hyphenation pattern set for some language. Obviously, when a
dedicated category is created, it is the category name that needs to be fully
qualified to avoid conflicts; resources within a dedicated category can have
simple names. The exception would be if the category is likely to attract
contributed resources from many sources; then some sensible naming scheme for
resources in the category might be proposed by the category's creator.
While the set of resource categories is unmistakably designed and documented to be extensible (PLRM 3.9.3), the DSC authors seem not to have noticed, and specified a fixed set of keywords for six categories (TN 5001, p. 36)—not even the full set the language already defines. Also, the keywords do not match the spelling of the corresponding category names in the PostScript interpreter: the existing category names are mixed case, but the DSC keywords are lowercase, leaving unspecified what should be used as the DSC keyword for a category not listed. It seems unwise to allow non-listed category keywords to differ in spelling from the category names, because a document manager would have no built-in knowledge to associate the two. If new category names are based on domain names (which are case-insensitive by definition), then it is perhaps reasonable to follow the Java convention and spell them in lowercase consistently; then the names and keywords will be spelled the same and the keywords will be lowercase.
A deeper problem is that distinct syntax rules are given in the DSC for
<filename>, <fontname>, <formname>,
<patternname>, <procname>,
and
<vectorname>
, leaving unspecified what the syntax should
even be for expressing in DSC the name of a resource in an unlisted
category. Four of the given nonterminals reduce to <text>
,
but <fontname>
and <procname>
have more
structure. I have been using the syntax of <procname>
for
newly-created categories because I think version numbers are valuable, but
that may have to be revisited based on reports from people who actually use
document manager software, on what gives it the least indigestion.
The rule for measuring VM usage for the %%VMusage
comment
requires disabling garbage collection (TN 5001, p. 66). The first number is
the difference in available VM after downloading the resource file once; the
second is the new difference after downloading it, redundantly, again. Both
numbers, because GC is disabled, reflect not only the final size of the
resource but the total size of everything that might have been allocated and
thrown away in running the resource file to create it. The numbers might be of
interest to someone writing for a pre-Level 2 interpreter, which had no
garbage collection, but that someone wouldn't be using named resources and
categories, binary tokens or filters, all Level 2 features, or reading this
page, either—even if that someone used the web, which is younger than
Level 2 PostScript. I have made a habit of including another comment—not
an official DSC comment, obviously, just a comment—giving the size after
loading the resource and running a GC, because that's the number that really
counts.
The rule also says to “make sure there are no dependencies on other resources or conditions”—a bit impractical if there are other resources your resource deliberately does depend on. The real rule, of course, should be that you load the prerequisite resources first, run a GC, disable GC, then take the baseline measurement and measure the size of your resource against that.
For the deployment technique of
file available to interpreter, you need to
know where to put the resource files so that the interpreter will find
them. In the general case, say for a printer with a hard disk, you can
find out by asking the interpreter. For example, if you want to save a
ProcSet
resource named com.example.foo
on the
printer's disk, send the printer a query like this:
/ProcSet /Category findresource begin /com.example.foo 128 string ResourceFileName end =
The returned string is the name of the file the interpreter would seek
if presented with /com.example.foo /ProcSet findresource
.
If you save the resource on the printer's disk under exactly that name,
findresource
will just work.
The technique above can also be used with Ghostscript to find the default
location where it will look for resource files. The result will involve a
directory named Resource
located where Ghostscript was installed,
unless it was installed with a different default. However, Ghostscript (in
current versions) can in fact look for resources along a path of directories;
this can be helpful if you do not have write access to the main resource
directory for your Ghostscript installation, as you can create a personal
resource directory and add it to an environment variable or on the
gs
command line. The details are in the
Ghostscript documentation.
Beyond placing the resource files in the right directories,
nothing needs to be done. If the file is present under the proper name,
Ghostscript will find it.
This description applies to Adobe Distiller as available for Unix systems (and so, presumably, on Mac OS X). I do not know if the rules change significantly for Windows, but I will add that information if I find out.
Like Ghostscript, Adobe's Distiller will search a path of directories for
resource files. The path is given in the environment variable
PSRESOURCEPATH
, as a list of directories separated by colons,
to be searched in order. Two consecutive colons indicate where Distiller's
customary default location should fall in the search order.
So far, so good, but it is not enough simply to point
PSRESOURCEPATH
at a directory that contains resource files. When
Distiller searches the resource path, it does not look for resource files
themselves, but for resource database files, which are simply text
files with the extension .upr
that list the resource categories
and files available. So, if you have chosen a directory Resource
and downloaded the Anastigmatix resources into its subdirectories
ProcSet
, Category
,
net.anastigmatix.encoding
and so on—just as they are
organized here—then the Resource
directory should also contain the file net.anastigmatix.upr
.
Distiller will then find the resources as long as the Resource
directory is on the PSRESOURCEPATH
. Comments in the database
file further describe its format, if you are curious.
The pure PostScript
net.anastigmatix.Packager resource
was preceded by a build/packaging system that calls for a
fairly complete Unix-like environment: ghostscript, the Korn shell (as far as
I know, they work under pdksh
also, but there are differences,
and AT&T has now released ksh
under CPL so nobody is really
stuck with pdksh
), and typical Unix command-line tools.
The system requires a Unix-derived OS where /dev/fd/n
really shares file descriptors; that includes the BSDs—and therefore Mac
OS X—but not Linux, which brilliantly emulates
/dev/fd/n
in a way that makes the descriptors unshared.
[There may be a workaround for Linux: if you go to
the AT&T Research
download page (where you would go to get ksh
anyway), and
download not the minimal ast-ksh
package but the full
ast-open
distribution, you get a bunch of other utilities,
some of which are really cool. One of them, 3d
, is intended to
emulate a union file system by shared-library interposition when the OS
does not have union mounts; it just happens to emulate a working
/dev/fd/n
also, and I successfully used it once to
make something work on Linux that needed that.]
The easiest way to hack on a resource that still relies on the old build
system is probably to edit it to use net.anastigmatix.Packager
instead, and then hack it.
%!PS % $Id: resource.html,v 1.18 2009/12/05 02:24:26 chap Exp $ % Copyright Chapman Flack, www.anastigmatix.net. May be freely used and % distributed provided this notice remains intact. Provided in the hope % that it will be useful but AS-IS WITHOUT ANY EXPRESS OR IMPLIED WARRANTY % AND WITHOUT REPRESENTATION AS TO ITS SUITABILITY FOR ANY PURPOSE. % A .gs and not a .ps file - depends on ==only which is a ghostscript extra. % % Prepend to any Level2 file created by anastigmatix-binwrite.gs and feed to % ghostscript to see the contents of the file. For example, could say: % cat anastigmatix-bindump.ps somefile.ps | gsnd -q - % % Some files may have additional layers of encoding, such as for compression. % In that case, what you will see unpacked is probably a short string for % setting up a decode filter, followed by gibberish and/or an error. If so: % 1. Count the number of times the word 'exec' appears in the unpacked string. % 2. Repeat the command above, but add -sExtraExecs=n after 'gsnd', where n is % the number of execs you counted. % 3. Repeat as needed (adding to the value of ExtraExecs) until you see the main % layer unpacked. /buf ( ) def /1st //true def /decode { 1st { /1st //false store buf } { inf buf readstring pop } ifelse } bind def /readit { % toktyp *readit* token buf 0 3 -1 roll put /1st //true store //decode 0 () /SubFileDecode filter token pop } bind def /ptypes << /booleantype { { (//true) } { (//false) } ifelse print } bind >> def /printit { ptypes 1 index type 2 copy known { get exec } { pop pop ==only } ifelse } bind def { << /exec { % file cvlit /inf exch def /ouf (%stdout) (w) file def { inf read not { exit } if dup 128 ge 1 index 160 lt and { ( ) print dup readit printit ( % ) print = } { ouf exch write } ifelse } loop inf closefile ouf flushfile end } /findresource { {findresource cleardictstack} aload pop countdictstack array dictstack dup length 3 sub 3 exch getinterval cleardictstack {begin} {forall} 0 get 5 array astore cvx executeonly exec } >> begin /ExtraExecs dup where { exch get cvi } { pop 0 } ifelse [ << /exec { end exec } >> {begin} 0 get ] cvx repeat } bind % this bind is sort of crucial :) exec