summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--pxl/makefile59
-rw-r--r--pxl/news-pxl843
-rw-r--r--pxl/pxasm.bat1
-rw-r--r--pxl/pxasm.ps192
-rw-r--r--pxl/pxattr.h10
-rw-r--r--pxl/pxbfont.c2048
-rw-r--r--pxl/pxbfont.h17
-rw-r--r--pxl/pxbfont.ps120
-rw-r--r--pxl/pxcet.txt170
-rw-r--r--pxl/pxdict.h66
-rw-r--r--pxl/pxdiff.txt315
-rw-r--r--pxl/pxenum.h10
-rw-r--r--pxl/pxerrors.c284
-rw-r--r--pxl/pxerrors.h160
-rw-r--r--pxl/pxffont.c603
-rw-r--r--pxl/pxfont.c758
-rw-r--r--pxl/pxfont.h90
-rw-r--r--pxl/pxfts.txt153
-rw-r--r--pxl/pxgstate.c914
-rw-r--r--pxl/pxgstate.h190
-rw-r--r--pxl/pximage.c685
-rw-r--r--pxl/pxink.c714
-rw-r--r--pxl/pxl.mak196
-rw-r--r--pxl/pxl_top.mak38
-rw-r--r--pxl/pxl_ugcc.mak59
-rw-r--r--pxl/pxlib.txt222
-rw-r--r--pxl/pxmain.c306
-rw-r--r--pxl/pxoper.h97
-rw-r--r--pxl/pxpaint.c799
-rw-r--r--pxl/pxparse.c873
-rw-r--r--pxl/pxparse.h90
-rw-r--r--pxl/pxprint.bat17
-rwxr-xr-xpxl/pxprint.tcl16
-rw-r--r--pxl/pxptable.c673
-rw-r--r--pxl/pxptable.h54
-rw-r--r--pxl/pxsessio.c485
-rw-r--r--pxl/pxspec.txt563
-rw-r--r--pxl/pxstate.c107
-rw-r--r--pxl/pxstate.h164
-rw-r--r--pxl/pxstream.c150
-rw-r--r--pxl/pxsymbol.ps111
-rw-r--r--pxl/pxtag.h10
-rw-r--r--pxl/pxvalue.c66
-rw-r--r--pxl/pxvalue.h99
44 files changed, 13597 insertions, 0 deletions
diff --git a/pxl/makefile b/pxl/makefile
new file mode 100644
index 000000000..1540ae275
--- /dev/null
+++ b/pxl/makefile
@@ -0,0 +1,59 @@
+# Copyright (C) 1997, 1998 Aladdin Enterprises. All rights reserved.
+# Unauthorized use, copying, and/or distribution prohibited.
+
+# pxl_ugcc.mak
+# Top-level makefile for PCL XL on Unix/gcc platforms.
+
+# Define the name of this makefile.
+MAKEFILE=../pxl/pxl_ugcc.mak
+
+# Directories
+GLSRCDIR=/home/henrys/tmp/gs5.28
+GLGENDIR=../pxl/obj
+GLOBJDIR=../pxl/obj
+PLSRCDIR=../pl
+PLGENDIR=../pxl/obj
+PLOBJDIR=../pxl/obj
+PXLSRCDIR=../pxl
+PXLGENDIR=../pxl/obj
+PXLOBJDIR=../pxl/obj
+COMMONDIR=../common
+GENDIR=$(PXLGENDIR)
+
+# Language and configuration. These are actually platform-independent,
+# but we define them here just to keep all parameters in one place.
+CONFIG=6
+TARGET_DEVS=$(PXLOBJDIR)/pxl.dev
+TARGET_XE=pclxl
+MAIN_OBJ=$(PXLOBJDIR)/pxmain.$(OBJ)
+
+# Assorted definitions. Some of these should probably be factored out....
+# We use -O0 for debugging, because optimization confuses gdb.
+# Note that the omission of -Dconst= rules out the use of gcc versions
+# between 2.7.0 and 2.7.2 inclusive. (2.7.2.1 is OK.)
+#GCFLAGS=-Dconst= -Wall -Wpointer-arith -Wstrict-prototypes
+GCFLAGS=-Wall -Wcast-qual -Wpointer-arith -Wstrict-prototypes -Wwrite-strings
+CFLAGS=-g -O0 $(GCFLAGS) $(XCFLAGS)
+LDFLAGS=$(XLDFLAGS)
+EXTRALIBS=
+XINCLUDE=-I/usr/local/X/include
+XLIBDIRS=-L/usr/X11/lib
+XLIBDIR=
+XLIBS=Xt SM ICE Xext X11
+
+CCLD=gcc
+
+DEVICE_DEVS=x11.dev x11mono.dev x11alpha.dev x11cmyk.dev\
+ djet500.dev ljet4.dev\
+ pcxmono.dev pcxgray.dev\
+ pbmraw.dev pgmraw.dev ppmraw.dev
+
+# Generic makefile
+include $(COMMONDIR)/ugcc_top.mak
+
+# Subsystems
+include $(PLSRCDIR)/pl.mak
+include $(PXLSRCDIR)/pxl.mak
+
+# Main program.
+include $(PXLSRCDIR)/pxl_top.mak
diff --git a/pxl/news-pxl b/pxl/news-pxl
new file mode 100644
index 000000000..0e9426778
--- /dev/null
+++ b/pxl/news-pxl
@@ -0,0 +1,843 @@
+ Copyright (C) 1996 Aladdin Enterprises. All rights reserved.
+ Unauthorized use, copying, and/or distribution prohibited.
+
+- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+This file, NEWS-XL, describes the changes in the most recent releases of
+Aladdin's PCL XL code, in reverse chronological order.
+
+Version 1.15 (8/6/98)
+=====================
+
+Procedures
+----------
+
+Updates all makefiles to work with the new directory structure in the
+makefiles for library version 5.30. (pxl.mak, pxl_ugcc.mak)
+
+Interpreter
+-----------
+
+Updates GC procedures to work with the new interfaces in gsstruct.h.
+(pxgstate.c, pxparse.c, pxstate.c)
+
+Removes a few const-breaking casts. (pxffont.c)
+
+Updates image initialization to work with a change in the library's
+image_t_init_color procedure. (pxgstate.h, pxgstate.c, pximage.c, pxink.c)
+
+Version 1.14 (8/6/98)
+=====================
+
+Procedures
+----------
+
+Changes the compilation flags for gcc to assume that the version being used
+doesn't have the const code generation bug. (pxl_ugcc.mak)
+
+Utilities
+---------
+
+Makes the assembler produce a UEL, a PJL ENTER LANGUAGE, and a PCL XL header
+at the beginning of the output. (pxasm.ps)
+
+Version 1.13 (6/20/97)
+======================
+
+This is just a checkpoint for copying onto LPD's laptop.
+
+This version requires graphics library version 4.80 or later.
+
+Interpreter
+-----------
+
+Fixes bugs:
+ - In the case of an error, the parser still tried to save any
+remaining input data in its internal 17-byte buffer, signalling an
+InternalOverflow error if this failed. NOTE: this fix was delivered to our
+first major OEM customer by e-mail on 6/9/97. (pxparse.c)
+
+Factors out the table of paper sizes, which is now in a header file in the
+graphics library. (pxsessio.c)
+
+Updates uses of rc_adjust_only and rc_decrement to match a change in the
+graphics library. (pxgstate.c, pximage.c)
+
+Version 1.12 (3/26/97)
+======================
+
+Procedures
+----------
+
+Restructures the makefiles in preparation for the move to CVS. (pxl.mak,
+pxl_top.mak, pxl_ugcc.mak, pxl_watc.mak)
+
+Version 1.11 (2/27/97)
+======================
+
+Procedures
+----------
+
+Updates the non-Unix makefiles to include the new version.mak file. (This
+change is not needed for Unix makefiles.) (bcpcl.mak, watcpcl.mak)
+
+Interpreter
+-----------
+
+Fixes bugs:
+ - A failure when initializing to render a pattern could cause an
+invalid memory access. (pxink.c)
+
+Changes the parser so that it always reads all the input data (except in the
+case of an error or UEL). (pxparse.h, pxparse.c)
+
+Version 1.10 (2/10/97)
+============
+
+This version is the first one delivered to a customer as "final" code.
+It requires library version 4.51 or later.
+
+Known problems:
+ - If an error occurs while downloading data in parts, the partially
+loaded data isn't freed.
+ - The Borland makefile (bcpcl.mak) doesn't work.
+
+Interpreter
+-----------
+
+Fixes bugs:
+ - The page buffer wasn't cleared properly when the media size
+changed. (pxerrors.c, pxmain.c, pxsessio.c)
+ - In downloaded fonts with more than about 300 characters, some
+characters could get lost. (pxfont.c)
+ - Rectangles and ellipses weren't always drawn with the same
+starting point and direction as in H-P printers. (pxpaint.c)
+ - The computation of arc endpoints was slightly inaccurate. (It's
+still slightly inaccurate, but less so.) (pxpaint.c)
+ - PopGS popped one graphics state too many from the stack.
+(pxsessio.c)
+
+Increases the thickness of very thin lines slightly, to match the behavior
+of H-P printers more closely. (pxpaint.c)
+
+Changes the default halftone to one that matches the H-P printers more
+closely. It still doesn't have exactly the right behavior under 'or'ing,
+but it's closer. (pxink.c)
+
+Version 1.09 (1/31/97)
+============
+
+This version requires library version 4.50 or later.
+
+Procedures
+----------
+
+Adds a MS-DOS batch file for printing. (pxprint.bat)
+
+Changes the buffer size in the printing script from 500K to 200K, to match
+all other scripts. (pxprint.tcl)
+
+Changes the makefile to match changes in the library's method for selecting
+the options for band lists, and to explicitly include color image
+capability. (pclxl.mak, ugccpcl.mak, watcpcl.mak)
+
+Adds the pcxgray device to the default set of devices. (ugccpcl.mak,
+watcpcl.mak)
+
+Utilities
+---------
+
+Updates the assembler to reflect the move of the language definition header
+files to the graphics library. (pxasm.ps)
+
+Interpreter
+-----------
+
+Fixes bugs:
+ - The base font set by SetFont wasn't saved and restored with the
+graphics state. (pxgstate.h, pxstate.h, pxfont.c, pxgstate.c)
+ - Bitmap fonts weren't scaled correctly to match the device
+resolution. (pxfont.c)
+ - An error occurring outside a session or a page caused a crash.
+(pxerrors.c, pxgstate.c, pxmain.c)
+ - Very shallow or narrow gray-scale patterns didn't render properly,
+because they are halftoned before being cached. (pxgstate.h, pxgstate.c,
+pxink.c)
+ - ReadRastPattern with a block height of zero gave an error.
+(pximage.c)
+ - Scan line painting used the dash pattern, which it shouldn't. We
+rewrote scan line painting to use rectfill instead of stroke. (pxgstate.h,
+pximage.c)
+ - The size of indexed RGB pattern data wasn't computed correctly.
+(pximage.c, pxink.c)
+ - The deferred setting of the halftone didn't work properly.
+(pxink.c)
+
+Removes the clip_region from the state, since it is only relevant for the
+current command. (pxgstate.h, pxgstate.c)
+
+Resets the GC trigger at the end of each file, so that a very large file
+doesn't leave the trigger set to a high level. (pxmain.c)
+
+Includes the time and memory printing code even in non-DEBUG configurations.
+(pxmain.c)
+
+Adds recognition of @-files on the command line. (pxmain.c)
+
+Version 1.08 (1/21/97)
+============
+
+This version fixes a couple more problems. It requires library version 4.41
+or later.
+
+Documentation
+-------------
+
+Creates a new document that describes the additions to the graphics library
+that were required to handle the PCL XL graphics model. (pxlib.txt)
+
+Procedures
+----------
+
+Changes the makefile to match a change in the library's method for selecting
+the compression filter for RAM-based band lists. (ugccpcl.mak)
+
+Utilities
+---------
+
+Adds ESC as an assembler command to generate a UEL. (pxasm.ps)
+
+Interpreter
+-----------
+
+Fixes bugs:
+ - The allocator didn't start using string freelists early enough,
+leading to memory leaks. (pxmain.c)
+ - Images with non-default RasterOps didn't render correctly, because
+a recent change overlooked the need to call gx_set_dev_color. (pldraw.c)
+ - Our model of character transformation was wrong. Apparently only
+the most recent rotation, shearing, and scaling are applied, but they are
+applied in the order in which they were specified, not in a fixed order.
+(pxgstate.h, pxfont.c, pxgstate.c)
+
+Cleans up data structures earlier at the end of a page, and more thoroughly
+at the end of a session. (pxsessio.c)
+
+Moves the header files defining tag, attribute, and enumeration values for
+the PCL XL syntax to a device driver in the graphics library. (pclxl.mak,
+pxattr.h, pxenum.h, pxtag.h)
+
+Version 1.07 (1/13/97)
+============
+
+This version fixes the last known graphics model incompatibilities between
+PCL XL and our graphics library. It requires library version 4.40 or later.
+
+Procedures
+----------
+
+Always includes the debugging code for printing time and memory usage.
+(pxmain.c)
+
+Interpreter
+-----------
+
+Fixes bugs:
+ - Because of a missing #include, memmove was undefined on systems
+that didn't include it in the library. (pxerrors.c)
+ - Curves drawn with no join and with butt caps were "bristly"
+instead of smooth. (pxgstate.h, pxgstate.c, pxmain.c, pxsessio.c)
+ - Some other objects like rounded rectangles and ellipses had gaps
+in them when using no join and butt caps. (pxpaint.c)
+ - The flatness was set too large. (pxsessio.c)
+
+Invokes the garbage collector at the end of each page where more than a
+given cumulative amount of memory (currently 250K) has been allocated since
+the last garbage collection. (pxmain.c)
+
+Increases the input buffer size to 1K to reduce breakage, primarily for
+images. (pxmain.c)
+
+Removes some unnecessary buffering and processing from images and raster
+patterns. (pximage.c)
+
+Removes some code that has been moved to the library. (pxmain.c)
+
+Library
+-------
+
+Splits off the library news into a separate file. (NEWS-PL)
+
+Version 1.06 (1/1/97)
+============
+
+This version is the first one that merits an "official" run of the Genoa
+suite. It requires graphics library version 4.39 or later.
+
+Utilities
+---------
+
+Enhances the assembler so it can process trace output. (pxasm.ps)
+
+Interpreter
+-----------
+
+Fixes bugs:
+ - Font names in error and warning messages weren't terminated
+properly, leading to garbage characters on output. (pxfont.c)
+ - The error page font was much too large. (pxerrors.c)
+ - Unique IDs were only unique within a session, which confused the
+library. (pxfont.h, pxstate.h, pxffont.c, pxerrors.c, pxfont.c, pximage.c,
+pxsessio.c)
+ - Pies were drawn in the order arc, line, close, rather than the
+correct order line, arc, close. (pxpaint.c)
+ - pxtSetPaintTxMode was incorrectly named pxtSetPatternTxMode.
+(pxtag.h)
+ - When substituting fonts, serif and weight properties weren't given
+priority over the fixed-spacing property. (pxffont.c)
+ - Setting a halftone shouldn't affect the rendering of the current
+pen or brush. We can't implement this exactly right with a reasonable
+amount of work; instead, we simply defer setting the halftone until the next
+SetBrushSource or SetPenSource that requires it. (pxgstate.h, pxgstate.c)
+ - If an input buffer boundary fell after the very first byte of a
+RLE-compressed bitmap, a spurious error (MissingData or ExtraData) could
+occur. (pximage.c)
+ - Setting the miter limit to 0 should be equivalent to setting it to
+its default value of 10. (pxgstate.c)
+
+Removes recognition of @PJL commands in PCL XL context, since this isn't
+appropriate. (pxparse.c, pxptable.c)
+
+Changes the tracing output slightly so it can serve as input to the
+assembler. (pxmain.c, pxparse.c)
+
+Speeds up painting by eliminating the gsave and grestore used to preserve
+the path. (pxpaint.c)
+
+Removes some redundant code in setting up bounding boxes. (pxvalue.h,
+pxpaint.c)
+
+Speeds up drawing of rectangles. (pxpaint.c)
+
+Makes the default CTM values integers if this is possible without
+introducing inaccuracies. (pxsessio.c)
+
+Library
+-------
+
+Removes some inefficiency in freeing fonts. (plfont.c)
+
+Speeds up rendering of pseudo-bold characters. (plchar.c)
+
+Version 1.05 (12/19/96)
+============
+
+This version is the first one that runs the Genoa CET without crashing. (In
+fact, it runs most of the CET correctly, as well as most of the FTS.)
+
+This release requires graphics library version 4.37 or later.
+
+Documentation
+-------------
+
+Updates the discrepancy report: Rectangles are drawn clockwise.
+(pxspec.txt)
+
+Adds a report on the Genoa PCL6 CET. (pxcet.txt)
+
+Sanitizes the reports to confine Aladdin's name to a separate cover page.
+(pxspec.txt, pxfts.txt)
+
+Procedures
+----------
+
+Fixes bugs:
+ - The Watcom makefile didn't allocate a large enough stack.
+(watcpcl.mak)
+
+PJL
+---
+
+Adds the ability to discard the remainder of a job (through a UEL).
+(pjparse.h, pjparse.c)
+
+Interpreter
+-----------
+
+Fixes bugs:
+ - Setting character properties before setting a font caused an
+invalid memory access. (pxgstate.c)
+ - The state wasn't cleaned up properly after an error or UEL.
+(pxstate.h, pxmain.c, pxsessio.c)
+ - The code didn't skip to a UEL and continue after an error.
+(pxmain.c)
+ - Very large arrays caused a memory access error. (pxoper.h,
+pxparse.c)
+ - Contrary to the documentation, CharShear allows values of -32768
+and 32767 (i.e., the endpoints are included, not excluded). (pxptable.c)
+ - Specifying the same attribute twice was considered an error.
+(This is legal; the last value has priority.) (pxparse.c)
+ - An out-of-range MediaSize or MediaSource was considered an error;
+it should be ignored. (pxptable.c, pxsessio.c)
+ - Chord, Arc, and Pie operators didn't allow degenerate boxes (with
+zero width or height). (pxpaint.c)
+ - Apparently, Ellipse[Path] starts and ends at what the library
+considers the 180 degree point, not the 0 degree point. (pxpaint.c)
+ - Contrary to the specification, Rectangles should be drawn
+clockwise. (pxpaint.c)
+ - Default error reporting wasn't initialized properly (eErrorPage
+beforethe first BeginSession, eNoReporting within a session). (pxsessio.c,
+pxstate.c)
+ - Bitmap fonts weren't immune to scaling, rotation, and shearing.
+(pxgstate.c)
+ - The recorded list of warnings wasn't reset after producing the
+error page. (pxerrors.h, pxerrors.c, pxmain.c)
+ - Apparently, printers only remember the most recent warning of each
+type from pxRemoveFont. (pxerrors.h, pxerrors.c, pxfont.c)
+ - Univers CdMd was omitted from the list of built-in fonts.
+(pxffont.c)
+ - Bitmaps weren't scaled to match their resolution to the device
+resolution. (pxgstate.c)
+ - Only stroking with a zero-width pen if the brush is null was
+wrong. We instituted this in 1.04 to get around a problem with stroking the
+result of SetPathToClip, but now that we've fixed the underlying problem,
+we've gone back to doing what the spec says, namely, always stroking unless
+the pen is null. (pxpaint.c)
+ - Attempting to rotate, shear, or scale a bitmap font should cause
+the Text and TextPath operators to report an IllegalFontData error, but it
+didn't. (pxgstate.h, pxfont.c, pxgstate.c)
+ - If the path wasn't empty, ScanLineRel would stroke the path
+instead of ignoring it. (pximage.c)
+ - Compressed bitmap data could be parsed incorrectly, leading to
+spurious MissingData or ExtraData errors. (pximage.c)
+ - Scan lines were painted with an unpredictable color (usually
+black) instead of the current brush. (pximage.c)
+ - 8- and 16-bit stream names could be confused with each other.
+(pxstate.h, pxstream.c)
+ - The flatness was set too small, leading to occasional anomalies.
+(pxsessio.c)
+ - Apparently, text transforms *objects*, whereas images and paths
+transform *coordinates*. This requires applying transformations in the
+opposite order, which in turn requires keeping two separate transformation
+matrices. (pxgstate.h, pxfont.c, pxgstate.c)
+ - If the coordinates of an arc, pie or wedge were specified in a
+non-standard order, the wrong part of the arc was drawn. (pxpaint.c)
+ - Apparently SetColorSpace doesn't ever set the brush or pen to
+black. (pxgstate.c)
+ - A palette could be freed while it was still in use by another
+state on the graphics stack, leading to memory corruption. (pxgstate.c)
+ - The "margins" (the offset on the page) weren't set to zero. (This
+isn't right, but it's better than a non-zero value.) (pxsessio.c)
+ - The IllegalTag error message printed the last operator name rather
+than the tag in question. (pxparse.c)
+ - SetPaintTxMode was incorrectly named SetPatternTxMode.
+(pxgstate.c, pxptable.c)
+ - Occasionally a PJL command was incorrectly processed as PCL XL.
+(pxmain.c)
+ - Some other errors detected by the parser were incorrectly reported
+as IllegalTag errors. (pxparse.c)
+ - If both MissingAttribute and IllegalAttribute errors could be
+generated, IllegalAttribute should take priority. (pxparse.c)
+ - IllegalAttributeDataType errors were reported when the attribute
+was read, not at the next operator. (pxparse.c)
+ - Some errors occurring during Text and TextPath were ignored.
+(pxfont.c)
+ - Errors occurring within user-defined streams weren't reported
+properly. (pxoper.h, pxparse.c, pxstream.c)
+ - An illegal Orientation value produced an error rather than a
+warning. (pxptable.c, pxsessio.c)
+ - Built-in fonts weren't marked as "internal", so they could be
+removed. (pxffont.c)
+ - The warning message for attempting to remove an internal font was
+incorrect. (pxfont.c)
+ - Arial, rather than Courier, was the default font if no better
+substitute was found. (pxffont.c)
+ - The operator count for IllegalOperatorSequence errors was off by
+1. (pxparse.c)
+ - An attempt was made to find a substitute font even when no known
+font had a compatible symbol set. (pxfont.h, pxffont.c, pxfont.c)
+ - Some errors didn't include relevant additional information.
+(pxerrors.h, pxfont.h, pxstate.h, pxerrors.c, pxffont.c, pxfont.c, pxmain.c)
+ - ReadFontHeader didn't check the font header fields for legality.
+(pxfont.c)
+
+Revises the warning messages to look more like those from H-P printers.
+(pxerrors.h, pxfont.h, pxerrors.c, pxffont.c, pxfont.c)
+
+Increases the maximum number of dash pattern elements to match the
+(apparent) value of 20 in the H-P printers. (pxgstate.c)
+
+Changes the handling of font substitution so that if the requested font was
+one of the built-in set, we don't produce a warning message, even if we had
+to substitute another (Windows) font for it. (pxfont.h, pxffont.c,
+pxfont.c)
+
+Changes the computation of the char_matrix to be lazy. (pxgstate.h,
+pxfont.c, pxgstate.c)
+
+Changes the subsystem name in error messages from N/A to either MAIN or
+GRAPHICS. (pxmain.c, pxparse.c)
+
+Library
+-------
+
+Fixes bugs:
+ - The character count in the header of downloaded fonts was taken as
+a hard limit rather than a hint. (plfont.h, plchar.c)
+ - The rehashing algorithm for looking up downloaded characters could
+get into an endless loop. (plfont.h, plchar.c)
+ - Attempting to free a partially-constructed font caused a memory
+access error. (plfont.c)
+ - The segments of downloaded fonts weren't checked carefully enough.
+(plfont.h, plfont.c)
+
+Version 1.04 (12/10/96)
+============
+
+We thought 1.03 ran over 90% of the Genoa FTS, but when we re-ran the full
+test, we discovered that we'd broken some things in the course of fixing
+others. This release fixes them all. This release also fixes all memory
+leaks on the Genoa FTS except for one.
+
+This release requires graphics library version 4.37 or later.
+
+Documentation
+-------------
+
+Adds a report on the PCL XL specification and its relationship to the LJ 6MP
+printer. (pxspec.txt)
+
+Adds a report on the Genoa PCL6 FTS. (pxfts.txt)
+
+Procedures
+----------
+
+Removes -O (optimization) from the default configuration in the PCL
+makefile, because it creates too many problems for debugging. (ugccpcl.mak)
+
+Changes the gcc makefile to offer the option of using emulated floating
+point when profiling, to get more realistic timings for embedded systems.
+(ugccpcl.mak)
+
+Fonts
+-----
+
+Fixes bugs:
+ - The italic and bold-italic variants of the built-in fonts were
+interchanged. (pxffont.c)
+
+Interpreter
+-----------
+
+Fixes bugs:
+ - If a "known" font file couldn't be opened, the result was a crash
+rather than an error. (pxffont.c)
+ - Stroking with a zero-width pen was suppressed, which is wrong. We
+think a better approach is to stroke with a zero-width pen only if the brush
+is null, but we aren't sure this is right either. (pxpaint.c)
+ - A non-const pointer was declared as const, upsetting some
+compilers. (pxgstate.c)
+ - We thought that SetClipIntersect intersects the interior of the
+clipping region and the interior of the current region, even if the clipping
+mode is exterior, but apparently it intersects the effective clipping region
+(interior or exterior according to the most recently set clipping region)
+with either the interior or the exterior of the current region.
+(pxgstate.c)
+ - If the current brush or pen was a raster pattern, SetColorSpace
+didn't decrement its reference count properly, causing a memory leak.
+(pxgstate.c)
+ - Setting a user-defined dither matrix didn't reset the transfer
+function to the identity function. (pxgstate.c)
+ - An unused label appeared in the source code, preventing a clean
+compilation. (pxpaint.c)
+ - If SetHalftoneMethod didn't provide a DitherMatrixOrigin, the
+origin wasn't reset to (0,0). (pxgstate.c)
+ - User-defined dither matrices were never freed. causing a memory
+leak. (pxgstate.h, pxgstate.c)
+
+Changes the single-object drawing operators (Chord, Ellipse, Pie, Rectangle,
+and RoundRectangle) so that they clear the path (but preserve the cursor
+position) instead of leaving it set to the object. This matches the
+LaserJet 5/6 implementation, but is different from the published
+specification. (pxpaint.c)
+
+Modifies the transfer function slightly to match the LJ 6MP output better.
+(pxmain.c)
+
+Restructures the set_source procedure slightly to avoid a compiler complaint
+about an uninitialized variable. (pxgstate.c)
+
+Makes the default halftone method use an ordered dither, and offsets the
+source and paint screens from each other in order to match the observed
+behavior of the LaserJet 5 and 6 printers. (pxgstate.c)
+
+Library
+-------
+
+Fixes bugs:
+ - The tilde character position was mapped incorrectly in many symbol
+sets. (plsymbol.c)
+
+Version 1.03 (12/3/96)
+============
+
+This release runs a substantial fraction of the Genoa ATS correctly, as well
+as over 90% of the FTS.
+
+This release requires library version 4.36 or later.
+
+Utilities
+---------
+
+Removes the extra \n that pxasm used to produce after each operator.
+(pxasm.ps)
+
+Interpreter
+-----------
+
+Fixes bugs:
+ - If the current brush was null, images didn't appear. (pximage.c)
+ - Font UniqueIDs were reused for each session, causing confusion in
+the character cache. (pxfont.h, pxsessio.c, pxffont.c, pxfont.c)
+ - Synthetic bolding wasn't implemented. (pxfont.c)
+ - Downloaded fonts weren't registered properly with the library.
+(pxfont.c)
+ - After defining a stream, one copy of the stream name wasn't freed.
+(pxvalue.h, pxstream.c)
+ - The graphics state stack and path weren't cleared at the end of
+each page. (pxsessio.c)
+ - Color palettes were never freed. (pxgstate.h, pxgstate.c)
+ - A bookkeeping structure wasn't freed after rendering an image.
+(pximage.c)
+ - The decision of whether or not to use gamma correction was made
+incorrectly, so printers didn't get gamma-corrected. (pxmain.c)
+ - Because a variable wasn't initialized, BeginPage could signal a
+non-existent error. (pxsessio.c)
+ - Patterns were never released. (pxgstate.c, pximage.c)
+ - Patterns weren't given a unique ID, so they couldn't be cached
+effectively. (pxgstate.h, pxstate.h, pxfont.c, pxgstate.c, pximage.c,
+pxsessio.c)
+ - Patterns only kept a single pointer to their instantiation,
+causing both memory management and output problems. (pxgstate.h,
+pxgstate.c, pximage.c)
+
+Changes the interpreter so that user-defined streams do *not* persist past
+the end of the session. We think this is the normal behavior for printers.
+(pxstate.c, pxsessio.c)
+
+Changes the -ZI debugging switch so it prints out the contents of arrays and
+data streams as well as tags and scalar values; -Zi continues to print out
+only tags and scalars. (pxparse.c)
+
+Adds /win95/fonts as a third possible directory for fonts. (pxffont.c)
+
+Library
+-------
+
+Fixes bugs:
+ - Empty TrueType glyphs (with no outline data at all) caused an
+error. (plfont.c)
+ - "Built-in" fonts weren't registered properly with the library.
+(plfont.c)
+
+Adds support for synthetic bolding, indicated by the top 7 bits of WMode.
+(plfont.h, plchar.c, plfont.c)
+
+Splits font-level procedures from character-level procedures, since plfont.c
+was getting too large. (plchar.c, plfont.c)
+
+Adds a preliminary map from MSL to Unicode. THIS MAP HAS MANY BUGS IN IT
+AND SHOULD NOT BE USED YET. (plvocab.h, plvocab.c)
+
+Version 1.02 (11/24/96)
+============
+
+This version produces correct output for 67 of the 76 files of the Genoa
+PCL6 FTS (except for the image bug introduced in 1.02 and fixed in 1.03),
+and runs at least part of the ATS without crashing or producing
+inappropriate error messages.
+
+Procedures
+----------
+
+Adds ljet4 and pcxmono to the list of compiled-in drivers. (ugccpcl.mak,
+watcpcl.mak, bcpcl.mak)
+
+Interpreter
+-----------
+
+Fixes bugs:
+ - All RGB color values except black were interpreted as white.
+(pxgstate.c)
+ - Rounded rectangles were drawn with incorrect corner radii.
+(pxpaint.c)
+ - Ellipses and circles weren't drawn at all. (pxpaint.c)
+ - Real-valued data weren't printed properly when tracing.
+(pxparse.c)
+ - UnitsPerMeasure weren't interpreted properly if they were real
+values rather than integers. (pxstate.h, pxsessio.c)
+ - SetPageRotation rotated the page in the opposite direction of the
+intended one. (pxgstate.c)
+ - A blank page was always produced before an error page.
+(pxstate.h, pxerrors.c, pximage.c, pxpaint.c, pxsessio.c)
+ - The error page could be blank if an unusual Unit of Measure was in
+use. (pxerrors.c)
+ - Character shearing had no effect. (pxgstate.c)
+ - Characters on the error page were piled on top of each other.
+(pxbfont.ps, pxerrors.c)
+ - The error page was printed upside-down. (pxerrors.c)
+ - The size and spacing of error messages weren't consistent.
+(pxbfont.ps, pxbfont.h, pxerrors.c)
+ - SetClipToPage didn't reset the current path. (pxgstate.c)
+ - Arcs, chords, and pies with equal starting and ending points were
+drawn as 0 degrees rather than 360 degrees. (pxpaint.c)
+ - Ellipse[Path] and Rectangle[Path] didn't leave the cursor in the
+correct position. (pxpaint.c)
+ - Apparently, SetMiterLimit with a parameter of 0 means always
+miter, rather than never miter. (pxgstate.c)
+ - Text didn't get painted with the correct color. (pxpaint.c)
+ - 16-bit characters weren't implemented. (They still probably
+aren't implemented quite right.) (pxfont.c)
+ - Vertical character substitution wasn't implemented. (pxgstate.h,
+pxfont.c, pxgstate.c)
+ - Text with a null brush caused an error. (pxpaint.c)
+ - If a raster pattern was downloaded in multiple blocks, blocks
+other than the first overwrote the first block. (pximage.c)
+ - Raster patterns that weren't a multiple of 4 bytes wide produced
+incorrect output. (pximage.c)
+ - Ellipses and 360 degree arcs weren't closed. (pxpaint.c)
+ - Text with a non-invertible combined CTM and text matrix (e.g., a
+shear of (1,1)) didn't work. (pxfont.c)
+ - Rectangles were drawn clockwise rather than counter-clockwise.
+(pxpaint.c)
+ - SetClipIntersect didn't construct the correct clipping region if
+outside clipping was selected. (pxgstate.c) ****** STILL NOT CORRECT ******
+ - Stroking with a zero-width pen wasn't a no-op. (pxpaint.c)
+ - RasterOp with images didn't use the current color/pattern.
+(pxgstate.h, pxgstate.c, pximage.c, pxpaint.c)
+ - Downloaded dither matrices could have garbage interspersed.
+(pxgstate.c)
+ - Galley character substitution wasn't implemented. (plfont.h,
+plfont.c)
+ - Font substitution didn't take family names or modifiers into
+account properly. (pxffont.c)
+ - Some procedures weren't declared (prototyped) at all points of
+use. (pxgstate.c, pximage.c, pxmain.c, pxpaint.c, pxsessio.c)
+ - Images with zero width or height followed by a zero-length
+embedded data block caused a syntax error. (pxparse.c)
+ - The character size and symbol set weren't saved and restored by
+Push/PopGS. (pxgstate.h, pxstate.h, pxfont.c)
+ - The cmap table for TrueType fonts wasn't interpreted correctly,
+causing incorrect output. (plfont.c)
+
+Allows specifying a list of directories, rather than a single directory, for
+loading known fonts. (pxfont.c)
+
+Improves the interpreter tracing output. (pxparse.c, pxptable.c)
+
+Splits off font definition and lookup into a separate module. (pxfont.h,
+pxffont.c, pxfont.c)
+
+Adapts some code to the new access procedures. (pxfont.c)
+
+Moves SetFont to a more appropriate file. (pxgstate.h, pxfont.c,
+pxgstate.c)
+
+Adapts code to the new library design for fonts and characters. (pxfont.h,
+pxerrors.c, pxffont.c, pxfont.c, pxgstate.c)
+
+Adds the ability to request printing/displaying only a selected range of
+pages. (pxstate.h, pxmain.c)
+
+Library
+-------
+
+Fixes bugs:
+ - Replacing a character in a downloaded font didn't remove the old
+definition from the character cache. (plfont.c)
+
+Adds definitions for a substantial number of the built-in symbol sets.
+(plsymbol.h, plsymbol.c)
+
+Redesigns the library procedures associated with fonts and characters,
+including the pl_font_t structure. This is a fairly massive change.
+(plfont.h, plfont.c)
+
+Adds a procedure for obtaining the vertical substitute for a TrueType glyph.
+(plfont.h, plfont.c)
+
+Defines common accessor procedures for big-endian multi-byte values.
+(pcllib.mak, plvalue.h, plfont.c, plvalue.c)
+
+Adds the ability to request printing/displaying of only a selected range of
+pages. (plmain.h, plmain.c)
+
+Version 1.01 (11/11/96)
+============
+
+This version runs the Genoa PCL6 FTS without crashing or producing
+inappropriate error messages.
+
+Procedures
+----------
+
+Fixes bugs:
+ - The Watcom makefile didn't work. (watcpcl.mak)
+
+Interpreter
+-----------
+
+Fixes bugs:
+ - We were using a fill adjustment of 0.25 rather than 0.0
+(center-of-pixel rule). (pximage.c, pxmain.c)
+ - The graphics state was initialized in a way that didn't work
+properly if the input file did an extra PopGS. (pxgstate.h, pxgstate.c,
+pxsessio.c)
+ - If a BeginSession didn't have an ErrorReport attribute, a memory
+access error occurred. (pxsessio.c)
+ - When stroking, PaintPath didn't set the raster op to 240 and the
+paint transparency to true, per documentation. (pxpaint.c)
+ - CloseDataSource incorrectly indicated an error if no data source
+was open. (pxsessio.c)
+ - Bezier[Rel]Path incorrectly attempted to read too much data from
+the data source. (pxpaint.c)
+ - The CharShear attribute was incorrectly tagged as taking a scalar
+value rather than an X/Y pair. (pxptable.c)
+ - SetMiterLimit didn't handle values less than 1, which the graphics
+library considers illegal. (pxgstate.c)
+ - Ellipse[Path] incorrectly indicated an error if the bounding box
+had zero width and/or height. (pxpaint.c)
+ - Apparently, PopGS with an empty stack should nothing, even though
+the documentation says this is illegal. (pxgstate.h, pxgstate.c)
+ - Set(Brush|Pen)Source apparently should allow setting patterns
+defined in other than the current color space (the documentation is unclear
+about this, but generally suggests that this is not allowed). (pxgstate.c)
+ - EndSession stopped execution if there were any warnings associated
+with the session. (pxmain.c)
+ - The bitmap font used for printing the error page wasn't
+initialized correctly. (pxfont.c, pxsessio.c)
+ - If the Measure for a session was specified as millimeters or
+tenths of a millimeter, the scale was set up incorrectly. (pxenum.h)
+ - SetColorSpace apparently should *not* set the pen or brush to
+black if it was previously null. (pxgstate.c)
+ - Apparently the X pair values in ScanLineRel are (x, dx) and not
+(x0, x1). (The documentation is very poorly written, but it definitely
+suggests the latter rather than the former.) (pximage.c)
+
+Rewrites the main program to start in PJL mode, and to handle multiple input
+files. (pxmain.c, pxparse.c)
+
+Removes waiting for a typed character after each operator when -ZI is
+selected. (pxparse.c)
+
+Library
+-------
+
+Moves PJL processing into the library. (pcllib.mak, pclxl.mak, plparse.h)
+
+Recognizes UEL in PJL mode. (pjparse.c)
+
+Version 1.0 (11/7/96)
+===========
+
+This is the first version to be archived.
diff --git a/pxl/pxasm.bat b/pxl/pxasm.bat
new file mode 100644
index 000000000..4c50ffb8c
--- /dev/null
+++ b/pxl/pxasm.bat
@@ -0,0 +1 @@
+@..\gs386 -I.. -q -dNODISPLAY -sOutputFile=%2 pxasm.ps %1 -c quit \ No newline at end of file
diff --git a/pxl/pxasm.ps b/pxl/pxasm.ps
new file mode 100644
index 000000000..cd9511b5d
--- /dev/null
+++ b/pxl/pxasm.ps
@@ -0,0 +1,192 @@
+%!
+% Copyright (C) 1996, 1997 Aladdin Enterprises. All rights reserved.
+% Unauthorized use, copying, and/or distribution prohibited.
+
+% Assemble a PCL XL "program". The syntax is designed to minimize the size
+% of the assembler, not for the convenience of users.
+
+/big_endian false def
+big_endian { 1 } { 2 } ifelse setobjectformat
+
+/pxdict 1000 dict def
+pxdict begin
+
+/libfile % <filename> libfile <file>
+ { findlibfile
+ { exch pop }
+ { print ( not found!\n) print flush stop }
+ ifelse
+ } bind def
+
+% Define output utility procedures.
+/.out /OutputFile where { pop OutputFile } { (%stdout) } ifelse (w) file def
+/.w { //.out exch write } bind def
+/.w1 { 255 and //.out exch write } bind def
+/.w2 { dup -8 bitshift big_endian not { exch } if .w1 .w1 } bind def
+/.w4 { dup -16 bitshift big_endian not { exch } if .w2 .w2 } bind def
+/.wr % <real> .wr -
+ { cvr =string /NullEncode filter dup 3 -1 roll 0 writeobject
+ dup flushfile closefile
+ .out =string 8 4 getinterval writestring
+ }
+bind def
+/.ws { .out exch writestring } bind def
+
+% Define the attributes.
+/.asmattr { 16#f8 .w .w } bind def
+/.defattr { /.asmattr cvx 2 packedarray cvx def } bind def
+(gdevpxat.h) libfile
+ { dup =string readline pop (typedef) anchorsearch { pop pop exit } if pop
+ } loop
+0
+ { 1 index =string readline not { pop exit } if
+ ( pxa) anchorsearch
+ { pop ( = ) search
+ { exch pop exch (,) search pop exch pop exch pop cvi
+ 3 -1 roll pop exch
+ }
+ { (,) search pop exch pop exch pop
+ }
+ ifelse
+ (@) exch concatstrings 1 index .defattr
+ 1 add
+ }
+ { pop
+ }
+ ifelse
+ }
+loop pop closefile
+
+% Define the enumerated values.
+/.defenum { /b cvx 2 packedarray cvx def } bind def
+(gdevpxen.h) libfile
+0
+ { 1 index =string readline not { pop exit } if
+ (,) search { exch pop exch pop } if
+ dup ( e) anchorsearch
+ { pop pop token pop exch
+ % Stack: file value name rest
+ token
+ { % The token must be an '='.
+ pop token pop exch pop 3 -1 roll pop exch
+ }
+ if
+ % Stack: file value name
+ 1 index .defenum 1 add
+ }
+ { pop pop
+ }
+ ifelse
+ }
+loop pop closefile
+
+% Define the operators.
+/.asmop /.w load def
+/.defop { /.asmop cvx 2 packedarray cvx def } bind def
+(gdevpxop.h) libfile
+ { dup =string readline pop (typedef) anchorsearch { pop pop exit } if pop
+ } loop
+0
+ { 1 index =string readline not { pop exit } if
+ ( pxt) anchorsearch
+ { pop
+ (, pxt) search pop exch pop 2 index .defop
+ (, pxt) search pop exch pop 2 index 1 add .defop
+ (, pxt) search pop exch pop 2 index 2 add .defop
+ (,) search { exch pop exch pop } if 1 index 3 add .defop
+ 4 add
+ }
+ { pop
+ }
+ ifelse
+ }
+loop pop closefile
+
+% Define syntactic elements for numbers, points, and boxes.
+/b { 16#c0 .w .w1 } def
+/us { 16#c1 .w .w2 } def
+/ul { 16#c2 .w .w4 } def
+/ss { 16#c3 .w .w2 } def
+/sl { 16#c4 .w .w4 } def
+/r { 16#c5 .w .wr } def
+/.xy { .w 3 -1 roll 1 index exec exec } bind def
+/bp { {.w1} 16#d0 .xy } def
+/usp { {.w2} 16#d1 .xy } def
+/ulp { {.w4} 16#d2 .xy } def
+/ssp { {.w2} 16#d3 .xy } def
+/slp { {.w4} 16#d4 .xy } def
+/rp { {.wr} 16#d5 .xy } def
+/.box { .w 5 1 roll 4 packedarray exch forall } bind def
+/bq { {.w1} 16#e0 .box } def
+/usq { {.w2} 16#e1 .box } def
+/ulq { {.w4} 16#e2 .box } def
+/ssq { {.w2} 16#e3 .box } def
+/slq { {.w4} 16#e4 .box } def
+/rq { {.wr} 16#e5 .box } def
+
+% Define syntactic elements for arrays.
+/.array { .w 1 index length dup 255 gt { us } { b } ifelse forall } bind def
+/ba { {.w1} 16#c8 .array } def
+/usa { {.w2} 16#c9 .array } def
+/ula { {.w4} 16#ca .array } def
+/ssa { {.w2} 16#cb .array } def
+/sla { {.w4} 16#cc .array } def
+/ra { {.wr} 16#cd .array } def
+
+% Define other syntactic elements.
+/c { .w } def % single character
+/s { .ws } def % string
+
+% Define tokens that allow us to assemble the output from tracing.
+/next { currentfile token pop } bind def
+/next2 { next next } def
+/next4 { next next next next } def
+/skip { next pop } bind def
+/pos= { skip } def
+/tag= { skip } def
+/ESC { (\033%-12345X) s } def
+% Scalars
+/_ubyte { next b } def
+/_uint16 { next us } def
+/_uint32 { next ul } def
+/_sint16 { next ss } def
+/_sint32 { next sl } def
+/_real32 { next r } def
+% Points
+/_ubyte_xy { next2 bp } def
+/_uint16_xy { next2 usp } def
+/_uint32_xy { next2 ulp } def
+/_sint16_xy { next2 ssp } def
+/_sint32_xy { next2 slp } def
+/_real32_xy { next2 rp } def
+% Boxes
+/_ubyte_box { next4 bq } def
+/_uint16_box { next4 usq } def
+/_uint32_box { next4 ulq } def
+/_sint16_box { next4 ssq } def
+/_sint32_box { next4 slq } def
+/_real32_box { next4 rq } def
+% Data and arrays
+/data,
+ { next pop next dup 255 le { 16#fb .w .w } { 16#fa .w .w4 } ifelse
+ } def
+/data: { next s } def
+/nextarray { skip skip skip skip next } def
+/_ubyte_array { nextarray ba } def
+/_uint16_array { nextarray usa } def
+/_uint32_array { nextarray ula } def
+/_sint16_array { nextarray ssa } def
+/_sint32_array { nextarray sla } def
+/_real32_array { nextarray ra } def
+/_ubyte_array... { } def
+/_uint16_array... { } def
+/_uint32_array... { } def
+/_sint16_array... { } def
+/_sint32_array... { } def
+/_real32_array... { } def
+
+% Write a header that pacifies H-P printers.
+(\033%-12345X@PJL ENTER LANGUAGE = PCLXL\n\) HP-PCL XL;1;1\n) .ws
+
+% Now just execute the source as a PostScript program.
+% The assembled code will be written to stdout.
diff --git a/pxl/pxattr.h b/pxl/pxattr.h
new file mode 100644
index 000000000..c056157d2
--- /dev/null
+++ b/pxl/pxattr.h
@@ -0,0 +1,10 @@
+/* Copyright (C) 1997 Aladdin Enterprises. All rights reserved.
+ Unauthorized use, copying, and/or distribution prohibited.
+ */
+
+/* pxattr.h */
+/* Attribute ID definitions for PCL XL parser */
+
+/* The contents of this file have been moved to the graphics library, */
+/* so they can be used in the PCL XL driver. */
+#include "gdevpxat.h"
diff --git a/pxl/pxbfont.c b/pxl/pxbfont.c
new file mode 100644
index 000000000..d85b0ad6e
--- /dev/null
+++ b/pxl/pxbfont.c
@@ -0,0 +1,2048 @@
+/* Copyright (C) 1996 Aladdin Enterprises. All rights reserved.
+ Unauthorized use, copying, and/or distribution prohibited.
+ */
+
+#include "stdpre.h"
+#include "pxbfont.h"
+
+const byte px_bitmap_font_char_data[] = {
+ 34 /* " */, 4, 0, 14, 1, 0, 0, 0, 0, 0, 30, 0, 23, 0, 14, 0, 92,
+1,252,254,
+1,252,254,
+0,248,124,
+0,248,124,
+0,248,124,
+0,248,124,
+0,248,124,
+0,248,124,
+0,248,124,
+0,248,124,
+0,112,56,
+0,112,56,
+0,112,56,
+0,112,56,
+ 40 /* ( */, 4, 0, 14, 1, 0, 0, 0, 0, 0, 30, 0, 23, 0, 36, 0, 92,
+0,0,6,
+0,0,14,
+0,0,12,
+0,0,28,
+0,0,56,
+0,0,56,
+0,0,112,
+0,0,112,
+0,0,112,
+0,0,224,
+0,0,224,
+0,0,224,
+0,0,192,
+0,1,192,
+0,1,192,
+0,1,192,
+0,1,192,
+0,1,192,
+0,1,192,
+0,1,192,
+0,1,192,
+0,1,192,
+0,1,192,
+0,0,192,
+0,0,224,
+0,0,224,
+0,0,224,
+0,0,112,
+0,0,112,
+0,0,48,
+0,0,56,
+0,0,56,
+0,0,28,
+0,0,12,
+0,0,14,
+0,0,6,
+ 41 /* ) */, 4, 0, 14, 1, 0, 0, 0, 0, 0, 30, 0, 16, 0, 36, 0, 64,
+1,128,
+0,192,
+0,224,
+0,96,
+0,112,
+0,48,
+0,56,
+0,24,
+0,28,
+0,28,
+0,14,
+0,14,
+0,14,
+0,6,
+0,7,
+0,7,
+0,7,
+0,7,
+0,7,
+0,7,
+0,7,
+0,7,
+0,6,
+0,14,
+0,14,
+0,12,
+0,28,
+0,28,
+0,24,
+0,56,
+0,48,
+0,112,
+0,96,
+0,224,
+0,192,
+1,128,
+ 45 /* - */, 4, 0, 14, 1, 0, 0, 0, 0, 0, 15, 0, 26, 0, 2, 0, 104,
+15,255,255,192,
+15,255,255,192,
+ 47 /* / */, 4, 0, 14, 1, 0, 0, 0, 0, 0, 33, 0, 24, 0, 37, 0, 96,
+0,0,3,
+0,0,3,
+0,0,7,
+0,0,6,
+0,0,14,
+0,0,12,
+0,0,28,
+0,0,24,
+0,0,56,
+0,0,48,
+0,0,48,
+0,0,96,
+0,0,96,
+0,0,192,
+0,0,192,
+0,1,128,
+0,1,128,
+0,3,128,
+0,3,0,
+0,7,0,
+0,6,0,
+0,14,0,
+0,12,0,
+0,28,0,
+0,24,0,
+0,24,0,
+0,48,0,
+0,48,0,
+0,96,0,
+0,96,0,
+0,192,0,
+0,192,0,
+1,128,0,
+1,128,0,
+3,128,0,
+3,0,0,
+3,0,0,
+ 48 /* 0 */, 4, 0, 14, 1, 0, 0, 0, 0, 0, 31, 0, 24, 0, 32, 0, 96,
+0,15,192,
+0,63,240,
+0,120,120,
+0,96,24,
+0,192,12,
+1,192,14,
+1,128,6,
+1,128,6,
+1,128,6,
+3,0,3,
+3,0,3,
+3,0,3,
+3,0,3,
+3,0,3,
+3,0,3,
+3,0,3,
+3,0,3,
+3,0,3,
+3,0,3,
+3,0,3,
+3,0,3,
+3,0,3,
+3,0,3,
+1,128,6,
+1,128,6,
+1,128,6,
+0,192,14,
+0,192,12,
+0,96,24,
+0,120,120,
+0,31,224,
+0,15,192,
+ 49 /* 1 */, 4, 0, 14, 1, 0, 0, 0, 0, 0, 30, 0, 24, 0, 30, 0, 96,
+0,7,0,
+0,127,0,
+3,251,0,
+3,195,0,
+0,3,0,
+0,3,0,
+0,3,0,
+0,3,0,
+0,3,0,
+0,3,0,
+0,3,0,
+0,3,0,
+0,3,0,
+0,3,0,
+0,3,0,
+0,3,0,
+0,3,0,
+0,3,0,
+0,3,0,
+0,3,0,
+0,3,0,
+0,3,0,
+0,3,0,
+0,3,0,
+0,3,0,
+0,3,0,
+0,3,0,
+0,3,0,
+3,255,255,
+3,255,255,
+ 50 /* 2 */, 4, 0, 14, 1, 0, 0, 0, 0, 0, 31, 0, 24, 0, 31, 0, 96,
+0,15,128,
+0,127,240,
+0,240,120,
+1,192,28,
+3,128,14,
+3,0,6,
+7,0,3,
+6,0,3,
+0,0,3,
+0,0,3,
+0,0,3,
+0,0,6,
+0,0,14,
+0,0,28,
+0,0,56,
+0,0,112,
+0,0,224,
+0,1,192,
+0,3,128,
+0,7,0,
+0,14,0,
+0,28,0,
+0,56,0,
+0,112,0,
+0,224,0,
+1,192,0,
+3,128,3,
+7,0,3,
+12,0,3,
+15,255,255,
+15,255,255,
+ 51 /* 3 */, 4, 0, 14, 1, 0, 0, 0, 0, 0, 31, 0, 25, 0, 32, 0, 100,
+0,15,192,0,
+0,127,240,0,
+0,240,60,0,
+1,192,14,0,
+3,128,6,0,
+1,0,7,0,
+0,0,3,0,
+0,0,3,0,
+0,0,3,0,
+0,0,3,0,
+0,0,6,0,
+0,0,14,0,
+0,0,28,0,
+0,7,248,0,
+0,7,224,0,
+0,1,240,0,
+0,0,28,0,
+0,0,14,0,
+0,0,7,0,
+0,0,3,0,
+0,0,1,128,
+0,0,1,128,
+0,0,1,128,
+0,0,1,128,
+0,0,1,128,
+0,0,3,0,
+0,0,3,0,
+6,0,6,0,
+7,128,14,0,
+1,224,124,0,
+0,127,240,0,
+0,31,192,0,
+ 52 /* 4 */, 4, 0, 14, 1, 0, 0, 0, 0, 0, 30, 0, 24, 0, 30, 0, 96,
+0,0,248,
+0,0,248,
+0,1,152,
+0,1,152,
+0,3,24,
+0,7,24,
+0,6,24,
+0,12,24,
+0,12,24,
+0,24,24,
+0,56,24,
+0,48,24,
+0,112,24,
+0,96,24,
+0,192,24,
+1,192,24,
+1,128,24,
+3,128,24,
+3,0,24,
+6,0,24,
+7,255,255,
+7,255,255,
+0,0,24,
+0,0,24,
+0,0,24,
+0,0,24,
+0,0,24,
+0,0,24,
+0,3,255,
+0,3,255,
+ 53 /* 5 */, 4, 0, 14, 1, 0, 0, 0, 0, 0, 30, 0, 25, 0, 31, 0, 100,
+1,255,254,0,
+1,255,254,0,
+1,128,0,0,
+1,128,0,0,
+1,128,0,0,
+1,128,0,0,
+1,128,0,0,
+1,128,0,0,
+1,128,0,0,
+1,128,0,0,
+1,135,224,0,
+1,191,248,0,
+1,248,60,0,
+1,192,14,0,
+0,0,7,0,
+0,0,3,0,
+0,0,3,0,
+0,0,1,128,
+0,0,1,128,
+0,0,1,128,
+0,0,1,128,
+0,0,1,128,
+0,0,1,128,
+0,0,3,128,
+0,0,3,0,
+6,0,7,0,
+7,0,6,0,
+3,192,28,0,
+1,240,56,0,
+0,127,240,0,
+0,15,192,0,
+ 54 /* 6 */, 4, 0, 14, 1, 0, 0, 0, 0, 0, 31, 0, 25, 0, 32, 0, 100,
+0,0,63,0,
+0,1,255,128,
+0,3,193,128,
+0,15,0,0,
+0,28,0,0,
+0,56,0,0,
+0,48,0,0,
+0,112,0,0,
+0,96,0,0,
+0,192,0,0,
+0,192,0,0,
+0,192,0,0,
+1,128,0,0,
+1,131,240,0,
+1,143,252,0,
+1,158,30,0,
+1,184,6,0,
+1,176,3,0,
+1,224,3,0,
+1,192,1,128,
+1,192,1,128,
+1,128,1,128,
+0,128,1,128,
+0,192,1,128,
+0,192,1,128,
+0,96,3,128,
+0,96,3,0,
+0,48,7,0,
+0,56,6,0,
+0,28,28,0,
+0,15,248,0,
+0,3,240,0,
+ 55 /* 7 */, 4, 0, 14, 1, 0, 0, 0, 0, 0, 30, 0, 24, 0, 30, 0, 96,
+7,255,255,
+7,255,255,
+6,0,3,
+6,0,3,
+6,0,7,
+0,0,6,
+0,0,6,
+0,0,14,
+0,0,12,
+0,0,12,
+0,0,12,
+0,0,24,
+0,0,24,
+0,0,24,
+0,0,48,
+0,0,48,
+0,0,48,
+0,0,96,
+0,0,96,
+0,0,96,
+0,0,192,
+0,0,192,
+0,0,192,
+0,1,128,
+0,1,128,
+0,1,128,
+0,3,128,
+0,3,0,
+0,3,0,
+0,3,0,
+ 56 /* 8 */, 4, 0, 14, 1, 0, 0, 0, 0, 0, 31, 0, 24, 0, 32, 0, 96,
+0,15,192,
+0,63,240,
+0,112,56,
+0,192,12,
+1,128,6,
+1,128,6,
+1,0,2,
+3,0,3,
+3,0,3,
+3,0,3,
+1,0,2,
+1,128,6,
+0,192,12,
+0,240,56,
+0,63,240,
+0,31,224,
+0,112,56,
+0,224,28,
+1,192,14,
+1,128,6,
+1,0,2,
+3,0,3,
+3,0,3,
+3,0,3,
+3,0,3,
+1,0,2,
+1,128,6,
+1,192,14,
+0,224,28,
+0,112,56,
+0,63,240,
+0,15,192,
+ 57 /* 9 */, 4, 0, 14, 1, 0, 0, 0, 0, 0, 31, 0, 26, 0, 32, 0, 104,
+0,7,192,0,
+0,31,240,0,
+0,56,56,0,
+0,112,12,0,
+0,224,6,0,
+0,192,7,0,
+1,192,3,0,
+1,128,3,128,
+1,128,1,128,
+1,128,1,128,
+1,128,1,192,
+1,128,3,192,
+1,128,3,192,
+0,192,6,192,
+0,224,14,192,
+0,96,28,192,
+0,56,120,192,
+0,31,240,192,
+0,15,192,192,
+0,0,1,128,
+0,0,1,128,
+0,0,3,128,
+0,0,3,0,
+0,0,7,0,
+0,0,6,0,
+0,0,14,0,
+0,0,28,0,
+0,0,56,0,
+0,0,240,0,
+1,131,192,0,
+1,255,128,0,
+0,124,0,0,
+ 58 /* : */, 4, 0, 14, 1, 0, 0, 0, 0, 0, 21, 0, 19, 0, 22, 0, 76,
+0,7,128,
+0,15,192,
+0,31,224,
+0,31,224,
+0,31,224,
+0,15,192,
+0,7,128,
+0,0,0,
+0,0,0,
+0,0,0,
+0,0,0,
+0,0,0,
+0,0,0,
+0,0,0,
+0,0,0,
+0,7,128,
+0,15,192,
+0,31,224,
+0,31,224,
+0,31,224,
+0,15,192,
+0,7,128,
+ 59 /* ; */, 4, 0, 14, 1, 0, 0, 0, 0, 0, 21, 0, 17, 0, 28, 0, 68,
+0,15,0,
+0,31,128,
+0,63,128,
+0,63,128,
+0,63,128,
+0,31,128,
+0,14,0,
+0,0,0,
+0,0,0,
+0,0,0,
+0,0,0,
+0,0,0,
+0,0,0,
+0,0,0,
+0,63,128,
+0,63,0,
+0,63,0,
+0,62,0,
+0,126,0,
+0,124,0,
+0,124,0,
+0,248,0,
+0,240,0,
+0,240,0,
+0,224,0,
+1,224,0,
+1,192,0,
+1,192,0,
+ 63 /* ? */, 4, 0, 14, 1, 0, 0, 0, 0, 0, 29, 0, 24, 0, 30, 0, 96,
+0,15,224,
+0,127,248,
+1,240,60,
+1,192,14,
+1,128,6,
+1,128,3,
+1,128,3,
+0,0,3,
+0,0,3,
+0,0,3,
+0,0,7,
+0,0,14,
+0,0,28,
+0,0,56,
+0,0,240,
+0,3,192,
+0,3,0,
+0,3,0,
+0,3,0,
+0,3,0,
+0,0,0,
+0,0,0,
+0,0,0,
+0,0,0,
+0,0,0,
+0,15,192,
+0,15,192,
+0,15,224,
+0,15,192,
+0,7,128,
+ 65 /* A */, 4, 0, 14, 1, 0, 0, 0, 0, 0, 28, 0, 29, 0, 28, 0, 116,
+7,255,128,0,
+7,255,128,0,
+0,13,128,0,
+0,12,192,0,
+0,24,192,0,
+0,24,224,0,
+0,24,96,0,
+0,48,96,0,
+0,48,112,0,
+0,48,48,0,
+0,96,48,0,
+0,96,24,0,
+0,224,24,0,
+0,192,28,0,
+0,192,12,0,
+1,128,12,0,
+1,128,14,0,
+1,255,254,0,
+3,255,255,0,
+3,0,3,0,
+7,0,3,0,
+6,0,3,128,
+6,0,1,128,
+12,0,1,128,
+12,0,0,192,
+12,0,0,192,
+127,192,31,248,
+127,224,31,248,
+ 66 /* B */, 4, 0, 14, 1, 0, 0, 0, 0, 0, 28, 0, 27, 0, 28, 0, 108,
+63,255,240,0,
+63,255,252,0,
+3,0,30,0,
+3,0,7,0,
+3,0,3,0,
+3,0,1,128,
+3,0,1,128,
+3,0,1,128,
+3,0,1,128,
+3,0,3,0,
+3,0,7,0,
+3,0,30,0,
+3,255,252,0,
+3,255,252,0,
+3,0,31,0,
+3,0,3,128,
+3,0,1,192,
+3,0,0,192,
+3,0,0,96,
+3,0,0,96,
+3,0,0,96,
+3,0,0,96,
+3,0,0,96,
+3,0,0,192,
+3,0,1,192,
+3,0,7,128,
+63,255,255,0,
+63,255,252,0,
+ 67 /* C */, 4, 0, 14, 1, 0, 0, 0, 0, 0, 29, 0, 27, 0, 30, 0, 108,
+0,15,224,0,
+0,63,248,192,
+0,240,62,192,
+1,192,7,192,
+3,128,3,192,
+7,0,1,192,
+6,0,0,192,
+12,0,0,192,
+12,0,0,192,
+12,0,0,0,
+24,0,0,0,
+24,0,0,0,
+24,0,0,0,
+24,0,0,0,
+24,0,0,0,
+24,0,0,0,
+24,0,0,0,
+24,0,0,0,
+24,0,0,0,
+24,0,0,0,
+12,0,0,0,
+12,0,0,0,
+14,0,0,0,
+6,0,0,96,
+3,0,0,192,
+1,128,1,192,
+0,224,7,128,
+0,120,30,0,
+0,63,252,0,
+0,7,224,0,
+ 68 /* D */, 4, 0, 14, 1, 0, 0, 0, 0, 0, 28, 0, 26, 0, 28, 0, 104,
+63,255,192,0,
+63,255,240,0,
+6,0,56,0,
+6,0,14,0,
+6,0,6,0,
+6,0,3,0,
+6,0,3,128,
+6,0,1,128,
+6,0,1,128,
+6,0,0,192,
+6,0,0,192,
+6,0,0,192,
+6,0,0,192,
+6,0,0,192,
+6,0,0,192,
+6,0,0,192,
+6,0,0,192,
+6,0,0,192,
+6,0,0,192,
+6,0,1,128,
+6,0,1,128,
+6,0,3,128,
+6,0,7,0,
+6,0,6,0,
+6,0,28,0,
+6,0,120,0,
+63,255,240,0,
+63,255,192,0,
+ 69 /* E */, 4, 0, 14, 1, 0, 0, 0, 0, 0, 28, 0, 26, 0, 28, 0, 104,
+63,255,255,128,
+63,255,255,128,
+3,0,1,128,
+3,0,1,128,
+3,0,1,128,
+3,0,1,128,
+3,0,1,128,
+3,0,1,128,
+3,0,0,0,
+3,0,192,0,
+3,0,192,0,
+3,0,192,0,
+3,255,192,0,
+3,255,192,0,
+3,0,192,0,
+3,0,192,0,
+3,0,192,0,
+3,0,192,0,
+3,0,0,0,
+3,0,0,192,
+3,0,0,192,
+3,0,0,192,
+3,0,0,192,
+3,0,0,192,
+3,0,0,192,
+3,0,0,192,
+63,255,255,192,
+63,255,255,192,
+ 70 /* F */, 4, 0, 14, 1, 0, 0, 0, 0, 0, 28, 0, 26, 0, 28, 0, 104,
+63,255,255,192,
+63,255,255,192,
+3,0,0,192,
+3,0,0,192,
+3,0,0,192,
+3,0,0,192,
+3,0,0,192,
+3,0,0,192,
+3,0,0,0,
+3,0,192,0,
+3,0,192,0,
+3,0,192,0,
+3,255,192,0,
+3,255,192,0,
+3,0,192,0,
+3,0,192,0,
+3,0,192,0,
+3,0,192,0,
+3,0,0,0,
+3,0,0,0,
+3,0,0,0,
+3,0,0,0,
+3,0,0,0,
+3,0,0,0,
+3,0,0,0,
+3,0,0,0,
+63,255,128,0,
+63,255,128,0,
+ 71 /* G */, 4, 0, 14, 1, 0, 0, 0, 0, 0, 29, 0, 28, 0, 30, 0, 112,
+0,15,224,0,
+0,63,252,192,
+0,248,30,192,
+1,224,3,192,
+3,128,1,192,
+7,0,0,192,
+6,0,0,192,
+14,0,0,192,
+12,0,0,0,
+12,0,0,0,
+24,0,0,0,
+24,0,0,0,
+24,0,0,0,
+24,0,0,0,
+24,0,0,0,
+24,0,0,0,
+24,0,0,0,
+24,0,255,240,
+24,0,255,240,
+24,0,0,192,
+28,0,0,192,
+12,0,0,192,
+12,0,0,192,
+6,0,0,192,
+7,0,0,192,
+3,128,0,192,
+1,192,1,192,
+0,248,15,128,
+0,63,254,0,
+0,7,240,0,
+ 72 /* H */, 4, 0, 14, 1, 0, 0, 0, 0, 0, 28, 0, 27, 0, 28, 0, 108,
+15,240,63,192,
+15,240,63,192,
+3,0,3,0,
+3,0,3,0,
+3,0,3,0,
+3,0,3,0,
+3,0,3,0,
+3,0,3,0,
+3,0,3,0,
+3,0,3,0,
+3,0,3,0,
+3,0,3,0,
+3,255,255,0,
+3,255,255,0,
+3,0,3,0,
+3,0,3,0,
+3,0,3,0,
+3,0,3,0,
+3,0,3,0,
+3,0,3,0,
+3,0,3,0,
+3,0,3,0,
+3,0,3,0,
+3,0,3,0,
+3,0,3,0,
+3,0,3,0,
+31,240,63,224,
+31,240,63,224,
+ 73 /* I */, 4, 0, 14, 1, 0, 0, 0, 0, 0, 28, 0, 24, 0, 28, 0, 96,
+3,255,255,
+3,255,255,
+0,3,0,
+0,3,0,
+0,3,0,
+0,3,0,
+0,3,0,
+0,3,0,
+0,3,0,
+0,3,0,
+0,3,0,
+0,3,0,
+0,3,0,
+0,3,0,
+0,3,0,
+0,3,0,
+0,3,0,
+0,3,0,
+0,3,0,
+0,3,0,
+0,3,0,
+0,3,0,
+0,3,0,
+0,3,0,
+0,3,0,
+0,3,0,
+3,255,255,
+3,255,255,
+ 74 /* J */, 4, 0, 14, 1, 0, 0, 0, 0, 0, 28, 0, 29, 0, 29, 0, 116,
+0,15,255,248,
+0,15,255,248,
+0,0,6,0,
+0,0,6,0,
+0,0,6,0,
+0,0,6,0,
+0,0,6,0,
+0,0,6,0,
+0,0,6,0,
+0,0,6,0,
+0,0,6,0,
+0,0,6,0,
+0,0,6,0,
+0,0,6,0,
+0,0,6,0,
+0,0,6,0,
+12,0,6,0,
+12,0,6,0,
+12,0,6,0,
+12,0,6,0,
+12,0,6,0,
+12,0,6,0,
+12,0,14,0,
+12,0,12,0,
+14,0,28,0,
+7,128,56,0,
+1,224,240,0,
+0,255,224,0,
+0,63,128,0,
+ 75 /* K */, 4, 0, 14, 1, 0, 0, 0, 0, 0, 28, 0, 28, 0, 28, 0, 112,
+63,248,31,224,
+63,248,31,224,
+3,0,3,128,
+3,0,6,0,
+3,0,28,0,
+3,0,56,0,
+3,0,112,0,
+3,0,224,0,
+3,1,192,0,
+3,3,128,0,
+3,14,0,0,
+3,28,0,0,
+3,63,0,0,
+3,119,128,0,
+3,225,192,0,
+3,192,224,0,
+3,128,112,0,
+3,0,56,0,
+3,0,24,0,
+3,0,28,0,
+3,0,12,0,
+3,0,14,0,
+3,0,6,0,
+3,0,6,0,
+3,0,3,0,
+3,0,3,0,
+63,248,3,240,
+63,248,1,240,
+ 76 /* L */, 4, 0, 14, 1, 0, 0, 0, 0, 0, 28, 0, 27, 0, 28, 0, 108,
+31,255,128,0,
+31,255,128,0,
+0,96,0,0,
+0,96,0,0,
+0,96,0,0,
+0,96,0,0,
+0,96,0,0,
+0,96,0,0,
+0,96,0,0,
+0,96,0,0,
+0,96,0,0,
+0,96,0,0,
+0,96,0,0,
+0,96,0,0,
+0,96,0,0,
+0,96,0,0,
+0,96,0,0,
+0,96,0,96,
+0,96,0,96,
+0,96,0,96,
+0,96,0,96,
+0,96,0,96,
+0,96,0,96,
+0,96,0,96,
+0,96,0,96,
+0,96,0,96,
+31,255,255,224,
+31,255,255,224,
+ 77 /* M */, 4, 0, 14, 1, 0, 0, 0, 0, 0, 28, 0, 29, 0, 28, 0, 116,
+127,0,1,248,
+127,0,3,248,
+15,0,3,96,
+13,128,6,96,
+13,128,6,96,
+12,192,14,96,
+12,192,12,96,
+12,96,12,96,
+12,96,24,96,
+12,112,24,96,
+12,48,48,96,
+12,56,48,96,
+12,24,96,96,
+12,24,96,96,
+12,12,224,96,
+12,12,192,96,
+12,7,192,96,
+12,7,128,96,
+12,3,128,96,
+12,3,0,96,
+12,0,0,96,
+12,0,0,96,
+12,0,0,96,
+12,0,0,96,
+12,0,0,96,
+12,0,0,96,
+127,224,15,248,
+127,224,15,248,
+ 78 /* N */, 4, 0, 14, 1, 0, 0, 0, 0, 0, 28, 0, 28, 0, 28, 0, 112,
+127,0,63,240,
+127,128,63,240,
+7,128,1,128,
+7,192,1,128,
+6,192,1,128,
+6,96,1,128,
+6,112,1,128,
+6,48,1,128,
+6,56,1,128,
+6,28,1,128,
+6,12,1,128,
+6,14,1,128,
+6,6,1,128,
+6,3,1,128,
+6,3,129,128,
+6,1,129,128,
+6,1,193,128,
+6,0,193,128,
+6,0,97,128,
+6,0,113,128,
+6,0,49,128,
+6,0,57,128,
+6,0,25,128,
+6,0,13,128,
+6,0,15,128,
+6,0,7,128,
+63,240,3,128,
+63,240,3,128,
+ 79 /* O */, 4, 0, 14, 1, 0, 0, 0, 0, 0, 29, 0, 27, 0, 30, 0, 108,
+0,15,192,0,
+0,63,240,0,
+0,240,60,0,
+1,192,14,0,
+3,128,7,0,
+3,0,3,0,
+6,0,1,128,
+6,0,1,128,
+12,0,0,192,
+12,0,0,192,
+12,0,0,192,
+24,0,0,96,
+24,0,0,96,
+24,0,0,96,
+24,0,0,96,
+24,0,0,96,
+24,0,0,96,
+24,0,0,96,
+24,0,0,96,
+12,0,0,192,
+12,0,0,192,
+12,0,0,192,
+6,0,1,128,
+6,0,1,128,
+3,0,3,0,
+3,128,7,0,
+1,192,14,0,
+0,240,60,0,
+0,63,240,0,
+0,15,192,0,
+ 80 /* P */, 4, 0, 14, 1, 0, 0, 0, 0, 0, 28, 0, 25, 0, 28, 0, 100,
+63,255,224,0,
+63,255,248,0,
+3,0,28,0,
+3,0,14,0,
+3,0,3,0,
+3,0,3,128,
+3,0,1,128,
+3,0,1,128,
+3,0,1,128,
+3,0,1,128,
+3,0,3,128,
+3,0,3,0,
+3,0,14,0,
+3,0,60,0,
+3,255,248,0,
+3,255,224,0,
+3,0,0,0,
+3,0,0,0,
+3,0,0,0,
+3,0,0,0,
+3,0,0,0,
+3,0,0,0,
+3,0,0,0,
+3,0,0,0,
+3,0,0,0,
+3,0,0,0,
+63,255,0,0,
+63,255,0,0,
+ 81 /* Q */, 4, 0, 14, 1, 0, 0, 0, 0, 0, 29, 0, 27, 0, 35, 0, 108,
+0,15,192,0,
+0,63,240,0,
+0,240,60,0,
+1,192,14,0,
+3,128,7,0,
+3,0,3,0,
+6,0,1,128,
+6,0,1,128,
+12,0,0,192,
+12,0,0,192,
+12,0,0,192,
+24,0,0,96,
+24,0,0,96,
+24,0,0,96,
+24,0,0,96,
+24,0,0,96,
+24,0,0,96,
+24,0,0,96,
+24,0,0,96,
+12,0,0,192,
+12,0,0,192,
+12,0,0,192,
+6,0,1,128,
+6,0,1,128,
+3,0,3,0,
+3,128,7,0,
+1,224,30,0,
+0,248,124,0,
+0,63,240,0,
+0,15,128,0,
+0,28,0,0,
+0,63,192,96,
+0,255,248,224,
+1,248,63,192,
+0,128,15,0,
+ 82 /* R */, 4, 0, 14, 1, 0, 0, 0, 0, 0, 28, 0, 29, 0, 28, 0, 116,
+63,255,224,0,
+63,255,248,0,
+3,0,30,0,
+3,0,6,0,
+3,0,3,0,
+3,0,3,128,
+3,0,1,128,
+3,0,1,128,
+3,0,1,128,
+3,0,3,128,
+3,0,3,0,
+3,0,15,0,
+3,0,62,0,
+3,255,248,0,
+3,255,224,0,
+3,0,224,0,
+3,0,112,0,
+3,0,56,0,
+3,0,28,0,
+3,0,14,0,
+3,0,6,0,
+3,0,7,0,
+3,0,3,0,
+3,0,1,128,
+3,0,1,192,
+3,0,0,192,
+63,248,0,248,
+63,248,0,120,
+ 83 /* S */, 4, 0, 14, 1, 0, 0, 0, 0, 0, 29, 0, 25, 0, 30, 0, 100,
+0,15,192,0,
+0,63,243,0,
+0,240,59,0,
+1,192,15,0,
+1,128,7,0,
+3,128,7,0,
+3,0,3,0,
+3,0,3,0,
+3,0,1,0,
+3,0,0,0,
+3,128,0,0,
+1,192,0,0,
+0,224,0,0,
+0,126,0,0,
+0,31,240,0,
+0,1,252,0,
+0,0,14,0,
+0,0,7,0,
+0,0,3,128,
+0,0,1,128,
+0,0,1,128,
+6,0,1,128,
+6,0,1,128,
+6,0,1,128,
+7,0,3,0,
+7,128,7,0,
+7,192,14,0,
+6,240,60,0,
+6,63,248,0,
+0,15,224,0,
+ 84 /* T */, 4, 0, 14, 1, 0, 0, 0, 0, 0, 28, 0, 26, 0, 28, 0, 104,
+15,255,255,192,
+15,255,255,192,
+12,3,0,192,
+12,3,0,192,
+12,3,0,192,
+12,3,0,192,
+12,3,0,192,
+0,3,0,0,
+0,3,0,0,
+0,3,0,0,
+0,3,0,0,
+0,3,0,0,
+0,3,0,0,
+0,3,0,0,
+0,3,0,0,
+0,3,0,0,
+0,3,0,0,
+0,3,0,0,
+0,3,0,0,
+0,3,0,0,
+0,3,0,0,
+0,3,0,0,
+0,3,0,0,
+0,3,0,0,
+0,3,0,0,
+0,3,0,0,
+0,255,252,0,
+0,255,252,0,
+ 85 /* U */, 4, 0, 14, 1, 0, 0, 0, 0, 0, 28, 0, 28, 0, 29, 0, 112,
+63,240,63,240,
+63,240,63,240,
+6,0,1,128,
+6,0,1,128,
+6,0,1,128,
+6,0,1,128,
+6,0,1,128,
+6,0,1,128,
+6,0,1,128,
+6,0,1,128,
+6,0,1,128,
+6,0,1,128,
+6,0,1,128,
+6,0,1,128,
+6,0,1,128,
+6,0,1,128,
+6,0,1,128,
+6,0,1,128,
+6,0,1,128,
+6,0,1,128,
+6,0,1,128,
+6,0,1,128,
+3,0,3,0,
+3,0,3,0,
+1,128,6,0,
+1,192,14,0,
+0,240,60,0,
+0,63,240,0,
+0,15,192,0,
+ 86 /* V */, 4, 0, 14, 1, 0, 0, 0, 0, 0, 28, 0, 29, 0, 28, 0, 116,
+127,224,31,248,
+127,192,15,248,
+12,0,0,192,
+12,0,0,192,
+12,0,0,192,
+6,0,1,128,
+6,0,1,128,
+7,0,3,0,
+3,0,3,0,
+3,0,3,0,
+1,128,6,0,
+1,128,6,0,
+1,192,12,0,
+0,192,12,0,
+0,192,12,0,
+0,96,24,0,
+0,96,24,0,
+0,112,48,0,
+0,48,48,0,
+0,48,48,0,
+0,56,96,0,
+0,24,96,0,
+0,24,192,0,
+0,12,192,0,
+0,12,192,0,
+0,15,128,0,
+0,7,128,0,
+0,7,0,0,
+ 87 /* W */, 4, 0, 14, 1, 0, 0, 0, 0, 0, 28, 0, 29, 0, 28, 0, 116,
+127,224,31,248,
+127,224,31,248,
+24,0,0,96,
+24,0,0,96,
+24,0,0,96,
+12,0,0,64,
+12,7,128,192,
+12,7,128,192,
+12,7,128,192,
+12,7,192,192,
+12,12,192,192,
+12,12,192,192,
+12,12,192,192,
+14,24,96,192,
+6,24,97,128,
+6,24,97,128,
+6,24,113,128,
+6,48,49,128,
+6,48,49,128,
+6,48,49,128,
+6,96,25,128,
+6,96,25,128,
+7,96,27,0,
+3,96,27,0,
+3,192,15,0,
+3,192,15,0,
+3,192,15,0,
+3,128,15,0,
+ 88 /* X */, 4, 0, 14, 1, 0, 0, 0, 0, 0, 28, 0, 28, 0, 28, 0, 112,
+31,224,31,224,
+31,224,31,224,
+7,0,3,128,
+3,0,7,0,
+1,128,6,0,
+1,192,12,0,
+0,224,28,0,
+0,112,56,0,
+0,48,48,0,
+0,56,96,0,
+0,28,224,0,
+0,15,192,0,
+0,7,128,0,
+0,3,0,0,
+0,7,128,0,
+0,15,192,0,
+0,28,192,0,
+0,24,96,0,
+0,48,112,0,
+0,112,56,0,
+0,224,28,0,
+0,192,12,0,
+1,128,14,0,
+3,128,7,0,
+7,0,3,128,
+6,0,1,128,
+63,224,31,240,
+63,224,31,240,
+ 89 /* Y */, 4, 0, 14, 1, 0, 0, 0, 0, 0, 28, 0, 27, 0, 28, 0, 108,
+31,224,31,224,
+31,224,31,224,
+7,0,3,128,
+3,128,3,0,
+1,128,6,0,
+1,192,14,0,
+0,224,12,0,
+0,96,24,0,
+0,112,56,0,
+0,56,48,0,
+0,24,96,0,
+0,28,224,0,
+0,14,192,0,
+0,7,128,0,
+0,7,128,0,
+0,3,0,0,
+0,3,0,0,
+0,3,0,0,
+0,3,0,0,
+0,3,0,0,
+0,3,0,0,
+0,3,0,0,
+0,3,0,0,
+0,3,0,0,
+0,3,0,0,
+0,3,0,0,
+0,255,252,0,
+0,255,254,0,
+ 90 /* Z */, 4, 0, 14, 1, 0, 0, 0, 0, 0, 28, 0, 25, 0, 28, 0, 100,
+3,255,255,0,
+3,255,255,0,
+3,0,7,0,
+3,0,6,0,
+3,0,12,0,
+3,0,28,0,
+3,0,56,0,
+3,0,48,0,
+3,0,96,0,
+0,0,224,0,
+0,0,192,0,
+0,1,128,0,
+0,3,128,0,
+0,7,0,0,
+0,6,0,0,
+0,12,0,0,
+0,28,0,0,
+0,24,0,0,
+0,48,1,128,
+0,112,1,128,
+0,224,1,128,
+0,192,1,128,
+1,128,1,128,
+3,128,1,128,
+3,0,1,128,
+6,0,1,128,
+7,255,255,128,
+7,255,255,128,
+ 95 /* _ */, 4, 0, 14, 1, 0, 0, 0, 0, 0, 252, 0, 31, 0, 2, 0, 124,
+255,255,255,254,
+255,255,255,254,
+ 97 /* a */, 4, 0, 14, 1, 0, 0, 0, 0, 0, 22, 0, 27, 0, 23, 0, 108,
+0,15,192,0,
+0,255,240,0,
+3,248,120,0,
+3,192,28,0,
+0,0,14,0,
+0,0,6,0,
+0,0,6,0,
+0,0,6,0,
+0,0,6,0,
+0,63,230,0,
+1,255,254,0,
+3,224,30,0,
+7,128,6,0,
+14,0,6,0,
+14,0,6,0,
+12,0,6,0,
+12,0,6,0,
+12,0,14,0,
+14,0,62,0,
+14,0,118,0,
+7,131,231,224,
+3,255,135,224,
+0,254,0,0,
+ 98 /* b */, 4, 0, 14, 1, 0, 0, 0, 0, 0, 30, 0, 27, 0, 31, 0, 108,
+126,0,0,0,
+126,0,0,0,
+6,0,0,0,
+6,0,0,0,
+6,0,0,0,
+6,0,0,0,
+6,0,0,0,
+6,0,0,0,
+6,7,224,0,
+6,31,252,0,
+6,56,30,0,
+6,96,7,0,
+6,192,3,128,
+7,128,1,128,
+7,0,0,192,
+7,0,0,192,
+7,0,0,224,
+6,0,0,96,
+6,0,0,96,
+6,0,0,96,
+6,0,0,96,
+6,0,0,96,
+6,0,0,96,
+7,0,0,192,
+7,0,0,192,
+7,128,1,128,
+6,192,3,128,
+6,96,7,0,
+126,56,30,0,
+126,31,252,0,
+0,7,224,0,
+ 99 /* c */, 4, 0, 14, 1, 0, 0, 0, 0, 0, 22, 0, 27, 0, 23, 0, 108,
+0,15,224,0,
+0,63,252,192,
+0,248,62,192,
+1,224,7,192,
+3,128,3,192,
+3,0,1,192,
+7,0,0,192,
+6,0,0,192,
+14,0,0,128,
+12,0,0,0,
+12,0,0,0,
+12,0,0,0,
+12,0,0,0,
+12,0,0,0,
+14,0,0,0,
+14,0,0,0,
+6,0,0,0,
+7,0,0,224,
+3,128,1,224,
+1,192,7,192,
+0,248,31,0,
+0,127,252,0,
+0,31,240,0,
+ 100 /* d */, 4, 0, 14, 1, 0, 0, 0, 0, 0, 30, 0, 29, 0, 31, 0, 116,
+0,0,31,128,
+0,0,31,128,
+0,0,1,128,
+0,0,1,128,
+0,0,1,128,
+0,0,1,128,
+0,0,1,128,
+0,0,1,128,
+0,31,129,128,
+0,127,225,128,
+1,224,113,128,
+3,128,25,128,
+7,0,13,128,
+6,0,7,128,
+12,0,3,128,
+12,0,3,128,
+28,0,1,128,
+24,0,1,128,
+24,0,1,128,
+24,0,1,128,
+24,0,1,128,
+24,0,1,128,
+24,0,1,128,
+12,0,3,128,
+12,0,3,128,
+6,0,7,128,
+7,0,13,128,
+3,128,25,128,
+1,224,113,248,
+0,127,225,248,
+0,31,128,0,
+ 101 /* e */, 4, 0, 14, 1, 0, 0, 0, 0, 0, 22, 0, 26, 0, 23, 0, 104,
+0,31,192,0,
+0,127,240,0,
+1,240,124,0,
+3,192,30,0,
+7,0,7,0,
+6,0,3,0,
+14,0,3,128,
+12,0,1,128,
+28,0,1,192,
+24,0,0,192,
+31,255,255,192,
+31,255,255,192,
+24,0,0,0,
+28,0,0,0,
+12,0,0,0,
+12,0,0,0,
+14,0,0,0,
+7,0,0,0,
+3,128,1,192,
+3,192,7,192,
+0,240,63,0,
+0,127,252,0,
+0,31,240,0,
+ 102 /* f */, 4, 0, 14, 1, 0, 0, 0, 0, 0, 30, 0, 27, 0, 30, 0, 108,
+0,0,255,128,
+0,3,255,224,
+0,7,0,0,
+0,6,0,0,
+0,12,0,0,
+0,12,0,0,
+0,12,0,0,
+0,12,0,0,
+0,12,0,0,
+3,255,255,0,
+3,255,255,0,
+0,12,0,0,
+0,12,0,0,
+0,12,0,0,
+0,12,0,0,
+0,12,0,0,
+0,12,0,0,
+0,12,0,0,
+0,12,0,0,
+0,12,0,0,
+0,12,0,0,
+0,12,0,0,
+0,12,0,0,
+0,12,0,0,
+0,12,0,0,
+0,12,0,0,
+0,12,0,0,
+0,12,0,0,
+7,255,255,0,
+7,255,255,0,
+ 103 /* g */, 4, 0, 14, 1, 0, 0, 0, 0, 0, 22, 0, 28, 0, 31, 0, 112,
+0,63,0,0,
+0,255,195,240,
+1,224,243,240,
+3,128,59,0,
+7,0,27,0,
+14,0,15,0,
+12,0,7,0,
+12,0,7,0,
+24,0,3,0,
+24,0,3,0,
+24,0,3,0,
+24,0,3,0,
+24,0,3,0,
+24,0,3,0,
+12,0,7,0,
+12,0,7,0,
+6,0,15,0,
+7,0,27,0,
+1,224,115,0,
+0,255,227,0,
+0,63,131,0,
+0,0,3,0,
+0,0,3,0,
+0,0,3,0,
+0,0,3,0,
+0,0,3,0,
+0,0,6,0,
+0,0,14,0,
+0,0,60,0,
+0,127,248,0,
+0,127,224,0,
+ 104 /* h */, 4, 0, 14, 1, 0, 0, 0, 0, 0, 30, 0, 27, 0, 30, 0, 108,
+63,0,0,0,
+63,0,0,0,
+3,0,0,0,
+3,0,0,0,
+3,0,0,0,
+3,0,0,0,
+3,0,0,0,
+3,0,0,0,
+3,7,224,0,
+3,31,248,0,
+3,56,60,0,
+3,112,14,0,
+3,224,6,0,
+3,192,7,0,
+3,128,3,0,
+3,0,3,0,
+3,0,3,0,
+3,0,3,0,
+3,0,3,0,
+3,0,3,0,
+3,0,3,0,
+3,0,3,0,
+3,0,3,0,
+3,0,3,0,
+3,0,3,0,
+3,0,3,0,
+3,0,3,0,
+3,0,3,0,
+31,240,63,224,
+31,240,63,224,
+ 105 /* i */, 4, 0, 14, 1, 0, 0, 0, 0, 0, 31, 0, 25, 0, 31, 0, 100,
+0,6,0,0,
+0,6,0,0,
+0,6,0,0,
+0,6,0,0,
+0,6,0,0,
+0,0,0,0,
+0,0,0,0,
+0,0,0,0,
+0,0,0,0,
+0,0,0,0,
+1,255,0,0,
+1,255,0,0,
+0,3,0,0,
+0,3,0,0,
+0,3,0,0,
+0,3,0,0,
+0,3,0,0,
+0,3,0,0,
+0,3,0,0,
+0,3,0,0,
+0,3,0,0,
+0,3,0,0,
+0,3,0,0,
+0,3,0,0,
+0,3,0,0,
+0,3,0,0,
+0,3,0,0,
+0,3,0,0,
+0,3,0,0,
+7,255,255,128,
+7,255,255,128,
+ 106 /* j */, 4, 0, 14, 1, 0, 0, 0, 0, 0, 31, 0, 23, 0, 40, 0, 92,
+0,0,48,
+0,0,48,
+0,0,48,
+0,0,48,
+0,0,48,
+0,0,0,
+0,0,0,
+0,0,0,
+0,0,0,
+0,0,0,
+0,255,254,
+0,255,254,
+0,0,6,
+0,0,6,
+0,0,6,
+0,0,6,
+0,0,6,
+0,0,6,
+0,0,6,
+0,0,6,
+0,0,6,
+0,0,6,
+0,0,6,
+0,0,6,
+0,0,6,
+0,0,6,
+0,0,6,
+0,0,6,
+0,0,6,
+0,0,6,
+0,0,6,
+0,0,6,
+0,0,6,
+0,0,6,
+0,0,6,
+0,0,12,
+0,0,28,
+0,0,56,
+0,255,240,
+1,255,192,
+ 107 /* k */, 4, 0, 14, 1, 0, 0, 0, 0, 0, 30, 0, 27, 0, 30, 0, 108,
+31,128,0,0,
+31,128,0,0,
+1,128,0,0,
+1,128,0,0,
+1,128,0,0,
+1,128,0,0,
+1,128,0,0,
+1,128,0,0,
+1,128,0,0,
+1,128,255,0,
+1,128,255,128,
+1,128,112,0,
+1,128,224,0,
+1,129,192,0,
+1,131,128,0,
+1,135,0,0,
+1,158,0,0,
+1,184,0,0,
+1,252,0,0,
+1,238,0,0,
+1,199,0,0,
+1,131,128,0,
+1,129,192,0,
+1,128,224,0,
+1,128,112,0,
+1,128,56,0,
+1,128,28,0,
+1,128,14,0,
+31,128,63,224,
+31,128,63,224,
+ 108 /* l */, 4, 0, 14, 1, 0, 0, 0, 0, 0, 30, 0, 25, 0, 30, 0, 100,
+1,255,0,0,
+1,255,0,0,
+0,3,0,0,
+0,3,0,0,
+0,3,0,0,
+0,3,0,0,
+0,3,0,0,
+0,3,0,0,
+0,3,0,0,
+0,3,0,0,
+0,3,0,0,
+0,3,0,0,
+0,3,0,0,
+0,3,0,0,
+0,3,0,0,
+0,3,0,0,
+0,3,0,0,
+0,3,0,0,
+0,3,0,0,
+0,3,0,0,
+0,3,0,0,
+0,3,0,0,
+0,3,0,0,
+0,3,0,0,
+0,3,0,0,
+0,3,0,0,
+0,3,0,0,
+0,3,0,0,
+7,255,255,128,
+7,255,255,128,
+ 109 /* m */, 4, 0, 14, 1, 0, 0, 0, 0, 0, 22, 0, 30, 0, 22, 0, 120,
+0,48,15,0,
+124,252,63,128,
+125,198,113,192,
+15,3,224,224,
+15,1,192,96,
+14,1,192,96,
+12,1,128,96,
+12,1,128,96,
+12,1,128,96,
+12,1,128,96,
+12,1,128,96,
+12,1,128,96,
+12,1,128,96,
+12,1,128,96,
+12,1,128,96,
+12,1,128,96,
+12,1,128,96,
+12,1,128,96,
+12,1,128,96,
+12,1,128,96,
+127,1,224,124,
+127,1,224,124,
+ 110 /* n */, 4, 0, 14, 1, 0, 0, 0, 0, 0, 22, 0, 27, 0, 22, 0, 108,
+0,1,128,0,
+31,15,240,0,
+31,60,60,0,
+3,112,14,0,
+3,224,6,0,
+3,192,7,0,
+3,128,3,0,
+3,0,3,0,
+3,0,3,0,
+3,0,3,0,
+3,0,3,0,
+3,0,3,0,
+3,0,3,0,
+3,0,3,0,
+3,0,3,0,
+3,0,3,0,
+3,0,3,0,
+3,0,3,0,
+3,0,3,0,
+3,0,3,0,
+31,240,31,224,
+31,240,31,224,
+ 111 /* o */, 4, 0, 14, 1, 0, 0, 0, 0, 0, 22, 0, 26, 0, 23, 0, 104,
+0,31,224,0,
+0,127,248,0,
+0,240,124,0,
+1,192,14,0,
+3,128,7,0,
+7,0,3,128,
+7,0,3,128,
+6,0,1,128,
+14,0,1,192,
+12,0,0,192,
+12,0,0,192,
+12,0,0,192,
+12,0,0,192,
+12,0,0,192,
+14,0,1,192,
+14,0,1,192,
+6,0,1,128,
+7,0,3,128,
+3,128,7,0,
+1,192,14,0,
+0,240,60,0,
+0,127,248,0,
+0,31,224,0,
+ 112 /* p */, 4, 0, 14, 1, 0, 0, 0, 0, 0, 22, 0, 27, 0, 31, 0, 108,
+30,7,224,0,
+126,31,252,0,
+126,120,30,0,
+6,224,7,0,
+7,192,3,128,
+7,128,1,192,
+7,0,0,192,
+7,0,0,192,
+6,0,0,96,
+6,0,0,96,
+6,0,0,96,
+6,0,0,96,
+6,0,0,96,
+6,0,0,96,
+7,0,0,192,
+7,128,0,192,
+7,128,1,128,
+7,224,7,128,
+6,248,31,0,
+6,63,252,0,
+6,15,240,0,
+6,0,0,0,
+6,0,0,0,
+6,0,0,0,
+6,0,0,0,
+6,0,0,0,
+6,0,0,0,
+6,0,0,0,
+6,0,0,0,
+127,248,0,0,
+127,248,0,0,
+ 113 /* q */, 4, 0, 14, 1, 0, 0, 0, 0, 0, 22, 0, 29, 0, 31, 0, 116,
+0,31,128,0,
+0,127,225,248,
+1,224,121,248,
+3,128,29,128,
+7,0,13,128,
+14,0,7,128,
+12,0,3,128,
+12,0,3,128,
+24,0,1,128,
+24,0,1,128,
+24,0,1,128,
+24,0,1,128,
+24,0,1,128,
+24,0,1,128,
+12,0,3,128,
+14,0,3,128,
+6,0,7,128,
+3,128,29,128,
+1,224,121,128,
+0,255,241,128,
+0,63,193,128,
+0,0,1,128,
+0,0,1,128,
+0,0,1,128,
+0,0,1,128,
+0,0,1,128,
+0,0,1,128,
+0,0,1,128,
+0,0,1,128,
+0,0,127,248,
+0,0,127,248,
+ 114 /* r */, 4, 0, 14, 1, 0, 0, 0, 0, 0, 22, 0, 27, 0, 22, 0, 108,
+0,16,31,0,
+7,240,63,128,
+7,240,241,192,
+0,49,192,224,
+0,51,128,96,
+0,55,0,0,
+0,62,0,0,
+0,60,0,0,
+0,56,0,0,
+0,48,0,0,
+0,48,0,0,
+0,48,0,0,
+0,48,0,0,
+0,48,0,0,
+0,48,0,0,
+0,48,0,0,
+0,48,0,0,
+0,48,0,0,
+0,48,0,0,
+0,48,0,0,
+15,255,254,0,
+15,255,254,0,
+ 115 /* s */, 4, 0, 14, 1, 0, 0, 0, 0, 0, 22, 0, 25, 0, 23, 0, 100,
+0,31,192,0,
+0,127,243,0,
+0,240,63,0,
+1,192,15,0,
+3,128,7,0,
+3,0,7,0,
+3,0,3,0,
+3,128,0,0,
+1,192,0,0,
+0,254,0,0,
+0,127,240,0,
+0,7,252,0,
+0,0,31,0,
+0,0,7,0,
+0,0,3,128,
+6,0,1,128,
+6,0,1,128,
+6,0,1,128,
+7,0,3,128,
+7,128,15,0,
+7,240,62,0,
+6,127,252,0,
+0,31,224,0,
+ 116 /* t */, 4, 0, 14, 1, 0, 0, 0, 0, 0, 28, 0, 25, 0, 29, 0, 100,
+1,128,0,0,
+1,128,0,0,
+1,128,0,0,
+1,128,0,0,
+1,128,0,0,
+1,128,0,0,
+1,128,0,0,
+63,255,248,0,
+63,255,252,0,
+1,128,0,0,
+1,128,0,0,
+1,128,0,0,
+1,128,0,0,
+1,128,0,0,
+1,128,0,0,
+1,128,0,0,
+1,128,0,0,
+1,128,0,0,
+1,128,0,0,
+1,128,0,0,
+1,128,0,0,
+1,128,0,0,
+1,128,0,0,
+1,128,0,0,
+1,192,0,0,
+0,192,3,128,
+0,240,31,0,
+0,63,252,0,
+0,15,224,0,
+ 117 /* u */, 4, 0, 14, 1, 0, 0, 0, 0, 0, 21, 0, 27, 0, 22, 0, 108,
+63,0,127,0,
+63,0,127,0,
+3,0,3,0,
+3,0,3,0,
+3,0,3,0,
+3,0,3,0,
+3,0,3,0,
+3,0,3,0,
+3,0,3,0,
+3,0,3,0,
+3,0,3,0,
+3,0,3,0,
+3,0,3,0,
+3,0,3,0,
+3,0,3,0,
+3,0,3,0,
+3,0,7,0,
+3,128,15,0,
+1,128,59,0,
+1,224,243,224,
+0,127,195,224,
+0,31,0,0,
+ 118 /* v */, 4, 0, 14, 1, 0, 0, 0, 0, 0, 21, 0, 28, 0, 21, 0, 112,
+63,240,63,240,
+63,240,63,240,
+6,0,1,128,
+3,0,3,0,
+3,0,3,0,
+3,128,7,0,
+1,128,6,0,
+1,192,6,0,
+0,192,12,0,
+0,192,12,0,
+0,96,24,0,
+0,96,24,0,
+0,48,56,0,
+0,48,48,0,
+0,56,48,0,
+0,24,96,0,
+0,28,96,0,
+0,12,192,0,
+0,12,192,0,
+0,7,128,0,
+0,7,128,0,
+ 119 /* w */, 4, 0, 14, 1, 0, 0, 0, 0, 0, 21, 0, 28, 0, 21, 0, 112,
+63,192,15,240,
+63,192,15,240,
+12,0,0,192,
+12,0,0,192,
+12,0,0,192,
+6,3,1,128,
+6,7,129,128,
+6,7,129,128,
+6,7,129,128,
+6,14,193,128,
+3,12,195,0,
+3,12,195,0,
+3,24,99,0,
+3,24,99,0,
+1,152,102,0,
+1,176,54,0,
+1,176,54,0,
+1,176,54,0,
+1,224,30,0,
+0,224,28,0,
+0,224,28,0,
+ 120 /* x */, 4, 0, 14, 1, 0, 0, 0, 0, 0, 21, 0, 27, 0, 21, 0, 108,
+15,240,63,192,
+15,240,63,192,
+3,128,7,0,
+1,192,14,0,
+0,224,28,0,
+0,112,56,0,
+0,56,112,0,
+0,28,224,0,
+0,15,192,0,
+0,7,128,0,
+0,7,128,0,
+0,15,192,0,
+0,28,224,0,
+0,56,112,0,
+0,112,56,0,
+0,224,28,0,
+1,192,14,0,
+3,128,7,0,
+7,0,3,128,
+31,240,63,224,
+31,240,63,224,
+ 121 /* y */, 4, 0, 14, 1, 0, 0, 0, 0, 0, 21, 0, 27, 0, 30, 0, 108,
+31,224,31,224,
+31,224,31,224,
+6,0,1,128,
+7,0,1,128,
+3,0,3,0,
+3,128,3,0,
+1,128,6,0,
+1,192,6,0,
+0,192,12,0,
+0,224,12,0,
+0,96,24,0,
+0,112,24,0,
+0,48,48,0,
+0,56,48,0,
+0,24,96,0,
+0,28,96,0,
+0,12,192,0,
+0,14,192,0,
+0,7,128,0,
+0,7,128,0,
+0,3,0,0,
+0,3,0,0,
+0,6,0,0,
+0,6,0,0,
+0,12,0,0,
+0,12,0,0,
+0,24,0,0,
+0,24,0,0,
+31,255,128,0,
+31,255,128,0,
+ 122 /* z */, 4, 0, 14, 1, 0, 0, 0, 0, 0, 21, 0, 24, 0, 21, 0, 96,
+3,255,254,
+3,255,254,
+3,0,14,
+3,0,28,
+3,0,56,
+3,0,48,
+0,0,112,
+0,0,224,
+0,1,192,
+0,3,128,
+0,7,0,
+0,14,0,
+0,28,0,
+0,24,0,
+0,56,0,
+0,112,3,
+0,224,3,
+1,192,3,
+3,128,3,
+3,255,255,
+3,255,255,
+ 32 /* */, 4, 0, 14, 1, 0, 0, 0, 0, 0, 0, 0, 16, 0, 0, 0, 64,
+ 0
+};
+
+const int px_bitmap_font_point_size = 24;
+const int px_bitmap_font_resolution = 150;
+const byte px_bitmap_font_header[] = {
+ 0, 0, 0, 14, 254, 0, 0, 72,
+ 'B', 'R', 0, 0, 0, 4,
+ 0, 150, 0, 150,
+ 0xff, 0xff, 0, 0, 0, 0
+};
+const uint px_bitmap_font_header_size = sizeof(px_bitmap_font_header);
diff --git a/pxl/pxbfont.h b/pxl/pxbfont.h
new file mode 100644
index 000000000..824d0aa1a
--- /dev/null
+++ b/pxl/pxbfont.h
@@ -0,0 +1,17 @@
+/* Copyright (C) 1996 Aladdin Enterprises. All rights reserved.
+ Unauthorized use, copying, and/or distribution prohibited.
+ */
+
+/* pxbfont.h */
+/* Interface to PCL XL built-in bitmap font */
+
+/*
+ * This bitmap font is included in the interpreter solely for the
+ * purpose of producing error pages.
+ */
+
+extern const int px_bitmap_font_point_size;
+extern const int px_bitmap_font_resolution;
+extern const byte px_bitmap_font_header[];
+extern const uint px_bitmap_font_header_size;
+extern const byte px_bitmap_font_char_data[];
diff --git a/pxl/pxbfont.ps b/pxl/pxbfont.ps
new file mode 100644
index 000000000..f08affe25
--- /dev/null
+++ b/pxl/pxbfont.ps
@@ -0,0 +1,120 @@
+%!
+% Copyright (C) 1996 Aladdin Enterprises. All rights reserved.
+% Unauthorized use, copying, and/or distribution prohibited.
+
+% Construct the bitmap font used for printing the error page.
+
+% Put space last so we can fake the width.
+/Chars
+ ("\(\)-/0123456789:;?ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz )
+def
+/Res 150 def
+/Face /Courier def
+/Points 24 def
+
+% Write the copyright notice.
+(/* Copyright (C) 1996 Aladdin Enterprises. All rights reserved.
+ Unauthorized use, copying, and/or distribution prohibited.
+ */
+
+#include "stdpre.h"
+#include "pxbfont.h"
+) print
+
+% Create the character bitmaps.
+(
+const byte px_bitmap_font_char_data[] = {
+) print
+/Asc 0 def
+/Desc 0 def
+/Base Res 1 add 2 idiv def
+/Sc Res 72 div def
+% Round up the device width so there won't be trailing garbage bits
+% when we read out the character bitmaps.
+/ID [Sc 0 0 Sc neg 0 Res]
+ Res dup neg 7 and add Res <ff 00> makeimagedevice def
+nulldevice ID setdevice
+/Row Res 7 add 8 idiv string def
+/ZRow Row length string def
+Face Points selectfont
+/print16
+ { dup 8 bitshift 255 and =only (, ) print 255 and =only
+ } def
+Chars
+ { 1 string dup 0 4 -1 roll put /Ch exch def
+ erasepage 0 36 moveto Ch show
+ 0 Res
+ % Remove blank lines at bottom.
+ { 2 copy eq { exit } if
+ dup 1 sub ID exch Row copyscanlines ZRow ne { exit } if
+ 1 sub
+ }
+ loop
+ % Remove blank lines at top.
+ { 2 copy eq { exit } if
+ 1 index ID exch Row copyscanlines ZRow ne { exit } if
+ exch 1 add exch
+ }
+ loop
+ % Determine the character width.
+ /B 0 def
+ /W 0 def
+ 2 copy 1 sub 1 exch
+ { ID exch Row copyscanlines pop
+ Row length 1 sub -1 W
+ { Row 1 index get dup 0 ne
+ { 1 index W eq { B or } if /B exch def /W exch def exit
+ }
+ if pop pop
+ }
+ for
+ }
+ for
+ /WB W 8 mul 8 add B 16#100 or % might be an empty character
+ { dup 1 and 0 ne { pop exit } if
+ exch 1 sub exch -1 bitshift
+ }
+ loop
+ dup 0 eq { pop Asc 2 idiv } if def % fake width of empty characters
+ 2 copy eq { pop pop Base dup } if % empty character
+ /Asc Asc Base 4 index sub max def
+ /Desc Desc 2 index Base sub max def
+ % Write the character code.
+ (\t) print Ch 0 get =only ( /* ) print Ch =only ( */, ) print
+ % Write the character header.
+ % We use the PCL5 format, rather than the PCL XL format,
+ % so that we can represent the inter-character spacing.
+ (4, 0, 14, 1, 0, 0, 0, 0, ) =only
+ Base 2 index sub print16 (, ) print
+ WB print16 (, ) print
+ 2 copy exch sub print16 (, ) print
+ WB 4 mul print16 (,) =
+ % Write the bits.
+ 1 sub 1 exch
+ { ID exch Row copyscanlines 0 W 1 add getinterval
+ { =only (,) print
+ } forall () =
+ }
+ for
+ }
+forall
+(\t0
+};
+) print
+
+% Create the font header.
+(
+const int px_bitmap_font_point_size = ) print Points =only (;
+const int px_bitmap_font_resolution = ) print Res =only (;
+) print
+(const byte px_bitmap_font_header[] = {
+ 0, 0, 0, 14, 254, 0, 0, ) print Chars length =only (,
+ 'B', 'R', 0, 0, 0, 4,
+ 0, ) print Res =only (, 0, ) print Res =only (,
+ 0xff, 0xff, 0, 0, 0, 0
+};
+const uint px_bitmap_font_header_size = sizeof(px_bitmap_font_header);
+) print
+
+% All done.
+flush quit
diff --git a/pxl/pxcet.txt b/pxl/pxcet.txt
new file mode 100644
index 000000000..8b511a35d
--- /dev/null
+++ b/pxl/pxcet.txt
@@ -0,0 +1,170 @@
+
+ Copyright (C) 1996 Aladdin Enterprises. All rights reserved.
+ Unauthorized use, copying, and/or distribution prohibited.
+
+This document presents the results of Aladdin's investigation of
+discrepancies between the Genoa LaserJet 5 PCL6 Command-Level Emulation
+Test, the published PCL XL Feature Reference Protocol Class 1.1
+specification, and the behavior of the H-P LaserJet 6MP printer.
+
+ Report on Genoa LaserJet 5 PCL6 CET and LaserJet 6MP
+
+Introduction
+============
+
+In the course of validating our PCL XL interpreter using the Genoa LaserJet
+5 PCL6 Command-Level Emulation Test (CET), we observed a number of
+discrepancies that, after careful investigation, we concluded were due to
+problems in the CET and/or the printer firmware. In this document, we
+present these in detail.
+
+This document should be read in conjunction with our separate report on
+discrepancies between the specification and the firmware independent of the
+Genoa tests.
+
+Changes in a given revision of this document are marked with the revision
+number in [brackets]. Revision history:
+ first issued December 12, 1996
+ rev. [1] December 13, 1996
+ rev. [2] December 19, 1996
+ rev. [3] January 1, 1997
+ rev. [4] January 17, 1997
+ rev. [5] January 29, 1997
+
+Command emulation tests
+=======================
+
+c101
+----
+
+On the pages that include both a UnitsOfMeasure command setting different
+units for the X and Y axes and a PageScale command, text scaling appears to
+be affected only by one of these and not the other. (We didn't analyze the
+output carefully enough to determine which one.) This affects pages 16
+through 19 of c101. This is apparently caused by a LJ5 firmware bug that
+has been corrected in the LJ6MP: the 6MP agrees with our reading of the
+specification.
+
+This bug affects other tests as well; we have tried to note every instance
+of it below.
+
+c102
+----
+
+The bug noted under c101 also affects page 4 of c102.
+
+c302
+----
+
+The bug noted under c101 also affects pages 3 and 6 of c302.
+
+c306
+----
+
+[5] On page 8 of c306, the patterns with an origin of -32767 in X or Y are
+apparently incorrect: they appear as though the origin were +32767.
+
+Specifying a NewDestinationSize for a raster pattern in the SetBrushSource
+or SetPenSource command apparently changes the destination size permanently,
+or at least for a subsequent SetBrush/PenSource command that doesn't specify
+a NewDestinationSize. Page 43 of c306 tests for this explicitly, and the
+LJ5 fails the test. This apparent firmware bug has [not] been fixed in the
+LJ6MP.
+
+c307
+----
+
+The bug affecting page 43 of c306 also affects page 44 of c307.
+
+[1] c308
+--------
+
+[1] The text on page 23 says that "Printing a bitmap font at an angle
+produces an error." However, no error occurs. By experiment, we have
+determined that rotating the *page* causes bitmaps to rotate appropriately,
+but setting the character angle, scale, or shear factor to a non-default
+value causes an IllegalFontData error.
+
+[2] c319
+--------
+
+The "PenWidth_UI = 600" shape on page 37 is actually created from a
+lower-case 'e' in the Arial font, so it may appear different depending on
+the details of the font and the rasterizer.
+
+[1] c321
+--------
+
+[1] It appears that if the current clip mode is even/odd, SetClipIntersect
+with an exterior clip region disregards the previous clip path and does the
+equivalent of SetClipReplace. The first, and simplest, example of this is
+the upper right figure on page 3: the "intersection" clip region is simply
+the exterior of the two lightly-drawn rectangles, with no relationship to
+the darker-drawn rectangle which is the original clipping region. This bug
+shows up in the Genoa printouts and has also been observed on the LJ6MP.
+
+[1] This bug also affects many other pages in this test.
+
+[2] c330
+--------
+
+On the last page of this test, a partial 'B' should appear in portrait
+orientation to the left of the partial 'A'. The input data read as follows
+(slightly abridged):
+
+ 0 6600 usp @PageOrigin SetPageOrigin
+ 90 ss @PageAngle SetPageRotation
+ PushGS
+ -2300 1900 ssp @PageOrigin SetPageOrigin
+ 2.23 1.1 rp @PageScale SetPageScale
+ (Courier Bd) ba @FontName 1000 us @CharSize
+ 629 us @SymbolSet SetFont
+ 0 b @NullBrush SetBrushSource
+ NewPath
+ 1500 0 3500 1200 usq @BoundingBox
+ 2100 0 ssp @StartPoint
+ 2600 0 ssp @EndPoint
+ PiePath PaintPath
+ 0 b @ClipRegion SetClipReplace
+ <79 7b 7d> ba @RGBColor SetBrushSource
+ 2500 900 ssp @PageOrigin SetPageOrigin
+ 0 0 usp @Point SetCursor
+ -180 ss @PageAngle SetPageRotation
+ (A) ba @TextData Text
+ SetPageDefaultCTM
+ 2100 3600 usp @Point SetCursor
+ (B) ba @TextData Text
+
+Even when we modified the test data to paint an entire page of 'B' at this
+point, no characters appeared. We assume this results from a firmware bug,
+but we have no theories as to its nature.
+
+[4] c333
+--------
+
+In the "Effect on CharAngle" and "Effect on CharShear" lines of page 16, the
+character appears to be scaled by the page scale before being rotated by the
+character angle or sheared, rather than vice versa. We believe this is a
+firmware bug because we verified, by testing all 125 possible sequences of 3
+operations chosen from the set {SetCharAngle, SetCharScale, SetCharShear,
+SetPageRotation, SetPageScale}, that the character transformations uniformly
+occur before the page transformations.
+
+The bug noted under c101 also affects pages 18 through 20 of c333.
+
+Error emulation tests
+=====================
+
+[2] e120
+--------
+
+The H-P implementation apparently supports only the little-endian binary
+binding: [3] an "unavailable binding" error occurs when the file attempts to
+start an input stream with big-endian binding. Furthermore, there is an
+apparent data error in the subsequent big-endian data: at byte position 101
+in the big-endian section (counting the first byte of PCL XL data after the
+stream header as byte 0), the length of the font name "Courier" is
+incorrectly coded as (16,0) (little-endian) rather than (0,16) (big-endian).
+The resulting 4096-byte count causes the entire remainder of the file to be
+interpreted as a data stream; since this exceeds the length of the file, the
+test terminates.
diff --git a/pxl/pxdict.h b/pxl/pxdict.h
new file mode 100644
index 000000000..caeee62af
--- /dev/null
+++ b/pxl/pxdict.h
@@ -0,0 +1,66 @@
+/* Copyright (C) 1996 Aladdin Enterprises. All rights reserved.
+ Unauthorized use, copying, and/or distribution prohibited.
+ */
+
+/* pxdict.h */
+/* Dictionary interface for PCL XL parser */
+/* Requires gsmemory.h */
+
+#ifndef pxdict_INCLUDED
+# define pxdict_INCLUDED
+
+#include "pxvalue.h"
+#include "pldict.h"
+
+/*
+ * We use dictionaries to catalog 3 kinds of entities: fonts, user-defined
+ * streams, and raster patterns. (For raster patterns, each level of
+ * persistence has a separate dictionary.) The keys are strings (byte or
+ * uint16).
+ */
+
+typedef pl_dict_value_free_proc_t px_dict_value_free_proc;
+#ifndef px_dict_entry_DEFINED
+# define px_dict_entry_DEFINED
+typedef pl_dict_entry_t px_dict_entry_t;
+#endif
+typedef pl_dict_t px_dict_t;
+#define st_px_dict st_pl_dict
+#define st_px_dict_max_ptrs st_pl_dict_max_ptrs
+
+/* Get the data and size of a byte or uint16 string. */
+#define key_data(key)\
+ ((key)->value.array.data)
+#define key_size(key)\
+ ((key)->value.array.size * value_size(key))
+
+/* Initialize a dictionary. */
+#define px_dict_init(pdict, memory, free_proc)\
+ pl_dict_init(pdict, memory, free_proc)\
+
+/*
+ * Look up an entry in a dictionary. Return true and set *pvalue if found.
+ */
+#define px_dict_find(pdict, key, pvalue)\
+ pl_dict_find(pdict, key_data(key), key_size(key), pvalue)
+
+/*
+ * Add an entry to a dictionary. Return 1 if it replaces an existing entry.
+ * Return -1 if we couldn't allocate memory.
+ */
+#define px_dict_put(pdict, key, value)\
+ pl_dict_put(pdict, key_data(key), key_size(key), value)
+
+/*
+ * Remove an entry from a dictionary. Return true if the entry was present.
+ */
+#define px_dict_undef(pdict, key)\
+ pl_dict_undef(pdict, key_data(key), key_size(key))
+
+/*
+ * Release a dictionary by freeing all keys, values, and other storage.
+ */
+#define px_dict_release(pdict)\
+ pl_dict_release(pdict)\
+
+#endif /* pxdict_INCLUDED */
diff --git a/pxl/pxdiff.txt b/pxl/pxdiff.txt
new file mode 100644
index 000000000..2a8d86804
--- /dev/null
+++ b/pxl/pxdiff.txt
@@ -0,0 +1,315 @@
+
+ Copyright (C) 1996, 1997 Aladdin Enterprises. All rights reserved.
+ Unauthorized use, copying, and/or distribution prohibited.
+
+This document presents known discrepancies between Aladdin's PCL XL
+interpreter and the output of the Genoa LaserJet 5 PCL6 FTS and CET.
+
+ Discrepancies from Genoa LaserJet 5 PCL6 Output
+
+Introduction
+============
+
+This document presents discrepancies between our interpreter and the output
+of individual Genoa tests due to any of the following:
+
+ A) Residual problems in our code.
+
+ B) Differences in halftone screen.
+
+ C) Differences in TrueType font rasterizer.
+
+ D) Different implementation choices that we believe will be fully
+ acceptable to customers.
+
+ E) [2] Implementation shortcuts that are visible to customers but
+ that are too much work to change unless we receive an explicit
+ request to do so.
+
+ F) Likely H-P firmware bugs.
+
+We will fix category (A) to the best of our ability in the initial product
+code release, with some additional fixes likely to be required in later
+releases. [4] We will fix category (B) when we have been able to analyze
+the H-P screen in complete detail, which we have not yet been able to do.
+We do not plan to do anything about (C) or (D). We are willing to discuss
+items in (E) individually.
+
+Some discrepancies are inevitable given that our interpreter is designed to
+meet the following not always consistent specifications:
+
+ 1) The published "PCL XL Feature Reference Protocol Class 1.1"
+ document from Hewlett-Packard.
+
+ 2) The printed output provided by Genoa with their LaserJet 5
+ PCL6 test suites.
+
+[4] 3) The printed output we obtained from a LaserJet 6MP.
+
+ 4) In a few cases, primarily where #1 is silent or ambiguous
+ or where #1 and #2 disagree, our best guess as to H-P's
+ intentions.
+
+In general, when we could find an interpretation of the H-P document that
+was consistent with the Genoa output, we have followed it in our
+implementation, even if that interpretation is not the most obvious one.
+[4] In the few cases of discrepancies between the LJ5 and the LJ6MP, we have
+followed the latter, since in all such cases the 6MP agrees with our
+understanding of the specification.
+
+Changes in a given revision of this document are marked with the revision
+number in [brackets]. Revision history:
+ first issued January 1, 1997
+ rev. [1] January 11, 1997
+ rev. [2] January 15, 1997
+ rev. [3] January 23, 1997
+ rev. [4] February 9, 1997
+
+Code problems
+=============
+
+Page shifting
+-------------
+
+We do not currently handle discrepancies between the printer's (0,0) point
+and the physical page corner correctly. For example, when printing to the
+LaserJet 4/5/6, all pages are shifted down and to the right by approximately
+1/4". (This should not affect OEM customers.)
+
+Memory management
+-----------------
+
+If an error occurs in the middle of downloading an object like a font or a
+pattern, the current code does not free the partially-built structure.
+
+We have not attempted to exhaustively test graceful recovery from
+out-of-memory conditions; it is likely that such conditions may, at least
+under some circumstances, require reinitializing the interpreter. [4] The
+interpreter does not store any state in global variables, so simply
+releasing all memory acquired by the interpreter and reinitializing the
+interpreter is always safe.
+
+Miscellaneous
+-------------
+
+Our current implementation of pseudo-bold characters requires a large amount
+of buffer space, which causes a memory overflow error on the following
+files:
+ CET c309
+
+[4] Our current implementation of elliptical arcs has a slight rounding
+inaccuracy that produces an inappropriate visible hairline in 4 of the 5
+images in the rightmost column of the bottom half of
+ CET c404 p. 25
+This inaccuracy may affect other CET pages where the closing lines of two
+chords whose bounding boxes have different aspect ratios are supposed to
+coincide.
+
+[4] In our current implementation, reading back the clipping path as the
+current path may produce a path that covers a region as much as 1 pixel
+larger all the way around as the original path that was specified for
+clipping. This produces an inappropriate hairline border around the objects
+in image 02 of
+ FTS t324
+
+Halftone screen
+===============
+
+We have not yet been able to duplicate the behavior of H-P's carefully
+designed halftone screen, which produces striking and unusual effects when
+ORing together different source and paint gray shades using RasterOp. Many
+of the Genoa tests do this, even though this is not something that we expect
+to occur frequently (or perhaps at all) in application output.
+Unfortunately, the visible effects are not subtle (they arise from something
+similar to Moire' patterns). We can't give an exhaustive list of the test
+pages that this affects, since many pages on many tests do this. [4] One
+unobvious result of this is that certain objects simply disappear: for
+example, some gray-shaded rectangles in
+ CET c420 pp. 5-14
+
+TrueType rasterizer
+===================
+
+Our current TrueType font rasterizer is not of production quality: it
+disregards the hinting instructions and does not handle correctly multi-part
+characters where one part is offset with respect to another. This causes
+many differences in the output, mostly minor. We plan to offer a
+commercial-quality rasterizer at some time in the future; however, our PCL
+XL interpreter is an OEM product for which our customers are expected to
+provide their own rasterizers.
+
+Implementation choices
+======================
+
+Range of coordinates
+--------------------
+
+Our interpreter uses a fixed-point representation for device coordinates (20
+bits of integer, 12 bits of fraction) and keeps all coordinates in this
+form, including the cursor position. Thus, any operation that sends the
+cursor outside this range will cause an InternalOverflow error. At 600 dpi,
+this corresponds to positioning the cursor more than about 72 feet outside
+the page. A number of the CET files do this, to exercise the full range of
+possible parameter values. We recognize this is a limitation, but we do not
+currently intend to do anything about it, since it is extremely unlikely to
+cause problems in practice. The files on which this causes an error are:
+ CET c309, c310
+
+Line Printer font implementation
+--------------------------------
+
+Our interpreter implements the Line Printer fonts as aliases for Courier,
+unlike the H-P printers which apparently implement them using bitmaps. As a
+result, attempting to scale, rotate, or shear them does not produce an error
+in our implementation. This causes an output difference in:
+ CET e104
+
+Graphics state storage management
+---------------------------------
+
+Our interpreter allocates graphics states from the main storage pool, rather
+than imposing a fixed limit on the number of PushGS levels. As a result,
+our implementation does not report an error, even though the H-P printer
+produces one, in:
+ CET e112
+
+Downloaded font checking
+------------------------
+
+Our interpreter defers almost all of its checking of downloaded font headers
+until it receives the EndFontHeader command. Because of this, many of the
+errors associated with font downloading occur on an EndFontHeader instead of
+a ReadFontHeader, at a slightly later position in the file. This affects
+the output from:
+ CET e114
+Also, our implementation does not produce the very first error from this
+file (the "InternalError 0x50").
+
+Byte order support
+------------------
+
+The H-P implementation apparently supports only the little-endian binary
+binding; our interpreter supports both big- and little-endian binary
+bindings. The only file that tests this is:
+ CET e120
+Because our interpreter supports both bindings, this file should produce at
+least one fewer error than the H-P printers. However, there is an error in
+the file itself: at byte position 101 in the big-endian section (counting
+the first byte of PCL XL data after the stream header as byte 0), the length
+of the font name "Courier" is incorrectly coded as (16,0) (little-endian)
+rather than (0,16) (big-endian). The resulting 4096-byte count causes the
+entire remainder of the file to be interpreted as a data stream; since this
+exceeds the length of the file, the test terminates.
+
+[2] Implementation shortcuts
+============================
+
+[1] Negative dash pattern elements
+----------------------------------
+
+SetLineDash allows negative dash pattern elements, but the H-P documentation
+gives no clue about what these are supposed to do. The H-P printers
+apparently interpret it as drawing a line backwards in the current direction
+(which may extend outside the original subpath) with no visible caps; a dash
+pattern with a negative total length crashed the LJ 6MP firmware so badly
+the printer had to be power cycled! We have chosen to take a different
+approach: we compensate for negative elements by propagating them to
+adjacent positive ones. This doesn't produce quite the same output as the
+H-P printers do (segments drawn in the reverse direction do have caps, and
+if the line is shorter than one complete cycle of the pattern, some parts of
+the line may be stroked that the H-P printers skip), but this is such an
+obscure feature that we don't think it's worth the trouble to emulate
+exactly. The difference affects:
+ CET c318
+
+Rounding
+--------
+
+On the page:
+ FTS t421 p. 4
+our implementation shows faint vertical "seams" on the two left-hand
+patterns in image 09. While the Genoa output doesn't have a seam, a test we
+made using H-P's default halftone does show a seam. We conclude that our
+implementation and H-P's both have some rounding sensitivities, and they
+simply happen to show up on slightly different input values. [2] Matching
+H-P's implementation at this level of detail would be extremely onerous.
+
+[3] A similar seam occurs in the lower image on the page:
+ CET c422 p. 38
+
+[4] For a similar reason, in image 06 of
+ FTS t315 p. 2
+our implementation does not produce either bevels or miters. The reason is
+that the line join point coincides exactly with the end of an "ink off" dash
+segment: H-P's implementation has a rounding inaccuracy that causes it to
+produce a vertical hairline as the last segment of the horizontal line
+(which is drawn in the -X direction), and then join this hairline with the
+first segment of the diagonal line.
+
+H-P firmware bugs
+=================
+
+[4] Except for the instances mentioned here, our implementation emulates
+behavior of the H-P printers that we have stated elsewhere we believe result
+from firmware bugs.
+
+Changing destination size of raster patterns is permanent
+---------------------------------------------------------
+
+Specifying a NewDestinationSize for a raster pattern in the SetBrushSource
+or SetPenSource command apparently changes the destination size permanently,
+or at least for a subsequent SetBrush/PenSource command that doesn't specify
+a NewDestinationSize. This is tested explicitly (and the H-P printers fail
+the test) in:
+ CET c306 p. 43, c307 p. 44
+
+Mysterious missing character
+----------------------------
+
+In:
+ CET c330 last page
+a partial 'B' should appear in portrait orientation to the left of the
+partial 'A'. The input data read as follows (slightly abridged):
+
+ 0 6600 usp @PageOrigin SetPageOrigin
+ 90 ss @PageAngle SetPageRotation
+ PushGS
+ -2300 1900 ssp @PageOrigin SetPageOrigin
+ 2.23 1.1 rp @PageScale SetPageScale
+ (Courier Bd) ba @FontName 1000 us @CharSize
+ 629 us @SymbolSet SetFont
+ 0 b @NullBrush SetBrushSource
+ NewPath
+ 1500 0 3500 1200 usq @BoundingBox
+ 2100 0 ssp @StartPoint
+ 2600 0 ssp @EndPoint
+ PiePath PaintPath
+ 0 b @ClipRegion SetClipReplace
+ <79 7b 7d> ba @RGBColor SetBrushSource
+ 2500 900 ssp @PageOrigin SetPageOrigin
+ 0 0 usp @Point SetCursor
+ -180 ss @PageAngle SetPageRotation
+ (A) ba @TextData Text
+ SetPageDefaultCTM
+ 2100 3600 usp @Point SetCursor
+ (B) ba @TextData Text
+
+Even when we modified the test data to paint an entire page of 'B' at this
+point, no characters appeared. We assume this results from a firmware bug,
+but we have no theories as to its nature.
+
+[2] Characters transformed in wrong order
+-----------------------------------------
+
+In
+ CET c333 p. 16
+in the "Effect on CharAngle" and "Effect on CharShear" lines, the character
+appears to be scaled by the page scale before being rotated by the character
+angle or sheared, rather than vice versa. We believe this is a firmware bug
+because we verified, by testing all 125 possible sequences of 3 operations
+chosen from the set {SetCharAngle, SetCharScale, SetCharShear,
+SetPageRotation, SetPageScale}, that the character transformations uniformly
+occur before the page transformations. (We have identified two other
+firmware bugs relating to character transformations, one of which is present
+in the LJ5 but not the LJ6MP: see our separate report on the CET for
+details.)
diff --git a/pxl/pxenum.h b/pxl/pxenum.h
new file mode 100644
index 000000000..f7b908567
--- /dev/null
+++ b/pxl/pxenum.h
@@ -0,0 +1,10 @@
+/* Copyright (C) 1997 Aladdin Enterprises. All rights reserved.
+ Unauthorized use, copying, and/or distribution prohibited.
+ */
+
+/* pxenum.h */
+/* Enumerated attribute value definitions for PCL XL parser */
+
+/* The contents of this file have been moved to the graphics library, */
+/* so they can be used in the PCL XL driver. */
+#include "gdevpxen.h"
diff --git a/pxl/pxerrors.c b/pxl/pxerrors.c
new file mode 100644
index 000000000..62ee9c535
--- /dev/null
+++ b/pxl/pxerrors.c
@@ -0,0 +1,284 @@
+/* Copyright (C) 1996, 1997 Aladdin Enterprises. All rights reserved.
+ Unauthorized use, copying, and/or distribution prohibited.
+ */
+
+/* pxerrors.c */
+/* PCL XL error reporting */
+
+#include "memory_.h"
+#include "stdio_.h" /* for sprintf */
+#include "string_.h"
+#include "gsmemory.h"
+#include "gstypes.h" /* for gsmatrix.h */
+#include "gsccode.h" /* for gxfont.h */
+#include "gsmatrix.h" /* for gsfont.h */
+#include "gsstate.h" /* for gsfont.h, gspath.h */
+#include "gspaint.h" /* for gs_erasepage */
+#include "gscoord.h"
+#include "gspath.h"
+#include "gsutil.h"
+#include "gxfixed.h" /* for gxchar.h */
+#include "gxchar.h"
+#include "gxfont.h"
+#include "scommon.h" /* for pxparse.h */
+#include "pxbfont.h"
+#include "pxerrors.h"
+#include "pxparse.h"
+#include "pxptable.h" /* for px_operator_names */
+#include "pxstate.h"
+#include "pxfont.h"
+
+/* Imported operators */
+px_operator_proc(pxEndPage);
+px_operator_proc(pxSetPageDefaultCTM);
+
+/* ---------------- Initialization ---------------- */
+
+/* Create the error page font, and preallocate other structures so that */
+/* printing an error message never needs to allocate storage. */
+int
+pxerrors_init(px_state_t *pxs)
+{ px_font_t *pxfont = pl_alloc_font(pxs->memory, "error_page_font");
+ gs_show_enum *penum = gs_show_enum_alloc(pxs->memory, pxs->pgs,
+ "error_page_show_enum");
+ int code;
+
+ if ( pxfont == 0 || penum == 0 )
+ code = gs_note_error(errorInsufficientMemory);
+ else
+ { pxfont->storage = pxfsInternal;
+ pxfont->font_type = plft_Unicode; /* as good as any */
+ pxs->known_fonts_base_id = gs_next_ids(px_num_known_fonts + 1);
+ code =
+ px_define_font(pxfont, px_bitmap_font_header,
+ px_bitmap_font_header_size,
+ pxs->known_fonts_base_id + px_num_known_fonts,
+ pxs);
+ { const byte *cdata = px_bitmap_font_char_data;
+ while ( *cdata && code >= 0 )
+ { code = pl_font_add_glyph(pxfont, *cdata, cdata + 1);
+ ++cdata;
+ cdata = cdata + 16 +
+ ((uint16at(cdata + 10, true) + 7) >> 3) *
+ uint16at(cdata + 12, true);
+ }
+ }
+ }
+ if ( code < 0 )
+ { gs_free_object(pxs->memory, penum, "pxs->error_page_show_enum");
+ gs_free_object(pxs->memory, pxfont, "pxs->error_page_font");
+ return code;
+ }
+ pxs->error_page_font = pxfont;
+ pxs->error_page_show_enum = penum;
+ return 0;
+}
+
+/* ---------------- Procedures ---------------- */
+
+/* Record a warning. */
+/* Return 1 if the warning table overflowed. */
+/* If save_all is false, only remember the last warning with the same */
+/* first word as this one. */
+int
+px_record_warning(const char *message, bool save_all, px_state_t *pxs)
+{ uint end = pxs->warning_length;
+ char *str = pxs->warnings + end;
+ char *word_end = strchr(message, ' ');
+
+ if ( end + strlen(message) + 1 > px_max_warning_message )
+ return 1;
+ if ( !save_all && word_end )
+ { /* Delete any existing message of the same type. */
+ /* (There is at most one.) */
+ uint word_len = word_end - message;
+ char *next = pxs->warnings;
+ uint len1;
+
+ for ( ; next != str; next += len1 )
+ { len1 = strlen(next) + 1;
+ if ( len1 > word_len && !strncmp(next, message, word_len) )
+ { /* Delete the old message. */
+ memmove(next, next + len1, str - (next + len1));
+ str -= len1;
+ break;
+ }
+ }
+ }
+ strcpy(str, message);
+ pxs->warning_length = str + strlen(str) + 1 - pxs->warnings;
+ return 0;
+}
+
+/* Generate a line of an error message starting at internal position N; */
+/* return an updated value of N. When done, return -1. */
+int
+px_error_message_line(char message[px_max_error_line+1], int N,
+ const char *subsystem, int code, const px_parser_state_t *st,
+ const px_state_t *pxs)
+{ if ( N == 0 )
+ { strcpy(message, "PCL XL error\n");
+ return 1;
+ }
+ if ( code == errorWarningsReported )
+ { /*
+ * Generate a line of warnings.
+ * 1 = first line, otherwise N = position in warnings buffer.
+ */
+ switch ( N )
+ {
+ case 1:
+ N = 0;
+ /* falls through */
+ default:
+ if ( N == pxs->warning_length )
+ return -1;
+ { const char *str = pxs->warnings + N;
+ uint len = strlen(str);
+ uint warn_len;
+
+ strcpy(message, " Warning: ");
+ warn_len = strlen(message) + 1;
+ if ( len > px_max_error_line - warn_len )
+ { strncat(message, str, px_max_error_line - warn_len);
+ message[px_max_error_line - 1] = 0;
+ }
+ else
+ strcat(message, str);
+ strcat(message, "\n");
+ return N + len + 1;
+ }
+ }
+ }
+ else
+ { /* Generate the N'th line of an error message. */
+ char *end;
+ switch ( N )
+ {
+ case 1:
+ sprintf(message, " Subsystem: %s\n", subsystem);
+ break;
+ case 2:
+ strcpy(message, " Error: ");
+ { char *end = message + strlen(message);
+ if ( pxs->error_line[0] )
+ { /* Ignore the error code, use the error line. */
+ int len = strlen(pxs->error_line);
+ int max_len = px_max_error_line - 2 - strlen(message);
+
+ if ( len <= max_len )
+ strcpy(end, pxs->error_line);
+ else
+ { strncpy(end, pxs->error_line, max_len);
+ message[px_max_error_line - 1] = 0;
+ }
+ strcat(end, "\n");
+ }
+ else if ( code >= px_error_first && code < px_error_next )
+ sprintf(end, "%s\n",
+ px_error_names[code - px_error_first]);
+ else
+ sprintf(end, "Internal error 0x%x\n", code);
+ }
+ break;
+ case 3:
+ { int last_operator = st->last_operator;
+ const char *oname;
+
+ strcpy(message, " Operator: ");
+ end = message + strlen(message);
+ if ( last_operator >= 0x40 && last_operator < 0xc0 &&
+ (oname = px_operator_names[last_operator - 0x40]) != 0
+ )
+ sprintf(end, "%s\n", oname);
+ else
+ sprintf(end, "0x%02x\n", last_operator);
+ }
+ break;
+ case 4:
+ strcpy(message, " Position: ");
+ end = message + strlen(message);
+ if ( st->parent_operator_count )
+ sprintf(end, "%ld;%ld\n", st->parent_operator_count,
+ st->operator_count);
+ else
+ sprintf(end, "%ld\n", st->operator_count);
+ break;
+ default:
+ return -1;
+ }
+ return N + 1;
+ }
+}
+
+/* Begin an error page. Return the initial Y value. */
+int
+px_begin_error_page(px_state_t *pxs)
+{ gs_state *pgs = pxs->pgs;
+
+ if ( pxs->have_page )
+ { px_args_t args;
+ args.pv[0] = 0;
+ pxEndPage(&args, pxs);
+ }
+ gs_initgraphics(pgs);
+ gs_erasepage(pgs);
+ /* Don't call pxSetPageDefaultCTM -- we don't want rotation or */
+ /* unusual Units of Measure -- but do invert the Y axis. */
+ /*pxSetPageDefaultCTM(NULL, pxs);*/
+ gs_translate(pgs, 0.0, pxs->media_size.y);
+ gs_scale(pgs, 1.0, -1.0);
+ gs_setfont(pgs, (gs_font *)pxs->error_page_font->pfont);
+ return 90;
+}
+
+/* Print a message on an error page. */
+/* Return the updated Y value. */
+int
+px_error_page_show(const char *message, int ytop, px_state_t *pxs)
+{ gs_state *pgs = pxs->pgs;
+ int y = ytop;
+ const char *m = message;
+ const char *p;
+ gs_show_enum *penum = pxs->error_page_show_enum;
+ /* Normalize for a 10-point font. */
+#define point_size 10.0
+ double scale = 72.0 / px_bitmap_font_resolution *
+ point_size / px_bitmap_font_point_size;
+ int code = 0;
+
+ /* Peel off the next line and display it. */
+ for ( p = m; ; m = ++p )
+ { while ( *p != 0 && *p != '\n' )
+ ++p;
+ gs_moveto(pgs, 36.0, y);
+ gs_scale(pgs, scale, scale);
+ code = gs_show_n_init(penum, pgs, m, p - m);
+ if ( code >= 0 )
+ { code = gs_show_next(penum);
+ if ( code > 0 )
+ code = gs_note_error(errorBadFontData); /* shouldn't happen! */
+ }
+ if ( code < 0 )
+ gs_show_enum_release(penum, NULL);
+ gs_scale(pgs, 1 / scale, 1 / scale);
+ y += point_size * 8 / 5;
+ if ( !*p || !p[1] )
+ break;
+ }
+ return (code < 0 ? code : y);
+}
+
+/* Reset the warning table. */
+void
+px_reset_errors(px_state_t *pxs)
+{ pxs->error_line[0] = 0;
+ pxs->warning_length = 0;
+}
+
+/* ---------------- Error names ---------------- */
+
+#undef pxerrors_INCLUDED
+#define INCLUDE_ERROR_NAMES
+#include "pxerrors.h"
+#undef INCLUDE_ERROR_NAMES
diff --git a/pxl/pxerrors.h b/pxl/pxerrors.h
new file mode 100644
index 000000000..a3cad67bd
--- /dev/null
+++ b/pxl/pxerrors.h
@@ -0,0 +1,160 @@
+/* Copyright (C) 1996 Aladdin Enterprises. All rights reserved.
+ Unauthorized use, copying, and/or distribution prohibited.
+ */
+
+/* pxerrors.h */
+/* Error code definitions for PCL XL parser */
+
+#ifndef pxerrors_INCLUDED
+# define pxerrors_INCLUDED
+
+/* ---------------- Procedural interface ---------------- */
+
+#ifndef px_parser_state_t_DEFINED
+# define px_parser_state_t_DEFINED
+typedef struct px_parser_state_s px_parser_state_t;
+#endif
+
+#ifndef px_state_DEFINED
+# define px_state_DEFINED
+typedef struct px_state_s px_state_t;
+#endif
+
+/* Record a warning. */
+/* Return 1 if the warning table overflowed. */
+/* If save_all is false, only remember the last warning with the same */
+/* first word as this one. */
+int px_record_warning(P3(const char *message, bool save_all, px_state_t *pxs));
+
+/* Generate a line of an error message starting at internal position N; */
+/* return an updated value of N. When done, return -1. */
+#define px_max_error_line 120
+int px_error_message_line(P6(char message[px_max_error_line+1], int N,
+ const char *subsystem, int code, const px_parser_state_t *st,
+ const px_state_t *pxs));
+
+/* Begin an error page. Return the initial Y value. */
+int px_begin_error_page(P1(px_state_t *pxs));
+
+/* Print a message on an error page. */
+/* Return the updated Y value. */
+int px_error_page_show(P3(const char *message, int ytop, px_state_t *pxs));
+
+/* Reset the warning table. */
+void px_reset_errors(P1(px_state_t *pxs));
+
+/* ---------------- Error name table ---------------- */
+
+/*
+ * The following peculiar structure allows us to include this file wherever
+ * error code definitions are needed, and use the same file to generate the
+ * table of error names by setting INCLUDE_ERROR_NAMES.
+ */
+
+#undef _e_ /* in case we're including this file twice */
+
+# ifdef INCLUDE_ERROR_NAMES
+
+const char *px_error_names[] = {
+# define _e_(code, name) name,
+
+# else /* !INCLUDE_ERROR_NAMES */
+
+extern const char *px_error_names[];
+#define px_error_first -1000
+# define _e_(code,name) code,
+typedef enum {
+
+# endif /* (!)INCLUDE_ERROR_NAMES */
+
+ _e_(errorIllegalOperatorSequence = px_error_first, "IllegalOperatorSequence")
+ _e_(errorIllegalTag, "IllegalTag")
+ _e_(errorInsufficientMemory, "InsufficientMemory")
+ _e_(errorInternalOverflow, "InternalOverflow")
+
+ _e_(errorIllegalArraySize, "IllegalArraySize")
+ _e_(errorIllegalAttribute, "IllegalAttribute")
+ _e_(errorIllegalAttributeCombination, "IllegalAttributeCombination")
+ _e_(errorIllegalAttributeDataType, "IllegalAttributeDataType")
+ _e_(errorIllegalAttributeValue, "IllegalAttributeValue")
+ _e_(errorMissingAttribute, "MissingAttribute")
+
+ _e_(errorCurrentCursorUndefined, "CurrentCursorUndefined")
+
+ _e_(errorNoCurrentFont, "NoCurrentFont")
+ _e_(errorBadFontData, "BadFontData")
+
+ _e_(errorDataSourceNotOpen, "DataSourceNotOpen")
+ _e_(errorExtraData, "ExtraData")
+ _e_(errorIllegalDataLength, "IllegalDataLength")
+ _e_(errorIllegalDataValue, "IllegalDataValue")
+ _e_(errorMissingData, "MissingData")
+
+ _e_(errorCannotReplaceCharacter, "CannotReplaceCharacter")
+ _e_(errorFontUndefined, "FontUndefined")
+
+ _e_(errorFontNameAlreadyExists, "FontNameAlreadyExists")
+
+ _e_(errorImagePaletteMismatch, "ImagePaletteMismatch")
+ _e_(errorMissingPalette, "MissingPalette")
+
+ _e_(errorIllegalMediaSize, "IllegalMediaSize")
+ _e_(errorIllegalMediaSource, "IllegalMediaSource")
+ _e_(errorIllegalOrientation, "IllegalOrientation")
+
+ _e_(errorStreamUndefined, "StreamUndefined")
+
+ _e_(errorDataSourceNotClosed, "DataSourceNotClosed")
+
+ _e_(errorMaxGSLevelsExceeded, "MaxGSLevelsExceeded")
+
+ _e_(errorFSTMismatch, "FSTMismatch")
+ _e_(errorUnsupportedCharacterClass, "UnsupportedCharacterClass")
+ _e_(errorUnsupportedCharacterFormat, "UnsupportedCharacterFormat")
+ _e_(errorIllegalCharacterData, "IllegalCharacterData")
+
+ _e_(errorIllegalFontData, "IllegalFontData")
+ _e_(errorIllegalFontHeaderFields, "IllegalFontHeaderFields")
+ _e_(errorIllegalNullSegmentSize, "IllegalNullSegmentSize")
+ _e_(errorIllegalFontSegment, "IllegalFontSegment")
+ _e_(errorMissingRequiredSegment, "MissingRequiredSegment")
+ _e_(errorIllegalGlobalTrueTypeSegment, "IllegalGlobalTrueTypeSegment")
+ _e_(errorIllegalGalleyCharacterSegment, "IllegalGalleyCharacterSegment")
+ _e_(errorIllegalVerticalTxSegment, "IllegalVerticalTxSegment")
+ _e_(errorIllegalBitmapResolutionSegment, "IllegalBitmapResolutionSegment")
+
+ _e_(errorUndefinedFontNotRemoved, "UndefinedFontNotRemoved")
+ _e_(errorInternalFontNotRemoved, "InternalFontNotRemoved")
+ _e_(errorMassStorageFontNotRemoved, "MassStorageFontNotRemoved")
+
+ _e_(errorColorSpaceMismatch, "ColorSpaceMismatch")
+ _e_(errorRasterPatternUndefined, "RasterPatternUndefined")
+
+ _e_(errorClipModeMismatch, "ClipModeMismatch")
+
+ _e_(errorFontUndefinedNoSubstituteFound, "FontUndefinedNoSubstituteFound")
+ _e_(errorSymbolSetRemapUndefined, "SymbolSetRemapUndefined")
+
+ _e_(errorUnsupportedBinding, "UnsupportedBinding")
+ _e_(errorUnsupportedClassName, "UnsupportedClassName")
+ _e_(errorUnsupportedProtocol, "UnsupportedProtocol")
+ _e_(errorIllegalStreamHeader, "IllegalStreamHeader")
+
+ /* We define a special "error" that EndSession returns */
+ /* to indicate that there were warnings to report. */
+
+ _e_(errorWarningsReported, "WarningsReported")
+
+# ifdef INCLUDE_ERROR_NAMES
+
+ 0
+};
+
+# else /* !INCLUDE_ERROR_NAMES */
+
+ px_error_next
+} px_error_t;
+
+# endif /* (!)INCLUDE_ERROR_NAMES */
+
+#endif /* pxerrors_INCLUDED */
diff --git a/pxl/pxffont.c b/pxl/pxffont.c
new file mode 100644
index 000000000..ab91fa6af
--- /dev/null
+++ b/pxl/pxffont.c
@@ -0,0 +1,603 @@
+/* Copyright (C) 1996 Aladdin Enterprises. All rights reserved.
+ Unauthorized use, copying, and/or distribution prohibited.
+ */
+
+/* pxffont.c */
+/* PCL XL font-finding procedures */
+
+#include "string_.h"
+#include "gx.h"
+#include "gschar.h"
+#include "gsmatrix.h" /* for gsfont.h */
+#include "gxfont.h"
+#include "gxfont42.h"
+#include "pxoper.h"
+#include "pxstate.h"
+#include "pxfont.h"
+
+/*
+ * Define the structure and parameters for doing font substitution.
+ * Ideally, we should build in a few hundred well-known font names and
+ * use PANOSE numbers; this is a refinement for the future.
+ */
+typedef enum {
+ pxff_fixed = 1,
+ pxff_variable = 2,
+ pxff_serif = 4,
+ pxff_sans = 8,
+ pxff_upright = 0x10,
+ pxff_oblique = 0x20,
+ pxff_italic = 0x40,
+ pxff_plain = 0x80,
+ pxff_bold = 0x100,
+ pxff_narrow = 0x200,
+ pxff_symbolic = 0x400,
+ pxff_typeface = 0x0010000,
+ pxff_typeface_mask = 0xfff0000,
+ /* This is a pretty random selection of typefaces. */
+ /* It just happens to be the ones in the standard PostScript set */
+ /* plus the ones in the Windows set. */
+ pxffArial = pxff_typeface * 218,
+ pxffAvantGarde = pxff_typeface * 31,
+ pxffBookman = pxff_typeface * 402,
+ pxffCourier = pxff_typeface * 3,
+ pxffHelvetica = pxff_typeface * 4,
+ pxffNewCenturySchlbk = pxff_typeface * 23,
+ pxffPalatino = pxff_typeface * 15,
+ pxffSymbol = pxff_typeface * 302,
+ pxffTimes = pxff_typeface * 5,
+ pxffWingdings = pxff_typeface * 2730,
+ /* These are the remaining typefaces in the PCL XL base set. */
+ pxffAlbertus = pxff_typeface * 266,
+ pxffAntiqueOlive = pxff_typeface * 72,
+ pxffClarendon = pxff_typeface * 44,
+ pxffCoronet = pxff_typeface * 20,
+ pxffGaramond = pxff_typeface * 18,
+ pxffLetterGothic = pxff_typeface * 6,
+ pxffLinePrinter = pxff_typeface * 0,
+ pxffMarigold = pxff_typeface * 201,
+ pxffUnivers = pxff_typeface * 52,
+ /* These two are not in the H-P catalog. */
+ pxffCGOmega = pxffGaramond,
+ pxffCGTimes = pxffTimes
+} px_font_flags_t;
+#define pxff_spacing (pxff_fixed | pxff_variable)
+#define pxff_serifs (pxff_serif | pxff_sans)
+#define pxff_posture (pxff_upright | pxff_oblique)
+#define pxff_weight (pxff_plain | pxff_bold)
+#define pxff_width (pxff_narrow)
+#define pxff_all_properties ((px_font_flags_t)0xffff)
+#define pxff_all (pxff_all_properties | pxff_typeface_mask)
+typedef struct px_font_desc_s {
+ /* All font names are in Unicode. See pxfont.h. */
+ char16 fname[17];
+ px_font_flags_t flags;
+} px_font_desc_t;
+
+/*
+ * Here we list the 45 built-in LaserJet 5 and 6 fonts. For host-based
+ * testing, we also provide the file names of the corresponding Windows
+ * TrueType fonts if available.
+ */
+typedef struct px_stored_font_s {
+ px_font_desc_t descriptor;
+ const char *file_name;
+} px_stored_font_t;
+private const char *known_font_file_prefixes[] = {
+ "/windows/system/", "/windows/fonts/", "/win95/fonts/", 0
+};
+private const px_stored_font_t known_fonts[] = {
+ {{{'A','l','b','e','r','t','u','s',' ',' ',' ',' ',' ',' ','X','b'},
+ pxffAlbertus | pxff_variable | pxff_sans | pxff_upright | pxff_bold},
+ 0},
+ {{{'A','l','b','e','r','t','u','s',' ',' ',' ',' ',' ',' ','M','d'},
+ pxffAlbertus | pxff_variable | pxff_sans | pxff_upright | pxff_plain},
+ 0},
+ {{{'A','n','t','i','q','O','l','i','v','e',' ',' ',' ',' ',' ',' '},
+ pxffAntiqueOlive | pxff_variable | pxff_sans | pxff_upright | pxff_plain},
+ 0},
+ {{{'A','n','t','i','q','O','l','i','v','e',' ',' ',' ',' ','B','d'},
+ pxffAntiqueOlive | pxff_variable | pxff_sans | pxff_upright | pxff_bold},
+ 0},
+ {{{'A','n','t','i','q','O','l','i','v','e',' ',' ',' ',' ','I','t'},
+ pxffAntiqueOlive | pxff_variable | pxff_sans | pxff_oblique | pxff_plain},
+ 0},
+ {{{'A','r','i','a','l',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '},
+ pxffArial | pxff_variable | pxff_sans | pxff_upright | pxff_plain},
+ "arial.ttf"},
+ {{{'A','r','i','a','l',' ',' ',' ',' ',' ',' ',' ',' ',' ','B','d'},
+ pxffArial | pxff_variable | pxff_sans | pxff_upright | pxff_bold},
+ "arialbd.ttf"},
+ {{{'A','r','i','a','l',' ',' ',' ',' ',' ',' ',' ','B','d','I','t'},
+ pxffArial | pxff_variable | pxff_sans | pxff_oblique | pxff_bold},
+ "arialbi.ttf"},
+ {{{'A','r','i','a','l',' ',' ',' ',' ',' ',' ',' ',' ',' ','I','t'},
+ pxffArial | pxff_variable | pxff_sans | pxff_oblique | pxff_plain},
+ "ariali.ttf"},
+ {{{'C','G',' ','O','m','e','g','a',' ',' ',' ',' ',' ',' ',' ',' '},
+ pxffCGOmega | pxff_variable | pxff_sans | pxff_upright | pxff_plain},
+ 0},
+ {{{'C','G',' ','O','m','e','g','a',' ',' ',' ',' ',' ',' ','B','d'},
+ pxffCGOmega | pxff_variable | pxff_sans | pxff_upright | pxff_bold},
+ 0},
+ {{{'C','G',' ','O','m','e','g','a',' ',' ',' ',' ','B','d','I','t'},
+ pxffCGOmega | pxff_variable | pxff_sans | pxff_oblique | pxff_bold},
+ 0},
+ {{{'C','G',' ','O','m','e','g','a',' ',' ',' ',' ',' ',' ','I','t'},
+ pxffCGOmega | pxff_variable | pxff_sans | pxff_oblique | pxff_plain},
+ 0},
+ {{{'C','G',' ','T','i','m','e','s',' ',' ',' ',' ',' ',' ',' ',' '},
+ pxffCGTimes | pxff_variable | pxff_serif | pxff_upright | pxff_plain},
+ 0},
+ {{{'C','G',' ','T','i','m','e','s',' ',' ',' ',' ',' ',' ','B','d'},
+ pxffCGTimes | pxff_variable | pxff_serif | pxff_upright | pxff_bold},
+ 0},
+ {{{'C','G',' ','T','i','m','e','s',' ',' ',' ',' ','B','d','I','t'},
+ pxffCGTimes | pxff_variable | pxff_serif | pxff_oblique | pxff_italic | pxff_bold},
+ 0},
+ {{{'C','G',' ','T','i','m','e','s',' ',' ',' ',' ',' ',' ','I','t'},
+ pxffCGTimes | pxff_variable | pxff_serif | pxff_oblique | pxff_italic | pxff_plain},
+ 0},
+ {{{'C','l','a','r','e','n','d','o','n',' ',' ',' ','C','d','B','d'},
+ pxffClarendon | pxff_variable | pxff_serif | pxff_upright | pxff_bold},
+ 0},
+ {{{'C','o','r','o','n','e','t',' ',' ',' ',' ',' ',' ',' ',' ',' '},
+ pxffCoronet | pxff_variable | pxff_serif | pxff_oblique | pxff_italic | pxff_plain},
+ 0},
+ {{{'C','o','u','r','i','e','r',' ',' ',' ',' ',' ',' ',' ',' ',' '},
+ pxffCourier | pxff_fixed | pxff_serif | pxff_upright | pxff_plain},
+ "cour.ttf"},
+ {{{'C','o','u','r','i','e','r',' ',' ',' ',' ',' ',' ',' ','B','d'},
+ pxffCourier | pxff_fixed | pxff_serif | pxff_upright | pxff_bold},
+ "courbd.ttf"},
+ {{{'C','o','u','r','i','e','r',' ',' ',' ',' ',' ',' ',' ','I','t'},
+ pxffCourier | pxff_fixed | pxff_serif | pxff_oblique | pxff_plain},
+ "couri.ttf"},
+ {{{'C','o','u','r','i','e','r',' ',' ',' ',' ',' ','B','d','I','t'},
+ pxffCourier | pxff_fixed | pxff_serif | pxff_oblique | pxff_bold},
+ "courbi.ttf"},
+ {{{'G','a','r','a','m','o','n','d',' ','A','n','t','i','q','u','a'},
+ pxffGaramond | pxff_variable | pxff_serif | pxff_upright | pxff_plain},
+ 0},
+ {{{'G','a','r','a','m','o','n','d',' ',' ',' ',' ',' ','H','l','b'},
+ pxffGaramond | pxff_variable | pxff_serif | pxff_upright | pxff_bold},
+ 0},
+ {{{'G','a','r','a','m','o','n','d',' ',' ',' ',' ','K','r','s','v'},
+ pxffGaramond | pxff_variable | pxff_serif | pxff_oblique | pxff_italic | pxff_plain},
+ 0},
+ {{{'G','a','r','a','m','o','n','d',' ','K','r','s','v','H','l','b'},
+ pxffGaramond | pxff_variable | pxff_serif | pxff_oblique | pxff_italic | pxff_bold},
+ 0},
+ {{{'L','e','t','t','e','r','G','o','t','h','i','c',' ',' ',' ',' '},
+ pxffLetterGothic | pxff_fixed | pxff_sans | pxff_upright | pxff_plain},
+ 0},
+ {{{'L','e','t','t','e','r','G','o','t','h','i','c',' ',' ','B','d'},
+ pxffLetterGothic | pxff_fixed | pxff_sans | pxff_upright | pxff_bold},
+ 0},
+ {{{'L','e','t','t','e','r','G','o','t','h','i','c',' ',' ','I','t'},
+ pxffLetterGothic | pxff_fixed | pxff_sans | pxff_oblique | pxff_plain},
+ 0},
+ /* The line printer fonts are identical except for symbol set. */
+#define LinePrinter(ss1,ss2,ss3)\
+ {{{'L','i','n','e',' ','P','r','i','n','t','e','r',' ',ss1,ss2,ss3},\
+ pxffLinePrinter | pxff_fixed | pxff_serif | pxff_upright | pxff_plain},\
+ 0}
+ LinePrinter(' ', '0', 'N'),
+ LinePrinter('1', '0', 'U'),
+ LinePrinter('1', '1', 'U'),
+ LinePrinter('1', '2', 'U'),
+ LinePrinter(' ', '1', 'U'),
+ LinePrinter(' ', '2', 'N'),
+ LinePrinter(' ', '5', 'N'),
+ LinePrinter(' ', '8', 'U'),
+#undef LinePrinter
+ {{{'M','a','r','i','g','o','l','d',' ',' ',' ',' ',' ',' ',' ',' '},
+ pxffMarigold | pxff_variable | pxff_serif | pxff_oblique | pxff_italic | pxff_plain},
+ 0},
+ {{{'S','y','m','b','o','l',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '},
+ pxffSymbol | pxff_variable | pxff_upright | pxff_symbolic},
+ "symbol.ttf"},
+ {{{'T','i','m','e','s','N','e','w','R','m','n',' ',' ',' ',' ',' '},
+ pxffTimes | pxff_variable | pxff_serif | pxff_upright | pxff_plain},
+ "times.ttf"},
+ {{{'T','i','m','e','s','N','e','w','R','m','n',' ',' ',' ','B','d'},
+ pxffTimes | pxff_variable | pxff_serif | pxff_upright | pxff_bold},
+ "timesbd.ttf"},
+ {{{'T','i','m','e','s','N','e','w','R','m','n',' ','B','d','I','t'},
+ pxffTimes | pxff_variable | pxff_serif | pxff_oblique | pxff_italic | pxff_bold},
+ "timesbi.ttf"},
+ {{{'T','i','m','e','s','N','e','w','R','m','n',' ',' ',' ','I','t'},
+ pxffTimes | pxff_variable | pxff_serif | pxff_oblique | pxff_italic | pxff_plain},
+ "timesi.ttf"},
+ {{{'U','n','i','v','e','r','s',' ',' ',' ',' ',' ',' ',' ','B','d'},
+ pxffUnivers | pxff_variable | pxff_sans | pxff_upright | pxff_bold},
+ 0},
+ {{{'U','n','i','v','e','r','s',' ',' ',' ','C','d','B','d','I','t'},
+ pxffUnivers | pxff_variable | pxff_sans | pxff_oblique | pxff_bold | pxff_narrow},
+ 0},
+ {{{'U','n','i','v','e','r','s',' ',' ',' ',' ',' ','B','d','I','t'},
+ pxffUnivers | pxff_variable | pxff_sans | pxff_oblique | pxff_bold},
+ 0},
+ {{{'U','n','i','v','e','r','s',' ',' ',' ',' ',' ',' ',' ','M','d'},
+ pxffUnivers | pxff_variable | pxff_sans | pxff_upright | pxff_plain},
+ 0},
+ {{{'U','n','i','v','e','r','s',' ',' ',' ',' ',' ','C','d','M','d'},
+ pxffUnivers | pxff_variable | pxff_sans | pxff_upright | pxff_plain | pxff_narrow},
+ 0},
+ {{{'U','n','i','v','e','r','s',' ',' ',' ','C','d','M','d','I','t'},
+ pxffUnivers | pxff_variable | pxff_sans | pxff_oblique | pxff_plain | pxff_narrow},
+ 0},
+ {{{'U','n','i','v','e','r','s',' ',' ',' ',' ',' ','M','d','I','t'},
+ pxffUnivers | pxff_variable | pxff_sans | pxff_oblique | pxff_plain},
+ 0},
+ {{{'W','i','n','g','d','i','n','g','s',' ',' ',' ',' ',' ',' ',' '},
+ pxffWingdings | pxff_variable | pxff_upright | pxff_symbolic},
+ "wingding.ttf"}
+};
+#define num_known_fonts countof(known_fonts)
+const uint px_num_known_fonts = num_known_fonts;
+
+/*
+ * Define some font name substrings that give clues about appropriate
+ * substitutions.
+ */
+#define ffAvantGarde (pxffAvantGarde | pxff_serif)
+#define ffBookman (pxffBookman | pxff_serif)
+#define ffCourier (pxffCourier | pxff_fixed)
+#define ffHelvetica (pxffHelvetica | pxff_sans)
+#define ffNewCenturySchlbk (pxffNewCenturySchlbk | pxff_serif)
+#define ffPalatino (pxffPalatino | pxff_serif)
+#define ffTimes (pxffTimes | pxff_serif)
+private const px_font_desc_t subst_font_families[] = {
+ /* Guess at suitable substitutions for random unknown fonts. */
+ {{'G','o','t','h'}, ffTimes},
+ {{'G','r','o','t'}, ffTimes},
+ {{'R','o','m','a','n'}, ffTimes},
+ {{'B','o','o','k'}, ffNewCenturySchlbk},
+ /* If the family name appears in the font name, */
+ /* use a font from that family. */
+ {{'A','l','b','e','r','t','u','s'}, ffHelvetica},
+ {{'A','r','i','a','l'}, ffHelvetica},
+ {{'A','v','a','n','t'}, ffAvantGarde},
+ {{'B','o','d','o','n','i'}, ffBookman},
+ {{'B','o','o','k','m','a','n'}, ffBookman},
+ {{'C','e','n','t','u','r','y'}, ffNewCenturySchlbk},
+ {{'C','l','a','r','e','n','d','o','n'}, ffTimes},
+ {{'C','o','u','r'}, ffCourier},
+ {{'G','e','n','e','v','a'}, ffHelvetica},
+ {{'G','o','u','d','y'}, ffNewCenturySchlbk},
+ {{'G','r','a','p','h','o','s'}, ffCourier},
+ {{'H','e','l','v'}, ffHelvetica},
+ {{'M','e','t','r','o','s','t','y','l','e'}, ffHelvetica},
+ {{'N','e','w','Y','o','r','k'}, ffTimes},
+ {{'P','a','l'}, ffPalatino},
+ {{'R','o','m'}, ffTimes},
+ {{'S','a','n'}, ffHelvetica},
+ {{'S','c','h','l','b','k'}, ffNewCenturySchlbk},
+ {{'S','e','r','i','f'}, ffTimes},
+ {{'S','o','r','t','s'}, pxff_symbolic},
+ {{'S','w','i','s','s'}, ffHelvetica},
+ {{'T','i','m','e','s'}, ffTimes},
+ {{'U','n','i','v','e','r','s'}, ffHelvetica},
+ /* Substitute for Adobe Multiple Master fonts. */
+ {{'M','y','r','i','a','d'}, ffTimes},
+ {{'M','i','n','i','o','n'}, ffHelvetica},
+};
+private const px_font_desc_t subst_font_attributes[] = {
+ {{'M','o','n'}, pxff_fixed},
+ {{'T','y','p','e','w','r','i','t','e','r'}, pxff_fixed},
+ {{'I','t'}, pxff_italic},
+ {{'O','b','l','i','q','u','e'}, pxff_oblique},
+ {{'B','d'}, pxff_bold},
+ {{'B','o','l','d'}, pxff_bold},
+ {{'b','o','l','d'}, pxff_bold},
+ {{'D','e','m','i'}, pxff_bold},
+ {{'H','e','a','v','y'}, pxff_bold},
+ {{'S','b'}, pxff_bold},
+ {{'X','b'}, pxff_bold},
+ {{'C','d'}, pxff_narrow},
+ {{'C','o','n','d'}, pxff_narrow},
+ {{'N','a','r','r','o','w'}, pxff_narrow},
+};
+
+/* All the font-related structures are big-endian, so: */
+#define u16(ptr) uint16at(ptr, true)
+#define s16(ptr) sint16at(ptr, true)
+#define u32(ptr) uint32at(ptr, true)
+
+#if arch_is_big_endian
+# define pxd_native_byte_order pxd_big_endian
+#else
+# define pxd_native_byte_order 0
+#endif
+
+/* ---------------- Operator utilities ---------------- */
+
+/* Compute the length of a Unicode string. */
+private uint
+ustrlen(const char16 *ustr)
+{ uint i;
+ for ( i = 0; ustr[i]; ++i ) ;
+ return i;
+}
+
+/* Search for a Unicode string in another Unicode string. */
+private const char16 *
+ustrstr(const char16 *str, uint strlen, const char16 *pattern, uint patlen)
+{ uint i;
+
+ for ( i = 0; i + patlen <= strlen; ++i )
+ if ( !memcmp(str + i, pattern, patlen * sizeof(char16)) )
+ return (const char16 *)(str + i);
+ return 0;
+}
+
+/* Widen and/or byte-swap a font name to Unicode if needed. */
+/* The argument is a ubyte or uint16 array; the result is a uint16 array */
+/* with the elements in native byte order. */
+/* We don't deal with mappings: we just widen 8-bit to 16-bit characters */
+/* and hope for the best.... */
+private int
+px_widen_font_name(px_value_t *pfnv, px_state_t *pxs)
+{ uint type = pfnv->type;
+
+ if ( (type & (pxd_uint16 | pxd_big_endian)) ==
+ (pxd_uint16 | pxd_native_byte_order)
+ )
+ return 0; /* already in correct format */
+ { byte *old_data = (byte *)pfnv->value.array.data; /* break const */
+ uint size = pfnv->value.array.size;
+ char16 *new_data;
+ uint i;
+
+ if ( type & pxd_on_heap )
+ old_data = (byte *)
+ (new_data =
+ (char16 *)gs_resize_object(pxs->memory, old_data,
+ size * 2, "px_widen_font_name"));
+ else
+ new_data =
+ (char16 *)gs_alloc_byte_array(pxs->memory, size, sizeof(char16),
+ "px_widen_font_name");
+ if ( new_data == 0 )
+ return_error(errorInsufficientMemory);
+ for ( i = size; i; )
+ { --i;
+ new_data[i] =
+ (type & pxd_ubyte ? old_data[i] :
+ uint16at(old_data + i * 2, type & pxd_big_endian));
+ }
+ pfnv->value.array.data = (byte *)new_data;
+ }
+ pfnv->type = (type & ~(pxd_ubyte | pxd_big_endian)) |
+ (pxd_uint16 | pxd_native_byte_order | pxd_on_heap);
+ return 0;
+}
+
+/* ---------------- Operator implementations ---------------- */
+
+/* Open the file for a known font. */
+private FILE *
+px_open_font_file(const char *fname)
+{ const char **pprefix = known_font_file_prefixes;
+
+ for ( ; *pprefix; ++pprefix )
+ { char file_name[80];
+ FILE *in;
+
+ strcpy(file_name, *pprefix);
+ strcat(file_name, fname);
+ in = fopen(file_name, "rb");
+ if ( in )
+ return in;
+ in = fopen(file_name, "r");
+ if ( in )
+ return in;
+ }
+ return 0;
+}
+
+/* Look up a font name and return its description, if known. */
+/* The font name must already have been widened to Unicode. */
+private const px_stored_font_t *
+px_find_known_font(px_value_t *pfnv, px_state_t *pxs)
+{ uint i;
+
+ if ( pfnv->value.array.size != 16 )
+ return 0;
+ for ( i = 0; i < num_known_fonts; ++i )
+ if ( !memcmp(known_fonts[i].descriptor.fname,
+ pfnv->value.array.data, 16 * sizeof(char16))
+ )
+ return &known_fonts[i];
+ return 0;
+}
+
+/* Look up a font name and return an existing font. */
+/* This procedure may widen and/or byte-swap the font name. */
+/* If this font is supposed to be built in but no .TTF file is available, */
+/* or if loading the font fails, return >= 0 and store 0 in *ppxfont. */
+int
+px_find_existing_font(px_value_t *pfnv, px_font_t **ppxfont,
+ px_state_t *pxs)
+{ int code;
+
+ /* Normalize the font name to Unicode. */
+ code = px_widen_font_name(pfnv, pxs);
+ if ( code < 0 )
+ return code;
+
+ /* Check if we know the font already. */
+ { void *pxfont;
+
+ if ( px_dict_find(&pxs->font_dict, pfnv, &pxfont) )
+ { /* Make sure this isn't a partially defined font */
+ /* in the process of being downloaded. */
+ if ( ((px_font_t *)pxfont)->pfont )
+ { *ppxfont = pxfont;
+ return 0;
+ }
+ }
+ }
+
+ /* Check for a built-in font. */
+ { const px_stored_font_t *psf = px_find_known_font(pfnv, pxs);
+
+ if ( psf == 0 )
+ return_error(errorFontUndefined);
+ *ppxfont = 0; /* in case of later failure */
+ if ( psf->file_name == 0 )
+ return 0;
+
+ { /* Load the TrueType font data. */
+ FILE *in = px_open_font_file(psf->file_name);
+ px_font_t *pxfont;
+
+ if ( in == 0 )
+ return 0; /* TrueType font not available */
+ code =
+ pl_load_tt_font(in, pxs->font_dir, pxs->memory,
+ (psf - known_fonts + pxs->known_fonts_base_id),
+ &pxfont);
+ if ( code >= 0 )
+ { pxfont->storage = pxfsInternal;
+ pxfont->params.symbol_set = 0; /* not known */
+ /* The character complements are bogus.... */
+ memcpy(pxfont->character_complement,
+ (psf->descriptor.flags & pxff_symbolic ?
+ "\377\377\377\377\377\377\377\376" :
+ "\377\377\377\377\000\000\000\006"),
+ 8);
+ code = px_dict_put(&pxs->font_dict, pfnv, pxfont);
+ if ( code >= 0 )
+ *ppxfont = pxfont;
+ }
+ }
+ }
+ return code;
+}
+
+/* Look for a partial match with a known font. */
+private int
+px_find_subst_font(px_font_flags_t flags, px_font_flags_t mask,
+ px_font_t **ppxfont, const char16 **psfname, px_state_t *pxs)
+{ uint i;
+
+ for ( i = 0; i < num_known_fonts; ++i )
+ { px_font_flags_t known_flags = known_fonts[i].descriptor.flags;
+ /* Require an exact match on typeface, but only inclusion */
+ /* on properties. */
+ if ( !((known_flags ^ flags) & pxff_typeface_mask & mask) &&
+ !(flags & ~known_flags & pxff_all_properties & mask)
+ )
+ { const char16 *sfname = known_fonts[i].descriptor.fname;
+ px_value_t fnv;
+ int code;
+
+ fnv.type = pxd_array | pxd_uint16 | pxd_native_byte_order;
+ fnv.value.array.data = (const byte *)sfname;
+ fnv.value.array.size = ustrlen(sfname);
+ code = px_find_existing_font(&fnv, ppxfont, pxs);
+ if ( code < 0 || *ppxfont == 0 )
+ continue;
+ *psfname = sfname;
+ return 0;
+ }
+ }
+ return_error(errorFontUndefinedNoSubstituteFound);
+}
+
+/* Look up a font name and return a font, possibly with substitution. */
+/* This procedure implements most of the SetFont operator. */
+/* This procedure may widen and/or byte-swap the font name. */
+int
+px_find_font(px_value_t *pfnv, uint symbol_set, px_font_t **ppxfont,
+ px_state_t *pxs)
+{ int code;
+ bool built_in;
+
+ /* Check if we know the font already. */
+ /* Note that px_find_existing_font normalizes the font name. */
+
+ code = px_find_existing_font(pfnv, ppxfont, pxs);
+ if ( code >= 0 )
+ { if ( *ppxfont != 0 )
+ return code;
+ built_in = true;
+ }
+ else if ( code == errorFontUndefined )
+ built_in = false;
+ else
+ return code;
+
+ /* If we're asking for an unknown symbol set, give up. */
+ { const pl_symbol_map_t **pmap = pl_built_in_symbol_maps;
+ const pl_symbol_map_t *map;
+
+ for ( ; (map = *pmap) != 0; ++pmap )
+ { uint map_set = (map->id[0] << 8) + map->id[1];
+ if ( map_set == symbol_set )
+ break;
+ }
+ if ( map == 0 )
+ return_error(built_in ? errorSymbolSetRemapUndefined :
+ errorFontUndefinedNoSubstituteFound);
+ }
+
+ /* Try to find a substitute font. */
+
+ { const char16 *fname = (const char16 *)pfnv->value.array.data;
+ uint fnsize = pfnv->value.array.size;
+ px_font_flags_t flags = 0;
+ const char16 *sfname;
+ int i;
+
+ if ( built_in )
+ flags = px_find_known_font(pfnv, pxs)->descriptor.flags;
+ else
+ { for ( i = 0; i < countof(subst_font_families); ++i )
+ { const char16 *pattern = subst_font_families[i].fname;
+ if ( ustrstr(fname, fnsize, pattern, ustrlen(pattern)) )
+ flags = subst_font_families[i].flags;
+ }
+ for ( i = 0; i < countof(subst_font_attributes); ++i )
+ { const char16 *pattern = subst_font_attributes[i].fname;
+ if ( ustrstr(fname, fnsize, pattern, ustrlen(pattern)) )
+ flags |= subst_font_attributes[i].flags;
+ }
+ }
+ if ( flags == 0 )
+ { /* This is a completely unknown font. Substitute Courier. */
+ flags = pxffCourier | pxff_fixed | pxff_serif | pxff_upright | pxff_plain;
+ }
+ if ( (code = px_find_subst_font(flags, pxff_all, ppxfont, &sfname, pxs)) >= 0 ||
+ (code = px_find_subst_font(flags, pxff_all_properties, ppxfont, &sfname, pxs)) >= 0 ||
+#define mask (pxff_serifs | pxff_posture | pxff_weight | pxff_symbolic)
+ (code = px_find_subst_font(flags, mask, ppxfont, &sfname, pxs)) >= 0 ||
+#undef mask
+#define mask (pxff_symbolic)
+ (code = px_find_subst_font(flags, mask, ppxfont, &sfname, pxs)) >= 0 ||
+#undef mask
+ (code = px_find_subst_font(0, 0, ppxfont, &sfname, pxs)) >= 0
+ )
+ { if ( code >= 0 && !built_in )
+ { /* Issue a warning. */
+ px_value_t sfnv;
+ static const char *mid_message = " substituted for ";
+ char message[px_max_error_line + 1];
+
+ message[0] = 0;
+ sfnv.type = pxd_array | pxd_uint16 | pxd_native_byte_order;
+ sfnv.value.array.data = (byte *)sfname; /* break const, but not really */
+ sfnv.value.array.size = ustrlen(sfname);
+ px_concat_font_name(message, px_max_error_line, &sfnv);
+ strncat(message, mid_message,
+ px_max_error_line - strlen(message));
+ message[px_max_error_line] = 0;
+ px_concat_font_name(message, px_max_error_line, pfnv);
+ code = px_record_warning(message, true, pxs);
+ }
+ }
+ return code;
+ }
+}
diff --git a/pxl/pxfont.c b/pxl/pxfont.c
new file mode 100644
index 000000000..b0b74e545
--- /dev/null
+++ b/pxl/pxfont.c
@@ -0,0 +1,758 @@
+/* Copyright (C) 1996, 1997 Aladdin Enterprises. All rights reserved.
+ Unauthorized use, copying, and/or distribution prohibited.
+ */
+
+/* pxfont.c */
+/* PCL XL font operators */
+
+#include "math_.h"
+#include "stdio_.h"
+#include "string_.h"
+#include "gdebug.h"
+#include "plvalue.h"
+#include "pxoper.h"
+#include "pxstate.h"
+#include "pxfont.h"
+#include "gserrors.h"
+#include "gsstruct.h"
+#include "gschar.h"
+#include "gspaint.h"
+#include "gspath.h"
+#include "gsstate.h"
+#include "gscoord.h"
+#include "gsimage.h"
+#include "gsutil.h" /* for string_match */
+#include "gxfont.h"
+#include "gxfont42.h"
+/* We really shouldn't need the following, but currently they are needed */
+/* for pgs->path and penum->log2_current_scale in px_build_char. */
+#include "gxfixed.h"
+#include "gxchar.h"
+#include "gxpath.h"
+#include "gzstate.h"
+
+/*
+ * There appears to be a combination of undocumented behavior and
+ * firmware bugs in the H-P printers that make it difficult to determine
+ * the proper interaction of character and page transformations.
+ * The following two conditionals probably should not be enabled,
+ * but we leave them here just in case this turns out to be wrong.
+ */
+/*#define TRANSFORM_TEXT_AS_OBJECTS*/
+/*#define POST_TRANSFORM_CHARS*/
+
+/* ---------------- Initialization ---------------- */
+
+int
+pxfont_init(px_state_t *pxs)
+{ /* Allocate the font directory now. */
+ pxs->font_dir = gs_font_dir_alloc(pxs->memory);
+ if ( pxs->font_dir == 0 )
+ return_error(errorInsufficientMemory);
+ return 0;
+}
+
+/* ---------------- Operator utilities ---------------- */
+
+/* Compute the symbol map from the font and symbol set. */
+private void
+set_symbol_map(px_state_t *pxs)
+{ px_gstate_t *pxgs = pxs->pxgs;
+ px_font_t *pxfont = pxgs->base_font;
+ uint symbol_set = pxgs->symbol_set;
+
+ if ( symbol_set == pxfont->params.symbol_set )
+ { /* Exact match, no mapping required. */
+ pxgs->symbol_map = 0;
+ }
+ else if ( pl_font_is_bound(pxfont) )
+ { /****** CAN'T REMAP YET ******/
+ pxgs->symbol_map = 0;
+ }
+ else
+ { /* See if we know about the requested symbol set. */
+ /****** ASSUME UNICODE ******/
+ const pl_symbol_map_t **ppsm = pl_built_in_symbol_maps;
+
+ while ( *ppsm != 0 && pl_get_uint16((*ppsm)->id) != symbol_set )
+ ++ppsm;
+ /* If we didn't find it, default to Roman-8. */
+ pxgs->symbol_map = (*ppsm ? *ppsm : pl_built_in_symbol_maps[0]);
+ }
+}
+
+/* Recompute the combined character matrix from character and font */
+/* parameters. Note that this depends on the current font. */
+private double
+adjust_scale(double scale)
+{ double int_scale = floor(scale + 0.5);
+ double diff = scale - int_scale;
+
+ /* If the scale is very close to an integer, make it integral, */
+ /* so we won't have to scale the bitmap (or can scale it fast). */
+ return (diff < 0.001 && diff > -0.001 ? int_scale : scale);
+}
+private int
+px_set_char_matrix(px_state_t *pxs)
+{ px_gstate_t *pxgs = pxs->pxgs;
+ px_font_t *pxfont = pxgs->base_font;
+ gs_matrix mat;
+
+ if ( pxfont == 0 )
+ return_error(errorNoCurrentFont);
+ if ( pxfont->scaling_technology == plfst_bitmap )
+ { /*
+ * Bitmaps don't scale, shear, or rotate; however, we have to
+ * scale them to make the resolution match that of the device.
+ * Note that we disregard the character size, and, in px_text,
+ * all but the translation and orientation components of the
+ * CTM.
+ */
+ if ( pxgs->char_angle != 0 ||
+ pxgs->char_shear.x != 0 || pxgs->char_shear.y != 0 ||
+ pxgs->char_scale.x != 1 || pxgs->char_scale.y != 1
+ )
+ return_error(errorIllegalFontData);
+ gs_defaultmatrix(pxs->pgs, &mat);
+ gs_make_scaling(
+ adjust_scale(fabs(mat.xx + mat.yx) * 72 / pxfont->resolution.x),
+ adjust_scale(fabs(mat.yy + mat.xy) * 72 / pxfont->resolution.y),
+ &mat);
+ /*
+ * Rotate the bitmap to undo the effect of its built-in
+ * orientation.
+ */
+ gs_matrix_rotate(&mat, pxfont->header[1] * 90, &mat);
+ }
+ else
+ { float char_size = pxgs->char_size;
+ int i;
+
+ gs_make_identity(&mat);
+ /* H-P and Microsoft have Y coordinates running opposite ways. */
+ gs_matrix_scale(&mat, char_size, -char_size, &mat);
+ /* Apply the character transformations in the reverse order. */
+ for ( i = 0; i < 3; ++i )
+ switch ( pxgs->char_transforms[i] )
+ {
+ case pxct_rotate:
+ if ( pxgs->char_angle != 0 )
+ gs_matrix_rotate(&mat, pxgs->char_angle, &mat);
+ break;
+ case pxct_shear:
+ if ( pxgs->char_shear.x != 0 || pxgs->char_shear.y != 0 )
+ { gs_matrix smat;
+ gs_make_identity(&smat);
+ smat.yx = pxgs->char_shear.x;
+ smat.xy = pxgs->char_shear.y;
+ gs_matrix_multiply(&smat, &mat, &mat);
+ }
+ break;
+ case pxct_scale:
+ if ( pxgs->char_scale.x != 1 || pxgs->char_scale.y != 1 )
+ gs_matrix_scale(&mat, pxgs->char_scale.x,
+ pxgs->char_scale.y, &mat);
+ break;
+ }
+ }
+ pxgs->char_matrix = mat;
+ pxgs->char_matrix_set = true;
+ return 0;
+}
+
+/* ---------------- Operator implementations ---------------- */
+
+/* Define a font. The caller must fill in pxfont->storage and ->font_type. */
+/* This procedure implements FontHeader loading; it is exported for */
+/* initializing the error page font. */
+int
+px_define_font(px_font_t *pxfont, const byte *header, ulong size, gs_id id,
+ px_state_t *pxs)
+{ gs_memory_t *mem = pxs->memory;
+ uint num_chars;
+
+ /* Check for a valid font. */
+ if ( size < 8 /* header */ + 6 /* 1 required segment */ +
+ 6 /* NULL segment */
+ )
+ return_error(errorIllegalFontData);
+ if ( header[0] != 0 /* format */ ||
+ header[5] != 0 /* variety */
+ )
+ return_error(errorIllegalFontHeaderFields);
+ pxfont->header = header;
+ pxfont->header_size = size;
+ { static const pl_font_offset_errors_t errors = {
+ errorIllegalFontData,
+ errorIllegalFontSegment,
+ errorIllegalFontHeaderFields,
+ errorIllegalNullSegmentSize,
+ errorMissingRequiredSegment,
+ errorIllegalGlobalTrueTypeSegment,
+ errorIllegalGalleyCharacterSegment,
+ errorIllegalVerticalTxSegment,
+ errorIllegalBitmapResolutionSegment
+ };
+ int code = pl_font_scan_segments(pxfont, 4, 8, size, true, &errors);
+
+ if ( code < 0 )
+ return code;
+ }
+ num_chars = pl_get_uint16(header + 6);
+ /* Allocate the character table. */
+ { /* Some fonts ask for unreasonably large tables.... */
+ int code = pl_font_alloc_glyph_table(pxfont, min(num_chars, 300),
+ mem, "px_define_font(glyphs)");
+ if ( code < 0 )
+ return code;
+ }
+ /* Now construct a gs_font. */
+ if ( pxfont->scaling_technology == plfst_bitmap )
+ { /* Bitmap font. */
+ gs_font_base *pfont =
+ gs_alloc_struct(mem, gs_font_base, &st_gs_font_base,
+ "px_define_font(gs_font_base)");
+ int code;
+
+ if ( pfont == 0 )
+ return_error(errorInsufficientMemory);
+ code = px_fill_in_font((gs_font *)pfont, pxfont, pxs);
+ if ( code < 0 )
+ return code;
+ pl_fill_in_bitmap_font(pfont, id);
+ }
+ else
+ { /* TrueType font. */
+ gs_font_type42 *pfont =
+ gs_alloc_struct(mem, gs_font_type42, &st_gs_font_type42,
+ "px_define_font(gs_font_type42)");
+ int code;
+
+ if ( pfont == 0 )
+ return_error(errorInsufficientMemory);
+ /* Some fonts ask for unreasonably large tables.... */
+ code = pl_tt_alloc_char_glyphs(pxfont, min(num_chars, 300), mem,
+ "px_define_font(char_glyphs)");
+ if ( code < 0 )
+ return code;
+ code = px_fill_in_font((gs_font *)pfont, pxfont, pxs);
+ if ( code < 0 )
+ return code;
+ pl_fill_in_tt_font(pfont, NULL, id);
+ }
+ pxfont->params.symbol_set = pl_get_uint16(header + 2);
+ return gs_definefont(pxs->font_dir, pxfont->pfont);
+}
+
+/* Concatenate a widened (16-bit) font name onto an error message string. */
+void
+px_concat_font_name(char *message, uint max_message, const px_value_t *pfnv)
+{ char *mptr = message + strlen(message);
+ uint fnsize = pfnv->value.array.size;
+ uint i;
+
+ /*
+ **** We truncate 16-bit font name chars to 8 bits
+ **** for the message.
+ */
+ for ( i = 0; i < fnsize && mptr - message < max_message; ++mptr, ++i )
+ if ( (*mptr = (byte)integer_elt(pfnv, i)) < 32 )
+ *mptr = '?';
+ *mptr = 0;
+}
+
+/* Paint text or add it to the path. */
+/* This procedure implements the Text and TextPath operators. */
+/* Attributes: pxaTextData, pxaXSpacingData, pxaYSpacingData. */
+private font_proc_next_char(px_next_char_8);
+private font_proc_next_char(px_next_char_16big);
+private font_proc_next_char(px_next_char_16little);
+int
+px_text(px_args_t *par, px_state_t *pxs, bool to_path)
+{ gs_memory_t *mem = pxs->memory;
+ gs_state *pgs = pxs->pgs;
+ px_gstate_t *pxgs = pxs->pxgs;
+ gs_show_enum *penum;
+ const px_value_t *pstr = par->pv[0];
+ int index_shift = (pstr->type & pxd_ubyte ? 0 : 1);
+ const char *str = (const char *)pstr->value.array.data;
+ uint len = pstr->value.array.size;
+ const px_value_t *pxdata = par->pv[1];
+ const px_value_t *pydata = par->pv[2];
+ gs_matrix save_ctm;
+ gs_font *pfont = gs_currentfont(pgs);
+ font_proc_next_char((*save_next_char)) = pfont->procs.next_char;
+ int code = 0;
+
+ if ( (pxdata != 0 && pxdata->value.array.size != len) ||
+ (pydata != 0 && pydata->value.array.size != len)
+ )
+ return_error(errorIllegalArraySize);
+ if ( !pxgs->base_font )
+ return_error(errorNoCurrentFont);
+ if ( !pxgs->char_matrix_set )
+ { code = px_set_char_matrix(pxs);
+ if ( code < 0 )
+ return code;
+ }
+ penum = gs_show_enum_alloc(mem, pgs, "px_text");
+ if ( penum == 0 )
+ return_error(errorInsufficientMemory);
+ gs_currentmatrix(pgs, &save_ctm);
+#if 0
+ /*
+ * I don't know why this code was here (briefly); if it turns out
+ * to be important, we can put it back in, but it breaks Genoa FTS
+ * t202 images 3-5.
+ */
+ if ( pxgs->base_font->scaling_technology == plfst_bitmap )
+ { /*
+ * Bitmap fonts don't scale or rotate: the char_matrix is the
+ * actual matrix to use, aside from translation.
+ */
+ gs_matrix mat;
+ mat = pxgs->char_matrix;
+ mat.tx = save_ctm.tx;
+ mat.ty = save_ctm.ty;
+ gs_setmatrix(pgs, &mat);
+ }
+ else
+#endif
+#ifdef TRANSFORM_TEXT_AS_OBJECTS
+ /* Keep the current translation, but post-scale and rotate. */
+ { gs_matrix tmat, rmat;
+ tmat = pxs->initial_matrix;
+ gs_matrix_rotate(&tmat, 90 * (int)pxs->orientation, &tmat);
+ gs_matrix_multiply(&tmat, &pxgs->text_ctm, &tmat);
+ gs_make_rotation(-90 * (int)pxs->orientation, &rmat);
+ gs_matrix_multiply(&tmat, &rmat, &tmat);
+ tmat.tx = save_ctm.tx;
+ tmat.ty = save_ctm.ty;
+ gs_setmatrix(pgs, &tmat);
+ }
+#endif
+#ifdef POST_TRANSFORM_CHARS
+ { gs_matrix cmat, mat;
+ gs_currentmatrix(pgs, &cmat);
+ gs_matrix_multiply(&cmat, &pxgs->char_matrix, &mat);
+ gs_setmatrix(pgs, &mat);
+ }
+#else
+ gs_concat(pgs, &pxgs->char_matrix);
+#endif
+ pfont->procs.next_char =
+ (!index_shift ? px_next_char_8 :
+ pstr->type & pxd_big_endian ? px_next_char_16big :
+ px_next_char_16little);
+ pfont->WMode =
+ (pxgs->char_sub_mode == eVerticalSubstitution ? 1 : 0) +
+ ((int)(pxgs->char_bold_value * 127) * 2);
+ if ( to_path )
+ { /* TextPath */
+ /*
+ **** We really want a combination of charpath & kshow,
+ **** maybe by adding flags to show*_init?
+ * Lacking this, we have to add each character individually.
+ */
+ uint i;
+ for ( i = 0; i < len; ++i )
+ { gs_fixed_point origin, dist;
+ code = gx_path_current_point(pgs->path, &origin);
+ if ( code < 0 )
+ break;
+ code = gs_charpath_n_init(penum, pgs,
+ str + (i << index_shift), 1, false);
+ if ( code < 0 )
+ break;
+ code = gs_show_next(penum);
+ if ( code != 0 )
+ { if ( code > 0 )
+ code = gs_note_error(errorBadFontData); /* shouldn't happen! */
+ break;
+ }
+ /* We cheat here, knowing that gs_d_t2f doesn't actually */
+ /* require a gs_matrix_fixed but only a gs_matrix. */
+ gs_distance_transform2fixed((const gs_matrix_fixed *)&save_ctm,
+ (pxdata ? real_elt(pxdata, i) : 0.0),
+ (pydata ? real_elt(pydata, i) : 0.0),
+ &dist);
+ code = gx_path_add_point(pgs->path,
+ origin.x + dist.x, origin.y + dist.y);
+ if ( code < 0 )
+ break;
+ }
+ }
+ else
+ { /* Text */
+ uint i;
+ code = gs_xyshow_n_init(penum, pgs, str, len);
+ if ( code < 0 )
+ goto x;
+ for ( i = 0; i < len; ++i )
+ { gs_fixed_point dist;
+ code = gs_show_next(penum);
+ if ( code != gs_show_move )
+ { if ( code >= 0 )
+ code = gs_note_error(errorBadFontData); /* shouldn't happen! */
+ break;
+ }
+ /* See above re the cast in the next line. */
+ gs_distance_transform2fixed((const gs_matrix_fixed *)&save_ctm,
+ (pxdata ? real_elt(pxdata, i) : 0.0),
+ (pydata ? real_elt(pydata, i) : 0.0),
+ &dist);
+ code = gx_path_add_relative_point(pgs->path, dist.x, dist.y);
+ if ( code < 0 )
+ break;
+ }
+ if ( code >= 0 )
+ { code = gs_show_next(penum);
+ if ( code != 0 )
+ { if ( code >= 0 )
+ code = gs_note_error(errorBadFontData); /* shouldn't happen! */
+ }
+ }
+ }
+x: if ( code < 0 )
+ gs_show_enum_release(penum, mem);
+ else
+ gs_free_object(mem, penum, "px_text");
+ gs_setmatrix(pgs, &save_ctm);
+ pfont->WMode = 0;
+ pfont->procs.next_char = save_next_char;
+ return (code == gs_error_invalidfont ?
+ gs_note_error(errorBadFontData) : code);
+}
+/* Next-character procedures, with symbol mapping. */
+private gs_char
+map_symbol(uint chr, const gs_show_enum *penum)
+{ px_gstate_t *pxgs = gs_state_client_data(penum->pgs);
+ const pl_symbol_map_t *psm = pxgs->symbol_map;
+ uint first_code;
+
+ if ( psm == 0 )
+ return chr;
+ first_code = pl_get_uint16(psm->first_code);
+ if ( chr < first_code || chr > pl_get_uint16(psm->last_code) )
+ return 0xffff;
+ return psm->codes[chr - first_code];
+}
+private int
+px_next_char_8(gs_show_enum *penum, gs_char *pchr)
+{ if ( penum->index == penum->text.size )
+ return 2;
+ *pchr = map_symbol(penum->text.data.bytes[penum->index++], penum);
+ return 0;
+}
+private int
+px_next_char_16big(gs_show_enum *penum, gs_char *pchr)
+{ if ( penum->index == penum->text.size )
+ return 2;
+ *pchr = map_symbol(uint16at(&penum->text.data.bytes[penum->index++ << 1], true), penum);
+ return 0;
+}
+private int
+px_next_char_16little(gs_show_enum *penum, gs_char *pchr)
+{ if ( penum->index == penum->text.size )
+ return 2;
+ *pchr = map_symbol(uint16at(&penum->text.data.bytes[penum->index++ << 1], false), penum);
+ return 0;
+}
+
+/* ---------------- Operators ---------------- */
+
+const byte apxSetFont[] = {
+ pxaFontName, pxaCharSize, pxaSymbolSet, 0, 0
+};
+int
+pxSetFont(px_args_t *par, px_state_t *pxs)
+{ px_gstate_t *pxgs = pxs->pxgs;
+ px_font_t *pxfont;
+ px_value_t *pfnv = par->pv[0];
+ uint symbol_set = par->pv[2]->value.i;
+ int code = px_find_font(pfnv, symbol_set, &pxfont, pxs);
+
+ if ( code < 0 )
+ { switch ( code )
+ {
+ case errorFontUndefined:
+ strcpy(pxs->error_line, "FontUndefined - ");
+ goto undef;
+ case errorFontUndefinedNoSubstituteFound:
+ strcpy(pxs->error_line, "FontUndefinedNoSubstituteFound - ");
+undef: px_concat_font_name(pxs->error_line, px_max_error_line, pfnv);
+ break;
+ case errorSymbolSetRemapUndefined:
+ strcpy(pxs->error_line, "SymbolSetRemapUndefined - ");
+ px_concat_font_name(pxs->error_line, px_max_error_line, pfnv);
+ { char setstr[26]; /* 64-bit value plus message */
+ sprintf(setstr, " : %d", symbol_set);
+ strncat(pxs->error_line, setstr,
+ px_max_error_line - strlen(pxs->error_line));
+ pxs->error_line[px_max_error_line] = 0;
+ }
+ break;
+ }
+ return code;
+ }
+ code = gs_setfont(pxs->pgs, pxfont->pfont);
+ if ( code < 0 )
+ return code;
+ pxgs->char_size = real_value(par->pv[1], 0);
+ pxgs->symbol_set = symbol_set;
+ pxgs->base_font = pxfont;
+ set_symbol_map(pxs);
+ pxgs->char_matrix_set = false;
+ return 0;
+}
+
+const byte apxBeginFontHeader[] = {
+ pxaFontName, pxaFontFormat, 0, 0};
+int
+pxBeginFontHeader(px_args_t *par, px_state_t *pxs)
+{ px_value_t *pfnv = par->pv[0];
+ gs_memory_t *mem = pxs->memory;
+ px_font_t *pxfont;
+ int code = px_find_existing_font(pfnv, &pxfont, pxs);
+
+ if ( code >= 0 )
+ { strcpy(pxs->error_line, "FontNameAlreadyExists - ");
+ px_concat_font_name(pxs->error_line, px_max_error_line, pfnv);
+ return_error(errorFontNameAlreadyExists);
+ }
+ /* Make a partially filled-in dictionary entry. */
+ pxfont = pl_alloc_font(mem, "pxBeginFontHeader(pxfont)");
+ if ( pxfont == 0 )
+ return_error(errorInsufficientMemory);
+ pxfont->storage = pxfsDownLoaded;
+ code = px_dict_put(&pxs->font_dict, par->pv[0], pxfont);
+ if ( code < 0 )
+ { gs_free_object(mem, pxfont, "pxBeginFontHeader(pxfont)");
+ return code;
+ }
+ pxs->download_font = pxfont;
+ pxs->download_bytes.data = 0;
+ pxs->download_bytes.size = 0;
+ return 0;
+}
+
+const byte apxReadFontHeader[] = {
+ pxaFontHeaderLength, 0, 0
+};
+int
+pxReadFontHeader(px_args_t *par, px_state_t *pxs)
+{ ulong len = par->pv[0]->value.i;
+ ulong left = len - par->source.position;
+ int code = pxNeedData;
+
+ if ( left > 0 )
+ { ulong pos;
+ if ( par->source.position == 0 )
+ { /* (Re-)allocate the downloaded data. */
+ void *new_data;
+
+ if ( par->source.available == 0 )
+ return code;
+ new_data =
+ (pxs->download_bytes.size == 0 ?
+ gs_alloc_bytes(pxs->memory, len, "pxReadFontHeader") :
+ gs_resize_object(pxs->memory, pxs->download_bytes.data,
+ pxs->download_bytes.size + len,
+ "pxReadFontHeader"));
+ if ( new_data == 0 )
+ return_error(errorInsufficientMemory);
+ pxs->download_bytes.data = new_data;
+ pxs->download_bytes.size += len;
+ }
+ if ( left > par->source.available )
+ left = par->source.available;
+ else
+ code = 0;
+ pos = pxs->download_bytes.size - len + par->source.position;
+ memcpy(pxs->download_bytes.data + pos, par->source.data, left);
+ par->source.position += left;
+ par->source.data += left;
+ par->source.available -= left;
+ if ( pos < 8 && pos + left >= 8 )
+ { /* Check the font header fields now. */
+ const byte *data = pxs->download_bytes.data;
+ if ( data[0] | data[5] )
+ return_error(errorIllegalFontHeaderFields);
+ switch ( data[4] )
+ {
+ case plfst_TrueType:
+ if ( data[1] )
+ return_error(errorIllegalFontHeaderFields);
+ break;
+ case plfst_bitmap:
+ if ( data[1] & ~3 )
+ return_error(errorIllegalFontHeaderFields);
+ break;
+ default:
+ return_error(errorIllegalFontHeaderFields);
+ }
+ }
+ }
+ return code;
+}
+
+const byte apxEndFontHeader[] = {0, 0};
+int
+pxEndFontHeader(px_args_t *par, px_state_t *pxs)
+{ px_font_t *pxfont = pxs->download_font;
+ int code = px_define_font(pxfont, pxs->download_bytes.data,
+ (ulong)pxs->download_bytes.size,
+ gs_next_ids(1), pxs);
+
+ /****** HOW TO DETERMINE FONT TYPE? ******/
+ pxfont->font_type = plft_16bit;
+ /* Clear pointers for GC */
+ pxs->download_font = 0;
+ pxs->download_bytes.data = 0;
+ return code;
+}
+
+const byte apxBeginChar[] = {
+ pxaFontName, 0, 0
+};
+int
+pxBeginChar(px_args_t *par, px_state_t *pxs)
+{ px_value_t *pfnv = par->pv[0];
+ px_font_t *pxfont;
+ int code = px_find_existing_font(pfnv, &pxfont, pxs);
+
+ if ( code >= 0 && pxfont == 0 )
+ code = gs_note_error(errorFontUndefined);
+ if ( code < 0 )
+ { if ( code == errorFontUndefined )
+ { strcpy(pxs->error_line, "FontUndefined - ");
+ px_concat_font_name(pxs->error_line, px_max_error_line, pfnv);
+ }
+ return code;
+ }
+ if ( pxfont->storage != pxfsDownLoaded )
+ return_error(errorCannotReplaceCharacter);
+ pxs->download_font = pxfont;
+ return 0;
+}
+
+const byte apxReadChar[] = {
+ pxaCharCode, pxaCharDataSize, 0, 0
+};
+int
+pxReadChar(px_args_t *par, px_state_t *pxs)
+{ uint char_code = par->pv[0]->value.i;
+ uint size = par->pv[1]->value.i;
+ uint pos = par->source.position;
+
+ if ( pos == 0 )
+ { /* We're starting a character definition. */
+ byte *def;
+
+ if ( size < 2 )
+ return_error(errorIllegalCharacterData);
+ if ( par->source.available == 0 )
+ return pxNeedData;
+ def = gs_alloc_bytes(pxs->memory, size, "pxReadChar");
+ if ( def == 0 )
+ return_error(errorInsufficientMemory);
+ pxs->download_bytes.data = def;
+ pxs->download_bytes.size = size;
+ }
+ while ( pos < size )
+ { uint copy = min(par->source.available, size - pos);
+
+ if ( copy == 0 )
+ return pxNeedData;
+ memcpy(pxs->download_bytes.data + pos, par->source.data, copy);
+ par->source.data += copy;
+ par->source.available -= copy;
+ par->source.position = pos += copy;
+ }
+ /* We have the complete character. */
+ /* Do error checks before installing. */
+ { const byte *header = pxs->download_font->header;
+ const byte *data = pxs->download_bytes.data;
+ int code = 0;
+
+ switch ( data[0] )
+ {
+ case 0: /* bitmap */
+ if ( header[4] != plfst_bitmap )
+ code = gs_note_error(errorFSTMismatch);
+ else if ( data[1] != 0 )
+ code = gs_note_error(errorUnsupportedCharacterClass);
+ else if ( size < 10 ||
+ size != 10 + ((pl_get_uint16(data + 6) + 7) >> 3) *
+ pl_get_uint16(data + 8)
+ )
+ code = gs_note_error(errorIllegalCharacterData);
+ break;
+ case 1: /* TrueType outline */
+ if ( header[4] != plfst_TrueType )
+ code = gs_note_error(errorFSTMismatch);
+ else if ( data[1] != 0 )
+ code = gs_note_error(errorUnsupportedCharacterClass);
+ else if ( size < 6 || size != 2 + pl_get_uint16(data + 2) )
+ code = gs_note_error(errorIllegalCharacterData);
+ break;
+ default:
+ code = gs_note_error(errorUnsupportedCharacterFormat);
+ }
+ if ( code >= 0 )
+ { code = pl_font_add_glyph(pxs->download_font, char_code, data);
+ if ( code < 0 )
+ code = gs_note_error(errorInternalOverflow);
+ }
+ if ( code < 0 )
+ gs_free_object(pxs->memory, pxs->download_bytes.data,
+ "pxReadChar");
+ pxs->download_bytes.data = 0;
+ return code;
+ }
+}
+
+const byte apxEndChar[] = {0, 0};
+int
+pxEndChar(px_args_t *par, px_state_t *pxs)
+{ return 0;
+}
+
+const byte apxRemoveFont[] = {
+ pxaFontName, 0, 0
+};
+int
+pxRemoveFont(px_args_t *par, px_state_t *pxs)
+{ px_value_t *pfnv = par->pv[0];
+ px_font_t *pxfont;
+ int code = px_find_existing_font(pfnv, &pxfont, pxs);
+ const char *error = 0;
+
+ if ( code < 0 )
+ error = "UndefinedFontNotRemoved - ";
+ else if ( pxfont == 0 ) /* built-in font, assume internal */
+ error = "InternalFontNotRemoved - ";
+ else
+ switch ( pxfont->storage )
+ {
+ case pxfsInternal:
+ error = "InternalFontNotRemoved - ";
+ break;
+ case pxfsMassStorage:
+ error = "MassStorageFontNotRemoved - ";
+ break;
+ default: /* downloaded */
+ ;
+ }
+ if ( error )
+ { /* Construct a warning message including the font name. */
+ char message[px_max_error_line + 1];
+
+ strcpy(message, error);
+ px_concat_font_name(message, px_max_error_line, pfnv);
+ code = px_record_warning(message, false, pxs);
+ }
+ /****** WHAT IF THIS IS THE CURRENT FONT? ******/
+ px_dict_undef(&pxs->font_dict, par->pv[0]);
+ return 0;
+}
diff --git a/pxl/pxfont.h b/pxl/pxfont.h
new file mode 100644
index 000000000..af4f01083
--- /dev/null
+++ b/pxl/pxfont.h
@@ -0,0 +1,90 @@
+/* Copyright (C) 1996 Aladdin Enterprises. All rights reserved.
+ Unauthorized use, copying, and/or distribution prohibited.
+ */
+
+/* pxfont.h */
+/* Interface to PCL XL utilities */
+
+#ifndef pxfont_INCLUDED
+# define pxfont_INCLUDED
+
+/* Rename types. */
+#ifdef px_font_t_DEFINED
+# define pl_font_t_DEFINED
+#endif
+#define pl_font_s px_font_s
+#define pl_font_t px_font_t
+#include "plfont.h"
+#define px_font_t_DEFINED
+
+/*
+ * This file provides a layer of specialization and renaming around
+ * the font handling library.
+ *
+ * We store all font names as Unicode strings. Each element of a font
+ * name is a char16 (a 16-bit unsigned value in native byte order).
+ */
+
+/* Define storage locations for fonts. */
+typedef enum {
+ pxfsDownLoaded,
+ pxfsInternal,
+ pxfsMassStorage
+} px_font_storage_t;
+
+/*
+ * At the beginning of execution, we acquire a block of N+1 unique IDs for
+ * the built-in fonts: if the first ID is B, we assign B through B+N-1 for
+ * the built-in fonts, and B+N for the error page font. (The value B is
+ * stored in the known_fonts_base_id element of the px_state_t.) At the end
+ * of each session, we purge from the cache all fonts whose UniqueID is
+ * greater than B+N.
+ */
+extern const uint px_num_known_fonts;
+
+/* Fill in generic font boilerplate. */
+#define px_fill_in_font(pfont, pxfont, pxs)\
+ pl_fill_in_font(pfont, pxfont, pxs->font_dir, pxs->memory)
+
+/*
+ * Define a font. The caller must fill in pxfont->storage and ->font_type.
+ */
+int px_define_font(P5(px_font_t *pxfont, const byte *header, ulong size,
+ gs_id id, px_state_t *pxs));
+
+/*
+ * Look up a font name and return the base font. This procedure implements
+ * most of the SetFont operator. Note that this procedure will widen and/or
+ * byte-swap the font name if necessary.
+ */
+int px_find_font(P4(px_value_t *pfnv, uint symbol_set, px_font_t **ppxfont,
+ px_state_t *pxs));
+
+/* Look up a font name and return an existing font. */
+/* This procedure may widen and/or byte-swap the font name. */
+/* If this font is supposed to be built in but no .TTF file is available, */
+/* return >= 0 and store 0 in *ppxfont. */
+int px_find_existing_font(P3(px_value_t *pfnv, px_font_t **ppxfont,
+ px_state_t *pxs));
+
+/*
+ * Concatenate a widened (16-bit) font name onto an error message string.
+ */
+void px_concat_font_name(P3(char *message, uint max_message,
+ const px_value_t *pfnv));
+
+/*
+ * Paint text or add it to the path.
+ * This procedure implements the Text and TextPath operators.
+ */
+int px_text(P3(px_args_t *par, px_state_t *pxs, bool to_path));
+
+/*
+ * Free a font. This is the freeing procedure in the font dictionary.
+ * We have to define the name without parameters so that we can use it
+ * as a procedure constant.
+ */
+/*#define px_free_font(mem, pxf, cname) pl_free_font(mem, pxf, cname)*/
+#define px_free_font pl_free_font
+
+#endif /* pxfont_INCLUDED */
diff --git a/pxl/pxfts.txt b/pxl/pxfts.txt
new file mode 100644
index 000000000..a85437116
--- /dev/null
+++ b/pxl/pxfts.txt
@@ -0,0 +1,153 @@
+
+ Copyright (C) 1996 Aladdin Enterprises. All rights reserved.
+ Unauthorized use, copying, and/or distribution prohibited.
+
+ Report on Genoa PCL6 FTS and LaserJet 6MP
+
+This document presents the results of Aladdin's investigation of
+discrepancies between the Genoa PCL6 Functional Test Suite, the published
+PCL XL Feature Reference Protocol Class 1.1 specification, and the behavior
+of the H-P LaserJet 6MP printer.
+
+ Report on Genoa PCL6 FTS and LaserJet 6MP
+
+Introduction
+============
+
+In the course of validating our PCL XL interpreter using the Genoa PCL6
+Functional Test Suite (FTS), we observed a number of discrepancies that,
+after careful investigation, we concluded were due to problems in the FTS
+and/or the printer firmware. In this document, we present these in detail.
+
+This document should be read in conjunction with our separate report on
+discrepancies between the specification and the firmware independent of the
+Genoa FTS.
+
+Changes in a given revision of this document are marked with the revision
+number in [brackets]. Revision history:
+ first issued December 6, 1996
+ rev. [1] December 22, 1996
+ rev. [2] February 6, 1997
+
+General
+=======
+
+We noticed that in many places, the input data contain an extra PopGS that
+doesn't have a matching PushGS. The published specification says that this
+is invalid (i.e., the existence of a PushGS is part of the precondition for
+PopGS). Apparently the printer ignores the extra PopGS.
+
+Individual tests
+================
+
+[2] t101 - BeginSession
+-----------------------
+
+On pages 2 and 3, in the LJ5 printouts, the characters are not distorted
+(stretched vertically on page 2, compressed vertically on page 3) to match
+the non-uniform UnitsPerMeasure. This is a bug in the LJ5 firmware that has
+been corrected in the LJ 6MP.
+
+t305 - SetColorSpace
+--------------------
+
+The two lower rectangles in image 03 are stroked with gray, and are not
+painted. The comment on this image says "The 2nd and 3rd rectangles should
+be stroked and painted with black color." [1] See the entry for
+SetColorSpace in the discrepancies report.
+
+t314 - SetFillMode
+------------------
+
+The lower figure in all 3 images, ostensibly drawn with the non-zero winding
+number rule, is clearly not drawn with this rule. [1] See the discussion of
+clipping and of SetClipIntersect in the discrepancies report.
+
+t321 - SetClipIntersect
+-----------------------
+
+In the lower figure of image 01, the dark lines stop at the edges of an
+invisible rectangle rather than extending all the way to the ellipse. This
+is consistent with the interpretation of SetClipIntersect documented in the
+discrepancies report, but not with the published specification.
+
+t322 - SetClipRectangle
+-----------------------
+
+In image 03, the rectangle border is a medium gray. However, the input data
+are as follows:
+
+ NewPath
+ 200 @GrayLevel SetBrushSource
+ 120 @GrayLevel SetPenSource
+ 40 @PenWidth SetPenWidth
+ 0 @ClipRegion [1200 400 2000 2200] @BoundingBox SetClipRectangle
+ [1200 400 2000 2200] @BoundingBox Rectangle
+ 20 @GrayLevel SetPenSource
+ 0 2925 @Point SetCursor
+ 2400 0 @EndPoint LinePath
+ PaintPath
+
+The Rectangle operator is supposed to leave the path set to a rectangle, so
+the final PaintPath should paint both the rectangle and the line a very dark
+gray; however, as noted in the discrepancies report, Rectangle actually
+clears the path.
+
+t325 - SetClipMode
+------------------
+
+The middle figure of image 03 is a solid disk. However, the input data are
+as follows:
+
+ 0 @GrayLevel SetBrushSource
+ [300 300 4500 2625] @BoundingBox Rectangle
+ PushGS
+ 0 @ClipMode SetClipMode
+ [1800 2325 3000 3525] @BoundingBox EllipsePath
+ [2100 2625 2700 3225] @BoundingBox EllipsePath
+ 0 @ClipRegion SetClipReplace
+ 0 @GrayLevel SetBrushSource
+ [300 2250 4500 3600] @BoundingBox Rectangle
+
+Again, if Rectangle left the path set as documented, this would draw (part
+of) the rectangle as well as the disk.
+
+[2] t329 - SetHalftoneMethod
+----------------------------
+
+The DitherOrigin of the halftone is incorrectly set relative to the origin
+of the default (rotated) user coordinate system rather than to the current
+user coordinate system. This causes the patterns to be shifted slightly
+relative to the Genoa printout. For a test example, see our separate report
+on firmware discrepancies.
+
+t409 - Ellipse
+--------------
+
+Image 04 is light gray. However, it should actually be painted with an
+outer stripe of light gray and an inner strip of dark gray:
+
+ 1 @ColorSpace SetColorSpace
+ 255 @GrayLevel SetBrushSource
+ 180 @GrayLevel SetPenSource
+ 160 @PenWidth SetPenWidth
+ [300 300 2100 800] @BoundingBox RectanglePath % no-op
+ [300 1100 2100 1600] @BoundingBox Ellipse
+ 60 @GrayLevel SetPenSource
+ 60 @PenWidth SetPenWidth
+ PaintPath
+
+The Ellipse command should leave the path set, and the final PaintPath
+should erase the interior of the ellipse (including the inner half of the
+lighter stripe) and then repaint the annulus with the darker color with the
+narrower pen. This is the same problem as in t322 and t325.
+
+t413 - Pie
+----------
+
+Image 05 shows the same problem as noted under t409.
+
+t415 - Rectangle
+----------------
+
+Image 02 shows the same problem as noted under t322 and t325.
diff --git a/pxl/pxgstate.c b/pxl/pxgstate.c
new file mode 100644
index 000000000..dda9c2af9
--- /dev/null
+++ b/pxl/pxgstate.c
@@ -0,0 +1,914 @@
+/* Copyright (C) 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+ Unauthorized use, copying, and/or distribution prohibited.
+ */
+
+/* pxgstate.c */
+/* PCL XL graphics state operators */
+
+#include "math_.h"
+#include "memory_.h"
+#include "stdio_.h" /* std.h + NULL */
+#include "gstypes.h"
+#include "gsmemory.h"
+#include "gsstruct.h"
+#include "pxoper.h"
+#include "pxstate.h"
+#include "gsstate.h"
+#include "gscoord.h"
+#include "gxcspace.h" /* must precede gscolor2.h */
+#include "gsimage.h"
+#include "gspath.h"
+#include "gspath2.h"
+#include "gsrop.h"
+#include "gxstate.h"
+
+/*
+ * There is an apparent bug in the LJ5 and LJ6MP firmware that causes
+ * SetClipIntersect with even/odd mode and exterior region to behave the
+ * same as SetClipReplace. To emulate this bug, uncomment the following
+ * #define.
+ */
+#define CLIP_INTERSECT_EXTERIOR_REPLACES
+/*
+ * H-P printers apparently have a maximum dash pattern length of 20.
+ * Our library doesn't impose a limit, but we may as well emulate the
+ * H-P limit here for error checking and Genoa test compatibility.
+ */
+#define MAX_DASH_ELEMENTS 20
+
+/* Imported operators */
+px_operator_proc(pxRectanglePath);
+/* Forward references */
+px_operator_proc(pxSetClipIntersect);
+
+/* Imported color space types */
+extern const gs_color_space_type gs_color_space_type_Indexed; /* gscolor2.c */
+extern const gs_color_space_type gs_color_space_type_Pattern; /* gspcolor.c */
+
+/* GC descriptors */
+private_st_px_paint();
+#define ppt ((px_paint_t *)vptr)
+private ENUM_PTRS_BEGIN(px_paint_enum_ptrs) {
+ if ( ppt->type != pxpPattern )
+ return 0;
+ return ENUM_SUPER_ELT(px_paint_t, st_client_color,
+ value.pattern.color, 1);
+}
+ case 0:
+ ENUM_RETURN((ppt->type == pxpPattern ? ppt->value.pattern.pattern : 0));
+ENUM_PTRS_END
+private RELOC_PTRS_BEGIN(px_paint_reloc_ptrs) {
+ if ( ppt->type == pxpPattern ) {
+ RELOC_PTR(px_paint_t, value.pattern.pattern);
+ RELOC_SUPER_ELT(px_paint_t, st_client_color, value.pattern.color);
+ }
+} RELOC_PTRS_END
+#undef ppt
+private_st_px_gstate();
+#define pxgs ((px_gstate_t *)vptr)
+private ENUM_PTRS_BEGIN(px_gstate_enum_ptrs) {
+ index -= px_gstate_num_ptrs + px_gstate_num_string_ptrs;
+ if ( index == 0 )
+ ENUM_RETURN_CONST_STRING_PTR(px_gstate_t, palette.data);
+ index--;
+ if ( index < st_px_paint_max_ptrs )
+ return ENUM_SUPER_ELT(px_gstate_t, st_px_paint, brush, 0);
+ index -= st_px_paint_max_ptrs;
+ if ( index < st_px_paint_max_ptrs )
+ return ENUM_SUPER_ELT(px_gstate_t, st_px_paint, pen, 0);
+ index -= st_px_paint_max_ptrs;
+ return ENUM_SUPER_ELT(px_gstate_t, st_px_dict, temp_pattern_dict, 0);
+ }
+#define mp(i,e) ENUM_PTR(i, px_gstate_t, e);
+#define ms(i,e) ENUM_STRING_PTR(px_gstate_num_ptrs + i, px_gstate_t, e);
+ px_gstate_do_ptrs(mp)
+ px_gstate_do_string_ptrs(ms)
+#undef mp
+#undef ms
+ENUM_PTRS_END
+private RELOC_PTRS_BEGIN(px_gstate_reloc_ptrs) {
+ RELOC_SUPER_ELT(px_gstate_t, st_px_paint, brush);
+ RELOC_CONST_STRING_PTR(px_gstate_t, palette);
+ RELOC_SUPER_ELT(px_gstate_t, st_px_paint, pen);
+ RELOC_SUPER_ELT(px_gstate_t, st_px_dict, temp_pattern_dict);
+#define mp(i,e) RELOC_PTR(px_gstate_t, e);
+#define ms(i,e) RELOC_STRING_PTR(px_gstate_t, e);
+ px_gstate_do_ptrs(mp)
+ px_gstate_do_string_ptrs(ms)
+#undef mp
+#undef ms
+} RELOC_PTRS_END
+#undef pxgs
+
+/* Define 'client procedures' for copying the PCL XL state. */
+/* We export the reference count adjustment procedures for the sake of */
+/* the state setting code in pxink.c. */
+void
+px_paint_rc_adjust(px_paint_t *ppt, int delta, gs_memory_t *mem)
+{ if ( ppt->type == pxpPattern )
+ { /*
+ * There is no public API for adjusting the reference count of a
+ * gs_client_color, and even the private API requires having a
+ * color space available. We'll need to fix this properly
+ * sooner or later, but for the moment, fake it.
+ */
+ gs_color_space cspace;
+
+ /*
+ * Even though this is a colored Pattern, and hence does have a
+ * base space, we set has_base_space to false to prevent the
+ * adjust_color_count procedure from trying to call the
+ * adjustment procedures for the base space.
+ */
+ cspace.type = &gs_color_space_type_Pattern;
+ cspace.params.pattern.has_base_space = false;
+ (*cspace.type->adjust_color_count)(&ppt->value.pattern.color,
+ &cspace, NULL, delta);
+ rc_adjust_only(ppt->value.pattern.pattern, delta,
+ "px_paint_rc_adjust");
+ }
+}
+void
+px_gstate_rc_adjust(px_gstate_t *pxgs, int delta, gs_memory_t *mem)
+{ px_paint_rc_adjust(&pxgs->pen, delta, mem);
+ px_paint_rc_adjust(&pxgs->brush, delta, mem);
+}
+private void *
+px_gstate_client_alloc(gs_memory_t *mem)
+{ px_gstate_t *pxgs =
+ gs_alloc_struct(mem, px_gstate_t, &st_px_gstate, "px_gstate_alloc");
+
+ if ( pxgs == 0 )
+ return 0;
+ /* Initialize reference-counted pointers and other pointers */
+ /* needed to establish invariants. */
+ pxgs->memory = mem;
+ pxgs->halftone.thresholds.data = 0;
+ pxgs->halftone.thresholds.size = 0;
+ pxgs->dither_matrix.data = 0;
+ pxgs->dither_matrix.size = 0;
+ pxgs->brush.type = pxpNull;
+ pxgs->pen.type = pxpNull;
+ px_dict_init(&pxgs->temp_pattern_dict, mem, px_free_pattern);
+ return pxgs;
+}
+private int
+px_gstate_client_copy_for(void *to, void *from, gs_state_copy_reason_t reason)
+{
+#define pxfrom ((px_gstate_t *)from)
+#define pxto ((px_gstate_t *)to)
+ px_gstate_rc_adjust(pxfrom, 1, pxfrom->memory);
+ px_gstate_rc_adjust(pxto, -1, pxto->memory);
+ /*
+ * In the context of the PCL XL code, this routine may be called for
+ * gsave, grestore, or gstate (copying the gstate for makepattern
+ * or Pattern rendering). See gxstate.h for details of the 'from'
+ * and 'to' arguments for each of these cases.
+ *
+ * We have some structures that belong to the individual gstates for
+ * storage management purposes. Currently these are:
+ * dither_matrix, temp_pattern_dict
+ * px_gstate_client_alloc initializes them to an empty state.
+ * For gsave and gstate, the new current gstate or copied gstate
+ * respectively should have the empty structure. For grestore,
+ * we want to swap the structures, because we will then free the
+ * saved gstate (and release the structure that was in the current
+ * gstate before the grestore).
+ *
+ * halftone.thresholds is a different special case. We need to
+ * copy it for gsave and gstate, and free it on grestore.
+ */
+ { gs_string tmat;
+ gs_string thtt;
+ pl_dict_t tdict;
+ gs_string *phtt;
+
+ tmat = pxto->dither_matrix;
+ thtt = pxto->halftone.thresholds;
+ tdict = pxto->temp_pattern_dict;
+ *pxto = *pxfrom;
+ switch ( reason )
+ {
+ case copy_for_gstate:
+ /* Just put back the (empty) 'to' structures. */
+ pxto->dither_matrix = tmat;
+ pxto->temp_pattern_dict = tdict;
+ phtt = &pxto->halftone.thresholds;
+ goto copy;
+ case copy_for_gsave:
+ /* Swap the structures, but set the parent of the new */
+ /* (empty) dictionary to the old one. */
+ pxfrom->dither_matrix = tmat;
+ pxfrom->temp_pattern_dict = tdict;
+ pl_dict_set_parent(&pxfrom->temp_pattern_dict,
+ &pxto->temp_pattern_dict);
+ phtt = &pxfrom->halftone.thresholds;
+copy: if ( phtt->data )
+ { byte *str = gs_alloc_string(pxfrom->memory, phtt->size,
+ "px_gstate_client_copy(thresholds)");
+ if ( str == 0 )
+ return_error(errorInsufficientMemory);
+ memcpy(str, phtt->data, phtt->size);
+ phtt->data = str;
+ }
+ break;
+ default: /* copy_for_grestore */
+ /* Swap the structures. */
+ pxfrom->dither_matrix = tmat;
+ pxfrom->halftone.thresholds = thtt;
+ pxfrom->temp_pattern_dict = tdict;
+ }
+ }
+ return 0;
+#undef pxfrom
+#undef pxto
+}
+private void
+px_gstate_client_free(void *old, gs_memory_t *mem)
+{ px_gstate_t *pxgs = old;
+
+ px_dict_release(&pxgs->temp_pattern_dict);
+ if ( pxgs->halftone.thresholds.data )
+ gs_free_string(mem, pxgs->halftone.thresholds.data,
+ pxgs->halftone.thresholds.size,
+ "px_gstate_free(halftone.thresholds)");
+ if ( pxgs->dither_matrix.data )
+ gs_free_string(mem, pxgs->dither_matrix.data,
+ pxgs->dither_matrix.size,
+ "px_gstate_free(dither_matrix)");
+ px_gstate_rc_adjust(old, -1, mem);
+ gs_free_object(mem, old, "px_gstate_free");
+}
+private const gs_state_client_procs px_gstate_procs = {
+ px_gstate_client_alloc,
+ 0, /* copy -- superseded by copy_for */
+ px_gstate_client_free,
+ px_gstate_client_copy_for
+};
+
+/* ---------------- Initialization ---------------- */
+
+/* Allocate a px_gstate_t. */
+px_gstate_t *
+px_gstate_alloc(gs_memory_t *mem)
+{ px_gstate_t *pxgs = px_gstate_client_alloc(mem);
+
+ if ( pxgs == 0 )
+ return 0;
+ pxgs->stack_depth = 0;
+ px_gstate_init(pxgs, NULL);
+ return pxgs;
+}
+
+/* Initialize a px_gstate_t. */
+void
+px_gstate_init(px_gstate_t *pxgs, gs_state *pgs)
+{ pxgs->halftone.method = eDeviceBest;
+ pxgs->halftone.set = false;
+ pxgs->halftone.origin.x = pxgs->halftone.origin.y = 0;
+ /* halftone.thresholds was initialized at alloc time */
+ px_gstate_reset(pxgs);
+ if ( pgs )
+ gs_state_set_client(pgs, pxgs, &px_gstate_procs);
+}
+
+/* Initialize the graphics state for a page. */
+/* Note that this takes a px_state_t, not a px_gstate_t. */
+int
+px_initgraphics(px_state_t *pxs)
+{ gs_state *pgs = pxs->pgs;
+
+ px_gstate_reset(pxs->pxgs);
+ gs_initgraphics(pgs);
+
+ /* PCL XL uses the center-of-pixel rule. */
+ gs_setfilladjust(pgs, 0.0, 0.0);
+
+ { gs_point inch;
+ float dpi;
+
+ gs_dtransform(pgs, 72.0, 0.0, &inch);
+ dpi = fabs(inch.x) + fabs(inch.y);
+
+ /* Stroke adjustment leads to anomalies at high resolutions. */
+ if ( dpi >= 150 )
+ gs_setstrokeadjust(pgs, false);
+
+ /* We need the H-P interpretation of zero-length lines */
+ /* and of using bevel joins for the segments of flattened curves. */
+ gs_setdotlength(pgs, 72.0 / 300, true);
+ }
+ return 0;
+}
+
+/* Reset a px_gstate_t, initially or at the beginning of a page. */
+void
+px_gstate_reset(px_gstate_t *pxgs)
+{ pxgs->brush.type = pxpGray;
+ pxgs->brush.value.gray = 0;
+ pxgs->char_angle = 0;
+ pxgs->char_bold_value = 0;
+ pxgs->char_scale.x = pxgs->char_scale.y = 1;
+ pxgs->char_shear.x = pxgs->char_shear.y = 0;
+ /* The order of transforms is arbitrary, */
+ /* because the transforms are all no-ops. */
+ pxgs->char_transforms[0] = pxct_rotate;
+ pxgs->char_transforms[1] = pxct_shear;
+ pxgs->char_transforms[2] = pxct_scale;
+ pxgs->char_sub_mode = eNoSubstitution;
+ pxgs->clip_mode = eNonZeroWinding;
+ pxgs->color_space = eRGB;
+ pxgs->palette.size = 0;
+ pxgs->palette.data = 0;
+ pxgs->palette_is_shared = false;
+ pxgs->base_font = 0;
+ /* halftone_method was set above. */
+ pxgs->fill_mode = eNonZeroWinding;
+ pxgs->dashed = false;
+ pxgs->pen.type = pxpGray;
+ pxgs->pen.value.gray = 0;
+ /* temp_pattern_dict was set at allocation time. */
+ gs_make_identity(&pxgs->text_ctm);
+ pxgs->char_matrix_set = false;
+ pxgs->symbol_map = 0;
+}
+
+/* ---------------- Utilities ---------------- */
+
+/* Set up the color space information for a bitmap image or pattern. */
+int
+px_image_color_space(gs_color_space *pcs, gs_image_t *pim,
+ const px_bitmap_params_t *params, const gs_const_string *palette,
+ const gs_state *pgs)
+{ int depth = params->depth;
+ const gs_color_space_type _ds *pcst;
+
+ switch ( params->color_space )
+ {
+ case eGray:
+ pcst = &gs_color_space_type_DeviceGray;
+ break;
+ case eRGB:
+ pcst = &gs_color_space_type_DeviceRGB;
+ break;
+ default:
+ return_error(errorIllegalAttributeValue);
+ }
+ if ( params->indexed )
+ { pcs->params.indexed.base_space.type = pcst;
+ pcs->params.indexed.hival = (1 << depth) - 1;
+ pcs->params.indexed.lookup.table = *palette;
+ pcs->params.indexed.use_proc = 0;
+ pcs->type = &gs_color_space_type_Indexed;
+ }
+ else
+ { pcs->type = pcst;
+ }
+ gs_image_t_init_rgb(pim, (const gs_imager_state *)pgs);
+ pim->ColorSpace = pcs;
+ pim->BitsPerComponent = depth;
+ if ( params->indexed )
+ pim->Decode[1] = (1 << depth) - 1;
+ return 0;
+}
+
+/* Check the setting of the clipping region. */
+#define check_clip_region(par, pxs)\
+ if ( par->pv[0]->value.i == eExterior && pxs->pxgs->clip_mode != eEvenOdd )\
+ return_error(errorClipModeMismatch)
+
+/* Record the most recent character transformation. */
+private void near
+add_char_transform(px_gstate_t *pxgs, px_char_transform_t trans)
+{ /* Promote this transformation to the head of the list. */
+ if ( pxgs->char_transforms[2] == trans )
+ pxgs->char_transforms[2] = pxgs->char_transforms[1],
+ pxgs->char_transforms[1] = pxgs->char_transforms[0];
+ else if ( pxgs->char_transforms[1] == trans )
+ pxgs->char_transforms[1] = pxgs->char_transforms[0];
+ pxgs->char_transforms[0] = trans;
+ pxgs->char_matrix_set = false;
+}
+
+/* ---------------- Operators ---------------- */
+
+const byte apxPopGS[] = {0, 0};
+int
+pxPopGS(px_args_t *par, px_state_t *pxs)
+{ gs_state *pgs = pxs->pgs;
+ px_gstate_t *pxgs = pxs->pxgs;
+ int code;
+
+ /*
+ * Even though the H-P documentation says that a PopGS with an
+ * empty stack is illegal, the implementations apparently simply
+ * do nothing in this case.
+ */
+ if ( pxgs->stack_depth == 0 )
+ return 0;
+ if ( pxgs->palette.data && !pxgs->palette_is_shared )
+ gs_free_string(pxs->memory, pxgs->palette.data,
+ pxgs->palette.size, "pxPopGS(palette)");
+ px_purge_pattern_cache(pxs, eTempPattern);
+ code = gs_grestore(pgs);
+ pxs->pxgs = gs_state_client_data(pgs);
+ return code;
+}
+
+const byte apxPushGS[] = {0, 0};
+int
+pxPushGS(px_args_t *par, px_state_t *pxs)
+{ gs_state *pgs = pxs->pgs;
+ int code = gs_gsave(pgs);
+ px_gstate_t *pxgs;
+
+ if ( code < 0 )
+ return code;
+ pxgs = pxs->pxgs = gs_state_client_data(pgs);
+ if ( pxgs->palette.data )
+ pxgs->palette_is_shared = true;
+ ++(pxgs->stack_depth);
+ return code;
+}
+
+const byte apxSetClipReplace[] = {
+ pxaClipRegion, 0, 0
+};
+int
+pxSetClipReplace(px_args_t *par, px_state_t *pxs)
+{ int code;
+
+ check_clip_region(par, pxs);
+ if ( (code = gs_initclip(pxs->pgs)) < 0 )
+ return code;
+ return pxSetClipIntersect(par, pxs);
+}
+
+const byte apxSetCharAngle[] = {
+ pxaCharAngle, 0, 0
+};
+int
+pxSetCharAngle(px_args_t *par, px_state_t *pxs)
+{ real angle = real_value(par->pv[0], 0);
+ px_gstate_t *pxgs = pxs->pxgs;
+
+ if ( angle != pxgs->char_angle ||
+ pxgs->char_transforms[0] != pxct_rotate
+ )
+ { pxgs->char_angle = angle;
+ add_char_transform(pxgs, pxct_rotate);
+ }
+ return 0;
+}
+
+const byte apxSetCharScale[] = {
+ pxaCharScale, 0, 0
+};
+int
+pxSetCharScale(px_args_t *par, px_state_t *pxs)
+{ real x_scale = real_value(par->pv[0], 0);
+ real y_scale = real_value(par->pv[0], 1);
+ px_gstate_t *pxgs = pxs->pxgs;
+
+ if ( x_scale != pxgs->char_scale.x || y_scale != pxgs->char_scale.y ||
+ pxgs->char_transforms[0] != pxct_scale
+ )
+ { pxgs->char_scale.x = x_scale;
+ pxgs->char_scale.y = y_scale;
+ add_char_transform(pxgs, pxct_scale);
+ }
+ return 0;
+}
+
+const byte apxSetCharShear[] = {
+ pxaCharShear, 0, 0
+};
+int
+pxSetCharShear(px_args_t *par, px_state_t *pxs)
+{ real x_shear = real_value(par->pv[0], 0);
+ real y_shear = real_value(par->pv[0], 1);
+ px_gstate_t *pxgs = pxs->pxgs;
+
+ if ( x_shear != pxgs->char_shear.x || y_shear != pxgs->char_shear.y ||
+ pxgs->char_transforms[0] != pxct_shear
+ )
+ { pxgs->char_shear.x = x_shear;
+ pxgs->char_shear.y = y_shear;
+ add_char_transform(pxgs, pxct_shear);
+ }
+ return 0;
+}
+
+const byte apxSetClipIntersect[] = {
+ pxaClipRegion, 0, 0
+};
+int
+pxSetClipIntersect(px_args_t *par, px_state_t *pxs)
+{ gs_state *pgs = pxs->pgs;
+ pxeClipRegion_t clip_region = par->pv[0]->value.i;
+ int code;
+
+ check_clip_region(par, pxs);
+ /*
+ * The discussion of ClipMode and ClipRegion in the published
+ * specification is confused and incorrect. Based on testing with
+ * the LaserJet 6MP, we believe that ClipMode works just like
+ * PostScript clip and eoclip, and that ClipRegion applies only to
+ * the *current* path (i.e., support for "outside clipping" in the
+ * library is unnecessary). If we had only known....
+ */
+#if 0
+ gs_setclipoutside(pgs, false);
+#endif
+ if ( clip_region == eExterior )
+ { /*
+ * We know clip_mode is eEvenOdd, so we can complement the
+ * region defined by the current path by just adding a
+ * rectangle that encloses the entire page.
+ */
+ gs_rect bbox;
+
+ code = gs_gsave(pgs);
+ if ( code < 0 )
+ return code;
+ gs_initclip(pgs);
+ if ( (code = gs_clippath(pgs)) < 0 ||
+ (code = gs_pathbbox(pgs, &bbox)) < 0
+ )
+ DO_NOTHING;
+ gs_grestore(pgs);
+ if ( code < 0 ||
+ (code = gs_rectappend(pgs, &bbox, 1)) < 0
+ )
+ return code;
+#ifdef CLIP_INTERSECT_EXTERIOR_REPLACES
+ gs_initclip(pgs);
+#endif
+ }
+ code =
+ (pxs->pxgs->clip_mode == eEvenOdd ? gs_eoclip(pgs) : gs_clip(pgs));
+ if ( code < 0 )
+ return code;
+#if 0
+ gs_setclipoutside(pgs, clip_region == eExterior);
+#endif
+ return gs_newpath(pgs);
+}
+
+const byte apxSetClipRectangle[] = {
+ pxaClipRegion, pxaBoundingBox, 0, 0
+};
+int
+pxSetClipRectangle(px_args_t *par, px_state_t *pxs)
+{ px_args_t args;
+ gs_state *pgs = pxs->pgs;
+ int code;
+
+ check_clip_region(par, pxs);
+ gs_newpath(pgs);
+ args.pv[0] = par->pv[1];
+ if ( (code = pxRectanglePath(&args, pxs)) < 0 )
+ return code;
+ return pxSetClipReplace(par, pxs);
+}
+
+const byte apxSetClipToPage[] = {0, 0};
+int
+pxSetClipToPage(px_args_t *par, px_state_t *pxs)
+{ gs_newpath(pxs->pgs);
+ return gs_initclip(pxs->pgs);
+}
+
+const byte apxSetCursor[] = {
+ pxaPoint, 0, 0
+};
+int
+pxSetCursor(px_args_t *par, px_state_t *pxs)
+{ return gs_moveto(pxs->pgs, real_value(par->pv[0], 0),
+ real_value(par->pv[0], 1));
+}
+
+const byte apxSetCursorRel[] = {
+ pxaPoint, 0, 0
+};
+int
+pxSetCursorRel(px_args_t *par, px_state_t *pxs)
+{ return gs_rmoveto(pxs->pgs, real_value(par->pv[0], 0),
+ real_value(par->pv[0], 1));;
+}
+
+/* SetHalftoneMethod is in pxink.c */
+
+const byte apxSetFillMode[] = {
+ pxaFillMode, 0, 0
+};
+int
+pxSetFillMode(px_args_t *par, px_state_t *pxs)
+{ pxs->pxgs->fill_mode = par->pv[0]->value.i;
+ return 0;
+}
+
+/* SetFont is in pxfont.c */
+
+const byte apxSetLineDash[] = {
+ 0, pxaLineDashStyle, pxaDashOffset, pxaSolidLine, 0
+};
+int
+pxSetLineDash(px_args_t *par, px_state_t *pxs)
+{ px_gstate_t *pxgs = pxs->pxgs;
+ gs_state *pgs = pxs->pgs;
+
+ if ( par->pv[0] )
+ { float pattern[MAX_DASH_ELEMENTS * 2];
+ uint size = par->pv[0]->value.array.size;
+ real offset = (par->pv[1] ? real_value(par->pv[1], 0) : 0.0);
+ int code;
+
+ if ( par->pv[2] )
+ return_error(errorIllegalAttributeCombination);
+ if ( size > MAX_DASH_ELEMENTS )
+ return_error(errorIllegalArraySize);
+ /*
+ * The H-P documentation gives no clue about what a negative
+ * dash pattern element is supposed to do. The H-P printers
+ * apparently interpret it as drawing a line backwards in the
+ * current direction (which may extend outside the original
+ * subpath) with the caps inside the line instead of outside; a
+ * dash pattern with a negative total length crashes the LJ 6MP
+ * firmware so badly the printer has to be power cycled! We
+ * take a different approach here: we compensate for negative
+ * elements by propagating them to adjacent positive ones. This
+ * doesn't produce quite the same output as the H-P printers do,
+ * but this is such an obscure feature that we don't think it's
+ * worth the trouble to emulate exactly.
+ */
+ { uint orig_size = size;
+ int i;
+
+ /* Acquire the pattern, duplicating it if the length is odd. */
+ if ( size & 1 )
+ size <<= 1;
+ for ( i = 0; i < size; ++i )
+ pattern[i] = real_elt(par->pv[0], i % orig_size);
+ /* Get rid of negative draws. */
+ if ( pattern[0] < 0 )
+ offset -= pattern[0],
+ pattern[size - 1] += pattern[0],
+ pattern[1] += pattern[0],
+ pattern[0] = -pattern[0];
+ for ( i = 2; i < size; i += 2 )
+ if ( pattern[i] < 0 )
+ pattern[i - 1] += pattern[i],
+ pattern[i + 1] += pattern[i],
+ pattern[i] = -pattern[i];
+ /*
+ * Now propagate negative skips iteratively. Since each step
+ * decreases either the remaining total of negative skips or
+ * the total number of pattern elements, the process is
+ * guaranteed to terminate.
+ */
+elim: for ( i = 0; i < size; i += 2 )
+ { float draw = pattern[i], skip = pattern[i + 1];
+ int inext, iprev;
+ float next, prev;
+
+ if ( skip > 0 )
+ continue;
+ if ( size == 2 ) /* => i == 0 */
+ { if ( (pattern[0] = draw + skip) <= 0 )
+ pattern[0] = -pattern[0];
+ pattern[1] = 0;
+ break;
+ }
+ inext = (i == size - 2 ? 0 : i + 2);
+ next = pattern[inext];
+ /*
+ * Consider the sequence D, -S, E, where D and E are draws
+ * and -S is a negative skip. If S <= D, replace the 3
+ * elements with D - S + E.
+ */
+ if ( draw + skip >= 0 )
+ { next += draw + skip;
+ goto shrink;
+ }
+ /*
+ * Otherwise, let T be the skip preceding D. Replace T
+ * with T + D - S. If S > E, replace D, -S, E with E, S -
+ * (D + E), D; otherwise, replace D, -S, E with E. In
+ * both cases, net progress has occurred.
+ */
+ iprev = (i == 0 ? size - 1 : i - 1);
+ prev = pattern[iprev];
+ pattern[iprev] = prev + draw + skip;
+ if ( -skip > next )
+ { pattern[i] = next;
+ pattern[i + 1] = -(skip + draw + next);
+ pattern[i + 2] = draw;
+ goto elim;
+ }
+shrink: if ( inext == 0 )
+ { offset += next - pattern[0];
+ pattern[0] = next;
+ }
+ else
+ { pattern[i] = next;
+ memmove(&pattern[i + 1], &pattern[i + 3],
+ (size - (i + 3)) * sizeof(pattern[0]));
+ }
+ size -= 2;
+ goto elim;
+ }
+ }
+ code = gs_setdash(pgs, pattern, size, offset);
+ if ( code < 0 )
+ return code;
+ pxgs->dashed = size != 0;
+ gs_currentmatrix(pgs, &pxgs->dash_matrix);
+ return 0;
+ }
+ else if ( par->pv[2] )
+ { if ( par->pv[1] )
+ return_error(errorIllegalAttributeCombination);
+ pxgs->dashed = false;
+ return gs_setdash(pgs, NULL, 0, 0.0);
+ }
+ else
+ return_error(errorMissingAttribute);
+}
+
+const byte apxSetLineCap[] = {
+ pxaLineCapStyle, 0, 0
+};
+int
+pxSetLineCap(px_args_t *par, px_state_t *pxs)
+{ static const gs_line_cap cap_map[] = pxeLineCap_to_library;
+ return gs_setlinecap(pxs->pgs, cap_map[par->pv[0]->value.i]);
+}
+
+const byte apxSetLineJoin[] = {
+ pxaLineJoinStyle, 0, 0
+};
+int
+pxSetLineJoin(px_args_t *par, px_state_t *pxs)
+{ static const gs_line_join join_map[] = pxeLineJoin_to_library;
+ return gs_setlinejoin(pxs->pgs, join_map[par->pv[0]->value.i]);
+}
+
+const byte apxSetMiterLimit[] = {
+ pxaMiterLength, 0, 0
+};
+int
+pxSetMiterLimit(px_args_t *par, px_state_t *pxs)
+{ float limit = real_value(par->pv[0], 0);
+
+ if ( limit == 0 )
+ { /*
+ * H-P printers interpret this to mean use the default value
+ * of 10, even though nothing in the documentation says or
+ * implies this.
+ */
+ limit = 10;
+ }
+ else
+ { /* PCL XL, but not the library, allows limit values <1. */
+ if ( limit < 1 ) limit = 1;
+ }
+ return gs_setmiterlimit(pxs->pgs, limit);
+}
+
+const byte apxSetPageDefaultCTM[] = {0, 0};
+int
+pxSetPageDefaultCTM(px_args_t *par, px_state_t *pxs)
+{ gs_make_identity(&pxs->pxgs->text_ctm);
+ return gs_setmatrix(pxs->pgs, &pxs->initial_matrix);
+}
+
+const byte apxSetPageOrigin[] = {
+ pxaPageOrigin, 0, 0
+};
+int
+pxSetPageOrigin(px_args_t *par, px_state_t *pxs)
+{ return gs_translate(pxs->pgs, real_value(par->pv[0], 0),
+ real_value(par->pv[0], 1));
+}
+
+const byte apxSetPageRotation[] = {
+ pxaPageAngle, 0, 0
+};
+int
+pxSetPageRotation(px_args_t *par, px_state_t *pxs)
+{ /* Since the Y coordinate of user space is inverted, */
+ /* we must negate rotation angles. */
+ real angle = -real_value(par->pv[0], 0);
+ int code = gs_rotate(pxs->pgs, angle);
+
+ if ( code < 0 )
+ return code;
+ /* Post-multiply the text CTM by the rotation matrix. */
+ { gs_matrix rmat;
+ px_gstate_t *pxgs = pxs->pxgs;
+
+ gs_make_rotation(angle, &rmat);
+ gs_matrix_multiply(&pxgs->text_ctm, &rmat, &pxgs->text_ctm);
+ }
+ return 0;
+}
+
+const byte apxSetPageScale[] = {
+ pxaPageScale, 0, 0
+};
+int
+pxSetPageScale(px_args_t *par, px_state_t *pxs)
+{ real sx = real_value(par->pv[0], 0), sy = real_value(par->pv[0], 1);
+ int code = gs_scale(pxs->pgs, sx, sy);
+
+ if ( code < 0 )
+ return code;
+ /* Post-multiply the text CTM by the scale matrix. */
+ { gs_matrix smat;
+ px_gstate_t *pxgs = pxs->pxgs;
+
+ gs_make_scaling(sx, sy, &smat);
+ gs_matrix_multiply(&pxgs->text_ctm, &smat, &pxgs->text_ctm);
+ }
+ return 0;
+}
+
+const byte apxSetPaintTxMode[] = {
+ pxaTxMode, 0, 0
+};
+int
+pxSetPaintTxMode(px_args_t *par, px_state_t *pxs)
+{ gs_settexturetransparent(pxs->pgs, par->pv[0]->value.i == eTransparent);
+ return 0;
+}
+
+const byte apxSetPenWidth[] = {
+ pxaPenWidth, 0, 0
+};
+int
+pxSetPenWidth(px_args_t *par, px_state_t *pxs)
+{ return gs_setlinewidth(pxs->pgs, real_value(par->pv[0], 0));
+}
+
+const byte apxSetROP[] = {
+ pxaROP3, 0, 0
+};
+int
+pxSetROP(px_args_t *par, px_state_t *pxs)
+{ gs_setrasterop(pxs->pgs, (gs_rop3_t)(par->pv[0]->value.i));
+ return 0;
+}
+
+const byte apxSetSourceTxMode[] = {
+ pxaTxMode, 0, 0
+};
+int
+pxSetSourceTxMode(px_args_t *par, px_state_t *pxs)
+{ gs_setsourcetransparent(pxs->pgs, par->pv[0]->value.i == eTransparent);
+ return 0;
+}
+
+const byte apxSetCharBoldValue[] = {
+ pxaCharBoldValue, 0, 0
+};
+int
+pxSetCharBoldValue(px_args_t *par, px_state_t *pxs)
+{ pxs->pxgs->char_bold_value = real_value(par->pv[0], 0);
+ return 0;
+}
+
+const byte apxSetClipMode[] = {
+ pxaClipMode, 0, 0
+};
+int
+pxSetClipMode(px_args_t *par, px_state_t *pxs)
+{ pxs->pxgs->clip_mode = par->pv[0]->value.i;
+ return 0;
+}
+
+const byte apxSetPathToClip[] = {0, 0};
+int
+pxSetPathToClip(px_args_t *par, px_state_t *pxs)
+{ return gs_clippath(pxs->pgs);
+}
+
+const byte apxSetCharSubMode[] = {
+ pxaCharSubModeArray, 0, 0
+};
+int
+pxSetCharSubMode(px_args_t *par, px_state_t *pxs)
+{ /*
+ * It isn't clear from the documentation why the attribute is an
+ * array rather than just a Boolean, but we have to assume there
+ * is some reason for this.
+ */
+ const px_value_t *psubs = par->pv[0];
+
+ if ( psubs->value.array.size != 1 ||
+ psubs->value.array.data[0] >= pxeCharSubModeArray_next
+ )
+ return_error(errorIllegalAttributeValue);
+ pxs->pxgs->char_sub_mode = psubs->value.array.data[0];
+ return 0;
+}
diff --git a/pxl/pxgstate.h b/pxl/pxgstate.h
new file mode 100644
index 000000000..eb77b3e15
--- /dev/null
+++ b/pxl/pxgstate.h
@@ -0,0 +1,190 @@
+/* Copyright (C) 1996, 1997 Aladdin Enterprises. All rights reserved.
+ Unauthorized use, copying, and/or distribution prohibited.
+ */
+
+/* pxgstate.h */
+/* Graphics state extension for PCL XL interpreter */
+
+#ifndef pxgstate_INCLUDED
+# define pxgstate_INCLUDED
+
+#include "gsccolor.h" /* for gs_client_color */
+#include "gsmatrix.h" /* must precede gsiparam.h */
+#include "gsiparam.h" /* for px_image_color_space */
+#include "gsrefct.h"
+#include "gxbitmap.h"
+#include "plsymbol.h"
+#include "pxdict.h"
+#include "pxenum.h"
+
+/* Define an abstract type for the PostScript graphics state. */
+#ifndef gs_state_DEFINED
+# define gs_state_DEFINED
+typedef struct gs_state_s gs_state;
+#endif
+
+/* Define an abstract type for a font. */
+#ifndef px_font_t_DEFINED
+# define px_font_t_DEFINED
+typedef struct px_font_s px_font_t;
+#endif
+
+/* Define the type of the PCL XL state. */
+#ifndef px_state_DEFINED
+# define px_state_DEFINED
+typedef struct px_state_s px_state_t;
+#endif
+
+/* Define the parameters for a downloaded bitmap (image or raster pattern). */
+typedef struct px_bitmap_params_s {
+ uint width, height;
+ int depth;
+ pxeColorSpace_t color_space;
+ bool indexed;
+ real dest_width, dest_height;
+} px_bitmap_params_t;
+
+/* Define the structure for downloaded raster patterns. */
+typedef struct px_pattern_s {
+ rc_header rc; /* counts refs from gstates, dicts */
+ /* Original parameters */
+ px_bitmap_params_t params;
+ gs_const_string palette; /* copy of palette if indexed color */
+ byte *data; /* raster data */
+ /* Internal values */
+ gx_bitmap_id id; /* PCL XL ID * #persistence + persistence */
+} px_pattern_t;
+#define private_st_px_pattern() /* in pximage.c */\
+ gs_private_st_composite(st_px_pattern, px_pattern_t, "px_pattern_t",\
+ px_pattern_enum_ptrs, px_pattern_reloc_ptrs);
+/* Define the freeing procedure for patterns in a dictionary. */
+void px_free_pattern(P3(gs_memory_t *, void *, client_name_t));
+/* Purge the pattern cache up to a given persistence level. */
+void px_purge_pattern_cache(P2(px_state_t *, pxePatternPersistence_t));
+
+/* Define a structure for a brush or pen. These only exist */
+/* within a px_gstate_t; they are never allocated separately. */
+typedef enum {
+ pxpNull,
+ pxpGray,
+ pxpRGB,
+ pxpPattern
+} px_paint_type_t;
+typedef struct px_paint_s {
+ px_paint_type_t type;
+ union pv_ {
+ float gray;
+ float rgb[3];
+ struct p_ {
+ px_pattern_t *pattern;
+ gs_client_color color;
+ } pattern;
+ } value;
+ /* Cached values */
+ bool needs_halftone;
+} px_paint_t;
+#define private_st_px_paint() /* in pxgstate.c */\
+ gs_private_st_composite(st_px_paint, px_paint_t, "px_paint_t",\
+ px_paint_enum_ptrs, px_paint_reloc_ptrs)
+#define st_px_paint_max_ptrs (st_client_color_max_ptrs + 1)
+
+/* Define the types of character transformation, for remembering the order */
+/* in which to apply them. */
+typedef enum {
+ pxct_rotate = 0,
+ pxct_shear,
+ pxct_scale
+} px_char_transform_t;
+
+/* Define the PCL XL extension of the PostScript graphics state. */
+typedef struct px_gstate_s {
+ gs_memory_t *memory;
+ /* Since this is what the 'client data' of the gs_state points to, */
+ /* we need a pointer back to the px_state_t. */
+ px_state_t *pxs;
+ /* State information */
+ px_paint_t brush;
+ float char_angle;
+ float char_bold_value;
+ gs_point char_scale;
+ gs_point char_shear;
+ /*
+ * The strange H-P rules for character rotation, scaling, and
+ * shearing require us to keep track of the order in which they should
+ * be applied when we finally construct the text matrix.
+ * This array is always a permutation of the 3 possible values.
+ */
+ px_char_transform_t char_transforms[3];
+ pxeCharSubModeArray_t char_sub_mode;
+ pxeClipMode_t clip_mode;
+ pxeColorSpace_t color_space;
+ gs_const_string palette;
+ bool palette_is_shared; /* with next higher gstate */
+ float char_size;
+ uint symbol_set;
+ px_font_t *base_font; /* 0 if no font set */
+ struct ht_ {
+ pxeDitherMatrix_t method;
+ bool set; /* true if we have done gs_sethalftone */
+ /* with these parameters */
+ uint width;
+ uint height;
+ gs_point origin;
+ gs_string thresholds;
+ } halftone;
+ gs_string dither_matrix; /* dither matrix downloaded at this level */
+ pxeFillMode_t fill_mode;
+ bool dashed;
+ gs_matrix dash_matrix;
+ px_paint_t pen;
+ /* Pattern dictionary */
+ px_dict_t temp_pattern_dict;
+ /* Cached values */
+ gs_matrix text_ctm; /* scale/rotate transformations applied in */
+ /* the reverse order */
+ gs_matrix char_matrix; /* char_size+angle+scale+shear */
+ bool char_matrix_set;
+ int stack_depth; /* # of unmatched PushGS */
+ const pl_symbol_map_t *symbol_map; /* symbol mapping */
+} px_gstate_t;
+#define private_st_px_gstate() /* in pxgstate.c */\
+ gs_private_st_composite(st_px_gstate, px_gstate_t, "px_gstate_t",\
+ px_gstate_enum_ptrs, px_gstate_reloc_ptrs)
+#define px_gstate_do_ptrs(m)\
+ m(0,pxs) m(1,base_font)
+#define px_gstate_num_ptrs 2
+#define px_gstate_do_string_ptrs(m)\
+ m(0,halftone.thresholds) m(1,dither_matrix)
+#define px_gstate_num_string_ptrs 2
+
+/* Allocate a px_gstate_t. */
+px_gstate_t *px_gstate_alloc(P1(gs_memory_t *));
+
+/* Initialize a px_gstate_t. */
+void px_gstate_init(P2(px_gstate_t *, gs_state *));
+
+/* Initialize the graphics state for a page. */
+/* Note that this takes a px_state_t, not a px_gstate_t. */
+int px_initgraphics(P1(px_state_t *));
+
+/* Reset a px_gstate_t, initially or at the beginning of a page. */
+void px_gstate_reset(P1(px_gstate_t *));
+
+/* Set up the color space information for a bitmap image or pattern. */
+int px_image_color_space(P5(gs_color_space *pcs, gs_image_t *pim,
+ const px_bitmap_params_t *params,
+ const gs_const_string *palette,
+ const gs_state *pgs));
+
+/* Set the color in the graphics state to the pen or brush. */
+int px_set_paint(P2(const px_paint_t *ppt, px_state_t *pxs));
+
+/* Set the halftone in the graphics state to the most recently selected one. */
+int px_set_halftone(P1(px_state_t *pxs));
+
+/* Adjust the paint reference counts in the graphics state. */
+/* These are exported by pxgstate.c for pxink.c. */
+void px_paint_rc_adjust(P3(px_paint_t *ppt, int delta, gs_memory_t *mem));
+void px_gstate_rc_adjust(P3(px_gstate_t *pxgs, int delta, gs_memory_t *mem));
+
+#endif /* pxstate_INCLUDED */
diff --git a/pxl/pximage.c b/pxl/pximage.c
new file mode 100644
index 000000000..772993b42
--- /dev/null
+++ b/pxl/pximage.c
@@ -0,0 +1,685 @@
+/* Copyright (C) 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+ Unauthorized use, copying, and/or distribution prohibited.
+ */
+
+/* pximage.c */
+/* PCL XL bitmap painting operators */
+
+#include "std.h"
+#include "pxerrors.h"
+#include "pxoper.h"
+#include "pxstate.h"
+#include "gsrop.h"
+#include "gsrefct.h"
+#include "gsstruct.h"
+#include "gsstate.h"
+#include "gscoord.h"
+#include "gsimage.h"
+#include "gspaint.h"
+#include "gspath.h"
+#include "gspath2.h"
+#include "gsuid.h" /* for gxpcolor.h */
+#include "gsutil.h"
+#include "gxbitmap.h"
+#include "gxcspace.h"
+#include "gxdevice.h" /* for gxpcolor.h */
+#include "gxpcolor.h"
+#include "scommon.h"
+#include "strimpl.h"
+#include "srlx.h"
+#include "pldraw.h"
+
+/* GC descriptors */
+private_st_px_pattern();
+#define ppat ((px_pattern_t *)vptr)
+private ENUM_PTRS_BEGIN(px_pattern_enum_ptrs) return 0;
+ ENUM_CONST_STRING_PTR(0, px_pattern_t, palette);
+ ENUM_PTR(1, px_pattern_t, data);
+ENUM_PTRS_END
+private RELOC_PTRS_BEGIN(px_pattern_reloc_ptrs) {
+ RELOC_CONST_STRING_PTR(px_pattern_t, palette);
+ RELOC_PTR(px_pattern_t, data);
+} RELOC_PTRS_END
+#undef ppat
+
+/* Define the "freeing" procedure for patterns in a dictionary. */
+void
+px_free_pattern(gs_memory_t *mem, void *vptr, client_name_t cname)
+{ px_pattern_t *pattern = vptr;
+ rc_decrement(pattern, cname);
+}
+/* Define the real freeing procedure for patterns. */
+private void
+rc_free_px_pattern(gs_memory_t *mem, void *vptr, client_name_t cname)
+{ px_pattern_t *pattern = vptr;
+
+ gs_free_string(mem, pattern->palette.data, pattern->palette.size,
+ cname);
+ gs_free_object(mem, pattern->data, cname);
+ gs_free_object(mem, pattern, cname);
+}
+/* Define the purging procedure for the Pattern cache. */
+/* The proc_data points to a pxePatternPersistence_t that specifies */
+/* the maximum persistence level to purge. */
+private bool
+px_pattern_purge_proc(gx_color_tile *ctile, void *proc_data)
+{ return ctile->uid.id % pxePatternPersistence_next <=
+ *(pxePatternPersistence_t *)proc_data;
+}
+void
+px_purge_pattern_cache(px_state_t *pxs, pxePatternPersistence_t max_persist)
+{ gx_pattern_cache_winnow(gstate_pattern_cache(pxs->pgs),
+ px_pattern_purge_proc,
+ (void *)&max_persist);
+}
+
+/* Define the structure for enumerating a bitmap being downloaded. */
+typedef struct px_bitmap_enum_s {
+ uint input_per_row; /* # of (decompressed) input bytes per row */
+ uint data_per_row; /* ditto minus possible trailing padding */
+ bool initialized;
+ stream_RLD_state stream_state; /* decompressor state */
+} px_bitmap_enum_t;
+
+/* Define our image enumerator. */
+#ifndef px_image_enum_DEFINED
+# define px_image_enum_DEFINED
+typedef struct px_image_enum_s px_image_enum_t;
+#endif
+struct px_image_enum_s {
+ gs_color_space color_space; /* must be first, for subclassing */
+ gs_image_t image;
+ byte *rows; /* buffer for rows */
+ int num_rows;
+ int next_row;
+ uint raster;
+ void *info; /* state structure for driver */
+ px_bitmap_enum_t benum;
+};
+gs_private_st_suffix_add2(st_px_image_enum, px_image_enum_t, "px_image_enum_t",
+ px_image_enum_enum_ptrs, px_image_enum_reloc_ptrs, st_color_space,
+ image.ColorSpace, rows);
+
+/* ---------------- Utilities ---------------- */
+
+/* Extract the parameters for reading a bitmap image or raster pattern. */
+/* Attributes: pxaColorMapping, pxaColorDepth, pxaSourceWidth, */
+/* pxaSourceHeight, pxaDestinationSize. */
+private int
+begin_bitmap(px_bitmap_params_t *params, px_bitmap_enum_t *benum,
+ const px_args_t *par, const px_state_t *pxs)
+{ px_gstate_t *pxgs = pxs->pxgs;
+ int depth = "\001\004\010"[par->pv[1]->value.i];
+ int num_components = (pxgs->color_space == eGray ? 1 : 3);
+
+ params->width = par->pv[2]->value.i;
+ params->height = par->pv[3]->value.i;
+ params->depth = depth;
+ params->color_space = pxgs->color_space;
+ if ( par->pv[0]->value.i == eIndexedPixel )
+ { if ( pxgs->palette.data == 0 )
+ return_error(errorMissingPalette);
+ if ( pxgs->palette.size != (1 << depth) * num_components )
+ return_error(errorImagePaletteMismatch);
+ params->indexed = true;
+ num_components = 1;
+ }
+ else
+ params->indexed = false;
+ params->dest_width = real_value(par->pv[4], 0);
+ params->dest_height = real_value(par->pv[4], 1);
+ benum->data_per_row =
+ round_up(params->width * params->depth * num_components, 8) >> 3;
+ benum->input_per_row = round_up(benum->data_per_row, 4);
+ benum->initialized = false;
+ return 0;
+}
+
+/*
+ * Read a (possibly partial) row of bitmap data. This is most of the
+ * implementation of ReadImage and ReadRastPattern. We use source.position
+ * to track the uncompressed input byte position within the current block.
+ * Return 0 if we've processed all the data in the block, 1 if we have a
+ * complete scan line, pxNeedData for an incomplete scan line, or <0 for
+ * an error condition. *pdata must point to a scan line buffer; this
+ * routine may reset it to point into the input buffer (if a complete
+ * scan line is available).
+ * Attributes: pxaStartLine (ignored), pxaBlockHeight, pxaCompressMode.
+ */
+private int
+read_bitmap(px_bitmap_enum_t *benum, byte **pdata, px_args_t *par)
+{ uint input_per_row = benum->input_per_row;
+ ulong end_pos = (ulong)input_per_row * par->pv[1]->value.i;
+
+ if ( par->source.position >= end_pos )
+ return 0;
+ { uint data_per_row = benum->data_per_row;
+ uint pos_in_row = par->source.position % input_per_row;
+ const byte *data = par->source.data;
+ uint avail = par->source.available;
+ uint used;
+
+ if ( par->pv[2]->value.i == eRLECompression )
+ { /* RLE decompress the input. */
+#define ss (&benum->stream_state)
+#define st ((stream_state *)ss)
+ stream_cursor_read r;
+ stream_cursor_write w;
+
+ if ( !benum->initialized )
+ { ss->EndOfData = false;
+ s_RLD_init_inline(ss);
+ benum->initialized = true;
+ }
+ r.ptr = data - 1;
+ r.limit = r.ptr + avail;
+ if ( pos_in_row < data_per_row )
+ { /* Read more of the current row. */
+ byte *data = *pdata;
+
+ w.ptr = data + pos_in_row - 1;
+ w.limit = data + data_per_row - 1;
+ (*s_RLD_template.process)(st, &r, &w, false);
+ used = w.ptr + 1 - data - pos_in_row;
+ pos_in_row += used;
+ par->source.position += used;
+ }
+ if ( pos_in_row >= data_per_row && pos_in_row < input_per_row )
+ { /* We've read all the real data; skip the padding. */
+ byte pad[3]; /* maximum padding per row */
+
+ w.ptr = pad - 1;
+ w.limit = w.ptr + input_per_row - pos_in_row;
+ (*s_RLD_template.process)(st, &r, &w, false);
+ used = w.ptr + 1 - pad;
+ pos_in_row += used;
+ par->source.position += used;
+ }
+ used = r.ptr + 1 - data;
+ par->source.data = r.ptr + 1;
+ par->source.available = avail - used;
+ return (pos_in_row < input_per_row ? pxNeedData : 1);
+#undef ss
+#undef st
+ }
+ else
+ { /* Just read the input. */
+ int code;
+ if ( avail >= input_per_row && pos_in_row == 0 )
+ { /* Use the data directly from the input buffer. */
+ *pdata = (byte *)data; /* actually const */
+ used = input_per_row;
+ code = 1;
+ }
+ else
+ { used = min(avail, input_per_row - pos_in_row);
+ if ( pos_in_row < data_per_row )
+ memcpy(*pdata + pos_in_row, data,
+ min(used, data_per_row - pos_in_row));
+ code = (pos_in_row + used < input_per_row ?
+ pxNeedData : 1);
+ }
+ par->source.position += used;
+ par->source.data = data + used;
+ par->source.available = avail - used;
+ return code;
+ }
+ }
+}
+
+/* ---------------- Image operators ---------------- */
+
+const byte apxBeginImage[] = {
+ pxaColorMapping, pxaColorDepth, pxaSourceWidth, pxaSourceHeight,
+ pxaDestinationSize, 0, 0
+};
+int
+pxBeginImage(px_args_t *par, px_state_t *pxs)
+{ gs_point origin;
+ px_bitmap_params_t params;
+ px_bitmap_enum_t benum;
+ gs_state *pgs = pxs->pgs;
+ px_gstate_t *pxgs = pxs->pxgs;
+ px_image_enum_t *pxenum;
+ int code;
+
+ if ( gs_currentpoint(pgs, &origin) < 0 )
+ return_error(errorCurrentCursorUndefined);
+ /*
+ * If the current logical operation doesn't involve the texture,
+ * don't set a null brush, which would cause the image not to
+ * appear.
+ */
+ if ( pxs->pxgs->brush.type == pxpNull &&
+ !rop3_uses_T(gs_currentrasterop(pgs))
+ )
+ code = gs_setgray(pgs, 0.0);
+ else
+ code = px_set_paint(&pxgs->brush, pxs);
+ if ( code < 0 )
+ return code;
+ /*
+ * Make sure the proper halftone is current.
+ */
+ code = px_set_halftone(pxs);
+ if ( code < 0 )
+ return code;
+ code = begin_bitmap(&params, &benum, par, pxs);
+ if ( code < 0 )
+ return code;
+ pxenum =
+ gs_alloc_struct(pxs->memory, px_image_enum_t,
+ &st_px_image_enum, "setup_bitmap(pxenum)");
+
+ if ( pxenum == 0 )
+ return_error(errorInsufficientMemory);
+ { uint raster = round_up(benum.data_per_row, align_bitmap_mod);
+ int count = 400 / raster;
+
+ pxenum->num_rows = count = max(count, 1);
+ pxenum->next_row = 0;
+ pxenum->raster = raster;
+ pxenum->rows = gs_alloc_byte_array(pxs->memory, count, raster,
+ "pxReadImage(rows)");
+ if ( pxenum->rows == 0 )
+ code = gs_note_error(errorInsufficientMemory);
+ else
+ code = px_image_color_space(&pxenum->color_space, &pxenum->image,
+ &params, &pxgs->palette, pgs);
+ }
+ if ( code < 0 )
+ { gs_free_object(pxs->memory, pxenum->rows, "pxReadImage(rows)");
+ gs_free_object(pxs->memory, pxenum, "pxReadImage(pxenum)");
+ return code;
+ }
+ /* Set up the image parameters. */
+ pxenum->image.Width = params.width;
+ pxenum->image.Height = params.height;
+ { gs_matrix imat, dmat;
+ /* We need the cast because height is unsigned. */
+ /* We also need to account for the upside-down H-P */
+ /* coordinate system. */
+ gs_make_scaling(params.width, params.height, &imat);
+ gs_make_translation(origin.x, origin.y, &dmat);
+ gs_matrix_scale(&dmat, params.dest_width, params.dest_height,
+ &dmat);
+ /* The ImageMatrix is dmat' * imat. */
+ gs_matrix_invert(&dmat, &dmat);
+ gs_matrix_multiply(&dmat, &imat, &pxenum->image.ImageMatrix);
+ }
+ pxenum->image.CombineWithColor = true;
+ code = pl_begin_image(pgs, &pxenum->image, &pxenum->info);
+ if ( code < 0 )
+ { gs_free_object(pxs->memory, pxenum->rows, "pxReadImage(rows)");
+ gs_free_object(pxs->memory, pxenum, "pxBeginImage(pxenum)");
+ return code;
+ }
+ /*
+ * If the padding for the bitmap is at least as large as the
+ * padding in the file, we can eliminate some overhead by not
+ * treating the file padding specially.
+ */
+ if ( pxenum->raster >= benum.input_per_row )
+ benum.data_per_row = benum.input_per_row;
+ pxenum->benum = benum;
+ pxs->image_enum = pxenum;
+ return 0;
+}
+
+const byte apxReadImage[] = {
+ pxaStartLine, pxaBlockHeight, pxaCompressMode, 0, 0
+};
+int
+pxReadImage(px_args_t *par, px_state_t *pxs)
+{ px_image_enum_t *pxenum = pxs->image_enum;
+ gx_device *dev = gs_currentdevice(pxs->pgs);
+
+ if ( par->pv[1]->value.i == 0 )
+ return 0; /* no data */
+ /* Make a quick check for the first call, when no data is available. */
+ if ( par->source.available == 0 )
+ return pxNeedData;
+ if ( pxenum->num_rows == 1 )
+ { /* We only had space to buffer a single row. */
+ for ( ; ; )
+ { byte *data = pxenum->rows;
+ int code = read_bitmap(&pxenum->benum, &data, par);
+
+ if ( code != 1 )
+ return code;
+ code = (*dev_proc(dev, image_data))
+ (dev, pxenum->info, &data, 0, pxenum->benum.data_per_row, 1);
+ if ( code < 0 )
+ return code;
+ pxs->have_page = true;
+ }
+ }
+ else
+ { /* Buffer multiple rows to reduce fixed overhead. */
+ for ( ; ; )
+ { byte *data = pxenum->rows + pxenum->next_row * pxenum->raster;
+ byte *rdata = data;
+ int code = read_bitmap(&pxenum->benum, &rdata, par);
+
+ if ( code != 1 )
+ return code;
+ if ( rdata != data )
+ memcpy(data, rdata, pxenum->benum.data_per_row);
+ if ( ++(pxenum->next_row) == pxenum->num_rows )
+ { code = (*dev_proc(dev, image_data))
+ (dev, pxenum->info, &pxenum->rows, 0,
+ pxenum->raster, pxenum->num_rows);
+ pxenum->next_row = 0;
+ if ( code < 0 )
+ return code;
+ }
+ pxs->have_page = true;
+ }
+ }
+}
+
+const byte apxEndImage[] = {0, 0};
+int
+pxEndImage(px_args_t *par, px_state_t *pxs)
+{ gx_device *dev = gs_currentdevice(pxs->pgs);
+ px_image_enum_t *pxenum = pxs->image_enum;
+
+ if ( pxenum->next_row > 0 )
+ { /* Output any remaining rows. */
+ int code = (*dev_proc(dev, image_data))
+ (dev, pxenum->info, &pxenum->rows, 0,
+ pxenum->raster, pxenum->next_row);
+ pxenum->next_row = 0;
+ if ( code < 0 )
+ return code;
+ pxs->have_page = true;
+ }
+ (*dev_proc(dev, end_image))(dev, pxenum->info, true);
+ gs_free_object(pxs->memory, pxenum->rows, "pxEndImage(rows)");
+ gs_free_object(pxs->memory, pxenum, "pxEndImage(pxenum)");
+ pxs->image_enum = 0;
+ return 0;
+}
+
+/* ---------------- Raster pattern operators ---------------- */
+
+/* Define the enumerator for downloading raster patterns. */
+#ifndef px_pattern_enum_DEFINED
+# define px_pattern_enum_DEFINED
+typedef struct px_pattern_enum_s px_pattern_enum_t;
+#endif
+struct px_pattern_enum_s {
+ px_bitmap_enum_t benum;
+ integer pattern_id;
+ pxePatternPersistence_t persistence;
+ px_pattern_t *pattern;
+};
+gs_private_st_ptrs1(st_px_pattern_enum, px_pattern_enum_t,
+ "px_pattern_enum_t", pattern_enum_enum_ptrs, pattern_enum_reloc_ptrs,
+ pattern);
+
+const byte apxBeginRastPattern[] = {
+ pxaColorMapping, pxaColorDepth, pxaSourceWidth, pxaSourceHeight,
+ pxaDestinationSize, pxaPatternDefineID, pxaPatternPersistence, 0, 0
+};
+int
+pxBeginRastPattern(px_args_t *par, px_state_t *pxs)
+{ gs_memory_t *mem = pxs->memory;
+ px_bitmap_params_t params;
+ px_pattern_t *pattern;
+ px_pattern_enum_t *pxenum;
+ px_bitmap_enum_t benum;
+ byte *data;
+ uint psize;
+ byte *pdata;
+ int code = begin_bitmap(&params, &benum, par, pxs);
+
+ if ( code < 0 )
+ return code;
+ rc_alloc_struct_1(pattern, px_pattern_t, &st_px_pattern, mem,
+ return_error(errorInsufficientMemory),
+ "raster pattern");
+ pattern->rc.free = rc_free_px_pattern;
+ data = gs_alloc_byte_array(mem, params.height, benum.data_per_row,
+ "raster pattern data");
+ if ( params.indexed )
+ { psize = pxs->pxgs->palette.size;
+ pdata = gs_alloc_string(mem, psize, "raster pattern palette");
+ if ( pdata != 0 )
+ memcpy(pdata, pxs->pxgs->palette.data, psize);
+ }
+ else
+ { psize = 0;
+ pdata = 0;
+ }
+ pxenum = gs_alloc_struct(mem, px_pattern_enum_t, &st_px_pattern_enum,
+ "raster pattern enum");
+ if ( data == 0 || (params.indexed && pdata == 0) || pxenum == 0 )
+ { gs_free_object(mem, pxenum, "raster pattern enum");
+ gs_free_string(mem, pdata, psize, "raster pattern palette");
+ gs_free_object(mem, data, "raster pattern data");
+ gs_free_object(mem, pattern, "raster pattern");
+ return_error(errorInsufficientMemory);
+ }
+ pxenum->benum = benum;
+ pxenum->pattern_id = par->pv[5]->value.i;
+ pxenum->persistence = par->pv[6]->value.i;
+ pattern->params = params;
+ pattern->palette.data = pdata;
+ pattern->palette.size = psize;
+ pattern->data = data;
+ pattern->id = gs_next_ids(1);
+ pxenum->pattern = pattern;
+ pxs->pattern_enum = pxenum;
+ return 0;
+}
+
+const byte apxReadRastPattern[] = {
+ pxaStartLine, pxaBlockHeight, pxaCompressMode, 0, 0
+};
+int
+pxReadRastPattern(px_args_t *par, px_state_t *pxs)
+{ px_pattern_enum_t *pxenum = pxs->pattern_enum;
+ int code;
+
+ /* Make a quick check for the first call, when no data is available. */
+ if ( par->source.available == 0 && par->pv[1]->value.i != 0 )
+ return pxNeedData;
+ for ( ; ; )
+ { byte *data = pxenum->pattern->data +
+ (par->pv[0]->value.i +
+ par->source.position / pxenum->benum.input_per_row)
+ * pxenum->benum.data_per_row;
+ byte *rdata = data;
+
+ code = read_bitmap(&pxenum->benum, &rdata, par);
+ if ( code != 1 )
+ break;
+ if ( rdata != data )
+ memcpy(data, rdata, pxenum->benum.data_per_row);
+ }
+ return code;
+}
+
+const byte apxEndRastPattern[] = {0, 0};
+int
+pxEndRastPattern(px_args_t *par, px_state_t *pxs)
+{ px_pattern_enum_t *pxenum = pxs->pattern_enum;
+ /* We extract the key and value from the pattern_enum structure */
+ /* and then free the structure, to encourage LIFO allocation. */
+ px_pattern_t *pattern = pxenum->pattern;
+ integer id = pxenum->pattern_id;
+ px_value_t key;
+ px_dict_t *pdict;
+
+ switch ( pxenum->persistence )
+ {
+ case eTempPattern:
+ pdict = &pxs->pxgs->temp_pattern_dict;
+ break;
+ case ePagePattern:
+ pdict = &pxs->page_pattern_dict;
+ break;
+ case eSessionPattern:
+ pdict = &pxs->session_pattern_dict;
+ break;
+ default: /* can't happen */
+ return_error(errorIllegalAttributeValue);
+ }
+ key.type = pxd_array | pxd_ubyte;
+ key.value.array.data = (byte *)&id;
+ key.value.array.size = sizeof(id);
+ gs_free_object(pxs->memory, pxenum, "pxEndRastPattern(pxenum)");
+ return px_dict_put(pdict, &key, pattern);
+}
+
+/* ---------------- Scan line operators ---------------- */
+
+const byte apxBeginScan[] = {0, 0};
+int
+pxBeginScan(px_args_t *par, px_state_t *pxs)
+{ int code = px_set_paint(&pxs->pxgs->brush, pxs);
+
+ if ( code < 0 )
+ return code;
+ /* We may as well reset the path now instead of at the end. */
+ return gs_newpath(pxs->pgs);
+}
+
+const byte apxEndScan[] = {0, 0};
+int
+pxEndScan(px_args_t *par, px_state_t *pxs)
+{ return 0;
+}
+
+const byte apxScanLineRel[] = {
+ 0, pxaNumberOfScanLines, 0
+};
+int
+pxScanLineRel(px_args_t *par, px_state_t *pxs)
+{ /*
+ * In order to keep the number of intermediate states down to a
+ * reasonable number, we require enough data to be present to be
+ * able to read the control information for each line, or an entire
+ * x-pair. Initially, source.position is zero. As soon as we have
+ * read the X/YStart type byte, we change it to:
+ * (X/YStart type) << 28 + (x-pair type << 24) +
+ * (# of full or partial scan lines left to process) + 1
+ * We use the separate variable source.count to keep track of
+ * the number of x-pairs left in the scan line.
+ */
+ gs_state *pgs = pxs->pgs;
+ bool big_endian = pxs->data_source_big_endian;
+ const byte *data = par->source.data;
+ pxeDataType_t
+ xystart_type = (par->source.position >> 28) & 0xf,
+ xpair_type = (par->source.position >> 24) & 0xf;
+ int code = 0;
+ int rcount;
+ gs_rect rlist[20]; /* 20 is arbitrary */
+
+ /* Check for initial state. */
+ if ( par->source.position == 0 )
+ { /* Read XStart/YStart data type. */
+ if ( par->source.available < 1 )
+ return pxNeedData;
+ xystart_type = data[0];
+ if ( xystart_type != eSInt16 )
+ return_error(errorIllegalDataValue);
+ par->source.position =
+ ((ulong)xystart_type << 28) +
+ (par->pv[0] ? par->pv[0]->value.i : 1) + 1;
+ par->source.data = data += 1;
+ par->source.available -= 1;
+ par->source.count = 0;
+ }
+ for ( rcount = 0; ; )
+ {
+
+ /* Check for start of scan line. */
+ if ( par->source.count == 0 )
+ { int ystart;
+
+ if ( (par->source.position & 0xffffff) == 1 )
+ { code = 0;
+ break;
+ }
+ /* Read XStart and YStart values. */
+ /* We know that eSInt16 is the only possible data type. */
+ if ( par->source.available < 7 )
+ { code = pxNeedData;
+ break;
+ }
+ pxs->scan_point.x = sint16at(data, big_endian);
+ ystart = sint16at(data + 2, big_endian);
+ pxs->scan_point.y0 = ystart - 0.5;
+ pxs->scan_point.y1 = ystart + 0.5;
+ par->source.count = uint16at(data + 4, big_endian);
+ if ( par->source.count == 0 )
+ { code = gs_note_error(errorIllegalDataValue);
+ break;
+ }
+ xpair_type = data[6];
+ par->source.position =
+ (par->source.position & 0xf0ffffff) +
+ ((ulong)xpair_type << 24);
+ par->source.data = data += 7;
+ par->source.available -= 7;
+ }
+ /* Read and process one x-pair. */
+ { uint x0, x1;
+ uint used;
+
+ switch ( xpair_type )
+ {
+ case eUByte:
+ if ( par->source.available < 2 )
+ { code = pxNeedData;
+ goto out; /* 2-level break */
+ }
+ x0 = data[0];
+ x1 = data[1];
+ used = 2;
+ break;
+ case eUInt16:
+ if ( par->source.available < 4 )
+ { code = pxNeedData;
+ goto out; /* 2-level break */
+ }
+ x0 = uint16at(data, big_endian);
+ x1 = uint16at(data + 2, big_endian);
+ used = 4;
+ break;
+ default:
+ code = gs_note_error(errorIllegalDataValue);
+ goto out; /* 2-level break; */
+ }
+ if ( rcount == countof(rlist) )
+ { code = gs_rectfill(pgs, rlist, rcount);
+ rcount = 0;
+ pxs->have_page = true;
+ if ( code < 0 )
+ break;
+ }
+ { gs_rect *pr = &rlist[rcount++];
+
+ pr->p.x = pxs->scan_point.x += x0;
+ pr->p.y = pxs->scan_point.y0;
+ pr->q.x = pxs->scan_point.x += x1;
+ pr->q.y = pxs->scan_point.y1;
+ }
+ par->source.data = data += used;
+ par->source.available -= used;
+ }
+ if ( !--(par->source.count) )
+ --(par->source.position);
+ }
+out:
+ if ( rcount > 0 && code >= 0 )
+ { int rcode = gs_rectfill(pgs, rlist, rcount);
+ pxs->have_page = true;
+ if ( rcode < 0 )
+ code = rcode;
+ }
+ return code;
+}
diff --git a/pxl/pxink.c b/pxl/pxink.c
new file mode 100644
index 000000000..63efff194
--- /dev/null
+++ b/pxl/pxink.c
@@ -0,0 +1,714 @@
+/* Copyright (C) 1997, 1998 Aladdin Enterprises. All rights reserved.
+ Unauthorized use, copying, and/or distribution prohibited.
+ */
+
+/* pxink.c */
+/* PCL XL ink setting operators */
+
+#include "math_.h"
+#include "stdio_.h" /* for NULL */
+#include "gstypes.h"
+#include "gsmemory.h"
+#include "pxoper.h"
+#include "pxstate.h"
+#include "gxarith.h"
+#include "gsstate.h"
+#include "gxcspace.h" /* must precede gscolor2.h */
+#include "gscolor2.h"
+#include "gscoord.h"
+#include "gsimage.h"
+#include "gspath.h"
+#include "gxdevice.h"
+#include "gxht.h"
+#include "gxstate.h"
+
+/*
+ * Contrary to the documentation, SetColorSpace apparently doesn't set the
+ * brush or pen to black. To produce this behavior, uncomment the
+ * following #define.
+ */
+#define SET_COLOR_SPACE_NO_SET_BLACK
+
+/* Forward references */
+private int px_set_default_screen(P3(px_state_t *pxs, int method,
+ const gs_point *origin));
+
+/* ---------------- Utilities ---------------- */
+
+/* ------ Halftones ------ */
+
+/* Define a transfer function without gamma correction. */
+private float
+identity_transfer(floatp tint, const gx_transfer_map *ignore_map)
+{ return tint;
+}
+
+/* Set the default halftone screen. */
+static const byte order16x16[256] = {
+#if 0
+ /*
+ * The following is a standard 16x16 ordered dither, except that
+ * the very last pass goes in the order (0,1,2,3) rather than
+ * (0,3,1,2). This leads to excessive cancellation when the source
+ * and paint halftones interact, but it's better than the standard
+ * order, which has inadequate cancellation.
+ *
+ * This matrix is generated by the following call:
+ * [ <00 88 08 80> <00 44 04 40> <00 22 02 20> <00 01 10 11> ]
+ * { } makedither
+ */
+ 0,64,32,96,8,72,40,104,2,66,34,98,10,74,42,106,
+ 128,192,160,224,136,200,168,232,130,194,162,226,138,202,170,234,
+ 48,112,16,80,56,120,24,88,50,114,18,82,58,122,26,90,
+ 176,240,144,208,184,248,152,216,178,242,146,210,186,250,154,218,
+ 12,76,44,108,4,68,36,100,14,78,46,110,6,70,38,102,
+ 140,204,172,236,132,196,164,228,142,206,174,238,134,198,166,230,
+ 60,124,28,92,52,116,20,84,62,126,30,94,54,118,22,86,
+ 188,252,156,220,180,244,148,212,190,254,158,222,182,246,150,214,
+ 3,67,35,99,11,75,43,107,1,65,33,97,9,73,41,105,
+ 131,195,163,227,139,203,171,235,129,193,161,225,137,201,169,233,
+ 51,115,19,83,59,123,27,91,49,113,17,81,57,121,25,89,
+ 179,243,147,211,187,251,155,219,177,241,145,209,185,249,153,217,
+ 15,79,47,111,7,71,39,103,13,77,45,109,5,69,37,101,
+ 143,207,175,239,135,199,167,231,141,205,173,237,133,197,165,229,
+ 63,127,31,95,55,119,23,87,61,125,29,93,53,117,21,85,
+ 191,255,159,223,183,247,151,215,189,253,157,221,181,245,149,213
+# define source_phase_x 1
+# define source_phase_y 1
+#else
+/*
+ * The following is a 45 degree spot screen with the spots enumerated
+ * in a defined order. This matrix is generated by the following call:
+
+/gamma_transfer {
+ dup dup 0 le exch 1 ge or not {
+ dup 0.17 lt
+ { 3 mul }
+ { dup 0.35 lt { 0.78 mul 0.38 add } { 0.53 mul 0.47 add } ifelse }
+ ifelse
+ } if
+} def
+
+[ [0 136 8 128 68 204 76 196]
+ [18 33 17 34 1 2 19 35 50 49 32 16 3 48 0 51
+ -15 -14 20 36 66 65 47 31 -13 64 15 52 -30 81 30 37]
+] /gamma_transfer load makedither
+
+ */
+ 38,11,14,32,165,105,90,171,38,12,14,33,161,101,88,167,
+ 30,6,0,16,61,225,231,125,30,6,1,17,63,222,227,122,
+ 27,3,8,19,71,242,205,110,28,4,9,20,74,246,208,106,
+ 35,24,22,40,182,46,56,144,36,25,22,41,186,48,58,148,
+ 152,91,81,174,39,12,15,34,156,95,84,178,40,13,16,34,
+ 69,212,235,129,31,7,2,18,66,216,239,133,32,8,2,18,
+ 79,254,203,114,28,4,10,20,76,250,199,118,29,5,10,21,
+ 193,44,54,142,36,26,23,42,189,43,52,139,37,26,24,42,
+ 39,12,15,33,159,99,87,169,38,11,14,33,163,103,89,172,
+ 31,7,1,17,65,220,229,123,30,6,1,17,62,223,233,127,
+ 28,4,9,20,75,248,210,108,27,3,9,19,72,244,206,112,
+ 36,25,23,41,188,49,60,150,35,25,22,41,184,47,57,146,
+ 157,97,85,180,40,13,16,35,154,93,83,176,39,13,15,34,
+ 67,218,240,135,32,8,3,19,70,214,237,131,31,7,2,18,
+ 78,252,197,120,29,5,11,21,80,255,201,116,29,5,10,21,
+ 191,43,51,137,37,27,24,43,195,44,53,140,37,26,23,42
+# define source_phase_x 4
+# define source_phase_y 0
+#endif
+};
+private int
+px_set_default_screen(px_state_t *pxs, int method, const gs_point *origin)
+{ gs_state *pgs = pxs->pgs;
+ int px = (int)origin->x, py = (int)origin->y;
+ gs_halftone ht;
+ int code;
+
+ gs_settransfer(pgs, identity_transfer);
+ ht.type = ht_type_threshold;
+ ht.params.threshold.width = ht.params.threshold.height = 16;
+ ht.params.threshold.thresholds.data = order16x16;
+ ht.params.threshold.thresholds.size = 256;
+ ht.params.threshold.transfer = 0;
+ ht.params.threshold.transfer_closure.proc = 0;
+ code = gs_sethalftone(pgs, &ht);
+ if ( code < 0 )
+ return code;
+ code = gs_sethalftonephase(pgs, px, py);
+ /*
+ * Here is where we do the dreadful thing that appears to be
+ * necessary to match the observed behavior of LaserJet 5 and
+ * 6 printers with respect to superimposing halftoned source
+ * and pattern.
+ */
+ if ( code < 0 )
+ return code;
+ return gs_setscreenphase(pgs, px + source_phase_x,
+ py + source_phase_y, gs_color_select_source);
+}
+
+/* Set the size for a default halftone screen. */
+private void
+px_set_default_screen_size(px_state_t *pxs, int method)
+{ px_gstate_t *pxgs = pxs->pxgs;
+
+ pxgs->halftone.width = pxgs->halftone.height = 16;
+}
+
+/* If necessary, set the halftone in the graphics state. */
+int
+px_set_halftone(px_state_t *pxs)
+{ px_gstate_t *pxgs = pxs->pxgs;
+ int code;
+
+ if ( pxgs->halftone.set )
+ return 0;
+ if ( pxgs->halftone.method != eDownloaded )
+ code = px_set_default_screen(pxs, pxgs->halftone.method,
+ &pxgs->halftone.origin);
+ else
+ { gs_state *pgs = pxs->pgs;
+ gs_halftone ht;
+
+ ht.type = ht_type_threshold;
+ switch ( pxs->orientation )
+ {
+ case ePortraitOrientation:
+ case eReversePortrait:
+ ht.params.threshold.width = pxgs->halftone.width;
+ ht.params.threshold.height = pxgs->halftone.height;
+ break;
+ case eLandscapeOrientation:
+ case eReverseLandscape:
+ ht.params.threshold.width = pxgs->halftone.height;
+ ht.params.threshold.height = pxgs->halftone.width;
+ break;
+ }
+ /* Stupid C compilers don't allow structure assignment where */
+ /* the only incompatibility is an assignment of a T * to a */
+ /* const T * (which *is* allowed as a simple assignment): */
+ ht.params.threshold.thresholds.data =
+ pxgs->halftone.thresholds.data;
+ ht.params.threshold.thresholds.size =
+ pxgs->halftone.thresholds.size;
+ /* Downloaded dither matrices disable the transfer function. */
+ ht.params.threshold.transfer = identity_transfer;
+ code = gs_sethalftone(pgs, &ht);
+ if ( code >= 0 )
+ code = gs_sethalftonephase(pgs,
+ (int)pxgs->halftone.origin.x,
+ (int)pxgs->halftone.origin.y);
+ if ( code < 0 )
+ gs_free_string(pxs->memory, pxgs->halftone.thresholds.data,
+ pxgs->halftone.thresholds.size,
+ "px_set_halftone(thresholds)");
+ else
+ { gs_free_string(pxs->memory, pxgs->dither_matrix.data,
+ pxgs->dither_matrix.size,
+ "px_set_halftone(dither_matrix)");
+ pxgs->dither_matrix = pxgs->halftone.thresholds;
+ }
+ pxgs->halftone.thresholds.data = 0;
+ pxgs->halftone.thresholds.size = 0;
+ }
+ if ( code < 0 )
+ return code;
+ pxgs->halftone.set = true;
+ /* Cached patterns have already been halftoned, so clear the cache. */
+ px_purge_pattern_cache(pxs, eSessionPattern);
+ return 0;
+}
+
+/* ------ Patterns ------ */
+
+/*
+ * The library caches patterns in their fully rendered form, i.e., after
+ * halftoning. In order to avoid seams or anomalies, we have to replicate
+ * the pattern so that its size is an exact multiple of the halftone size.
+ */
+
+private uint
+ilcm(uint x, uint y)
+{ return x * (y / igcd(x, y));
+}
+
+/* Render a pattern. */
+private int
+px_paint_pattern(const gs_client_color *pcc, gs_state *pgs)
+{ const gs_client_pattern *ppat = gs_getpattern(pcc);
+ const px_pattern_t *pattern = ppat->client_data;
+ const byte *dp = pattern->data;
+ gs_image_enum *penum;
+ gs_color_space color_space;
+ gs_image_t image;
+ int code;
+ int num_components =
+ (pattern->params.indexed || pattern->params.color_space == eGray ?
+ 1 : 3);
+ uint rep_width = pattern->params.width;
+ uint rep_height = pattern->params.height;
+ uint full_width = (uint)ppat->XStep;
+ uint full_height = (uint)ppat->YStep;
+ uint bits_per_row, bytes_per_row;
+ int x;
+
+ code = px_image_color_space(&color_space, &image, &pattern->params,
+ &pattern->palette, pgs);
+ if ( code < 0 )
+ return code;
+ penum = gs_image_enum_alloc(gs_state_memory(pgs), "px_paint_pattern");
+ if ( penum == 0 )
+ return_error(errorInsufficientMemory);
+ bits_per_row = rep_width * image.BitsPerComponent * num_components;
+ bytes_per_row = (bits_per_row + 7) >> 3;
+ /*
+ * As noted above, in general, we have to replicate the original
+ * pattern to a multiple that avoids halftone seams. If the
+ * number of bits per row is a multiple of 8, we can do this with
+ * a single image; otherwise, we need one image per X replica.
+ * To simplify the code, we always use the (slightly) slower method.
+ */
+ image.Width = rep_width;
+ image.Height = full_height;
+ for ( x = 0; x < full_width; x += rep_width )
+ { int y;
+
+ image.ImageMatrix.tx = -x;
+ code = gs_image_init(penum, &image, false, pgs);
+ if ( code < 0 )
+ break;
+ for ( y = 0; code >= 0 && y < full_height; ++y )
+ { const byte *row = dp + (y % rep_height) * bytes_per_row;
+ uint used;
+
+ code = gs_image_next(penum, row, bytes_per_row, &used);
+ }
+ gs_image_cleanup(penum);
+ }
+ gs_free_object(gs_state_memory(pgs), penum, "px_paint_pattern");
+ return code;
+}
+
+/* Create the rendering of a pattern. */
+private int
+render_pattern(gs_client_color *pcc, const px_pattern_t *pattern,
+ const px_value_t *porigin, const px_value_t *pdsize, px_state_t *pxs)
+{ px_gstate_t *pxgs = pxs->pxgs;
+ uint rep_width = pattern->params.width;
+ uint rep_height = pattern->params.height;
+ uint full_width, full_height;
+ gs_state *pgs = pxs->pgs;
+ gs_client_pattern template;
+
+ /*
+ * If halftoning may occur, replicate the pattern so we don't get
+ * halftone seams.
+ */
+ { gx_device *dev = gs_currentdevice(pgs);
+ if ( dev->color_info.max_gray > 31 &&
+ (dev->color_info.num_components == 1 ||
+ dev->color_info.max_color > 31)
+ )
+ { /* No halftoning. */
+ full_width = rep_width;
+ full_height = rep_height;
+ }
+ else
+ { full_width = ilcm(rep_width, pxgs->halftone.width);
+ full_height = ilcm(rep_height, pxgs->halftone.height);
+ /*
+ * If the pattern would be enormous, don't replicate it.
+ * This is a HACK.
+ */
+ if ( full_width > 10000 )
+ full_width = rep_width;
+ if ( full_height > 10000 )
+ full_height = rep_height;
+ }
+ }
+ /* Construct a Pattern for the library, and render it. */
+ uid_set_UniqueID(&template.uid, pattern->id);
+ template.PaintType = 1;
+ template.TilingType = 1;
+ template.BBox.p.x = 0;
+ template.BBox.p.y = 0;
+ template.BBox.q.x = full_width;
+ template.BBox.q.y = full_height;
+ template.XStep = full_width;
+ template.YStep = full_height;
+ template.PaintProc = px_paint_pattern;
+ template.client_data = pattern;
+ { gs_matrix mat;
+ gs_point dsize;
+ int code;
+
+ if ( porigin )
+ gs_make_translation(real_value(porigin, 0),
+ real_value(porigin, 1), &mat);
+ else
+ gs_make_identity(&mat);
+ if ( pdsize )
+ { dsize.x = real_value(pdsize, 0);
+ dsize.y = real_value(pdsize, 1);
+ }
+ else
+ { dsize.x = pattern->params.dest_width;
+ dsize.y = pattern->params.dest_height;
+ }
+ gs_matrix_scale(&mat, dsize.x / rep_width, dsize.y / rep_height,
+ &mat);
+ /*
+ * gs_makepattern will make a copy of the current gstate.
+ * We don't want this copy to contain any circular back pointers
+ * to px_pattern_ts: such pointers are unnecessary, because
+ * px_paint_pattern doesn't use the pen and brush (in fact,
+ * it doesn't even reference the px_gstate_t). We also want to
+ * reset the path and clip path. The easiest (although not by
+ * any means the most efficient) way to do this is to do a gsave,
+ * reset the necessary things, do the makepattern, and then do
+ * a grestore.
+ */
+ code = gs_gsave(pgs);
+ if ( code < 0 )
+ return code;
+ { px_gstate_t *pxgs = pxs->pxgs;
+
+ px_gstate_rc_adjust(pxgs, -1, pxgs->memory);
+ pxgs->brush.type = pxgs->pen.type = pxpNull;
+ gs_newpath(pgs);
+ gs_initclip(pgs);
+ }
+ code = gs_makepattern(pcc, &template, &mat, pgs, NULL);
+ gs_grestore(pgs);
+ return code;
+ }
+}
+
+/* ------ Brush/pen ------ */
+
+/* Check parameters and execute SetBrushSource or SetPenSource: */
+/* pxaRGBColor, pxaGrayLevel, pxaNullBrush/Pen, pxaPatternSelectID, */
+/* pxaPatternOrigin, pxaNewDestinationSize */
+private ulong near
+int_type_max(px_data_type_t type)
+{ return
+ (type & pxd_ubyte ? 255 :
+ type & pxd_uint16 ? 65535 :
+ type & pxd_sint16 ? 32767 :
+ type & pxd_uint32 ? (ulong)0xffffffff :
+ /* type & pxd_sint32 */ 0x7fffffff);
+}
+private real near
+fraction_value(const px_value_t *pv, int i)
+{ px_data_type_t type = pv->type;
+ real v;
+
+ if ( type & pxd_real32 )
+ return pv->value.ra[i];
+ v = pv->value.ia[i];
+ return (v < 0 ? 0 : v / int_type_max(type));
+}
+private int near
+set_source(const px_args_t *par, px_state_t *pxs, px_paint_t *ppt)
+{ px_gstate_t *pxgs = pxs->pxgs;
+ int code = 0;
+
+ if ( par->pv[3] ) /* pxaPatternSelectID */
+ { px_value_t key;
+ void *value;
+ px_pattern_t *pattern;
+ gs_client_color ccolor;
+ int code;
+
+ if ( par->pv[0] || par->pv[1] || par->pv[2] )
+ return_error(errorIllegalAttributeCombination);
+ key.type = pxd_array | pxd_ubyte;
+ key.value.array.data = (byte *)&par->pv[3]->value.i;
+ key.value.array.size = sizeof(integer);
+ if ( !(px_dict_find(&pxgs->temp_pattern_dict, &key, &value) ||
+ px_dict_find(&pxs->page_pattern_dict, &key, &value) ||
+ px_dict_find(&pxs->session_pattern_dict, &key, &value))
+ )
+ return_error(errorRasterPatternUndefined);
+ pattern = value;
+ code = render_pattern(&ccolor, pattern, par->pv[4], par->pv[5],
+ pxs);
+ /*
+ * We don't use px_paint_rc_adjust(... 1 ...) here, because
+ * gs_makepattern creates pattern instances with a reference
+ * count already set to 1.
+ */
+ rc_increment(pattern);
+ if ( code < 0 )
+ return code;
+ px_paint_rc_adjust(ppt, -1, pxs->memory);
+ ppt->type = pxpPattern;
+ ppt->value.pattern.pattern = pattern;
+ ppt->value.pattern.color = ccolor;
+ ppt->needs_halftone = true;
+ }
+ else if ( par->pv[4] || par->pv[5] )
+ return_error(errorIllegalAttributeCombination);
+ else if ( par->pv[0] ) /* pxaRGBColor */
+ { const px_value_t *prgb = par->pv[0];
+ uint i;
+
+ if ( par->pv[1] || par->pv[2] )
+ return_error(errorIllegalAttributeCombination);
+ if ( pxgs->color_space != eRGB )
+ return_error(errorColorSpaceMismatch);
+ px_paint_rc_adjust(ppt, -1, pxs->memory);
+ ppt->type = pxpRGB;
+ for ( i = 0; i < 3; ++i )
+ if ( prgb->type & pxd_any_real )
+ ppt->value.rgb[i] = real_elt(prgb, i);
+ else
+ { integer v = integer_elt(prgb, i);
+ ppt->value.rgb[i] =
+ (v < 0 ? 0 : (real)v / int_type_max(prgb->type));
+ }
+#define rgb_is(v)\
+ (ppt->value.rgb[0] == v && ppt->value.rgb[1] == v && ppt->value.rgb[2] == v)
+ ppt->needs_halftone = !(rgb_is(0) || rgb_is(1));
+#undef rgb_is
+ }
+ else if ( par->pv[1] ) /* pxaGrayLevel */
+ { if ( par->pv[2] )
+ return_error(errorIllegalAttributeCombination);
+ if ( pxgs->color_space != eGray )
+ return_error(errorColorSpaceMismatch);
+ px_paint_rc_adjust(ppt, -1, pxs->memory);
+ ppt->type = pxpGray;
+ ppt->value.gray = fraction_value(par->pv[1], 0);
+ ppt->needs_halftone = ppt->value.gray != 0 && ppt->value.gray != 1;
+ }
+ else if ( par->pv[2] ) /* pxaNullBrush/Pen */
+ { px_paint_rc_adjust(ppt, -1, pxs->memory);
+ ppt->type = pxpNull;
+ ppt->needs_halftone = false;
+ }
+ else
+ return_error(errorMissingAttribute);
+ /*
+ * Update the halftone to the most recently set one.
+ * This will do the wrong thing if we set the brush or pen source,
+ * set the halftone, and then set the other source, but we have
+ * no way to handle this properly with the current library.
+ */
+ if ( code >= 0 && ppt->needs_halftone )
+ code = px_set_halftone(pxs);
+ return code;
+}
+
+/* Set up a brush or pen for drawing. */
+/* If it is a pattern, SetBrush/PenSource guaranteed that it is compatible */
+/* with the current color space. */
+int
+px_set_paint(const px_paint_t *ppt, px_state_t *pxs)
+{ gs_state *pgs = pxs->pgs;
+
+ switch ( ppt->type )
+ {
+ case pxpNull:
+ gs_setnullcolor(pgs);
+ return 0;
+ case pxpGray:
+ return gs_setgray(pgs, ppt->value.gray);
+ case pxpRGB:
+ return gs_setrgbcolor(pgs, ppt->value.rgb[0], ppt->value.rgb[1],
+ ppt->value.rgb[2]);
+ case pxpPattern:
+ return gs_setpattern(pgs, &ppt->value.pattern.color);
+ default: /* can't happen */
+ return_error(errorIllegalAttributeValue);
+ }
+}
+
+/* ---------------- Operators ---------------- */
+
+const byte apxSetBrushSource[] = {
+ 0, pxaRGBColor, pxaGrayLevel, pxaNullBrush, pxaPatternSelectID,
+ pxaPatternOrigin, pxaNewDestinationSize, 0
+};
+int
+pxSetBrushSource(px_args_t *par, px_state_t *pxs)
+{ return set_source(par, pxs, &pxs->pxgs->brush);
+}
+
+const byte apxSetColorSpace[] = {
+ pxaColorSpace, 0, pxaPaletteDepth, pxaPaletteData, 0
+};
+int
+pxSetColorSpace(px_args_t *par, px_state_t *pxs)
+{ px_gstate_t *pxgs = pxs->pxgs;
+ pxeColorSpace_t cspace = par->pv[0]->value.i;
+
+ if ( par->pv[1] && par->pv[2] )
+ { int ncomp = (cspace == eRGB ? 3 : 1);
+ uint size = par->pv[2]->value.array.size;
+
+ if ( !(size == ncomp << 1 || size == ncomp << 4 ||
+ size == ncomp << 8)
+ )
+ return_error(errorIllegalAttributeValue);
+ /* The palette is in an array, but we want a string. */
+ { if ( pxgs->palette.data && !pxgs->palette_is_shared &&
+ pxgs->palette.size != size
+ )
+ { gs_free_string(pxs->memory, pxgs->palette.data,
+ pxgs->palette.size,
+ "pxSetColorSpace(old palette)");
+ pxgs->palette.data = 0;
+ pxgs->palette.size = 0;
+ }
+ if ( pxgs->palette.data == 0 || pxgs->palette_is_shared )
+ { byte *pdata =
+ gs_alloc_string(pxs->memory, size,
+ "pxSetColorSpace(palette)");
+
+ if ( pdata == 0 )
+ return_error(errorInsufficientMemory);
+ pxgs->palette.data = pdata;
+ pxgs->palette.size = size;
+ }
+ memcpy(pxgs->palette.data, par->pv[2]->value.array.data, size);
+ }
+ }
+ else if ( par->pv[1] || par->pv[2] )
+ return_error(errorMissingAttribute);
+ else if ( pxgs->palette.data )
+ { if ( !pxgs->palette_is_shared )
+ gs_free_string(pxs->memory, pxgs->palette.data,
+ pxgs->palette.size,
+ "pxSetColorSpace(old palette)");
+ pxgs->palette.data = 0;
+ pxgs->palette.size = 0;
+ }
+ pxgs->palette_is_shared = false;
+ pxgs->color_space = cspace;
+#ifndef SET_COLOR_SPACE_NO_SET_BLACK
+ { px_paint_rc_adjust(&pxgs->brush, -1, pxs->memory);
+ pxgs->brush.type = pxpGray;
+ pxgs->brush.value.gray = 0;
+ }
+ { px_paint_rc_adjust(&pxgs->pen, -1, pxs->memory);
+ pxgs->pen.type = pxpGray;
+ pxgs->pen.value.gray = 0;
+ }
+#endif
+ return 0;
+}
+
+const byte apxSetHalftoneMethod[] = {
+ 0, pxaDitherOrigin, pxaDeviceMatrix, pxaDitherMatrixDataType,
+ pxaDitherMatrixSize, pxaDitherMatrixDepth, 0
+};
+int
+pxSetHalftoneMethod(px_args_t *par, px_state_t *pxs)
+{ gs_state *pgs = pxs->pgs;
+ px_gstate_t *pxgs = pxs->pxgs;
+ pxeDitherMatrix_t method;
+
+ if ( par->pv[1] )
+ { /* Internal halftone */
+ if ( par->pv[2] || par->pv[3] || par->pv[4] )
+ return_error(errorIllegalAttributeCombination);
+ method = par->pv[1]->value.i;
+ px_set_default_screen_size(pxs, method);
+ pxs->download_string.data = 0;
+ pxs->download_string.size = 0;
+ }
+ else if ( par->pv[2] && par->pv[3] && par->pv[4] )
+ { /* Dither matrix */
+ uint width = par->pv[3]->value.ia[0];
+ uint source_width = (width + 3) & ~3;
+ uint height = par->pv[3]->value.ia[1];
+ uint size = width * height;
+ uint source_size = source_width * height;
+
+ if ( par->source.position == 0 )
+ { byte *data;
+ if ( par->source.available == 0 )
+ return pxNeedData;
+ data = gs_alloc_string(pxs->memory, size, "dither matrix");
+ if ( data == 0 )
+ return_error(errorInsufficientMemory);
+ pxs->download_string.data = data;
+ pxs->download_string.size = size;
+ }
+ while ( par->source.position < source_size )
+ { uint source_x = par->source.position % source_width;
+ uint source_y = par->source.position / source_width;
+ uint used;
+
+ if ( par->source.available == 0 )
+ return pxNeedData;
+ if ( source_x >= width )
+ { /* Skip padding bytes at end of row. */
+ used = min(par->source.available, source_width - source_x);
+ }
+ else
+ { /* Read data. */
+ const byte *src = par->source.data;
+ byte *dest = pxs->download_string.data;
+ uint i;
+ int skip;
+
+ used = min(par->source.available, width - source_x);
+ /*
+ * The documentation doesn't say this, but we have to
+ * rotate the dither matrix to match the orientation,
+ * remembering that we have a Y-inverted coordinate
+ * system. This is quite a nuisance!
+ */
+ switch ( pxs->orientation )
+ {
+ case ePortraitOrientation:
+ dest += source_y * width + source_x;
+ skip = 1;
+ break;
+ case eLandscapeOrientation:
+ dest += (width - 1 - source_x) * height + source_y;
+ skip = -height;
+ break;
+ case eReversePortrait:
+ dest += (height - 1 - source_y) * width +
+ width - 1 - source_x;
+ skip = -1;
+ break;
+ case eReverseLandscape:
+ dest += source_x * height + width - 1 - source_y;
+ skip = height;
+ break;
+ }
+ for ( i = 0; i < used; ++i, ++src, dest += skip )
+ *dest = *src;
+ }
+ par->source.position += used;
+ par->source.available -= used;
+ par->source.data += used;
+
+ }
+ pxgs->halftone.width = width;
+ pxgs->halftone.height = height;
+ method = eDownloaded;
+ }
+ else
+ return_error(errorMissingAttribute);
+ if ( par->pv[0] )
+ gs_transform(pgs, real_value(par->pv[0], 0),
+ real_value(par->pv[0], 1), &pxgs->halftone.origin);
+ else
+ gs_transform(pgs, 0.0, 0.0, &pxgs->halftone.origin);
+ pxgs->halftone.thresholds = pxs->download_string;
+ pxgs->halftone.method = method;
+ pxgs->halftone.set = false;
+ return 0;
+}
+
+const byte apxSetPenSource[] = {
+ 0, pxaRGBColor, pxaGrayLevel, pxaNullPen, pxaPatternSelectID,
+ pxaPatternOrigin, pxaNewDestinationSize, 0
+};
+int
+pxSetPenSource(px_args_t *par, px_state_t *pxs)
+{ return set_source(par, pxs, &pxs->pxgs->pen);
+}
diff --git a/pxl/pxl.mak b/pxl/pxl.mak
new file mode 100644
index 000000000..584f65764
--- /dev/null
+++ b/pxl/pxl.mak
@@ -0,0 +1,196 @@
+# Copyright (C) 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+# Unauthorized use, copying, and/or distribution prohibited.
+
+# makefile for PCL XL interpreters.
+# Users of this makefile must define the following:
+# GLSRCDIR - the GS library source directory
+# GLGENDIR - the GS library generated file directory
+# PLSRCDIR - the PCL* support library source directory
+# PLOBJDIR - the PCL* support library object / executable directory
+# PXLSRCDIR - the source directory
+# PXLGENDIR - the directory for source files generated during building
+# PXLOBJDIR - the object / executable directory
+
+PLOBJ=$(PLOBJDIR)$(D)
+
+PXLSRC=$(PXLSRCDIR)$(D)
+PXLGEN=$(PXLGENDIR)$(D)
+PXLOBJ=$(PXLOBJDIR)$(D)
+PXLO_=$(O_)$(PXLOBJ)
+
+PXLCCC=$(CCC) -I$(PXLSRCDIR) -I$(PXLGENDIR) -I$(PLSRCDIR) -I$(GLSRCDIR) -I$(GLGENDIR) $(C_)
+
+# Define the name of this makefile.
+PXL_MAK=$(PXLSRC)pxl.mak
+
+pxl.clean: pxl.config-clean pxl.clean-not-config-clean
+
+# We don't delete these files, just in case PXLGEN = PXLSRC.
+# $(RMN_) $(PXLGEN)pxbfont.c $(PXLGEN)pxsymbol.c $(PXLGEN)pxsymbol.h
+pxl.clean-not-config-clean:
+ $(RM_) $(PXLOBJ)*.$(OBJ)
+
+pxl.config-clean:
+ $(RM_) $(PXLOBJ)*.dev
+
+################ PCL XL ################
+
+#### Functionality left to implement:
+# Symbol set mapping
+#### Other stuff:
+# Free subsidiary objects when freeing patterns
+
+pxattr_h=$(PXLSRC)pxattr.h $(gdevpxat_h)
+pxbfont_h=$(PXLSRC)pxbfont.h
+pxenum_h=$(PXLSRC)pxenum.h $(gdevpxen_h)
+pxerrors_h=$(PXLSRC)pxerrors.h
+pxfont_h=$(PXLSRC)pxfont.h $(plfont_h)
+pxptable_h=$(PXLSRC)pxptable.h
+pxtag_h=$(PXLSRC)pxtag.h $(gdevpxop_h)
+pxsymbol_h=$(PXLGEN)pxsymbol.h
+pxvalue_h=$(PXLSRC)pxvalue.h $(gstypes_h) $(pxattr_h)
+# Nested headers
+pxdict_h=$(PXLSRC)pxdict.h $(pldict_h) $(pxvalue_h)
+pxgstate_h=$(PXLSRC)pxgstate.h $(gsccolor_h) $(gsiparam_h) $(gsmatrix_h) $(gsrefct_h) $(gxbitmap_h) $(plsymbol_h) $(pxdict_h) $(pxenum_h)
+pxoper_h=$(PXLSRC)pxoper.h $(gserror_h) $(pxattr_h) $(pxerrors_h) $(pxvalue_h)
+pxparse_h=$(PXLSRC)pxparse.h $(pxoper_h)
+pxstate_h=$(PXLSRC)pxstate.h $(gsmemory_h) $(pxgstate_h)
+
+# To avoid having to build the Ghostscript interpreter to generate pxbfont.c,
+# we normally ship a pre-constructed pxbfont.c with the source code.
+# If the following rule doesn't work, try:
+# /usr/bin/gs -I/usr/lib/ghostscript -q -dNODISPLAY pxbfont.ps >pxbfont.c
+$(PXLSRC)pxbfont.c: $(PXLSRC)pxbfont.ps
+ $(GS_XE) -q -dNODISPLAY $(PXLSRC)pxbfont.ps >$(PXLSRC)pxbfont.c
+
+$(PXLGEN)pxbfont.c: $(PXLSRC)pxbfont.c
+ $(CP_) $(PXLSRC)pxbfont.c $(PXLGEN)pxbfont.c
+
+$(PXLOBJ)pxbfont.$(OBJ): $(PXLGEN)pxbfont.c $(AK) $(stdpre_h)\
+ $(pxbfont_h)
+ $(PXLCCC) $(PXLGEN)pxbfont.c $(PXLO_)pxbfont.$(OBJ)
+
+$(PXLOBJ)pxerrors.$(OBJ): $(PXLSRC)pxerrors.c $(AK)\
+ $(memory__h) $(stdio__h) $(string__h)\
+ $(gsccode_h) $(gscoord_h) $(gsmatrix_h) $(gsmemory_h)\
+ $(gspaint_h) $(gspath_h) $(gsstate_h) $(gstypes_h) $(gsutil_h)\
+ $(gxchar_h) $(gxfixed_h) $(gxfont_h) $(scommon_h)\
+ $(pxbfont_h) $(pxerrors_h) $(pxfont_h) $(pxparse_h) $(pxptable_h) $(pxstate_h)
+ $(PXLCCC) $(PXLSRC)pxerrors.c $(PXLO_)pxerrors.$(OBJ)
+
+$(PXLOBJ)pxparse.$(OBJ): $(PXLSRC)pxparse.c $(AK) $(memory__h) $(stdio__h)\
+ $(gdebug_h) $(gserror_h) $(gstypes_h)\
+ $(plparse_h)\
+ $(pxattr_h) $(pxenum_h) $(pxerrors_h) $(pxoper_h) $(pxparse_h) $(pxptable_h)\
+ $(pxstate_h) $(pxtag_h) $(pxvalue_h)
+ $(PXLCCC) $(PXLSRC)pxparse.c $(PXLO_)pxparse.$(OBJ)
+
+$(PXLOBJ)pxstate.$(OBJ): $(PXLSRC)pxstate.c $(AK) $(stdio__h)\
+ $(gsmemory_h) $(gsstruct_h) $(gstypes_h)\
+ $(pxstate_h)
+ $(PXLCCC) $(PXLSRC)pxstate.c $(PXLO_)pxstate.$(OBJ)
+
+# See the comment above under pxbfont.c.
+# /usr/bin/gs -I/usr/lib/ghostscript -q -dNODISPLAY pxsymbol.ps >pxsymbol.c
+$(PXLSRC)pxsymbol.h: $(PXLSRC)pxsymbol.ps
+ $(GS_XE) -q -dNODISPLAY -dHEADER $(PXLSRC)pxsymbol.ps >$(PXLSRC)pxsymbol.h
+
+$(PXLGEN)pxsymbol.h: $(PXLSRC)pxsymbol.h
+ $(CP_) $(PXLSRC)pxsymbol.h $(PXLGEN)pxsymbol.h
+
+# See the comment above under pxbfont.c.
+# /usr/bin/gs -I/usr/lib/ghostscript -q -dNODISPLAY -dHEADER pxsymbol.ps >pxsymbol.h
+$(PXLSRC)pxsymbol.c: $(PXLSRC)pxsymbol.ps
+ $(GS_XE) -q -dNODISPLAY $(PXLSRC)pxsymbol.ps >$(PXLSRC)pxsymbol.c
+
+$(PXLGEN)pxsymbol.c: $(PXLSRC)pxsymbol.c
+ $(CP_) $(PXLSRC)pxsymbol.c $(PXLGEN)pxsymbol.c
+
+$(PXLOBJ)pxsymbol.$(OBJ): $(PXLGEN)pxsymbol.c $(AK) $(pxsymbol_h)
+ $(PXLCCC) $(PXLGEN)pxsymbol.c $(PXLO_)pxsymbol.$(OBJ)
+
+$(PXLOBJ)pxptable.$(OBJ): $(PXLSRC)pxptable.c $(AK) $(std_h)\
+ $(pxenum_h) $(pxoper_h) $(pxptable_h) $(pxvalue_h)
+ $(PXLCCC) $(PXLSRC)pxptable.c $(PXLO_)pxptable.$(OBJ)
+
+$(PXLOBJ)pxvalue.$(OBJ): $(PXLSRC)pxvalue.c $(AK) $(std_h) $(gsmemory_h) $(pxvalue_h)
+ $(PXLCCC) $(PXLSRC)pxvalue.c $(PXLO_)pxvalue.$(OBJ)
+
+# We have to break up pxl_other because of the MS-DOS command line
+# limit of 120 characters.
+pxl_other_obj1=$(PXLOBJ)pxbfont.$(OBJ) $(PXLOBJ)pxerrors.$(OBJ) $(PXLOBJ)pxparse.$(OBJ)
+pxl_other_obj2=$(PXLOBJ)pxstate.$(OBJ) $(PXLOBJ)pxptable.$(OBJ) $(PXLOBJ)pxvalue.$(OBJ)
+pxl_other_obj=$(pxl_other_obj1) $(pxl_other_obj2)
+
+# Operators
+
+# This implements finding fonts by name, but doesn't implement any operators
+# per se.
+$(PXLOBJ)pxffont.$(OBJ): $(PXLSRC)pxffont.c $(AK) $(string__h)\
+ $(gschar_h) $(gsmatrix_h) $(gx_h) $(gxfont_h) $(gxfont42_h)\
+ $(pxfont_h) $(pxoper_h) $(pxstate_h)
+ $(PXLCCC) $(PXLSRC)pxffont.c $(PXLO_)pxffont.$(OBJ)
+
+$(PXLOBJ)pxfont.$(OBJ): $(PXLSRC)pxfont.c $(AK) $(math__h) $(stdio__h) $(string__h)\
+ $(gdebug_h) $(gschar_h) $(gscoord_h) $(gserrors_h) $(gsimage_h)\
+ $(gspaint_h) $(gspath_h) $(gsstate_h) $(gsstruct_h) $(gsutil_h)\
+ $(gxchar_h) $(gxfixed_h) $(gxfont_h) $(gxfont42_h) $(gxpath_h) $(gzstate_h)\
+ $(plvalue_h)\
+ $(pxfont_h) $(pxoper_h) $(pxstate_h)
+ $(PXLCCC) $(PXLSRC)pxfont.c $(PXLO_)pxfont.$(OBJ)
+
+$(PXLOBJ)pxgstate.$(OBJ): $(PXLSRC)pxgstate.c $(AK) $(math__h) $(memory__h) $(stdio__h)\
+ $(gscoord_h) $(gsimage_h) $(gsmemory_h) $(gspath_h) $(gspath2_h) $(gsrop_h)\
+ $(gsstate_h) $(gsstruct_h) $(gstypes_h)\
+ $(gxcspace_h) $(gxstate_h)\
+ $(pxoper_h) $(pxstate_h)
+ $(PXLCCC) $(PXLSRC)pxgstate.c $(PXLO_)pxgstate.$(OBJ)
+
+$(PXLOBJ)pximage.$(OBJ): $(PXLSRC)pximage.c $(AK) $(std_h)\
+ $(gscoord_h) $(gsimage_h) $(gspaint_h) $(gspath_h) $(gspath2_h)\
+ $(gsrefct_h) $(gsrop_h) $(gsstate_h) $(gsstruct_h) $(gsuid_h) $(gsutil_h)\
+ $(gxbitmap_h) $(gxcspace_h) $(gxdevice_h) $(gxpcolor_h)\
+ $(scommon_h) $(srlx_h) $(strimpl_h)\
+ $(pldraw_h)\
+ $(pxerrors_h) $(pxoper_h) $(pxstate_h)
+ $(PXLCCC) $(PXLSRC)pximage.c $(PXLO_)pximage.$(OBJ)
+
+$(PXLOBJ)pxink.$(OBJ): $(PXLSRC)pxink.c $(math__h) $(stdio__h)\
+ $(gscolor2_h) $(gscoord_h) $(gsimage_h) $(gsmemory_h) $(gspath_h)\
+ $(gstypes_h)\
+ $(gxarith_h) $(gxcspace_h) $(gxdevice_h) $(gxht_h) $(gxstate_h)\
+ $(pxoper_h) $(pxstate_h)
+ $(PXLCCC) $(PXLSRC)pxink.c $(PXLO_)pxink.$(OBJ)
+
+$(PXLOBJ)pxpaint.$(OBJ): $(PXLSRC)pxpaint.c $(AK) $(math__h) $(stdio__h)\
+ $(gscoord_h) $(gspaint_h) $(gspath_h) $(gspath2_h) $(gsrop_h) $(gsstate_h)\
+ $(gxfarith_h) $(gxfixed_h) $(gxistate_h) $(gxmatrix_h) $(gxpath_h)\
+ $(pxfont_h) $(pxoper_h) $(pxstate_h)
+ $(PXLCCC) $(PXLSRC)pxpaint.c $(PXLO_)pxpaint.$(OBJ)
+
+$(PXLOBJ)pxsessio.$(OBJ): $(PXLSRC)pxsessio.c $(AK) $(math__h) $(stdio__h)\
+ $(gschar_h) $(gscoord_h) $(gserrors_h) $(gspaint_h) $(gsparam_h) $(gsstate_h)\
+ $(gxfcache_h) $(gxfixed_h)\
+ $(pxfont_h) $(pxoper_h) $(pxstate_h)
+ $(PXLCCC) $(PXLSRC)pxsessio.c $(PXLO_)pxsessio.$(OBJ)
+
+$(PXLOBJ)pxstream.$(OBJ): $(PXLSRC)pxstream.c $(AK) $(memory__h)\
+ $(gsmemory_h) $(scommon_h)\
+ $(pxoper_h) $(pxparse_h) $(pxstate_h)
+ $(PXLCCC) $(PXLSRC)pxstream.c $(PXLO_)pxstream.$(OBJ)
+
+# We have to break up pxl_ops because of the MS-DOS command line
+# limit of 120 characters.
+pxl_ops_obj1=$(PXLOBJ)pxffont.$(OBJ) $(PXLOBJ)pxfont.$(OBJ) $(PXLOBJ)pxgstate.$(OBJ) $(PXLOBJ)pximage.$(OBJ)
+pxl_ops_obj2=$(PXLOBJ)pxink.$(OBJ) $(PXLOBJ)pxpaint.$(OBJ) $(PXLOBJ)pxsessio.$(OBJ) $(PXLOBJ)pxstream.$(OBJ)
+pxl_ops_obj=$(pxl_ops_obj1) $(pxl_ops_obj2)
+
+# Note that we must initialize pxfont before pxerrors.
+$(PXLOBJ)pxl.dev: $(PXL_MAK) $(ECHOGS_XE) $(pxl_other_obj) $(pxl_ops_obj)\
+ $(PLOBJ)pl.dev $(PLOBJ)pjl.dev
+ $(SETMOD) $(PXLOBJ)pxl $(pxl_other_obj1)
+ $(ADDMOD) $(PXLOBJ)pxl $(pxl_other_obj2)
+ $(ADDMOD) $(PXLOBJ)pxl $(pxl_ops_obj1)
+ $(ADDMOD) $(PXLOBJ)pxl $(pxl_ops_obj2)
+ $(ADDMOD) $(PXLOBJ)pxl -include $(PLOBJ)pl $(PLOBJ)pjl
+ $(ADDMOD) $(PXLOBJ)pxl -init pxfont pxerrors
diff --git a/pxl/pxl_top.mak b/pxl/pxl_top.mak
new file mode 100644
index 000000000..9ccbdffcd
--- /dev/null
+++ b/pxl/pxl_top.mak
@@ -0,0 +1,38 @@
+# Copyright (C) 1997 Aladdin Enterprises. All rights reserved.
+# Unauthorized use, copying, and/or distribution prohibited.
+
+# pxl_top.mak
+# Top-level platform-independent makefile for PCL XL
+
+# This file must be preceded by pxl.mak.
+
+#DEVICE_DEVS is defined in the platform-specific file.
+FEATURE_DEVS=colimlib.dev dps2lib.dev path1lib.dev patlib.dev psl2cs.dev rld.dev roplib.dev ttflib.dev
+
+default: $(TARGET_XE)
+ echo Done.
+
+clean: config-clean clean-not-config-clean
+
+clean-not-config-clean: pl.clean-not-config-clean pxl.clean-not-config-clean
+ $(RMN_) $(TARGET_XE)
+
+config-clean: pl.config-clean pxl.config-clean
+ $(RMN_) *.tr $(GD)devs.tr$(CONFIG) $(GD)ld$(CONFIG).tr
+ $(RMN_) $(PXLGEN)pconf$(CONFIG).h $(PXLGEN)pconfig.h
+
+#### Main program
+
+# Note: we always compile the main program with -DDEBUG.
+$(PXLOBJ)pxmain.$(OBJ): $(PXLSRC)pxmain.c $(AK)\
+ $(malloc__h) $(math__h) $(memory__h) $(stdio__h) $(string__h)\
+ $(gdebug_h) $(gp_h)\
+ $(gsargs_h) $(gscdefs_h) $(gscoord_h) $(gsdevice_h) $(gserrors_h) $(gsgc_h)\
+ $(gslib_h) $(gsmatrix_h) $(gsmemory_h) $(gspaint_h) $(gsparam_h)\
+ $(gsstate_h) $(gsstruct_h) $(gstypes_h)\
+ $(gxalloc_h) $(gxstate_h)\
+ $(plmain_h) $(plparse_h) $(pjparse_h)\
+ $(pxattr_h) $(pxerrors_h) $(pxparse_h) $(pxptable_h) $(pxstate_h) $(pxvalue_h)\
+ $(PXLGEN)pconf$(CONFIG).h
+ $(CP_) $(PXLGEN)pconf$(CONFIG).h $(PXLGEN)pconfig.h
+ $(PXLCCC) $(PXLSRC)pxmain.c $(PXLO_)pxmain.$(OBJ)
diff --git a/pxl/pxl_ugcc.mak b/pxl/pxl_ugcc.mak
new file mode 100644
index 000000000..1540ae275
--- /dev/null
+++ b/pxl/pxl_ugcc.mak
@@ -0,0 +1,59 @@
+# Copyright (C) 1997, 1998 Aladdin Enterprises. All rights reserved.
+# Unauthorized use, copying, and/or distribution prohibited.
+
+# pxl_ugcc.mak
+# Top-level makefile for PCL XL on Unix/gcc platforms.
+
+# Define the name of this makefile.
+MAKEFILE=../pxl/pxl_ugcc.mak
+
+# Directories
+GLSRCDIR=/home/henrys/tmp/gs5.28
+GLGENDIR=../pxl/obj
+GLOBJDIR=../pxl/obj
+PLSRCDIR=../pl
+PLGENDIR=../pxl/obj
+PLOBJDIR=../pxl/obj
+PXLSRCDIR=../pxl
+PXLGENDIR=../pxl/obj
+PXLOBJDIR=../pxl/obj
+COMMONDIR=../common
+GENDIR=$(PXLGENDIR)
+
+# Language and configuration. These are actually platform-independent,
+# but we define them here just to keep all parameters in one place.
+CONFIG=6
+TARGET_DEVS=$(PXLOBJDIR)/pxl.dev
+TARGET_XE=pclxl
+MAIN_OBJ=$(PXLOBJDIR)/pxmain.$(OBJ)
+
+# Assorted definitions. Some of these should probably be factored out....
+# We use -O0 for debugging, because optimization confuses gdb.
+# Note that the omission of -Dconst= rules out the use of gcc versions
+# between 2.7.0 and 2.7.2 inclusive. (2.7.2.1 is OK.)
+#GCFLAGS=-Dconst= -Wall -Wpointer-arith -Wstrict-prototypes
+GCFLAGS=-Wall -Wcast-qual -Wpointer-arith -Wstrict-prototypes -Wwrite-strings
+CFLAGS=-g -O0 $(GCFLAGS) $(XCFLAGS)
+LDFLAGS=$(XLDFLAGS)
+EXTRALIBS=
+XINCLUDE=-I/usr/local/X/include
+XLIBDIRS=-L/usr/X11/lib
+XLIBDIR=
+XLIBS=Xt SM ICE Xext X11
+
+CCLD=gcc
+
+DEVICE_DEVS=x11.dev x11mono.dev x11alpha.dev x11cmyk.dev\
+ djet500.dev ljet4.dev\
+ pcxmono.dev pcxgray.dev\
+ pbmraw.dev pgmraw.dev ppmraw.dev
+
+# Generic makefile
+include $(COMMONDIR)/ugcc_top.mak
+
+# Subsystems
+include $(PLSRCDIR)/pl.mak
+include $(PXLSRCDIR)/pxl.mak
+
+# Main program.
+include $(PXLSRCDIR)/pxl_top.mak
diff --git a/pxl/pxlib.txt b/pxl/pxlib.txt
new file mode 100644
index 000000000..e7b42a0c1
--- /dev/null
+++ b/pxl/pxlib.txt
@@ -0,0 +1,222 @@
+
+ Copyright (C) 1997 Aladdin Enterprises. All rights reserved.
+ Unauthorized use, copying, and/or distribution prohibited.
+
+This document describes the additions to Aladdin's PostScript-oriented
+graphics library that were required to handle the full PCL graphics model.
+
+ Graphics model extensions from PostScript to PCL
+
+Introduction
+============
+
+The PCL XL graphics model is very close to the PostScript model, but it is
+not identical, and it includes several features that are difficult or
+impossible to emulate using the PostScript concepts. This document presents
+the additions we made to our PostScript-oriented graphics library in order
+to implement the full PCL XL graphics model. We have noted the places where
+we did this only for efficiency, and where we saw no alternative.
+
+PCL5 (including HP-GL/2) also requires a very small number of additions
+beyond PCL XL. We have included these in this document as well.
+
+Changes in a given revision of this document are marked with the revision
+number in [brackets]. Revision history:
+ first issued January 18, 1997
+
+Both PCL5 and PCL XL
+====================
+
+RasterOp and transparency
+-------------------------
+
+All drawing operations must be capable of incorporating an arbitrary 3-input
+Boolean operation (RasterOp). With the RasterOp extension, imaging
+operations compute a function D = F(D,S,T), where F is an arbitrary 3-input
+Boolean function, D is the destination (frame buffer or print buffer), S is
+the source (described below), and T is the texture (always the current
+PostScript color, which may be a pattern). The source and texture depend on
+the PostScript imaging operation:
+
+ - For fill and stroke, the source is solid black, covering the
+ region to be painted.
+
+ - For *show and imagemask, the source is solid black, covering the
+ pixels to be painted.
+
+ - For image and colorimage, the source is the image data.
+
+On black-and-white devices, D, S, and T are considered to be 1-bit values in
+RGB space; this require modifying F if the device actually uses 0 = white, 1
+= black. (Our library handles this automatically.) On such devices, F is
+applied directly to the rendered bits, i.e. after transfer function and
+halftoning. On color (even non-contone) or gray-scale devices, D, S, and T
+are considered to be N-bit RGB values, and F is applied before any color
+realization.
+
+The source and destination each also have a "transparent" flag: if the
+corresponding flag is set, then white pixels in the source or destination
+respectively suppress writing into the frame buffer, regardless of what F
+may othewise compute. For black-and-white devices, transparency can be
+implemented by modifying F; for gray-scale or color devices, an explicit
+additional test is required.
+
+We see no reasonable way for clients to implement RasterOp, or transparency
+on non-black-and-white devices.
+
+Triangular caps
+---------------
+
+PCL supports triangular caps. If the square cap looks like this:
+
+ ________
+ | |
+ | |
+ | |
+
+then the triangular cap looks like this:
+
+ / \
+ / \
+ | |
+
+i.e. its lines meet the ends of the stroke at 135 degrees, and each other at
+90 degrees.
+
+If the library provides a way for clients to get the result of applying the
+current dash pattern to a path (similar in spirit to flattenpath), clients
+could in principle implement triangular caps.
+
+Null joins
+----------
+
+PCL supports a join style called null joins, in which a pair of caps is used
+instead of a join. I.e., with null joins, each line, curve, or arc is
+considered to be a separate subpath. Null join style does not affect the
+junctures between the line segments produced by flattening curves or arcs:
+these are always "smooth". (When null joins are selected, our library uses
+bevel joins between curve and arc segments.)
+
+While in principle clients could implement null joins, it would require
+extra work because it is the join style in effect at path painting time, not
+at path construction time, that determines the output; thus the client
+cannot use the obvious approach of breaking the path up as it is being
+constructed. Instead, the client must record the sequence of path drawing
+operations and replay them in modified form if the join style at painting
+time is different from the join style at construction time.
+
+Dots
+----
+
+When a dash pattern includes zero-length drawn segments, PostScript
+interpreters produce a dot if round caps are in effect, and nothing
+otherwise. H-P's PCL XL interpreters, however, always draw a pair of caps
+(including a hairline for butt caps); something similar happens in PCL5.
+
+We thought clients could implement this by replacing zero-length drawn
+elements of the dash pattern with tiny nearly-zero-length elements.
+However, this doesn't work:
+
+ - If the tiny element shortens the following skipped distance, then
+no dot will get drawn at the very end of a line that is an exact multiple of
+the pattern length.
+
+ - If the tiny element shortens the previous skipped distance (or a
+final skip if it is the first element of the pattern), the entire pattern
+will be displaced slightly, leading to other problems at points that exactly
+line up with the boundaries of pattern elements.
+
+ - If the tiny element shortens both the previous and following
+distance, then if it falls exactly on a corner, the caps will not be at 180
+degrees to each other, leading to visual anomalies.
+
+We verified through experimentation that all these problems do occur with a
+client-side implementation, and that they do not occur in H-P's printers.
+We still suspect there is a way for clients to implement this behavior,
+especially if the library can deliver the dash expansion as noted for
+triangular caps above.
+
+Accurate curves
+---------------
+
+The curve flattening method implied by the PostScript Language Reference
+Manual always uses chords to approximate curves. As a result, the last
+chord is not quite a segment of the tangent, and so a butt or square cap is
+not quite perpendicular to the tangent. In PostScript, this is usually not
+a problem, because line joining takes care of filling the small wedge-shaped
+gap that would otherwise occur between the end of an arc and an adjacent
+line continuing in the same direction. However, with null joins, the wedge
+becomes visible. Also, with a wide brush, the discrepancy from horizontal
+or vertical at the ends of semicircles becomes visually noticeable. Our
+library provides a flag that creates a very short tangent segment as the
+first and last line of a flattened curve, thereby avoiding this problem.
+
+In principle, clients could create accurate curve ends by adding the short
+tangent lines explicitly (and adjusting the end parameters of the curve
+appropriately), but it would be quite a nuisance.
+
+Character bolding
+-----------------
+
+PCL supports pseudo-bolding for characters. This is defined as "smearing"
+the filled region to the right and upward before imaging it onto the page.
+In particular, smearing happens before RasterOp.
+
+We don't implement this directly in our graphics library; however, our PCL
+interpreters implement it in the callback for rendering characters that are
+not yet in the cache. A client could implement it by asking for the bitmap
+of the character and then doing the smearing, but this would prevent the
+smeared character from being cached. (This may not be important in
+practice.)
+
+PCL5 only
+=========
+
+Triangular joins
+----------------
+
+PCL5 supports triangular joins. The triangular "spike" is the reflection of
+filling the outside "notch" where the lines meet. The spike is not affected
+by the miter limit.
+
+As with triangular caps, clients could implement triangular joins (with a
+bit of trouble) if the library did the dash pattern expansion first.
+
+PCL XL only
+===========
+
+Negative dash pattern elements
+------------------------------
+
+PCL XL allows negative lengths in dash patterns. What this appears to mean
+is that a negative segment is drawn backwards from the current point at the
+time it is encountered, with no caps (or perhaps the caps are reflected 180
+degrees so they are actually inside the segment -- we didn't experiment with
+segments short enough to test this), and also skips over an equal amount of
+following positive pattern elements. What seems bizarre to us is that the
+negative segment is not drawn back along the line: it is drawn in a straight
+line in a direction 180 degrees from the current line segment. If this
+occurs just after a corner, for example, the drawn segment sticks out in a
+direction far off the actual path.
+
+Our PCL XL interpreter currently handles negative lengths in the client, by
+transforming the pattern, but the results are not correct if the line is
+shorter than one full pattern repetition or if the line has sharp corners.
+Clients could handle the former if dash patterns were generalized so that
+instead of an offset, they had a separate initial part, i.e., if they could
+be of the form A B C D E D E D E...; but we don't see how clients could
+handle the odd H-P interpretation of the latter. (We doubt that it needs to
+be handled in practice.)
+
+Dual halftones
+--------------
+
+By default, source and texture (i.e., colors/patterns and images) use
+slightly different halftones. We think they are the same halftone with
+slightly different phase, but we aren't sure of this yet. This is required
+in order to produce certain subtle (but highly visible) interaction effects
+when combining halftoned source and texture with RasterOp.
+
+We don't see any way for clients to implement dual halftones when RasterOp
+is involved, because not all 3-input Boolean functions can be handled by
+performing two 2-input operations with a change of halftone in between.
diff --git a/pxl/pxmain.c b/pxl/pxmain.c
new file mode 100644
index 000000000..33515708b
--- /dev/null
+++ b/pxl/pxmain.c
@@ -0,0 +1,306 @@
+/* Copyright (C) 1996, 1997 Aladdin Enterprises. All rights reserved.
+ Unauthorized use, copying, and/or distribution prohibited.
+ */
+
+/* pxmain.c */
+/* Main (test) program for PCL XL */
+#undef DEBUG
+#define DEBUG /* always enable debug output */
+#include "malloc_.h"
+#include "math_.h"
+#include "memory_.h"
+#include "stdio_.h"
+#include "string_.h"
+#include "gdebug.h"
+#include "gserrors.h"
+#include "gstypes.h"
+#include "gscdefs.h"
+#include "gsgc.h"
+#include "gsmemory.h"
+#include "gsmalloc.h"
+#include "gsargs.h"
+#include "gp.h"
+#include "gsmatrix.h"
+#include "gsstate.h" /* must precede gsdevice.h */
+#include "gscoord.h"
+#include "gsdevice.h"
+#include "gslib.h"
+#include "gspaint.h" /* for gs_erasepage */
+#include "gsparam.h"
+#include "gsstruct.h" /* for gxalloc.h */
+#include "gxalloc.h"
+#include "gxstate.h"
+#include "plparse.h"
+#include "pjparse.h"
+#include "plmain.h"
+#include "pxattr.h" /* for pxparse.h */
+#include "pxerrors.h"
+#include "pxvalue.h" /* for pxparse.h */
+#include "pxparse.h"
+#include "pxptable.h"
+#include "pxstate.h"
+
+/* Define the input buffer size. */
+#define buf_size max(px_parser_min_input_size, 1000) /* see pxparse.h */
+
+extern FILE *gs_debug_out;
+
+/* Imported operators */
+px_operator_proc(pxEndPage);
+px_operator_proc(pxBeginSession);
+
+/* Define the table of pointers to initialization data. */
+#define init_(init) int init(P1(px_state_t *));
+typedef init_((*px_init_proc));
+#include "pconfig.h"
+#undef init_
+const px_init_proc px_init_table[] = {
+#define init_(init) &init,
+#include "pconfig.h"
+#undef init_
+ 0
+};
+
+/* If inst.pause is set, pause at the end of each page. */
+private int
+pause_end_page(px_state_t *pxs, int num_copies, int flush)
+{ pl_main_instance_t *pmi = pxs->client_data;
+ gs_memory_t *mem = pxs->memory;
+
+ /*
+ * Check whether it's worth doing a garbage collection.
+ * Note that this only works if we don't relocate pointers,
+ * because pxs might get relocated.
+ */
+ { gs_memory_status_t status;
+ gs_memory_status(mem, &status);
+ if ( status.allocated > pmi->prev_allocated + 250000 ) {
+ if_debug2(':', "[:]%lu > %lu + 250K, garbage collecting\n",
+ (ulong)status.allocated, (ulong)pmi->prev_allocated);
+ gs_reclaim(&pmi->spaces, true);
+ gs_memory_status(mem, &status);
+ pmi->prev_allocated = status.allocated;
+ }
+ }
+ return pl_finish_page(pmi, pxs->pgs, num_copies, flush);
+}
+
+/* Initialize the parser state, and session parameters in case we get */
+/* an error before the session is established. */
+private int
+px_main_init(px_parser_state_t *st, px_state_t *pxs, bool big_endian)
+{ px_process_init(st, big_endian);
+ { px_args_t args;
+ px_value_t v[3];
+
+ /* Measure */
+ v[0].type = pxd_scalar | pxd_ubyte;
+ v[0].value.i = eInch;
+ args.pv[0] = &v[0];
+ /* UnitsPerMeasure */
+ v[1].type = pxd_xy | pxd_uint16;
+ v[1].value.ia[0] = v[1].value.ia[1] = 100; /* arbitrary */
+ args.pv[1] = &v[1];
+ /* ErrorReporting */
+ v[2].type = pxd_scalar | pxd_ubyte;
+ v[2].value.i = eErrorPage;
+ args.pv[2] = &v[2];
+ return pxBeginSession(&args, pxs);
+ }
+}
+
+/* Here is the main program. */
+int
+main(int argc, char *argv[])
+{ gs_ref_memory_t *imem;
+#define mem ((gs_memory_t *)imem)
+ pl_main_instance_t inst;
+ gs_state *pgs;
+ px_parser_state_t *st;
+ px_state_t *pxs;
+ const char *arg;
+ pjl_parser_state_t pjstate;
+ arg_list args;
+
+ /* Initialize the library. */
+ gp_init();
+ gs_lib_init(stderr);
+ gs_debug_out = stdout;
+ imem = ialloc_alloc_state((gs_raw_memory_t *)&gs_memory_default, 20000);
+ imem->space = 0; /****** WRONG ******/
+ pl_main_init(&inst, mem);
+ pl_main_process_options(&inst, &args, argv, argc);
+ pl_main_make_gstate(&inst, &pgs);
+ st = px_process_alloc(mem);
+ pxs = px_state_alloc(mem);
+ px_state_init(pxs, pgs);
+ pxs->client_data = &inst;
+ px_initgraphics(pxs);
+ /* gsave and grestore (among other places) assume that */
+ /* there are at least 2 gstates on the graphics stack. */
+ /* Ensure that now. */
+ gs_gsave(pgs);
+ pxs->end_page = pause_end_page;
+
+ /* gs_reclaim may change some procedures in the allocator; */
+ /* get them set up now. */
+ gs_reclaim(&inst.spaces, true);
+
+ while ( (arg = arg_next(&args)) != 0 )
+ { /* Process one input file. */
+ FILE *in = fopen(arg, "rb");
+ byte buf[buf_size];
+#define buf_last (buf + (buf_size - 1))
+ int len;
+ int code = 0;
+ stream_cursor_read r;
+ int in_pjl = 1; /* -1 = skipping after error, */
+ /* 0 = in PCL XL, 1 = in PJL */
+ int end_left = -1;
+
+ if ( gs_debug_c(':') )
+ { dprintf1("%% Reading %s:\n", arg);
+ pl_print_usage(mem, &inst, "Start");
+ }
+ if ( in == 0 )
+ { fprintf(stderr, "Unable to open %s for reading.\n", arg);
+ exit(1);
+ }
+
+ pjl_process_init(&pjstate);
+ r.limit = buf - 1;
+ for ( ; ; )
+ { if_debug1('i', "%% file pos=%ld\n", (long)ftell(in));
+ r.ptr = buf - 1;
+ len = fread(r.limit + 1, 1, buf_last - r.limit, in);
+ if ( len <= 0 )
+ { if ( r.limit - r.ptr == end_left )
+ break;
+ end_left = r.limit - r.ptr;
+ }
+ else
+ r.limit += len;
+process: switch ( in_pjl )
+ {
+ case -1: /* skipping after error */
+ if ( pjl_skip_to_uel(&r) )
+ in_pjl = 1;
+ goto move;
+ case 1: /* PJL */
+ code = pjl_process(&pjstate, NULL, &r);
+ if ( code > 0 )
+ { byte chr;
+ in_pjl = 0;
+ chr = (r.ptr == r.limit ? fgetc(in) : *++r.ptr);
+ switch ( chr )
+ {
+ case '(': px_main_init(st, pxs, true); break;
+ case ')': px_main_init(st, pxs, false); break;
+ default:
+ /* Initialize state to avoid confusion */
+ px_main_init(st, pxs, true);
+ code = gs_note_error(errorUnsupportedBinding);
+ goto err;
+ }
+ while ( (r.ptr == r.limit ? fgetc(in) : *++r.ptr) != '\n' )
+ ;
+ px_reset_errors(pxs);
+ goto process;
+ }
+ goto move;
+ case 0: /* PCL XL */
+ code = px_process(st, pxs, &r);
+ if ( code == e_ExitLanguage )
+ { in_pjl = 1;
+ px_state_cleanup(pxs);
+ code = 0;
+ }
+ }
+err: if ( code < 0 )
+ { /* Print an error report. */
+ int report =
+ (inst.error_report < 0 ? pxs->error_report :
+ inst.error_report);
+ const char *subsystem =
+ (code <= px_error_next ? "MAIN" : "GRAPHICS");
+ char message[px_max_error_line+1];
+ int N = 0;
+ int y;
+
+ /* Map library error codes to PCL XL codes when possible. */
+ switch ( code )
+ {
+#define subst(gs_error, px_error)\
+ case gs_error: code = px_error; break
+ subst(gs_error_invalidfont, errorIllegalFontData);
+ subst(gs_error_limitcheck, errorInternalOverflow);
+ subst(gs_error_nocurrentpoint, errorCurrentCursorUndefined);
+ subst(gs_error_rangecheck, errorIllegalAttributeValue);
+ subst(gs_error_VMerror, errorInsufficientMemory);
+#undef subst
+ }
+ if ( report & eErrorPage )
+ y = px_begin_error_page(pxs);
+ while ( (N = px_error_message_line(message, N, subsystem,
+ code, st, pxs)) >= 0
+ )
+ { if ( report & eBackChannel )
+ fputs(message, stderr);
+ if ( report & eErrorPage )
+ y = px_error_page_show(message, y, pxs);
+ }
+ if ( report & pxeErrorReport_next )
+ { long pos = ftell(in) - (r.limit - r.ptr);
+ fprintf(stderr, "file position of error = %ld\n", pos);
+ }
+ if ( report & eBackChannel )
+ fflush(stderr);
+ if ( report & eErrorPage )
+ { px_args_t args;
+ args.pv[0] = 0;
+ pxEndPage(&args, pxs);
+ }
+ px_reset_errors(pxs);
+ if ( code == errorWarningsReported )
+ { /* The parser doesn't skip over the EndSession */
+ /* operator, because an "error" occurred. */
+ r.ptr++;
+ }
+ else
+ { px_state_cleanup(pxs);
+ in_pjl = -1; /* skip to end of job */
+ }
+ }
+ if ( code != 0 )
+ { if_debug1('i', "%% Return code = %d\n", code);
+ }
+move: /* Move unread data to the start of the buffer. */
+ len = r.limit - r.ptr;
+ if ( len > 0 )
+ memmove(buf, r.ptr + 1, len);
+ r.limit = buf + (len - 1);
+ }
+ gs_reclaim(&inst.spaces, true);
+ /* Reset the GC trigger. */
+ { gs_memory_status_t status;
+ gs_memory_status(mem, &status);
+ inst.prev_allocated = status.allocated;
+ }
+ if ( gs_debug_c(':') )
+ dprintf3("%% Final file position = %ld, exit code = %d, mode = %s\n",
+ (long)ftell(in) - (r.limit - r.ptr), code,
+ (in_pjl > 0 ? "PJL" : in_pjl < 0 ? "skipping error" :
+ "PCL XL"));
+ fclose(in);
+ }
+ if ( gs_debug_c(':') )
+ { pl_print_usage(mem, &inst, "Final");
+ dprintf1("%% Max allocated = %ld\n", gs_malloc_max);
+ }
+ px_state_finit(pxs);
+ /* At the library level, we currently can't count on */
+ /* the memory manager finalizing (closing) devices. */
+ gs_closedevice(gs_currentdevice(pgs));
+ gs_lib_finit(0, 0);
+ return 0;
+}
diff --git a/pxl/pxoper.h b/pxl/pxoper.h
new file mode 100644
index 000000000..d411996c5
--- /dev/null
+++ b/pxl/pxoper.h
@@ -0,0 +1,97 @@
+/* Copyright (C) 1996 Aladdin Enterprises. All rights reserved.
+ Unauthorized use, copying, and/or distribution prohibited.
+ */
+
+/* pxoper.h */
+/* Definitions for PCL XL operators */
+
+#ifndef pxoper_INCLUDED
+# define pxoper_INCLUDED
+
+#include "gserror.h"
+#include "pxattr.h"
+#include "pxerrors.h"
+#include "pxvalue.h"
+
+#ifndef px_state_DEFINED
+# define px_state_DEFINED
+typedef struct px_state_s px_state_t;
+#endif
+
+#ifndef px_parser_state_t_DEFINED
+# define px_parser_state_t_DEFINED
+typedef struct px_parser_state_s px_parser_state_t;
+#endif
+
+/*
+ * The first argument of an operator procedure is a px_args_t. Two kinds
+ * of arguments require (potentially) special treatment: arrays, and data
+ * read from the data source.
+ *
+ * By default, the storage for an array argument is released after the
+ * operator is called; the operator does not know or care whether the
+ * storage was allocated on the heap or somewhere else. However, if the
+ * operator wants the storage to persist, it should examine the pxd_on_heap
+ * flag in the array value. If this flag is set, the storage is already
+ * heap-allocated; the operator should simply clear the flag to prevent the
+ * storage from being released. If pxd_on_heap is not set, the operator
+ * should allocate storage for the array on the heap and then copy the
+ * contents to the heap storage.
+ *
+ * If an operator needs data from the data source, it should check the
+ * source.available member of the argument record. If at least as much data
+ * is available as the operator needs, the operator should read the data it
+ * needs, update source.data and source.available (and, if it wishes,
+ * source.position) accordingly, and return 0 as usual. If not enough data
+ * is available, the operator should read as much as it wants (which may not
+ * be all of what is available) and return the special value pxNeedData.
+ *
+ * The variables source.position and source.count are provided solely so
+ * that simple data-reading operators don't need to allocate a separate
+ * state record. The scanner itself doesn't touch them, except for
+ * initializing source.position to 0 before invoking the operator the first
+ * time.
+ *
+ * We provide parser_macro_state and parser_operator_count so that we can
+ * implement ExecStream by simple recursion.
+ */
+
+/* ---------------- Definitions ---------------- */
+
+/*
+ * Define the structure for operator arguments. This structure is never
+ * allocated separately (only embedded in a px_parser_state_t) or referenced
+ * persistently, and all its non-transient pointers point into the parser
+ * state. Consequently, we don't need a marking procedure for it, but we do
+ * need to relocate those pointers. We handle this within
+ * px_parser_state_reloc_ptrs.
+ */
+#define max_px_args 20
+typedef struct px_args_s {
+ struct ds_ {
+ ulong position; /* position in data block, initially 0, */
+ /* provided for the operator's convenience */
+ uint count; /* another variable for the operators */
+ uint available; /* amount of data available in block */
+ const byte *data;
+ } source;
+ /*
+ * ExecStream needs a pointer to the parser state so it can set the
+ * parser's macro-state after returning, and to report the stream's
+ * operator count in case of an error.
+ */
+ px_parser_state_t *parser;
+ px_value_t *pv[max_px_args];
+} px_args_t;
+
+/*
+ * Define the value that an operator returns if it needs more data from the
+ * data source.
+ */
+#define pxNeedData 42 /* not 0 or 1, and >0 */
+
+/* Define the argument list for operators. */
+#define px_operator_proc(proc)\
+ int proc(P2(px_args_t *, px_state_t *))
+
+#endif /* pxoper_INCLUDED */
diff --git a/pxl/pxpaint.c b/pxl/pxpaint.c
new file mode 100644
index 000000000..9b7f2ff51
--- /dev/null
+++ b/pxl/pxpaint.c
@@ -0,0 +1,799 @@
+/* Copyright (C) 1996, 1997 Aladdin Enterprises. All rights reserved.
+ Unauthorized use, copying, and/or distribution prohibited.
+ */
+
+/* pxpaint.c */
+/* PCL XL painting operators */
+
+#include "math_.h"
+#include "stdio_.h" /* std.h + NULL */
+#include "pxoper.h"
+#include "pxstate.h"
+#include "pxfont.h" /* for px_text */
+#include "gsstate.h"
+#include "gscoord.h"
+#include "gspaint.h"
+#include "gspath.h"
+#include "gspath2.h"
+#include "gsrop.h"
+#include "gxfarith.h"
+#include "gxfixed.h"
+#include "gxistate.h"
+#include "gxmatrix.h"
+#include "gxpath.h"
+
+/*
+ * The published H-P specification says that the single-object painting
+ * operators (Chord, Ellipse, Pie, Rectangle, RoundRectangle) leave the
+ * path set to the object; however, H-P printers apparently reset the
+ * path (but leave the current cursor defined). To obtain the latter
+ * behavior, uncomment the following #define.
+ */
+#define NEWPATH_AFTER_PAINT_SHAPE
+/*
+ * The published specification says that dash patterns do not scale when
+ * the CTM changes; however, H-P printers do scale the dash pattern.
+ * To make dash patterns not scale, uncomment the following #define.
+ */
+/*#define NO_SCALE_DASH_PATTERN*/
+/*
+ * The H-P documentation says we are supposed to draw rectangles
+ * counter-clockwise on the page, which is clockwise in user space.
+ * However, the LaserJet 6 (and probably the LJ 5 as well) draw rectangles
+ * clockwise! To draw rectangles clockwise, uncomment the following
+ * #define.
+ */
+#define DRAW_RECTANGLES_CLOCKWISE
+/*
+ * The H-P printers do really weird things for arcs, chords, or pies where
+ * the width and/or height of the bounding box is negative. To emulate
+ * their behavior, uncomment the following #define.
+ */
+#define REFLECT_NEGATIVE_ARCS
+
+/* Forward references */
+px_operator_proc(pxNewPath);
+
+/* ---------------- Utilities ---------------- */
+
+/* Add lines to the path. line_proc is gs_lineto or gs_rlineto. */
+/* Attributes: pxaEndPoint, pxaNumberOfPoints, pxaPointType. */
+private int
+add_lines(px_args_t *par, px_state_t *pxs,
+ int (*line_proc)(P3(gs_state *, floatp, floatp)))
+{ int code = 0;
+
+ if ( par->pv[0] )
+ { /* Single segment, specified as argument. */
+ if ( par->pv[1] || par->pv[2] )
+ return_error(errorIllegalAttributeCombination);
+ return (*line_proc)(pxs->pgs, real_value(par->pv[0], 0),
+ real_value(par->pv[0], 1));
+ }
+ /* Multiple segments, specified in source data. */
+ if ( !(par->pv[1] && par->pv[2]) )
+ return_error(errorMissingAttribute);
+ { integer num_points = par->pv[1]->value.i;
+ pxeDataType_t type = (pxeDataType_t)par->pv[2]->value.i;
+ int point_size = (type == eUByte || type == eSByte ? 2 : 4);
+
+ while ( par->source.position < num_points * point_size )
+ { const byte *dp = par->source.data;
+ int px, py;
+
+ if ( par->source.available < point_size )
+ { /* We don't even have one point's worth of source data. */
+ return pxNeedData;
+ }
+ switch ( type )
+ {
+ case eUByte:
+ px = dp[0];
+ py = dp[1];
+ break;
+ case eSByte:
+ px = (int)(dp[0] ^ 0x80) - 0x80;
+ py = (int)(dp[1] ^ 0x80) - 0x80;
+ break;
+ case eUInt16:
+ px = uint16at(dp, pxs->data_source_big_endian);
+ py = uint16at(dp + 2, pxs->data_source_big_endian);
+ break;
+ case eSInt16:
+ px = sint16at(dp, pxs->data_source_big_endian);
+ py = sint16at(dp + 2, pxs->data_source_big_endian);
+ break;
+ default: /* can't happen, pacify compiler */
+ return_error(errorIllegalAttributeValue);
+ }
+ code = (*line_proc)(pxs->pgs, (floatp)px, (floatp)py);
+ if ( code < 0 )
+ break;
+ par->source.position += point_size;
+ par->source.available -= point_size;
+ par->source.data += point_size;
+ }
+ }
+ return code;
+}
+
+/* Add Bezier curves to the path. curve_proc is gs_curveto or gs_rcurveto. */
+/* Attributes: pxaNumberOfPoints, pxaPointType, pxaControlPoint1, */
+/* pxaControlPoint2, pxaEndPoint. */
+private int
+add_curves(px_args_t *par, px_state_t *pxs,
+ int (*curve_proc)(P7(gs_state *, floatp, floatp, floatp, floatp, floatp, floatp)))
+{ int code = 0;
+
+ if ( par->pv[2] && par->pv[3] && par->pv[4] )
+ { /* Single curve, specified as argument. */
+ if ( par->pv[0] || par->pv[1] )
+ return_error(errorIllegalAttributeCombination);
+ return (*curve_proc)(pxs->pgs, real_value(par->pv[2], 0),
+ real_value(par->pv[2], 1),
+ real_value(par->pv[3], 0),
+ real_value(par->pv[3], 1),
+ real_value(par->pv[4], 0),
+ real_value(par->pv[4], 1));
+ }
+ /* Multiple segments, specified in source data. */
+ else if ( par->pv[0] && par->pv[1] )
+ { if ( par->pv[2] || par->pv[3] || par->pv[4] )
+ return_error(errorIllegalAttributeCombination);
+ }
+ else
+ return_error(errorMissingAttribute);
+ { integer num_points = par->pv[0]->value.i;
+ pxeDataType_t type = (pxeDataType_t)par->pv[1]->value.i;
+ int point_size = (type == eUByte || type == eSByte ? 2 : 4);
+ int segment_size = point_size * 3;
+
+ if ( num_points % 3 )
+ return_error(errorIllegalDataLength);
+ while ( par->source.position < num_points * point_size )
+ { const byte *dp = par->source.data;
+ int points[6];
+ int i;
+
+ if ( par->source.available < point_size * 3 )
+ { /* We don't even have one point's worth of source data. */
+ return pxNeedData;
+ }
+ switch ( type )
+ {
+ case eUByte:
+ for ( i = 0; i < 6; ++i )
+ points[i] = dp[i];
+ break;
+ case eSByte:
+ for ( i = 0; i < 6; ++i )
+ points[i] = (int)(dp[i] ^ 0x80) - 0x80;
+ break;
+ case eUInt16:
+ for ( i = 0; i < 12; i += 2 )
+ points[i >> 1] =
+ uint16at(dp + i, pxs->data_source_big_endian);
+ break;
+ case eSInt16:
+ for ( i = 0; i < 12; i += 2 )
+ points[i >> 1]
+ = sint16at(dp + i, pxs->data_source_big_endian);
+ break;
+ default: /* can't happen, pacify compiler */
+ return_error(errorIllegalAttributeValue);
+ }
+ code = (*curve_proc)(pxs->pgs,
+ (floatp)points[0], (floatp)points[1],
+ (floatp)points[2], (floatp)points[3],
+ (floatp)points[4], (floatp)points[5]);
+ if ( code < 0 )
+ break;
+ par->source.position += segment_size;
+ par->source.available -= segment_size;
+ par->source.data += segment_size;
+ }
+ }
+ return code;
+}
+
+/*
+ * Set up all the parameters for an arc, chord, ellipse, or pie. If pp3 and
+ * pp4 are NULL, we're filling the entire box. Store the upper left box
+ * corner (for repositioning the cursor), the center, the radius, and the
+ * two angles in *params, and return one of the following (or a negative
+ * error code):
+ */
+typedef enum {
+ /*
+ * Arc box is degenerate (zero width and/or height).
+ * Only origin and center have been set.
+ */
+ arc_degenerate = 0,
+ /*
+ * Arc box is square. No CTM adjustment was required; save_ctm is
+ * not set.
+ */
+ arc_square,
+ /*
+ * Arc box is rectangular, CTM has been saved and adjusted.
+ */
+ arc_rectangular
+} px_arc_type_t;
+typedef struct px_arc_params_s {
+ gs_point origin;
+ gs_point center;
+ double radius;
+ double ang3, ang4;
+ gs_matrix save_ctm;
+ bool reversed;
+} px_arc_params_t;
+private int /* px_arc_type_t or error code */
+setup_arc(px_arc_params_t *params, const px_value_t *pbox,
+ const px_value_t *pp3, const px_value_t *pp4, const px_state_t *pxs)
+{ real x1 = real_value(pbox, 0);
+ real y1 = real_value(pbox, 1);
+ real x2 = real_value(pbox, 2);
+ real y2 = real_value(pbox, 3);
+ real xc = (x1 + x2) * 0.5;
+ real yc = (y1 + y2) * 0.5;
+ real xr, yr;
+ bool rotated;
+ int code;
+
+#ifdef REFLECT_NEGATIVE_ARCS
+ rotated = x1 > x2;
+ params->reversed = rotated ^ (y1 > y2);
+#else
+ rotated = false;
+ params->reversed = false;
+#endif
+ if ( x1 > x2 )
+ { real temp = x1; x1 = x2; x2 = temp;
+ }
+ if ( y1 > y2 )
+ { real temp = y1; y1 = y2; y2 = temp;
+ }
+ params->origin.x = x1;
+ params->origin.y = y1;
+ xr = (x2 - x1) * 0.5;
+ yr = (y2 - y1) * 0.5;
+ if ( xr == 0 || yr == 0 )
+ { /* The bounding box is degenerate, set what we can and exit. */
+ params->center.x = xc;
+ params->center.y = yc;
+ return arc_degenerate;
+ }
+ if ( pp3 && pp4 )
+ { real dx3 = real_value(pp3, 0) - xc;
+ real dy3 = real_value(pp3, 1) - yc;
+ real dx4 = real_value(pp4, 0) - xc;
+ real dy4 = real_value(pp4, 1) - yc;
+
+ if ( (dx3 == 0 && dy3 == 0) || (dx4 == 0 && dy4 == 0) )
+ return_error(errorIllegalAttributeValue);
+ { double ang3 = atan2(dy3 * xr, dx3 * yr) * radians_to_degrees;
+ double ang4 = atan2(dy4 * xr, dx4 * yr) * radians_to_degrees;
+
+ if ( rotated )
+ ang3 += 180, ang4 += 180;
+ params->ang3 = ang3;
+ params->ang4 = ang4;
+ }
+ }
+ params->radius = yr;
+ if ( xr == yr )
+ { params->center.x = xc;
+ params->center.y = yc;
+ return arc_square;
+ }
+ else
+ { /* Must adjust the CTM. Move the origin to (xc,yc) */
+ /* for simplicity. */
+ if ( (code = gs_currentmatrix(pxs->pgs, &params->save_ctm)) < 0 ||
+ (code = gs_translate(pxs->pgs, xc, yc)) < 0 ||
+ (code = gs_scale(pxs->pgs, xr, yr)) < 0
+ )
+ return code;
+ params->center.x = 0;
+ params->center.y = 0;
+ params->radius = 1.0;
+ return arc_rectangular;
+ }
+}
+
+/* Paint the current path, optionally resetting it afterwards. */
+private int
+paint_path(px_state_t *pxs, bool reset)
+{ gs_state *pgs = pxs->pgs;
+ gx_path *ppath = gx_current_path(pgs);
+ px_gstate_t *pxgs = pxs->pxgs;
+ bool will_stroke = pxgs->pen.type != pxpNull;
+ gs_point cursor;
+ int code = 0;
+ gx_path *save_path = 0;
+
+ if ( gx_path_is_void(ppath) )
+ return 0; /* nothing to draw */
+#ifdef NO_SCALE_DASH_PATTERN
+# define save_for_stroke (!reset || pxgs->dashed)
+#else
+# define save_for_stroke (!reset)
+#endif
+ if ( pxgs->brush.type != pxpNull )
+ { int (*fill_proc)(P1(gs_state *)) =
+ (pxgs->fill_mode == eEvenOdd ? gs_eofill : gs_fill);
+
+ if ( (code = px_set_paint(&pxgs->brush, pxs)) < 0 )
+ return code;
+ pxs->have_page = true;
+ if ( !will_stroke && reset )
+ { gs_currentpoint(pgs, &cursor);
+ code = (*fill_proc)(pgs);
+ gs_moveto(pgs, cursor.x, cursor.y);
+ return code;
+ }
+ save_path = gx_path_alloc(pxs->memory, "paint_path(save_path)");
+ if ( save_path == 0 )
+ return_error(errorInsufficientMemory);
+ gx_path_assign_preserve(save_path, ppath);
+ gx_path_alloc_shared(ppath, pxs->memory, "paint_path(ppath)");
+ code = (*fill_proc)(pgs);
+ if ( code < 0 )
+ goto rx;
+ if ( !will_stroke )
+ goto rx;
+ gx_path_assign_preserve(ppath, save_path);
+ if ( save_for_stroke )
+ gx_path_alloc_shared(ppath, pxs->memory, "paint_path(ppath)");
+ else
+ { gs_currentpoint(pgs, &cursor);
+ gs_free_object(pxs->memory, save_path,
+ "paint_path(save_path)");
+ save_path = 0;
+ }
+ }
+ else if ( !will_stroke )
+ return 0;
+ else if ( save_for_stroke )
+ { save_path =
+ gx_path_alloc(pxs->memory, "paint_path(save_path)");
+ if ( save_path == 0 )
+ return_error(errorInsufficientMemory);
+ gx_path_assign_preserve(save_path, ppath);
+ gx_path_alloc_shared(ppath, pxs->memory, "paint_path(ppath)");
+ }
+ else
+ gs_currentpoint(pgs, &cursor);
+ /*
+ * The PCL XL documentation from H-P says that dash lengths do not
+ * scale according to the CTM, but according to H-P developer
+ * support, this isn't true. We went to the trouble of implementing
+ * the published specification, so if it turns out to be right after
+ * all, we execute the following block of code.
+ */
+#ifdef NO_SCALE_DASH_PATTERN
+ /*
+ * If we have a dash pattern that was defined with a different
+ * CTM than the current one, we must pre-expand the dashes.
+ * (Eventually we should expand the library API to handle this.)
+ */
+ if ( pxgs->dashed )
+ { gs_matrix mat;
+ gs_currentmatrix(pgs, &mat);
+ if ( mat.xx != pxgs->dash_matrix.xx ||
+ mat.xy != pxgs->dash_matrix.xy ||
+ mat.yx != pxgs->dash_matrix.yx ||
+ mat.yy != pxgs->dash_matrix.yy
+ )
+ { code = gs_flattenpath(pgs);
+ if ( code < 0 )
+ goto rx;
+ gs_setmatrix(pgs, &pxgs->dash_matrix);
+ code = gs_dashpath(pgs);
+ gs_setmatrix(pgs, &mat);
+ if ( code < 0 )
+ goto rx;
+ }
+ }
+#endif
+ /*
+ * Per the description in the PCL XL reference documentation,
+ * set a standard logical operation and transparency for stroking.
+ */
+ { gs_rop3_t save_rop = gs_currentrasterop(pgs);
+ bool save_transparent = gs_currenttexturetransparent(pgs);
+
+ gs_setrasterop(pgs, rop3_T);
+ gs_settexturetransparent(pgs, false);
+ pxs->have_page = true;
+ /*
+ * The H-P printers thicken very thin strokes slightly.
+ * We do the same here.
+ */
+ { float width = gs_currentlinewidth(pgs);
+ gs_matrix mat;
+ float sx, sy;
+
+ gs_currentmatrix(pgs, &mat);
+ sx = fabs(mat.xx) + fabs(mat.xy);
+ sy = fabs(mat.yx) + fabs(mat.yy);
+ width *= min(sx, sy);
+ if ( width < 5 )
+ gs_setfilladjust(pgs, 0.5, 0.5);
+ }
+ if ( (code = px_set_paint(&pxgs->pen, pxs)) < 0 ||
+ (code = gs_stroke(pgs)) < 0
+ )
+ DO_NOTHING;
+ gs_setrasterop(pgs, save_rop);
+ gs_settexturetransparent(pgs, save_transparent);
+ gs_setfilladjust(pgs, 0.0, 0.0);
+ }
+rx: if ( save_path )
+ { gx_path_assign_preserve(ppath, save_path);
+ gs_free_object(pxs->memory, save_path, "paint_path(save_path)");
+ }
+ else
+ gs_moveto(pgs, cursor.x, cursor.y);
+ return code;
+}
+
+/* Paint a shape defined by a one-operator path. */
+private int
+paint_shape(px_args_t *par, px_state_t *pxs, px_operator_proc((*path_op)))
+{ int code;
+ if ( (code = pxNewPath(par, pxs)) < 0 ||
+ (code = (*path_op)(par, pxs)) < 0
+ )
+ return code;
+ /* See the beginning of the file regarding the following. */
+#ifdef NEWPATH_AFTER_PAINT_SHAPE
+ return paint_path(pxs, true);
+#else
+ return paint_path(pxs, false);
+#endif
+}
+
+/* ---------------- Operators ---------------- */
+
+const byte apxCloseSubPath[] = {0, 0};
+int
+pxCloseSubPath(px_args_t *par, px_state_t *pxs)
+{ return gs_closepath(pxs->pgs);
+}
+
+const byte apxNewPath[] = {0, 0};
+int
+pxNewPath(px_args_t *par, px_state_t *pxs)
+{ return gs_newpath(pxs->pgs);
+}
+
+const byte apxPaintPath[] = {0, 0};
+int
+pxPaintPath(px_args_t *par, px_state_t *pxs)
+{ return paint_path(pxs, false);
+}
+
+const byte apxArcPath[] = {
+ pxaBoundingBox, pxaStartPoint, pxaEndPoint, 0,
+ pxaArcDirection, 0
+};
+int
+pxArcPath(px_args_t *par, px_state_t *pxs)
+{ /*
+ * Note that "clockwise" in user space is counter-clockwise on
+ * the page, because the Y coordinate is inverted.
+ */
+ bool clockwise =
+ (par->pv[3] != 0 && par->pv[3]->value.i == eClockWise);
+ px_arc_params_t params;
+ int code = setup_arc(&params, par->pv[0], par->pv[1], par->pv[2], pxs);
+ int rcode = code;
+
+ if ( code >= 0 && code != arc_degenerate )
+ { bool closed = params.ang3 == params.ang4;
+ clockwise ^= params.reversed;
+ if ( closed )
+ { if ( clockwise ) params.ang4 += 360;
+ else params.ang3 += 360;
+ }
+ code = gs_arc_add(pxs->pgs, !clockwise, params.center.x,
+ params.center.y, params.radius, params.ang3,
+ params.ang4, false);
+ if ( code >= 0 && closed )
+ { /* We have to close the path explicitly. */
+ code = gs_closepath(pxs->pgs);
+ }
+ }
+ if ( rcode == arc_rectangular )
+ gs_setmatrix(pxs->pgs, &params.save_ctm);
+ return code;
+}
+
+const byte apxBezierPath[] = {
+ 0, pxaNumberOfPoints, pxaPointType, pxaControlPoint1, pxaControlPoint2,
+ pxaEndPoint, 0
+};
+int
+pxBezierPath(px_args_t *par, px_state_t *pxs)
+{ return add_curves(par, pxs, gs_curveto);
+}
+
+const byte apxBezierRelPath[] = {
+ 0, pxaNumberOfPoints, pxaPointType, pxaControlPoint1, pxaControlPoint2,
+ pxaEndPoint, 0
+};
+int
+pxBezierRelPath(px_args_t *par, px_state_t *pxs)
+{ return add_curves(par, pxs, gs_rcurveto);
+}
+
+const byte apxChord[] = {
+ pxaBoundingBox, pxaStartPoint, pxaEndPoint, 0, 0
+};
+px_operator_proc(pxChordPath);
+int
+pxChord(px_args_t *par, px_state_t *pxs)
+{ return paint_shape(par, pxs, pxChordPath);
+}
+
+const byte apxChordPath[] = {
+ pxaBoundingBox, pxaStartPoint, pxaEndPoint, 0, 0
+};
+int
+pxChordPath(px_args_t *par, px_state_t *pxs)
+{ px_arc_params_t params;
+ int code = setup_arc(&params, par->pv[0], par->pv[1], par->pv[2], pxs);
+ int rcode = code;
+
+ /* See ArcPath above for the meaning of "clockwise". */
+ if ( code >= 0 && code != arc_degenerate )
+ { if ( params.ang3 == params.ang4 )
+ params.ang3 += 360;
+ code = gs_arc_add(pxs->pgs, !params.reversed,
+ params.center.x, params.center.y,
+ params.radius, params.ang3, params.ang4,
+ false);
+ if ( code >= 0 )
+ code = gs_closepath(pxs->pgs);
+ }
+ if ( rcode == arc_rectangular )
+ gs_setmatrix(pxs->pgs, &params.save_ctm);
+ if ( code >= 0 )
+ code = gs_moveto(pxs->pgs, params.origin.x, params.origin.y);
+ return code;
+
+}
+
+const byte apxEllipse[] = {
+ pxaBoundingBox, 0, 0
+};
+px_operator_proc(pxEllipsePath);
+int
+pxEllipse(px_args_t *par, px_state_t *pxs)
+{ return paint_shape(par, pxs, pxEllipsePath);
+}
+
+const byte apxEllipsePath[] = {
+ pxaBoundingBox, 0, 0
+};
+int
+pxEllipsePath(px_args_t *par, px_state_t *pxs)
+{ px_arc_params_t params;
+ int code = setup_arc(&params, par->pv[0], NULL, NULL, pxs);
+ int rcode = code;
+
+ /* See ArcPath above for the meaning of "clockwise". */
+ if ( code < 0 || code == arc_degenerate ||
+ (code = gs_arc_add(pxs->pgs, !params.reversed,
+ params.center.x, params.center.y,
+ params.radius, 180.0, -180.0, false)) < 0 ||
+ /* We have to close the path explicitly. */
+ (code = gs_closepath(pxs->pgs)) < 0
+ )
+ DO_NOTHING;
+ if ( rcode == arc_rectangular )
+ gs_setmatrix(pxs->pgs, &params.save_ctm);
+ if ( code >= 0 )
+ code = gs_moveto(pxs->pgs, params.origin.x, params.origin.y);
+ return code;
+}
+
+const byte apxLinePath[] = {
+ 0, pxaEndPoint, pxaNumberOfPoints, pxaPointType, 0
+};
+int
+pxLinePath(px_args_t *par, px_state_t *pxs)
+{ return add_lines(par, pxs, gs_lineto);
+}
+
+const byte apxLineRelPath[] = {
+ 0, pxaEndPoint, pxaNumberOfPoints, pxaPointType, 0
+};
+int
+pxLineRelPath(px_args_t *par, px_state_t *pxs)
+{ return add_lines(par, pxs, gs_rlineto);
+}
+
+const byte apxPie[] = {
+ pxaBoundingBox, pxaStartPoint, pxaEndPoint, 0, 0
+};
+px_operator_proc(pxPiePath);
+int
+pxPie(px_args_t *par, px_state_t *pxs)
+{ return paint_shape(par, pxs, pxPiePath);
+}
+
+const byte apxPiePath[] = {
+ pxaBoundingBox, pxaStartPoint, pxaEndPoint, 0, 0
+};
+int
+pxPiePath(px_args_t *par, px_state_t *pxs)
+{ px_arc_params_t params;
+ int code = setup_arc(&params, par->pv[0], par->pv[1], par->pv[2], pxs);
+ int rcode = code;
+
+ /* See ArcPath above for the meaning of "clockwise". */
+ if ( code >= 0 && code != arc_degenerate )
+ { if ( params.ang3 == params.ang4 )
+ params.ang3 += 360;
+ code = gs_moveto(pxs->pgs, params.center.x, params.center.y);
+ if ( code >= 0 )
+ { code = gs_arc_add(pxs->pgs, !params.reversed,
+ params.center.x, params.center.y,
+ params.radius, params.ang3, params.ang4,
+ true);
+ }
+ }
+ if ( rcode == arc_rectangular )
+ gs_setmatrix(pxs->pgs, &params.save_ctm);
+ if ( code < 0 || rcode == arc_degenerate ||
+ (code = gs_closepath(pxs->pgs)) < 0 ||
+ (code = gs_moveto(pxs->pgs, params.origin.x, params.origin.y)) < 0
+ )
+ DO_NOTHING;
+ return code;
+}
+
+const byte apxRectangle[] = {
+ pxaBoundingBox, 0, 0
+};
+px_operator_proc(pxRectanglePath);
+int
+pxRectangle(px_args_t *par, px_state_t *pxs)
+{ return paint_shape(par, pxs, pxRectanglePath);
+}
+
+const byte apxRectanglePath[] = {
+ pxaBoundingBox, 0, 0
+};
+int
+pxRectanglePath(px_args_t *par, px_state_t *pxs)
+{ floatp x1, y1, x2, y2;
+ gs_fixed_point p1;
+ gs_state *pgs = pxs->pgs;
+ gx_path *ppath = gx_current_path(pgs);
+ gs_fixed_point lines[3];
+#define p2 lines[1]
+#define pctm (&((const gs_imager_state *)pgs)->ctm)
+ int code;
+
+ set_box_value(x1, y1, x2, y2, par->pv[0]);
+ /*
+ * Rectangles are always drawn in a canonical order.
+ */
+ if ( x1 > x2 )
+ { floatp t = x1; x1 = x2; x2 = t; }
+ if ( y1 > y2 )
+ { floatp t = y1; y1 = y2; y2 = t; }
+ if ( (code = gs_point_transform2fixed(pctm, x1, y1, &p1)) < 0 ||
+ (code = gs_point_transform2fixed(pctm, x2, y2, &p2)) < 0 ||
+ (code = gx_path_add_point(ppath, p1.x, p1.y)) < 0
+ )
+ return code;
+#ifdef DRAW_RECTANGLES_CLOCKWISE
+ /*
+ * DRAW_RECTANGLES_CLOCKWISE means clockwise on the page, which is
+ * counter-clockwise in user space.
+ */
+ if ( (code = gs_point_transform2fixed(pctm, x2, y1, &lines[0])) < 0 ||
+ (code = gs_point_transform2fixed(pctm, x1, y2, &lines[2])) < 0
+ )
+ return code;
+#else
+ if ( (code = gs_point_transform2fixed(pctm, x1, y2, &lines[0])) < 0 ||
+ (code = gs_point_transform2fixed(pctm, x2, y1, &lines[2])) < 0
+ )
+ return code;
+#endif
+ if ( (code = gx_path_add_lines(ppath, lines, 3)) < 0 )
+ return code;
+ return gs_closepath(pgs);
+#undef pctm
+#undef p2
+}
+
+const byte apxRoundRectangle[] = {
+ pxaBoundingBox, pxaEllipseDimension, 0, 0
+};
+px_operator_proc(pxRoundRectanglePath);
+int
+pxRoundRectangle(px_args_t *par, px_state_t *pxs)
+{ return paint_shape(par, pxs, pxRoundRectanglePath);
+}
+
+const byte apxRoundRectanglePath[] = {
+ pxaBoundingBox, pxaEllipseDimension, 0, 0
+};
+int
+pxRoundRectanglePath(px_args_t *par, px_state_t *pxs)
+{ floatp x1, y1, x2, y2;
+ real xr = real_value(par->pv[1], 0) * 0.5;
+ real yr = real_value(par->pv[1], 1) * 0.5;
+ real xd, yd;
+ gs_matrix save_ctm;
+ gs_state *pgs = pxs->pgs;
+ int code;
+
+ set_box_value(x1, y1, x2, y2, par->pv[0]);
+ xd = x2 - x1;
+ yd = y2 - y1;
+ /*
+ * H-P printers give an error if the points are specified out
+ * of order.
+ */
+ if ( xd < 0 || yd < 0 )
+ return_error(errorIllegalAttributeValue);
+ if ( xr == 0 || yr == 0 )
+ return pxRectanglePath(par, pxs);
+ gs_currentmatrix(pgs, &save_ctm);
+ gs_translate(pgs, x1, y1);
+ if ( xr != yr )
+ { /* Change coordinates so the arcs are circular. */
+ double scale = xr / yr;
+ if ( (code = gs_scale(pgs, scale, 1.0)) < 0 )
+ return code;
+ xd *= yr / xr;
+ }
+#define r yr
+ /*
+ * Draw the rectangle counter-clockwise on the page, which is
+ * clockwise in user space. (This may be reversed if the
+ * coordinates are specified in a non-standard order.)
+ */
+ if ( (code = gs_moveto(pgs, 0.0, r)) < 0 ||
+ (code = gs_arcn(pgs, r, yd - r, r, 180.0, 90.0)) < 0 ||
+ (code = gs_arcn(pgs, xd - r, yd - r, r, 90.0, 0.0)) < 0 ||
+ (code = gs_arcn(pgs, xd - r, r, r, 0.0, 270.0)) < 0 ||
+ (code = gs_arcn(pgs, r, r, r, 270.0, 180.0)) < 0 ||
+ (code = gs_closepath(pgs)) < 0 ||
+ (code = gs_moveto(pgs, 0.0, 0.0)) < 0
+ )
+ return code;
+#undef r
+ return gs_setmatrix(pgs, &save_ctm);
+}
+
+const byte apxText[] = {
+ pxaTextData, 0, pxaXSpacingData, pxaYSpacingData, 0
+};
+int
+pxText(px_args_t *par, px_state_t *pxs)
+{ { int code = px_set_paint(&pxs->pxgs->brush, pxs);
+ if ( code < 0 )
+ return code;
+ }
+ if ( par->pv[0]->value.array.size != 0 &&
+ pxs->pxgs->brush.type != pxpNull
+ )
+ pxs->have_page = true;
+ return px_text(par, pxs, false);
+}
+
+const byte apxTextPath[] = {
+ pxaTextData, 0, pxaXSpacingData, pxaYSpacingData, 0
+};
+int
+pxTextPath(px_args_t *par, px_state_t *pxs)
+{ return px_text(par, pxs, true);
+}
diff --git a/pxl/pxparse.c b/pxl/pxparse.c
new file mode 100644
index 000000000..f4bc01333
--- /dev/null
+++ b/pxl/pxparse.c
@@ -0,0 +1,873 @@
+/* Copyright (C) 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+ Unauthorized use, copying, and/or distribution prohibited.
+ */
+
+/* pxparse.c */
+/* PCL XL parser */
+
+#include "memory_.h"
+#include "stdio_.h" /* for stderr for DEBUG */
+#include "gdebug.h"
+#include "gserror.h"
+#include "gstypes.h"
+#include "plparse.h"
+#include "pxattr.h"
+#include "pxenum.h"
+#include "pxerrors.h"
+#include "pxoper.h"
+#include "pxtag.h"
+#include "pxvalue.h"
+#include "pxparse.h" /* requires pxattr.h, pxvalue.h */
+#include "pxptable.h" /* requires pxenum.h, pxoper.h, pxvalue.h */
+#include "pxstate.h"
+
+/* GC structures and procedures */
+gs_private_st_composite(st_px_parser_state, px_parser_state_t,
+ "px_parser_state_t", parser_state_enum_ptrs, parser_state_reloc_ptrs);
+#define st ((px_parser_state_t *)vptr)
+private ENUM_PTRS_BEGIN(parser_state_enum_ptrs)
+ /* Mark from array pointers on the stack. */
+ if ( index < st->stack_count )
+ { px_value_t *pv = &st->stack[index];
+ ENUM_RETURN((!(~pv->type & (pxd_array | pxd_on_heap)) ?
+ pv->value.array.data : 0));
+ }
+ return 0;
+ENUM_PTRS_END
+private RELOC_PTRS_BEGIN(parser_state_reloc_ptrs) {
+ px_parser_state_t *st_new =
+ (*gc_proc(gcst, reloc_struct_ptr))((const void *)st, gcst);
+ long diff = (byte *)st_new - (byte *)st;
+
+ /* Relocate array pointers on the stack. */
+ { px_value_t *pv = &st->stack[0];
+ int i;
+
+ for ( i = 0; i < st->stack_count; ++pv, ++i )
+ if ( !(~pv->type & (pxd_array | pxd_on_heap)) )
+ RELOC_OBJ_VAR(pv->value.array.data);
+ }
+ /* Relocate the pointers from the args. */
+ st->args.parser = st_new;
+ { px_value_t **ppv = &st->args.pv[0];
+ int i;
+
+ for ( i = 0; i < max_px_args; ++ppv, ++i )
+ if ( *ppv )
+ *ppv = (px_value_t *)((byte *)*ppv + diff);
+ }
+} RELOC_PTRS_END
+#undef st
+
+/*
+ * We define the syntax of each possible tag by 4 parameters:
+ * - The minimum number of input bytes required to parse it;
+ * - A mask and match value for the state(s) in which it is legal;
+ * - A transition mask to be xor'ed with the state.
+ * See pxparse.h for the state mask values.
+ */
+typedef struct px_tag_syntax_s {
+ byte min_input;
+ byte state_value;
+ byte state_mask;
+ byte state_transition;
+} px_tag_syntax_t;
+
+/* Define the common syntaxes. */
+#define N (ptsData|ptsReading) /* normal state */
+#define I {1,0,0,0} /* illegal or unusual */
+#define P {1,ptsInPage,ptsInPage|N,0} /* ordinary operator */
+#define C {1,ptsInSession,ptsInSession|N,0} /* control operator */
+#define R(p,r,t) /* reading operator */\
+ {1,ptsInSession|p|r,ptsInSession|p|N,t}
+#define D(n) {n,0,ptsData,ptsData} /* data type */
+#define DI D(1) /* invalid data type */
+#define A(n) {n,ptsData,ptsData,ptsData} /* attribute */
+
+/* Define the syntax of all tags. */
+private const px_tag_syntax_t tag_syntax[256] = {
+/*0x*/
+ I,I,I,I,I,I,I,I, I,I,I,I,I,I,I,I,
+/*1x*/
+ I,I,I,I,I,I,I,I, I,I,I,{2,0,0,0},I,I,I,I,
+/*2x*/
+ I,I,I,I,I,I,I,I, I,I,I,I,I,I,I,I,
+/*3x*/
+ I,I,I,I,I,I,I,I, I,I,I,I,I,I,I,I,
+/*4x*/
+ {2,0,ptsInSession|N,0},
+ {1,0,ptsInSession|N,ptsInSession},
+ {1,ptsInSession,ptsInSession|ptsInPage|N,ptsInSession},
+ {1,ptsInSession,ptsInSession|ptsInPage|N,ptsInPage},
+ {1,ptsInPage,ptsInPage|N,ptsInPage},
+ P,P,C,
+ C,C,P,P,P,P,P,R(0,0,ptsReadingFont),
+/*5x*/
+ R(0,ptsReadingFont,0),
+ R(0,ptsReadingFont,ptsReadingFont),
+ R(0,0,ptsReadingChar),
+ R(0,ptsReadingChar,0),
+ R(0,ptsReadingChar,ptsReadingChar),
+ C,P,P,
+ P,P,P,
+ {1,ptsInSession,ptsInSession|ptsExecStream|N,ptsReadingStream},
+ {1,ptsInSession|ptsReadingStream,ptsInSession|ptsExecStream|N,0},
+ {1,ptsInSession|ptsReadingStream,ptsInSession|ptsExecStream|N,ptsReadingStream},
+ {1,ptsInSession,ptsInSession|ptsReadingStream|N,0},
+ P,
+/*6x*/
+ P,P,P,P,P,P,P,P, P,P,P,P,P,P,P,P,
+/*7x*/
+ P,P,P,P,P,P,P,P, P,P,P,P,P,P,P,P,
+/*8x*/
+ P,P,P,P,P,P,P,P, P,P,P,P,P,P,P,P,
+/*9x*/
+ P,P,P,P,P,P,P,P, P,P,P,P,P,P,P,P,
+/*ax*/
+ P,P,P,P,P,P,P,P, P,P,P,P,P,P,P,P,
+/*bx*/
+ R(ptsInPage,0,ptsReadingImage),
+ R(ptsInPage,ptsReadingImage,0),
+ R(ptsInPage,ptsReadingImage,ptsReadingImage),
+ R(ptsInPage,0,ptsReadingRastPattern),
+ R(ptsInPage,ptsReadingRastPattern,0),
+ R(ptsInPage,ptsReadingRastPattern,ptsReadingRastPattern),
+ R(ptsInPage,0,ptsReadingScan),
+ P,
+ R(ptsInPage,ptsReadingScan,ptsReadingScan),
+ R(ptsInPage,ptsReadingScan,0),
+ P,P,P,P,P,P,
+/*cx*/
+ D(2),D(3),D(5),D(3),D(5),D(5),DI,DI,
+ D(3),D(3),D(3),D(3),D(3),D(3),DI,DI,
+/*dx*/
+ D(3),D(5),D(9),D(5),D(9),D(9),DI,DI,
+ DI,DI,DI,DI,DI,DI,DI,DI,
+/*ex*/
+ D(5),D(9),D(17),D(9),D(17),D(17),DI,DI,
+ DI,DI,DI,DI,DI,DI,DI,DI,
+/*fx*/
+ I,I,I,I,I,I,I,I,
+ A(2),A(3),{5,0,0,0},{2,0,0,0},I,I,I,I
+};
+#undef I
+#undef P
+#undef C
+#undef R
+#undef D
+#undef DI
+#undef A
+
+/* Allocate a parser state. */
+px_parser_state_t *
+px_process_alloc(gs_memory_t *memory)
+{ px_parser_state_t *st = gs_alloc_struct(memory, px_parser_state_t,
+ &st_px_parser_state,
+ "px_process_alloc");
+ if ( st == 0 )
+ return 0;
+ st->memory = memory;
+ px_process_init(st, true);
+ return st;
+}
+
+/* Initialize the parser state. */
+void
+px_process_init(px_parser_state_t *st, bool big_endian)
+{ st->big_endian = big_endian;
+ st->operator_count = 0;
+ st->parent_operator_count = 0;
+ st->last_operator = pxtNull;
+ st->saved_count = 0;
+ st->data_left = 0;
+ st->macro_state = ptsInitial;
+ st->stack_count = 0;
+ st->data_proc = 0;
+ { int i;
+ for ( i = 0; i < max_px_args; ++i )
+ st->args.pv[i] = 0;
+ }
+ st->args.parser = 0; /* for garbage collector */
+ memset(st->attribute_indices, 0, px_attribute_next);
+}
+
+/* Get numeric values from the input. */
+#define get_uint16(st, p) uint16at(p, st->big_endian)
+#define get_sint16(st, p) sint16at(p, st->big_endian)
+#define get_uint32(st, p) uint32at(p, st->big_endian)
+#define get_sint32(st, p) sint32at(p, st->big_endian)
+#define get_real32(st, p) real32at(p, st->big_endian)
+
+/* Move an array to the heap for persistence if needed. */
+/* See pxoper.h for details. */
+private int
+px_save_array(px_value_t *pv, px_state_t *pxs, client_name_t cname,
+ uint nbytes)
+{ if ( pv->type & pxd_on_heap )
+ { /* Turn off the "on heap" bit, to prevent freeing. */
+ pv->type &= ~pxd_on_heap;
+ }
+ else
+ { /* Allocate a heap copy. Only the first nbytes bytes */
+ /* of the data are valid. */
+ uint num_elements = pv->value.array.size;
+ uint elt_size = value_size(pv);
+
+ byte *copy = gs_alloc_byte_array(pxs->memory, num_elements,
+ elt_size, cname);
+ if ( copy == 0 )
+ return_error(errorInsufficientMemory);
+ memcpy(copy, pv->value.array.data, nbytes);
+ pv->value.array.data = copy;
+ }
+ return 0;
+}
+
+/* Clear the stack and the attribute indices, */
+/* and free heap-allocated arrays. */
+#define clear_stack()\
+ for ( ; sp > st->stack; --sp )\
+ { if ( sp->type & pxd_on_heap )\
+ gs_free_object(memory, (void *)sp->value.array.data,\
+ "px stack pop");\
+ st->attribute_indices[sp->attribute] = 0;\
+ }
+
+/* Define data tracing if debugging. */
+#ifdef DEBUG
+# define trace_data(format, cast, ptr, count)\
+ do\
+ { uint i_;\
+ for ( i_ = 0; i_ < count; ++i_ )\
+ dprintf1(format, (cast)((ptr)[i_]));\
+ dputc('\n');\
+ }\
+ while (0)
+private void
+trace_array_data(const char *label, const px_value_t *pav)
+{ px_data_type_t type = pav->type;
+ const byte *ptr = pav->value.array.data;
+ uint count = pav->value.array.size;
+ bool big_endian = (type & pxd_big_endian) != 0;
+ bool text = (type & pxd_ubyte) != 0;
+ uint i;
+
+ dputs(label);
+ dputs((type & pxd_ubyte ? " <" : " {"));
+ for ( i = 0; i < count; ++i )
+ { if ( !(i & 15) && i )
+ { const char *p;
+ dputs("\n ");
+ for ( p = label; *p; ++p )
+ dputc(' ');
+ }
+ if ( type & pxd_ubyte )
+ { dprintf1("%02x ", ptr[i]);
+ if ( ptr[i] < 32 || ptr[i] > 126 )
+ text = false;
+ }
+ else if ( type & pxd_uint16 )
+ dprintf1("%u ", uint16at(ptr + i * 2, big_endian));
+ else if ( type & pxd_sint16 )
+ dprintf1("%d ", sint16at(ptr + i * 2, big_endian));
+ else if ( type & pxd_uint32 )
+ dprintf1("%lu ", (ulong)uint32at(ptr + i * 4, big_endian));
+ else if ( type & pxd_sint32 )
+ dprintf1("%ld ", (long)sint32at(ptr + i * 4, big_endian));
+ else if ( type & pxd_real32 )
+ dprintf1("%g ", real32at(ptr + i * 4, big_endian));
+ else
+ dputs("? ");
+ }
+ dputs((type & pxd_ubyte ? ">\n" : "}\n"));
+ if ( text )
+ { dputs("%chars: \"");
+ debug_print_string(ptr, count);
+ dputs("\"\n");
+ }
+}
+# define trace_array(pav)\
+ if ( gs_debug_c('I') )\
+ trace_array_data("array:", pav)
+#else
+# define trace_data(format, cast, ptr, count) DO_NOTHING
+# define trace_array(pav) DO_NOTHING
+#endif
+
+/* Process a buffer of PCL XL commands. */
+int
+px_process(px_parser_state_t *st, px_state_t *pxs, stream_cursor_read *pr)
+{ const byte *orig_p = pr->ptr;
+ const byte *next_p = orig_p; /* start of data not copied to saved */
+ const byte *p;
+ const byte *rlimit;
+ px_value_t *sp = &st->stack[st->stack_count];
+#define stack_limit &st->stack[max_stack - 1]
+ gs_memory_t *memory = st->memory;
+ int code = 0;
+ uint left;
+ uint min_left;
+ px_tag_t tag;
+ const px_tag_syntax_t *syntax = 0;
+
+ st->args.parser = st;
+ st->parent_operator_count = 0; /* in case of error */
+ /* Check for leftover data from the previous call. */
+parse: if ( st->saved_count )
+ { /* Fill up the saved buffer so we can make progress. */
+ int move = min(sizeof(st->saved) - st->saved_count,
+ pr->limit - next_p);
+ memcpy(&st->saved[st->saved_count], next_p + 1, move);
+ next_p += move;
+ p = st->saved - 1;
+ rlimit = p + st->saved_count + move;
+ }
+ else
+ { /* No leftover data, just read from the input. */
+ p = next_p;
+ rlimit = pr->limit;
+ }
+top: if ( st->data_left )
+ { /* We're in the middle of reading an array or data block. */
+ if ( st->data_proc )
+ { /* This is a data block. */
+ uint avail = min(rlimit - p, st->data_left);
+ uint used;
+
+ st->args.source.available = avail;
+ st->args.source.data = p + 1;
+ code = (*st->data_proc)(&st->args, pxs);
+ used = st->args.source.data - (p + 1);
+#ifdef DEBUG
+ if ( gs_debug_c('I') )
+ { px_value_t data_array;
+ data_array.type = pxd_ubyte;
+ data_array.value.array.data = p + 1;
+ data_array.value.array.size = used;
+ trace_array_data("data:", &data_array);
+ }
+#endif
+ p = st->args.source.data - 1;
+ st->data_left -= used;
+ if ( code < 0 )
+ { st->args.source.position = 0;
+ goto x;
+ }
+ else if ( code == pxNeedData )
+ { code = 0; /* exit for more data */
+ goto x;
+ }
+ else
+ { st->args.source.position = 0;
+ st->data_proc = 0;
+ if ( st->data_left != 0 )
+ { code = gs_note_error(errorExtraData);
+ goto x;
+ }
+ clear_stack();
+ }
+ }
+ else
+ { /* This is an array. */
+ uint size = sp->value.array.size;
+ uint scale = value_size(sp);
+ uint nbytes = size * scale;
+ byte *dest =
+ (byte *)sp->value.array.data + nbytes - st->data_left;
+
+ left = rlimit - p;
+ if ( left < st->data_left )
+ { /* We still don't have enough data to fill the array. */
+ memcpy(dest, p + 1, left);
+ st->data_left -= left;
+ p = rlimit;
+ code = 0;
+ goto x;
+ }
+ /* Complete the array and continue parsing. */
+ memcpy(dest, p + 1, st->data_left);
+ trace_array(sp);
+ p += st->data_left;
+ }
+ st->data_left = 0;
+ }
+ else if ( st->data_proc )
+ { /* An operator is awaiting data. */
+ /* Skip white space until we find some. */
+ code = 0; /* in case we exit */
+ while ( (left = rlimit - p) != 0 )
+ { switch ( (tag = p[1]) )
+ {
+ case pxtNull:
+ case pxtHT: case pxtLF: case pxtVT: case pxtFF: case pxtCR:
+ ++p;
+ continue;
+ case pxt_dataLength:
+ if ( left < 5 )
+ goto x; /* can't look ahead */
+ st->data_left = get_uint32(st, p + 2);
+ if_debug3('i', "pos=%8ld tag= 0x%2x data, length %u\n",
+ (long)(p - pr->ptr), p[1], st->data_left);
+ p += 5;
+ goto top;
+ case pxt_dataLengthByte:
+ if ( left < 2 )
+ goto x; /* can't look ahead */
+ st->data_left = p[2];
+ if_debug3('i', "pos=%8ld tag= 0x%2x data, length %u\n",
+ (long)(p - pr->ptr), p[1], st->data_left);
+ p += 2;
+ goto top;
+ default:
+ { code = gs_note_error(errorMissingData);
+ goto x;
+ }
+ }
+ }
+ }
+ st->args.source.position = 0;
+ st->args.source.available = 0;
+ while ( (left = rlimit - p) != 0 &&
+ left >= (min_left = (syntax = &tag_syntax[tag = p[1]])->min_input)
+ )
+ { int count;
+#ifdef DEBUG
+ if ( gs_debug_c('i') )
+ { dprintf2("pos=%8ld tag= 0x%02x ", (long)(p - pr->ptr), tag);
+ if ( tag == pxt_attr_ubyte || tag == pxt_attr_uint16 )
+ { px_attribute_t attr =
+ (tag == pxt_attr_ubyte ? p[2] : get_uint16(st, p + 2));
+ const char *aname = px_attribute_names[attr];
+ if ( aname )
+ dprintf1(" @%s\n", aname);
+ else
+ dprintf1(" attribute %u ???\n", attr);
+ }
+ else
+ { const char *format;
+ const char *tname;
+
+ if ( tag < 0x40 )
+ format = "%s\n", tname = px_tag_0_names[tag];
+ else if ( tag < 0xc0 )
+ format = "%s\n", tname = px_operator_names[tag - 0x40];
+ else
+ { tname = px_tag_c0_names[tag - 0xc0];
+ if ( tag < 0xf0 )
+ format = " %s"; /* data values follow */
+ else
+ format = "%s\n";
+ }
+ if ( tname )
+ dprintf1(format, tname);
+ else
+ dputs("???\n");
+ }
+ }
+#endif
+ if ( (st->macro_state & syntax->state_mask) != syntax->state_value )
+ { /*
+ * We should probably distinguish here between
+ * out-of-context operators and illegal tags, but it's too
+ * much trouble.
+ */
+ code = gs_note_error(errorIllegalOperatorSequence);
+ if ( tag >= 0x40 && tag < 0xc0 )
+ st->last_operator = tag;
+ goto x;
+ }
+ st->macro_state ^= syntax->state_transition;
+ switch ( tag >> 3 )
+ {
+ case 0:
+ switch ( tag )
+ {
+ case pxtNull: ++p; continue;
+ default: break;
+ }
+ break;
+ case 1:
+ switch ( tag )
+ {
+ case pxtHT: case pxtLF: case pxtVT: case pxtFF: case pxtCR:
+ ++p;
+ continue;
+ default:
+ break;
+ }
+ break;
+ case 3:
+ if ( tag == pxt1b ) /* ESC */
+ { /* Check for UEL */
+ if ( memcmp(p + 1, "\033%-12345X", min(left, 9)) )
+ break; /* not UEL, error */
+ if ( left < 9 )
+ goto x; /* need more data */
+ p += 9;
+ code = gs_note_error(e_ExitLanguage);
+ goto x;
+ }
+ break;
+ case 4:
+ switch ( tag )
+ {
+ case pxtSpace: continue;
+ default: break;
+ }
+ break;
+ case 8: case 9:
+ case 10: case 11: case 12: case 13: case 14:
+ case 15: case 16: case 17: case 18: case 19:
+ case 20: case 21: case 22: case 23:
+ /* Operators */
+ /* Make sure that we have all the required attributes, */
+ /* and no attributes that are neither required nor */
+ /* optional. (It's up to the operator to make any */
+ /* more precise checks than this. */
+ st->operator_count++;
+ st->last_operator = tag;
+ { const px_operator_definition_t *pod =
+ &px_operator_definitions[tag - 0x40];
+ int left = sp - st->stack;
+ const byte /*px_attribute_t*/ *pal = pod->attrs;
+ px_value_t **ppv = st->args.pv;
+ bool required = true;
+
+ code = 0;
+ /*
+ * Scan the attributes. Illegal attributes take priority
+ * over missing attributes, which in turn take priority
+ * over illegal data types.
+ */
+ for ( ; ; ++pal, ++ppv )
+ { px_attribute_t attr = *pal;
+ uint index;
+
+ if ( !attr )
+ { /*
+ * We've reached the end of either the required or
+ * the optional attribute list.
+ */
+ if ( !required )
+ break;
+ required = false;
+ --ppv; /* cancel incrementing */
+ continue;
+ }
+ if ( (index = st->attribute_indices[attr]) == 0 )
+ { if ( required )
+ code = gs_note_error(errorMissingAttribute);
+ else
+ *ppv = 0;
+ }
+ else
+ { /* Check the attribute data type and value. */
+ px_value_t *pv = *ppv = &st->stack[index];
+ const px_attr_value_type_t *pavt =
+ &px_attr_value_types[attr];
+ int acode;
+
+ if ( (~pavt->mask & pv->type &
+ (pxd_structure | pxd_representation)) ||
+ (pavt->mask == (pxd_scalar | pxd_ubyte) &&
+ (pv->value.i < 0 || pv->value.i > pavt->limit))
+ )
+ { if ( code >= 0 )
+ code = gs_note_error(errorIllegalAttributeDataType);
+ }
+ if ( pavt->proc != 0 && (acode = (*pavt->proc)(pv)) < 0 )
+ { if ( code >= 0 )
+ code = acode;
+ }
+ --left;
+ }
+ }
+
+ /* Make sure there are no attributes left over. */
+ if ( left )
+ code = gs_note_error(errorIllegalAttribute);
+ if ( code >= 0 )
+ code = (*pod->proc)(&st->args, pxs);
+ if ( code < 0 )
+ goto x;
+ /* Check whether the operator wanted source data. */
+ if ( code == pxNeedData )
+ { if ( !pxs->data_source_open )
+ { code = gs_note_error(errorDataSourceNotOpen);
+ goto x;
+ }
+ st->data_proc = pod->proc;
+ ++p;
+ goto top;
+ }
+ }
+ clear_stack();
+ ++p;
+ continue;
+ case 24: sp[1].type = pxd_scalar; count = 1; goto data;
+ case 26: sp[1].type = pxd_xy; count = 2; goto data;
+ case 28: sp[1].type = pxd_box; count = 4; goto data;
+ /* Scalar, point, and box data */
+data: { int i;
+ if ( sp == stack_limit )
+ { code = gs_note_error(errorInternalOverflow);
+ goto x;
+ }
+ ++sp;
+ sp->attribute = 0;
+ p += 2;
+#ifdef DEBUG
+# define trace_scalar(format, cast, alt)\
+ if ( gs_debug_c('i') )\
+ trace_data(format, cast, sp->value.alt, count)
+#else
+# define trace_scalar(format, cast, alt) DO_NOTHING
+#endif
+ switch ( tag & 7 )
+ {
+ case pxt_ubyte & 7:
+ sp->type |= pxd_ubyte;
+ for ( i = 0; i < count; ++p, ++i )
+ sp->value.ia[i] = *p;
+dux: trace_scalar(" %lu", ulong, ia);
+ --p;
+ continue;
+ case pxt_uint16 & 7:
+ sp->type |= pxd_uint16;
+ for ( i = 0; i < count; p += 2, ++i )
+ sp->value.ia[i] = get_uint16(st, p);
+ goto dux;
+ case pxt_uint32 & 7:
+ sp->type |= pxd_uint32;
+ for ( i = 0; i < count; p += 4, ++i )
+ sp->value.ia[i] = get_uint32(st, p);
+ goto dux;
+ case pxt_sint16 & 7:
+ sp->type |= pxd_sint16;
+ for ( i = 0; i < count; p += 2, ++i )
+ sp->value.ia[i] = get_sint16(st, p);
+dsx: trace_scalar(" %ld", long, ia);
+ --p;
+ continue;
+ case pxt_sint32 & 7:
+ sp->type |= pxd_sint32;
+ for ( i = 0; i < count; p += 4, ++i )
+ sp->value.ia[i] = get_sint32(st, p);
+ goto dsx;
+ case pxt_real32 & 7:
+ sp->type |= pxd_real32;
+ for ( i = 0; i < count; p += 4, ++i )
+ sp->value.ra[i] = get_real32(st, p);
+ trace_scalar(" %g", double, ra);
+ --p;
+ continue;
+ default:
+ break;
+ }
+ }
+ break;
+ case 25:
+ /* Array data */
+ { const byte *dp;
+ uint nbytes;
+ if ( sp == stack_limit )
+ { code = gs_note_error(errorInternalOverflow);
+ goto x;
+ }
+ switch ( p[2] )
+ {
+ case pxt_ubyte:
+ sp[1].value.array.size = p[3];
+ dp = p + 4;
+ break;
+ case pxt_uint16:
+ if ( left < 4 )
+ { if_debug0('i', "...\n");
+ /* Undo the state transition. */
+ st->macro_state ^= syntax->state_transition;
+ goto x;
+ }
+ sp[1].value.array.size = get_uint16(st, p+3);
+ dp = p + 5;
+ break;
+ default:
+ st->last_operator = tag; /* for error message */
+ code = gs_note_error(errorIllegalTag);
+ goto x;
+ }
+ nbytes = sp[1].value.array.size;
+ if_debug1('i', "[%u]\n", sp[1].value.array.size);
+ switch ( tag )
+ {
+ case pxt_ubyte_array:
+ sp[1].type = pxd_array | pxd_ubyte;
+array: ++sp;
+ if ( st->big_endian )
+ sp->type |= pxd_big_endian;
+ sp->value.array.data = dp;
+ sp->attribute = 0;
+ /* Check whether we have enough data for the entire */
+ /* array. */
+ if ( rlimit + 1 - dp < nbytes )
+ { /* Exit now, continue reading when we return. */
+ uint avail = rlimit + 1 - dp;
+
+ code = px_save_array(sp, pxs, "partial array",
+ avail);
+ if ( code < 0 )
+ goto x;
+ sp->type |= pxd_on_heap;
+ st->data_left = nbytes - avail;
+ st->data_proc = 0;
+ p = rlimit;
+ goto x;
+ }
+ p = dp + nbytes - 1;
+ trace_array(sp);
+ continue;
+ case pxt_uint16_array:
+ sp[1].type = pxd_array | pxd_uint16;
+a16: nbytes <<= 1;
+ goto array;
+ case pxt_uint32_array:
+ sp[1].type = pxd_array | pxd_uint32;
+a32: nbytes <<= 2;
+ goto array;
+ case pxt_sint16_array:
+ sp[1].type = pxd_array | pxd_sint16;
+ goto a16;
+ case pxt_sint32_array:
+ sp[1].type = pxd_array | pxd_sint32;
+ goto a32;
+ case pxt_real32_array:
+ sp[1].type = pxd_array | pxd_real32;
+ goto a32;
+ default:
+ break;
+ }
+ break;
+ }
+ break;
+ case 31:
+ { px_attribute_t attr;
+ const byte *pnext;
+
+ switch ( tag )
+ {
+ case pxt_attr_ubyte:
+ attr = p[2];
+ pnext = p + 2;
+ goto a;
+ case pxt_attr_uint16:
+ attr = get_uint16(st, p+2);
+ pnext = p + 3;
+a: if ( attr >= px_attribute_next )
+ break;
+ /*
+ * We could check the attribute value type here, but
+ * in order to match the behavior of the H-P printers,
+ * we don't do it until we see the operator.
+ *
+ * It is legal to specify the same attribute more than
+ * once; the last value has priority. If this happens,
+ * since the order of attributes doesn't matter, we can
+ * just replace the former value on the stack.
+ */
+ sp->attribute = attr;
+ if ( st->attribute_indices[attr] != 0 )
+ { px_value_t *old_sp =
+ &st->stack[st->attribute_indices[attr]];
+ /* If the old value is on the heap, free it. */
+ if ( old_sp->type & pxd_on_heap )
+ gs_free_object(memory, old_sp->value.array.data,
+ "old value for duplicate attribute");
+ *old_sp = *sp--;
+ }
+ else
+ st->attribute_indices[attr] = sp - st->stack;
+ p = pnext;
+ continue;
+ case pxt_dataLength:
+ /*
+ * Unexpected data length operators are normally not
+ * allowed, but there might be a zero-length data
+ * block immediately following a zero-size image,
+ * which doesn't ask for any data.
+ */
+ if ( uint32at(p + 2, true /*arbitrary*/) == 0 )
+ { p += 5;
+ continue;
+ }
+ break;
+ case pxt_dataLengthByte:
+ /* See the comment under pxt_dataLength above. */
+ if ( p[2] == 0 )
+ { p += 2;
+ continue;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ /* Unknown tag value. Report an error. */
+ st->last_operator = tag; /* for error message */
+ code = gs_note_error(errorIllegalTag);
+ break;
+ }
+x: /* Save any leftover input. */
+ left = rlimit - p;
+ if ( rlimit != pr->limit )
+ { /* We were reading saved input. */
+ if ( left <= next_p - orig_p )
+ { /* We finished reading the previously saved input. */
+ /* Continue reading current input, unless we got an error. */
+ p = next_p -= left;
+ rlimit = pr->limit;
+ st->saved_count = 0;
+ if ( code >= 0 )
+ goto parse;
+ }
+ else
+ { /* There's still some previously saved input left over. */
+ memmove(st->saved, p + 1, st->saved_count = left);
+ p = next_p;
+ rlimit = pr->limit;
+ left = rlimit - p;
+ }
+ }
+ /* Except in case of error, save any remaining input. */
+ if ( code >= 0 )
+ { if ( left + st->saved_count > sizeof(st->saved) )
+ { /* Fatal error -- shouldn't happen! */
+ code = gs_note_error(errorInternalOverflow);
+ st->saved_count = 0;
+ }
+ else
+ { memcpy(&st->saved[st->saved_count], p + 1, left);
+ st->saved_count += left;
+ p = rlimit;
+ }
+ }
+ pr->ptr = p;
+ st->stack_count = sp - st->stack;
+ /* Move to the heap any arrays whose data was being referenced */
+ /* directly in the input buffer. */
+ for ( ; sp > st->stack; --sp )
+ if ( (sp->type & (pxd_array | pxd_on_heap)) == pxd_array )
+ { int code = px_save_array(sp, pxs, "px stack array to heap",
+ sp->value.array.size * value_size(sp));
+ if ( code < 0 )
+ break;
+ sp->type |= pxd_on_heap;
+ }
+ if ( code < 0 && syntax != 0 )
+ { /* Undo the state transition. */
+ st->macro_state ^= syntax->state_transition;
+ }
+ return code;
+}
diff --git a/pxl/pxparse.h b/pxl/pxparse.h
new file mode 100644
index 000000000..bf29886b3
--- /dev/null
+++ b/pxl/pxparse.h
@@ -0,0 +1,90 @@
+/* Copyright (C) 1996, 1997 Aladdin Enterprises. All rights reserved.
+ Unauthorized use, copying, and/or distribution prohibited.
+ */
+
+/* pxparse.h */
+/* Definitions and interface for PCL XL parser */
+/* Requires gsmemory.h */
+
+#ifndef pxparse_INCLUDED
+# define pxparse_INCLUDED
+
+#include "pxoper.h"
+
+/* ---------------- Definitions ---------------- */
+
+/*
+ * Define the minimum parser input buffer size. The input buffer can
+ * contain as much or as little data as the client wishes to supply;
+ * the parser will always consume all of it.
+ *
+ * The minimum input buffer size is determined by the largest minimum size
+ * required by the data decompression methods for bitmaps, which is currently
+ * 1 byte for the RLE method. Aside from that, the minimum size demanded
+ * by the language itself is the size of the largest fixed-size language
+ * token, which is 17 bytes (real32 or int32 box). However, there is no
+ * requirement that the client provide a buffer of this size: the parser
+ * will buffer data internally if necessary.
+ */
+#define px_parser_max_token_size 17
+#define px_parser_min_input_size px_parser_max_token_size
+
+/* Define the parser macro-states (used only for checking). */
+#define ptsInSession 0x01
+#define ptsInPage 0x02
+#define ptsData 0x04 /* read data, want attribute */
+#define ptsReadingStream 0x08
+#define ptsReadingRastPattern 0x10
+#define ptsReadingChar 0x18
+#define ptsReadingFont 0x20
+#define ptsReadingImage 0x28
+#define ptsReadingScan 0x30
+#define ptsReading 0x38 /* reading state */
+#define ptsExecStream 0x40 /* executing a stream */
+#define ptsInitial 0
+
+/* Define the parser state. */
+#ifndef px_parser_state_t_DEFINED
+# define px_parser_state_t_DEFINED
+typedef struct px_parser_state_s px_parser_state_t;
+#endif
+#define max_stack max_px_args /* must not exceed 256 */
+struct px_parser_state_s {
+ /* Set at initialization */
+ gs_memory_t *memory;
+ bool big_endian;
+ /* Updated dynamically, for error reporting only */
+ long operator_count;
+ long parent_operator_count;
+ int /*px_tag_t*/ last_operator; /* pxtNull if none */
+ /* Updated dynamically */
+ int saved_count; /* amount of leftover input in saved */
+ uint data_left; /* amount left to read of data or array */
+ uint macro_state; /* mask for macro-state */
+ int stack_count;
+ px_operator_proc((*data_proc)); /* operator awaiting data, if any */
+ byte saved[px_parser_max_token_size];
+ px_value_t stack[max_stack + 2];
+ px_args_t args;
+ byte attribute_indices[px_attribute_next]; /* indices of attrs on stack */
+};
+
+/* Define an abstract type for the state. */
+#ifndef px_state_DEFINED
+# define px_state_DEFINED
+typedef struct px_state_s px_state_t;
+#endif
+
+/* ---------------- Procedural interface ---------------- */
+
+/* Allocate a parser state. */
+px_parser_state_t *px_process_alloc(P1(gs_memory_t *memory));
+
+/* Initialize the parser state. */
+void px_process_init(P2(px_parser_state_t *st, bool big_endian));
+
+/* Process a buffer of PCL XL commands. */
+int px_process(P3(px_parser_state_t *st, px_state_t *pxs,
+ stream_cursor_read *pr));
+
+#endif /* pxparse_INCLUDED */
diff --git a/pxl/pxprint.bat b/pxl/pxprint.bat
new file mode 100644
index 000000000..ee9605c0e
--- /dev/null
+++ b/pxl/pxprint.bat
@@ -0,0 +1,17 @@
+if '%1'=='' goto usage
+if '%2'=='' goto p1
+if '%3'=='' goto p2
+if '%4'=='' goto p3
+goto usage
+:p3
+pclxl -sDEVICE#ljet4 -dMaxBitmap#200000 -dBufferSpace#200000 -sOutputFile#PRN -dNOPAUSE %1 -dFirstPage#%2 -dLastPage#%3
+goto x
+:p2
+pclxl -sDEVICE#ljet4 -dMaxBitmap#200000 -dBufferSpace#200000 -sOutputFile#PRN -dNOPAUSE %1 -dFirstPage#%2
+goto x
+:p1
+pclxl -sDEVICE#ljet4 -dMaxBitmap#200000 -dBufferSpace#200000 -sOutputFile#PRN -dNOPAUSE %1
+goto x
+:usage
+echo Usage: pxprint file [first-page [last-page]]
+:x
diff --git a/pxl/pxprint.tcl b/pxl/pxprint.tcl
new file mode 100755
index 000000000..459936d73
--- /dev/null
+++ b/pxl/pxprint.tcl
@@ -0,0 +1,16 @@
+#!/usr/bin/tclsh
+proc print {file {first ""} {last ""}} {
+ if {$first == ""} {
+ set first 1
+ set last 99999
+ } elseif {$last == ""} {
+ set last $first
+ }
+ exec pclxl -sDEVICE=ljet4 -dMaxBitmap=200000 -dBufferSpace=200000 -sOutputFile=/prn -dNOPAUSE -dFirstPage=$first -dLastPage=$last $file >@stdout 2>@stderr
+}
+set argc [llength $argv]
+if {$argc < 1 || $argc > 3} {
+ puts stderr "Usage: pxprint file [first-page [last-page]]"
+ exit
+}
+eval print $argv
diff --git a/pxl/pxptable.c b/pxl/pxptable.c
new file mode 100644
index 000000000..29b473a9a
--- /dev/null
+++ b/pxl/pxptable.c
@@ -0,0 +1,673 @@
+/* Copyright (C) 1996 Aladdin Enterprises. All rights reserved.
+ Unauthorized use, copying, and/or distribution prohibited.
+ */
+
+/* pxptable.c */
+/* PCL XL parser tables */
+
+#include "std.h"
+#include "pxenum.h"
+#include "pxoper.h"
+#include "pxvalue.h"
+#include "pxptable.h" /* requires pxenum.h, pxoper.h, pxvalue.h */
+
+/* ---------------- Attribute values ---------------- */
+
+#define sc(sizes) {pxd_scalar|sizes} /* scalar */
+#define scp(sizes,proc) {pxd_scalar|sizes, 0, proc}
+#define scub() {pxd_scalar|ub, 255}
+#define xy(sizes) {pxd_xy|sizes} /* XY pair */
+#define xyp(sizes,proc) {pxd_xy|sizes, 0, proc}
+#define box(sizes) {pxd_box|sizes} /* box */
+#define arr(sizes) {pxd_array|sizes} /* array */
+#define arrp(sizes,proc) {pxd_array|sizes, 0, proc}
+#define en(limit) {pxd_scalar|pxd_ubyte, (limit)-1} /* enumeration */
+#define enp(limit,proc) {pxd_scalar|pxd_ubyte, (limit)-1, proc}
+#define zero en(1) /* must be zero */
+#define ub pxd_ubyte
+#define us pxd_uint16
+#define ul pxd_uint32
+#define ss pxd_sint16
+#define sl pxd_sint32
+#define rl pxd_real32
+
+#define none {0}
+#define none5 none, none, none, none, none
+
+#define ok_iff(test)\
+ ((test) ? 0 : gs_note_error(errorIllegalAttributeValue))
+
+private int
+checkCharAngle(const px_value_t *pv)
+{ real v = real_value(pv, 0);
+ return ok_iff(v >= -360 && v <= 360);
+}
+private int
+checkCharBoldValue(const px_value_t *pv)
+{ return ok_iff(pv->value.r >= 0 && pv->value.r <= 1);
+}
+private int
+checkCharScale(const px_value_t *pv)
+{ real x = real_value(pv, 0), y = real_value(pv, 1);
+ return ok_iff(x >= -32768 && x <= 32767 && y >= -32768 && y <= 32767);
+}
+#define checkCharShear checkCharScale
+private int
+checkDestinationSize(const px_value_t *pv)
+{ return ok_iff(pv->value.ia[0] != 0 && pv->value.ia[1] != 0);
+}
+private int
+checkDitherMatrixDataType(const px_value_t *pv)
+{ return ok_iff(pv->value.i == eUByte);
+}
+private int
+checkDitherMatrixDepth(const px_value_t *pv)
+{ return ok_iff(pv->value.i == e8Bit);
+}
+private int
+checkDitherMatrixSize(const px_value_t *pv)
+{ return ok_iff(pv->value.i >= 1 && pv->value.i <= 256);
+}
+private int
+checkGrayLevel(const px_value_t *pv)
+{ return ok_iff(pv->type & pxd_any_real ?
+ pv->value.r >= 0 && pv->value.r <= 1 :
+ true);
+}
+#define checkNewDestinationSize checkDestinationSize
+private int
+checkPageAngle(const px_value_t *pv)
+{ integer angle = pv->value.i;
+ return ok_iff(angle >= -360 && angle <= 360 && (angle % 90) == 0);
+}
+private int
+checkPageScale(const px_value_t *pv)
+{ real x = real_value(pv, 0), y = real_value(pv, 1);
+ return ok_iff(x >= 0 && x <= 32767 && y >= 0 && y <= 32767);
+}
+private int
+checkRGBColor(const px_value_t *pv)
+{ if ( pv->value.array.size != 3 )
+ return_error(errorIllegalArraySize);
+ if ( pv->type & pxd_any_real )
+ { /* Check for values between 0 and 1. */
+ uint i;
+ for ( i = 0; i < pv->value.array.size; ++i )
+ { real v = real_elt(pv, i);
+ if ( v < 0.0 || v > 1.0 )
+ return_error(errorIllegalAttributeValue);
+ }
+ }
+ return 0;
+}
+private int
+checkSourceHeight(const px_value_t *pv)
+{ return ok_iff(pv->value.i >= 1);
+}
+#define checkSourceWidth checkSourceHeight
+private int
+checkUnitsPerMeasure(const px_value_t *pv)
+{ real x = real_value(pv, 0), y = real_value(pv, 1);
+ return ok_iff(x > 0 && x <= 65535 && y > 0 && y <= 65535);
+}
+
+#undef ok_iff
+
+const px_attr_value_type_t px_attr_value_types[] = {
+ none,
+ none,
+ en(pxeColorDepth_next), /* PaletteDepth = 2 */
+ en(pxeColorSpace_next), /* ColorSpace */
+ zero, /* NullBrush */
+ zero, /* NullPen */
+ arr(ub), /* PaletteData */
+ none,
+ sc(ss), /* PatternSelectID = 8 */
+ scp(ub|rl, checkGrayLevel), /* GrayLevel */
+ none,
+ arrp(ub|rl, checkRGBColor), /* RGBColor = 11 */
+ xy(ss), /* PatternOrigin */
+ xyp(us, checkNewDestinationSize), /* NewDestinationSize */
+ none,
+ none5,
+ none5,
+ none5,
+ none,
+ none,
+ none,
+ en(pxeDitherMatrix_next), /* DeviceMatrix = 33 */
+ enp(pxeDataType_next, checkDitherMatrixDataType), /* DitherMatrixDataType */
+ xy(ub|us|ss), /* DitherOrigin */
+ scub(), /* MediaDestination -- NOT DOCUMENTED, THIS IS A GUESS */
+ scub(), /* MediaSize */
+ scub(), /* MediaSource */
+ scub(), /* MediaType -- NOT DOCUMENTED, THIS IS A GUESS */
+ scub(), /* Orientation -- illegal values only produce a warning! */
+ scp(us|ss, checkPageAngle), /* PageAngle */
+ xy(ub|us|ss), /* PageOrigin */
+ xyp(ub|us|rl, checkPageScale), /* PageScale */
+ scub(), /* ROP3 */
+ en(pxeTxMode_next), /* TxMode */
+ none,
+ xy(us|rl), /* CustomMediaSize = 47 */
+ en(pxeMeasure_next), /* CustomMediaSizeUnits */
+ sc(us), /* PageCopies */
+ xyp(us, checkDitherMatrixSize), /* DitherMatrixSize */
+ enp(pxeColorDepth_next, checkDitherMatrixDepth), /* DitherMatrixDepth */
+ en(pxeSimplexPageMode_next), /* SimplexPageMode */
+ en(pxeDuplexPageMode_next), /* DuplexPageMode */
+ en(pxeDuplexPageSide_next), /* DuplexPageSide */
+ none5,
+ none5,
+ en(pxeArcDirection_next), /* ArcDirection = 65 */
+ box(ub|us|ss), /* BoundingBox */
+ sc(ub|us|ss), /* DashOffset */
+ xy(ub|us), /* EllipseDimension */
+ xy(ub|us|ss), /* EndPoint */
+ en(pxeFillMode_next), /* FillMode */
+ en(pxeLineCap_next), /* LineCapStyle */
+ en(pxeLineJoin_next), /* LineJoinStyle */
+ sc(ub|us), /* MiterLength */
+ arr(ub|us|ss), /* LineDashStyle */
+ sc(ub|us), /* PenWidth */
+ xy(ub|us|ss), /* Point */
+ sc(ub|us), /* NumberOfPoints */
+ zero, /* SolidLine */
+ xy(ub|us|ss), /* StartPoint */
+ en(pxeDataType_next), /* PointType */
+ xy(ub|us|ss), /* ControlPoint1 */
+ xy(ub|us|ss), /* ControlPoint2 */
+ en(pxeClipRegion_next), /* ClipRegion */
+ en(pxeClipMode_next), /* ClipMode */
+ none5,
+ none5,
+ none,
+ none,
+ none,
+ en(pxeColorDepth_next), /* ColorDepth = 98 */
+ sc(us), /* BlockHeight */
+ en(pxeColorMapping_next), /* ColorMapping */
+ en(pxeCompressMode_next), /* CompressMode */
+ box(us), /* DestinationBox */
+ xyp(us, checkDestinationSize), /* DestinationSize */
+ en(pxePatternPersistence_next), /* PatternPersistence */
+ sc(ss), /* PatternDefineID */
+ none,
+ scp(us, checkSourceHeight), /* SourceHeight = 107 */
+ scp(us, checkSourceWidth), /* SourceWidth */
+ sc(us), /* StartLine */
+ none,
+ none,
+ none,
+ none,
+ none,
+ sc(us), /* NumberOfScanLines = 115 */
+ none,
+ none,
+ none,
+ none,
+ none5,
+ none,
+ none,
+ none,
+ none,
+ arr(ub|us), /* CommentData = 129 */
+ en(pxeDataOrg_next), /* DataOrg */
+ none,
+ none,
+ none,
+ en(pxeMeasure_next), /* Measure = 134 */
+ none,
+ en(pxeDataSource_next), /* SourceType = 136 */
+ xyp(us|rl, checkUnitsPerMeasure), /* UnitsPerMeasure */
+ none,
+ arr(ub|us), /* StreamName = 139 */
+ sc(ul), /* StreamDataLength */
+ none,
+ none,
+ en(pxeErrorReport_next), /* ErrorReport = 143 */
+ none,
+ none5,
+ none5,
+ none5,
+ none,
+ scp(us|ss|rl, checkCharAngle), /* CharAngle = 161 */
+ sc(ub|us), /* CharCode */
+ /* The spec says CharDataSize requires a uint16 argument, */
+ /* but the H-P driver (sometimes?) emits a uint32. */
+ sc(us|ul), /* CharDataSize */
+ xyp(ub|us|rl, checkCharScale), /* CharScale */
+ xyp(ub|us|ss|rl, checkCharShear), /* CharShear */
+ sc(ub|us|rl), /* CharSize */
+ sc(us), /* FontHeaderLength */
+ arr(ub|us), /* FontName */
+ zero, /* FontFormat */
+ sc(us), /* SymbolSet */
+ arr(ub|us), /* TextData */
+ arr(ub), /* CharSubModeArray */
+ none,
+ none,
+ arr(ub|us|ss), /* XSpacingData = 175 */
+ arr(ub|us|ss), /* YSpacingData */
+ scp(rl, checkCharBoldValue), /* CharBoldValue */
+};
+
+#undef v
+#undef vp
+#undef vub
+#undef xy
+#undef xyp
+#undef box
+#undef array
+#undef arrayp
+#undef en
+#undef enp
+#undef zero
+#undef ub
+#undef us
+#undef ul
+#undef ss
+#undef sl
+#undef rl
+#undef none
+#undef none5
+
+/* ---------------- Attribute names for debugging ---------------- */
+
+#ifdef DEBUG
+
+const char *px_attribute_names[] = {
+/*0*/
+ 0, 0, "PaletteDepth", "ColorSpace", "NullBrush",
+ "NullPen", "PaletteData", 0, "PatternSelectID", "GrayLevel",
+/*10*/
+ 0, "RGBColor", "PatternOrigin", "NewDestinationSize", 0,
+ 0, 0, 0, 0, 0,
+/*20*/
+ 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0,
+/*30*/
+ 0, 0, 0, "DeviceMatrix", "DitherMatrixDataType",
+ "DitherOrigin", "MediaDestination", "MediaSize", "MediaSource", "MediaType",
+/*40*/
+ "Orientation", "PageAngle", "PageOrigin", "PageScale", "ROP3",
+ "TxMode", 0, "CustomMediaSize", "CustomMediaSizeUnits", "PageCopies",
+/*50*/
+ "DitherMatrixSize", "DitherMatrixDepth", "SimplexPageMode", "DuplexPageMode",
+ "DuplexPageSide",
+ 0, 0, 0, 0, 0,
+/*60*/
+ 0, 0, 0, 0, 0,
+ "ArcDirection", "BoundingBox", "DashOffset", "EllipseDimension", "EndPoint",
+/*70*/
+ "FillMode", "LineCapStyle", "LineJoinStyle", "MiterLength", "LineDashStyle",
+ "PenWidth", "Point", "NumberOfPoints", "SolidLine", "StartPoint",
+/*80*/
+ "PointType", "ControlPoint1", "ControlPoint2", "ClipRegion", "ClipMode",
+ 0, 0, 0, 0, 0,
+/*90*/
+ 0, 0, 0, 0, 0,
+ 0, 0, 0, "ColorDepth", "BlockHeight",
+/*100*/
+ "ColorMapping", "CompressMode", "DestinationBox", "DestinationSize",
+ "PatternPersistence",
+ "PatternDefineID", 0, "SourceHeight", "SourceWidth", "StartLine",
+/*110*/
+ 0, 0, 0, 0, 0,
+ "NumberOfScanLines", 0, 0, 0, 0,
+/*120*/
+ 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, "CommentData",
+/*130*/
+ "DataOrg", 0, 0, 0, "Measure",
+ 0, "SourceType", "UnitsPerMeasure", 0, "StreamName",
+/*140*/
+ "StreamDataLength", 0, 0, "ErrorReport", 0,
+ 0, 0, 0, 0, 0,
+/*150*/
+ 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0,
+/*160*/
+ 0, "CharAngle", "CharCode", "CharDataSize", "CharScale",
+ "CharShear", "CharSize", "FontHeaderLength", "FontName", "FontFormat",
+/*170*/
+ "SymbolSet", "TextData", "CharSubModeArray", 0, 0,
+ "XSpacingData", "YSpacingData", "CharBoldValue"
+};
+
+#endif
+
+/* ---------------- Tag names for debugging ---------------- */
+
+#ifdef DEBUG
+
+const char *px_tag_0_names[0x40] = {
+/*0x*/
+ "Null", 0, 0, 0,
+ 0, 0, 0, 0,
+ 0, "HT", "LF", "VT",
+ "FF", "CR", 0, 0,
+/*1x*/
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, "ESC",
+ 0, 0, 0, 0,
+/*2x*/
+ "Space", 0, 0, 0,
+ 0, 0, 0, "_beginASCII",
+ "_beginBinaryMSB", "_beginBinaryLSB", 0, 0,
+ 0, 0, 0, 0,
+/*3x*/
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0
+};
+
+const char *px_tag_c0_names[0x40] = {
+/*cx*/
+ "_ubyte", "_uint16", "_uint32", "_sint16",
+ "_sint32", "_real32", 0, 0,
+ "_ubyte_array", "_uint16_array", "_uint32_array", "_sint16_array",
+ "_sint32_array", "_real32_array", 0, 0,
+/*dx*/
+ "_ubyte_xy", "_uint16_xy", "_uint32_xy", "_sint16_xy",
+ "_sint32_xy", "_real32_xy", 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+/*ex*/
+ "_ubyte_box", "_uint16_box", "_uint32_box", "_sint16_box",
+ "_sint32_box", "_real32_box", 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+/*fx*/
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ "_attr_ubyte", "_attr_uint16", "_dataLength", "_dataLengthByte",
+ 0, 0, 0, 0
+};
+
+#endif
+
+/* ---------------- Operator names ---------------- */
+
+/* These are needed even in non-debug configurations, */
+/* for producing error reports. */
+
+const char *px_operator_names[0x80] = {
+/*4x*/
+ 0, "BeginSession", "EndSession", "BeginPage",
+ "EndPage", 0, 0, "Comment",
+ "OpenDataSource", "CloseDataSource", 0, 0,
+ 0, 0, 0, "BeginFontHeader",
+/*5x*/
+ "ReadFontHeader", "EndFontHeader", "BeginChar", "ReadChar",
+ "EndChar", "RemoveFont", 0, 0,
+ 0, 0, 0, "BeginStream",
+ "ReadStream", "EndStream", "ExecStream", 0,
+/*6x*/
+ "PopGS", "PushGS", "SetClipReplace", "SetBrushSource",
+ "SetCharAngle", "SetCharScale", "SetCharShear", "SetClipIntersect",
+ "SetClipRectangle", "SetClipToPage", "SetColorSpace", "SetCursor",
+ "SetCursorRel", "SetHalftoneMethod", "SetFillMode", "SetFont",
+/*7x*/
+ "SetLineDash", "SetLineCap", "SetLineJoin", "SetMiterLimit",
+ "SetPageDefaultCTM", "SetPageOrigin", "SetPageRotation", "SetPageScale",
+ "SetPaintTxMode", "SetPenSource", "SetPenWidth", "SetROP",
+ "SetSourceTxMode", "SetCharBoldValue", 0, "SetClipMode",
+/*8x*/
+ "SetPathToClip", "SetCharSubMode", 0, 0,
+ "CloseSubPath", "NewPath", "PaintPath", 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+/*9x*/
+ 0, "ArcPath", 0, "BezierPath",
+ 0, "BezierRelPath", "Chord", "ChordPath",
+ "Ellipse", "EllipsePath", 0, "LinePath",
+ 0, "LineRelPath", "Pie", "PiePath",
+/*ax*/
+ "Rectangle", "RectanglePath", "RoundRectangle", "RoundRectanglePath",
+ 0, 0, 0, 0,
+ "Text", "TextPath", 0, 0,
+ 0, 0, 0, 0,
+/*bx*/
+ "BeginImage", "ReadImage", "EndImage", "BeginRastPattern",
+ "ReadRastPattern", "EndRastPattern", "BeginScan", 0,
+ "EndScan", "ScanLineRel", 0, 0,
+ 0, 0, 0, 0
+};
+
+/* ---------------- Operator definitions ---------------- */
+
+#define odef(proc, args)\
+ extern px_operator_proc(proc);\
+ extern const byte /*px_attribute*/ args[]
+
+/* Define the implementation of undefined operators. */
+private const byte apxBadOperator[] = {0, 0};
+private int
+pxBadOperator(px_args_t *par, px_state_t *pxs)
+{ return_error(errorIllegalTag);
+}
+
+odef(pxBeginSession, apxBeginSession);
+odef(pxEndSession, apxEndSession);
+odef(pxBeginPage, apxBeginPage);
+odef(pxEndPage, apxEndPage);
+odef(pxComment, apxComment);
+odef(pxOpenDataSource, apxOpenDataSource);
+odef(pxCloseDataSource, apxCloseDataSource);
+odef(pxBeginFontHeader, apxBeginFontHeader);
+odef(pxReadFontHeader, apxReadFontHeader);
+odef(pxEndFontHeader, apxEndFontHeader);
+odef(pxBeginChar, apxBeginChar);
+odef(pxReadChar, apxReadChar);
+odef(pxEndChar, apxEndChar);
+odef(pxRemoveFont, apxRemoveFont);
+odef(pxBeginStream, apxBeginStream);
+odef(pxReadStream, apxReadStream);
+odef(pxEndStream, apxEndStream);
+odef(pxExecStream, apxExecStream);
+odef(pxPopGS, apxPopGS);
+odef(pxPushGS, apxPushGS);
+odef(pxSetClipReplace, apxSetClipReplace);
+odef(pxSetBrushSource, apxSetBrushSource);
+odef(pxSetCharAngle, apxSetCharAngle);
+odef(pxSetCharScale, apxSetCharScale);
+odef(pxSetCharShear, apxSetCharShear);
+odef(pxSetClipIntersect, apxSetClipIntersect);
+odef(pxSetClipRectangle, apxSetClipRectangle);
+odef(pxSetClipToPage, apxSetClipToPage);
+odef(pxSetColorSpace, apxSetColorSpace);
+odef(pxSetCursor, apxSetCursor);
+odef(pxSetCursorRel, apxSetCursorRel);
+odef(pxSetHalftoneMethod, apxSetHalftoneMethod);
+odef(pxSetFillMode, apxSetFillMode);
+odef(pxSetFont, apxSetFont);
+odef(pxSetLineDash, apxSetLineDash);
+odef(pxSetLineCap, apxSetLineCap);
+odef(pxSetLineJoin, apxSetLineJoin);
+odef(pxSetMiterLimit, apxSetMiterLimit);
+odef(pxSetPageDefaultCTM, apxSetPageDefaultCTM);
+odef(pxSetPageOrigin, apxSetPageOrigin);
+odef(pxSetPageRotation, apxSetPageRotation);
+odef(pxSetPageScale, apxSetPageScale);
+odef(pxSetPaintTxMode, apxSetPaintTxMode);
+odef(pxSetPenSource, apxSetPenSource);
+odef(pxSetPenWidth, apxSetPenWidth);
+odef(pxSetROP, apxSetROP);
+odef(pxSetSourceTxMode, apxSetSourceTxMode);
+odef(pxSetCharBoldValue, apxSetCharBoldValue);
+odef(pxSetClipMode, apxSetClipMode);
+odef(pxSetPathToClip, apxSetPathToClip);
+odef(pxSetCharSubMode, apxSetCharSubMode);
+odef(pxCloseSubPath, apxCloseSubPath);
+odef(pxNewPath, apxNewPath);
+odef(pxPaintPath, apxPaintPath);
+odef(pxArcPath, apxArcPath);
+odef(pxBezierPath, apxBezierPath);
+odef(pxBezierRelPath, apxBezierRelPath);
+odef(pxChord, apxChord);
+odef(pxChordPath, apxChordPath);
+odef(pxEllipse, apxEllipse);
+odef(pxEllipsePath, apxEllipsePath);
+odef(pxLinePath, apxLinePath);
+odef(pxLineRelPath, apxLineRelPath);
+odef(pxPie, apxPie);
+odef(pxPiePath, apxPiePath);
+odef(pxRectangle, apxRectangle);
+odef(pxRectanglePath, apxRectanglePath);
+odef(pxRoundRectangle, apxRoundRectangle);
+odef(pxRoundRectanglePath, apxRoundRectanglePath);
+odef(pxText, apxText);
+odef(pxTextPath, apxTextPath);
+odef(pxBeginImage, apxBeginImage);
+odef(pxReadImage, apxReadImage);
+odef(pxEndImage, apxEndImage);
+odef(pxBeginRastPattern, apxBeginRastPattern);
+odef(pxReadRastPattern, apxReadRastPattern);
+odef(pxEndRastPattern, apxEndRastPattern);
+odef(pxBeginScan, apxBeginScan);
+odef(pxEndScan, apxEndScan);
+odef(pxScanLineRel, apxScanLineRel);
+
+#define none {pxBadOperator, apxBadOperator}
+
+const px_operator_definition_t px_operator_definitions[] = {
+/*4x*/
+ none,
+ {pxBeginSession, apxBeginSession},
+ {pxEndSession, apxEndSession},
+ {pxBeginPage, apxBeginPage},
+ {pxEndPage, apxEndPage},
+ none,
+ none,
+ {pxComment, apxComment},
+ {pxOpenDataSource, apxOpenDataSource},
+ {pxCloseDataSource, apxCloseDataSource},
+ none,
+ none,
+ none,
+ none,
+ none,
+ {pxBeginFontHeader, apxBeginFontHeader},
+/*5x*/
+ {pxReadFontHeader, apxReadFontHeader},
+ {pxEndFontHeader, apxEndFontHeader},
+ {pxBeginChar, apxBeginChar},
+ {pxReadChar, apxReadChar},
+ {pxEndChar, apxEndChar},
+ {pxRemoveFont, apxRemoveFont},
+ none,
+ none,
+ none,
+ none,
+ none,
+ {pxBeginStream, apxBeginStream},
+ {pxReadStream, apxReadStream},
+ {pxEndStream, apxEndStream},
+ {pxExecStream, apxExecStream},
+ none,
+/*6x*/
+ {pxPopGS, apxPopGS},
+ {pxPushGS, apxPushGS},
+ {pxSetClipReplace, apxSetClipReplace},
+ {pxSetBrushSource, apxSetBrushSource},
+ {pxSetCharAngle, apxSetCharAngle},
+ {pxSetCharScale, apxSetCharScale},
+ {pxSetCharShear, apxSetCharShear},
+ {pxSetClipIntersect, apxSetClipIntersect},
+ {pxSetClipRectangle, apxSetClipRectangle},
+ {pxSetClipToPage, apxSetClipToPage},
+ {pxSetColorSpace, apxSetColorSpace},
+ {pxSetCursor, apxSetCursor},
+ {pxSetCursorRel, apxSetCursorRel},
+ {pxSetHalftoneMethod, apxSetHalftoneMethod},
+ {pxSetFillMode, apxSetFillMode},
+ {pxSetFont, apxSetFont},
+/*7x*/
+ {pxSetLineDash, apxSetLineDash},
+ {pxSetLineCap, apxSetLineCap},
+ {pxSetLineJoin, apxSetLineJoin},
+ {pxSetMiterLimit, apxSetMiterLimit},
+ {pxSetPageDefaultCTM, apxSetPageDefaultCTM},
+ {pxSetPageOrigin, apxSetPageOrigin},
+ {pxSetPageRotation, apxSetPageRotation},
+ {pxSetPageScale, apxSetPageScale},
+ {pxSetPaintTxMode, apxSetPaintTxMode},
+ {pxSetPenSource, apxSetPenSource},
+ {pxSetPenWidth, apxSetPenWidth},
+ {pxSetROP, apxSetROP},
+ {pxSetSourceTxMode, apxSetSourceTxMode},
+ {pxSetCharBoldValue, apxSetCharBoldValue},
+ none,
+ {pxSetClipMode, apxSetClipMode},
+/*8x*/
+ {pxSetPathToClip, apxSetPathToClip},
+ {pxSetCharSubMode, apxSetCharSubMode},
+ none,
+ none,
+ {pxCloseSubPath, apxCloseSubPath},
+ {pxNewPath, apxNewPath},
+ {pxPaintPath, apxPaintPath},
+ none,
+ none,
+ none,
+ none,
+ none,
+ none,
+ none,
+ none,
+ none,
+/*9x*/
+ none,
+ {pxArcPath, apxArcPath},
+ none,
+ {pxBezierPath, apxBezierPath},
+ none,
+ {pxBezierRelPath, apxBezierRelPath},
+ {pxChord, apxChord},
+ {pxChordPath, apxChordPath},
+ {pxEllipse, apxEllipse},
+ {pxEllipsePath, apxEllipsePath},
+ none,
+ {pxLinePath, apxLinePath},
+ none,
+ {pxLineRelPath, apxLineRelPath},
+ {pxPie, apxPie},
+ {pxPiePath, apxPiePath},
+/*ax*/
+ {pxRectangle, apxRectangle},
+ {pxRectanglePath, apxRectanglePath},
+ {pxRoundRectangle, apxRoundRectangle},
+ {pxRoundRectanglePath, apxRoundRectanglePath},
+ none,
+ none,
+ none,
+ none,
+ {pxText, apxText},
+ {pxTextPath, apxTextPath},
+ none,
+ none,
+ none,
+ none,
+ none,
+ none,
+/*bx*/
+ {pxBeginImage, apxBeginImage},
+ {pxReadImage, apxReadImage},
+ {pxEndImage, apxEndImage},
+ {pxBeginRastPattern, apxBeginRastPattern},
+ {pxReadRastPattern, apxReadRastPattern},
+ {pxEndRastPattern, apxEndRastPattern},
+ {pxBeginScan, apxBeginScan},
+ none,
+ {pxEndScan, apxEndScan},
+ {pxScanLineRel, apxScanLineRel},
+ none,
+ none,
+ none,
+ none,
+ none,
+ none
+};
diff --git a/pxl/pxptable.h b/pxl/pxptable.h
new file mode 100644
index 000000000..39316f51d
--- /dev/null
+++ b/pxl/pxptable.h
@@ -0,0 +1,54 @@
+/* Copyright (C) 1996 Aladdin Enterprises. All rights reserved.
+ Unauthorized use, copying, and/or distribution prohibited.
+ */
+
+/* pxptable.h */
+/* Definitions for PCL XL parser tables */
+/* Requires pxenum.h, pxoper.h, pxvalue.h */
+
+#ifndef pxptable_INCLUDED
+# define pxptable_INCLUDED
+
+/*
+ * Define the table for checking attribute values.
+ * The 'and' of the mask and the actual data type must be non-zero.
+ * If the data type is ubyte, the value must be less than or equal to
+ * the limit value.
+ * If the procedure is not null, it provides an extra check, returning
+ * 0 or an error code.
+ */
+#define value_check_proc(proc)\
+ int proc(P1(const px_value_t *))
+typedef struct px_attr_value_type_s {
+ ushort mask;
+ ushort limit;
+ value_check_proc((*proc));
+} px_attr_value_type_t;
+
+extern const px_attr_value_type_t px_attr_value_types[];
+
+/*
+ * Define the table for checking and dispatching operators.
+ * Each operator references a string of attributes: first a list of
+ * required attributes, then 0, then a list of optional attributes,
+ * then another 0.
+ */
+typedef struct px_operator_definition_s {
+ px_operator_proc((*proc));
+ const byte /*px_attribute*/ *attrs;
+} px_operator_definition_t;
+
+extern const px_operator_definition_t px_operator_definitions[];
+
+/* Define tag and attribute names for debugging. */
+#ifdef DEBUG
+extern const char *px_tag_0_names[0x40]; /* tags 0-3f */
+extern const char *px_tag_c0_names[0x40]; /* tags c0-ff */
+extern const char *px_attribute_names[];
+#endif
+
+/* Define the table of operator names. */
+/* This is needed even when not debugging, for producing error reports. */
+extern const char *px_operator_names[0x80]; /* tags 40-bf */
+
+#endif /* pxptable_INCLUDED */
diff --git a/pxl/pxsessio.c b/pxl/pxsessio.c
new file mode 100644
index 000000000..3a495a549
--- /dev/null
+++ b/pxl/pxsessio.c
@@ -0,0 +1,485 @@
+/* Copyright (C) 1996, 1997 Aladdin Enterprises. All rights reserved.
+ Unauthorized use, copying, and/or distribution prohibited.
+ */
+
+/* pxsessio.c */
+/* PCL XL session operators */
+
+#include "math_.h" /* for fabs */
+#include "stdio_.h"
+#include "pxoper.h"
+#include "pxstate.h"
+#include "pxfont.h" /* for px_free_font */
+#include "gschar.h"
+#include "gscoord.h"
+#include "gserrors.h" /* for gs_error_undefined */
+#include "gspaint.h"
+#include "gsparam.h"
+#include "gsstate.h"
+#include "gxfixed.h"
+#include "gxfcache.h"
+
+/* Imported operators */
+px_operator_proc(pxCloseDataSource);
+px_operator_proc(pxNewPath);
+px_operator_proc(pxPopGS);
+px_operator_proc(pxPushGS);
+px_operator_proc(pxSetHalftoneMethod);
+px_operator_proc(pxSetPageDefaultCTM);
+
+/*
+ * Define the known paper sizes and unprintable margins. For convenience,
+ * we define this in terms of 300 dpi pixels, in portrait orientation. This
+ * table should obviously be device-dependent.
+ */
+#define media_size_scale (72.0 / 300.0)
+typedef struct px_media_s {
+ pxeMediaSize_t ms_enum;
+ short width, height;
+ short m_left, m_top, m_right, m_bottom;
+} px_media_t;
+#define m_default 50, 50, 50, 50
+#define m_data(ms_enum, res, width, height)\
+ {ms_enum, width * 300 / (res), height * 300 / (res), m_default},
+private const px_media_t known_media[] = {
+ px_enumerate_media(m_data)
+ /* The list ends with a comma, so add a dummy entry */
+ /* that can't be matched because its key is a duplicate. */
+ {eLetterPaper}
+};
+#undef m_data
+private const px_media_t default_media =
+ {eLetterPaper, 2550, 3300, m_default};
+#undef m_default
+
+/* Define the mapping from the Measure enumeration to points. */
+private const double measure_to_points[] = pxeMeasure_to_points;
+
+/* ---------------- Internal procedures ---------------- */
+
+/* Finish putting one device parameter. */
+private int
+px_put1(gx_device *dev, gs_c_param_list *plist, int ecode)
+{ int code = ecode;
+ if ( code >= 0 )
+ { gs_c_param_list_read(plist);
+ code = gs_putdeviceparams(dev, (gs_param_list *)plist);
+ }
+ gs_c_param_list_release(plist);
+ return (code == 0 || code == gs_error_undefined ? ecode : code);
+}
+
+/* Adjust one scale factor to an integral value if we can. */
+private double
+px_adjust_scale(double value, double extent)
+{ /* If we can make the value an integer with a total error */
+ /* of less than 1/2 pixel over the entire page, we do it. */
+ double int_value = floor(value + 0.5);
+
+ return (fabs((int_value - value) * extent) < 0.5 ? int_value : value);
+}
+
+/* Clean up at the end of a page, but before rendering. */
+private void
+px_end_page_cleanup(px_state_t *pxs)
+{ px_dict_release(&pxs->page_pattern_dict);
+ /* Clean up stray gstate information. */
+ while ( pxs->pxgs->stack_depth > 0 )
+ pxPopGS(NULL, pxs);
+ /* Pop an extra time to mirror the push in BeginPage. */
+ pxs->pxgs->stack_depth++;
+ pxPopGS(NULL, pxs);
+ pxNewPath(NULL, pxs);
+ px_purge_pattern_cache(pxs, ePagePattern);
+}
+
+/* Purge characters in non-built-in fonts. */
+private bool
+match_temporary_glyph(cached_char *cc, void *vpxs)
+{ const cached_fm_pair *pair = cc->pair;
+ px_state_t *pxs = vpxs;
+
+ return (uid_is_UniqueID(&pair->UID) &&
+ pair->UID.id > pxs->known_fonts_base_id + px_num_known_fonts);
+}
+
+/* Clean up at the end of a session. */
+private void
+px_end_session_cleanup(px_state_t *pxs)
+{ if ( pxs->data_source_open )
+ pxCloseDataSource(NULL, pxs);
+ gx_purge_selected_cached_chars(pxs->font_dir, match_temporary_glyph,
+ pxs);
+ px_dict_release(&pxs->font_dict);
+ px_dict_release(&pxs->session_pattern_dict);
+ px_purge_pattern_cache(pxs, eSessionPattern);
+ /* We believe that streams do *not* persist across sessions.... */
+ px_dict_release(&pxs->stream_dict);
+}
+
+/* ---------------- Non-operator procedures ---------------- */
+
+/* Clean up after an error or UEL. */
+void
+px_state_cleanup(px_state_t *pxs)
+{ px_end_page_cleanup(pxs);
+ px_end_session_cleanup(pxs);
+ pxs->have_page = false;
+}
+
+/* ---------------- Operators ---------------- */
+
+const byte apxBeginSession[] = {
+ pxaMeasure, pxaUnitsPerMeasure, 0,
+ pxaErrorReport, 0
+};
+int
+pxBeginSession(px_args_t *par, px_state_t *pxs)
+{ pxs->measure = par->pv[0]->value.i;
+ pxs->units_per_measure.x = real_value(par->pv[1], 0);
+ pxs->units_per_measure.y = real_value(par->pv[1], 1);
+ pxs->error_report = (par->pv[2] ? par->pv[2]->value.i : eNoReporting);
+ px_dict_init(&pxs->session_pattern_dict, pxs->memory, px_free_pattern);
+ /* Set media parameters to device defaults, in case BeginPage */
+ /* doesn't specify valid ones. */
+ /* This is obviously device-dependent. */
+ pxs->media_source = eDefaultSource;
+ pxs->media_size.x = default_media.width * media_size_scale;
+ pxs->media_size.y = default_media.height * media_size_scale;
+ pxs->duplex = false;
+ pxs->media_destination = eDefaultDestination;
+ pxs->media_type = eDefaultType;
+ px_dict_init(&pxs->font_dict, pxs->memory, px_free_font);
+ return 0;
+}
+
+const byte apxEndSession[] = {0, 0};
+int
+pxEndSession(px_args_t *par, px_state_t *pxs)
+{ px_end_session_cleanup(pxs);
+ if ( pxs->warning_length )
+ return_error(errorWarningsReported);
+ return 0;
+}
+
+/**** NOTE: MediaDestination and MediaType are not documented by H-P. ****/
+/* We are guessing that they are enumerations, like MediaSize/Source. */
+const byte apxBeginPage[] = {
+ pxaOrientation, 0,
+ pxaMediaSource, pxaMediaSize, pxaCustomMediaSize, pxaCustomMediaSizeUnits,
+ pxaSimplexPageMode, pxaDuplexPageMode, pxaDuplexPageSide,
+ pxaMediaDestination, pxaMediaType, 0
+};
+int
+pxBeginPage(px_args_t *par, px_state_t *pxs)
+{ gs_state *pgs = pxs->pgs;
+ gx_device *dev = gs_currentdevice(pgs);
+ const px_media_t *pm;
+ gs_point page_size_pixels;
+
+ /* Check parameter presence for legal combinations. */
+ if ( par->pv[2] )
+ { if ( par->pv[3] || par->pv[4] )
+ return_error(errorIllegalAttributeCombination);
+ }
+ else if ( par->pv[3] && par->pv[4] )
+ { if ( par->pv[2] )
+ return_error(errorIllegalAttributeCombination);
+ }
+ else
+ return_error(errorMissingAttribute);
+ if ( par->pv[5] )
+ { if ( par->pv[6] || par->pv[7] )
+ return_error(errorIllegalAttributeCombination);
+ }
+ else if ( par->pv[6] )
+ { if ( par->pv[5] )
+ return_error(errorIllegalAttributeCombination);
+ }
+
+ /* Copy parameters to the PCL XL state. */
+ { /* For some reason, invalid Orientations only produce a warning. */
+ integer orientation = par->pv[0]->value.i;
+ if ( orientation < 0 || orientation >= pxeOrientation_next )
+ { px_record_warning("IllegalOrientation", true, pxs);
+ orientation = ePortraitOrientation;
+ }
+ pxs->orientation = (pxeOrientation_t)orientation;
+ }
+ if ( par->pv[1] )
+ pxs->media_source = par->pv[1]->value.i;
+ if ( par->pv[2] )
+ { pxeMediaSize_t ms_enum = par->pv[2]->value.i;
+ int i;
+
+ for ( pm = known_media, i = 0; i < countof(known_media);
+ ++pm, ++i
+ )
+ if ( pm->ms_enum == ms_enum )
+ break;
+ if ( i == countof(known_media) )
+ { /* No match, select default media. */
+ pm = &default_media;
+ px_record_warning("IllegalMediaSize", false, pxs);
+ }
+ pxs->media_size.x = pm->width * media_size_scale;
+ pxs->media_size.y = pm->height * media_size_scale;
+ }
+ else
+ { double scale = measure_to_points[par->pv[4]->value.i];
+ pxs->media_size.x = real_value(par->pv[3], 0) * scale;
+ pxs->media_size.y = real_value(par->pv[3], 1) * scale;
+ /*
+ * Assume the unprintable margins for custom media are the same
+ * as for the default media. This may not be right.
+ */
+ pm = &default_media;
+ }
+ if ( par->pv[5] )
+ { pxs->duplex = false;
+ }
+ else if ( par->pv[6] )
+ { pxs->duplex = true;
+ pxs->duplex_page_mode = par->pv[6]->value.i;
+ if ( par->pv[7] )
+ pxs->duplex_back_side = (par->pv[7]->value.i == eBackMediaSide);
+ }
+ if ( par->pv[8] )
+ pxs->media_destination = par->pv[8]->value.i;
+ if ( par->pv[9] )
+ pxs->media_type = par->pv[9]->value.i;
+
+ /* Pass the media parameters to the device. */
+ { gs_memory_t *mem = pxs->memory;
+ gs_c_param_list list;
+#define plist ((gs_param_list *)&list)
+ gs_param_float_array fa;
+ float fv[4];
+ int iv;
+ bool bv;
+ int ecode = 0;
+ int code;
+
+ fa.data = fv;
+ fa.persistent = false;
+
+ gs_c_param_list_write(&list, mem);
+ iv = pxs->orientation; /* might not be an int */
+ code = param_write_int(plist, "Orientation", &iv);
+ ecode = px_put1(dev, &list, ecode);
+
+ gs_c_param_list_write(&list, mem);
+ fv[0] = pxs->media_size.x;
+ fv[1] = pxs->media_size.y;
+ fa.size = 2;
+ code = param_write_float_array(plist, ".MediaSize", &fa);
+ ecode = px_put1(dev, &list, ecode);
+
+ gs_c_param_list_write(&list, mem);
+ fv[0] = pm->m_left * media_size_scale;
+ fv[1] = pm->m_top * media_size_scale;
+ fv[2] = pm->m_right * media_size_scale;
+ fv[3] = pm->m_bottom * media_size_scale;
+ fa.size = 4;
+ code = param_write_float_array(plist, ".HWMargins", &fa);
+ ecode = px_put1(dev, &list, ecode);
+
+ /* Set the mis-named "Margins" (actually the offset on the page) */
+ /* to zero. */
+ gs_c_param_list_write(&list, mem);
+ fv[0] = 0;
+ fv[1] = 0;
+ fa.size = 2;
+ code = param_write_float_array(plist, "Margins", &fa);
+ ecode = px_put1(dev, &list, ecode);
+
+ iv = pxs->media_source; /* might not be an int */
+ if ( iv < 0 || iv >= pxeMediaSource_next )
+ px_record_warning("IllegalMediaSource", false, pxs);
+ else
+ { gs_c_param_list_write(&list, mem);
+ code = param_write_int(plist, ".MediaSource", &iv);
+ ecode = px_put1(dev, &list, ecode);
+ }
+
+ gs_c_param_list_write(&list, mem);
+ code = param_write_bool(plist, "Duplex", &pxs->duplex);
+ ecode = px_put1(dev, &list, ecode);
+
+ gs_c_param_list_write(&list, mem);
+ bv = pxs->duplex_page_mode == eDuplexHorizontalBinding;
+ code = param_write_bool(plist, "Tumble", &bv);
+ ecode = px_put1(dev, &list, ecode);
+
+ gs_c_param_list_write(&list, mem);
+ bv = !pxs->duplex_back_side;
+ code = param_write_bool(plist, "FirstSide", &bv);
+ ecode = px_put1(dev, &list, ecode);
+
+ gs_c_param_list_write(&list, mem);
+ iv = pxs->media_destination; /* might not be an int */
+ code = param_write_int(plist, ".MediaDestination", &iv);
+ ecode = px_put1(dev, &list, ecode);
+
+ gs_c_param_list_write(&list, mem);
+ iv = pxs->media_type; /* might not be an int */
+ code = param_write_int(plist, ".MediaType", &iv);
+ ecode = px_put1(dev, &list, ecode);
+
+ /*
+ * We aren't sure what to do if the device rejects the parameter
+ * value....
+ */
+ switch ( ecode )
+ {
+ case 1:
+ code = gs_setdevice(pgs, dev);
+ if ( code < 0 )
+ return code;
+ case 0:
+ break;
+ default:
+ return_error(errorIllegalAttributeValue);
+ }
+#undef plist
+ }
+ { int code;
+
+ px_initgraphics(pxs);
+ gs_dtransform(pgs, pxs->media_size.x, pxs->media_size.y,
+ &page_size_pixels);
+ { /*
+ * Put the origin at the upper left corner of the page;
+ * also account for the orientation.
+ */
+ gs_matrix orient;
+
+ orient.xx = orient.xy = orient.yx = orient.yy =
+ orient.tx = orient.ty = 0;
+ switch ( pxs->orientation )
+ {
+ case ePortraitOrientation:
+ code = gs_translate(pgs, 0.0, pxs->media_size.y);
+ orient.xx = 1, orient.yy = -1;
+ break;
+ case eLandscapeOrientation:
+ code = 0;
+ orient.xy = 1, orient.yx = 1;
+ break;
+ case eReversePortrait:
+ code = gs_translate(pgs, pxs->media_size.x, 0);
+ orient.xx = -1, orient.yy = 1;
+ break;
+ case eReverseLandscape:
+ code = gs_translate(pgs, pxs->media_size.x, pxs->media_size.y);
+ orient.xy = -1, orient.yx = -1;
+ break;
+ default: /* can't happen */
+ return_error(errorIllegalAttributeValue);
+ }
+ if ( code < 0 ||
+ (code = gs_concat(pgs, &orient)) < 0
+ )
+ return code;
+ }
+ { /* Scale according to session parameters. */
+ /* If we can make the scale integral safely, we do. */
+ double scale = measure_to_points[pxs->measure];
+ gs_matrix mat;
+
+ if ( (code = gs_scale(pgs, scale / pxs->units_per_measure.x,
+ scale / pxs->units_per_measure.y)) < 0
+ )
+ return code;
+ gs_currentmatrix(pgs, &mat);
+ mat.xx = px_adjust_scale(mat.xx, page_size_pixels.x);
+ mat.xy = px_adjust_scale(mat.xy, page_size_pixels.y);
+ mat.yx = px_adjust_scale(mat.yx, page_size_pixels.x);
+ mat.yy = px_adjust_scale(mat.yy, page_size_pixels.y);
+ gs_setmatrix(pgs, &mat);
+ pxs->initial_matrix = mat;
+ }
+ }
+ { /*
+ * Set the default halftone method. We have to do this here,
+ * rather than earlier, so that the origin is set correctly.
+ */
+ px_args_t args;
+ px_value_t device_matrix;
+
+ args.pv[0] = 0; /* DitherOrigin */
+ args.pv[1] = &device_matrix; /* DeviceMatrix */
+ device_matrix.type = pxd_scalar | pxd_ubyte;
+ device_matrix.value.i = eDeviceBest;
+ args.pv[2] = 0; /* DitherMatrixDataType */
+ args.pv[3] = 0; /* DitherMatrixSize */
+ args.pv[4] = 0; /* DitherMatrixDepth */
+ pxSetHalftoneMethod(&args, pxs);
+ }
+ /* Initialize other parts of the PCL XL state. */
+ px_dict_init(&pxs->page_pattern_dict, pxs->memory, px_free_pattern);
+ gs_erasepage(pgs);
+ pxs->have_page = false;
+ /* Make sure there is a legitimate halftone installed. */
+ { int code = px_set_halftone(pxs);
+ if ( code < 0 )
+ return code;
+ }
+ /*
+ * Do a gsave so we can be sure to get rid of all page-related
+ * state at the end of the page, but make sure PopGS doesn't pop
+ * this state from the stack.
+ */
+ { int code = pxPushGS(NULL, pxs);
+ if ( code < 0 )
+ return code;
+ pxs->pxgs->stack_depth--;
+ return code;
+ }
+}
+
+const byte apxEndPage[] = {
+ 0,
+ pxaPageCopies, 0
+};
+int
+pxEndPage(px_args_t *par, px_state_t *pxs)
+{ px_end_page_cleanup(pxs);
+ (*pxs->end_page)(pxs, (par->pv[0] ? par->pv[0]->value.i : 1), 1);
+ pxs->have_page = false;
+ return 0;
+}
+/* The default end-page procedure just calls the device procedure. */
+int
+px_default_end_page(px_state_t *pxs, int num_copies, int flush)
+{ return gs_output_page(pxs->pgs, num_copies, flush);
+}
+
+const byte apxComment[] = {
+ 0,
+ pxaCommentData, 0
+};
+int
+pxComment(px_args_t *par, px_state_t *pxs)
+{ return 0;
+}
+
+const byte apxOpenDataSource[] = {
+ pxaSourceType, pxaDataOrg, 0, 0
+};
+int
+pxOpenDataSource(px_args_t *par, px_state_t *pxs)
+{ if ( pxs->data_source_open )
+ return_error(errorDataSourceNotClosed);
+ pxs->data_source_open = true;
+ pxs->data_source_big_endian =
+ par->pv[1]->value.i == eBinaryHighByteFirst;
+ return 0;
+}
+
+const byte apxCloseDataSource[] = {0, 0};
+int
+pxCloseDataSource(px_args_t *par, px_state_t *pxs)
+{ pxs->data_source_open = false;
+ return 0;
+}
diff --git a/pxl/pxspec.txt b/pxl/pxspec.txt
new file mode 100644
index 000000000..c14d7635e
--- /dev/null
+++ b/pxl/pxspec.txt
@@ -0,0 +1,563 @@
+
+ Copyright (C) 1996 Aladdin Enterprises. All rights reserved.
+ Unauthorized use, copying, and/or distribution prohibited.
+
+This document presents the results of Aladdin's investigation of the PCL XL
+Feature Reference Protocol Class 1.1 specification and of its relationship
+to the implementation in the H-P LaserJet 6MP printer.
+
+ Report on PCL XL and LaserJet 6MP
+
+Introduction
+============
+
+In order to implement a fully test-suite-compliant PCL XL interpreter, we
+had to compare the output of our own code with the output printed by an H-P
+printer for the same input data. These comparisons uncovered a number of
+disagreements between the specification and the implementation in the
+printer we used (the LJ 6MP), as well as omission of many details necessary
+to create compatible implementations. In some cases, it seemed likely to us
+that the published specification did not properly describe the intended
+behavior; in other cases, it seemed likely that the observed behavior was
+the result of a printer firmware bug. Of course, only H-P can answer
+authoritatively the question of which alternative is correct.
+
+In presenting test cases below, we have used an imagined source syntax for
+PCL XL. This code is meant to be executed in an environment where one user
+space unit is 1/300" and where all elements of the graphics state have their
+default values.
+
+This document only addresses a very few of the many typographical and
+grammatical errors and internal inconsistencies in the published PCL XL
+specification. In most cases, we found the intention clear.
+
+Changes in a given revision of this document are marked with the revision
+number in [brackets]. Revision history:
+ first issued December 6, 1996
+ rev. [1] December 12, 1996
+ rev. [2] December 13, 1996
+ rev. [3] December 19, 1996
+ rev. [4] December 31, 1996
+ rev. [5] January 7, 1997
+ rev. [6] January 17, 1997
+ rev. [7] February 6, 1997
+
+Miscellaneous
+=============
+
+[7] Embedded data streams
+-------------------------
+
+H-P printers require that if a command reads data from the data source, then
+all the data required by the command must follow the command in a *single*
+data block; it cannot be divided up into multiple blocks whose total length
+is the amount of data required.
+
+Clipping
+--------
+
+The overall discussion of clipping mode and clipping region is inconsistent
+in many places both with itself and with the implementation. Based on
+experiments with the printer, we believe the following is the intended
+behavior:
+
+ - The ClipMode in the graphics state only controls which rule is
+used to determine the inside of the region defined by the *newly presented
+path*. The inside of that region is then intersected with the existing
+clipping region (which is simply an abstract region of the plane,
+independent of being defined by a particular path) to define the new
+clipping region, again as an abstract region of the plane.
+
+ - The ClipRegion parameter of the SetClip operators only controls
+whether the interior or exterior of the region defined by the *newly
+presented path* should be used to intersect with the existing clipping
+region. The result of the operator is an abstract region of the plane.
+
+This interpretation is consistent with the PostScript clipping model and is
+much simpler to understand (and implement) than what the specification
+attempts to describe. Unfortunately, the sample code to illustrate these
+conclusions is too large to present here.
+
+On top of all this, there appears to be a firmware bug in the implementation
+of SetClipIntersect. See below.
+
+[5] Line joins
+--------------
+
+Unlike PostScript, which applies a line join at the ends of every line
+segment (including the segments produced by flattening curves), PCL XL [7]
+does not apply the line join within an individual arc or Bezier (including
+curves that are part of TrueType characters). This produces a "smooth"
+rather than a "bristly" effect when a null line join is selected.
+
+Session operators
+=================
+
+[3] BeginPage
+-------------
+
+Apparently illegal values for Orientation, like MediaSize and MediaSource,
+only produce warnings, not errors.
+
+Font control operators
+======================
+
+[7] RemoveFont
+--------------
+
+[2] If RemoveFont provokes multiple warnings within a single page,
+apparently only the last occurrence of each warning is remembered. Test
+case:
+
+ (Bogus1) ba @FontName RemoveFont
+ (Bogus2) ba @FontName RemoveFont
+ (Arial ) ba @FontName RemoveFont
+ (Bogus3) ba @FontName RemoveFont
+ (CG Times ) ba @FontName RemoveFont
+
+Only Bogus3 (UndefinedFontNotRemoved) and CG Times (InternalFontNotRemoved)
+will appear on the error page.
+
+Graphics state operators
+========================
+
+Miscellaneous
+-------------
+
+The specification fails to state (section 5.0) that CharBoldValue and
+CharSubMode are elements of the graphics state, and that their default
+values are 0 and eNoSubstitution respectively.
+
+SetColorSpace
+-------------
+
+The specification says that this operator sets both the pen and brush to
+paint black. In fact, [3] the operator does no such thing. Test case:
+
+ 20 us @PenWidth SetPenWidth
+ 4 4 usp @PageScale SetPageScale
+
+ 1 b @ColorSpace SetColorSpace % eGray
+ 0.5 r @GrayLevel SetPenSource
+ 0.8 r @GrayLevel SetBrushSource
+ 100 100 200 200 usq @BoundingBox Rectangle
+
+ 2 b @ColorSpace SetColorSpace % eRGB
+ 300 100 400 200 usq @BoundingBox Rectangle
+
+The two rectangles are identical. We think this is probably an error in the
+specification, since the observed behavior seems reasonable.
+
+SetBrushSource, SetPenSource
+----------------------------
+
+The statement "The paint source identified in the attribute list is
+compatible with the current color space" is misleading, since it is
+acceptable to set the brush source to a raster pattern defined in a color
+space different from the current one.
+
+[6] SetCharAngle, SetCharScale, SetCharShear
+--------------------------------------------
+
+The statement that these are "not cumulative" is somewhat misleading. What
+the H-P printers apparently do is remember only the most recent invocation
+of these commands, including the order in which they were received. Thus,
+for example, the sequences <Angle1, Scale, Angle2> and <Scale, Angle1,
+Angle2> are both equivalent to <Scale, Angle2>, but <Angle1, Angle2, Scale>
+is equivalent to <Angle2, Scale>, which is different.
+
+SetCharSubMode
+--------------
+
+Character substitution is not defined or discussed anywhere else in this
+document. However, from other reading, we are quite certain that this
+operator requires an array with exactly 1 element, and that it turns on or
+off vertical substitution using the VT segment of TrueType fonts, as for
+PCL5.
+
+SetMiterLimit
+-------------
+
+Setting a miter limit of 0 is [4] apparently equivalent to setting the miter
+limit to its default value of 10. We believe this behavior is deliberate
+and that the specification omitted this point accidentally.
+
+SetLineDash
+-----------
+
+The last sentence, stating that "the dash style of a line is not scaled when
+a line is scaled", is simply wrong. Test case:
+
+ [40 20] @LineDashStyle SetLineDash
+ [100 100] @Point SetCursor
+ [300 0] @EndPoint LineRelPath
+ PaintPath NewPath
+ [100 200] @Point SetCursor
+ [2 2] @Scale SetPageScale
+ [300 0] @EndPoint LineRelPath
+ PaintPath
+
+In the second line, the dashes and gaps are twice as long. We think the
+text in the specification may be a holdover from an earlier version of the
+design, and that the scaling behavior is the one that is actually intended,
+since, for example, it matches the behavior of PostScript's setdash
+operator.
+
+SetClip*
+--------
+
+The discussion of ClipMode and ClipRegion is wrong almost everywhere. See
+the "Graphics State" section above.
+
+SetClipIntersect
+----------------
+
+It appears that SetClipIntersect disregards the ClipRegion attribute if any
+intersection actually occurs. Test cases ("region1" and "region2" are
+parameters, eInterior or eExterior):
+
+ eEvenOdd @ClipMode SetClipMode
+ [120 120] @Point SetCursor
+ [100 0] @EndPoint LineRelPath
+ [-100 100] @EndPoint LineRelPath
+ CloseSubPath
+ region1 @ClipRegion SetClipIntersect
+ [120 120] @Point SetCursor
+ [100 0] @EndPoint LineRelPath
+ [0 100] @EndPoint LineRelPath
+ CloseSubPath
+ region2 @ClipRegion SetClipIntersect
+ [100 100 440 440] @BoundingBox Rectangle
+
+The outputs with region2 = eInterior are what one would expect (triangles
+pointing "south" and "west"), but the outputs with region2 = eExterior are
+the same (a square with a triangle cut-out pointing "northeast") whether
+region1 = eInterior or region1 = eExterior. We were unable to come up with
+an interpretation of the specification that would make this the correct
+behavior, so we think this is a firmware bug.
+
+SetROP
+------
+
+The specification, read literally, would require keeping an internal
+representation of the page in which every pixel was represented as a 24-bit
+RGB value. The implementation in the printer does no such thing: it applies
+the given RasterOp/transparency algorithm to the physical device pixels
+*after halftoning*, with white pixels resulting from halftoning being
+treated as transparent if the corresponding transparency mode is set. This
+is tremendously simpler and cheaper to implement than what is in the
+specification, but it is also produces very different results. Test case:
+
+ 1 @ColorSpace SetColorSpace
+ 15 @GrayLevel SetBrushSource
+ [100 100 200 200] @BoundingBox Rectangle
+ 86 @ROP3 SetROP % D ^ (T | S)
+ 240 @GrayLevel SetBrushSource
+ [100 100 200 200] @BoundingBox Rectangle
+
+The specification requires painting the interior of the rectangle with the
+XOR of the 15 and the 240, i.e., 255, i.e., white. In fact, this produces a
+dark gray shade resulting from XORing the two halftone masks together.
+
+The equations for transparency processing (section 5.7.5) are badly
+presented: Src and Paint in the first equation of each case (the computation
+of Temporary_ROP3) refer to the actual pixels, while Src and Paint in the
+other equations refer to masks that have 1s where the corresponding pixel is
+not white. For black-and-white printers with black = 1, the two are
+equivalent, but for color printers, they are quite different.
+
+In order to make ORing different gray shades together produce a result
+approximating the sum of the shade values rather than the maximum, H-P has
+apparently used very carefully designed default halftone screens, and
+*different* screens for the source and paint operands of RasterOp. There is
+no way to achieve this effect with user-defined dither matrices, because the
+same matrix is used for all cases. Test case:
+
+ << optionally download a dither matrix >>
+ eRGB @ColorSpace SetColorSpace
+ For values of x from 0 by 5 to 255
+ For values of y from 0 to 5 by 255
+ Let X = x * 10 + 100, Y = y * 10 + 100
+ 252 @ROP3 SetROP
+ x @GrayValue SetBrushSource
+ [X Y X+10 Y+10] @BoundingBox Rectangle
+ 238 @ROP3 SetROP
+ 0 @GrayValue SetBrushSource
+ [X Y] @Point SetCursor
+ 0 @ColorMapping 2 @ColorDepth
+ 1 @SourceWidth 1 @SourceHeight
+ [10 10] @DestinationSize
+ BeginImage
+ 0 @StartLine 1 @BlockHeight 0 @CompressMode
+ ReadImage
+ <fb 04> % stream preamble
+ <y 00 00 00> % stream data
+ EndImage
+
+The output for the downloaded matrix is very different from the output for
+the default screen; a careful examination of the output with the default
+screen will reveal that the square at (x,y) is different from the square at
+(y,x), confirming that different screens are used for source and paint.
+
+[3] SetHalftoneMethod
+---------------------
+
+[4] Setting a new halftone method does not affect the current brush or pen:
+apparently SetBrushSource and SetPenSource immediately render the color
+using the current halftone method, and PaintPath uses that rendering. To
+verify this:
+
+ << SetBrushSource with a gray level >>
+ ... SetHalftoneMethod ...
+ << construct a path >>
+ PaintPath
+
+The path will be painted with a brush that uses the old halftone method, not
+the new one.
+
+[7] The DitherOrigin is apparently relative not to the current user
+coordinate system (as documented), but to the default user coordinate system
+in the current orientation. Here is a test file:
+
+ 150 600 usp @PageOrigin SetPageOrigin
+ 0 b @DitherMatrixDataType
+ 32 32 usp @DitherMatrixSize
+ 2 b @DitherMatrixDepth
+ SetHalftoneMethod
+ << 1024 bytes of distinctive-pattern matrix omitted >>
+ 2 b @ColorSpace SetColorSpace
+ [100 100 100] ba @RGBColor SetBrushSource
+ 0 0 32 32 usq @BoundingBox Rectangle
+
+If the DitherOrigin were taken correctly, the output would consist of a
+single, unshifted copy of the halftone tile. However, the tile is shifted.
+
+[7] When using the default dither matrix, the X component of the
+DitherOrigin is ignored. For example:
+
+ eGray @ColorSpace SetColorSpace
+ 0 b @NullPen SetPenSource
+ 90 b @ROP3 SetROP % D ^ T
+ 175 b @GrayLevel SetBrushSource
+ 0 0 100 100 usq Rectangle
+ 1 0 usp @DitherOrigin
+ eDeviceBest @DeviceMatrix
+ SetHalftoneMethod
+ 175 b @GrayLevel SetBrushSource
+ 0 0 100 100 usq Rectangle
+
+The result is white: the dither matrix was not translated, and the gray
+pattern cancelled itself out. If one replaces the 6th line with
+
+ 0 1 usp @DitherOrigin
+
+the result is a gray square, showing that the matrix was translated.
+
+Painting operators
+==================
+
+[3] ArcPath
+-----------
+
+If the corners of the BoundingBox are specified with x1 > x2 or y1 > y2, the
+following peculiar changes occur in the output:
+
+ - x1 < x2, y1 < y2: the arc is drawn counter-clockwise from
+ StartPoint to EndPoint, per the specification.
+
+ - x1 < x2, y1 > y2: the arc is drawn clockwise from EndPoint to
+ StartPoint.
+
+ - x1 > x2, y1 < y2: the arc is drawn clockwise from the point
+ opposite EndPoint to the point opposite StartPoint.
+
+ - x1 > x2, y1 > y2: the arc is drawn counter-clockwise from
+ point opposite StartPoint to the point opposite EndPoint.
+
+There is probably some simple way of characterizing these changes
+mathematically, but we haven't found it. We think this is probably a
+firmware bug, but it could also be a specification error.
+
+[3] Chord
+---------
+
+The arc of the chord is drawn before the line. This is not documented; it
+matters when a dash pattern is being used.
+
+Chord behaves like ArcPath for BoundingBox values with x1 < x2 or y1 < y2.
+
+Chord probably clears the path, rather than leaving it set to the shape.
+(We didn't verify this, but it seems likely given that Ellipse and Pie do
+it.) See Rectangle below.
+
+[3] ChordPath
+-------------
+
+The arc of the chord is drawn before the line. This is not documented; it
+matters when a dash pattern is being used.
+
+ChordPath behaves like ArcPath for BoundingBox values with x1 < x2 or y1 <
+y2.
+
+[1] Ellipse
+-----------
+
+[1] The specification does not say what the starting point of an ellipse is,
+or in which direction the ellipse is drawn; this matters when a dash pattern
+is being used. [7] The starting point and drawing direction for ellipses
+are as follows:
+
+ - x1 < x2, y1 < y2: starts at 180 degree point (on the ellipse's X
+ axis at minimum X), draws counter-clockwise.
+
+ - x1 < x2, y1 > y2: starts at 180 degree point, draws clockwise.
+
+ - x1 > x2, y1 < y2: starts at 0 degree point, draws clockwise.
+
+ - x1 > x2, y1 > y2: starts at 0 degree point, draws
+ counter-clockwise.
+
+Ellipse clears the path, rather than leaving it set to the shape. See
+Rectangle below.
+
+[3] Pie
+-------
+
+The line from the center to the starting point of the arc is drawn first,
+then the arc, then the line back to the center. This is not documented; it
+matters when a dash pattern is being used.
+
+Pie behaves like ArcPath for BoundingBox values with x1 < x2 or y1 < y2.
+
+Pie clears the path, rather than leaving it set to the shape. See Rectangle
+below.
+
+[3] PiePath
+-----------
+
+PiePath draws in the same order as Pie.
+
+PiePath behaves like ArcPath for BoundingBox values with x1 < x2 or y1 < y2.
+
+Rectangle
+---------
+
+The specification says that Rectangle is equivalent to
+ NewPath RectanglePath PaintPath
+
+Since PaintPath does not reset the path, Rectangle should leave the path set
+to the rectangle; the postcondition in the specification says this
+explicitly. However, the implementation resets the path after Rectangle.
+Test case:
+
+ 25 @PenWidth SetPenWidth
+ 0 @NullBrush SetBrushSource
+ eGray @ColorSpace SetColorSpace
+ 60 @GrayLevel SetPenSource
+ 100 100 @Point SetCursor
+ 200 0 @EndPoint LineRelPath
+ PaintPath % should not reset path
+ 120 @GrayLevel SetPenSource
+ 100 150 @Point SetCursor
+ 200 0 @EndPoint LineRelPath
+ PaintPath % should repaint first line
+ 180 @GrayLevel SetPenSource
+ 100 200 200 300 @BoundingBox Rectangle % should leave path set to rectangle
+ 230 @GrayLevel SetPenSource
+ 100 400 @Point SetCursor
+ 200 0 @EndPoint LineRelPath
+ PaintPath % should repaint rectangle
+
+The last line is painted a lighter gray than the rectangle, demonstrating
+that Rectangle left the path empty. We are not sure whether this is a
+firmware bug or a change in the intended specification.
+
+We verified that Ellipse and Pie behave the same way, but we did not test
+whether this behavior extends to the other operators (Chord, RoundRectangle)
+that one might expect to behave similarly.
+
+Rectangle, RectanglePath
+------------------------
+
+[1] Even though the specification says rectangles are drawn
+counter-clockwise, they are actually drawn clockwise, starting in the upper
+left corner. Test case:
+
+ 10 us @PenWidth SetPenWidth
+ [130 10 40 10 30 10 20 10] ssa @LineDashStyle
+ 0 ss @DashOffset SetLineDash
+ 0 b @NullBrush SetBrushSource
+ 200 200 700 400 ssq @BoundingBox Rectangle
+
+The dash pattern clearly starts in the upper left corner and runs clockwise.
+
+[7] Rectangles allow specifying the bounding box points in any order; the
+rectangle is always drawn clockwise, starting with the point that has the
+lesser user space coordinates.
+
+RoundRectangle, RoundRectanglePath
+----------------------------------
+
+The BoundingBox attribute gives the bounding box for the rectangle, not the
+ellipse. This is just a typo, but a substantial one.
+
+[1] Round rectangles, unlike rectangles, *are* drawn counter-clockwise.
+starting at the top of the straight part of the left edge. Test case:
+
+ 10 us @PenWidth SetPenWidth
+ [130 10 40 10 30 10 20 10] ssa @LineDashStyle
+ 0 ss @DashOffset SetLineDash
+ 0 b @NullBrush SetBrushSource
+ 200 200 700 400 ssq @BoundingBox
+ 120 120 usp @EllipseDimension RoundRectangle
+
+[7] Round rectangles give an IllegalAttributeValue error if the bounding box
+points are not specified with x1 < x2, y1 < y2.
+
+ScanLineRel
+-----------
+
+The description of x-pairs in section 6.12 is either misleading or wrong.
+If the current X position is X and the values in the x-pair are U and V, the
+x-pair causes a line to be drawn from X+U to X+U+V, not X+U to X+V. (The
+test case is too messy to present here.)
+
+[3] Text, TextPath
+------------------
+
+[3] It appears that text transforms *objects*, whereas images and paths
+transform *coordinates*. Mathematically, this requires applying
+transformations to text in the opposite order from graphics. Test case:
+
+ (TimesNewRmn ) ba @FontName
+ 200 us @CharSize 277 us @SymbolSet SetFont
+ 2500 2500 usp @PageOrigin SetPageOrigin
+ 4 1 usp @PageScale SetPageScale
+ NewPath 0 0 usp @Point SetCursor
+
+ << repeat 2-4 times: >>
+
+ PushGS
+ (1) ba @TextData Text
+ 200 0 usp @Point SetCursor
+ 1 1.5 rp @PageScale SetPageScale
+ (2) ba @TextData TextPath PaintPath NewPath
+ 400 0 usp @PageOrigin SetPageOrigin
+ 1 1.5 rp @PageScale SetPageScale
+ 90 us @PageAngle SetPageRotation
+ 0 0 usp @Point SetCursor
+ (3) ba @TextData Text
+ 200 0 usp @Point SetCursor
+ 1 1.5 rp @PageScale SetPageScale
+ (4) ba @TextData TextPath PaintPath NewPath
+ PopGS
+ 90 us @PageAngle SetPageRotation
+
+ << end repeat >>
+
+For example, the '1' characters are all the same shape, indicating that they
+were scaled (as objects) before being rotated; if this transformation had
+been applied to the coordinates, both the portrait and the landscape
+characters would have been stretched in the page X direction. This
+interpretation of the specification is far from obvious, but it is not
+unreasonable, so we think it is what is intended rather than a bug.
diff --git a/pxl/pxstate.c b/pxl/pxstate.c
new file mode 100644
index 000000000..a189a9e1c
--- /dev/null
+++ b/pxl/pxstate.c
@@ -0,0 +1,107 @@
+/* Copyright (C) 1996, 1998 Aladdin Enterprises. All rights reserved.
+ Unauthorized use, copying, and/or distribution prohibited.
+ */
+
+/* pxstate.c */
+/* State allocation/initialization/cleanup */
+
+#include "stdio_.h" /* std.h + NULL */
+#include "gstypes.h"
+#include "gsmemory.h"
+#include "gsstruct.h"
+#include "pxstate.h"
+
+/* Import the initialization procedure table from pxmain.c. */
+typedef int (*px_init_proc)(P1(px_state_t *));
+extern const px_init_proc px_init_table[];
+
+/* GC descriptors */
+private_st_px_state();
+#define pxs ((px_state_t *)vptr)
+private ENUM_PTRS_BEGIN(px_state_enum_ptrs) {
+ index -= px_state_num_ptrs + px_state_num_string_ptrs;
+#define md(i,e)\
+ if ( index < st_px_dict_max_ptrs )\
+ return ENUM_SUPER_ELT(px_state_t, st_px_dict, e, 0);\
+ index -= st_px_dict_max_ptrs;
+ px_state_do_dicts(md)
+#undef md
+ return 0;
+ }
+#undef ENUM_DICT
+#define mp(i,e) ENUM_PTR(i, px_state_t, e);
+#define ms(i,e) ENUM_STRING_PTR(px_state_num_ptrs + i, px_state_t, e);
+ px_state_do_ptrs(mp)
+ px_state_do_string_ptrs(ms)
+#undef mp
+#undef ms
+ENUM_PTRS_END
+private RELOC_PTRS_BEGIN(px_state_reloc_ptrs) {
+#define mp(i,e) RELOC_PTR(px_state_t, e);
+#define ms(i,e) RELOC_STRING_PTR(px_state_t, e);
+#define md(i,e)\
+ RELOC_SUPER_ELT(px_state_t, st_px_dict, e);
+ px_state_do_ptrs(mp)
+ px_state_do_string_ptrs(ms)
+ px_state_do_dicts(md)
+#undef mp
+#undef ms
+#undef md
+} RELOC_PTRS_END
+#undef pxs
+
+/* Allocate a px_state_t. */
+px_state_t *
+px_state_alloc(gs_memory_t *memory)
+{ px_state_t *pxs = gs_alloc_struct(memory, px_state_t, &st_px_state,
+ "px_state_alloc");
+ px_gstate_t *pxgs = px_gstate_alloc(memory);
+
+ if ( pxs == 0 || pxgs == 0 )
+ { gs_free_object(memory, pxgs, "px_gstate_alloc");
+ gs_free_object(memory, pxs, "px_state_alloc");
+ return 0;
+ }
+ pxs->memory = memory;
+ /* Clear all pointers known to GC. */
+#define mp(i,e) pxs->e = 0;
+#define ms(i,e) pxs->e.data = 0;
+#define md(i,e) pl_dict_init(&pxs->e, pxs->memory, NULL);
+ px_state_do_ptrs(mp)
+ px_state_do_string_ptrs(ms)
+ px_state_do_dicts(md)
+#undef mp
+#undef ms
+#undef md
+ pxs->pxgs = pxgs;
+ pxgs->pxs = pxs;
+ px_state_init(pxs, NULL);
+ /* Run module initialization code. */
+ { const px_init_proc *init;
+ for ( init = px_init_table; *init; ++init )
+ (*init)(pxs);
+ }
+ return pxs;
+}
+
+/* Do one-time state initialization. */
+/* There isn't much of this: most state is initialized per-session. */
+void
+px_state_init(px_state_t *pxs, gs_state *pgs)
+{ pxs->pgs = pgs;
+ px_gstate_init(pxs->pxgs, pgs);
+ pxs->error_report = eErrorPage; /* default before first session */
+ pxs->end_page = px_default_end_page;
+ pxs->data_source_open = false;
+ px_dict_init(&pxs->stream_dict, pxs->memory, NULL);
+ pxs->warning_length = 0;
+}
+
+/* Do one-time finalization at the end of execution. */
+void
+px_state_finit(px_state_t *pxs)
+{ /* If streams persisted across sessions, we would release them here. */
+#if 0
+ px_dict_release(&pxs->stream_dict);
+#endif
+}
diff --git a/pxl/pxstate.h b/pxl/pxstate.h
new file mode 100644
index 000000000..6d1631872
--- /dev/null
+++ b/pxl/pxstate.h
@@ -0,0 +1,164 @@
+/* Copyright (C) 1996, 1997 Aladdin Enterprises. All rights reserved.
+ Unauthorized use, copying, and/or distribution prohibited.
+ */
+
+/* pxstate.h */
+/* State definitions for PCL XL interpreter */
+
+#ifndef pxstate_INCLUDED
+# define pxstate_INCLUDED
+
+#include "gsmemory.h"
+#include "pxgstate.h"
+
+/* Define an abstract type for a font directory. */
+#ifndef gs_font_dir_DEFINED
+# define gs_font_dir_DEFINED
+typedef struct gs_font_dir_s gs_font_dir;
+#endif
+
+/* Define an abstract type for a text enumerator. */
+/* We only need this for the preallocated enumerator for the error page. */
+#ifndef gs_show_enum_DEFINED
+# define gs_show_enum_DEFINED
+typedef struct gs_show_enum_s gs_show_enum;
+#endif
+
+/* Define an abstract type for an image enumerator. */
+#ifndef px_image_enum_DEFINED
+# define px_image_enum_DEFINED
+typedef struct px_image_enum_s px_image_enum_t;
+#endif
+
+/* Define an abstract type for an pattern enumerator. */
+#ifndef px_pattern_enum_DEFINED
+# define px_pattern_enum_DEFINED
+typedef struct px_pattern_enum_s px_pattern_enum_t;
+#endif
+
+/* Define the type of the PCL XL state. */
+#ifndef px_state_DEFINED
+# define px_state_DEFINED
+typedef struct px_state_s px_state_t;
+#endif
+
+/* This structure captures the entire state of the PCL XL "virtual */
+/* machine", except for graphics state parameters in the gs_state. */
+struct px_state_s {
+
+ gs_memory_t *memory;
+ /* Hook back to client data, for callback procedures */
+ void *client_data;
+ gs_id known_fonts_base_id;
+
+ /* Session state */
+
+ pxeMeasure_t measure;
+ gs_point units_per_measure;
+ pxeErrorReport_t error_report;
+ /* Pattern dictionary */
+ px_dict_t session_pattern_dict;
+
+ /* Page state */
+
+ pxeOrientation_t orientation;
+ pxeMediaSource_t media_source;
+ gs_point media_size; /* in 1/72" units */
+ bool duplex;
+ pxeDuplexPageMode_t duplex_page_mode;
+ bool duplex_back_side;
+ pxeMediaDestination_t media_destination;
+ pxeMediaType_t media_type;
+ int (*end_page)(P3(px_state_t *pxs, int num_copies, int flush));
+ /* Pattern dictionary */
+ px_dict_t page_pattern_dict;
+ /* Internal variables */
+ bool have_page; /* true if anything has been written on page */
+ /* Cached values */
+ gs_matrix initial_matrix;
+
+ /* Data source */
+
+ bool data_source_open;
+ bool data_source_big_endian;
+ /* Stream dictionary */
+ px_dict_t stream_dict;
+ /* Stream reading state */
+ gs_string stream_name;
+ struct sd2_ {
+ byte *data;
+ uint size;
+ } stream_def;
+
+ /* Font dictionary */
+ px_dict_t font_dict;
+ /* Font/character downloading state */
+ px_font_t *download_font;
+ int font_format;
+ /* Global structures */
+ gs_font_dir *font_dir;
+ px_font_t *error_page_font;
+ gs_show_enum *error_page_show_enum;
+
+ /* Graphics state */
+
+ gs_state *pgs; /* PostScript graphics state */
+ px_gstate_t *pxgs;
+ /* Image/pattern reading state */
+ px_image_enum_t *image_enum;
+ px_pattern_enum_t *pattern_enum;
+
+ /* Miscellaneous */
+ struct db_ {
+ byte *data;
+ uint size;
+ } download_bytes; /* font/character/halftone data */
+ gs_string download_string; /* ditto */
+ struct sp_ {
+ int x;
+ double y0, y1;
+ } scan_point; /* position when reading scan lines */
+
+ /* We put the warning table and error line buffer at the end */
+ /* so that the offsets of the scalars will stay small. */
+#define px_max_error_line 120
+ char error_line[px_max_error_line + 1]; /* for errors with their own msg */
+#define px_max_warning_message 500
+ uint warning_length;
+ char warnings[px_max_warning_message + 1];
+
+};
+#define private_st_px_state() /* in pxstate.c */\
+ gs_private_st_composite(st_px_state, px_state_t, "px_state",\
+ px_state_enum_ptrs, px_state_reloc_ptrs)
+/* Enumerate the various kinds of pointers in a px_state_t. */
+#define px_state_do_ptrs(m)\
+ m(0,client_data) m(1,stream_def.data)\
+ m(2,download_font) m(3,font_dir) m(4,error_page_font)\
+ m(5,error_page_show_enum) m(6,pgs) m(7,pxgs) m(8,image_enum)\
+ m(9,pattern_enum) m(10,download_bytes.data)
+#define px_state_num_ptrs 11
+#define px_state_do_string_ptrs(m)\
+ m(0,stream_name) m(1,download_string)
+#define px_state_num_string_ptrs 2
+#define px_state_do_dicts(m)\
+ m(0,session_pattern_dict) m(1,page_pattern_dict) m(2,stream_dict)\
+ m(4,font_dict)
+#define px_state_num_dicts 4
+
+/* Allocate a px_state_t. */
+px_state_t *px_state_alloc(P1(gs_memory_t *));
+
+/* Do one-time state initialization. */
+void px_state_init(P2(px_state_t *, gs_state *));
+
+/* Define the default end-of-page procedure. */
+int px_default_end_page(P3(px_state_t *, int, int));
+
+/* Clean up after an error or UEL. */
+void px_state_cleanup(P1(px_state_t *));
+
+/* Do one-time finalization. */
+void px_state_finit(P1(px_state_t *));
+
+#endif /* pxstate_INCLUDED */
diff --git a/pxl/pxstream.c b/pxl/pxstream.c
new file mode 100644
index 000000000..b3c6d4e30
--- /dev/null
+++ b/pxl/pxstream.c
@@ -0,0 +1,150 @@
+/* Copyright (C) 1996 Aladdin Enterprises. All rights reserved.
+ Unauthorized use, copying, and/or distribution prohibited.
+ */
+
+/* pxstream.c */
+/* PCL XL user-defined stream operators */
+
+#include "memory_.h"
+#include "gsmemory.h"
+#include "scommon.h"
+#include "pxoper.h"
+#include "pxstate.h"
+#include "pxparse.h"
+
+/* ---------------- Internal procedures ---------------- */
+
+/* Tag a stream name with its character width. */
+private int
+tag_stream_name(const px_value_t *psnv, gs_string *pstr,
+ gs_memory_t *mem, client_name_t cname)
+{ uint size = array_value_size(psnv);
+ byte *str = gs_alloc_string(mem, size + 1, cname);
+
+ if ( str == 0 )
+ return_error(errorInsufficientMemory);
+ str[0] = value_size(psnv);
+ memcpy(str + 1, psnv->value.array.data, size);
+ pstr->data = str;
+ pstr->size = size + 1;
+ return 0;
+}
+
+/* ---------------- Operators ---------------- */
+
+const byte apxBeginStream[] = {
+ pxaStreamName, 0, 0
+};
+int
+pxBeginStream(px_args_t *par, px_state_t *pxs)
+{ int code = tag_stream_name(par->pv[0], &pxs->stream_name, pxs->memory,
+ "pxBeginStream(name)");
+
+ if ( code < 0 )
+ return code;
+ pxs->stream_def.size = 0;
+ pl_dict_undef(&pxs->stream_dict, pxs->stream_name.data,
+ pxs->stream_name.size);
+ return 0;
+}
+
+const byte apxReadStream[] = {
+ pxaStreamDataLength, 0, 0
+};
+int
+pxReadStream(px_args_t *par, px_state_t *pxs)
+{ ulong len = par->pv[0]->value.i;
+ ulong copy = min(len - par->source.position, par->source.available);
+ uint old_size = pxs->stream_def.size;
+ byte *str;
+
+ if ( copy == 0 )
+ return pxNeedData;
+ if ( old_size == 0 )
+ str = gs_alloc_bytes(pxs->memory, copy, "pxReadStream");
+ else
+ str = gs_resize_object(pxs->memory, pxs->stream_def.data,
+ old_size + copy, "pxReadStream");
+ if ( str == 0 )
+ return_error(errorInsufficientMemory);
+ memcpy(str + old_size, par->source.data, copy);
+ pxs->stream_def.data = str;
+ pxs->stream_def.size = old_size + copy;
+ par->source.data += copy;
+ par->source.available -= copy;
+ return ((par->source.position += copy) == len ? 0 : pxNeedData);
+}
+
+const byte apxEndStream[] = {0, 0};
+int
+pxEndStream(px_args_t *par, px_state_t *pxs)
+{ int code = pl_dict_put(&pxs->stream_dict, pxs->stream_name.data,
+ pxs->stream_name.size, pxs->stream_def.data);
+
+ gs_free_string(pxs->memory, pxs->stream_name.data,
+ pxs->stream_name.size, "pxEndStream(name)");
+ return (code < 0 ? gs_note_error(errorInsufficientMemory) : 0);
+}
+
+const byte apxExecStream[] = {
+ pxaStreamName, 0, 0
+};
+int
+pxExecStream(px_args_t *par, px_state_t *pxs)
+{ gs_string str;
+ void *def;
+ const byte *def_data;
+ uint def_size;
+ bool big_endian;
+ const byte *start;
+ px_parser_state_t *pst = par->parser;
+ px_parser_state_t st;
+ stream_cursor_read r;
+ int code = tag_stream_name(par->pv[0], &str, pxs->memory,
+ "pxExecStream(name)");
+
+ if ( code < 0 )
+ return code;
+ { bool found = pl_dict_find(&pxs->stream_dict, str.data, str.size,
+ &def);
+
+ gs_free_string(pxs->memory, str.data, str.size,
+ "pxExecStream(name)");
+ if ( !found )
+ return_error(errorStreamUndefined);
+ }
+ def_data = def;
+ def_size = gs_object_size(pxs->memory, def);
+ /* We do all the syntax checking for streams here, rather than */
+ /* in ReadStream or EndStream, for simplicity. */
+ switch ( def_data[0] )
+ {
+ case '(': big_endian = true; break;
+ case ')': big_endian = false; break;
+ default: return_error(errorUnsupportedBinding);
+ }
+ if ( def_size < 16 ||
+ strncmp(def_data + 1, " HP-PCL XL", 10)
+ )
+ return_error(errorUnsupportedClassName);
+ if ( strncmp(def_data + 11, ";1;", 3) )
+ return_error(errorUnsupportedProtocol);
+ start = memchr(def_data + 14, '\n', def_size - 14);
+ if ( !start )
+ return_error(errorIllegalStreamHeader);
+ st.memory = pxs->memory;
+ px_process_init(&st, big_endian);
+ st.macro_state = pst->macro_state | ptsExecStream;
+ st.last_operator = pst->last_operator;
+ r.ptr = start;
+ r.limit = def_data + def_size - 1;
+ code = px_process(&st, pxs, &r);
+ pst->macro_state = st.macro_state & ~ptsExecStream;
+ if ( code < 0 )
+ { /* Set the operator counts for error reporting. */
+ pst->parent_operator_count = pst->operator_count;
+ pst->operator_count = st.operator_count;
+ pst->last_operator = st.last_operator;
+ }
+ return code;
+}
diff --git a/pxl/pxsymbol.ps b/pxl/pxsymbol.ps
new file mode 100644
index 000000000..e8ccae018
--- /dev/null
+++ b/pxl/pxsymbol.ps
@@ -0,0 +1,111 @@
+%!
+% Copyright (C) 1996 Aladdin Enterprises. All rights reserved.
+% Unauthorized use, copying, and/or distribution prohibited.
+
+% Construct the PCL XL symbol set mappings.
+% This generates both a .c and a .h file.
+
+% Load all known encodings.
+(../gs_???_e.ps)
+ { (r) file
+ dup token pop dup /currentglobal eq
+ { % The next 4 tokens are standard.
+ 5 { pop dup token pop } repeat
+ }
+ if
+ % stack: file token
+ /Encoding resourcestatus
+ { pop 2 ne
+ { closefile }
+ { dup 0 setfileposition run }
+ ifelse
+ }
+ { dup 0 setfileposition run
+ }
+ ifelse
+ }
+100 string filenameforall
+
+% Construct a list of all the glyphs.
+/glyphs 1000 dict def
+(*)
+ { /Encoding findresource { glyphs exch dup put } forall
+ }
+100 string /Encoding resourceforall
+glyphs /.notdef undef
+
+% Write the copyright notice and initial boilerplate.
+(/* Copyright (C) 1996 Aladdin Enterprises. All rights reserved.
+ Unauthorized use, copying, and/or distribution prohibited.
+ */
+) print
+
+/HEADER where { pop HEADER } { false } ifelse
+ { (
+/* pxsymbol.h */
+/* PCL XL symbol set table declarations */
+
+) print
+ (*)
+ { (extern const unsigned short px) print =only ([];
+) print
+ }
+ 100 string /Encoding resourceforall
+ flush quit
+ }
+if
+
+(
+/* pxsymbol.c */
+/* PCL XL symbol set mapping definitions */
+
+#include "pxsymbol.h"
+) print
+
+% Write the enumeration of the glyphs.
+% It would be too much work to really sort them, so we only sort by
+% the first character.
+(
+typedef enum {
+ pxg__notdef = 0) print
+
+65 1 122
+ { /first exch def
+ glyphs
+ { =string cvs 0 get first eq
+ { (,
+ pxg_) print =only }
+ { pop }
+ ifelse
+ }
+ forall
+ }
+for
+(
+} px_glyph_t;
+) print
+
+% Write the actual encodings.
+(*)
+ { (
+const unsigned short px) print dup =only ([257] = {) print
+ /Encoding findresource
+ 0 4 255
+ { (
+ ) print
+ dup 1 exch 3 add
+ { ( pxg_) print 2 copy get dup /.notdef eq { pop (_notdef) } if
+ =only (,) print pop
+ }
+ for
+ }
+ for
+ (
+ pxg__notdef
+};
+) print pop
+ }
+100 string /Encoding resourceforall
+
+% All done.
+flush quit
diff --git a/pxl/pxtag.h b/pxl/pxtag.h
new file mode 100644
index 000000000..e999bdab2
--- /dev/null
+++ b/pxl/pxtag.h
@@ -0,0 +1,10 @@
+/* Copyright (C) 1997 Aladdin Enterprises. All rights reserved.
+ Unauthorized use, copying, and/or distribution prohibited.
+ */
+
+/* pxtag.h */
+/* Operator and other tag definitions for PCL XL parser */
+
+/* The contents of this file have been moved to the graphics library, */
+/* so they can be used in the PCL XL driver. */
+#include "gdevpxop.h"
diff --git a/pxl/pxvalue.c b/pxl/pxvalue.c
new file mode 100644
index 000000000..6a70bbd92
--- /dev/null
+++ b/pxl/pxvalue.c
@@ -0,0 +1,66 @@
+/* Copyright (C) 1996 Aladdin Enterprises. All rights reserved.
+ Unauthorized use, copying, and/or distribution prohibited.
+ */
+
+/* pxvalue.c */
+/* Value accessing for PCL XL */
+
+#include "std.h"
+#include "gsmemory.h"
+#include "pxvalue.h"
+
+/* Get numeric values from the input or an array. */
+uint
+uint16at(const byte *p, bool big_endian)
+{ return (big_endian ? (p[0] << 8) + p[1] : (p[1] << 8) + p[0]);
+}
+int
+sint16at(const byte *p, bool big_endian)
+{ return ((int)uint16at(p, big_endian) ^ 0x8000) - 0x8000;
+}
+integer
+uint32at(const byte *p, bool big_endian)
+{ return
+ (big_endian ?
+ ((integer)((p[0] << 8) + p[1]) << 16) + (p[2] << 8) + p[3] :
+ ((integer)((p[3] << 8) + p[2]) << 16) + (p[1] << 8) + p[0]);
+}
+integer
+sint32at(const byte *p, bool big_endian)
+{ return (uint32at(p, big_endian) ^ 0x80000000) - 0x80000000;
+}
+real
+real32at(const byte *p, bool big_endian)
+{ float f;
+ *(integer *)&f = uint32at(p, big_endian);
+ return (real)f;
+}
+
+/* Get an element from an array. */
+/* The caller does all index and type checking. */
+integer
+integer_elt(const px_value_t *pav, uint index)
+{ px_data_type_t type = pav->type;
+ const byte *base = pav->value.array.data;
+ bool big_endian;
+
+ if ( type & pxd_ubyte )
+ return base[index];
+ big_endian = (type & pxd_big_endian) != 0;
+ if ( type & pxd_uint16 )
+ return uint16at(base + (index << 1), big_endian);
+ else if ( type & pxd_sint16 )
+ return sint16at(base + (index << 1), big_endian);
+ else if ( type & pxd_uint32 )
+ return uint32at(base + (index << 2), big_endian);
+ else /* ( type & pxd_sint32 ) */
+ return sint32at(base + (index << 2), big_endian);
+}
+real
+real_elt(const px_value_t *pav, uint index)
+{ return
+ (pav->type & pxd_real32 ?
+ real32at(pav->value.array.data + (index << 2),
+ (pav->type & pxd_big_endian) != 0) :
+ (real)integer_elt(pav, index));
+}
diff --git a/pxl/pxvalue.h b/pxl/pxvalue.h
new file mode 100644
index 000000000..07720356f
--- /dev/null
+++ b/pxl/pxvalue.h
@@ -0,0 +1,99 @@
+/* Copyright (C) 1996 Aladdin Enterprises. All rights reserved.
+ Unauthorized use, copying, and/or distribution prohibited.
+ */
+
+/* pxvalue.h */
+/* Value definitions for PCL XL parser */
+
+#ifndef pxvalue_INCLUDED
+# define pxvalue_INCLUDED
+
+#include "gstypes.h"
+#include "pxattr.h"
+
+/* ---------------- Values on stack ---------------- */
+
+/* Define masks for checking data types of attribute values. */
+typedef enum {
+ /* Structure */
+ pxd_scalar = 1,
+ pxd_xy = 2,
+ pxd_box = 4,
+ pxd_array = 8,
+ pxd_structure = 0xf,
+ /* Representation */
+ pxd_ubyte = 0x10,
+ pxd_uint16 = 0x20,
+ pxd_uint32 = 0x40,
+ pxd_sint16 = 0x80,
+ pxd_sint32 = 0x100,
+ pxd_any_int = pxd_ubyte | pxd_uint16 | pxd_uint32 | pxd_sint16 | pxd_sint32,
+ pxd_real32 = 0x200,
+ pxd_any_real = pxd_real32,
+ pxd_representation = 0x3f0,
+ /* Byte ordering for arrays */
+ pxd_big_endian = 0x400,
+ /* Heap allocation for arrays */
+ pxd_on_heap = 0x800
+} px_data_type_t;
+
+/* Define a type for a 32-bit integer. */
+#if arch_ints_are_short
+typedef long integer;
+#else
+typedef int integer;
+#endif
+typedef float real;
+
+/* Define a type for values on the stack. */
+typedef struct px_array_s {
+ uint size; /* number of elements, not length in bytes */
+ const byte *data;
+} px_array_t;
+typedef struct {
+ px_data_type_t type;
+ px_attribute_t attribute;
+ union v_ {
+ integer i;
+ integer ia[4]; /* ia[0] aliases i */
+ real r;
+ real ra[4]; /* ra[0] aliases r */
+ px_array_t array;
+ } value;
+} px_value_t;
+
+#define real_value(pv, n)\
+ ((pv)->type & pxd_any_real ? (pv)->value.ra[n] : (real)(pv)->value.ia[n])
+
+#define set_box_value(x1, y1, x2, y2, pv)\
+ do {\
+ const px_value_t *pv_ = (pv);\
+ if ( pv_->type & pxd_any_real )\
+ x1 = pv_->value.ra[0], y1 = pv_->value.ra[1],\
+ x2 = pv_->value.ra[2], y2 = pv_->value.ra[3];\
+ else\
+ x1 = pv_->value.ia[0], y1 = pv_->value.ia[1],\
+ x2 = pv_->value.ia[2], y2 = pv_->value.ia[3];\
+ } while ( 0 )
+
+#define value_size(pv)\
+ ((pv)->type & pxd_ubyte ? 1 : (pv)->type & (pxd_uint16 | pxd_sint16) ? 2 : 4)
+
+#define array_value_size(pav)\
+ ((pav)->value.array.size * value_size(pav))
+
+/* ---------------- Procedures ---------------- */
+
+/* Get numeric values from the input or an array. */
+uint uint16at(P2(const byte *p, bool big_endian));
+int sint16at(P2(const byte *p, bool big_endian));
+integer uint32at(P2(const byte *p, bool big_endian));
+integer sint32at(P2(const byte *p, bool big_endian));
+real real32at(P2(const byte *p, bool big_endian));
+
+/* Get an element from an array. */
+/* The caller does all index and type checking. */
+integer integer_elt(P2(const px_value_t *pav, uint index));
+real real_elt(P2(const px_value_t *pav, uint index));
+
+#endif /* pxvalue_INCLUDED */