<<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:
It can be also specified in a more concise manner. As shown below, the nested element values are delimited by<MasterUnit> <ary> <int>720</int> <int>432</int> </ary> </MasterUnit>
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.
When the integer array short form is combined with the attributed short-form mentioned earlier, it can be specified as simple as shown below:<MasterUnit> <intary>720 432</intary> </MasterUnit>
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:
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.<Declarations> <dict> <entry name="MasterUnit"><intary>720 432</intary></entry> <!-- ... --> </dict> </Declarations>
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.
The conciseness due to the short-hand form is much preferred since such a construct is so typical to appear almost everywhere in a<Declarations> <MasterUnit intary="720 432"/> <!-- ... --> </Declarations>
XPDO
document. 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,
Loading the<!-- 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>
derived.xpdo
file would collect the following single XPDO dictionary object in memory as
a result of merging its extended base.xpdo
file:
The<?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>
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:
Executable Objects and Execution ContextHP_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
In 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).
The load Executable Object and Parameter Dictionaries| | 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 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 -->
Note that the pre-definded parameter<CmdYMoveAbsolute> <!-- ... --> <load name="DestY"/> <!-- ... --> </CmdYMoveAbsolute>
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:
The switch Executable Object| | 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 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.
The printable origin of A4 size in case of portrait printing requested by a user is<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>
(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:
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-".<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>
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:
The returned value will be 3 given that the current value of<idiv> <load name="DestY"/> <int>2</int> </idiv>
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.
The<CmdYMoveAbsolute> <tostring> <str>(1B)*p</str> <load name="DestY"/> <str>Y</str> </tostring> </CmdYMoveAbsolute>
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.
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<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) -->
<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:
which will return a string object which is the same as the object encoded below:<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>
For two more real-life examples:<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) -->
The following shows what the system dictionary stack looks like for the evaluation of <CmdSendBlockData> in the example (similar to the <CmdYMoveRelUp>).<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>
| | 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