diff options
-rw-r--r-- | pxl/makefile | 59 | ||||
-rw-r--r-- | pxl/news-pxl | 843 | ||||
-rw-r--r-- | pxl/pxasm.bat | 1 | ||||
-rw-r--r-- | pxl/pxasm.ps | 192 | ||||
-rw-r--r-- | pxl/pxattr.h | 10 | ||||
-rw-r--r-- | pxl/pxbfont.c | 2048 | ||||
-rw-r--r-- | pxl/pxbfont.h | 17 | ||||
-rw-r--r-- | pxl/pxbfont.ps | 120 | ||||
-rw-r--r-- | pxl/pxcet.txt | 170 | ||||
-rw-r--r-- | pxl/pxdict.h | 66 | ||||
-rw-r--r-- | pxl/pxdiff.txt | 315 | ||||
-rw-r--r-- | pxl/pxenum.h | 10 | ||||
-rw-r--r-- | pxl/pxerrors.c | 284 | ||||
-rw-r--r-- | pxl/pxerrors.h | 160 | ||||
-rw-r--r-- | pxl/pxffont.c | 603 | ||||
-rw-r--r-- | pxl/pxfont.c | 758 | ||||
-rw-r--r-- | pxl/pxfont.h | 90 | ||||
-rw-r--r-- | pxl/pxfts.txt | 153 | ||||
-rw-r--r-- | pxl/pxgstate.c | 914 | ||||
-rw-r--r-- | pxl/pxgstate.h | 190 | ||||
-rw-r--r-- | pxl/pximage.c | 685 | ||||
-rw-r--r-- | pxl/pxink.c | 714 | ||||
-rw-r--r-- | pxl/pxl.mak | 196 | ||||
-rw-r--r-- | pxl/pxl_top.mak | 38 | ||||
-rw-r--r-- | pxl/pxl_ugcc.mak | 59 | ||||
-rw-r--r-- | pxl/pxlib.txt | 222 | ||||
-rw-r--r-- | pxl/pxmain.c | 306 | ||||
-rw-r--r-- | pxl/pxoper.h | 97 | ||||
-rw-r--r-- | pxl/pxpaint.c | 799 | ||||
-rw-r--r-- | pxl/pxparse.c | 873 | ||||
-rw-r--r-- | pxl/pxparse.h | 90 | ||||
-rw-r--r-- | pxl/pxprint.bat | 17 | ||||
-rwxr-xr-x | pxl/pxprint.tcl | 16 | ||||
-rw-r--r-- | pxl/pxptable.c | 673 | ||||
-rw-r--r-- | pxl/pxptable.h | 54 | ||||
-rw-r--r-- | pxl/pxsessio.c | 485 | ||||
-rw-r--r-- | pxl/pxspec.txt | 563 | ||||
-rw-r--r-- | pxl/pxstate.c | 107 | ||||
-rw-r--r-- | pxl/pxstate.h | 164 | ||||
-rw-r--r-- | pxl/pxstream.c | 150 | ||||
-rw-r--r-- | pxl/pxsymbol.ps | 111 | ||||
-rw-r--r-- | pxl/pxtag.h | 10 | ||||
-rw-r--r-- | pxl/pxvalue.c | 66 | ||||
-rw-r--r-- | pxl/pxvalue.h | 99 |
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(¶ms, &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, + ¶ms, &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(¶ms, &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, ¶ms->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(¶ms, 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, ¶ms.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(¶ms, 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, ¶ms.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(¶ms, 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, ¶ms.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(¶ms, 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, ¶ms.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 */ |