anastigmatix.net

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 home
  • Packager Reference
  • Packager dictionary contents
  • Making DSC-conformant packages
  • atload
  • tokenwriter
  • copytokens
  • cpath
  • pathprep
  • wmdict
  • text
  • fstack
  • stackf
  • ExpectedFileName
  • Manipulating DSC-conformant packages
  • ExistingFileName
  • trimleft
  • DSCtext
  • DSCqualifier?
  • DSCresources
  • DSCscan
  • Precedes
  • Release
  • ResourceDescr
  • ResourceAddPrereq
  • ResourceSupply
  • ReadNeededFromFS
  • QualifierSatisfies
  • VMquery
  • rnresdict
  • Macros for DSC headers
  • PSResource
  • DocumentNeededResources
  • DocumentSuppliedResources
  • IncludeResource
  • BeginResource
  • For use in macros etc.
  • PackagerIdent
  • ##
  • CategoryTo46Key
  • 46KeyToCategory
  • Version-control integration
  • RCSfile
  • RCSrevision
  • Major+RCS
  • Re-exported for convenience
  • extfilter
  • \markup
  • Filters
  • DSCDataEncode
  • WSMarkEncode
  • InUPathDecode
  • Usage notes
  • Output file naming
  • Packager: PostScript resources for packaging PostScript resources

    Packager is a pure PostScript procedure-set resource used for packaging PostScript resources in the form described in my earlier survey of PostScript resource packaging.

    It replaces an earlier implementation that required Ghostscript and a hodge-podge of Unix tools and was never packaged for easy use by others. Packager, as a pure PostScript implementation, can be used anywhere there is a conformant language-level 2 or 3 PostScript interpreter with a filesystem—even, in a pinch, on a printer with a hard drive. It has, of course, been used to package itself.

    The remainder of this page is a reference for the capabilities provided by Packager. For a more conceptual view of what it does and why you would want to, please see the links above.

    A resource that uses Packager for packaging is a staged program. Its source form is a PostScript program that runs, at “packaging time”, imports net.anastigmatix.Packager, and generates another PostScript program—the next stage. That file can be saved on a printer's disk or compactly included in PostScript documents, and when it runs—at “load time”—it generates the final stage, the in-memory form of the resource. Staged programming can involve a good deal of design flexibility, such as the chance to move computations out of later stages into earlier ones if they do not depend on late-stage data, or to delay computations to later stages if the run-time cost would be outweighed by a smaller intermediate file size.

    The typical source form of a resource using Packager will resemble this outline:

    1. Import the Packager procedure set with /net.anastigmatix.Packager /ProcSet findresource begin and then push a writable dictionary above it.
    2. Identify the resource being packaged and its dependencies by defining Category, Key, and the other keys described in the macros section below.
    3. Open an output file, and write its DSC header and any initial portion meant to be readable without unpacking, using the text configuration of Markup and the available macros.
    4. Define a watermark, if desired, for the packed portion of the resource file. Any small bitmap can be used. The cpath configuration of Markup can be used to create one from text.
    5. Define any constants, a/k/a results computable at packaging time. Numeric arrays, in particular, that can be precomputed here and referred to by immediately-evaluated names in the packed portion of the code, can be packed more efficiently than if they appear literally in the packed code.
    6. Create a filter stack for packing the remainder of the file. Built on the open output file, the filters will typically be DSCDataEncode, WSMarkEncode, and ASCII85Encode in that order, on which the binary tokenwriter will write. If the resource is large and will only be used on language-level 3 interpreters, or others that have a FlateDecode, then a FlateEncode filter can be added after the ASCII85Encode.
    7. Declare, with atload, all names that appear in immediately-evaluated (//) form in the code to be packed and should be dereferenced at load time rather than at packaging time.
    8. Pack the remainder of currentfile through the filter stack, using copytokens.

    Because the source form is a complete PostScript program that runs at packaging time to package itself, it need not be organized rigidly as outlined above, but can do whatever is natural and efficient. The packaging survey describes some important issues and techniques.

    Packager Reference

    Packager is a ProcSet resource. To make it available to your own code, include in the setup section of your file:

    /net.anastigmatix.Packager /ProcSet findresource begin
    

    The findresource will succeed if you have made the Packager resource file [download] available in any of the usual ways. However, because this resource is really a development tool for other resources, not all of those deployment methods make equal sense. You will probably be using this resource with a PostScript interpreter on a computer with a filesystem (though with determination you could use it on a printer with a filesystem), and will save this resource file (and the others it depends on) on the filesystem under the expected names.

    Packager relies on other resources, and you will need those files also. If you intend to use it by directly including resources in your file's prolog, the prolog has to contain all of the needed resources, in any order so long as no resource comes before one it depends on, and categories come before resources that belong in them. Other methods should just work as long as all the files are where they need to be. These are the resources you will need:

    ResourceCategoryDescription
    net.anastigmatix.MetaPre ProcSet Staged-programming extensions for PostScript
    net.anastigmatix.filter Category Category to contain filter resources usable with StreamIO.
    DSCDataEncode net.anastigmatix.filter Filter for writing arbitrary data properly enclosed in %%BeginData/%%EndData DSC comments.
    DSCDecode net.anastigmatix.filter Filter for scanning DSC-conformant input.
    WSMarkEncode net.anastigmatix.filter Filter for embedding a small bitmap as a whitespace “watermark” in a block of ASCII85 or hex data.
    InUPathDecode net.anastigmatix.filter Filter for extracting a small bitmap from a figure defined as a user path.
    net.anastigmatix.StreamIO ProcSet Additional I/O functions and extensible filters.
    net.anastigmatix.BinaryIO ProcSet Binary input and output of integers, reals, and arrays.
    net.anastigmatix.Markup ProcSet A minimalist PostScript front-end for text markup.

    The resource file is in a compact form. That is for efficiency, not to keep you from viewing it; there is a script for that on the resource packaging page.

    The Packager dictionary is read-only. Before creating any definitions, you will want either
    userdict begin or your own dict begin so that you have a writable dictionary on top of the dictionary stack.

    Packager dictionary contents

    This section describes the contents of the read-only dictionary that is returned by /net.anastigmatix.Packager /ProcSet findresource.

    Making DSC-conformant resource packages

    atload
    array atload dict

    The array contains names that will be referenced in immediately-evaluated (//) form in the code to be packaged, but which should be dereferenced at the time the packaged code is loaded, not at the time it is packaged. The array and the names in it can be either literal or executable; executable notation can be more concise, as the leading slashes can be left off the names.

    A dict is returned that maps each name in array to a literal name spelled the same but with a leading slash. If this dictionary is on the lookup stack while the code to be packaged is tokenized, the immediately-evaluated names will be replaced with the slashified literal names, which will be prefixed with another slash (like any literal name) when written in the output, and will therefore appear as immediately-evaluated name references at load time.

    tokenwriter
    file tokenwriter proc

    Given a file object open for writing, return a proc (obj proc –) that can be used to write PostScript objects one by one onto file. The intended use is with objects produced by the token operator, so not all PostScript object types are supported (though literal arrays and booleans are, as a convenience, even though token can't produce one except from an immediately-evaluated name or a binary token). A net.anastigmatix.UnsupportedType error will be signalled if proc is given an object of a type not supported.

    Integers and strings are written in the most compact binary token form that fits. Names that have system name encodings in the binary token form use the encoding. A boolean object is written in the binary boolean token form (the simplest way to get a boolean object is to use //true and //false in the code to be packaged; by contrast true and false are normal executable names and will appear full length in the output because they do not have name encodings in the binary token form). Reals are written in IEEE format.

    Literal arrays are written in the most compact homogeneous-number-array format that fits, if any, otherwise in the usual form of a [ followed by recursive application of proc to each object in the array, followed by ]. Executable arrays get the same recursive handling except with { and }.

    If an object passed to proc has different attributes (readonly, for example) than objects of that type normally produced by token, those attribues will not be preserved. The object at load time will have the attributes normally supplied by token, and any necessary modifiers must appear explicitly in the code being packaged to modify those attributes at load time. A non-writable array, or non-readable packed array (neither of which can be produced by token except via an immediately-evaluated name) will incur a net.anastigmatix.UndeclaredImmediate error—because it will most often indicate a mistake where an immediately-evaluated name meant for load time has not been declared with atload.

    copytokens
    proc dict file copytokens

    Given a proc produced by tokenwriter, an input file object, and dict produced by atload, read tokens from file and write them with proc until the token operator reports EOD on file. The token operator is invoked with dict pushed on the lookup stack so that immediately-evaluated names declared with atload are properly deferred to load time.

    cpath
    num cpath dict

    Return a dict for use with \markup to write text into the current path in the graphics state using true charpath. The initial spacing between consecutive text baselines is given by num. The dictionary, and what can be done with it, is otherwise as described for Basic markup but without the implied glyphshow for embedded PostScript that returns a name (there is no “glyphcharpath” operator).

    pathprep
    pathprep userpath dict

    Capture the current path in the graphics state as userpath and clear the current path. Construct a filter dictionary dict with default settings that will work with the InUPathDecode filter for reading the path back as a bitmap. ImageMatrix is set up for unity scaling but a top-down row order (vertical scale -1), translated so the first pixel maps to the upper left of the path's bounding box. Width and Height are computed from the bounding box and rounded up to the nearest integers. Half the amount of any rounding is included in the translation component of ImageMatrix. PathMatrix is set to a fixed [ 6 0 0 6 0 0 ], which often gives good results. (See InUPathDecode for the role of PathMatrix.)

    The default values in the dictionary can be modified for desired effects such as shifting the position of a watermark image. The same effect can sometimes be achieved more simply by using setbbox during construction of the path. The rectangle given to setbbox is always enlarged to include the pre-existing extents of the path, and the resulting bounds are enforced on later additions to the path (excursions generate rangecheck errors). Therefore a setbbox at the start of path construction can be used to ensure the figure stays within a certain size, or a setbbox after path construction can be used to enlarge the final bounding box in one or both dimensions, influencing pathprep's calculated bounds.

    For example, a watermark image of right-justified text could be created using cpath in RagLeft mode, placing the vertical alignment at x=72 with 72 0 moveto. The leftmost extent of the resulting text could be well to the right of zero, reducing the image width computed by pathprep and causing the watermark to appear shifted left. A simple 0 0 0 0 setbbox after building the path will solve the problem by enlarging the reported bounding box to include 0,0.

    wmdict
    userpath dict wmdict dict

    From a userpath and InUPathDecode filter parameter dictionary dict as could have been obtained from pathprep (and possibly modified to change the default dimensions, position, or orientation), construct a parameter dictionary for the WSMarkEncode filter. Width and Height are copied from the input dict, and DataSource is set to an InUPathDecode filter opened on userpath and dict. Other keys are left to default, but can be added to the result dict if desired before creating a WSMarkEncode filter.

    text
    file text dict

    Return a dict for use with \markup to write text onto the given file object open for writing. Formatting features are minimal: lines of text are written as lines of text; strings produced by embedded PostScript are written where the embedded PostScript appears. An executable object produced by embedded PostScript is executed after pushing file on the stack.

    fstack
    file array bool fstack proc file

    Build a stack of encoding filters over an output file as specified by array, returning a file object for the last filter created, and a proc to be used (instead of closefile) to close the filter stack. The array must have a multiple of four elements, forming dict filter pre post groups. Each filter is the name of a filter and dict is a (mandatory, but possibly empty) parameter dictionary for that filter. Before the filter is created, pre will be written on the immediately lower-level file (or, if pre is executable, it will be executed with the lower-level file object on the stack).

    The filters in the stack will not be given the language-level 3 CloseTarget parameter. The filter stack should be closed by executing the returned proc, which will tear down the filter stack by closing the filters in reverse order. After each filter is closed, its post is written to the immediately underlying file object (or, if executable, executed with that object on the stack). The bool specifies whether proc should end by closing the original file object supplied to fstack.

    stackf
    proc file dict filter pre post stackf proc file

    Add to a stack of encoding filters. The input proc and file are a closing-procedure and file object as might be produced by fstack, and dict filter pre post are exactly as used in the specification array for fstack. Returns proc and file representing the new top of the filter stack.

    ExpectedFileName
    ExpectedFileName string

    Queries the PostScript interpreter for the name that the resource being packaged would be expected to have on the filesystem, if any. Requires that a dictionary in scope define Category and Key as described under macros.

    Processing and manipulating DSC-conformant resource packages

    Once DSC-conformant resource packages have been made, they can be manipulated in useful ways that make use of the DSC comments. Using the procedures in this section, resource files can be combined with the other resources they require in a workable total order, such as to combine them into a single distribution file, or to generate a query for the incremental VM usage of a single resource after loading those it depends on. Other tasks that could be automated include installing all or a subset of a collection of resources onto a printer filesystem or into initial VM. A document manager in pure PostScript could be built.

    This section's procedures are documented in a bit lower level than usual, to ensure it is clear how to adapt or imitate them for tasks that do not yet have an implementation supplied in this resource.

    ExistingFileName
    key category ExpectedFileName false | string true

    Queries the PostScript interpreter for the name of an existing file corresponding to the resource named by key and category. Uses ResourceFileName from the category implementation dictionary, then confirms with status that such a file exists.

    If not, and the system parameter GenericResourceDir can be altered without a password, saves the current parameter value and retries with an empty string for this parameter, restoring the original value after. If the interpreter in use is GhostScript and a path of multiple resource directory roots has been configured, this step will cause the path to be searched rather than just the single GenericResourceDir. With such a setup, a staging directory can be put first on the path, into which all newly packaged files will be written when ExpectedFileName is used, but ExistingFileName can be used to retrieve already-packaged resources and will find newly-built ones in the staging area and the rest in the installed location.

    While the code that does this is pure PostScript and will run without error on other interpreters, it will not have this useful effect on interpreters that lack the notion of a resource path. Setting up a convenient build environment may then require a bit more cleverness.

    trimleft
    string trimleft string

    Returns string with any leading spaces, HT, and LF characters removed.

    DSCtext
    string DSCtext post text

    Apply trimleft to string, then attempt to parse the <text> production from TN 5001, namely either a string terminating at the first whitespace, or a string in PostScript parenthesized form. Return the string so parsed (or the empty string if the parse failed) as text, and the remainder of the original string as post. If text is empty on return, post is simply the result of trimleft on the original string.

    DSCqualifier?
    string DSCtext post qual

    Apply trimleft to string, then attempt to parse a version qualifier in the <version> <revision> form specified in TN 5001 for both <procname> and the %%Version: keyword: a real and a uint. Return in qual the entire span of text so matched (or the empty string if not matched), and the remainder of the original string in post.

    DSCresources
    string proc DSCresources

    For string a single line (i.e., treating %%+ continuations as separate lines) in the format of %%DocumentNeededResources: or %%DocumentSuppliedResources:, where a category keyword is followed by one or more resource keys in that category, possibly with version qualifiers, invoke proc once per resource name encountered.

    proc is executed with category key qualifier pushed on the stack above whatever was below string and proc, and is allowed to have other stack effects than simply consuming category key qualifier as long as that's ok with the caller of DSCresources.

    Any key is allowed to be followed by a version qualifier. After parsing a key, if what follows can be parsed as a qualifier it will be, otherwise the qualifier is taken to be the empty string and what follows is parsed as another key.

    DSCscan
    file dict DSCscan file

    Given file open for reading and dict a parameter dictionary for the DSCDecode filter with the additional Callbacks entry described below, create a DSCDecode filter on file and read and discard data until the filter signals EOD, either on occurrence of a DSC keyword for which a callback was specified, or on another EOD condition. If the cause was a keyword with a specified callback, invoke the callback, create a new DSCDecode filter to resume reading file, and repeat, until a filter signals EOD on a condition other than a keyword with a callback. dict must be writable; Keywords, Pushback, and Status entries may be created in it. Any code that resumes reading file after DSCscan completes must consult the Pushback entry in dict upon completion for up to three bytes to be treated as preceding the next read from file. The DSCDecode link above describes the use of the pushback buffer.

    The Callbacks entry in dict must be a dictionary. Keys are the keyword strings of interest, just as for the Keywords entry in an ordinary DSCDecode filter dictionary. Values are three element arrays containing two bools and a proc.

    The first element, if true, indicates that only the first occurrence of the associated keyword in file should be processed. This is the proper treatment for most DSC header comments, of which the first one encountered is the truth. (The reverse convention is needed for (atend) comments, which are not yet supported.) Of course the first occurrence may have any number of %%+ continuation lines; only later new occurrences of the keyword will be suppressed. If this element is false, the keyword will be processed as often as it occurs.

    The second element, if true, indicates that the logical content returned by the DSCDecode filter, which may cover several %%+ continuation lines, should be handled by invoking proc once for the content of each line. If this element is false, proc will be invoked once on the logical content as a single string, a single LF byte indicating each continuation.

    The third element, proc, is executed with one string pushed on the stack above whatever was below file and dict. It will usually consume the string, but may have other stack effects that are ok with the caller of DSCscan.

    Precedes
    array1 array2 Precedes

    Given two arrays where the first entry of each (index OR_FOLQ) is a queue and the second (index OR_NPREQ) is an integer, register an ordering constraint that array1 precedes array2. That is, increment the OR_NPREQ count of array2, and enqueue array2 on array1's OR_FOLQ so its prerequisite count can be decremented when array1 is Released.

    Release
    go-queue array Release

    Given go-queue a queue as used by enq and deq, and array in the form described for Precedes, decrement the OR_NPREQ count in every other array found on the queue at OR_FOLQ in array. Enqueue on go-queue every other array whose OR_NPREQ was decremented to zero.

    ResourceDescr
    key category dict ResourceDescr array bool

    Consult dict and return a resource descriptor for key and category, creating it if not already present. Return array (the descriptor), and a bool, true if the descriptor was newly created.

    The resource descriptor is an array of length RD_LEN whose elements (and their symbolic indices) are:

    OR_FOLQ Queue of resources this resource must precede
    OR_NPREQ Count of resources that must precede this one
    RD_KEY key
    RD_CAT category
    RD_NQUAL Qualifier of needed version of this resource, initially the empty string, and updated by ResourceAddPrereq from %%DocumentNeededResources: comments referring to this resource.
    RD_SQUAL Qualifier of supplied version of this resource, if any. Initially null, and updated by ResourceSupply from a %%DocumentSuppliedResources: comment in this resource.
    ResourceAddPrereq
    dict queue proc rd category key qualifier ResourceAddPrereq dict queue proc rd

    Given dict a repository of resource descriptors, queue of descriptors for resources yet to be scanned for dependencies, proc a process to check whether one version qualifier satisfies another, rd the descriptor for a resource being scanned, and category key qualifier for a resource named in %%DocumentNeededResources: for the resource being scanned, retrieve or create the descriptor for category key, register it as preceding rd and, if it was newly created, enqueue it to be scanned.

    If the descriptor was not newly created, compare qualifier to the RD_NQUAL already recorded. Retain the already recorded qualifier if it satisfies qualifier according to proc. Otherwise, if qualifier satisfies the earlier recorded qualifier, record qualifier in its place. Depending on how the comparison proc is defined, it can be possible for neither qualifier to satisfy the other (for example, if changes in the major revision are considered incompatible). In that case, report net.anastigmatix.QualifierMismatch. That case cannot occur when proc is QualifierSatisfies.

    If an RD_SQUAL is already recorded for the needed resource and would not satisfy qualifier according to proc, report net.anastigmatix.QualifierMismatch.

    ResourceSupply
    dict queue proc rd category key qualifier ResourceSupply dict queue proc rd

    Given dict a repository of resource descriptors, queue of descriptors for resources yet to be scanned for dependencies, proc a process to check whether one version qualifier satisfies another, rd the descriptor for a resource being scanned, and category key qualifier for a resource named in %%DocumentSuppliedResources: for the resource being scanned, save qualifier as RD_SQUAL in rd if RD_CAT and RD_KEY match category and key, otherwise do nothing.

    If RD_SQUAL is already non-null, report net.anastigmatix.ResourceMultiplySupplied. If qualifier would not satisfy a recorded RD_NQUAL according to proc, report net.anastigmatix.QualifierMismatch.

    ReadNeededFromFS
    go-queue dict scan-queue proc ReadNeededFromFS go-queue dict

    Given go-queue a (probably empty) queue, dict repository of resource descriptors, scan-queue a queue on which one or more resource descriptors of interest have already been enqueued, and proc a comparison defining when one version qualifier can satisfy another, repeat as long as scan-queue is nonempty:

    1. Take the next resource descriptor from scan-queue.
    2. Open the associated file as determined by ExistingFileName and scan it for %%DocumentNeededResources: and %%DocumentSuppliedResources.
    3. Use ResourceAddPrereq to declare each needed resource as a prerequisite (which will add it to scan-queue on first encounter). Use ResourceSupply to record the supplied version qualifier of the resource being scanned.
    4. If no needed resources were found, add this resource descriptor to go-queue.

    This procedure is called ReadNeededFromFS because it assumes the resources to be scanned have been installed on the file system where they can simply be opened as needed under the names used by ExistingFileName. It will not work where the files are not randomly accessible, such as when inlined into a distribution file.

    rnresdict

    Readonly prototype of a DSCScan dictionary for use in scanning for resource dependencies. Must be copied to a writable dictionary.

    QualifierSatisfies
    string1 string2 QualifierSatisfies bool

    Return true if the version qualifier represented by string1 is considered to satisfy the one represented by string2. This procedure parses the strings according to the <version> <revision> form specified in TN 5001 for both <procname> and the %%Version: keyword (a real and a uint) and compares them numerically. The result is true if the <version> in string1 is greater than that in string2, or if the <version>s are equal and the <revision> in string1 is greater than or equal to that in string2. (This condition in less conservative than the one in TN 5001, which considers a <version> bump to indicate incompatibility.)

    VMquery
    key category file | key category proc file VMquery

    Generate on file, which must be open for writing, PostScript code to determine the VM usage for the resource identified by key and category. It must be possible to use ExistingFileName to find readable files for the resource and all others (transitively) referred to in %%DocumentNeededResources: comments. The qualifier comparison proc is not necessary except as a sanity check on installed versions; a version that unconditionally returns true could be used. If proc is omitted, it defaults to QualifierSatisfies.

    The output consists of true setglobal followed by the concatenation of all prerequisite resources, a forced garbage collection, and code to disable GC and record the current VM usage as the baseline. Next is the requested resource and code to take the current VM usage and subtract the baseline to obtain the first figure needed for a %%VMusage: comment, then force another GC and measure the usage after GC (not required by TN 5001, but more useful than the numbers that are). Next is a second copy of the resource and code to measure its incremental VM usage over the first copy, giving the second figure for a %%VMusage: comment.

    The generated code will report the results by writing definitions of the form:

    /VMusage (int int) def
    /VMafterGC (int) def

    The generated code can be run on whatever interpreter is chosen for measuring VM usage. For example, it could be sent over a bidirectional link to a printer with an Adobe interpreter to produce results meaningful for that implementation.

    Macros for DSC headers

    Several macros can be used within a text \markup block to generate parts of the DSC header boilerplate. Some or all of the following names must be defined in a dictionary in scope before the macros can be used. (The description of each macro will indicate which of these values it needs.)

    Category
    The category of resource being packaged, as a name, e.g., /ProcSet. New categories can be defined, as well as new resources in existing categories. See this note for why new categories should have all-lowercase names.
    Key
    The key (name) of the resource being packaged, as a name, e.g., /com.example.Foo.
    Qualifier
    Anything that comes after the resource key in the DSC naming syntax for that category of resource. For example, for a ProcSet resource, Qualifier should be a version string of the form (0.1 2). See this discussion for more on category-specific syntax for DSC resource names. This can be an empty string if no qualifier should appear, but should not be undefined.
    VMusage
    A string containing two decimal integers separated by a space. The values are the max and min measured according to TN 5001 p. 66. See this note for some subtleties.
    NeededResources
    An array with a multiple of three elements, namely a category key qualifier triple for every resource directly required by the resource being packaged. As described above, category and key are names and qualifier is a string that may be empty but must be present. NeededResources may be undefined or an empty array if there are no prerequisite resources.
    SuppliedResources
    An array of category key qualifier triples as for NeededResources but naming all resources supplied by the file being packaged. If this name is not defined, the file is assumed to supply only the single resource identified by the Category, Key, and Qualifier dictionary entries. If this definition is present, the array must explicitly contain a triple for every resource supplied, including the one named by Category, Key, and Qualifier.

    The available macros are described next. Each macro is a procedure that places a procedure on the stack. The markup dictionary produced by text has a HandleResult entry that, if an embedded PostScript token produces a procedure, will execute that procedure with the output file on top of the stack.

    PSResource
    Produces the %!PS-Adobe line that must begin the file. Uses Category.
    DocumentNeededResources
    Produces %%DocumentNeededResources: lines for the DSC header. Uses NeededResources. Produces no output if that is undefined or an empty array.
    DocumentSuppliedResources
    Produces %%DocumentSuppliedResources: lines for the DSC header. Uses SuppliedResources or, if that is not defined, Category, Key, and Qualifier.
    IncludeResource
    Produces %%IncludeResource: lines, which belong after the DSC %%BeginProlog. Uses NeededResources. Produces no output if that is undefined or an empty array.
    BeginResource
    Produces the %%BeginResource: line that should precede the resource code. Uses Category, Key, Qualifier, and VMusage. This macro won't be much help in a file that supplies several resources, unless those four definitions are changed between uses.

    Procedures useful in macros, etc.

    PackagerIdent
    PackagerIdent string

    A string constant that is a PostScript comment containing the resource name and version of Packager itself, surrounded by newlines. A suggested use is as the post string for the WSMarkEncode layer of the filter stack.

    ##
    int ## string

    While this is arguably way too specific and single-purpose, a procedure that converts an int to a two-digit, zero-filled decimal string just came in handy.

    CategoryTo46Key
    key CategoryTo46Key string

    Translate a resource category key to a string as the category name should appear in the DSC <resource> syntax specified in TN 5001 section 4.6, that is, unchanged except for the five categories treated specially in that section, whose names get lowercased.

    46KeyToCategory
    key 46KeyToCategory string

    Translate a resource category key as it appears in DSC <resource> syntax (TN 5001 section 4.6) to the form it should have everywhere else, that is, unchanged except for the five categories treated specially in that section, which are converted from their lowercased DSC form to their actual mixed-case form.

    Version-control integration

    Most version-control systems have a way to automatically update strings in a versioned file to reflect its version. Here are some aids for working with those strings.

    RCSfile
    string RCSfile string

    Given a string in the form $RCSfile: foo.ps,v $ as produced by RCS or CVS, returns foo.ps.

    RCSrevision
    string RCSrevision string

    Given a string in the form $Revision: 1.2 $ as produced by RCS or CVS, returns 1.2.

    Major+RCS
    int string Major+RCS string

    Implements a scheme for mapping the major.minor revision numbers of RCS or CVS into the a.b c form specified for procset versioning on TN 5001 p. 35. The idea is you supply an int representing the most major version component a, incrementing it manually in the file for truly colossal changes. The RCS major.minor get mapped to b and c. So, for example, 0 (1.123) Major+RCS produces (0.1 123).

    Re-exported for convenience

    This resource re-exports extfilter and \markup (from net.anastigmatix.StreamIO and net.anastigmatix.Markup, respectively), so your packaging-time source can use them and need only one findresource for Packager itself.

    Filters

    The following filters are available as individual resource files.

    DSCDataEncode filter

    Wraps a stream of arbitrary data in the Document Structure Convention
    %%BeginData: n ASCII|Binary|Hex Bytes|Lines and %%EndData comments with n matching the data written. The target must be a positionable file; n is written by leaving enough space after the %%BeginData: , repositioning to that spot and writing n after writing all the data, then repositioning again to just after the %%EndData line. The parameter dictionary may contain the following parameters.

    KeyTypeSemantics
    Type string (Optional) One of the values ASCII, Binary, or Hex. The default is Binary. The string is copied to the %%BeginData: line, but the filter does not verify that the data stream conforms to the type.
    Count string (Optional) What to count, either Bytes or Lines. If Lines and the data stream does not end with a newline, the filter will add one so that the %%EndData comment begins a new line. If Bytes, the %%EndData will immediately follow the last data byte. The default is Bytes.

    WSMarkEncode filter

    A stream of ASCII85 or ASCIIHex data can be written through this filter to apply a "watermark" by inserting white space (which PostScript ignores when reading ASCII85 or hex data). The watermark is supplied as a small 1-bit-per-pixel image in the same data format that the image operator accepts.

    All original white space (in fact, every character below ! in ASCII) is stripped from the stream, which is reformatted in lines of the specified length containing spaces where indicated by the image data. The watermark image can be repeated through the data block vertically, horizontally, or both.

    Two small distortions to the image may be made. Regardless of the image data, no space will ever be added between the ~ and > that signal the end of ASCII85 data, and a space will always be added before a % that would otherwise begin a line, to eliminate the risk that random ASCII85 data could be picked up by naïve software as a Document Structure Convention comment.

    The parameter dictionary may contain the following parameters.

    KeyTypeSemantics
    DataSource various (Required) A procedure, file, or string supplying 1-bit-per-pixel image data just as for image. The entire image will be read into VM at the time the filter is opened.
    Width, Height integer (Required) The dimensions of the watermark image.
    ImageMatrix array (Optional) As for image, a mapping from “user space” (here, the character columns and rows in the ASCII85 data) to the image source space. Unlike for image, the “user space” is oriented so that rows increase downward, and as long as the image bits are presented in a top-down order (as they often are), the identity matrix is a usable default. So this parameter is optional and defaults to the identity.
    Decode array (Optional) As for image, allows for inverting the sense of the image bits. The default is [0 1], which causes spaces in the output to correspond to 1 bits in the image data.
    LineLength integer (Optional) The length to which output lines will be formatted. The default is 74 characters.
    XStep, YStep integer (Optional) Nonzero values cause the watermark to repeat in the output every XStep columns, every YStep lines, or both. Both parameters default to zero, for a watermark that appears only once.
    CloseTarget boolean (Optional) If true, the underlying file will be closed when the filter is closed. The default is false.

    InUPathDecode filter

    This filter can produce a small, crude bitmap image by rasterizing a user path. In producing a watermark, for example, the userpath could be constructed by upath after applying charpath to a watermark text in a simple (and unprotected) font.

    The filter's “source”—rather than an arbitrary file, procedure, or string—is a user path. Reading from the filter retrieves a 1-bit-per-pixel data stream in the format accepted by image. Each 1-bit sample is determined by inufill testing a single point at the center of the sample region. Blindingly fast it is not.

    The parameter dictionary may contain the following parameters.

    KeyTypeSemantics
    Width, Height integer (Required) The dimensions of the bitmap to be returned.
    ImageMatrix array (Optional) As for image, a mapping from user space to the image source space. Negative scaling and translation in the vertical dimension can be used to read the image in a top-down row order. The identity matrix is the default.
    PathMatrix array (Optional) A mapping from user space (in which the user path is defined) to any arbitrarily chosen “device space”. This matrix does not affect the size or position of the user path figure in the result image (see ImageMatrix for that), but can affect the fineness of detail in the image. Each image sample is determined by applying inufill to a single point at the center of the sample region, and inufill returns whether the “device pixel” containing that point would be painted. This matrix determines the size of the “device pixel” used in that test. Pixels much smaller than samples can leave fine detail missing, but pixels too close in size to samples will give blocky results. Side ratios of 3 to 6 (9 to 36 by area) seem to give good results.

    Usage notes

    Packager provides tools that can be used in many ways, but here are some ways of using it that work for me.

    Output file naming

    There is nothing in Packager that is specific to ghostscript or any other PostScript interpreter. However, I do use it in a way that takes advantage of a ghostscript feature. Ghostscript extends the notion of a Resource directory on disk to allow a path of such directories, as a colon-separated list in the GS_LIB environment variable.

    A findresource will load a resource file from the first place it finds one, but the default ResourceFileName procedure in category dictionaries will come back with the first location on the path, and therefore ExpectedFileName in this procset will too.

    That means I can have $HOME/postscript/Resource first in the path, and if my source files open ExpectedFileName as the output file, they will always write in that directory. Testing is easy because the versions in that directory are loaded first. Known good versions can be moved to the system location. If a bad version is created, it can just be removed to revert to the known good version. That's important as Packager can't build itself without working built versions of itself and several other resources.

    Caveat: the ghostscript people don't seem to be sure how they want this path feature to work, and in some versions it doesn't seem to behave as I have described.

    The source files for filters that Packager itself depends on turn out to need this code placed before the opening of the output file (which would not hurt in any case):

    
    % if I am already built, and used in the build process,
    % I'd better be in memory before opening the output file.
    Key Category resourcestatus {
      pop 2 eq { Key Category findresource pop } if
    } if
    
    

    Valid XHTML 1.0! Valid CSS! $Id: Packager.html,v 1.10 2009/11/24 02:58:50 chap Exp $