<<My apology for resending this email due to the lack of an attached document in a hurry for lunch>>
Hi,
Per Norbert Schade's request, I am trying to exchange what many of us
working for Sun Microsystem think in many areas of printer description
in terms of what may be a must and the possible solutions from the perspectives
of looking for a "long term" solution for universal (raster-based) printing services
(framework and drivers) under Unix and other platforms.
As a new late comer, I am not sure it is a good way or a good timing now but I am doing it anyway ...
The first thing I'd like to discuss is the requirement in printer description for run-time evaluation
to generate raster data and printer setup commands. I have an impression that it had been brought up
to UPDF's attention in the past (by Don Wright of Lexmark?) for the "completeness" of any page description file
or otherwise those commands would need to be "hard-coded" somewhere else in C code for which
the programming interface needs to be defined (and therefore must be covered as part of UPDF?).
Attached (xpdo.nov01.html) for your reference please find the extensible printer description object model
based on XML technologies. It is a very simple virtual machine for XML-based printer description language
in an attempt to address those command generation issues in addition to custom declaration, modulization and
binary encoding etc.
My appology that GPD teminologies are used in the document for a demonstration because GPD is
the only printer description format I know of that generates raster data for low-cost printers.
Table of Content:
-----------------
Introduction
Data Types: string, name, int, float, boolean, array, dictionary
File Modulization for Printer Model Family Hierarchies
Executable Objects and Execution Context
The load Executable Object and Paramter Dictionaries
The switch Executable Object
The Math Executable Objects: idiv, add, sub, expr
The Formatter Executable Objects: tostring, numformat, maxrepeat
Samples in the document:
------------------------
- for raster data commands generation:
<CmdSendBlockData> <!-- pre-definded parameter: NumOfDataBytes -->
<maxrepeat> <!-- an executable object that describes how a CmdSendBlockData is
generated with a value-range encoding limit -->
<int>5100</int> <!-- limit -->
<load name="NumOfDataBytes"> <!-- total -->
<tostring> <!-- 3rd executable object to be repeatedly evaluated -->
<str>{1B}*{03}</str>
<expr str="numformat(MaxRepeatInstance,'l')"/>
</tostring>
</maxrepeat>
</CmdSendBlockData>
<CmdYMoveRelUp> <!-- pre-definded parameter: DestYRel -->
<maxrepeat> <!-- an executable object that describes how a CmdSendBlockData is
generated with a value-range encoding limit -->
<int>12600</str> <!-- limit -->
<load name="DestYRel"/> <!-- total -->
<tostring> <!-- 3th executable object to be repeatedly evaluated -->
<str>{1B}(*p-</str>
<load name="MaxRepeatInstance"/>
</tostring>
</maxrepeat>
</CmdYMoveRelUp>
- for multiple-way runtime dependency (e.g., a printable area depends on the paper size and perhaps the orientation
chosen at runtime):
<PaperSize>
<Name str="Paper Size"/>
<Options>
<A4>
<Name str="A4, 210 x 297 mm"/>
<PrintableOrigin>
<switch name="Orientation"> <!-- returning an integer object as the
associated with the Orientation key
in the system setup dictionary
-->
<case name="PORTRAIT" intary="300 300"/>
<case name="LANDSCAPE_CC90" intary="200 180"/>
<default intary="180 200"/>
</switch>
</PrintableOrigin>
<!-- ... -->
</A4>
<!-- ... -->
</Options>
<!-- ... -->
</PaperSize>
- for Custom feature/option (e.g. paper size):
<PaperSize>
<Options>
<A4>
<Name str="A4, 210 x 297 mm"/> <!-- Display name is optional for predefined paper size -->
<PrintableOrigin intary="..."/> <!-- required for any paper size -->
<!-- ... -->
</A4>
<entry name="MyCustomPaperSize">
<Name str="My Custom Paper Size: x by y"/> <!-- Display name is required only for custom paper size -->
<PrintableOrigin intary="..."/> <!-- required for any paper size -->
<PageDimensions intary="..."/> <!-- required only for unknown custom paper size -->
<!-- ... -->
</entry>
<!-- ... -->
</Options>
<!-- ... -->
</PaperSize>
Best regards,
Jim Lo (jim.lo@eng.sun.com)
Internet Appliance Group
Sun Microsystems, Inc.
Menlo Park, California
XPDO (Extensible Printer Description Object Model) Virtual Machine
IntroductionIntroduction
XPDO Data Types: string, name, int, float, boolean, array, dictionary
XPDO File Modulization for Printer Model Family Hierarchies
Executable Objects and Execution Context
The load Executable Object and Paramter Dictionaries
The switch Executable Object
The Math Executable Objects: idiv, add, sub, expr
The Formatter Executable Objects: tostring, numformat, maxrepeat
This document describes a very simple virtual machine based on an extensible object model for printer description as a XML application. Many requirements in printer description such as run-time evaluation for raster data and printer setup command generation, custom declaration, modulization and binary encoding are addressed.
XPDO provides a object-based system which is strongly typed.
There are several primitive types. They are for string, name, integer, float and boolean objects which are encoded as below:
A<int>77</int> <float>-1.2</float> <bool>TRUE</bool> <str>a string</str> <name>aName</name>
XPDO numbers can be signed integer objects such as 12, -98, 0, +650 and floating point objects
such as -0.01 2.3 -4.56 -7. 0.0
A XPDO boolean object is for use in logical expression or returned as status information. The
names true and false are associated with the two values of this type.
The <TRUE/> and <FALSE/> are introduced for a short hand form of a boolean object
with true and false value, respectively.
A XPDO name object is of XML NMTOKEN type.
A XPDO string object is of XML CDATA type with one exception in favor of
hexadecimal encoding which is useful for arbitrary binary data which is often mixed in a printer command string.
The ASCII '{' character and the '}' are used to enter and exit the hexadecimal encoding mode respectively.
A string is initially in the normal ASCII encoding mode. For example, the selection command for
Letter size on Canon BJC-600 is the hexadecimal byte stream
1B 28:'(' 67:'g' 03 00 6E:'n' 01 72:'r' where all printable ASCII characters are shown
after a colon for the ease of reference. It can be specificed as below:
Note that the only way to encode '{' and '}' themselves is in the hexadecimal mode.{1B}(g{0300}n{01}r
To be more precise, a hexadecimal encoded data enclosed within '<' and '>' consists of a sequence of hex characters (the digits 0 through 9 and the letters A through F or a through f) . Each pair of hex digits defines a byte. White-space characters are ignored. If there is an odd number of digits, the final digit that is missing is assumed to be zero. For example, { 1B 28a } will be treated as {1b28a0}.
There is also a short-hand form which takes advantage of XML attribute syntax
when a primitive object is associated with other object in the context
of XPDO dictionary key/value pairing or XPDO run-time operator operand.
For example,
As shown in the example, the<XMoveUnit int="60"/>
XMoveUnit is an empty-element tag with a single attribute
to explicitly declare its type and value.
There are several XPDO compound types. They include array, dictionary and all executable objects
for flow control, math and formatting operations etc.
An array object is enclosed in the <ary> start-tag and </ary> end-tag.
The nested elements of an array can be any XPDO primitive and compound types.
When all nested elements are of the same primitive type except the string type, a short-hand form is provided.
Consider the the following example:
<MasterUnit>
<ary>
<int>720</int>
<int>432</int>
</ary>
</MasterUnit>
It can be also specified in a more concise manner. As shown below,
the nested element values are delimited by XML white spaces and
they must be all integer type which can be further reinforced via
the new <intary> start-tag and </intary> end-tag.
<MasterUnit>
<intary>720 432</intary>
</MasterUnit>
When the integer array short form is combined with the attributed short-form mentioned earlier,
it can be specified as simple as shown below:
Similarly to the name array, float array and boolean array except the new<MasterUnit intary="720 432"/>
<nameary>, <floatary>
and <boolary> tags are used respectively. A dictionary object is enclosed in the <dict> start-tag and </dict> end-tag. The nested elements of a dictionary are nothing but key/value pairs (also referred to as entries). Those entries are specified via the <entry> start-tag and </entry> end-tag as below:
<Declarations>
<dict>
<entry name="MasterUnit"><intary>720 432</intary></entry>
<!-- ... -->
</dict>
</Declarations>
As shown, there is an attribute to specify an entry key which can be of any primitive type and
array type. An entry value can be of any primitive and compound type including dictionary itself.
Note that when a dictionary entry key is a pre-definded XPDO name such as
MasterUnit in the previous example, the general <entry> tag which
is provided for printer vendor defined custom printer properties can be replaced
by the predefinded tag for that pre-definded name. Note also that when an entry value of an
pre-definded entry key name is allowed to be an dictionary only, the <dict> tag
can be removed. It is demonstrated below as a more concise version of the previous example.
<Declarations>
<MasterUnit intary="720 432"/>
<!-- ... -->
</Declarations>
The conciseness due to the short-hand form is much preferred since
such a construct is so typical to appear almost everywhere in a XPDOdocument. Finally, An example that deals with a variety of object types is shown below:
which can be abbreviated as below:<CmdSelect> <!-- pre-definded CmdSelect is an dictionary with three entries: two pre-definded Order, Cmd entries and a custom MyNotPredefined entry --> <dict> <Order> <!-- pre-definded Order is an array with two elements: a pre-definded name JOB_SETUP and an integer 10 --> <ary> <name>JOB_SETU;</name> <int>10</int> </ary> </Order> <Cmd str="printer control commands"/> <entry name="MyNotPredefined"><int>9</int></entry> </dict> </CmdSelect>
In addition to the three general short-hand forms which are attributed short-hand form, array short-hand form and dictionary short-hand form, there are also short-hand forms that are specific to particular pre-definded tags such as<CmdSelect> <Order name="JOB_SETUP" int="10"> <Cmd str="printer control commands"/> <entry name="MyNotPredefined"> <int>9</int> </entry> </CmdSelect>
<Order> as shown
in the previous example. A name object as the typical data type of a dicitonary key is much faster to match (as opposed to string type) in that it is the internal representation (usually the address where the name is stored) rather than the character string for the name that gets involved for the key matching process.
Note that the generic <entry> tag is for a custom entry whose key name is defined by
printer vendors. For another example,
A<PaperSize> <Options> <A4> <Name str="A4, 210 x 297 mm"/> <!-- optional for predefined paper size --> <PrintableOrigin intary="..."/> <!-- required for any paper size --> <!-- ... --> </A4> <entry name="MyCustomPaperSize"> <Name str="My Custom Paper Size: x by y"/> <!-- required only for custom paper size --> <PrintableOrigin intary="..."/> <!-- required for any paper size --> <PageDimensions intary="..."/> <!-- required only for custom paper size --> <!-- ... --> </entry> <!-- ... --> </Options> <!-- ... --> </PaperSize>
XPDO document is encoded as a single dictionary object which
contains some key/value pairs whose value may be of dictionary type. A XPDO object hierarchy
formed in that manner can then serve as a base for the XPDO modulization
(file merging for printer description inheritance) mechanism.
XPDO File Modulization for Printer Model Family Hierarchies
Printer models in a same product family share a lot in common. A bunch of related XPDO files
can be grouped together in a hierarchy via the xpdo extend processing instruction
in order to reflect the model structure of a printer product family in a nature way.
Consider the following two XPDO files to be merged,
<!-- base.xpdo file -->
<?xml version="1.0">
<XPDO>
<Dictionary>
<Base str="BASE"/>
<Untouched str="SAME"/>
</Dictionary>
<Base>
<!-- Base... -->
</Base>
<Untouched str="SAME"/>
</XPDO>
<!-- derived.xpdo file -->
<?xml version="1.0">
<?xpdo extend="base.xpdo"/>
<XPDO>
<Dictionary>
<Derived str="DERIVED"/>
<Base str="DERIVED"/>
</Dictionary>
<Derived str="DERIVED"/>
<Base str="DERIVED"/>
</XPDO>
Loading the derived.xpdo file would collect the following single XPDO dictionary object in memory as
a result of merging its extended base.xpdo file:
<?xml version="1.0">
<XPDO>
<Dictionary>
<Base str="DERIVED"/> <!-- overridden from the old value: (Base) -->
<Untouched str="SAME"/>
<Derived str="DERIVED"/> <!-- new entry -->
</Dictionary>
<Base str="DERIVED"/> <!-- overridden from the old value: Base dictionary -->
<Untouched str="SAME"/>
<Derived str="DERIVED"/> <!-- new entry -->
</XPDO>
The XPDO files merging based on the xpdo extend processing instruction can
therefore work in such a simple way that is described in the following rules based on dictionary
deep (recursively) merge:
<EntryOrder> entry can be added to explicitly specify the iteration order.
The value is an array consist of key names for all dictionary entries.
A XPDO hierarchy can be as deep as needed.
The following hierarchy demonstrates a possible real-life printer product family would look like in their entirety:
HP_Raster
HP_DeskJet_600_Monochrome
HP_DeskJet_540_Monochrome
HP_PaintJet
HP_DeskJet_Plus
HP_DeskJet_400_Monochrome
HP_DeskJet_6xx
HP_DeskJet_67x
HP_DeskJet_660Cse
HP_DeskJet_660C
HP_DeskJet_670C
HP_DeskJet_672C
HP_DeskJet_69x
HP_DeskJet_680C
HP_DeskJet_682C
HP_DeskJet_690C
HP_DeskJet_692C
HP_DeskJet_693C
HP_DeskJet_694C
HP_DeskJet_695C
HP_DeskJet_697C
HP_DeskJet_600
HP_QuietJet
HP_DeskJet_400
HP_DeskJet_85x
HP_DeskJet_850C
HP_DeskJet_855Cse
HP_DeskJet_855Cxi
HP_DeskJet_420
HP_DeskJet_87x
HP_DeskJet_870C
HP_DeskJet_870Cse
HP_DeskJet_870Cxi
HP_DeskJet_89x
HP_DeskJet_895Cse
HP_DeskJet_895Cxi
HP_DeskJet_560C
HP_DeskJet_82x
HP_DeskJet_820Cse
HP_DeskJet_820Cxi
HP_DeskJet_540
HP_OfficeJet
HP_DeskJet_510
HP_DeskJet_7xx
HP_DeskJet_710C
HP_DeskJet_712C
HP_DeskJet_720C
HP_DeskJet_722C
HP_OfficeJet_LX
HP_DeskJet_500
HP_DeskJet_520
HP_ThinkJet
HP_DeskJet_340_Monochrome
HP_DeskJet_320
HP_DeskJet_340
HP_OfficeJet_3xx
HP_OfficeJet_300
HP_OfficeJet_330
HP_OfficeJet_350
HP_QuietJet_Plus
HP_DeskJet_550C
HP_DeskJet_500C
HP_DeskJet_Portable
HP_DeskJet_310
HP_DeskJet
Executable Objects and Execution ContextIn the process of printer description, there are cases where a printing property is dependent on a user selection in runtime or actual printing data based on a raster scheme needs to be generated from actual parameters passed by printing system which does the actual graphics rendering to produce raster data.
An executable object is a XPDO object that is evaluated at run time.
An executable object can consist of one or more nested elements of any object type
as is the case with an array. The result of the evaluation is a single
XPDO object of any type.
An input value is associated with a key name which is referred in the body
Of an executable object.
There can be as many dictionary objects as needed for such an input parameter loading. A system level stack is introduced as a collection of those dictionaries and the key mapping order is defined to be from the top all way down to the bottom of the stack.
The first dictionary object automatically pushed is the so-called setup dictionary
which contains typically selected option (entry value) for all features (entry key).
| | Top
| |
| . |
| . |
| . |
| |
| +------------------------+ |
| | parameter dictionary | |
| | for a nested executable| | (Only during the evaluation of the nested executable object for the parameters)
| | objects, if any | |
| +------------------------+ |
| |
| +----------------------+ |
| | parameter dictionary | |
| | containing one or | | (Only during the evaluation of an executable object for the parameters)
| | more entries | |
| +----------------------+ |
| |
| +----------------------+ |
| | setup dictionary | | (Always available)
| +----------------------+ |
+----------------------------+ Bottom
System dictionary stack
(Execution Context)
The load Executable Object and Parameter DictionariesThe <load> executable object is introduced to explicitly load an object from the system dictionary stack.
For example, the following load object will load the currently selected paper size from the system setup dictionary:
Take another example shown below regarding a parametric executable object evaluation:<load name="PaperSize"> <!-- returning a A4 name object, for example -->
<CmdYMoveAbsolute>
<!-- ... -->
<load name="DestY"/>
<!-- ... -->
</CmdYMoveAbsolute>
Note that the pre-definded parameter DestY is specific to the
pre-definded CmdYMoveAbsolute entry. Mechanically, a parameter
dictionary containing the entry with the name DestY as its key and
the current DestY as its integer value will be pushed to the system dictionary
stack before the evaluation takes place. It is shown as below:
| | Top
| +----------------------+ |
| | parameter dictionary | |
| | containing the | | (Only during the evaluation of CmdYMoveAbsolute)
| | DestY entry | |
| +----------------------+ |
| |
| +----------------------+ |
| | setup dictionary | | (Always available)
| +----------------------+ |
+--------------------------+ Bottom
System Dictionary Stack
The switch Executable Object
The switch executable object provide a flow control at runtime. The
following example demonstrates how the origin of a printable area for a particular
paper size will depend on the paper orientation a user will select.
<PaperSize>
<Name str="Paper Size"/>
<Options>
<A4>
<Name str="A4, 210 x 297 mm"/>
<PrintableOrigin>
<switch name="Orientation"> <!-- returning an integer object as the
associated with the Orientation key
in the system setup dictionary
-->
<case name="PORTRAIT" intary="300 300"/>
<case name="LANDSCAPE_CC90" intary="200 180"/>
<default intary="180 200"/>
</switch>
</PrintableOrigin>
<!-- ... -->
</A4>
<!-- ... -->
</Options>
<!-- ... -->
</PaperSize>
The printable origin of A4 size in case of portrait printing requested by
a user is (300,300), (200,180) in case of LANDSCAPE (counter clockwise by 90 degree)
or (180, 200) in other cases. The <switch> construct shown in the previous example actually is an abbreviation of a general form of how a switch object Can be instantiated in memory or encoded in a binary stream. The most general form of the previous example is shown as below:
<switch>
<load name="Resolution">
<dict>
<entry name="PORTRAIT" intary="300 300"/>
<entry name="LANDSCAPE_CC90" intary="200 180"/>
<entry name="-default-" intary="180 200"/>
</dict>
</switch>
The <case> tag is only a more readable alias of <entry> tag and the
<default> tag is an abbreviation of a dictionary entry whose key is a
reserved name called "-default-". Just consider a switch object as yet another compound object. The first object It contains can be of any type that is qualified as a dictionary key as a runtime condition. As a control flow language construct, the object is typically evaluated at runtime as a retuned value of a <load> tag. The second object is a dictionary object whose entries serve a case pool. An entry key is used to match the first condition object and when matched its associated value is used for a returned value of a switch construct. The default entry provided a default case if present or otherwise a null object is returned to indicate an exceptional situation may arise. However, an warning message can also be issued by a validating tool at loading time for pre-definded names in the Switch construct.
The Math Executable Objects: idiv, add, sub, expr
There are cases where a parameter needs to be subject to mathematics operations such as addition, subtraction and division.
The idiv executable object must contain two integer objects. The first integer
will be divided by the second integer. Take the following as an example:
<idiv>
<load name="DestY"/>
<int>2</int>
</idiv>
The returned value will be 3 given that the current value of DestY is 7.
For many frequently used expression, the expr executable object
is introduced to provide an optimized short form by the expr executable object.
The previous example can then also be specified as below:
There are many other pre-definded expression such as "idiv(DestY,2)", "numformat(NumOfDataBytes+1,'l')".<expr str="idiv(DestX,2)"/>
Similarly to other math executable objects such as add and sub except that
the two nested elements they contains can be either integer or floating-point number type.
The Formatter Executable Objects
The value of the CmdYMoveAbsolute entry of the following example
will be (1B)*p7Y given that the current value of DestY is 7.
This is because a string object will be resulted from a concatenation of all nested elements
enclosed in the tostring executable object.
<CmdYMoveAbsolute>
<tostring>
<str>(1B)*p</str>
<load name="DestY"/>
<str>Y</str>
</tostring>
</CmdYMoveAbsolute>
The tostring executable object is evaluated in such a way that:
<numformat> executable object is introduced in case where a special integer formatting is needed.
For example, the '+' sign is not printed for signed integer if it is greater than 0 by the <tostring>.
The numformat executable object consists of one integer object followed by a string object indicating
its pre-definded formatting code.
<numformat int="12" str='d'/> <!-- returning "12" which consists of two ASCII characters '1' and '2'
as what the tostring formatting does
-->
<numformat><int>12</int><str>D</str></numformat> <!-- returning "+12" which is the same as above except that
the + sign is included if the integer is greater than 0.
-->
<expr str="numformat(DestX,'l')"> <!-- returning "(0201)" which is a two-byte string with low-order byte (LSB) first
given DestX = 0x0102 (16-bit integer)
-->
<expr str="numformat(DestX,'m')"> <!-- returning "(0102)" which is a two-byte string with high-order byte (MSB) first
given DestX = 0x0102 (16-bit integer)
-->
In cases where a printer command is forced to be emitted more than once due to its
allowable maximum value range in the command encoding,
the <maxrepeat> executable object can be very useful. The first and second object contained are both integer. The third object is an executable object which is evaluated repeatedly using a maximum allowed value as the first integer object) until the accumulated value is equal to a total value as the second integer object.
For the input of each executable object evaluation, a share value will be
associated automatically in the <MaxRepeatInstance
as is the case with any other pre-definded parameters handled in the system dictionary stack.
For each returned object as a result of the third executable object evaluation, it will be
automatically converted to a textual representation and concatenated with the accumulation of
all previous repeated evaluation outputs. In other words, the final returned object of the
<maxrepeat> executable object as a whole is the same as the returned object
of an <tostring> evaluation with all those repeatedly returned objects as its
nested elements.
Considering the following simple example:
<maxrepeat>
<int>2</str> <!-- 1st object: limit -->
<int>5</str> <!-- 2nd object: total -->
<load name="MaxRepeatInstance"/> <!-- 3rd executable object to be repeatedly evaluated
to collect all share values stored in the
pre-definded MaxRepeatInstance -->
</maxrepeat>
which will return a string object which is the same as the object encoded below:
<str>221</str> <!-- i.e., there are three shares, non of which exceeds the limit (2).
The sum of all shares (2+2+1) is the total (5) -->
For two more real-life examples:
<CmdSendBlockData> <!-- pre-definded parameter: NumOfDataBytes -->
<maxrepeat> <!-- an executable object that describes how a CmdSendBlockData is
generated with a value-range encoding limit -->
<int>5100</int> <!-- limit -->
<load name="NumOfDataBytes"> <!-- total -->
<tostring> <!-- 3rd executable object to be repeatedly evaluated -->
<str>{1B}*{03}</str>
<expr str="numformat(MaxRepeatInstance,'l')"/>
</tostring>
</maxrepeat>
</CmdSendBlockData>
<CmdYMoveRelUp> <!-- pre-definded parameter: DestYRel -->
<maxrepeat> <!-- an executable object that describes how a CmdSendBlockData is
generated with a value-range encoding limit -->
<int>12600</str> <!-- limit -->
<load name="DestYRel"/> <!-- total -->
<tostring> <!-- 3rd executable object to be repeatedly evaluated -->
<str>{1B}(*p-</str>
<load name="MaxRepeatInstance"/>
</tostring>
</maxrepeat>
</CmdYMoveRelUp>
The following shows what the system dictionary stack looks like for the evaluation of <CmdSendBlockData>
in the example (similar to the <CmdYMoveRelUp>).
| | Top
| +-------------------------+ |
| | parameter dictionary | |
| | containing the | | (Pushed and poped for each evaluation of 3rd executable object of maxrepeat)
| | MaxRepeatInstance entry | |
| +-------------------------+ |
| |
| +-------------------------+ |
| | parameter dictionary | |
| | containing the | | (Only during the evaluation of CmdSendBLockData)
| | NumOfDataBytes entry | |
| +-------------------------+ |
| |
| +-------------------------+ |
| | setup dictionary | | (Always available)
| +-------------------------+ |
+-----------------------------+ Bottom
System Dictionary Stack
This archive was generated by hypermail 2b29 : Fri Dec 01 2000 - 15:22:29 EST