summaryrefslogtreecommitdiff
path: root/devices
diff options
context:
space:
mode:
Diffstat (limited to 'devices')
-rw-r--r--devices/contrib.mak866
-rw-r--r--devices/devs.mak2058
-rw-r--r--devices/gdev3852.c186
-rw-r--r--devices/gdev3b1.c794
-rw-r--r--devices/gdev4081.c90
-rw-r--r--devices/gdev4693.c178
-rw-r--r--devices/gdev8510.c139
-rw-r--r--devices/gdev8bcm.c78
-rw-r--r--devices/gdev8bcm.h69
-rw-r--r--devices/gdevadmp.c401
-rw-r--r--devices/gdevatx.c267
-rw-r--r--devices/gdevbit.c820
-rw-r--r--devices/gdevbj10.c449
-rw-r--r--devices/gdevbjc.h287
-rw-r--r--devices/gdevbjcl.c249
-rw-r--r--devices/gdevbjcl.h395
-rw-r--r--devices/gdevbmp.c223
-rw-r--r--devices/gdevbmp.h35
-rw-r--r--devices/gdevbmpa.c711
-rw-r--r--devices/gdevbmpc.c235
-rw-r--r--devices/gdevccr.c289
-rw-r--r--devices/gdevcdj.c3930
-rw-r--r--devices/gdevcfax.c232
-rw-r--r--devices/gdevcif.c97
-rw-r--r--devices/gdevclj.c674
-rw-r--r--devices/gdevcljc.c100
-rw-r--r--devices/gdevcmykog.c805
-rw-r--r--devices/gdevcp50.c224
-rw-r--r--devices/gdevcslw.c145
-rw-r--r--devices/gdevdfax.c105
-rw-r--r--devices/gdevdjet.c608
-rw-r--r--devices/gdevdjtc.c274
-rw-r--r--devices/gdevdljm.c329
-rw-r--r--devices/gdevdljm.h149
-rw-r--r--devices/gdevdm24.c280
-rw-r--r--devices/gdevdsp.c1914
-rw-r--r--devices/gdevdsp.h265
-rw-r--r--devices/gdevdsp2.h46
-rw-r--r--devices/gdevepsc.c453
-rw-r--r--devices/gdevepsn.c496
-rw-r--r--devices/gdevescp.c412
-rw-r--r--devices/gdevevga.c114
-rw-r--r--devices/gdevfax.c314
-rw-r--r--devices/gdevfax.h64
-rw-r--r--devices/gdevfpng.c457
-rw-r--r--devices/gdevherc.c473
-rw-r--r--devices/gdevhl7x.c1016
-rw-r--r--devices/gdevicov.c203
-rw-r--r--devices/gdevifno.c811
-rw-r--r--devices/gdevijs.c1462
-rw-r--r--devices/gdevimgn.c569
-rw-r--r--devices/gdevjbig2.c126
-rw-r--r--devices/gdevjpeg.c556
-rw-r--r--devices/gdevjpx.c227
-rw-r--r--devices/gdevl256.c314
-rw-r--r--devices/gdevl31s.c281
-rw-r--r--devices/gdevlbp8.c215
-rw-r--r--devices/gdevlj56.c213
-rw-r--r--devices/gdevlp8k.c397
-rw-r--r--devices/gdevlxm.c417
-rw-r--r--devices/gdevmac.c780
-rw-r--r--devices/gdevmac.h164
-rw-r--r--devices/gdevmacpictop.h674
-rw-r--r--devices/gdevmacttf.h58
-rw-r--r--devices/gdevmeds.c92
-rw-r--r--devices/gdevmeds.h26
-rw-r--r--devices/gdevmgr.c431
-rw-r--r--devices/gdevmgr.h115
-rw-r--r--devices/gdevmiff.c83
-rw-r--r--devices/gdevmswn.c497
-rw-r--r--devices/gdevmswn.h108
-rw-r--r--devices/gdevmsxf.c464
-rw-r--r--devices/gdevn533.c207
-rw-r--r--devices/gdevo182.c307
-rw-r--r--devices/gdevokii.c323
-rw-r--r--devices/gdevos2p.c671
-rw-r--r--devices/gdevp2up.c144
-rw-r--r--devices/gdevpbm.c1401
-rw-r--r--devices/gdevpcfb.c908
-rw-r--r--devices/gdevpcfb.h200
-rw-r--r--devices/gdevpcl.c448
-rw-r--r--devices/gdevpcl.h71
-rw-r--r--devices/gdevpcx.c465
-rw-r--r--devices/gdevpe.c365
-rw-r--r--devices/gdevperm.c477
-rw-r--r--devices/gdevphex.c3378
-rw-r--r--devices/gdevpjet.c250
-rw-r--r--devices/gdevplan.c553
-rw-r--r--devices/gdevplib.c1021
-rw-r--r--devices/gdevplib.h61
-rw-r--r--devices/gdevpm.h37
-rw-r--r--devices/gdevpng.c1016
-rw-r--r--devices/gdevpsd.c1390
-rw-r--r--devices/gdevpsim.c388
-rw-r--r--devices/gdevpxut.c434
-rw-r--r--devices/gdevpxut.h89
-rw-r--r--devices/gdevrinkj.c1202
-rw-r--r--devices/gdevs3ga.c244
-rw-r--r--devices/gdevsco.c286
-rw-r--r--devices/gdevsgi.c288
-rw-r--r--devices/gdevsgi.h70
-rw-r--r--devices/gdevsj48.c286
-rw-r--r--devices/gdevsnfb.c114
-rw-r--r--devices/gdevsppr.c187
-rw-r--r--devices/gdevstc.c3573
-rw-r--r--devices/gdevstc.h248
-rw-r--r--devices/gdevstc1.c124
-rw-r--r--devices/gdevstc2.c421
-rw-r--r--devices/gdevstc3.c104
-rw-r--r--devices/gdevstc4.c294
-rw-r--r--devices/gdevsun.c685
-rw-r--r--devices/gdevsunr.c100
-rw-r--r--devices/gdevsvga.c1033
-rw-r--r--devices/gdevsvga.h92
-rw-r--r--devices/gdevtfax.c395
-rw-r--r--devices/gdevtfax.h25
-rw-r--r--devices/gdevtfnx.c202
-rw-r--r--devices/gdevtifs.c541
-rw-r--r--devices/gdevtifs.h90
-rw-r--r--devices/gdevtknk.c254
-rw-r--r--devices/gdevtrac.c703
-rw-r--r--devices/gdevtsep.c2820
-rw-r--r--devices/gdevupd.c7620
-rw-r--r--devices/gdevvglb.c379
-rw-r--r--devices/gdevwddb.c619
-rw-r--r--devices/gdevwdib.c736
-rw-r--r--devices/gdevwpr2.c1637
-rw-r--r--devices/gdevwprn.c680
-rw-r--r--devices/gdevx.c1298
-rw-r--r--devices/gdevx.h238
-rw-r--r--devices/gdevxalt.c864
-rw-r--r--devices/gdevxcf.c1470
-rw-r--r--devices/gdevxcmp.c897
-rw-r--r--devices/gdevxcmp.h145
-rw-r--r--devices/gdevxini.c951
-rw-r--r--devices/gdevxres.c84
-rw-r--r--devices/gxfcopy.c2602
-rw-r--r--devices/gxfcopy.h192
-rw-r--r--devices/minftrsz.c357
-rw-r--r--devices/minftrsz.h32
-rw-r--r--devices/rinkj/evenbetter-rll.c1802
-rw-r--r--devices/rinkj/evenbetter-rll.h103
-rw-r--r--devices/rinkj/rinkj-byte-stream.c108
-rw-r--r--devices/rinkj/rinkj-byte-stream.h38
-rw-r--r--devices/rinkj/rinkj-config.c153
-rw-r--r--devices/rinkj/rinkj-config.h26
-rw-r--r--devices/rinkj/rinkj-device.c87
-rw-r--r--devices/rinkj/rinkj-device.h55
-rw-r--r--devices/rinkj/rinkj-dither.c31
-rw-r--r--devices/rinkj/rinkj-dither.h30
-rw-r--r--devices/rinkj/rinkj-epson870.c1093
-rw-r--r--devices/rinkj/rinkj-epson870.h20
-rw-r--r--devices/rinkj/rinkj-screen-eb.c329
-rw-r--r--devices/rinkj/rinkj-screen-eb.h29
-rw-r--r--devices/vector/gdevagl.c4312
-rw-r--r--devices/vector/gdevagl.h37
-rw-r--r--devices/vector/gdevpdf.c3258
-rw-r--r--devices/vector/gdevpdfb.c654
-rw-r--r--devices/vector/gdevpdfb.h286
-rw-r--r--devices/vector/gdevpdfc.c1542
-rw-r--r--devices/vector/gdevpdfc.h66
-rw-r--r--devices/vector/gdevpdfd.c1552
-rw-r--r--devices/vector/gdevpdfe.c876
-rw-r--r--devices/vector/gdevpdfg.c3101
-rw-r--r--devices/vector/gdevpdfg.h365
-rw-r--r--devices/vector/gdevpdfi.c2925
-rw-r--r--devices/vector/gdevpdfj.c677
-rw-r--r--devices/vector/gdevpdfk.c883
-rw-r--r--devices/vector/gdevpdfm.c2656
-rw-r--r--devices/vector/gdevpdfo.c2083
-rw-r--r--devices/vector/gdevpdfo.h355
-rw-r--r--devices/vector/gdevpdfp.c940
-rw-r--r--devices/vector/gdevpdfr.c501
-rw-r--r--devices/vector/gdevpdft.c470
-rw-r--r--devices/vector/gdevpdfu.c2664
-rw-r--r--devices/vector/gdevpdfv.c1036
-rw-r--r--devices/vector/gdevpdfx.h1489
-rw-r--r--devices/vector/gdevpdt.c66
-rw-r--r--devices/vector/gdevpdt.h99
-rw-r--r--devices/vector/gdevpdtb.c824
-rw-r--r--devices/vector/gdevpdtb.h160
-rw-r--r--devices/vector/gdevpdtc.c983
-rw-r--r--devices/vector/gdevpdtd.c869
-rw-r--r--devices/vector/gdevpdtd.h169
-rw-r--r--devices/vector/gdevpdte.c1481
-rw-r--r--devices/vector/gdevpdtf.c1210
-rw-r--r--devices/vector/gdevpdtf.h450
-rw-r--r--devices/vector/gdevpdti.c1212
-rw-r--r--devices/vector/gdevpdti.h95
-rw-r--r--devices/vector/gdevpdts.c853
-rw-r--r--devices/vector/gdevpdts.h141
-rw-r--r--devices/vector/gdevpdtt.c3561
-rw-r--r--devices/vector/gdevpdtt.h332
-rw-r--r--devices/vector/gdevpdtv.c5509
-rw-r--r--devices/vector/gdevpdtv.h37
-rw-r--r--devices/vector/gdevpdtw.c895
-rw-r--r--devices/vector/gdevpdtw.h79
-rw-r--r--devices/vector/gdevpdtx.h96
-rw-r--r--devices/vector/gdevpsdf.h510
-rw-r--r--devices/vector/gdevpsdi.c976
-rw-r--r--devices/vector/gdevpsdp.c1198
-rw-r--r--devices/vector/gdevpsds.c1376
-rw-r--r--devices/vector/gdevpsds.h247
-rw-r--r--devices/vector/gdevpsdu.c520
-rw-r--r--devices/vector/gdevpsf.h309
-rw-r--r--devices/vector/gdevpsf1.c952
-rw-r--r--devices/vector/gdevpsf2.c1840
-rw-r--r--devices/vector/gdevpsfm.c318
-rw-r--r--devices/vector/gdevpsft.c1413
-rw-r--r--devices/vector/gdevpsfu.c353
-rw-r--r--devices/vector/gdevpsfx.c949
-rw-r--r--devices/vector/gdevpsu.c360
-rw-r--r--devices/vector/gdevpsu.h70
-rw-r--r--devices/vector/gdevpx.c2551
-rw-r--r--devices/vector/gdevtxtw.c2402
-rw-r--r--devices/vector/gdevxps.c2481
-rw-r--r--devices/vector/opdfread.h3964
-rw-r--r--devices/vector/whitelst.c579
-rw-r--r--devices/vector/whitelst.h25
219 files changed, 161010 insertions, 0 deletions
diff --git a/devices/contrib.mak b/devices/contrib.mak
new file mode 100644
index 000000000..f419be9b1
--- /dev/null
+++ b/devices/contrib.mak
@@ -0,0 +1,866 @@
+# Copyright (C) 2001-2012 Artifex Software, Inc.
+# All Rights Reserved.
+#
+# This software is provided AS-IS with no warranty, either express or
+# implied.
+#
+# This software is distributed under license and may not be copied,
+# modified or distributed except as expressly authorized under the terms
+# of the license contained in the file LICENSE in this distribution.
+#
+# Refer to licensing information at http://www.artifex.com or contact
+# Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+# CA 94903, U.S.A., +1(415)492-9861, for further information.
+#
+#
+# makefile for contributed device drivers.
+
+# Define the name of this makefile.
+CONTRIB_MAK=$(DEVSRC)contrib.mak
+
+###### --------------------------- Catalog -------------------------- ######
+
+# The following drivers are user-contributed, and maintained (if at all) by
+# users. Please report problems in these drivers to their authors, whose
+# e-mail addresses appear below: do not report them to mailing lists or
+# mailboxes for general Ghostscript problems.
+
+# Displays:
+# MS-DOS (note: not usable with Desqview/X):
+# herc Hercules Graphics display [MS-DOS only]
+# pe Private Eye display
+# Unix and VMS:
+# att3b1 AT&T 3b1/Unixpc monochrome display [3b1 only]
+# sonyfb Sony Microsystems monochrome display [Sony only]
+# sunview SunView window system [SunOS only]
+# Printers:
+# ap3250 Epson AP3250 printer
+# appledmp Apple Dot Matrix Printer (should also work with Imagewriter)
+# bj10e Canon BubbleJet BJ10e
+# bj200 Canon BubbleJet BJ200; also good for BJ300 in ProPrinter mode
+# (see comments in source code)
+# bjc600 Canon Color BubbleJet BJC-600, BJC-4000 and BJC-70
+# also good for Apple printers like the StyleWriter 2x00
+# bjc800 Canon Color BubbleJet BJC-800
+# ccr CalComp Raster format
+# cdeskjet H-P DeskJet 500C with 1 bit/pixel color
+# cdjcolor H-P DeskJet 500C with 24 bit/pixel color and
+# high-quality color (Floyd-Steinberg) dithering;
+# also good for DeskJet 540C and Citizen Projet IIc (-r200x300)
+# cdjmono H-P DeskJet 500C printing black only;
+# also good for DeskJet 510, 520, and 540C (black only)
+# cdj500 H-P DeskJet 500C (same as cdjcolor)
+# cdj550 H-P DeskJet 550C/560C/660C/660Cse
+# cljet5 H-P Color LaserJet 5/5M (see below for some notes)
+# cljet5c H-P Color LaserJet 5/5M (see below for some notes)
+# coslw2p CoStar LabelWriter II II/Plus
+# coslwxl CoStar LabelWriter XL
+# cp50 Mitsubishi CP50 color printer
+# declj250 alternate DEC LJ250 driver
+# djet500c H-P DeskJet 500C alternate driver
+# (does not work on 550C or 560C)
+# dnj650c H-P DesignJet 650C
+# epson Epson-compatible dot matrix printers (9- or 24-pin)
+# eps9mid Epson-compatible 9-pin, interleaved lines
+# (intermediate resolution)
+# eps9high Epson-compatible 9-pin, interleaved lines
+# (triple resolution)
+# epsonc Epson LQ-2550 and Fujitsu 3400/2400/1200 color printers
+# hl7x0 Brother HL 720 and HL 730 (HL 760 is PCL compliant);
+# also usable with the MFC6550MC Fax Machine.
+# ibmpro IBM 9-pin Proprinter
+# imagen Imagen ImPress printers
+# iwhi Apple Imagewriter in high-resolution mode
+# iwlo Apple Imagewriter in low-resolution mode
+# iwlq Apple Imagewriter LQ in 320 x 216 dpi mode
+# jetp3852 IBM Jetprinter ink-jet color printer (Model #3852)
+# lbp8 Canon LBP-8II laser printer
+# lips3 Canon LIPS III laser printer in English (CaPSL) mode
+# lj250 DEC LJ250 Companion color printer
+# lj3100sw H-P LaserJet 3100 (requires installed HP-Software)
+# lj4dith H-P LaserJet 4 with Floyd-Steinberg dithering
+# lp8000 Epson LP-8000 laser printer
+# lq850 Epson LQ850 printer at 360 x 360 DPI resolution;
+# also good for Canon BJ300 with LQ850 emulation
+# lxm5700m Lexmark 5700 monotone
+# m8510 C.Itoh M8510 printer
+# necp6 NEC P6/P6+/P60 printers at 360 x 360 DPI resolution
+# nwp533 Sony Microsystems NWP533 laser printer [Sony only]
+# oki182 Okidata MicroLine 182
+# okiibm Okidata MicroLine IBM-compatible printers
+# paintjet alternate H-P PaintJet color printer
+# photoex Epson Stylus Color Photo, Photo EX, Photo 700
+# pj H-P PaintJet XL driver
+# pjetxl alternate H-P PaintJet XL driver
+# pjxl H-P PaintJet XL color printer
+# pjxl300 H-P PaintJet XL300 color printer;
+# also good for PaintJet 1200C and CopyJet
+# r4081 Ricoh 4081 laser printer
+# sj48 StarJet 48 inkjet printer
+# sparc SPARCprinter
+# st800 Epson Stylus 800 printer
+# stcolor Epson Stylus Color
+# t4693d2 Tektronix 4693d color printer, 2 bits per R/G/B component
+# t4693d4 Tektronix 4693d color printer, 4 bits per R/G/B component
+# t4693d8 Tektronix 4693d color printer, 8 bits per R/G/B component
+# tek4696 Tektronix 4695/4696 inkjet plotter
+# uniprint Unified printer driver -- Configurable Color ESC/P-,
+# ESC/P2-, HP-RTL/PCL mono/color driver
+# Fax systems:
+# cfax SFF format for CAPI fax interface
+# dfaxhigh DigiBoard, Inc.'s DigiFAX software format (high resolution)
+# dfaxlow DigiFAX low (normal) resolution
+# Other raster file formats and devices:
+# cif CIF file format for VLSI
+# inferno Inferno bitmaps
+# mgrmono 1-bit monochrome MGR devices
+# mgrgray2 2-bit gray scale MGR devices
+# mgrgray4 4-bit gray scale MGR devices
+# mgrgray8 8-bit gray scale MGR devices
+# mgr4 4-bit (VGA) color MGR devices
+# mgr8 8-bit color MGR devices
+# sgirgb SGI RGB pixmap format
+# sunhmono Harlequin variant of 1-bit Sun raster file
+
+# If you add drivers, it would be nice if you kept each list
+# in alphabetical order.
+
+###### ----------------------- End of catalog ----------------------- ######
+
+###### ------------------- MS-DOS display devices ------------------- ######
+
+### ------------------- The Hercules Graphics display ------------------- ###
+
+herc_=$(DEVOBJ)gdevherc.$(OBJ)
+$(DD)herc.dev : $(herc_) $(MAKEDIRS)
+ $(SETDEV) $(DD)herc $(herc_)
+
+$(DEVOBJ)gdevherc.$(OBJ) : $(DEVSRC)gdevherc.c $(GDEV) $(dos__h)\
+ $(gsmatrix_h) $(gxbitmap_h) $(MAKEDIRS)
+ $(DEVCC) $(DEVO_)gdevherc.$(OBJ) $(C_) $(DEVSRC)gdevherc.c
+
+### ---------------------- The Private Eye display ---------------------- ###
+### Note: this driver was contributed by a user: ###
+### please contact narf@media-lab.media.mit.edu if you have questions. ###
+
+pe_=$(DEVOBJ)gdevpe.$(OBJ)
+$(DD)pe.dev : $(pe_) $(MAKEDIRS)
+ $(SETDEV) $(DD)pe $(pe_)
+
+$(DEVOBJ)gdevpe.$(OBJ) : $(DEVSRC)gdevpe.c $(GDEV) $(memory__h) $(MAKEDIRS)
+ $(DEVCC) $(DEVO_)gdevpe.$(OBJ) $(C_) $(DEVSRC)gdevpe.c
+
+###### ----------------------- Other displays ------------------------ ######
+
+### -------------- The AT&T 3b1 Unixpc monochrome display --------------- ###
+### Note: this driver was contributed by a user: please contact ###
+### Andy Fyfe (andy@cs.caltech.edu) if you have questions. ###
+
+att3b1_=$(DEVOBJ)gdev3b1.$(OBJ)
+$(DD)att3b1.dev : $(att3b1_) $(MAKEDIRS)
+ $(SETDEV) $(DD)att3b1 $(att3b1_)
+
+$(DEVOBJ)gdev3b1.$(OBJ) : $(DEVSRC)gdev3b1.c $(GDEV) $(MAKEDIRS)
+ $(DEVCC) $(DEVO_)gdev3b1.$(OBJ) $(C_) $(DEVSRC)gdev3b1.c
+
+### ------------------- Sony NeWS frame buffer device ------------------ ###
+### Note: this driver was contributed by a user: please contact ###
+### Mike Smolenski (mike@intertech.com) if you have questions. ###
+
+# This is implemented as a 'printer' device.
+sonyfb_=$(DEVOBJ)gdevsnfb.$(OBJ)
+$(DD)sonyfb.dev : $(sonyfb_) $(DD)page.dev $(MAKEDIRS)
+ $(SETPDEV) $(DD)sonyfb $(sonyfb_)
+
+$(DEVOBJ)gdevsnfb.$(OBJ) : $(DEVSRC)gdevsnfb.c $(PDEVH) $(MAKEDIRS)
+ $(DEVCC) $(DEVO_)gdevsnfb.$(OBJ) $(C_) $(DEVSRC)gdevsnfb.c
+
+### ------------------------ The SunView device ------------------------ ###
+### Note: this driver is maintained by a user: if you have questions, ###
+### please contact Andreas Stolcke (stolcke@icsi.berkeley.edu). ###
+
+sunview_=$(DEVOBJ)gdevsun.$(OBJ)
+$(DD)sunview.dev : $(sunview_) $(MAKEDIRS)
+ $(SETDEV) $(DD)sunview $(sunview_)
+ $(ADDMOD) $(DEVGENDIR)sunview -lib suntool sunwindow pixrect
+
+$(DEVOBJ)gdevsun.$(OBJ) : $(DEVSRC)gdevsun.c $(GDEV) $(malloc__h)\
+ $(gscdefs_h) $(gserrors_h) $(gsmatrix_h) $(MAKEDIRS)
+ $(DEVCC) $(DEVO_)gdevsun.$(OBJ) $(C_) $(DEVSRC)gdevsun.c
+
+###### --------------- Memory-buffered printer devices --------------- ######
+
+### --------------------- The Apple printer devices --------------------- ###
+### Note: these drivers were contributed by users. ###
+### If you have questions about the DMP driver, please contact ###
+### Mark Wedel (master@cats.ucsc.edu). ###
+### If you have questions about the Imagewriter drivers, please contact ###
+### Jonathan Luckey (luckey@rtfm.mlb.fl.us). ###
+### If you have questions about the Imagewriter LQ driver, please ###
+### contact Scott Barker (barkers@cuug.ab.ca). ###
+
+appledmp_=$(DEVOBJ)gdevadmp.$(OBJ)
+
+$(DEVOBJ)gdevadmp.$(OBJ) : $(DEVSRC)gdevadmp.c $(PDEVH) $(MAKEDIRS)
+ $(DEVCC) $(DEVO_)gdevadmp.$(OBJ) $(C_) $(DEVSRC)gdevadmp.c
+
+$(DD)appledmp.dev : $(appledmp_) $(DD)page.dev $(MAKEDIRS)
+ $(SETPDEV) $(DD)appledmp $(appledmp_)
+
+$(DD)iwhi.dev : $(appledmp_) $(DD)page.dev $(MAKEDIRS)
+ $(SETPDEV) $(DD)iwhi $(appledmp_)
+
+$(DD)iwlo.dev : $(appledmp_) $(DD)page.dev $(MAKEDIRS)
+ $(SETPDEV) $(DD)iwlo $(appledmp_)
+
+$(DD)iwlq.dev : $(appledmp_) $(DD)page.dev $(MAKEDIRS)
+ $(SETPDEV) $(DD)iwlq $(appledmp_)
+
+### ------------ The Canon BubbleJet BJ10e and BJ200 devices ------------ ###
+
+bj10e_=$(DEVOBJ)gdevbj10.$(OBJ)
+
+$(DD)bj10e.dev : $(bj10e_) $(DD)page.dev $(MAKEDIRS)
+ $(SETPDEV) $(DD)bj10e $(bj10e_)
+
+$(DD)bj200.dev : $(bj10e_) $(DD)page.dev $(MAKEDIRS)
+ $(SETPDEV) $(DD)bj200 $(bj10e_)
+
+$(DEVOBJ)gdevbj10.$(OBJ) : $(DEVSRC)gdevbj10.c $(PDEVH) $(MAKEDIRS)
+ $(DEVCC) $(DEVO_)gdevbj10.$(OBJ) $(C_) $(DEVSRC)gdevbj10.c
+
+### ------------- The CalComp Raster Format ----------------------------- ###
+### Note: this driver was contributed by a user: please contact ###
+### Ernst Muellner (ernst.muellner@oenzl.siemens.de) if you have ###
+### questions. ###
+
+ccr_=$(DEVOBJ)gdevccr.$(OBJ)
+$(DD)ccr.dev : $(ccr_) $(DD)page.dev $(MAKEDIRS)
+ $(SETPDEV) $(DD)ccr $(ccr_)
+
+$(DEVOBJ)gdevccr.$(OBJ) : $(DEVSRC)gdevccr.c $(PDEVH) $(MAKEDIRS)
+ $(DEVCC) $(DEVO_)gdevccr.$(OBJ) $(C_) $(DEVSRC)gdevccr.c
+
+### The H-P DeskJet, PaintJet, and DesignJet family color printer devices.###
+### Note: there are two different 500C drivers, both contributed by users.###
+### If you have questions about the djet500c driver, ###
+### please contact AKayser@et.tudelft.nl. ###
+### If you have questions about the cdj* drivers, ###
+### please contact g.cameron@biomed.abdn.ac.uk. ###
+### If you have questions about the dnj560c driver, ###
+### please contact koert@zen.cais.com. ###
+### If you have questions about the lj4dith driver, ###
+### please contact Eckhard.Rueggeberg@ts.go.dlr.de. ###
+### The BJC600/BJC4000, BJC800, and ESCP were originally contributed ###
+### by yves.arrouye@usa.net, but he no longer answers questions ###
+### about them. ###
+
+cdeskjet_=$(DEVOBJ)gdevcdj.$(OBJ) $(HPPCL)
+
+$(DD)cdeskjet.dev : $(cdeskjet_) $(DD)page.dev $(MAKEDIRS)
+ $(SETPDEV) $(DD)cdeskjet $(cdeskjet_)
+
+$(DD)cdjcolor.dev : $(cdeskjet_) $(DD)page.dev $(MAKEDIRS)
+ $(SETPDEV) $(DD)cdjcolor $(cdeskjet_)
+
+$(DD)cdjmono.dev : $(cdeskjet_) $(DD)page.dev $(MAKEDIRS)
+ $(SETPDEV) $(DD)cdjmono $(cdeskjet_)
+
+$(DD)cdj500.dev : $(cdeskjet_) $(DD)page.dev $(MAKEDIRS)
+ $(SETPDEV) $(DD)cdj500 $(cdeskjet_)
+
+$(DD)cdj550.dev : $(cdeskjet_) $(DD)page.dev $(MAKEDIRS)
+ $(SETPDEV) $(DD)cdj550 $(cdeskjet_)
+
+$(DD)declj250.dev : $(cdeskjet_) $(DD)page.dev $(MAKEDIRS)
+ $(SETPDEV) $(DD)declj250 $(cdeskjet_)
+
+$(DD)dnj650c.dev : $(cdeskjet_) $(DD)page.dev $(MAKEDIRS)
+ $(SETPDEV) $(DD)dnj650c $(cdeskjet_)
+
+$(DD)lj4dith.dev : $(cdeskjet_) $(DD)page.dev $(MAKEDIRS)
+ $(SETPDEV) $(DD)lj4dith $(cdeskjet_)
+
+$(DD)pj.dev : $(cdeskjet_) $(DD)page.dev $(MAKEDIRS)
+ $(SETPDEV) $(DD)pj $(cdeskjet_)
+
+$(DD)pjxl.dev : $(cdeskjet_) $(DD)page.dev $(MAKEDIRS)
+ $(SETPDEV) $(DD)pjxl $(cdeskjet_)
+
+# Note: the pjxl300 driver also works for the CopyJet.
+$(DD)pjxl300.dev : $(cdeskjet_) $(DD)page.dev $(MAKEDIRS)
+ $(SETPDEV) $(DD)pjxl300 $(cdeskjet_)
+
+# Note: the BJC600 driver also works for the BJC4000.
+$(DD)bjc600.dev : $(cdeskjet_) $(DD)page.dev $(MAKEDIRS)
+ $(SETPDEV) $(DD)bjc600 $(cdeskjet_)
+
+$(DD)bjc800.dev : $(cdeskjet_) $(DD)page.dev $(MAKEDIRS)
+ $(SETPDEV) $(DD)bjc800 $(cdeskjet_)
+
+$(DD)escp.dev : $(cdeskjet_) $(DD)page.dev $(MAKEDIRS)
+ $(SETPDEV) $(DD)escp $(cdeskjet_)
+
+# NB: you can also customise the build if required, using
+# -DBitsPerPixel=<number> if you wish the default to be other than 24
+# for the generic drivers (cdj500, cdj550, pjxl300, pjtest, pjxltest).
+
+gdevbjc_h=$(DEVSRC)gdevbjc.h
+
+$(DEVOBJ)gdevcdj.$(OBJ) : $(DEVSRC)gdevcdj.c $(std_h) $(PDEVH)\
+ $(gsparam_h) $(gsstate_h) $(gxlum_h)\
+ $(gdevbjc_h) $(gdevpcl_h) $(MAKEDIRS)
+ $(DEVCC) $(DEVO_)gdevcdj.$(OBJ) $(C_) $(DEVSRC)gdevcdj.c
+
+djet500c_=$(DEVOBJ)gdevdjtc.$(OBJ) $(HPPCL)
+$(DD)djet500c.dev : $(djet500c_) $(DD)page.dev $(MAKEDIRS)
+ $(SETPDEV) $(DD)djet500c $(djet500c_)
+
+$(DEVOBJ)gdevdjtc.$(OBJ) : $(DEVSRC)gdevdjtc.c $(PDEVH) $(malloc__h) $(gdevpcl_h) \
+ $(MAKEDIRS)
+ $(DEVCC) $(DEVO_)gdevdjtc.$(OBJ) $(C_) $(DEVSRC)gdevdjtc.c
+
+### -------------------- The H-P Color LaserJet 5/5M -------------------- ###
+
+### There are two different drivers for this device.
+### For questions about the cljet5/cljet5pr (more general) driver, contact
+### Jan Stoeckenius <jan@orimp.com>
+### For questions about the cljet5c (simple) driver, contact
+### Henry Stiles <henrys@meerkat.dimensional.com>
+### Note that this is a long-edge-feed device, so the default page size is
+### wider than it is high. To print portrait pages, specify the page size
+### explicitly, e.g. -c letter or -c a4 on the command line.
+
+cljet5_=$(DEVOBJ)gdevclj.$(OBJ) $(HPPCL)
+
+$(DD)cljet5.dev : $(DEVS_MAK) $(cljet5_) $(GLD)page.dev $(MAKEDIRS)
+ $(SETPDEV) $(DD)cljet5 $(cljet5_)
+
+# The cljet5pr driver has hacks for trying to handle page rotation.
+# The hacks only work with one special PCL interpreter. Don't use it!
+$(DD)cljet5pr.dev : $(DEVS_MAK) $(cljet5_) $(GLD)page.dev $(MAKEDIRS)
+ $(SETPDEV) $(DD)cljet5pr $(cljet5_)
+
+$(DEVOBJ)gdevclj.$(OBJ) : $(DEVSRC)gdevclj.c $(math__h) $(PDEVH)\
+ $(gx_h) $(gsparam_h) $(gdevpcl_h) $(MAKEDIRS)
+ $(DEVCC) $(DEVO_)gdevclj.$(OBJ) $(C_) $(DEVSRC)gdevclj.c
+
+cljet5c_=$(DEVOBJ)gdevcljc.$(OBJ) $(HPPCL)
+$(DD)cljet5c.dev : $(DEVS_MAK) $(cljet5c_) $(GLD)page.dev $(MAKEDIRS)
+ $(SETPDEV) $(DD)cljet5c $(cljet5c_)
+
+$(DEVOBJ)gdevcljc.$(OBJ) : $(DEVSRC)gdevcljc.c $(math__h) $(PDEVH) $(gdevpcl_h) \
+ $(MAKEDIRS)
+ $(DEVCC) $(DEVO_)gdevcljc.$(OBJ) $(C_) $(DEVSRC)gdevcljc.c
+
+### --------------- The H-P LaserJet 3100 software device --------------- ###
+
+### NOTE: This driver requires installed HP-Software to print. ###
+### It can be used with smbclient to print from an UNIX box to a ###
+### LaserJet 3100 printer attached to a MS-Windows box. ###
+### NOTE: this driver was contributed by a user: please contact ###
+### Ulrich Schmid (uschmid@mail.hh.provi.de) if you have questions. ###
+
+lj3100sw_=$(DEVOBJ)gdevl31s.$(OBJ) $(DEVOBJ)gdevmeds.$(OBJ)
+$(DD)lj3100sw.dev : $(lj3100sw_) $(DD)page.dev $(MAKEDIRS)
+ $(SETPDEV) $(DD)lj3100sw $(lj3100sw_)
+
+gdevmeds_h=$(DEVSRC)gdevmeds.h $(gdevprn_h)
+
+$(DEVOBJ)gdevl31s.$(OBJ) : $(DEVSRC)gdevl31s.c $(gdevmeds_h) $(PDEVH) \
+ $(MAKEDIRS)
+ $(DEVCC) $(DEVO_)gdevl31s.$(OBJ) $(C_) $(DEVSRC)gdevl31s.c
+
+$(DEVOBJ)gdevmeds.$(OBJ) : $(DEVSRC)gdevmeds.c $(AK) $(gdevmeds_h) \
+ $(MAKEDIRS)
+ $(DEVCC) $(DEVO_)gdevmeds.$(OBJ) $(C_) $(DEVSRC)gdevmeds.c
+
+### ------ CoStar LabelWriter II II/Plus device ------ ###
+### Contributed by Mike McCauley mikem@open.com.au ###
+
+coslw_=$(DEVOBJ)gdevcslw.$(OBJ)
+
+$(DD)coslw2p.dev : $(coslw_) $(DD)page.dev $(MAKEDIRS)
+ $(SETPDEV) $(DD)coslw2p $(coslw_)
+
+$(DD)coslwxl.dev : $(coslw_) $(DD)page.dev $(MAKEDIRS)
+ $(SETPDEV) $(DD)coslwxl $(coslw_)
+
+$(DEVOBJ)gdevcslw.$(OBJ) : $(DEVSRC)gdevcslw.c $(PDEVH) $(MAKEDIRS)
+ $(DEVCC) $(DEVO_)gdevcslw.$(OBJ) $(C_) $(DEVSRC)gdevcslw.c
+
+### -------------------- The Mitsubishi CP50 printer -------------------- ###
+### Note: this driver was contributed by a user: please contact ###
+### Michael Hu (michael@ximage.com) if you have questions. ###
+
+cp50_=$(DEVOBJ)gdevcp50.$(OBJ)
+$(DD)cp50.dev : $(cp50_) $(DD)page.dev $(MAKEDIRS)
+ $(SETPDEV) $(DD)cp50 $(cp50_)
+
+$(DEVOBJ)gdevcp50.$(OBJ) : $(DEVSRC)gdevcp50.c $(PDEVH) $(MAKEDIRS)
+ $(DEVCC) $(DEVO_)gdevcp50.$(OBJ) $(C_) $(DEVSRC)gdevcp50.c
+
+### ----------------- The generic Epson printer device ----------------- ###
+### Note: most of this code was contributed by users. Please contact ###
+### the following people if you have questions: ###
+### eps9mid - Guenther Thomsen (thomsen@cs.tu-berlin.de) ###
+### eps9high - David Wexelblat (dwex@mtgzfs3.att.com) ###
+### ibmpro - James W. Birdsall (jwbirdsa@picarefy.picarefy.com) ###
+
+epson_=$(DEVOBJ)gdevepsn.$(OBJ)
+
+$(DD)epson.dev : $(epson_) $(DD)page.dev $(MAKEDIRS)
+ $(SETPDEV) $(DD)epson $(epson_)
+
+$(DD)eps9mid.dev : $(epson_) $(DD)page.dev $(MAKEDIRS)
+ $(SETPDEV) $(DD)eps9mid $(epson_)
+
+$(DD)eps9high.dev : $(epson_) $(DD)page.dev $(MAKEDIRS)
+ $(SETPDEV) $(DD)eps9high $(epson_)
+
+$(DEVOBJ)gdevepsn.$(OBJ) : $(DEVSRC)gdevepsn.c $(PDEVH) $(MAKEDIRS)
+ $(DEVCC) $(DEVO_)gdevepsn.$(OBJ) $(C_) $(DEVSRC)gdevepsn.c
+
+### ----------------- The IBM Proprinter printer device ---------------- ###
+
+$(DD)ibmpro.dev : $(epson_) $(DD)page.dev $(MAKEDIRS)
+ $(SETPDEV) $(DD)ibmpro $(epson_)
+
+### -------------- The Epson LQ-2550 color printer device -------------- ###
+### Note: this driver was contributed by users: please contact ###
+### Dave St. Clair (dave@exlog.com) if you have questions. ###
+
+epsonc_=$(DEVOBJ)gdevepsc.$(OBJ)
+$(DD)epsonc.dev : $(epsonc_) $(DD)page.dev $(MAKEDIRS)
+ $(SETPDEV) $(DD)epsonc $(epsonc_)
+
+$(DEVOBJ)gdevepsc.$(OBJ) : $(DEVSRC)gdevepsc.c $(PDEVH) $(MAKEDIRS)
+ $(DEVCC) $(DEVO_)gdevepsc.$(OBJ) $(C_) $(DEVSRC)gdevepsc.c
+
+### ------------- The Epson ESC/P 2 language printer devices ------------- ###
+### Note: these drivers were contributed by users. ###
+### For questions about the Stylus 800 and AP3250 drivers, please contact ###
+### Richard Brown (rab@tauon.ph.unimelb.edu.au). ###
+### For questions about the Stylus Color drivers, please contact ###
+### Gunther Hess (gunther@elmos.de). ###
+
+ESCP2=$(DEVOBJ)gdevescp.$(OBJ)
+
+$(DEVOBJ)gdevescp.$(OBJ) : $(DEVSRC)gdevescp.c $(PDEVH) $(MAKEDIRS)
+ $(DEVCC) $(DEVO_)gdevescp.$(OBJ) $(C_) $(DEVSRC)gdevescp.c
+
+$(DD)ap3250.dev : $(ESCP2) $(DD)page.dev $(MAKEDIRS)
+ $(SETPDEV) $(DD)ap3250 $(ESCP2)
+
+$(DD)st800.dev : $(ESCP2) $(DD)page.dev $(MAKEDIRS)
+ $(SETPDEV) $(DD)st800 $(ESCP2)
+
+stcolor1_=$(DEVOBJ)gdevstc.$(OBJ) $(DEVOBJ)gdevstc1.$(OBJ) $(DEVOBJ)gdevstc2.$(OBJ)
+stcolor2_=$(DEVOBJ)gdevstc3.$(OBJ) $(DEVOBJ)gdevstc4.$(OBJ)
+$(DD)stcolor.dev : $(stcolor1_) $(stcolor2_) $(DD)page.dev $(MAKEDIRS)
+ $(SETPDEV) $(DD)stcolor $(stcolor1_)
+ $(ADDMOD) $(DD)stcolor -obj $(stcolor2_)
+
+gdevstc_h=$(DEVSRC)gdevstc.h $(gdevprn_h) $(gsparam_h) $(gsstate_h)
+
+$(DEVOBJ)gdevstc.$(OBJ) : $(DEVSRC)gdevstc.c $(gdevstc_h) $(PDEVH) $(MAKEDIRS)
+ $(DEVCC) $(DEVO_)gdevstc.$(OBJ) $(C_) $(DEVSRC)gdevstc.c
+
+$(DEVOBJ)gdevstc1.$(OBJ) : $(DEVSRC)gdevstc1.c $(gdevstc_h) $(PDEVH) $(MAKEDIRS)
+ $(DEVCC) $(DEVO_)gdevstc1.$(OBJ) $(C_) $(DEVSRC)gdevstc1.c
+
+$(DEVOBJ)gdevstc2.$(OBJ) : $(DEVSRC)gdevstc2.c $(gdevstc_h) $(PDEVH) $(MAKEDIRS)
+ $(DEVCC) $(DEVO_)gdevstc2.$(OBJ) $(C_) $(DEVSRC)gdevstc2.c
+
+$(DEVOBJ)gdevstc3.$(OBJ) : $(DEVSRC)gdevstc3.c $(gdevstc_h) $(PDEVH) $(MAKEDIRS)
+ $(DEVCC) $(DEVO_)gdevstc3.$(OBJ) $(C_) $(DEVSRC)gdevstc3.c
+
+$(DEVOBJ)gdevstc4.$(OBJ) : $(DEVSRC)gdevstc4.c $(gdevstc_h) $(PDEVH) $(MAKEDIRS)
+ $(DEVCC) $(DEVO_)gdevstc4.$(OBJ) $(C_) $(DEVSRC)gdevstc4.c
+
+### --------------- Ugly/Update -> Unified Printer Driver ---------------- ###
+### For questions about this driver, please contact: ###
+### Gunther Hess (gunther@elmos.de) ###
+
+uniprint_=$(DEVOBJ)gdevupd.$(OBJ)
+$(DD)uniprint.dev : $(uniprint_) $(DD)page.dev $(MAKEDIRS)
+ $(SETPDEV) $(DD)uniprint $(uniprint_)
+
+$(DEVOBJ)gdevupd.$(OBJ) : $(DEVSRC)gdevupd.c $(PDEVH) $(gsparam_h) $(MAKEDIRS)
+ $(DEVCC) $(DEVO_)gdevupd.$(OBJ) $(C_) $(DEVSRC)gdevupd.c
+
+### ------------ The H-P PaintJet color printer device ----------------- ###
+### Note: this driver also supports the DEC LJ250 color printer, which ###
+### has a PaintJet-compatible mode, and the PaintJet XL. ###
+### If you have questions about the XL, please contact Rob Reiss ###
+### (rob@moray.berkeley.edu). ###
+
+PJET=$(DEVOBJ)gdevpjet.$(OBJ) $(HPPCL)
+
+$(DEVOBJ)gdevpjet.$(OBJ) : $(DEVSRC)gdevpjet.c $(PDEVH) $(gdevpcl_h) $(MAKEDIRS)
+ $(DEVCC) $(DEVO_)gdevpjet.$(OBJ) $(C_) $(DEVSRC)gdevpjet.c
+
+$(DD)lj250.dev : $(PJET) $(DD)page.dev $(MAKEDIRS)
+ $(SETPDEV) $(DD)lj250 $(PJET)
+
+$(DD)paintjet.dev : $(PJET) $(DD)page.dev $(MAKEDIRS)
+ $(SETPDEV) $(DD)paintjet $(PJET)
+
+$(DD)pjetxl.dev : $(PJET) $(DD)page.dev $(MAKEDIRS)
+ $(SETPDEV) $(DD)pjetxl $(PJET)
+
+###--------------------- The Brother HL 7x0 printer --------------------- ###
+### Note: this driver was contributed by users: please contact ###
+### Pierre-Olivier Gaillard (pierre.gaillard@hol.fr) ###
+### for questions about the basic driver; ###
+### Ross Martin (ross@ross.interwrx.com, martin@walnut.eas.asu.edu) ###
+### for questions about usage with the MFC6550MC Fax Machine. ###
+
+hl7x0_=$(DEVOBJ)gdevhl7x.$(OBJ)
+$(DD)hl7x0.dev : $(hl7x0_) $(DD)page.dev $(MAKEDIRS)
+ $(SETPDEV) $(DD)hl7x0 $(hl7x0_)
+
+$(DEVOBJ)gdevhl7x.$(OBJ) : $(DEVSRC)gdevhl7x.c $(PDEVH) $(gdevpcl_h) $(MAKEDIRS)
+ $(DEVCC) $(DEVO_)gdevhl7x.$(OBJ) $(C_) $(DEVSRC)gdevhl7x.c
+
+### -------------- Imagen ImPress Laser Printer device ----------------- ###
+### Note: this driver was contributed by a user: please contact ###
+### Alan Millar (AMillar@bolis.sf-bay.org) if you have questions. ###
+### Set USE_BYTE_STREAM if using parallel interface; ###
+### Don't set it if using 'ipr' spooler (default). ###
+### You may also add -DA4 if needed for A4 paper. ###
+
+imagen_=$(DEVOBJ)gdevimgn.$(OBJ)
+$(DD)imagen.dev : $(imagen_) $(DD)page.dev $(MAKEDIRS)
+ $(SETPDEV) $(DD)imagen $(imagen_)
+
+# Uncomment the first line for the ipr spooler, the second line for parallel.
+IMGN_OPT=
+#IMGN_OPT=-DUSE_BYTE_STREAM
+$(DEVOBJ)gdevimgn.$(OBJ) : $(DEVSRC)gdevimgn.c $(PDEVH) $(MAKEDIRS)
+ $(DEVCC) $(IMGN_OPT) $(DEVO_)gdevimgn.$(OBJ) $(C_) $(DEVSRC)gdevimgn.c
+
+### ------- The IBM 3852 JetPrinter color inkjet printer device -------- ###
+### Note: this driver was contributed by users: please contact ###
+### Kevin Gift (kgift@draper.com) if you have questions. ###
+### Note that the paper size that can be addressed by the graphics mode ###
+### used in this driver is fixed at 7-1/2 inches wide (the printable ###
+### width of the jetprinter itself.) ###
+
+jetp3852_=$(DEVOBJ)gdev3852.$(OBJ)
+$(DD)jetp3852.dev : $(jetp3852_) $(DD)page.dev $(MAKEDIRS)
+ $(SETPDEV) $(DD)jetp3852 $(jetp3852_)
+
+$(DEVOBJ)gdev3852.$(OBJ) : $(DEVSRC)gdev3852.c $(PDEVH) $(gdevpcl_h) $(MAKEDIRS)
+ $(DEVCC) $(DEVO_)gdev3852.$(OBJ) $(C_) $(DEVSRC)gdev3852.c
+
+### ---------- The Canon LBP-8II and LIPS III printer devices ---------- ###
+### Note: these drivers were contributed by users. ###
+### For questions about these drivers, please contact ###
+### Lauri Paatero, lauri.paatero@paatero.pp.fi ###
+
+lbp8_=$(DEVOBJ)gdevlbp8.$(OBJ)
+$(DD)lbp8.dev : $(lbp8_) $(DD)page.dev $(MAKEDIRS)
+ $(SETPDEV) $(DD)lbp8 $(lbp8_)
+
+$(DD)lips3.dev : $(lbp8_) $(DD)page.dev $(MAKEDIRS)
+ $(SETPDEV) $(DD)lips3 $(lbp8_)
+
+$(DEVOBJ)gdevlbp8.$(OBJ) : $(DEVSRC)gdevlbp8.c $(PDEVH) $(MAKEDIRS)
+ $(DEVCC) $(DEVO_)gdevlbp8.$(OBJ) $(C_) $(DEVSRC)gdevlbp8.c
+
+### -------------- The Epson LP-8000 laser printer device -------------- ###
+### Note: this driver was contributed by a user: please contact Oleg ###
+### Oleg Fat'yanov <faty1@rlem.titech.ac.jp> if you have questions.###
+
+lp8000_=$(DEVOBJ)gdevlp8k.$(OBJ)
+$(DD)lp8000.dev : $(lp8000_) $(DD)page.dev $(MAKEDIRS)
+ $(SETPDEV) $(DD)lp8000 $(lp8000_)
+
+$(DEVOBJ)gdevlp8k.$(OBJ) : $(DEVSRC)gdevlp8k.c $(PDEVH) $(MAKEDIRS)
+ $(DEVCC) $(DEVO_)gdevlp8k.$(OBJ) $(C_) $(DEVSRC)gdevlp8k.c
+
+### -------------- The C.Itoh M8510 printer device --------------------- ###
+### Note: this driver was contributed by a user: please contact Bob ###
+### Smith <bob@snuffy.penfield.ny.us> if you have questions. ###
+
+m8510_=$(DEVOBJ)gdev8510.$(OBJ)
+$(DD)m8510.dev : $(m8510_) $(DD)page.dev $(MAKEDIRS)
+ $(SETPDEV) $(DD)m8510 $(m8510_)
+
+$(DEVOBJ)gdev8510.$(OBJ) : $(DEVSRC)gdev8510.c $(PDEVH) $(MAKEDIRS)
+ $(DEVCC) $(DEVO_)gdev8510.$(OBJ) $(C_) $(DEVSRC)gdev8510.c
+
+### -------------- 24pin Dot-matrix printer with 360DPI ---------------- ###
+### Note: this driver was contributed by users. Please contact: ###
+### Andreas Schwab (schwab@ls5.informatik.uni-dortmund.de) for ###
+### questions about the NEC P6; ###
+### Christian Felsch (felsch@tu-harburg.d400.de) for ###
+### questions about the Epson LQ850. ###
+
+dm24_=$(DEVOBJ)gdevdm24.$(OBJ)
+$(DD)necp6.dev : $(dm24_) $(DD)page.dev $(MAKEDIRS)
+ $(SETPDEV) $(DD)necp6 $(dm24_)
+
+$(DD)lq850.dev : $(dm24_) $(DD)page.dev $(MAKEDIRS)
+ $(SETPDEV) $(DD)lq850 $(dm24_)
+
+$(DEVOBJ)gdevdm24.$(OBJ) : $(DEVSRC)gdevdm24.c $(PDEVH) $(MAKEDIRS)
+ $(DEVCC) $(DEVO_)gdevdm24.$(OBJ) $(C_) $(DEVSRC)gdevdm24.c
+
+### ----------------- Lexmark 5700 printer ----------------------------- ###
+### Note: this driver was contributed by users. Please contact: ###
+### Stephen Taylor (setaylor@ma.ultranet.com) if you have questions. ###
+
+lxm5700m_=$(DEVOBJ)gdevlxm.$(OBJ)
+$(DD)lxm5700m.dev : $(lxm5700m_) $(DD)page.dev $(MAKEDIRS)
+ $(SETPDEV) $(DD)lxm5700m $(lxm5700m_)
+
+$(DEVOBJ)gdevlxm.$(OBJ) : $(DEVSRC)gdevlxm.c $(PDEVH) $(gsparams_h) $(MAKEDIRS)
+ $(DEVCC) $(DEVO_)gdevlxm.$(OBJ) $(C_) $(DEVSRC)gdevlxm.c
+
+### ----------------- The Okidata MicroLine 182 device ----------------- ###
+### Note: this driver was contributed by a user: please contact ###
+### Maarten Koning (smeg@bnr.ca) if you have questions. ###
+
+oki182_=$(DEVOBJ)gdevo182.$(OBJ)
+$(DD)oki182.dev : $(oki182_) $(DD)page.dev $(MAKEDIRS)
+ $(SETPDEV) $(DD)oki182 $(oki182_)
+
+$(DEVOBJ)gdevo182.$(OBJ) : $(DEVSRC)gdevo182.c $(PDEVH) $(MAKEDIRS)
+ $(DEVCC) $(DEVO_)gdevo182.$(OBJ) $(C_) $(DEVSRC)gdevo182.c
+
+### ------------- The Okidata IBM compatible printer device ------------ ###
+### Note: this driver was contributed by a user: please contact ###
+### Charles Mack (chasm@netcom.com) if you have questions. ###
+
+okiibm_=$(DEVOBJ)gdevokii.$(OBJ)
+$(DD)okiibm.dev : $(okiibm_) $(DD)page.dev $(MAKEDIRS)
+ $(SETPDEV) $(DD)okiibm $(okiibm_)
+
+$(DEVOBJ)gdevokii.$(OBJ) : $(DEVSRC)gdevokii.c $(PDEVH) $(MAKEDIRS)
+ $(DEVCC) $(DEVO_)gdevokii.$(OBJ) $(C_) $(DEVSRC)gdevokii.c
+
+### ------------------ The Epson Stylus Photo devices ------------------ ###
+### This driver was contributed by a user: please contact ###
+### Zoltan Kocsi (zoltan@bendor.com.au) if you have questions. ###
+
+photoex_=$(DEVOBJ)gdevphex.$(OBJ)
+$(DD)photoex.dev : $(photoex_) $(DD)page.dev $(MAKEDIRS)
+ $(SETPDEV) $(DD)photoex $(photoex_)
+
+$(DEVOBJ)gdevphex.$(OBJ) : $(DEVSRC)gdevphex.c $(PDEVH) $(MAKEDIRS)
+ $(DEVCC) $(DEVO_)gdevphex.$(OBJ) $(C_) $(DEVSRC)gdevphex.c
+
+### ------------- The Ricoh 4081 laser printer device ------------------ ###
+### Note: this driver was contributed by users: ###
+### please contact kdw@oasis.icl.co.uk if you have questions. ###
+
+r4081_=$(DEVOBJ)gdev4081.$(OBJ)
+$(DD)r4081.dev : $(r4081_) $(DD)page.dev $(MAKEDIRS)
+ $(SETPDEV) $(DD)r4081 $(r4081_)
+
+
+$(DEVOBJ)gdev4081.$(OBJ) : $(DEVSRC)gdev4081.c $(PDEVH) $(MAKEDIRS)
+ $(DEVCC) $(DEVO_)gdev4081.$(OBJ) $(C_) $(DEVSRC)gdev4081.c
+
+### -------------------- Sony NWP533 printer device -------------------- ###
+### Note: this driver was contributed by a user: please contact Tero ###
+### Kivinen (kivinen@joker.cs.hut.fi) if you have questions. ###
+
+nwp533_=$(DEVOBJ)gdevn533.$(OBJ)
+$(DD)nwp533.dev : $(nwp533_) $(DD)page.dev $(MAKEDIRS)
+ $(SETPDEV) $(DD)nwp533 $(nwp533_)
+
+$(DEVOBJ)gdevn533.$(OBJ) : $(DEVSRC)gdevn533.c $(PDEVH) $(MAKEDIRS)
+ $(DEVCC) $(DEVO_)gdevn533.$(OBJ) $(C_) $(DEVSRC)gdevn533.c
+
+### ------------------------- The SPARCprinter ------------------------- ###
+### Note: this driver was contributed by users: please contact Martin ###
+### Schulte (schulte@thp.uni-koeln.de) if you have questions. ###
+### He would also like to hear from anyone using the driver. ###
+### Please consult the source code for additional documentation. ###
+
+sparc_=$(DEVOBJ)gdevsppr.$(OBJ)
+$(DD)sparc.dev : $(sparc_) $(DD)page.dev $(MAKEDIRS)
+ $(SETPDEV) $(DD)sparc $(sparc_)
+
+$(DEVOBJ)gdevsppr.$(OBJ) : $(DEVSRC)gdevsppr.c $(PDEVH) $(MAKEDIRS)
+ $(DEVCC) $(DEVO_)gdevsppr.$(OBJ) $(C_) $(DEVSRC)gdevsppr.c
+
+### ----------------- The StarJet SJ48 device -------------------------- ###
+### Note: this driver was contributed by a user: if you have questions, ###
+### . ###
+### please contact Mats Akerblom (f86ma@dd.chalmers.se). ###
+
+sj48_=$(DEVOBJ)gdevsj48.$(OBJ)
+$(DD)sj48.dev : $(sj48_) $(DD)page.dev $(MAKEDIRS)
+ $(SETPDEV) $(DD)sj48 $(sj48_)
+
+$(DEVOBJ)gdevsj48.$(OBJ) : $(DEVSRC)gdevsj48.c $(PDEVH)
+ $(DEVCC) $(DEVO_)gdevsj48.$(OBJ) $(C_) $(DEVSRC)gdevsj48.c
+
+### ----------------- Tektronix 4396d color printer -------------------- ###
+### Note: this driver was contributed by a user: please contact ###
+### Karl Hakimian (hakimian@haney.eecs.wsu.edu) ###
+### if you have questions. ###
+
+t4693d_=$(DEVOBJ)gdev4693.$(OBJ)
+$(DD)t4693d2.dev : $(t4693d_) $(DD)page.dev $(MAKEDIRS)
+ $(SETPDEV) $(DD)t4693d2 $(t4693d_)
+
+$(DD)t4693d4.dev : $(t4693d_) $(DD)page.dev $(MAKEDIRS)
+ $(SETPDEV) $(DD)t4693d4 $(t4693d_)
+
+$(DD)t4693d8.dev : $(t4693d_) $(DD)page.dev $(MAKEDIRS)
+ $(SETPDEV) $(DD)t4693d8 $(t4693d_)
+
+$(DEVOBJ)gdev4693.$(OBJ) : $(DEVSRC)gdev4693.c $(PDEVH) $(MAKEDIRS)
+ $(DEVCC) $(DEVO_)gdev4693.$(OBJ) $(C_) $(DEVSRC)gdev4693.c
+
+### -------------------- Tektronix ink-jet printers -------------------- ###
+### Note: this driver was contributed by a user: please contact ###
+### Karsten Spang (spang@nbivax.nbi.dk) if you have questions. ###
+
+tek4696_=$(DEVOBJ)gdevtknk.$(OBJ)
+$(DD)tek4696.dev : $(tek4696_) $(DD)page.dev $(MAKEDIRS)
+ $(SETPDEV) $(DD)tek4696 $(tek4696_)
+
+$(DEVOBJ)gdevtknk.$(OBJ) : $(DEVSRC)gdevtknk.c $(PDEVH) $(malloc__h) $(MAKEDIRS)
+ $(DEVCC) $(DEVO_)gdevtknk.$(OBJ) $(C_) $(DEVSRC)gdevtknk.c
+
+###### ------------------------- Fax devices ------------------------- ######
+
+### ------------------------- CAPI fax devices -------------------------- ###
+### Note: this driver was contributed by a user: please contact ###
+### Peter Schaefer <peter.schaefer@gmx.de> if you have questions. ###
+
+cfax_=$(DEVOBJ)gdevcfax.$(OBJ)
+
+$(DD)cfax.dev : $(cfax_) $(DD)fax.dev $(MAKEDIRS)
+ $(SETDEV) $(DD)cfax $(cfax_)
+ $(ADDMOD) $(DD)cfax -include $(DD)fax
+
+$(DEVOBJ)gdevcfax.$(OBJ) : $(DEVSRC)gdevcfax.c $(PDEVH)\
+ $(gdevfax_h) $(scfx_h) $(strimpl_h) $(MAKEDIRS)
+ $(DEVCC) $(DEVO_)gdevcfax.$(OBJ) $(C_) $(DEVSRC)gdevcfax.c
+
+### ------------------------- The DigiFAX device ------------------------ ###
+### This driver outputs images in a format suitable for use with ###
+### DigiBoard, Inc.'s DigiFAX software. Use -sDEVICE=dfaxhigh for ###
+### high resolution output, -sDEVICE=dfaxlow for normal output. ###
+### Note: this driver was contributed by a user: please contact ###
+### Rick Richardson (rick@digibd.com) if you have questions. ###
+
+dfax_=$(DEVOBJ)gdevdfax.$(OBJ)
+
+$(DD)dfaxlow.dev : $(dfax_) $(DD)tfax.dev $(MAKEDIRS)
+ $(SETDEV) $(DD)dfaxlow $(dfax_)
+ $(ADDMOD) $(DEVGEN)dfaxlow -include $(DD)tfax
+
+$(DD)dfaxhigh.dev : $(dfax_) $(DD)tfax.dev $(MAKEDIRS)
+ $(SETDEV) $(DD)dfaxhigh $(dfax_)
+ $(ADDMOD) $(DEVGEN)dfaxhigh -include $(DD)tfax
+
+$(DEVOBJ)gdevdfax.$(OBJ) : $(DEVSRC)gdevdfax.c $(PDEVH)\
+ $(gdevfax_h) $(gdevtfax_h) $(scfx_h) $(strimpl_h) $(MAKEDIRS)
+ $(DEVCC) $(DEVO_)gdevdfax.$(OBJ) $(C_) $(DEVSRC)gdevdfax.c
+
+###### --------------------- Raster file formats --------------------- ######
+
+### -------------------- The CIF file format for VLSI ------------------ ###
+### Note: this driver was contributed by a user: please contact ###
+### Frederic Petrot (petrot@masi.ibp.fr) if you have questions. ###
+
+cif_=$(DEVOBJ)gdevcif.$(OBJ)
+$(DD)cif.dev : $(cif_) $(DD)page.dev $(MAKEDIRS)
+ $(SETPDEV) $(DD)cif $(cif_)
+
+$(DEVOBJ)gdevcif.$(OBJ) : $(DEVSRC)gdevcif.c $(PDEVH) $(MAKEDIRS)
+ $(DEVCC) $(DEVO_)gdevcif.$(OBJ) $(C_) $(DEVSRC)gdevcif.c
+
+### ------------------------- Inferno bitmaps -------------------------- ###
+### Note: this driver was contributed by a user: please contact ###
+### Russ Cox <rsc@plan9.bell-labs.com> if you have questions. ###
+
+inferno_=$(DEVOBJ)gdevifno.$(OBJ)
+$(DD)inferno.dev : $(inferno_) $(DD)page.dev $(MAKEDIRS)
+ $(SETPDEV) $(DD)inferno $(inferno_)
+
+$(DEVOBJ)gdevifno.$(OBJ) : $(DEVSRC)gdevifno.c $(PDEVH)\
+ $(gsparam_h) $(MAKEDIRS)
+ $(DEVCC) $(DEVO_)gdevifno.$(OBJ) $(C_) $(DEVSRC)gdevifno.c
+
+### --------------------------- MGR devices ---------------------------- ###
+### Note: these drivers were contributed by a user: please contact ###
+### Carsten Emde (ce@ceag.ch) if you have questions. ###
+
+MGR=$(DEVOBJ)gdevmgr.$(OBJ) $(DEVOBJ)gdevpccm.$(OBJ)
+
+gdevmgr_h= $(DEVSRC)gdevmgr.h
+
+$(DEVOBJ)gdevmgr.$(OBJ) : $(DEVSRC)gdevmgr.c $(PDEVH)\
+ $(gdevmgr_h) $(gdevpccm_h) $(MAKEDIRS)
+ $(DEVCC) $(DEVO_)gdevmgr.$(OBJ) $(C_) $(DEVSRC)gdevmgr.c
+
+$(DD)mgrmono.dev : $(MGR) $(DD)page.dev $(MAKEDIRS)
+ $(SETPDEV) $(DD)mgrmono $(MGR)
+
+$(DD)mgrgray2.dev : $(MGR) $(DD)page.dev $(MAKEDIRS)
+ $(SETPDEV) $(DD)mgrgray2 $(MGR)
+
+$(DD)mgrgray4.dev : $(MGR) $(DD)page.dev $(MAKEDIRS)
+ $(SETPDEV) $(DD)mgrgray4 $(MGR)
+
+$(DD)mgrgray8.dev : $(MGR) $(DD)page.dev $(MAKEDIRS)
+ $(SETPDEV) $(DD)mgrgray8 $(MGR)
+
+$(DD)mgr4.dev : $(MGR) $(DD)page.dev $(MAKEDIRS)
+ $(SETPDEV) $(DD)mgr4 $(MGR)
+
+$(DD)mgr8.dev : $(MGR) $(DD)page.dev $(MAKEDIRS)
+ $(SETPDEV) $(DD)mgr8 $(MGR)
+
+### -------------------------- SGI RGB pixmaps -------------------------- ###
+
+sgirgb_=$(DEVOBJ)gdevsgi.$(OBJ)
+$(DD)sgirgb.dev : $(sgirgb_) $(DD)page.dev $(MAKEDIRS)
+ $(SETPDEV) $(DD)sgirgb $(sgirgb_)
+
+gdevsgi_h=$(DEVSRC)gdevsgi.h
+
+$(DEVOBJ)gdevsgi.$(OBJ) : $(DEVSRC)gdevsgi.c $(PDEVH) $(gdevsgi_h) $(MAKEDIRS)
+ $(DEVCC) $(DEVO_)gdevsgi.$(OBJ) $(C_) $(DEVSRC)gdevsgi.c
+
+### ---------------- Sun raster files ---------------- ###
+
+sunr_=$(DEVOBJ)gdevsunr.$(OBJ)
+
+# Harlequin variant, 1-bit
+$(DD)sunhmono.dev : $(sunr_) $(DD)page.dev $(MAKEDIRS)
+ $(SETPDEV) $(DD)sunhmono $(sunr_)
+
+$(DEVOBJ)gdevsunr.$(OBJ) : $(DEVSRC)gdevsunr.c $(PDEVH) $(MAKEDIRS)
+ $(DEVCC) $(DEVO_)gdevsunr.$(OBJ) $(C_) $(DEVSRC)gdevsunr.c
+
+
+#########################################################################
+### --------------------Japanese printer addons --------------------- ###
+#########################################################################
+
+### These drivers are based on patches on existing device drivers in the
+### src/ directory, therefore they are not in addons/
+
+$(DD)ljet4pjl.dev : $(HPMONO) $(DD)page.dev $(MAKEDIRS)
+ $(SETPDEV) $(DD)ljet4pjl $(HPMONO)
+
+$(DD)lj4dithp.dev : $(cdeskjet_) $(DD)page.dev $(MAKEDIRS)
+ $(SETPDEV) $(DD)lj4dithp $(cdeskjet_)
+
+$(DD)dj505j.dev : $(cdeskjet_) $(MAKEDIRS)
+ $(SETPDEV) $(DD)dj505j $(cdeskjet_)
+
+$(DD)picty180.dev : $(cdeskjet_) $(MAKEDIRS)
+ $(SETPDEV) $(DD)picty180 $(cdeskjet_)
+
+#########################################################################
diff --git a/devices/devs.mak b/devices/devs.mak
new file mode 100644
index 000000000..ef210571e
--- /dev/null
+++ b/devices/devs.mak
@@ -0,0 +1,2058 @@
+# Copyright (C) 2001-2012 Artifex Software, Inc.
+# All Rights Reserved.
+#
+# This software is provided AS-IS with no warranty, either express or
+# implied.
+#
+# This software is distributed under license and may not be copied,
+# modified or distributed except as expressly authorized under the terms
+# of the license contained in the file LICENSE in this distribution.
+#
+# Refer to licensing information at http://www.artifex.com or contact
+# Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+# CA 94903, U.S.A., +1(415)492-9861, for further information.
+#
+# makefile for Artifex's device drivers.
+
+# Define the name of this makefile.
+DEVS_MAK=$(DEVSRC)devs.mak
+
+DEVSRC=$(DEVSRCDIR)$(D)
+DEVVEC=$(DEVSRC)vector
+DEVVECSRC=$(DEVVEC)$(D)
+
+DEVI_=$(DEVGENDIR) $(II)$(GLSRCDIR) $(II)$(GLGENDIR) $(II)$(DEVSRCDIR)
+DEVF_=
+
+DEVCCFLAGS=$(I_)$(DEVI_)$(_I) $(I_)$(DEVVEC)$(_I) $(DEVF_)
+DEVCC=$(CC_) $(DEVCCFLAGS)
+XPSDEVCC=$(CC_) $(XPSPRINTCFLAGS) $(DEVCCFLAGS)
+
+DEVJCC=$(GLJCC)
+DEVCCSHARED=$(GLCCSHARED)
+
+# All device drivers depend on the following:
+GDEVH=$(gserrors_h) $(gx_h) $(gxdevice_h)
+GDEV=$(AK) $(ECHOGS_XE) $(GDEVH)
+
+DEVOBJ=$(DEVOBJDIR)$(D)
+DEVO_=$(O_)$(DEVOBJ)
+
+DEVGEN=$(DEVGENDIR)$(D)
+
+###### --------------------------- Overview -------------------------- ######
+
+# It is possible to build Ghostscript with an arbitrary collection of device
+# drivers, although some drivers are supported only on a subset of the
+# target platforms.
+
+# The catalog in this file, devs.mak, lists all the drivers that were
+# written by Artifex, or by people working closely with Artifex, and for
+# which Artifex is willing to take problem reports (although since
+# Ghostscript is provided with NO WARRANTY and NO SUPPORT, we can't promise
+# that we'll solve your problem). Another file, contrib.mak, lists all the
+# drivers contributed by other people that are distributed by Artifex with
+# Ghostscript. Note in particular that all drivers for color inkjets and
+# other non-PostScript-capable color printers are in contrib.mak.
+
+# If you haven't configured Ghostscript before, or if you want to add a
+# driver that that isn't included in the catalogs (for which you have the
+# source code), we suggest you skip to the "End of catalog" below and read
+# the documentation there before continuing.
+
+###### --------------------------- Catalog -------------------------- ######
+
+# MS-DOS displays (note: not usable with Desqview/X):
+# MS-DOS EGA and VGA:
+# ega EGA (640x350, 16-color)
+# vga VGA (640x480, 16-color)
+# MS-DOS SuperVGA:
+# * ali SuperVGA using Avance Logic Inc. chipset, 256-color modes
+# * atiw ATI Wonder SuperVGA, 256-color modes
+# * cirr SuperVGA using Cirrus Logic CL-GD54XX chips, 256-color modes
+# * s3vga SuperVGA using S3 86C911 chip (e.g., Diamond Stealth board)
+# svga16 Generic SuperVGA in 800x600, 16-color mode
+# * tseng SuperVGA using Tseng Labs ET3000/4000 chips, 256-color modes
+# * tvga SuperVGA using Trident chipset, 256-color modes
+# ****** NOTE: The vesa device does not work with the Watcom (32-bit MS-DOS)
+# ****** compiler or executable.
+# vesa SuperVGA with VESA standard API driver
+# Other displays:
+# display For use on any platform that supports DLLs
+# MS Windows:
+# mswindll Microsoft Windows 3.1 DLL [MS Windows only]
+# mswinprn Microsoft Windows 3.0, 3.1 DDB printer [MS Windows only]
+# mswinpr2 Microsoft Windows 3.0, 3.1 DIB printer [MS Windows only]
+# OS/2:
+# * os2prn OS/2 printer [OS/2 only]
+# Unix and VMS:
+# ****** NOTE: For direct frame buffer addressing under SCO Unix or Xenix,
+# ****** edit the definition of EGAVGA below.
+# * lvga256 Linux vgalib, 256-color VGA modes [Linux only]
+# + vgalib Linux vgalib, 16-color VGA modes [Linux only]
+# x11 X Windows version 11, release >=4 [Unix and VMS only]
+# x11alpha X Windows masquerading as a device with alpha capability
+# x11cmyk X Windows masquerading as a 1-bit-per-plane CMYK device
+# x11cmyk2 X Windows as a 2-bit-per-plane CMYK device
+# x11cmyk4 X Windows as a 4-bit-per-plane CMYK device
+# x11cmyk8 X Windows as an 8-bit-per-plane CMYK device
+# x11gray2 X Windows as a 2-bit gray-scale device
+# x11gray4 X Windows as a 4-bit gray-scale device
+# x11mono X Windows masquerading as a black-and-white device
+# x11rg16x X Windows with G5/B5/R6 pixel layout for testing.
+# x11rg32x X Windows with G11/B10/R11 pixel layout for testing.
+# Printers:
+# + atx23 Practical Automation ATX-23 label printer
+# + atx24 Practical Automation ATX-24 label printer
+# + atx38 Practical Automation ATX-38 label printer
+# + itk24i Practical Automation ITK-24i thermal kiosk printer
+# + itk38 Practical Automation ITK-38 thermal kiosk printer
+# + deskjet H-P DeskJet and DeskJet Plus
+# djet500 H-P DeskJet 500; use -r600 for DJ 600 series
+# + fs600 Kyocera FS-600 (600 dpi)
+# + laserjet H-P LaserJet
+# + ljet2p H-P LaserJet IId/IIp/III* with TIFF compression
+# + ljet3 H-P LaserJet III* with Delta Row compression
+# + ljet3d H-P LaserJet IIID with duplex capability
+# + ljet4 H-P LaserJet 4 (defaults to 600 dpi)
+# + ljet4d H-P LaserJet 4 (defaults to 600 dpi) with duplex
+# + ljetplus H-P LaserJet Plus
+# lj5mono H-P LaserJet 5 & 6 family (PCL XL), bitmap:
+# see below for restrictions & advice
+# lj5gray H-P LaserJet 5 & 6 family, gray-scale bitmap;
+# see below for restrictions & advice
+# * lp2563 H-P 2563B line printer
+# * oce9050 OCE 9050 printer
+# (pxlmono) H-P black-and-white PCL XL printers (LaserJet 5 and 6 family)
+# (pxlcolor) H-P color PCL XL printers (e.g. Color LaserJet 4500)
+# Fax file format:
+# ****** NOTE: all of these drivers normally adjust the page size to match
+# ****** one of the three CCITT standard sizes (U.S. letter with A4 width,
+# ****** A4, or B4). To suppress this, use -dAdjustWidth=0.
+# faxg3 Group 3 fax, with EOLs but no header or EOD
+# faxg32d Group 3 2-D fax, with EOLs but no header or EOD
+# faxg4 Group 4 fax, with EOLs but no header or EOD
+# tiffcrle TIFF "CCITT RLE 1-dim" (= Group 3 fax with no EOLs)
+# tiffg3 TIFF Group 3 fax (with EOLs)
+# tiffg32d TIFF Group 3 2-D fax
+# tiffg4 TIFF Group 4 fax
+# High-level file formats:
+# pdfwrite PDF output (like Adobe Acrobat Distiller)
+# txtwrite ASCII or Unicode text output
+# pxlmono Black-and-white PCL XL
+# pxlcolor Color PCL XL
+# Other raster file formats and devices:
+# bit Plain bits, monochrome
+# bitrgb Plain bits, RGB
+# bitcmyk Plain bits, CMYK
+# bmpmono Monochrome MS Windows .BMP file format
+# bmpgray 8-bit gray .BMP file format
+# bmpsep1 Separated 1-bit CMYK .BMP file format, primarily for testing
+# bmpsep8 Separated 8-bit CMYK .BMP file format, primarily for testing
+# bmp16 4-bit (EGA/VGA) .BMP file format
+# bmp256 8-bit (256-color) .BMP file format
+# bmp16m 24-bit .BMP file format
+# bmp32b 32-bit pseudo-.BMP file format
+# jpeg JPEG format, RGB output
+# jpeggray JPEG format, gray output
+# jpegcmyk JPEG format, cmyk output
+# miff24 ImageMagick MIFF format, 24-bit direct color, RLE compressed
+# pamcmyk4 Portable Arbitrary Map file format 4-bit CMYK
+# pamcmyk32 Portable Arbitrary Map file format 32-bit CMYK
+# pcxmono PCX file format, monochrome (1-bit black and white)
+# pcxgray PCX file format, 8-bit gray scale
+# pcx16 PCX file format, 4-bit planar (EGA/VGA) color
+# pcx256 PCX file format, 8-bit chunky color
+# pcx24b PCX file format, 24-bit color (3 8-bit planes)
+# pcxcmyk PCX file format, 4-bit chunky CMYK color
+# pbm Portable Bitmap (plain format)
+# pbmraw Portable Bitmap (raw format)
+# pgm Portable Graymap (plain format)
+# pgmraw Portable Graymap (raw format)
+# pgnm Portable Graymap (plain format), optimizing to PBM if possible
+# pgnmraw Portable Graymap (raw format), optimizing to PBM if possible
+# pnm Portable Pixmap (plain format) (RGB), optimizing to PGM or PBM
+# if possible
+# pnmraw Portable Pixmap (raw format) (RGB), optimizing to PGM or PBM
+# if possible
+# pnmcmyk PAM 32-bit CMYK if colors, otherwise pgmraw.
+# ppm Portable Pixmap (plain format) (RGB)
+# ppmraw Portable Pixmap (raw format) (RGB)
+# pkm Portable inKmap (plain format) (4-bit CMYK => RGB)
+# pkmraw Portable inKmap (raw format) (4-bit CMYK => RGB)
+# pksm Portable Separated map (plain format) (4-bit CMYK => 4 pages)
+# pksmraw Portable Separated map (raw format) (4-bit CMYK => 4 pages)
+# * plan9bm Plan 9 bitmap format
+# plan PLANar device (24 bit RGB)
+# planm PLANar device (1 bit Mono)
+# plang PLANar device (8 bit Gray)
+# planc PLANar device (32 bit CMYK)
+# plank PLANar device (4 bit CMYK)
+# plib PLanar Interleaved Band buffer device (24 bit RGB)
+# plibm PLanar Interleaved Band buffer device (1 bit Mono)
+# plibg PLanar Interleaved Band buffer device (8 bit Gray)
+# plibc PLanar Interleaved Band buffer device (32 bit CMYK)
+# plibk PLanar Interleaved Band buffer device (4 bit CMYK)
+# pngmono Monochrome Portable Network Graphics (PNG)
+# pngmonod Monochrome (error diffused) Portable Network Graphics (PNG)
+# pnggray 8-bit gray Portable Network Graphics (PNG)
+# png16 4-bit color Portable Network Graphics (PNG)
+# png256 8-bit color Portable Network Graphics (PNG)
+# png16m 24-bit color Portable Network Graphics (PNG)
+# pngalpha 32-bit RGBA color Portable Network Graphics (PNG)
+# tiffgray TIFF 8-bit gray, no compression
+# tiff12nc TIFF 12-bit RGB, no compression
+# tiff24nc TIFF 24-bit RGB, no compression (NeXT standard format)
+# tiff48nc TIFF 48-bit RGB, no compression
+# tiff32nc TIFF 32-bit CMYK
+# tiff64nc TIFF 64-bit CMYK
+# tiffsep Creates tiffgray for each colorant plus a CMYK composite
+# tiffsep1 Creates halftoned tiff 1-bit per pixel for each colorant
+# tifflzw TIFF LZW (tag = 5) (monochrome)
+# tiffpack TIFF PackBits (tag = 32773) (monochrome)
+# tiffscaled TIFF (monochrome output, integer downsampled and dithered from grayscale rendering)
+# tiffscaled8 TIFF (greyscale output, integer downsampled and dithered from grayscale rendering)
+# tiffscaled24 TIFF (rgb output, integer downsampled and dithered from rgb rendering)
+# tiffscaled32 TIFF (cmyk output, integer downsampled and dithered from cmyk rendering)
+# tiffscaled4 TIFF (cmyk output, integer downsampled and dithered from cmyk rendering)
+
+# Note that MS Windows-specific drivers are defined in pcwin.mak, not here,
+# because they have special compilation requirements that require defining
+# parameter macros not relevant to other platforms; the OS/2-specific
+# drivers are there too, because they share some definitions.
+
+# User-contributed drivers marked with * require hardware or software
+# that is not available to Artifex Software Inc. Please contact the
+# original contributors, not Artifex Software Inc, if you have questions.
+# Contact information appears in the driver entry below.
+#
+# Drivers marked with a + are maintained by Artifex Software Inc with
+# the assistance of users, since Artifex Software Inc doesn't have access to
+# the hardware for these either.
+
+# If you add drivers, it would be nice if you kept each list
+# in alphabetical order.
+
+###### ----------------------- End of catalog ----------------------- ######
+
+# As noted in gs.mak, DEVICE_DEVS and DEVICE_DEVS1..15 select the devices
+# that should be included in a given configuration. By convention, these
+# are used as follows. Each of these must be limited to about 6 devices
+# so as not to overflow the 120 character limit on MS-DOS command lines.
+# DEVICE_DEVS - the default device, and any display devices.
+# DEVICE_DEVS1 - additional display devices if needed.
+# DEVICE_DEVS2 - dot matrix printers.
+# DEVICE_DEVS3 - H-P monochrome printers.
+# DEVICE_DEVS4 - H-P color printers.
+# DEVICE_DEVS5 - additional inkjet printers if needed.
+# DEVICE_DEVS6 - other ink-jet and laser printers.
+# DEVICE_DEVS7 - fax file formats.
+# DEVICE_DEVS8 - PCX file formats.
+# DEVICE_DEVS9 - PBM/PGM/PPM file formats.
+# DEVICE_DEVS10 - black-and-white TIFF file formats.
+# DEVICE_DEVS11 - BMP and color TIFF file formats.
+# DEVICE_DEVS12 - PostScript image and 'bit' file formats.
+# DEVICE_DEVS13 - PNG file formats.
+# DEVICE_DEVS14 - CGM, JPEG, and MIFF file formats.
+# DEVICE_DEVS15 - high-level (PostScript and PDF) file formats.
+# DEVICE_DEVS16 - additional high-level and utility drivers
+# DEVICE_DEVS17 - (overflow for PC platforms)
+# DEVICE_DEVS18 - (ditto)
+# DEVICE_DEVS19 - (ditto)
+# DEVICE_DEVS20 - (ditto)
+# Feel free to disregard this convention if it gets in your way.
+
+# If you want to add a new device driver, the examples below should be
+# enough of a guide to the correct form for the makefile rules.
+# Note that all drivers other than displays must include page.dev in their
+# dependencies and use $(SETPDEV) rather than $(SETDEV) in their rule bodies.
+
+# "Printer" drivers depend on the following:
+PDEVH=$(AK) $(gdevprn_h)
+
+gxfcopy_h=$(DEVSRC)gxfcopy.h $(gsccode_h)
+
+# Define the header files for device drivers. Every header file used by
+# more than one device driver family must be listed here.
+gdev8bcm_h=$(DEVSRC)gdev8bcm.h
+gdevcbjc_h=$(DEVSRC)gdevcbjc.h $(stream_h)
+
+gdevpcfb_h=$(DEVSRC)gdevpcfb.h $(dos__h)
+gdevpcl_h=$(DEVSRC)gdevpcl.h
+gdevpsu_h=$(DEVVECSRC)gdevpsu.h
+gdevsvga_h=$(DEVSRC)gdevsvga.h
+# Out of order
+gdevdljm_h=$(DEVSRC)gdevdljm.h $(gdevpcl_h)
+
+GDEVLDFJB2CC=$(CC_) $(I_)$(DEVI_) $(II)$(LDF_JB2I_)$(_I) $(JB2CF_) $(GLF_)
+GDEVLWFJPXCC=$(CC_) $(I_)$(DEVI_) $(II)$(LWF_JPXI_)$(_I) $(JPXCF_) $(GLF_)
+GDEVLWFJB2JPXCC=$(CC_) $(I_)$(DEVI_) $(II)$(LDF_JB2I_) $(II)$(LWF_JPXI_)$(_I) $(JB2CF_) $(JPXCF_) $(GLF_)
+
+###### ----------------------- Device support ----------------------- ######
+
+# Implement dynamic color management for 8-bit mapped color displays.
+$(DEVOBJ)gdev8bcm.$(OBJ) : $(DEVSRC)gdev8bcm.c $(AK)\
+ $(gx_h) $(gxdevice_h) $(gdev8bcm_h) $(MAKEDIRS)
+ $(DEVCC) $(DEVO_)gdev8bcm.$(OBJ) $(C_) $(DEVSRC)gdev8bcm.c
+
+# Generate Canon BJC command sequences.
+$(DEVOBJ)gdevcbjc.$(OBJ) : $(DEVSRC)gdevcbjc.c $(AK)\
+ $(std_h) $(stream_h) $(gdevcbjc_h) $(MAKEDIRS)
+ $(DEVCC) $(DEVO_)gdevcbjc.$(OBJ) $(C_) $(DEVSRC)gdevcbjc.c
+
+# Support for writing PostScript (high- or low-level).
+$(DEVOBJ)gdevpsu.$(OBJ) : $(DEVVECSRC)gdevpsu.c $(GX) $(GDEV) $(math__h) $(time__h)\
+ $(stat__h) $(unistd__h)\
+ $(gdevpsu_h) $(gscdefs_h) $(gxdevice_h)\
+ $(spprint_h) $(stream_h) $(MAKEDIRS)
+ $(DEVCC) $(DEVO_)gdevpsu.$(OBJ) $(C_) $(DEVVECSRC)gdevpsu.c
+
+###### ------------------- MS-DOS display devices ------------------- ######
+
+# There are really only three drivers: an EGA/VGA driver (4 bit-planes,
+# plane-addressed), a SuperVGA driver (8 bit-planes, byte addressed),
+# and a special driver for the S3 chip.
+
+### ----------------------- EGA and VGA displays ----------------------- ###
+
+EGAVGA_DOS=$(DEVOBJ)gdevevga.$(OBJ) $(DEVOBJ)gdevpcfb.$(OBJ) $(DEVOBJ)gdevpccm.$(OBJ)
+EGAVGA_SCO=$(DEVOBJ)gdevsco.$(OBJ) $(DEVOBJ)gdevpcfb.$(OBJ) $(DEVOBJ)gdevpccm.$(OBJ)
+# NOTE: for direct frame buffer addressing under SCO Unix or Xenix,
+# change DOS to SCO in the following line.
+EGAVGA=$(EGAVGA_DOS)
+
+#**************** $(CCD) gdevevga.c
+$(DEVOBJ)gdevevga.$(OBJ) : $(DEVSRC)gdevevga.c $(GDEV) $(memory__h) $(gdevpcfb_h) \
+ $(MAKEDIRS)
+ $(DEVCC) $(DEVO_)gdevevga.$(OBJ) $(C_) $(DEVSRC)gdevevga.c
+
+$(DEVOBJ)gdevsco.$(OBJ) : $(DEVSRC)gdevsco.c $(GDEV) $(memory__h) $(gdevpcfb_h) \
+ $(MAKEDIRS)
+ $(DEVCC) $(DEVO_)gdevsco.$(OBJ) $(C_) $(DEVSRC)gdevsco.c
+
+# Common code for MS-DOS and SCO.
+#**************** $(CCD) gdevpcfb.c
+$(DEVOBJ)gdevpcfb.$(OBJ) : $(DEVSRC)gdevpcfb.c $(GDEV) $(memory__h)\
+ $(gdevpccm_h) $(gdevpcfb_h) $(gsparam_h) $(MAKEDIRS)
+ $(DEVCC) $(DEVO_)gdevpcfb.$(OBJ) $(C_) $(DEVSRC)gdevpcfb.c
+
+# The EGA/VGA family includes EGA and VGA. Many SuperVGAs in 800x600,
+# 16-color mode can share the same code; see the next section below.
+$(DD)ega.dev : $(DEVS_MAK) $(EGAVGA) $(GDEV) $(MAKEDIRS)
+ $(SETDEV) $(DD)ega $(EGAVGA)
+
+$(DD)vga.dev : $(DEVS_MAK) $(EGAVGA) $(GDEV) $(MAKEDIRS)
+ $(SETDEV) $(DD)vga $(EGAVGA)
+
+### ------------------------- SuperVGA displays ------------------------ ###
+
+# SuperVGA displays in 16-color, 800x600 mode are really just slightly
+# glorified VGA's, so we can handle them all with a single driver.
+# The way to select them on the command line is with
+# -sDEVICE=svga16 -dDisplayMode=NNN
+# where NNN is the display mode in decimal. See Use.htm for the modes
+# for some popular display chipsets.
+
+$(DD)svga16.dev : $(DEVS_MAK) $(EGAVGA) $(GDEV) $(MAKEDIRS)
+ $(SETDEV) $(DD)svga16 $(EGAVGA)
+
+# More capable SuperVGAs have a wide variety of slightly differing
+# interfaces, so we need a separate driver for each one.
+
+SVGA=$(DEVOBJ)gdevsvga.$(OBJ) $(DEVOBJ)gdevpccm.$(OBJ)
+
+#**************** $(CCD) gdevsvga.c
+$(DEVOBJ)gdevsvga.$(OBJ) : $(DEVSRC)gdevsvga.c $(GDEV) $(memory__h)\
+ $(gsparam_h) $(gxarith_h) $(gdevpccm_h) $(gdevpcfb_h) $(gdevsvga_h) \
+ $(MAKEDIRS)
+ $(DEVCC) $(DEVO_)gdevsvga.$(OBJ) $(C_) $(DEVSRC)gdevsvga.c
+
+# The SuperVGA family includes: Avance Logic Inc., ATI Wonder, S3,
+# Trident, Tseng ET3000/4000, and VESA.
+
+$(DD)ali.dev : $(DEVS_MAK) $(SVGA) $(GDEV) $(MAKEDIRS)
+ $(SETDEV) $(DD)ali $(SVGA)
+
+$(DD)atiw.dev : $(DEVS_MAK) $(SVGA) $(GDEV) $(MAKEDIRS)
+ $(SETDEV) $(DD)atiw $(SVGA)
+
+$(DD)cirr.dev : $(DEVS_MAK) $(SVGA) $(GDEV) $(MAKEDIRS)
+ $(SETDEV) $(DD)cirr $(SVGA)
+
+$(DD)tseng.dev : $(DEVS_MAK) $(SVGA) $(GDEV) $(MAKEDIRS)
+ $(SETDEV) $(DD)tseng $(SVGA)
+
+$(DD)tvga.dev : $(DEVS_MAK) $(SVGA) $(GDEV) $(MAKEDIRS)
+ $(SETDEV) $(DD)tvga $(SVGA)
+
+$(DD)vesa.dev : $(DEVS_MAK) $(SVGA) $(GDEV) $(MAKEDIRS)
+ $(SETDEV) $(DD)vesa $(SVGA)
+
+# The S3 driver doesn't share much code with the others.
+
+s3vga_=$(DEVOBJ)gdevs3ga.$(OBJ) $(DEVOBJ)gdevsvga.$(OBJ) $(DEVOBJ)gdevpccm.$(OBJ)
+$(DD)s3vga.dev : $(DEVS_MAK) $(SVGA) $(s3vga_) $(GDEV) $(MAKEDIRS)
+ $(SETDEV) $(DD)s3vga $(SVGA)
+ $(ADDMOD) $(DD)s3vga -obj $(s3vga_)
+
+#**************** $(CCD) gdevs3ga.c
+$(DEVOBJ)gdevs3ga.$(OBJ) : $(DEVSRC)gdevs3ga.c $(GDEV) $(gdevpcfb_h) $(gdevsvga_h) \
+ $(MAKEDIRS)
+ $(DEVCC) $(DEVO_)gdevs3ga.$(OBJ) $(C_) $(DEVSRC)gdevs3ga.c
+
+###### ----------------------- Other displays ------------------------ ######
+
+### ------------------ Display device for DLL platforms ----------------- ###
+
+display_=$(DEVOBJ)gdevdsp.$(OBJ) $(DEVOBJ)gdevpccm.$(OBJ) $(GLOBJ)gdevdevn.$(OBJ) \
+ $(GLOBJ)gsequivc.$(OBJ) $(DEVOBJ)gdevdcrd.$(OBJ)
+$(DD)display.dev : $(display_) $(GDEV) $(MAKEDIRS)
+ $(SETDEV) $(DD)display $(display_)
+
+$(DEVOBJ)gdevdsp.$(OBJ) : $(DEVSRC)gdevdsp.c $(string__h)\
+ $(gp_h) $(gpcheck_h) $(gdevpccm_h) $(gsparam_h) $(gsdevice_h)\
+ $(GDEVH) $(gxdevmem_h) $(gdevdevn_h) $(gsequivc_h) $(gdevdsp_h) $(gdevdsp2_h) \
+ $(MAKEDIRS)
+ $(DEVCC) $(DEVO_)gdevdsp.$(OBJ) $(C_) $(DEVSRC)gdevdsp.c
+
+
+### ---------------------- Linux PC with vgalib ------------------------- ###
+### Note: these drivers were contributed by users. ###
+### For questions about the lvga256 driver, please contact ###
+### Ludger Kunz (ludger.kunz@fernuni-hagen.de). ###
+### For questions about the vgalib driver, please contact ###
+### Erik Talvola (talvola@gnu.ai.mit.edu). ###
+### Note that the vgalib device supports only 16-color VGA modes. ###
+
+lvga256_=$(DEVOBJ)gdevl256.$(OBJ)
+$(DD)lvga256.dev : $(DEVS_MAK) $(lvga256_) $(GDEV) $(MAKEDIRS)
+ $(SETDEV) $(DD)lvga256 $(lvga256_)
+ $(ADDMOD) $(DD)lvga256 -lib vga vgagl
+
+$(DEVOBJ)gdevl256.$(OBJ) : $(DEVSRC)gdevl256.c $(GDEV) $(memory__h) $(MAKEDIRS)
+ $(DEVCCSHARED) $(DEVO_)gdevl256.$(OBJ) $(C_) $(DEVSRC)gdevl256.c
+
+vgalib_=$(DEVOBJ)gdevvglb.$(OBJ) $(DEVOBJ)gdevpccm.$(OBJ)
+$(DD)vgalib.dev : $(DEVS_MAK) $(vgalib_) $(GDEV) $(MAKEDIRS)
+ $(SETDEV2) $(DD)vgalib $(vgalib_)
+ $(ADDMOD) $(DD)vgalib -lib vga
+
+$(DEVOBJ)gdevvglb.$(OBJ) : $(DEVSRC)gdevvglb.c $(GDEV) $(gdevpccm_h) $(gsparam_h) \
+ $(MAKEDIRS)
+ $(DEVCCSHARED) $(DEVO_)gdevvglb.$(OBJ) $(C_) $(DEVSRC)gdevvglb.c
+
+### Shared library object supporting vgalib.
+### NON PORTABLE, ONLY UNIX WITH GCC SUPPORT
+
+$(DEVOBJ)lvga256.so : $(lvga256_) $(MAKEDIRS)
+ $(CCLD) $(LDFLAGS) -shared -o $(DEVOBJ)lvga256.so $(lvga256_) -lvga -lvgagl
+
+$(DEVOBJ)vgalib.so : $(vgalib_) $(MAKEDIRS)
+ $(CCLD) $(LDFLAGS) -shared -o $(DEVOBJ)vgalib.so $(vgalib_) -lvga -lvgagl
+
+### -------------------------- The X11 device -------------------------- ###
+
+# Please note that Artifex Software Inc does not support Ghostview.
+# For more information about Ghostview, please contact Tim Theisen
+# (ghostview@cs.wisc.edu).
+
+gdevxcmp_h=$(DEVSRC)gdevxcmp.h
+gdevx_h=$(DEVSRC)gdevx.h $(gdevbbox_h) $(gdevxcmp_h)
+
+# See the main makefile for the definition of XLIBDIRS and XLIBS.
+x11_=$(DEVOBJ)gdevx.$(OBJ) $(DEVOBJ)gdevxcmp.$(OBJ) $(DEVOBJ)gdevxini.$(OBJ)\
+ $(DEVOBJ)gdevxres.$(OBJ) $(DEVOBJ)gsparamx.$(OBJ)
+$(DD)x11_.dev : $(DEVS_MAK) $(x11_) $(GLD)bboxutil.dev $(GDEV) $(MAKEDIRS)
+ $(SETMOD) $(DD)x11_ $(x11_)
+ $(ADDMOD) $(DD)x11_ -link $(XLIBDIRS)
+ $(ADDMOD) $(DD)x11_ -lib $(XLIBS)
+ $(ADDMOD) $(DD)x11_ -include $(GLD)bboxutil
+
+$(DD)x11.dev : $(DEVS_MAK) $(DD)x11_.dev $(GDEV) $(MAKEDIRS)
+ $(SETDEV2) $(DD)x11 -include $(DD)x11_
+
+# See the main makefile for the definition of XINCLUDE.
+GDEVX=$(GDEV) $(x__h) $(gdevx_h) $(TOP_MAKEFILES)
+$(DEVOBJ)gdevx.$(OBJ) : $(DEVSRC)gdevx.c $(GDEVX) $(math__h) $(memory__h)\
+ $(gscoord_h) $(gsdevice_h) $(gsiparm2_h) $(gsmatrix_h) $(gsparam_h)\
+ $(gxdevmem_h) $(gxgetbit_h) $(gxiparam_h) $(gxpath_h) $(MAKEDIRS)
+ $(DEVCCSHARED) $(XINCLUDE) $(DEVO_)gdevx.$(OBJ) $(C_) $(DEVSRC)gdevx.c
+
+$(DEVOBJ)gdevxcmp.$(OBJ) : $(DEVSRC)gdevxcmp.c $(GDEVX) $(math__h) \
+ $(MAKEDIRS)
+ $(DEVCCSHARED) $(XINCLUDE) $(DEVO_)gdevxcmp.$(OBJ) $(C_) $(DEVSRC)gdevxcmp.c
+
+$(DEVOBJ)gdevxini.$(OBJ) : $(DEVSRC)gdevxini.c $(GDEVX) $(memory__h)\
+ $(gserrors_h) $(gsparamx_h) $(gxdevmem_h) $(gdevbbox_h) $(MAKEDIRS)
+ $(DEVCCSHARED) $(XINCLUDE) $(DEVO_)gdevxini.$(OBJ) $(C_) $(DEVSRC)gdevxini.c
+
+# We have to compile gdevxres without warnings, because there is a
+# const/non-const cast required by the X headers that we can't work around.
+$(DEVOBJ)gdevxres.$(OBJ) : $(DEVSRC)gdevxres.c $(std_h) $(x__h)\
+ $(gsmemory_h) $(gstypes_h) $(gxdevice_h) $(gdevx_h) $(MAKEDIRS)
+ $(CC_NO_WARN) $(GLCCFLAGS) $(XINCLUDE) $(DEVO_)gdevxres.$(OBJ) $(C_) $(DEVSRC)gdevxres.c
+
+# Alternate X11-based devices to help debug other drivers.
+# x11alpha pretends to have 4 bits of alpha channel.
+# x11cmyk pretends to be a CMYK device with 1 bit each of C,M,Y,K.
+# x11cmyk2 pretends to be a CMYK device with 2 bits each of C,M,Y,K.
+# x11cmyk4 pretends to be a CMYK device with 4 bits each of C,M,Y,K.
+# x11cmyk8 pretends to be a CMYK device with 8 bits each of C,M,Y,K.
+# x11gray2 pretends to be a 2-bit gray-scale device.
+# x11gray4 pretends to be a 4-bit gray-scale device.
+# x11mono pretends to be a black-and-white device.
+# x11rg16x pretends to be a G5/B5/R6 color device.
+# x11rg16x pretends to be a G11/B10/R11 color device.
+x11alt_=$(DEVOBJ)gdevxalt.$(OBJ)
+$(DD)x11alt_.dev : $(DEVS_MAK) $(x11alt_) $(DD)x11_.dev $(GDEV) $(MAKEDIRS)
+ $(SETMOD) $(DD)x11alt_ $(x11alt_)
+ $(ADDMOD) $(DD)x11alt_ -include $(DD)x11_
+
+$(DD)x11alpha.dev : $(DEVS_MAK) $(DD)x11alt_.dev $(GDEV) $(MAKEDIRS)
+ $(SETDEV2) $(DD)x11alpha -include $(DD)x11alt_
+
+$(DD)x11cmyk.dev : $(DEVS_MAK) $(DD)x11alt_.dev $(GDEV) $(MAKEDIRS)
+ $(SETDEV2) $(DD)x11cmyk -include $(DD)x11alt_
+
+$(DD)x11cmyk2.dev : $(DEVS_MAK) $(DD)x11alt_.dev $(GDEV) $(MAKEDIRS)
+ $(SETDEV2) $(DD)x11cmyk2 -include $(DD)x11alt_
+
+$(DD)x11cmyk4.dev : $(DEVS_MAK) $(DD)x11alt_.dev $(GDEV) $(MAKEDIRS)
+ $(SETDEV2) $(DD)x11cmyk4 -include $(DD)x11alt_
+
+$(DD)x11cmyk8.dev : $(DEVS_MAK) $(DD)x11alt_.dev $(GDEV) $(MAKEDIRS)
+ $(SETDEV2) $(DD)x11cmyk8 -include $(DD)x11alt_
+
+$(DD)x11gray2.dev : $(DEVS_MAK) $(DD)x11alt_.dev $(GDEV) $(MAKEDIRS)
+ $(SETDEV2) $(DD)x11gray2 -include $(DD)x11alt_
+
+$(DD)x11gray4.dev : $(DEVS_MAK) $(DD)x11alt_.dev $(GDEV) $(MAKEDIRS)
+ $(SETDEV2) $(DD)x11gray4 -include $(DD)x11alt_
+
+$(DD)x11mono.dev : $(DEVS_MAK) $(DD)x11alt_.dev $(GDEV) $(MAKEDIRS)
+ $(SETDEV2) $(DD)x11mono -include $(DD)x11alt_
+
+$(DD)x11rg16x.dev : $(DEVS_MAK) $(DD)x11alt_.dev $(GDEV) $(MAKEDIRS)
+ $(SETDEV2) $(DD)x11rg16x -include $(DD)x11alt_
+
+$(DD)x11rg32x.dev : $(DEVS_MAK) $(DD)x11alt_.dev $(GDEV) $(MAKEDIRS)
+ $(SETDEV2) $(DD)x11rg32x -include $(DD)x11alt_
+
+$(DEVOBJ)gdevxalt.$(OBJ) : $(DEVSRC)gdevxalt.c $(GDEVX) $(math__h) $(memory__h)\
+ $(gsdevice_h) $(gsparam_h) $(gsstruct_h) $(GDEV) $(MAKEDIRS)
+ $(DEVCCSHARED) $(XINCLUDE) $(DEVO_)gdevxalt.$(OBJ) $(C_) $(DEVSRC)gdevxalt.c
+
+### Shared library object supporting X11.
+### NON PORTABLE, ONLY UNIX WITH GCC SUPPORT
+
+$(DEVOBJ)X11.so : $(x11alt_) $(x11_) $(MAKEDIRS)
+ $(CCLD) $(LDFLAGS) -shared -o $(DEVOBJ)X11.so $(x11alt_) $(x11_) -L/usr/X11R6/lib -lXt -lSM -lICE -lXext -lX11 $(XLIBDIRS)
+
+###### --------------- Memory-buffered printer devices --------------- ######
+
+### ---------------- Practical Automation label printers ---------------- ###
+
+atx_=$(DEVOBJ)gdevatx.$(OBJ)
+
+$(DD)atx23.dev : $(DEVS_MAK) $(atx_) $(GLD)page.dev $(GDEV) $(MAKEDIRS)
+ $(SETPDEV2) $(DD)atx23 $(atx_)
+
+$(DD)atx24.dev : $(DEVS_MAK) $(atx_) $(GLD)page.dev $(GDEV) $(MAKEDIRS)
+ $(SETPDEV2) $(DD)atx24 $(atx_)
+
+$(DD)atx38.dev : $(DEVS_MAK) $(atx_) $(GLD)page.dev $(GDEV) $(MAKEDIRS)
+ $(SETPDEV2) $(DD)atx38 $(atx_)
+
+$(DD)itk24i.dev : $(DEVS_MAK) $(atx_) $(GLD)page.dev $(GDEV) $(MAKEDIRS)
+ $(SETPDEV2) $(DD)itk24i $(atx_)
+
+$(DD)itk38.dev : $(DEVS_MAK) $(atx_) $(GLD)page.dev $(GDEV) $(MAKEDIRS)
+ $(SETPDEV2) $(DD)itk38 $(atx_)
+
+$(DEVOBJ)gdevatx.$(OBJ) : $(DEVSRC)gdevatx.c $(PDEVH) $(math__h) $(GDEV) \
+ $(MAKEDIRS)
+ $(DEVCC) $(DEVO_)gdevatx.$(OBJ) $(C_) $(DEVSRC)gdevatx.c
+
+### ----------- The H-P DeskJet and LaserJet printer devices ----------- ###
+
+### These are essentially the same device.
+### NOTE: printing at full resolution (300 DPI) requires a printer
+### with at least 1.5 Mb of memory. 150 DPI only requires .5 Mb.
+### Note that the lj4dith driver is included with the H-P color printer
+### drivers below.
+### For questions about the fs600 device, please contact ###
+### Peter Schildmann (peter.schildmann@etechnik.uni-rostock.de). ###
+
+HPPCL=$(DEVOBJ)gdevpcl.$(OBJ)
+HPDLJM=$(DEVOBJ)gdevdljm.$(OBJ) $(HPPCL)
+HPMONO=$(DEVOBJ)gdevdjet.$(OBJ) $(HPDLJM)
+
+$(DEVOBJ)gdevpcl.$(OBJ) : $(DEVSRC)gdevpcl.c $(PDEVH) $(math__h) $(gdevpcl_h)\
+ $(GDEV) $(MAKEDIRS)
+ $(DEVCC) $(DEVO_)gdevpcl.$(OBJ) $(C_) $(DEVSRC)gdevpcl.c
+
+$(DEVOBJ)gdevdljm.$(OBJ) : $(DEVSRC)gdevdljm.c $(PDEVH) $(gdevdljm_h) $(GDEV) \
+ $(MAKEDIRS)
+ $(DEVCC) $(DEVO_)gdevdljm.$(OBJ) $(C_) $(DEVSRC)gdevdljm.c
+
+$(DEVOBJ)gdevdjet.$(OBJ) : $(DEVSRC)gdevdjet.c $(PDEVH) $(gdevdljm_h) $(GDEV) \
+ $(MAKEDIRS)
+ $(DEVCC) $(DEVO_)gdevdjet.$(OBJ) $(C_) $(DEVSRC)gdevdjet.c
+
+$(DD)deskjet.dev : $(DEVS_MAK) $(HPMONO) $(GLD)page.dev $(GDEV) $(MAKEDIRS)
+ $(SETPDEV2) $(DD)deskjet $(HPMONO)
+
+$(DD)djet500.dev : $(DEVS_MAK) $(HPMONO) $(GLD)page.dev $(GDEV) $(MAKEDIRS)
+ $(SETPDEV2) $(DD)djet500 $(HPMONO)
+
+$(DD)fs600.dev : $(DEVS_MAK) $(HPMONO) $(GLD)page.dev $(GDEV) $(MAKEDIRS)
+ $(SETPDEV2) $(DD)fs600 $(HPMONO)
+
+$(DD)laserjet.dev : $(DEVS_MAK) $(HPMONO) $(GLD)page.dev $(GDEV) $(MAKEDIRS)
+ $(SETPDEV2) $(DD)laserjet $(HPMONO)
+
+$(DD)ljetplus.dev : $(DEVS_MAK) $(HPMONO) $(GLD)page.dev $(GDEV) $(MAKEDIRS)
+ $(SETPDEV2) $(DD)ljetplus $(HPMONO)
+
+### Selecting ljet2p provides TIFF (mode 2) compression on LaserJet III,
+### IIIp, IIId, IIIsi, IId, and IIp.
+
+$(DD)ljet2p.dev : $(DEVS_MAK) $(HPMONO) $(GLD)page.dev $(GDEV) $(MAKEDIRS)
+ $(SETPDEV2) $(DD)ljet2p $(HPMONO)
+
+### Selecting ljet3 provides Delta Row (mode 3) compression on LaserJet III,
+### IIIp, IIId, IIIsi.
+
+$(DD)ljet3.dev : $(DEVS_MAK) $(HPMONO) $(GLD)page.dev $(GDEV) $(MAKEDIRS)
+ $(SETPDEV2) $(DD)ljet3 $(HPMONO)
+
+### Selecting ljet3d also provides duplex printing capability.
+
+$(DD)ljet3d.dev : $(DEVS_MAK) $(HPMONO) $(GLD)page.dev $(GDEV) $(MAKEDIRS)
+ $(SETPDEV2) $(DD)ljet3d $(HPMONO)
+
+### Selecting ljet4 or ljet4d also provides Delta Row compression on
+### LaserJet IV series.
+
+$(DD)ljet4.dev : $(DEVS_MAK) $(HPMONO) $(GLD)page.dev $(GDEV) $(MAKEDIRS)
+ $(SETPDEV2) $(DD)ljet4 $(HPMONO)
+
+$(DD)ljet4d.dev : $(DEVS_MAK) $(HPMONO) $(GLD)page.dev $(GDEV) $(MAKEDIRS)
+ $(SETPDEV2) $(DD)ljet4d $(HPMONO)
+
+$(DD)lp2563.dev : $(DEVS_MAK) $(HPMONO) $(GLD)page.dev $(GDEV) $(MAKEDIRS)
+ $(SETPDEV2) $(DD)lp2563 $(HPMONO)
+
+$(DD)oce9050.dev : $(DEVS_MAK) $(HPMONO) $(GLD)page.dev $(GDEV) $(MAKEDIRS)
+ $(SETPDEV2) $(DD)oce9050 $(HPMONO)
+
+### ------------------ The H-P LaserJet 5 and 6 devices ----------------- ###
+
+### These drivers use H-P's new PCL XL printer language, like H-P's
+### LaserJet 5 Enhanced driver for MS Windows. We don't recommend using
+### them:
+### - If you have a LJ 5L or 5P, which isn't a "real" LaserJet 5,
+### use the ljet4 driver instead. (The lj5 drivers won't work.)
+### - If you have any other model of LJ 5 or 6, use the pxlmono
+### driver, which often produces much more compact output.
+
+
+gdevpxut_h=$(DEVSRC)gdevpxut.h
+
+
+$(DEVOBJ)gdevpxut.$(OBJ) : $(DEVSRC)gdevpxut.c $(math__h) $(string__h)\
+ $(gx_h) $(gxdevcli_h) $(stream_h)\
+ $(gdevpxat_h) $(gdevpxen_h) $(gdevpxop_h) $(gdevpxut_h) $(GDEV) \
+ $(MAKEDIRS)
+ $(DEVCC) $(DEVO_)gdevpxut.$(OBJ) $(C_) $(DEVSRC)gdevpxut.c
+
+ljet5_=$(DEVOBJ)gdevlj56.$(OBJ) $(DEVOBJ)gdevpxut.$(OBJ) $(HPPCL)
+$(DD)lj5mono.dev : $(DEVS_MAK) $(ljet5_) $(GLD)page.dev $(GDEV) \
+ $(MAKEDIRS)
+ $(SETPDEV) $(DD)lj5mono $(ljet5_)
+
+$(DD)lj5gray.dev : $(DEVS_MAK) $(ljet5_) $(GLD)page.dev $(GDEV) \
+ $(MAKEDIRS)
+ $(SETPDEV) $(DD)lj5gray $(ljet5_)
+
+$(DEVOBJ)gdevlj56.$(OBJ) : $(DEVSRC)gdevlj56.c $(PDEVH) $(gdevpcl_h)\
+ $(gdevpxat_h) $(gdevpxen_h) $(gdevpxop_h) $(gdevpxut_h) $(stream_h) \
+ $(MAKEDIRS)
+ $(DEVCC) $(DEVO_)gdevlj56.$(OBJ) $(C_) $(DEVSRC)gdevlj56.c
+
+### -------------------- The ijs client ----------------- ###
+
+ijs_=$(DEVOBJ)gdevijs.$(OBJ)
+
+#$(IJSOBJ)ijs.$(OBJ) $(IJSOBJ)ijs_client.$(OBJ) \
+# $(IJSOBJ)ijs_exec_$(IJSEXECTYPE).$(OBJ)
+
+$(DD)ijs.dev : $(ijs_) $(GLD)page.dev $(DD)ijslib.dev $(GDEV) $(MAKEDIRS)
+ $(SETPDEV) $(DD)ijs $(ijs_)
+ $(ADDMOD) $(DD)ijs -include $(GLD)ijslib
+
+$(DEVOBJ)gdevijs.$(OBJ) : $(DEVSRC)gdevijs.c $(PDEVH) $(unistd__h) $(gp_h)\
+ $(GDEV) $(MAKEDIRS)
+ $(CC_) $(I_)$(DEVI_) $(II)$(IJSI_)$(_I) $(II)$(IJSI_)$(D)..$(_I) \
+ $(GLF_) $(DEVO_)gdevijs.$(OBJ) $(C_) $(DEVSRC)gdevijs.c
+
+# Please see ijs.mak for the Makefile fragment which builds the IJS
+# library.
+
+
+### -------------------------- The rinkj device ------------------------ ###
+
+RINKJ_SRCDIR=$(DEVSRC)rinkj
+RINKJ_SRC=$(RINKJ_SRCDIR)$(D)
+RINKJ_OBJ=$(DEVOBJ)
+RINKJ_O_=$(O_)$(RINKJ_OBJ)
+
+RINKJ_INCL=$(I_)$(RINKJ_SRCDIR)$(_I)
+RINKJ_CC=$(CC_) $(RINKJ_INCL)
+
+rinkj_core=$(RINKJ_OBJ)evenbetter-rll.$(OBJ) \
+ $(RINKJ_OBJ)rinkj-byte-stream.$(OBJ) $(RINKJ_OBJ)rinkj-device.$(OBJ) \
+ $(RINKJ_OBJ)rinkj-config.$(OBJ) $(RINKJ_OBJ)rinkj-dither.$(OBJ) \
+ $(RINKJ_OBJ)rinkj-epson870.$(OBJ) $(RINKJ_OBJ)rinkj-screen-eb.$(OBJ)
+
+$(RINKJ_OBJ)evenbetter-rll.$(OBJ) : $(RINKJ_SRC)evenbetter-rll.c $(MAKEDIRS)
+ $(RINKJ_CC) $(RINKJ_O_)evenbetter-rll.$(OBJ) $(C_) $(RINKJ_SRC)evenbetter-rll.c
+
+$(RINKJ_OBJ)rinkj-byte-stream.$(OBJ) : $(RINKJ_SRC)rinkj-byte-stream.c $(MAKEDIRS)
+ $(RINKJ_CC) $(RINKJ_O_)rinkj-byte-stream.$(OBJ) $(C_) $(RINKJ_SRC)rinkj-byte-stream.c
+
+$(RINKJ_OBJ)rinkj-device.$(OBJ) : $(RINKJ_SRC)rinkj-device.c $(MAKEDIRS)
+ $(RINKJ_CC) $(RINKJ_O_)rinkj-device.$(OBJ) $(C_) $(RINKJ_SRC)rinkj-device.c
+
+$(RINKJ_OBJ)rinkj-config.$(OBJ) : $(RINKJ_SRC)rinkj-config.c $(MAKEDIRS)
+ $(RINKJ_CC) $(RINKJ_O_)rinkj-config.$(OBJ) $(C_) $(RINKJ_SRC)rinkj-config.c
+
+$(RINKJ_OBJ)rinkj-dither.$(OBJ) : $(RINKJ_SRC)rinkj-dither.c $(MAKEDIRS)
+ $(RINKJ_CC) $(RINKJ_O_)rinkj-dither.$(OBJ) $(C_) $(RINKJ_SRC)rinkj-dither.c
+
+$(RINKJ_OBJ)rinkj-epson870.$(OBJ) : $(RINKJ_SRC)rinkj-epson870.c $(MAKEDIRS)
+ $(RINKJ_CC) $(RINKJ_O_)rinkj-epson870.$(OBJ) $(C_) $(RINKJ_SRC)rinkj-epson870.c
+
+$(RINKJ_OBJ)rinkj-screen-eb.$(OBJ) : $(RINKJ_SRC)rinkj-screen-eb.c $(MAKEDIRS)
+ $(RINKJ_CC) $(RINKJ_O_)rinkj-screen-eb.$(OBJ) $(C_) $(RINKJ_SRC)rinkj-screen-eb.c
+
+rinkj_=$(DEVOBJ)gdevrinkj.$(OBJ) $(rinkj_core)
+
+$(DD)rinkj.dev : $(DEVS_MAK) $(rinkj_) $(GLD)page.dev $(GDEV) $(MAKEDIRS)
+ $(SETDEV) $(DD)rinkj $(rinkj_)
+
+$(DEVOBJ)gdevrinkj.$(OBJ) : $(DEVSRC)gdevrinkj.c $(PDEVH) $(math__h)\
+ $(gdevdcrd_h) $(gscrd_h) $(gscrdp_h) $(gsparam_h) $(gxlum_h)\
+ $(gxdcconv_h) $(gscms_h) $(gsicc_cache_h) $(gsicc_manage_h) \
+ $(MAKEDIRS)
+ $(DEVCC) $(DEVO_)gdevrinkj.$(OBJ) $(C_) $(DEVSRC)gdevrinkj.c
+
+
+###### ------------------- High-level file formats ------------------- ######
+
+# Support for PostScript and PDF
+
+gdevpsdf_h=$(DEVVECSRC)gdevpsdf.h $(gdevvec_h) $(gsparam_h)\
+ $(sa85x_h) $(scfx_h) $(spsdf_h) $(strimpl_h)
+gdevpsds_h=$(DEVVECSRC)gdevpsds.h $(strimpl_h) $(gsiparam_h)
+
+psdf_1=$(DEVOBJ)gdevpsdi.$(OBJ) $(DEVOBJ)gdevpsdp.$(OBJ)
+psdf_2=$(DEVOBJ)gdevpsds.$(OBJ) $(DEVOBJ)gdevpsdu.$(OBJ)
+psdf_3=$(DEVOBJ)scfparam.$(OBJ) $(DEVOBJ)sdcparam.$(OBJ) $(DEVOBJ)sdeparam.$(OBJ)
+psdf_4=$(DEVOBJ)spprint.$(OBJ) $(DEVOBJ)spsdf.$(OBJ) $(DEVOBJ)sstring.$(OBJ)
+psdf_5=$(DEVOBJ)gsparamx.$(OBJ)
+psdf_=$(psdf_1) $(psdf_2) $(psdf_3) $(psdf_4) $(psdf_5)
+psdf_inc1=$(GLD)vector.dev $(GLD)pngp.dev $(GLD)seexec.dev
+psdf_inc2=$(GLD)sdcte.dev $(GLD)slzwe.dev $(GLD)szlibe.dev
+psdf_inc=$(psdf_inc1) $(psdf_inc2)
+$(DD)psdf.dev : $(DEVS_MAK) $(ECHOGS_XE) $(psdf_) $(psdf_inc) $(GDEV) \
+ $(MAKEDIRS)
+ $(SETMOD) $(DD)psdf $(psdf_1)
+ $(ADDMOD) $(DD)psdf -obj $(psdf_2)
+ $(ADDMOD) $(DD)psdf -obj $(psdf_3)
+ $(ADDMOD) $(DD)psdf -obj $(psdf_4)
+ $(ADDMOD) $(DD)psdf -obj $(psdf_5)
+ $(ADDMOD) $(DD)psdf -include $(psdf_inc1)
+ $(ADDMOD) $(DD)psdf -include $(psdf_inc2)
+
+$(DEVOBJ)gdevpsdi.$(OBJ) : $(DEVVECSRC)gdevpsdi.c $(GXERR)\
+ $(jpeglib__h) $(math__h) $(stdio__h)\
+ $(gscspace_h)\
+ $(scfx_h) $(slzwx_h) $(spngpx_h)\
+ $(strimpl_h) $(szlibx_h)\
+ $(gdevpsdf_h) $(gdevpsds_h) $(gxdevmem_h) $(gxcspace_h) $(gxparamx_h)\
+ $(sjbig2_luratech_h) $(sjpx_luratech_h) $(gsicc_manage_h) $(MAKEDIRS)
+ $(GDEVLWFJB2JPXCC) $(DEVO_)gdevpsdi.$(OBJ) $(C_) $(DEVVECSRC)gdevpsdi.c
+
+$(DEVOBJ)gdevpsdp.$(OBJ) : $(DEVVECSRC)gdevpsdp.c $(GDEVH)\
+ $(string__h) $(jpeglib__h)\
+ $(scfx_h) $(sdct_h) $(slzwx_h) $(srlx_h) $(strimpl_h) $(szlibx_h)\
+ $(gsparamx_h) $(gsutil_h) $(gdevpsdf_h)\
+ $(sjbig2_luratech_h) $(sjpx_luratech_h) $(MAKEDIRS)
+ $(GDEVLWFJB2JPXCC) $(DEVO_)gdevpsdp.$(OBJ) $(C_) $(DEVVECSRC)gdevpsdp.c
+
+$(DEVOBJ)gdevpsds.$(OBJ) : $(DEVVECSRC)gdevpsds.c $(GX) $(memory__h)\
+ $(gserrors_h) $(gxdcconv_h) $(gdevpsds_h) $(gxbitmap_h)\
+ $(gxcspace_h) $(gsdcolor_h) $(gscspace_h) $(gxdevcli_h) \
+ $(MAKEDIRS)
+ $(DEVCC) $(DEVO_)gdevpsds.$(OBJ) $(C_) $(DEVVECSRC)gdevpsds.c
+
+$(DEVOBJ)gdevpsdu.$(OBJ) : $(DEVVECSRC)gdevpsdu.c $(GXERR)\
+ $(jpeglib__h) $(memory__h) $(stdio__h)\
+ $(sa85x_h) $(scfx_h) $(sdct_h) $(sjpeg_h) $(strimpl_h)\
+ $(gdevpsdf_h) $(spprint_h) $(gsovrc_h) $(MAKEDIRS)
+ $(DEVJCC) $(DEVO_)gdevpsdu.$(OBJ) $(C_) $(DEVVECSRC)gdevpsdu.c
+
+# Plain text writer
+
+gdevagl_h=$(DEVVECSRC)gdevagl.h
+
+txtwrite_=$(DEVOBJ)gdevtxtw.$(OBJ) $(DEVOBJ)gdevagl.$(OBJ)
+
+$(DD)txtwrite.dev : $(DEVS_MAK) $(ECHOGS_XE) $(txtwrite_) $(GDEV)\
+ $(gdevagl_h) $(MAKEDIRS)
+ $(SETDEV2) $(DD)txtwrite $(txtwrite_)
+
+$(DEVOBJ)gdevtxtw.$(OBJ) : $(DEVVECSRC)gdevtxtw.c $(GDEV)\
+ $(memory__h) $(string__h) $(gp_h) $(gsparam_h) $(gsutil_h) \
+ $(gsdevice_h) $(gxfont_h) $(gxfont0_h) $(gstext_h) $(gxfcid_h)\
+ $(gxistate_h) $(gxpath_h) $(gdevagl_h) $(MAKEDIRS)
+ $(DEVCC) $(DEVO_)gdevtxtw.$(OBJ) $(C_) $(DEVVECSRC)gdevtxtw.c
+
+$(DEVOBJ)gdevagl.$(OBJ) : $(DEVVECSRC)gdevagl.c $(GDEV)\
+ $(gdevagl_h) $(MAKEDIRS)
+ $(DEVCC) $(DEVO_)gdevagl.$(OBJ) $(C_) $(DEVVECSRC)gdevagl.c
+
+
+################ BEGIN PDF WRITER ################
+
+# We reserve slots here for gdevpdfa...z, just in case we need them.
+pdfwrite1_=$(DEVOBJ)gdevpdf.$(OBJ) $(DEVOBJ)gdevpdfb.$(OBJ)
+pdfwrite2_=$(DEVOBJ)gdevpdfc.$(OBJ) $(DEVOBJ)gdevpdfd.$(OBJ) $(DEVOBJ)gdevpdfe.$(OBJ)
+pdfwrite3_=$(DEVOBJ)gdevpdfg.$(OBJ)
+pdfwrite4_=$(DEVOBJ)gdevpdfi.$(OBJ) $(DEVOBJ)gdevpdfj.$(OBJ) $(DEVOBJ)gdevpdfk.$(OBJ)
+pdfwrite5_=$(DEVOBJ)gdevpdfm.$(OBJ)
+pdfwrite6_=$(DEVOBJ)gdevpdfo.$(OBJ) $(DEVOBJ)gdevpdfp.$(OBJ) $(DEVOBJ)gdevpdft.$(OBJ)
+pdfwrite7_=$(DEVOBJ)gdevpdfr.$(OBJ)
+pdfwrite8_=$(DEVOBJ)gdevpdfu.$(OBJ) $(DEVOBJ)gdevpdfv.$(OBJ) $(DEVOBJ)gdevagl.$(OBJ)
+pdfwrite9_= $(GLOBJ)ConvertUTF.$(OBJ)
+pdfwrite10_=$(DEVOBJ)gsflip.$(OBJ)
+pdfwrite11_=$(DEVOBJ)scantab.$(OBJ) $(DEVOBJ)sfilter2.$(OBJ)
+pdfwrite_=$(pdfwrite1_) $(pdfwrite2_) $(pdfwrite3_) $(pdfwrite4_)\
+ $(pdfwrite5_) $(pdfwrite6_) $(pdfwrite7_) $(pdfwrite8_) $(pdfwrite9_)\
+ $(pdfwrite10_) $(pdfwrite11_)
+
+# Since ps2write actually is a clone of pdfwrite,
+# we just depend on it.
+$(DD)ps2write.dev : $(DEVS_MAK) $(DD)pdfwrite.dev $(GDEV) \
+ $(MAKEDIRS)
+ $(SETDEV2) $(DD)ps2write
+ $(ADDMOD) $(DD)ps2write -include $(DD)pdfwrite.dev
+
+# Since eps2write actually is a clone of pdfwrite,
+# we just depend on it.
+$(DD)eps2write.dev : $(DEVS_MAK) $(DD)pdfwrite.dev $(GDEV) \
+ $(MAKEDIRS)
+ $(SETDEV2) $(DD)eps2write
+ $(ADDMOD) $(DD)eps2write -include $(DD)pdfwrite.dev
+
+# Note that for ps2pdf operation, we need to parse DSC comments to set
+# the Orientation (Page dict /Rotate value). This is not part of the
+# pdfwrite device, but part of the PS interpreter so that the pdfwrite
+# device can be used with other top level interpreters (such as PCL).
+$(DD)pdfwrite.dev : $(DEVS_MAK) $(ECHOGS_XE) $(pdfwrite_)\
+ $(GLD)cmyklib.dev $(GLD)cfe.dev $(GLD)lzwe.dev\
+ $(GLD)rle.dev $(GLD)sdcte.dev $(GLD)sdeparam.dev $(GLD)smd5.dev\
+ $(GLD)szlibe.dev $(GLD)psdf.dev $(GLD)sarc4.dev $(DD)pdtext.dev $(GDEV) \
+ $(MAKEDIRS)
+ $(SETDEV2) $(DD)pdfwrite $(pdfwrite1_)
+ $(ADDMOD) $(DD)pdfwrite $(pdfwrite2_)
+ $(ADDMOD) $(DD)pdfwrite $(pdfwrite3_)
+ $(ADDMOD) $(DD)pdfwrite $(pdfwrite4_)
+ $(ADDMOD) $(DD)pdfwrite $(pdfwrite5_)
+ $(ADDMOD) $(DD)pdfwrite $(pdfwrite6_)
+ $(ADDMOD) $(DD)pdfwrite $(pdfwrite7_)
+ $(ADDMOD) $(DD)pdfwrite $(pdfwrite8_)
+ $(ADDMOD) $(DD)pdfwrite $(pdfwrite9_)
+ $(ADDMOD) $(DD)pdfwrite $(pdfwrite10_)
+ $(ADDMOD) $(DD)pdfwrite $(pdfwrite11_)
+ $(ADDMOD) $(DD)pdfwrite -include $(GLD)cmyklib $(GLD)cfe $(GLD)lzwe
+ $(ADDMOD) $(DD)pdfwrite -include $(GLD)rle $(GLD)sdcte $(GLD)sdeparam
+ $(ADDMOD) $(DD)pdfwrite -include $(GLD)smd5 $(GLD)szlibe $(GLD)sarc4.dev
+ $(ADDMOD) $(DD)pdfwrite -include $(GLD)psdf
+ $(ADDMOD) $(DD)pdfwrite -include $(DD)pdtext
+
+gdevpdfb_h=$(DEVVECSRC)gdevpdfb.h
+gdevpdfc_h=$(DEVVECSRC)gdevpdfc.h
+gdevpdfg_h=$(DEVVECSRC)gdevpdfg.h $(gscspace_h)
+gdevpdfo_h=$(DEVVECSRC)gdevpdfo.h $(gsparam_h)
+gdevpdfx_h=$(DEVVECSRC)gdevpdfx.h\
+ $(gsparam_h) $(gsuid_h) $(gxdevice_h) $(gxfont_h) $(gxline_h)\
+ $(spprint_h) $(stream_h) $(gdevpsdf_h) $(gxdevmem_h) $(sarc4_h)
+
+opdfread_h=$(DEVVECSRC)opdfread.h
+
+$(DEVOBJ)gdevpdf.$(OBJ) : $(DEVVECSRC)gdevpdf.c $(GDEVH)\
+ $(fcntl__h) $(memory__h) $(string__h) $(time__h) $(unistd__h) $(gp_h)\
+ $(gdevpdfg_h) $(gdevpdfo_h) $(gdevpdfx_h) $(smd5_h) $(sarc4_h)\
+ $(gdevpdfb_h) $(gscms_h) $(MAKEDIRS)
+ $(DEVCC) $(DEVO_)gdevpdf.$(OBJ) $(C_) $(DEVVECSRC)gdevpdf.c
+
+$(DEVOBJ)gdevpdfb.$(OBJ) : $(DEVVECSRC)gdevpdfb.c\
+ $(string__h) $(gx_h)\
+ $(gdevpdfg_h) $(gdevpdfo_h) $(gdevpdfx_h)\
+ $(gserrors_h) $(gxcspace_h) $(gxdcolor_h) $(gxpcolor_h) $(gxhldevc_h)\
+ $(gsptype1_h) $(MAKEDIRS)
+ $(DEVCC) $(DEVO_)gdevpdfb.$(OBJ) $(C_) $(DEVVECSRC)gdevpdfb.c
+
+$(DEVOBJ)gdevpdfc.$(OBJ) : $(DEVVECSRC)gdevpdfc.c $(GXERR) $(math__h) $(memory__h)\
+ $(gdevpdfc_h) $(gdevpdfg_h) $(gdevpdfo_h) $(gdevpdfx_h)\
+ $(gscie_h) $(gscindex_h) $(gscspace_h) $(gscdevn_h) $(gscsepr_h) $(gsicc_h)\
+ $(sstring_h) $(stream_h) $(strimpl_h) $(gxcspace_h) $(gxcdevn_h) $(gscspace_h)\
+ $(gsicc_manage_h) $(MAKEDIRS)
+ $(DEVCC) $(DEVO_)gdevpdfc.$(OBJ) $(C_) $(DEVVECSRC)gdevpdfc.c
+
+$(DEVOBJ)gdevpdfd.$(OBJ) : $(DEVVECSRC)gdevpdfd.c $(math__h) $(memory__h)\
+ $(gx_h) $(gxdevice_h) $(gxfixed_h) $(gxistate_h) $(gxpaint_h)\
+ $(gxcoord_h) $(gxdevmem_h) $(gxcolor2_h) $(gxhldevc_h)\
+ $(gsstate_h) $(gserrors_h) $(gsptype2_h) $(gsshade_h)\
+ $(gzpath_h) $(gzcpath_h) $(gdevpdfx_h) $(gdevpdfg_h) $(gdevpdfo_h) $(gsutil_h) \
+ $(MAKEDIRS)
+ $(DEVCC) $(DEVO_)gdevpdfd.$(OBJ) $(C_) $(DEVVECSRC)gdevpdfd.c
+
+$(DEVOBJ)gdevpdfe.$(OBJ) : $(DEVVECSRC)gdevpdfe.c\
+ $(gx_h) $(gserrors_h) $(string__h) $(time__h) $(stream_h) $(gp_h) $(smd5_h) $(gscdefs_h)\
+ $(gdevpdfx_h) $(gdevpdfg_h) $(gdevpdfo_h) $(gdevpdtf_h) $(ConvertUTF_h) $(MAKEDIRS)
+ $(DEVCC) $(DEVO_)gdevpdfe.$(OBJ) $(C_) $(DEVVECSRC)gdevpdfe.c
+
+$(DEVOBJ)gdevpdfg.$(OBJ) : $(DEVVECSRC)gdevpdfg.c $(GXERR) $(math__h) $(string__h)\
+ $(memory__h) $(gdevpdfg_h) $(gdevpdfo_h) $(gdevpdfx_h)\
+ $(gsfunc0_h) $(gsstate_h) $(gxdcolor_h) $(gxpcolor_h) $(gxcolor2_h) $(gsptype2_h)\
+ $(gxbitmap_h) $(gxdht_h) $(gxfarith_h) $(gxfmap_h) $(gxht_h) $(gxistate_h)\
+ $(gzht_h) $(gsicc_manage_h) $(MAKEDIRS)
+ $(DEVCC) $(DEVO_)gdevpdfg.$(OBJ) $(C_) $(DEVVECSRC)gdevpdfg.c
+
+$(DEVOBJ)gdevpdfi.$(OBJ) : $(DEVVECSRC)gdevpdfi.c $(memory__h) $(math__h)\
+ $(gx_h)\
+ $(gserrors_h) $(gsdevice_h) $(gsflip_h) $(gsiparm4_h) $(gsstate_h) $(gscolor2_h)\
+ $(gdevpdfx_h) $(gdevpdfg_h) $(gdevpdfo_h)\
+ $(gxcspace_h) $(gximage3_h) $(gximag3x_h) $(gxdcolor_h) $(gxpcolor_h)\
+ $(gxhldevc_h) $(gsicc_manage_h) $(MAKEDIRS)
+ $(DEVCC) $(DEVO_)gdevpdfi.$(OBJ) $(C_) $(DEVVECSRC)gdevpdfi.c
+
+$(DEVOBJ)gdevpdfj.$(OBJ) : $(DEVVECSRC)gdevpdfj.c\
+ $(memory__h) $(string__h) $(gx_h) $(gserrors_h)\
+ $(gdevpdfx_h) $(gdevpdfg_h) $(gdevpdfo_h) $(gxcspace_h)\
+ $(gsiparm4_h) $(gdevpsds_h) $(spngpx_h) $(MAKEDIRS)
+ $(DEVJCC) $(DEVO_)gdevpdfj.$(OBJ) $(C_) $(DEVVECSRC)gdevpdfj.c
+
+$(DEVOBJ)gdevpdfk.$(OBJ) : $(DEVVECSRC)gdevpdfk.c $(GXERR) $(math__h) $(memory__h)\
+ $(gdevpdfc_h) $(gdevpdfg_h) $(gdevpdfo_h) $(gdevpdfx_h)\
+ $(gsicc_h) $(gxcie_h) $(gxcspace_h)\
+ $(stream_h) $(strimpl_h) $(MAKEDIRS)
+ $(DEVCC) $(DEVO_)gdevpdfk.$(OBJ) $(C_) $(DEVVECSRC)gdevpdfk.c
+
+$(DEVOBJ)gdevpdfm.$(OBJ) : $(DEVVECSRC)gdevpdfm.c\
+ $(math__h) $(memory__h) $(string__h) $(gx_h)\
+ $(gdevpdfo_h) $(gdevpdfx_h) $(gserrors_h) $(gsutil_h)\
+ $(szlibx_h) $(slzwx_h) $(MAKEDIRS)
+ $(DEVCC) $(DEVO_)gdevpdfm.$(OBJ) $(C_) $(DEVVECSRC)gdevpdfm.c
+
+$(DEVOBJ)gdevpdfo.$(OBJ) : $(DEVVECSRC)gdevpdfo.c $(memory__h) $(string__h)\
+ $(gx_h)\
+ $(gdevpdfo_h) $(gdevpdfx_h) $(gserrors_h) $(gsparam_h) $(gsutil_h)\
+ $(sa85x_h) $(sarc4_h) $(strimpl_h) $(MAKEDIRS)
+ $(DEVCC) $(DEVO_)gdevpdfo.$(OBJ) $(C_) $(DEVVECSRC)gdevpdfo.c
+
+$(DEVOBJ)gdevpdfp.$(OBJ) : $(DEVVECSRC)gdevpdfp.c $(memory__h) $(string__h) $(gx_h)\
+ $(gdevpdfo_h) $(gdevpdfg_h) $(gdevpdfx_h) $(gserrors_h) $(gsparamx_h) \
+ $(MAKEDIRS)
+ $(DEVCC) $(DEVO_)gdevpdfp.$(OBJ) $(C_) $(DEVVECSRC)gdevpdfp.c
+
+$(DEVOBJ)gdevpdfr.$(OBJ) : $(DEVVECSRC)gdevpdfr.c $(memory__h) $(string__h)\
+ $(gx_h)\
+ $(gdevpdfo_h) $(gdevpdfx_h) $(gserrors_h) $(gsutil_h)\
+ $(scanchar_h) $(sstring_h) $(strimpl_h) $(MAKEDIRS)
+ $(DEVCC) $(DEVO_)gdevpdfr.$(OBJ) $(C_) $(DEVVECSRC)gdevpdfr.c
+
+$(DEVOBJ)gdevpdft.$(OBJ) : $(DEVVECSRC)gdevpdft.c $(string__h)\
+ $(gx_h) $(gserrors_h) $(gstrans_h) $(gscolor2_h) $(gzstate_h)\
+ $(gdevpdfx_h) $(gdevpdfg_h) $(gdevpdfo_h) $(gsccolor_h) \
+ $(MAKEDIRS)
+ $(DEVCC) $(DEVO_)gdevpdft.$(OBJ) $(C_) $(DEVVECSRC)gdevpdft.c
+
+$(DEVOBJ)gdevpdfu.$(OBJ) : $(DEVVECSRC)gdevpdfu.c $(GXERR)\
+ $(jpeglib__h) $(memory__h) $(string__h)\
+ $(gdevpdfo_h) $(gdevpdfx_h) $(gdevpdfg_h) $(gdevpdtd_h) $(gscdefs_h)\
+ $(gsdsrc_h) $(gsfunc_h) $(gsfunc3_h)\
+ $(sa85x_h) $(scfx_h) $(sdct_h) $(slzwx_h) $(spngpx_h)\
+ $(srlx_h) $(sarc4_h) $(smd5_h) $(sstring_h) $(strimpl_h) $(szlibx_h)\
+ $(strmio_h) $(sjbig2_luratech_h) $(sjpx_luratech_h)\
+ $(opdfread_h) $(gdevagl_h) $(gs_mro_e_h) $(gs_mgl_e_h) \
+ $(MAKEDIRS)
+ $(GDEVLWFJB2JPXCC) $(DEVO_)gdevpdfu.$(OBJ) $(C_) $(DEVVECSRC)gdevpdfu.c
+
+$(DEVOBJ)gdevpdfv.$(OBJ) : $(DEVVECSRC)gdevpdfv.c $(GXERR) $(math__h) $(string__h)\
+ $(gdevpdfg_h) $(gdevpdfo_h) $(gdevpdfx_h)\
+ $(gscindex_h) $(gscoord_h) $(gsiparm3_h) $(gsmatrix_h) $(gsptype2_h)\
+ $(gxcolor2_h) $(gxdcolor_h) $(gxpcolor_h) $(gxshade_h) $(MAKEDIRS)
+ $(DEVCC) $(DEVO_)gdevpdfv.$(OBJ) $(C_) $(DEVVECSRC)gdevpdfv.c
+
+# ---------------- Font writing ---------------- #
+# This is not really a library facility, but one piece of interpreter test
+# code uses it.
+
+# Support for PostScript and PDF font writing
+
+gdevpsf_h=$(DEVVECSRC)gdevpsf.h $(gsccode_h) $(gsgdata_h)
+
+psf_1=$(DEVOBJ)gdevpsf1.$(OBJ) $(DEVOBJ)gdevpsf2.$(OBJ) $(DEVOBJ)gdevpsfm.$(OBJ)
+psf_2=$(DEVOBJ)gdevpsft.$(OBJ) $(DEVOBJ)gdevpsfu.$(OBJ) $(DEVOBJ)gdevpsfx.$(OBJ)
+psf_3=$(DEVOBJ)spsdf.$(OBJ)
+psf_=$(psf_1) $(psf_2) $(psf_3)
+$(DD)psf.dev : $(DEV_MAK) $(ECHOGS_XE) $(psf_) $(MAKEDIRS)
+ $(SETMOD) $(DD)psf $(psf_1)
+ $(ADDMOD) $(DD)psf -obj $(psf_2)
+ $(ADDMOD) $(DD)psf -obj $(psf_3)
+
+$(DEVOBJ)gdevpsf1.$(OBJ) : $(DEVVECSRC)gdevpsf1.c $(AK) $(gx_h)\
+ $(gserrors_h) $(memory__h) $(gsccode_h) $(gsmatrix_h)\
+ $(gxfixed_h) $(gxfont_h) $(gxfont1_h) $(gxmatrix_h) $(gxtype1_h)\
+ $(sfilter_h) $(sstring_h) $(stream_h) $(strimpl_h)\
+ $(gdevpsf_h) $(spprint_h) $(spsdf_h) $(math_h) $(MAKEDIRS)
+ $(DEVCC) $(DEVO_)gdevpsf1.$(OBJ) $(C_) $(DEVVECSRC)gdevpsf1.c
+
+$(DEVOBJ)gdevpsf2.$(OBJ) : $(DEVVECSRC)gdevpsf2.c $(AK) $(gx_h)\
+ $(gserrors_h) $(math__h) $(memory__h) $(gxarith_h) $(gsutil_h)\
+ $(gsccode_h) $(gscencs_h) $(gscrypt1_h) $(gsmatrix_h)\
+ $(gxfcid_h) $(gxfixed_h) $(gxfont_h) $(gxfont1_h)\
+ $(stream_h) $(gdevpsf_h) $(MAKEDIRS)
+ $(DEVCC) $(DEVO_)gdevpsf2.$(OBJ) $(C_) $(DEVVECSRC)gdevpsf2.c
+
+$(DEVOBJ)gdevpsfm.$(OBJ) : $(DEVVECSRC)gdevpsfm.c $(AK) $(gx_h)\
+ $(gserrors_h) $(gdevpsf_h) $(gxfcmap_h) $(spprint_h) $(spsdf_h) $(stream_h)\
+ $(MAKEDIRS)
+ $(DEVCC) $(DEVO_)gdevpsfm.$(OBJ) $(C_) $(DEVVECSRC)gdevpsfm.c
+
+$(DEVOBJ)gdevpsft.$(OBJ) : $(DEVVECSRC)gdevpsft.c $(AK) $(gx_h)\
+ $(gserrors_h) $(memory__h) $(gscencs_h) $(gsmatrix_h) $(gsutil_h)\
+ $(gxfcid_h) $(gxfont_h) $(gxfont42_h) $(gxttf_h)\
+ $(spprint_h) $(stream_h) $(gdevpsf_h) $(MAKEDIRS)
+ $(DEVCC) $(DEVO_)gdevpsft.$(OBJ) $(C_) $(DEVVECSRC)gdevpsft.c
+
+$(DEVOBJ)gdevpsfu.$(OBJ) : $(DEVVECSRC)gdevpsfu.c $(AK) $(gx_h)\
+ $(gserrors_h) $(memory__h) $(gsmatrix_h) $(gxfont_h) $(gdevpsf_h)\
+ $(MAKEDIRS)
+ $(DEVCC) $(DEVO_)gdevpsfu.$(OBJ) $(C_) $(DEVVECSRC)gdevpsfu.c
+
+$(DEVOBJ)gdevpsfx.$(OBJ) : $(DEVVECSRC)gdevpsfx.c $(AK) $(gx_h)\
+ $(gserrors_h) $(math__h) $(memory__h)\
+ $(gxfixed_h) $(gxfont_h) $(gxfont1_h) $(gxmatrix_h) $(gxtype1_h)\
+ $(stream_h) $(gdevpsf_h) $(gxistate_h) $(MAKEDIRS)
+ $(DEVCC) $(DEVO_)gdevpsfx.$(OBJ) $(C_) $(DEVVECSRC)gdevpsfx.c
+
+# ---------------- Font copying ---------------- #
+
+# This facility is not included in the core library. Currently it is used
+# only by pdfwrite.
+
+fcopy_=$(DEVOBJ)gxfcopy.$(OBJ)
+$(GLD)fcopy.dev : $(DEVS_MAK) $(ECHOGS_XE) $(fcopy_) $(MAKEDIRS)
+ $(SETMOD) $(GLD)fcopy $(fcopy_)
+
+$(DEVOBJ)gxfcopy.$(OBJ) : $(DEVSRC)gxfcopy.c $(memory__h) $(AK) $(gx_h)\
+ $(gserrors_h) $(gscencs_h) $(gsline_h) $(gspaint_h) $(gspath_h) $(gsstruct_h)\
+ $(gsutil_h) $(gschar_h) $(gxfont_h) $(gxfont1_h) $(gxfont42_h) $(gxchar_h)\
+ $(gxfcid_h) $(gxfcopy_h) $(gxfcache_h) $(gxistate_h) $(gxtext_h) $(gxtype1_h)\
+ $(smd5_h) $(gzstate_h) $(gdevpsf_h) $(stream_h) $(MAKEDIRS)
+ $(DEVCC) $(DEVO_)gxfcopy.$(OBJ) $(C_) $(DEVSRC)gxfcopy.c
+
+######## pdfwrite text
+
+# The text facilities for the PDF writer are so large and complex that
+# we give them their own module name and (for the new code) file name prefix.
+# However, logically they are part of pdfwrite and cannot be used separately.
+
+$(DD)pdtext.dev : $(DEVS_MAK) $(DD)pdxtext.dev $(GDEV) $(MAKEDIRS)
+ $(SETMOD) $(DD)pdtext -include $(DD)pdxtext
+
+# For a code roadmap, see gdevpdtx.h.
+
+gdevpdt_h=$(DEVVECSRC)gdevpdt.h
+gdevpdtx_h=$(DEVVECSRC)gdevpdtx.h $(gdevpdt_h)
+gdevpdtb_h=$(DEVVECSRC)gdevpdtb.h $(gdevpdtx_h)
+gdevpdtd_h=$(DEVVECSRC)gdevpdtd.h $(gdevpdtb_h) $(gdevpdtx_h)
+gdevpdtf_h=$(DEVVECSRC)gdevpdtf.h $(gdevpdtx_h)
+gdevpdti_h=$(DEVVECSRC)gdevpdti.h $(gdevpdt_h)
+gdevpdts_h=$(DEVVECSRC)gdevpdts.h $(gsmatrix_h)
+gdevpdtt_h=$(DEVVECSRC)gdevpdtt.h
+gdevpdtv_h=$(DEVVECSRC)gdevpdtv.h
+gdevpdtw_h=$(DEVVECSRC)gdevpdtw.h
+whitelst_h=$(DEVVECSRC)whitelst.h
+
+# We reserve space for all of a..z, just in case.
+pdxtext_ab=$(DEVOBJ)gdevpdt.$(OBJ) $(DEVOBJ)gdevpdtb.$(OBJ)
+pdxtext_cde=$(DEVOBJ)gdevpdtc.$(OBJ) $(DEVOBJ)gdevpdtd.$(OBJ) $(DEVOBJ)gdevpdte.$(OBJ)
+pdxtext_fgh=$(DEVOBJ)gdevpdtf.$(OBJ)
+pdxtext_ijk=$(DEVOBJ)gdevpdti.$(OBJ)
+pdxtext_lmn=
+pdxtext_opq=
+pdxtext_rst=$(DEVOBJ)gdevpdts.$(OBJ) $(DEVOBJ)gdevpdtt.$(OBJ)
+pdxtext_uvw=$(DEVOBJ)gdevpdtv.$(OBJ) $(DEVOBJ)gdevpdtw.$(OBJ) $(DEVOBJ)whitelst.$(OBJ)
+pdxtext_xyz=
+pdxtext_=$(pdxtext_ab) $(pdxtext_cde) $(pdxtext_fgh) $(pdxtext_ijk)\
+ $(pdxtext_lmn) $(pdxtext_opq) $(pdxtext_rst) $(pdxtext_uvw) $(pdxtext_xyz)\
+ $(DEVOBJ)gsfont0c.$(OBJ)
+$(DD)pdxtext.dev : $(DEVS_MAK) $(pdxtext_) $(GDEV)\
+ $(GLD)fcopy.dev $(GLD)psf.dev $(MAKEDIRS)
+ $(SETMOD) $(DD)pdxtext $(pdxtext_ab)
+ $(ADDMOD) $(DD)pdxtext $(pdxtext_cde)
+ $(ADDMOD) $(DD)pdxtext $(pdxtext_fgh)
+ $(ADDMOD) $(DD)pdxtext $(pdxtext_ijk)
+ $(ADDMOD) $(DD)pdxtext $(pdxtext_lmn)
+ $(ADDMOD) $(DD)pdxtext $(pdxtext_opq)
+ $(ADDMOD) $(DD)pdxtext $(pdxtext_rst)
+ $(ADDMOD) $(DD)pdxtext $(pdxtext_uvw)
+ $(ADDMOD) $(DD)pdxtext $(pdxtext_xyz)
+ $(ADDMOD) $(DD)pdxtext $(DEVOBJ)gsfont0c.$(OBJ)
+ $(ADDMOD) $(DD)pdxtext -include $(GLD)fcopy $(GLD)psf
+
+$(DEVOBJ)gdevpdt.$(OBJ) : $(DEVVECSRC)gdevpdt.c $(gx_h) $(gxpath_h) $(memory__h)\
+ $(gdevpdfx_h) $(gdevpdfg_h) $(gdevpdtf_h) $(gdevpdti_h) $(gdevpdtx_h) $(gdevpdt_h) \
+ $(MAKEDIRS)
+ $(DEVCC) $(DEVO_)gdevpdt.$(OBJ) $(C_) $(DEVVECSRC)gdevpdt.c
+
+$(DEVOBJ)gdevpdtb.$(OBJ) : $(DEVVECSRC)gdevpdtb.c $(memory__h) $(ctype__h) $(string__h)\
+ $(memory__h) $(ctype__h) $(string__h) $(gx_h) $(gserrors_h) $(gsutil_h) $(gxfcid_h)\
+ $(gxfcopy_h) $(gxfont_h) $(gxfont42_h) $(gdevpsf_h) $(gdevpdfx_h) $(gdevpdfo_h)\
+ $(gdevpdtb_h) $(gdevpdfg_h) $(gdevpdtf_h) $(smd5_h) $(MAKEDIRS)
+ $(DEVCC) $(DEVO_)gdevpdtb.$(OBJ) $(C_) $(DEVVECSRC)gdevpdtb.c
+
+$(DEVOBJ)gdevpdtc.$(OBJ) : $(DEVVECSRC)gdevpdtc.c $(gx_h) $(memory__h) $(string__h)\
+ $(gserrors_h) $(gxfcmap_h) $(gxfont_h) $(gxfont0_h) $(gxfont0c_h)\
+ $(gzpath_h) $(gxchar_h) $(gdevpsf_h) $(gdevpdfx_h) $(gdevpdtx_h)\
+ $(gdevpdtd_h) $(gdevpdtf_h) $(gdevpdts_h) $(gdevpdtt_h) $(MAKEDIRS)
+ $(DEVCC) $(DEVO_)gdevpdtc.$(OBJ) $(C_) $(DEVVECSRC)gdevpdtc.c
+
+$(DEVOBJ)gdevpdte.$(OBJ) : $(DEVVECSRC)gdevpdte.c $(gx_h) $(math__h) $(memory__h) $(string__h)\
+ $(gserrors_h) $(gsutil_h) $(gxfcmap_h) $(gxfcopy_h) $(gxfont_h) \
+ $(gxfont0_h) $(gxfont0c_h) $(gxpath_h) $(gdevpsf_h) $(gdevpdfx_h) \
+ $(gdevpdfg_h) $(gdevpdfo_h) $(gdevpdtx_h) $(gdevpdtd_h) $(gdevpdtf_h) $(gdevpdts_h) \
+ $(gdevpdtt_h) $(MAKEDIRS)
+ $(DEVCC) $(DEVO_)gdevpdte.$(OBJ) $(C_) $(DEVVECSRC)gdevpdte.c
+
+$(DEVOBJ)gdevpdtd.$(OBJ) : $(DEVVECSRC)gdevpdtd.c $(math__h) $(memory__h) $(gx_h)\
+ $(gserrors_h) $(gsrect_h) $(gscencs_h)\
+ $(gdevpdfo_h) $(gdevpdfx_h)\
+ $(gdevpdtb_h) $(gdevpdtd_h) $(gdevpdtf_h) $(MAKEDIRS)
+ $(DEVCC) $(DEVO_)gdevpdtd.$(OBJ) $(C_) $(DEVVECSRC)gdevpdtd.c
+
+$(DEVOBJ)gdevpdtf.$(OBJ) : $(DEVVECSRC)gdevpdtf.c $(gx_h) $(memory__h)\
+ $(string__h) $(gserrors_h) $(gsutil_h)\
+ $(gxfcache_h) $(gxfcid_h) $(gxfcmap_h) $(gxfcopy_h) $(gxfont_h) $(gxfont1_h)\
+ $(gdevpsf_h) $(gdevpdfx_h) $(gdevpdtb_h) $(gdevpdtd_h) $(gdevpdtf_h) $(gdevpdtw_h)\
+ $(gdevpdti_h) $(MAKEDIRS)
+ $(DEVCC) $(DEVO_)gdevpdtf.$(OBJ) $(C_) $(DEVVECSRC)gdevpdtf.c
+
+$(DEVOBJ)gdevpdti.$(OBJ) : $(DEVVECSRC)gdevpdti.c $(memory__h) $(string__h) $(gx_h)\
+ $(gserrors_h) $(gsutil_h)\
+ $(gdevpdfx_h) $(gdevpdfg_h)\
+ $(gdevpdtf_h) $(gdevpdti_h) $(gdevpdts_h) $(gdevpdtw_h) $(gdevpdtt_h) $(gdevpdfo_h) \
+ $(MAKEDIRS)
+ $(DEVCC) $(DEVO_)gdevpdti.$(OBJ) $(C_) $(DEVVECSRC)gdevpdti.c
+
+$(DEVOBJ)gdevpdts.$(OBJ) : $(DEVVECSRC)gdevpdts.c $(gx_h) $(math__h) $(memory__h)\
+ $(gserrors_h) $(gdevpdfx_h) $(gdevpdfg_h) $(gdevpdtx_h) $(gdevpdtf_h)\
+ $(gdevpdts_h) $(gdevpdtt_h) $(MAKEDIRS)
+ $(DEVCC) $(DEVO_)gdevpdts.$(OBJ) $(C_) $(DEVVECSRC)gdevpdts.c
+
+$(DEVOBJ)gdevpdtt.$(OBJ) : $(DEVVECSRC)gdevpdtt.c $(gx_h) $(math__h) $(string__h)\
+ $(gserrors_h) $(gsencs_h) $(gscedata_h) $(gsmatrix_h) $(gzstate_h)\
+ $(gxfcache_h) $(gxfont_h) $(gxfont0_h) $(gxfcid_h) $(gxfcopy_h)\
+ $(gxfcmap_h) $(gxpath_h) $(gxchar_h) $(gxstate_h) $(gdevpdfx_h) $(gdevpdfg_h)\
+ $(gdevpdfo_h) $(gdevpdtx_h) $(gdevpdtd_h) $(gdevpdtf_h) $(gdevpdts_h) $(gdevpdtt_h)\
+ $(gdevpdti_h) $(gxhldevc_h) $(MAKEDIRS)
+ $(DEVCC) $(DEVO_)gdevpdtt.$(OBJ) $(C_) $(DEVVECSRC)gdevpdtt.c
+
+$(DEVOBJ)gdevpdtv.$(OBJ) : $(DEVVECSRC)gdevpdtv.c $(gx_h) $(gdevpdtv_h) \
+ $(MAKEDIRS)
+ $(DEVCC) $(DEVO_)gdevpdtv.$(OBJ) $(C_) $(DEVVECSRC)gdevpdtv.c
+
+$(DEVOBJ)gdevpdtw.$(OBJ) : $(DEVVECSRC)gdevpdtw.c $(gx_h) $(gserrors_h) $(memory__h)\
+ $(gxfcmap_h) $(gxfont_h) $(gxfcopy_h) $(gscencs_h)\
+ $(gdevpsf_h) $(gdevpdfx_h) $(gdevpdfo_h)\
+ $(gdevpdtd_h) $(gdevpdtf_h) $(gdevpdti_h) $(gdevpdtw_h) $(gdevpdtv_h) $(sarc4_h) \
+ $(MAKEDIRS)
+ $(DEVCC) $(DEVO_)gdevpdtw.$(OBJ) $(C_) $(DEVVECSRC)gdevpdtw.c
+
+$(DEVOBJ)whitelst.$(OBJ) : $(DEVVECSRC)whitelst.c $(whitelst_h) \
+ $(MAKEDIRS)
+ $(DEVCC) $(DEVO_)whitelst.$(OBJ) $(C_) $(DEVVECSRC)whitelst.c
+
+################ END PDF WRITER ################
+
+# High-level PCL XL writer
+
+pxl_=$(DEVOBJ)gdevpx.$(OBJ) $(DEVOBJ)gdevpxut.$(OBJ) $(HPPCL)
+$(DD)pxlmono.dev : $(DEVS_MAK) $(pxl_) $(GDEV) $(GLD)vector.dev \
+ $(MAKEDIRS)
+ $(SETDEV2) $(DD)pxlmono $(pxl_)
+ $(ADDMOD) $(DD)pxlmono -include $(GLD)vector
+
+$(DD)pxlcolor.dev : $(DEVS_MAK) $(pxl_) $(GDEV) $(GLD)vector.dev \
+ $(MAKEDIRS)
+ $(SETDEV2) $(DD)pxlcolor $(pxl_)
+ $(ADDMOD) $(DD)pxlcolor -include $(GLD)vector
+
+$(DEVOBJ)gdevpx.$(OBJ) : $(DEVVECSRC)gdevpx.c\
+ $(math__h) $(memory__h) $(string__h)\
+ $(gx_h) $(gsccolor_h) $(gsdcolor_h) $(gxiparam_h) $(gserrors_h)\
+ $(gxcspace_h) $(gxdevice_h) $(gxpath_h)\
+ $(gdevpxat_h) $(gdevpxen_h) $(gdevpxop_h) $(gdevpxut_h) $(gdevvec_h)\
+ $(srlx_h) $(strimpl_h) $(jpeglib__h) $(sdct_h) $(sjpeg_h) \
+ $(MAKEDIRS)
+ $(DEVCC) $(DEVO_)gdevpx.$(OBJ) $(C_) $(DEVVECSRC)gdevpx.c
+
+# XPS writer. Uses libtiff for all images
+
+libtiff_dev=$(TIFFGENDIR)$(D)libtiff.dev
+tiff_i_=-include $(TIFFGENDIR)$(D)libtiff
+
+xpswrite_=$(DEVOBJ)gdevxps.$(OBJ)
+$(DD)xpswrite.dev : $(DEVS_MAK) $(xpswrite_) $(GDEV) $(GLD)vector.dev \
+$(libtiff_dev) $(MAKEDIRS)
+ $(SETDEV2) $(DD)xpswrite $(xpswrite_)
+ $(ADDMOD) $(DD)xpswrite -include $(GLD)vector $(tiff_i_)
+
+$(DEVOBJ)gdevxps.$(OBJ) : $(DEVVECSRC)gdevxps.c $(gdevvec_h) \
+$(string__h) $(stdio__h) $(libtiff_dev) $(gx_h) $(gserrors_h) \
+$(gxpath_h) $(gzcpath_h) $(stream_h) $(zlib_h) \
+$(stdint__h) $(gdevtifs_h) $(gsicc_create_h) $(gsicc_cache_h) \
+$(gximdecode_h) $(MAKEDIRS)
+ $(XPSDEVCC) $(I_)$(TI_)$(_I) $(GLO_)gdevxps.$(OBJ) $(C_) $(DEVVECSRC)gdevxps.c
+
+###### --------------------- Raster file formats --------------------- ######
+
+### --------------------- The "plain bits" devices ---------------------- ###
+
+# This device also exercises the driver CRD facilities, which is why it
+# needs some additional files.
+
+bit_=$(DEVOBJ)gdevbit.$(OBJ) $(DEVOBJ)gdevdcrd.$(OBJ)
+
+$(DD)bit.dev : $(DEVS_MAK) $(bit_) $(GLD)page.dev $(GLD)cielib.dev $(GDEV) \
+ $(MAKEDIRS)
+ $(SETPDEV2) $(DD)bit $(bit_)
+ $(ADDMOD) $(DD)bit -include $(GLD)cielib
+
+$(DD)bitrgb.dev : $(DEVS_MAK) $(bit_) $(GLD)page.dev $(GLD)cielib.dev $(GDEV) \
+ $(MAKEDIRS)
+ $(SETPDEV2) $(DD)bitrgb $(bit_)
+ $(ADDMOD) $(DD)bitrgb -include $(GLD)cielib
+
+$(DD)bitcmyk.dev : $(DEVS_MAK) $(bit_) $(GLD)page.dev $(GLD)cielib.dev $(GDEV) \
+ $(MAKEDIRS)
+ $(SETPDEV2) $(DD)bitcmyk $(bit_)
+ $(ADDMOD) $(DD)bitcmyk -include $(GLD)cielib
+
+$(DD)bitrgbtags.dev : $(DEVS_MAK) $(bit_) $(GLD)page.dev $(GLD)cielib.dev\
+ $(GDEV) $(MAKEDIRS)
+ $(SETPDEV2) $(DD)bitrgbtags $(bit_)
+ $(ADDMOD) $(DD)bitrgbtags -include $(GLD)cielib
+
+$(DEVOBJ)gdevbit.$(OBJ) : $(DEVSRC)gdevbit.c $(PDEVH)\
+ $(gsparam_h) $(gdevdcrd_h) $(gscrd_h) $(gscrdp_h) $(gxlum_h) $(gxdcconv_h)\
+ $(gsutil_h) $(MAKEDIRS)
+ $(DEVCC) $(DEVO_)gdevbit.$(OBJ) $(C_) $(DEVSRC)gdevbit.c
+
+### ------------------------- .BMP file formats ------------------------- ###
+
+gdevbmp_h=$(DEVSRC)gdevbmp.h
+
+bmp_=$(DEVOBJ)gdevbmp.$(OBJ) $(DEVOBJ)gdevbmpc.$(OBJ) $(DEVOBJ)gdevpccm.$(OBJ)
+
+$(DEVOBJ)gdevbmp.$(OBJ) : $(DEVSRC)gdevbmp.c $(PDEVH) $(gdevbmp_h) $(gdevpccm_h) \
+ $(MAKEDIRS)
+ $(DEVCC) $(DEVO_)gdevbmp.$(OBJ) $(C_) $(DEVSRC)gdevbmp.c
+
+$(DEVOBJ)gdevbmpc.$(OBJ) : $(DEVSRC)gdevbmpc.c $(PDEVH) $(gdevbmp_h) \
+ $(MAKEDIRS)
+ $(DEVCC) $(DEVO_)gdevbmpc.$(OBJ) $(C_) $(DEVSRC)gdevbmpc.c
+
+$(DD)bmpmono.dev : $(DEVS_MAK) $(bmp_) $(GLD)page.dev $(GDEV) \
+ $(MAKEDIRS)
+ $(SETPDEV2) $(DD)bmpmono $(bmp_)
+
+$(DD)bmpgray.dev : $(DEVS_MAK) $(bmp_) $(GLD)page.dev $(GDEV) \
+ $(MAKEDIRS)
+ $(SETPDEV2) $(DD)bmpgray $(bmp_)
+
+$(DD)bmpsep1.dev : $(DEVS_MAK) $(bmp_) $(GLD)page.dev $(GDEV) \
+ $(MAKEDIRS)
+ $(SETPDEV2) $(DD)bmpsep1 $(bmp_)
+
+$(DD)bmpsep8.dev : $(DEVS_MAK) $(bmp_) $(GLD)page.dev $(GDEV) \
+ $(MAKEDIRS)
+ $(SETPDEV2) $(DD)bmpsep8 $(bmp_)
+
+$(DD)bmp16.dev : $(DEVS_MAK) $(bmp_) $(GLD)page.dev $(GDEV) \
+ $(MAKEDIRS)
+ $(SETPDEV2) $(DD)bmp16 $(bmp_)
+
+$(DD)bmp256.dev : $(DEVS_MAK) $(bmp_) $(GLD)page.dev $(GDEV) \
+ $(MAKEDIRS)
+ $(SETPDEV2) $(DD)bmp256 $(bmp_)
+
+$(DD)bmp16m.dev : $(DEVS_MAK) $(bmp_) $(GLD)page.dev $(GDEV) \
+ $(MAKEDIRS)
+ $(SETPDEV2) $(DD)bmp16m $(bmp_)
+
+$(DD)bmp32b.dev : $(DEVS_MAK) $(bmp_) $(GLD)page.dev $(GDEV) \
+ $(MAKEDIRS)
+ $(SETPDEV2) $(DD)bmp32b $(bmp_)
+
+### ------------- BMP driver that serves as demo of async rendering ---- ###
+
+bmpa_=$(DEVOBJ)gdevbmpa.$(OBJ) $(DEVOBJ)gdevbmpc.$(OBJ) $(DEVOBJ)gdevpccm.$(OBJ) $(DEVOBJ)gdevppla.$(OBJ)
+
+$(DEVOBJ)gdevbmpa.$(OBJ) : $(DEVSRC)gdevbmpa.c $(AK) $(stdio__h)\
+ $(gdevbmp_h) $(gdevprna_h) $(gdevpccm_h) $(gdevppla_h)\
+ $(gserrors_h) $(gpsync_h) $(MAKEDIRS)
+ $(DEVCC) $(DEVO_)gdevbmpa.$(OBJ) $(C_) $(DEVSRC)gdevbmpa.c
+
+$(DD)bmpamono.dev : $(DEVS_MAK) $(bmpa_) $(GLD)page.dev $(GLD)async.dev\
+ $(GDEV) $(MAKEDIRS)
+ $(SETPDEV2) $(DD)bmpamono $(bmpa_)
+ $(ADDMOD) $(DD)bmpamono -include $(GLD)async
+
+$(DD)bmpasep1.dev : $(DEVS_MAK) $(bmpa_) $(GLD)page.dev $(GLD)async.dev\
+ $(GDEV) $(MAKEDIRS)
+ $(SETPDEV2) $(DD)bmpasep1 $(bmpa_)
+ $(ADDMOD) $(DD)bmpasep1 -include $(GLD)async
+
+$(DD)bmpasep8.dev : $(DEVS_MAK) $(bmpa_) $(GLD)page.dev $(GLD)async.dev\
+ $(GDEV) $(MAKEDIRS)
+ $(SETPDEV2) $(DD)bmpasep8 $(bmpa_)
+ $(ADDMOD) $(DD)bmpasep8 -include $(GLD)async
+
+$(DD)bmpa16.dev : $(DEVS_MAK) $(bmpa_) $(GLD)page.dev $(GLD)async.dev\
+ $(GDEV) $(MAKEDIRS)
+ $(SETPDEV2) $(DD)bmpa16 $(bmpa_)
+ $(ADDMOD) $(DD)bmpa16 -include $(GLD)async
+
+$(DD)bmpa256.dev : $(DEVS_MAK) $(bmpa_) $(GLD)page.dev $(GLD)async.dev\
+ $(GDEV) $(MAKEDIRS)
+ $(SETPDEV2) $(DD)bmpa256 $(bmpa_)
+ $(ADDMOD) $(DD)bmpa256 -include $(GLD)async
+
+$(DD)bmpa16m.dev : $(DEVS_MAK) $(bmpa_) $(GLD)page.dev $(GLD)async.dev\
+ $(GDEV) $(MAKEDIRS)
+ $(SETPDEV2) $(DD)bmpa16m $(bmpa_)
+ $(ADDMOD) $(DD)bmpa16m -include $(GLD)async
+
+$(DD)bmpa32b.dev : $(DEVS_MAK) $(bmpa_) $(GLD)page.dev $(GLD)async.dev\
+ $(GDEV) $(MAKEDIRS)
+ $(SETPDEV2) $(DD)bmpa32b $(bmpa_)
+ $(ADDMOD) $(DD)bmpa32b -include $(GLD)async
+
+### --------------------------- The XCF device ------------------------- ###
+
+xcf_=$(DEVOBJ)gdevxcf.$(OBJ)
+
+$(DD)xcf.dev : $(DEVS_MAK) $(xcf_) $(GLD)page.dev $(GDEV) \
+ $(MAKEDIRS)
+ $(SETDEV) $(DD)xcf $(xcf_)
+
+$(DD)xcfcmyk.dev : $(DEVS_MAK) $(xcf_) $(GLD)page.dev $(GDEV) \
+ $(MAKEDIRS)
+ $(SETDEV) $(DD)xcfcmyk $(xcf_)
+
+$(DEVOBJ)gdevxcf.$(OBJ) : $(DEVSRC)gdevxcf.c $(PDEVH) $(math__h)\
+ $(gdevdcrd_h) $(gscrd_h) $(gscrdp_h) $(gsparam_h) $(gxlum_h)\
+ $(gxdcconv_h) $(gscms_h) $(gsicc_cache_h) $(gsicc_manage_h) \
+ $(MAKEDIRS)
+ $(DEVCC) $(DEVO_)gdevxcf.$(OBJ) $(C_) $(DEVSRC)gdevxcf.c
+
+### --------------------------- The PSD device ------------------------- ###
+
+psd_=$(DEVOBJ)gdevpsd.$(OBJ) $(GLOBJ)gdevdevn.$(OBJ) $(GLOBJ)gsequivc.$(OBJ)
+
+$(DD)psdrgb.dev : $(DEVS_MAK) $(psd_) $(GLD)page.dev $(GDEV) $(MAKEDIRS)
+ $(SETDEV) $(DD)psdrgb $(psd_)
+
+$(DD)psdcmyk.dev : $(DEVS_MAK) $(psd_) $(GLD)page.dev $(GDEV) $(MAKEDIRS)
+ $(SETDEV) $(DD)psdcmyk $(psd_)
+
+$(DEVOBJ)gdevpsd.$(OBJ) : $(DEVSRC)gdevpsd.c $(PDEVH) $(math__h)\
+ $(gdevdcrd_h) $(gscrd_h) $(gscrdp_h) $(gsparam_h) $(gxlum_h)\
+ $(gstypes_h) $(gxdcconv_h) $(gdevdevn_h) $(gsequivc_h)\
+ $(gscms_h) $(gsicc_cache_h) $(gsicc_manage_h) $(gxgetbit_h)\
+ $(gdevppla_h) $(MAKEDIRS)
+ $(DEVCC) $(DEVO_)gdevpsd.$(OBJ) $(C_) $(DEVSRC)gdevpsd.c
+
+### ----------------------- The permutation device --------------------- ###
+
+perm_=$(DEVOBJ)gdevperm.$(OBJ)
+
+$(DD)perm.dev : $(DEVS_MAK) $(perm_) $(GLD)page.dev $(GDEV)
+ $(SETDEV) $(DD)perm $(perm_)
+
+$(DEVOBJ)gdevperm.$(OBJ) : $(DEVSRC)gdevperm.c $(PDEVH) $(math__h)\
+ $(gdevdcrd_h) $(gscrd_h) $(gscrdp_h) $(gsparam_h) $(gxlum_h) $(MAKEDIRS)
+ $(DEVCC) $(DEVO_)gdevperm.$(OBJ) $(C_) $(DEVSRC)gdevperm.c
+
+### ------------------------ JBIG2 testing device ---------------------- ###
+
+gdevjbig2_=$(DEVOBJ)gdevjbig2.$(OBJ)
+
+$(DD)gdevjbig2.dev : $(DEVS_MAK) $(gdevjbig2_) $(GLD)page.dev $(GDEV) \
+ $(MAKEDIRS)
+ $(SETPDEV2) $(DD)gdevjbig2 $(gdevjbig2_)
+
+$(DEVOBJ)gdevjbig2.$(OBJ) : $(DEVSRC)gdevjbig2.c $(PDEVH)\
+ $(stream_h) $(strimpl_h) $(sjbig2_luratech_h) $(MAKEDIRS)
+ $(GDEVLDFJB2CC) $(DEVO_)gdevjbig2.$(OBJ) $(C_) $(DEVSRC)gdevjbig2.c
+
+### ------------------------ JPX testing device ----------------------
+###
+
+gdevjpx_=$(DEVOBJ)gdevjpx.$(OBJ)
+
+$(DD)jpxrgb.dev : $(DEVS_MAK) $(gdevjpx_) $(GLD)page.dev $(GDEV) \
+ $(MAKEDIRS)
+ $(SETPDEV2) $(DD)jpxrgb $(gdevjpx_)
+
+$(DD)jpxgray.dev : $(DEVS_MAK) $(gdevjpx_) $(GLD)page.dev $(GDEV) \
+ $(MAKEDIRS)
+ $(SETPDEV2) $(DD)jpxgray $(gdevjpx_)
+
+$(DD)jpxcmyk.dev : $(DEVS_MAK) $(gdevjpx_) $(GLD)page.dev $(GDEV) \
+ $(MAKEDIRS)
+ $(SETPDEV2) $(DD)jpxcmyk $(gdevjpx_)
+
+$(DEVOBJ)gdevjpx.$(OBJ) : $(DEVSRC)gdevjpx.c $(PDEVH)\
+ $(stream_h) $(strimpl_h) $(sjpx_luratech_h) $(MAKEDIRS)
+ $(GDEVLWFJPXCC) $(DEVO_)gdevjpx.$(OBJ) $(C_) $(DEVSRC)gdevjpx.c
+
+### ------------------------- JPEG file format ------------------------- ###
+
+jpeg_=$(DEVOBJ)gdevjpeg.$(OBJ)
+
+# RGB output
+$(DD)jpeg.dev : $(DEVS_MAK) $(jpeg_) $(GLD)sdcte.dev $(GLD)page.dev $(GDEV) \
+ $(MAKEDIRS)
+ $(SETPDEV2) $(DD)jpeg $(jpeg_)
+ $(ADDMOD) $(DD)jpeg -include $(GLD)sdcte
+
+# Gray output
+$(DD)jpeggray.dev : $(DEVS_MAK) $(jpeg_) $(GLD)sdcte.dev $(GLD)page.dev\
+ $(GDEV) $(MAKEDIRS)
+ $(SETPDEV2) $(DD)jpeggray $(jpeg_)
+ $(ADDMOD) $(DD)jpeggray -include $(GLD)sdcte
+
+# CMYK output
+$(DD)jpegcmyk.dev : $(DEVS_MAK) $(jpeg_) $(GLD)sdcte.dev $(GLD)page.dev\
+ $(GDEV) $(MAKEDIRS)
+ $(SETPDEV2) $(DD)jpegcmyk $(jpeg_)
+ $(ADDMOD) $(DD)jpegcmyk -include $(GLD)sdcte
+
+$(DEVOBJ)gdevjpeg.$(OBJ) : $(DEVSRC)gdevjpeg.c $(PDEVH)\
+ $(stdio__h) $(jpeglib__h)\
+ $(sdct_h) $(sjpeg_h) $(stream_h) $(strimpl_h) $(MAKEDIRS)
+ $(DEVCC) $(DEVO_)gdevjpeg.$(OBJ) $(C_) $(DEVSRC)gdevjpeg.c
+
+### ------------------------- MIFF file format ------------------------- ###
+### Right now we support only 24-bit direct color, but we might add more ###
+### formats in the future. ###
+
+miff_=$(DEVOBJ)gdevmiff.$(OBJ)
+
+$(DD)miff24.dev : $(DEVS_MAK) $(miff_) $(GLD)page.dev $(GDEV) $(MAKEDIRS)
+ $(SETPDEV) $(DD)miff24 $(miff_)
+
+$(DEVOBJ)gdevmiff.$(OBJ) : $(DEVSRC)gdevmiff.c $(PDEVH) $(MAKEDIRS)
+ $(DEVCC) $(DEVO_)gdevmiff.$(OBJ) $(C_) $(DEVSRC)gdevmiff.c
+
+### ------------------------- PCX file formats ------------------------- ###
+
+pcx_=$(DEVOBJ)gdevpcx.$(OBJ) $(DEVOBJ)gdevpccm.$(OBJ)
+
+$(DEVOBJ)gdevpcx.$(OBJ) : $(DEVSRC)gdevpcx.c $(PDEVH) $(gdevpccm_h) $(gxlum_h) \
+ $(MAKEDIRS)
+ $(DEVCC) $(DEVO_)gdevpcx.$(OBJ) $(C_) $(DEVSRC)gdevpcx.c
+
+$(DD)pcxmono.dev : $(DEVS_MAK) $(pcx_) $(GLD)page.dev $(GDEV) \
+ $(MAKEDIRS)
+ $(SETPDEV2) $(DD)pcxmono $(pcx_)
+
+$(DD)pcxgray.dev : $(DEVS_MAK) $(pcx_) $(GLD)page.dev $(GDEV) \
+ $(MAKEDIRS)
+ $(SETPDEV2) $(DD)pcxgray $(pcx_)
+
+$(DD)pcx16.dev : $(DEVS_MAK) $(pcx_) $(GLD)page.dev $(GDEV) \
+ $(MAKEDIRS)
+ $(SETPDEV2) $(DD)pcx16 $(pcx_)
+
+$(DD)pcx256.dev : $(DEVS_MAK) $(pcx_) $(GLD)page.dev $(GDEV) \
+ $(MAKEDIRS)
+ $(SETPDEV2) $(DD)pcx256 $(pcx_)
+
+$(DD)pcx24b.dev : $(DEVS_MAK) $(pcx_) $(GLD)page.dev $(GDEV) \
+ $(MAKEDIRS)
+ $(SETPDEV2) $(DD)pcx24b $(pcx_)
+
+$(DD)pcxcmyk.dev : $(DEVS_MAK) $(pcx_) $(GLD)page.dev $(GDEV) \
+ $(MAKEDIRS)
+ $(SETPDEV2) $(DD)pcxcmyk $(pcx_)
+
+# The 2-up PCX device is here only as an example, and for testing.
+
+$(DD)pcx2up.dev : $(DEVS_MAK) $(LIB_MAK) $(DEVOBJ)gdevp2up.$(OBJ)\
+ $(GLD)page.dev $(DD)pcx256.dev $(GDEV) $(MAKEDIRS)
+ $(SETPDEV) $(DD)pcx2up $(DEVOBJ)gdevp2up.$(OBJ)
+ $(ADDMOD) $(DD)pcx2up -include $(DD)pcx256
+
+$(DEVOBJ)gdevp2up.$(OBJ) : $(DEVSRC)gdevp2up.c $(AK)\
+ $(gdevpccm_h) $(gdevprn_h) $(gxclpage_h) $(MAKEDIRS)
+ $(DEVCC) $(DEVO_)gdevp2up.$(OBJ) $(C_) $(DEVSRC)gdevp2up.c
+
+### ------------------- Portable Bitmap file formats ------------------- ###
+### For more information, see the pam(5), pbm(5), pgm(5), and ppm(5) ###
+### man pages. ###
+
+pxm_=$(DEVOBJ)gdevpbm.$(OBJ) $(GLOBJ)gdevppla.$(OBJ) $(GLOBJ)gdevmpla.$(OBJ)
+
+$(DEVOBJ)gdevpbm.$(OBJ) : $(DEVSRC)gdevpbm.c $(PDEVH)\
+ $(gdevmpla_h) $(gdevplnx_h) $(gdevppla_h)\
+ $(gscdefs_h) $(gscspace_h) $(gxgetbit_h) $(gxiparam_h) $(gxlum_h) \
+ $(MAKEDIRS)
+ $(DEVCC) $(DEVO_)gdevpbm.$(OBJ) $(C_) $(DEVSRC)gdevpbm.c
+
+### Portable Bitmap (PBM, plain or raw format, magic numbers "P1" or "P4")
+
+$(DD)pbm.dev : $(DEVS_MAK) $(pxm_) $(GLD)page.dev $(GDEV) $(MAKEDIRS)
+ $(SETPDEV2) $(DD)pbm $(pxm_)
+
+$(DD)pbmraw.dev : $(DEVS_MAK) $(pxm_) $(GLD)page.dev $(GDEV) \
+ $(MAKEDIRS)
+ $(SETPDEV2) $(DD)pbmraw $(pxm_)
+
+### Portable Graymap (PGM, plain or raw format, magic numbers "P2" or "P5")
+
+$(DD)pgm.dev : $(DEVS_MAK) $(pxm_) $(GLD)page.dev $(GDEV) $(MAKEDIRS)
+ $(SETPDEV2) $(DD)pgm $(pxm_)
+
+$(DD)pgmraw.dev : $(DEVS_MAK) $(pxm_) $(GLD)page.dev $(GDEV) \
+ $(MAKEDIRS)
+ $(SETPDEV2) $(DD)pgmraw $(pxm_)
+
+# PGM with automatic optimization to PBM if this is possible.
+
+$(DD)pgnm.dev : $(DEVS_MAK) $(pxm_) $(GLD)page.dev $(GDEV) $(MAKEDIRS)
+ $(SETPDEV2) $(DD)pgnm $(pxm_)
+
+$(DD)pgnmraw.dev : $(DEVS_MAK) $(pxm_) $(GLD)page.dev $(GDEV) \
+ $(MAKEDIRS)
+ $(SETPDEV2) $(DD)pgnmraw $(pxm_)
+
+### Portable Pixmap (PPM, plain or raw format, magic numbers "P3" or "P6")
+
+$(DD)ppm.dev : $(DEVS_MAK) $(pxm_) $(GLD)page.dev $(GDEV) \
+ $(MAKEDIRS)
+ $(SETPDEV2) $(DD)ppm $(pxm_)
+
+$(DD)ppmraw.dev : $(DEVS_MAK) $(pxm_) $(GLD)page.dev $(GDEV) \
+ $(MAKEDIRS)
+ $(SETPDEV2) $(DD)ppmraw $(pxm_)
+
+# PPM with automatic optimization to PGM or PBM if possible.
+
+$(DD)pnm.dev : $(DEVS_MAK) $(pxm_) $(GLD)page.dev $(GDEV) \
+ $(MAKEDIRS)
+ $(SETPDEV2) $(DD)pnm $(pxm_)
+
+$(DD)pnmraw.dev : $(DEVS_MAK) $(pxm_) $(GLD)page.dev $(GDEV) \
+ $(MAKEDIRS)
+ $(SETPDEV2) $(DD)pnmraw $(pxm_)
+
+$(DD)pnmcmyk.dev : $(DEVS_MAK) $(pxm_) $(GLD)page.dev $(GDEV) \
+ $(MAKEDIRS)
+ $(SETPDEV2) $(DD)pnmcmyk $(pxm_)
+
+### Portable inKmap (CMYK internally, converted to PPM=RGB at output time)
+
+$(DD)pkm.dev : $(DEVS_MAK) $(pxm_) $(GLD)page.dev $(GDEV) \
+ $(MAKEDIRS)
+ $(SETPDEV2) $(DD)pkm $(pxm_)
+
+$(DD)pkmraw.dev : $(DEVS_MAK) $(pxm_) $(GLD)page.dev $(GDEV) \
+ $(MAKEDIRS)
+ $(SETPDEV2) $(DD)pkmraw $(pxm_)
+
+### Portable Separated map (CMYK internally, produces 4 monobit pages)
+
+$(DD)pksm.dev : $(DEVS_MAK) $(pxm_) $(GLD)page.dev $(GDEV) \
+ $(MAKEDIRS)
+ $(SETPDEV2) $(DD)pksm $(pxm_)
+
+$(DD)pksmraw.dev : $(DEVS_MAK) $(pxm_) $(GLD)page.dev $(GDEV) \
+ $(MAKEDIRS)
+ $(SETPDEV2) $(DD)pksmraw $(pxm_)
+
+### Plan 9 bitmap format
+
+$(DD)plan9bm.dev : $(DEVS_MAK) $(pxm_) $(GLD)page.dev $(GDEV) \
+ $(MAKEDIRS)
+ $(SETPDEV2) $(DD)plan9bm $(pxm_)
+
+### Portable Arbitrary Map (PAM, magic number "P7", CMYK)
+
+$(DD)pamcmyk4.dev : $(DEVS_MAK) $(pxm_) $(GLD)page.dev $(GDEV) \
+ $(MAKEDIRS)
+ $(SETPDEV2) $(DD)pamcmyk4 $(pxm_)
+
+$(DD)pamcmyk32.dev : $(DEVS_MAK) $(pxm_) $(GLD)page.dev $(GDEV) \
+ $(MAKEDIRS)
+ $(SETPDEV2) $(DD)pamcmyk32 $(pxm_)
+
+# Keep the older (non-descriptive) name in case it is being used
+$(DD)pam.dev : $(DEVS_MAK) $(pxm_) $(GLD)page.dev $(GDEV) $(MAKEDIRS)
+ $(SETPDEV2) $(DD)pam $(pxm_)
+
+### --------------- Portable Network Graphics file format --------------- ###
+### Requires libpng 0.81 and zlib 0.95 (or more recent versions). ###
+### See png.mak and zlib.mak for more details. ###
+
+png_=$(DEVOBJ)gdevpng.$(OBJ) $(DEVOBJ)gdevpccm.$(OBJ)
+libpng_dev=$(PNGGENDIR)$(D)libpng.dev
+png_i_=-include $(PNGGENDIR)$(D)libpng
+
+$(DEVOBJ)gdevpng.$(OBJ) : $(DEVSRC)gdevpng.c\
+ $(gdevprn_h) $(gdevpccm_h) $(gscdefs_h) $(png__h) $(MAKEDIRS)
+ $(CC_) $(I_)$(DEVI_) $(II)$(PI_)$(_I) $(PCF_) $(GLF_) $(DEVO_)gdevpng.$(OBJ) $(C_) $(DEVSRC)gdevpng.c
+
+$(DD)pngmono.dev : $(DEVS_MAK) $(libpng_dev) $(png_) $(GLD)page.dev $(GDEV) \
+ $(MAKEDIRS)
+ $(SETPDEV2) $(DD)pngmono $(png_)
+ $(ADDMOD) $(DD)pngmono $(png_i_)
+
+$(DD)pngmonod.dev : $(DEVS_MAK) $(libpng_dev) $(png_) $(GLD)page.dev $(GDEV) \
+ $(MAKEDIRS)
+ $(SETPDEV2) $(DD)pngmonod $(png_)
+ $(ADDMOD) $(DD)pngmonod $(png_i_)
+
+$(DD)pnggray.dev : $(DEVS_MAK) $(libpng_dev) $(png_) $(GLD)page.dev $(GDEV) \
+ $(MAKEDIRS)
+ $(SETPDEV2) $(DD)pnggray $(png_)
+ $(ADDMOD) $(DD)pnggray $(png_i_)
+
+$(DD)png16.dev : $(DEVS_MAK) $(libpng_dev) $(png_) $(GLD)page.dev $(GDEV) \
+ $(MAKEDIRS)
+ $(SETPDEV2) $(DD)png16 $(png_)
+ $(ADDMOD) $(DD)png16 $(png_i_)
+
+$(DD)png256.dev : $(DEVS_MAK) $(libpng_dev) $(png_) $(GLD)page.dev $(GDEV) \
+ $(MAKEDIRS)
+ $(SETPDEV2) $(DD)png256 $(png_)
+ $(ADDMOD) $(DD)png256 $(png_i_)
+
+$(DD)png16m.dev : $(DEVS_MAK) $(libpng_dev) $(png_) $(GLD)page.dev $(GDEV) \
+ $(MAKEDIRS)
+ $(SETPDEV2) $(DD)png16m $(png_)
+ $(ADDMOD) $(DD)png16m $(png_i_)
+
+$(DD)png48.dev : $(DEVS_MAK) $(libpng_dev) $(png_) $(GLD)page.dev $(GDEV) \
+ $(MAKEDIRS)
+ $(SETPDEV2) $(DD)png48 $(png_)
+ $(ADDMOD) $(DD)png48 $(png_i_)
+
+$(DD)pngalpha.dev : $(DEVS_MAK) $(libpng_dev) $(png_) $(GLD)page.dev $(GDEV) \
+ $(MAKEDIRS)
+ $(SETPDEV2) $(DD)pngalpha $(png_)
+ $(ADDMOD) $(DD)pngalpha $(png_i_)
+
+### --------------- Portable Network Graphics file format --------------- ###
+### Requires zlib 0.95 (or more recent versions). ###
+### See zlib.mak for more details. ###
+
+fpng_=$(DEVOBJ)gdevfpng.$(OBJ) $(DEVOBJ)gdevpccm.$(OBJ)
+
+$(DEVOBJ)gdevfpng_0.$(OBJ) : $(DEVSRC)gdevfpng.c\
+ $(gdevprn_h) $(gdevpccm_h) $(gscdefs_h) $(zlib_h) $(MAKEDIRS)
+ $(CC_) $(I_)$(DEVI_) $(II)$(ZI_)$(_I) $(PCF_) $(GLF_) $(DEVO_)gdevfpng_0.$(OBJ) $(C_) $(DEVSRC)gdevfpng.c
+
+$(DEVOBJ)gdevfpng_1.$(OBJ) : $(DEVSRC)gdevfpng.c\
+ $(gdevprn_h) $(gdevpccm_h) $(gscdefs_h) $(MAKEDIRS)
+ $(CC_) $(I_)$(DEVI_) $(II)$(ZI_)$(_I) $(PCF_) $(GLF_) $(DEVO_)gdevfpng_1.$(OBJ) $(C_) $(DEVSRC)gdevfpng.c
+
+$(DEVOBJ)gdevfpng.$(OBJ) : $(DEVOBJ)gdevfpng_$(SHARE_ZLIB).$(OBJ) $(MAKEDIRS)
+ $(CP_) $(DEVOBJ)gdevfpng_$(SHARE_ZLIB).$(OBJ) $(DEVOBJ)gdevfpng.$(OBJ)
+
+$(DD)fpng.dev : $(DEVS_MAK) $(fpng_) $(GLD)page.dev $(GDEV) $(MAKEDIRS)
+ $(SETPDEV2) $(DD)fpng $(fpng_)
+ $(ADDMOD) $(DD)fpng $(fpng_i_)
+
+### ---------------------- PostScript image format ---------------------- ###
+### These devices make it possible to print monochrome Level 2 files on a ###
+### Level 1 printer, by converting them to a bitmap in PostScript ###
+### format. They also can convert big, complex color PostScript files ###
+### to (often) smaller and more easily printed bitmaps. ###
+
+psim_=$(DEVOBJ)gdevpsim.$(OBJ) $(DEVOBJ)gdevpsu.$(OBJ)
+
+$(DEVOBJ)gdevpsim.$(OBJ) : $(DEVSRC)gdevpsim.c $(PDEVH)\
+ $(gdevpsu_h)\
+ $(sa85x_h) $(srlx_h) $(stream_h) $(strimpl_h) $(MAKEDIRS)
+ $(DEVCC) $(DEVO_)gdevpsim.$(OBJ) $(C_) $(DEVSRC)gdevpsim.c
+
+### --- Minimum Feature Size support functions --- ###
+
+# Required by fax and 1bpp tiff functions. The grouping of functions
+# within files means it is also pulled in for color/cmyk tiff functions
+# too.
+
+minftrsz_h=$(DEVSRC)minftrsz.h $(std_h)
+minftrsz_=$(minftrsz_h) $(DEVOBJ)minftrsz.$(OBJ)
+
+$(DEVOBJ)minftrsz.$(OBJ) : $(DEVSRC)minftrsz.c $(minftrsz_h) $(MAKEDIRS)
+ $(DEVCC) $(DEVO_)minftrsz.$(OBJ) $(C_) $(DEVSRC)minftrsz.c
+
+
+### ---------------- Fax encoding ---------------- ###
+
+# By default, these drivers recognize 3 page sizes -- (U.S.) letter, A4, and
+# B4 -- and adjust the page width to the nearest legal value for real fax
+# systems (1728 or 2048 pixels). To suppress this, set the device parameter
+# AdjustWidth to 0 (e.g., -dAdjustWidth=0 on the command line).
+
+gdevfax_h=$(DEVSRC)gdevfax.h
+
+fax_=$(DEVOBJ)gdevfax.$(OBJ) $(DEVOBJ)minftrsz.$(OBJ)
+$(DD)fax.dev : $(DEVS_MAK) $(libtiff_dev) $(fax_) $(GLD)cfe.dev $(minftrsz_h)\
+ $(GDEV) $(MAKEDIRS)
+ $(SETMOD) $(DD)fax $(fax_)
+ $(ADDMOD) $(DD)fax -include $(GLD)cfe $(tiff_i_)
+
+$(DEVOBJ)gdevfax.$(OBJ) : $(DEVSRC)gdevfax.c $(PDEVH)\
+ $(gdevfax_h) $(scfx_h) $(strimpl_h) $(MAKEDIRS)
+ $(DEVCC) $(DEVO_)gdevfax.$(OBJ) $(C_) $(DEVSRC)gdevfax.c
+
+$(DD)faxg3.dev : $(DEVS_MAK) $(libtiff_dev) $(DD)fax.dev $(GDEV) \
+ $(MAKEDIRS)
+ $(SETDEV2) $(DD)faxg3 -include $(DD)fax
+ $(ADDMOD) $(DD)faxg3 $(tiff_i_)
+
+$(DD)faxg32d.dev : $(DEVS_MAK) $(libtiff_dev) $(DD)fax.dev $(GDEV) \
+ $(MAKEDIRS)
+ $(SETDEV2) $(DD)faxg32d -include $(DD)fax
+ $(ADDMOD) $(DD)faxg32d $(tiff_i_)
+
+$(DD)faxg4.dev : $(DEVS_MAK) $(libtiff_dev) $(DD)fax.dev $(GDEV) \
+ $(MAKEDIRS)
+ $(SETDEV2) $(DD)faxg4 -include $(DD)fax
+ $(ADDMOD) $(DD)faxg4 $(tiff_i_)
+
+### -------------------- Plain or TIFF fax encoding --------------------- ###
+### Use -sDEVICE=tiffg3 or tiffg4 and ###
+### -r204x98 for low resolution output, or ###
+### -r204x196 for high resolution output ###
+
+gdevtifs_h=$(DEVSRC)gdevtifs.h
+
+tfax_=$(DEVOBJ)gdevtfax.$(OBJ) $(DEVOBJ)minftrsz.$(OBJ)
+$(DD)tfax.dev : $(DEVS_MAK) $(libtiff_dev) $(tfax_) $(GLD)cfe.dev\
+ $(GLD)lzwe.dev $(GLD)rle.dev $(DD)fax.dev $(DD)tiffs.dev $(minftrsz_h)\
+ $(gstiffio_h) $(GDEV) $(MAKEDIRS)
+ $(SETMOD) $(DD)tfax $(tfax_)
+ $(ADDMOD) $(DD)tfax -include $(GLD)cfe $(GLD)lzwe $(GLD)rle
+ $(ADDMOD) $(DD)tfax -include $(DD)fax $(DD)tiffs $(tiff_i_)
+
+$(DEVOBJ)gdevtfax.$(OBJ) : $(DEVSRC)gdevtfax.c $(PDEVH)\
+ $(stdint__h) $(gdevfax_h) $(gdevtifs_h)\
+ $(scfx_h) $(slzwx_h) $(srlx_h) $(strimpl_h) $(MAKEDIRS)
+ $(DEVCC) $(I_)$(TI_)$(_I) $(DEVO_)gdevtfax.$(OBJ) $(C_) $(DEVSRC)gdevtfax.c
+
+### ---------------------------- TIFF formats --------------------------- ###
+
+tiffs_=$(DEVOBJ)gdevtifs.$(OBJ) $(DEVOBJ)minftrsz.$(OBJ)
+
+tiffgray_=$(DEVOBJ)gdevtsep.$(OBJ) $(GLOBJ)gsequivc.$(OBJ) $(DEVOBJ)minftrsz.$(OBJ)
+
+tiffsep_=$(tiffgray_) $(GLOBJ)gdevdevn.$(OBJ) $(GLOBJ)gsequivc.$(OBJ) \
+$(GLOBJ)gdevppla.$(OBJ)
+
+$(DD)tiffs.dev : $(DEVS_MAK) $(libtiff_dev) $(tiffs_) $(GLD)page.dev\
+ $(minftrsz_) $(GDEV) $(MAKEDIRS)
+ $(SETMOD) $(DD)tiffs $(tiffs_)
+ $(ADDMOD) $(DD)tiffs -include $(GLD)page $(tiff_i_)
+
+$(DEVOBJ)gdevtifs.$(OBJ) : $(DEVSRC)gdevtifs.c $(PDEVH) $(stdint__h) $(stdio__h) $(time__h)\
+ $(gdevtifs_h) $(gscdefs_h) $(gstypes_h) $(stream_h) $(strmio_h) $(gstiffio_h) $(MAKEDIRS)
+ $(DEVCC) $(I_)$(DEVI_) $(II)$(TI_)$(_I) $(DEVO_)gdevtifs.$(OBJ) $(C_) $(DEVSRC)gdevtifs.c
+
+# Black & white, G3/G4 fax
+# NOTE: see under faxg* above regarding page width adjustment.
+
+$(DD)tiffcrle.dev : $(DEVS_MAK) $(libtiff_dev) $(DD)tfax.dev $(minftrsz_)\
+ $(GDEV) $(MAKEDIRS)
+ $(SETDEV2) $(DD)tiffcrle -include $(DD)tfax
+ $(ADDMOD) $(DD)tiffcrle $(tiff_i_)
+
+$(DD)tiffg3.dev : $(DEVS_MAK) $(libtiff_dev) $(DD)tfax.dev $(minftrsz_)\
+ $(GDEV) $(MAKEDIRS)
+ $(SETDEV2) $(DD)tiffg3 -include $(DD)tfax
+ $(ADDMOD) $(DD)tiffg3 $(tiff_i_)
+
+$(DD)tiffg32d.dev : $(DEVS_MAK) $(libtiff_dev) $(DD)tfax.dev $(minftrsz_)\
+ $(GDEV) $(MAKEDIRS)
+ $(SETDEV2) $(DD)tiffg32d -include $(DD)tfax
+ $(ADDMOD) $(DD)tiffg32d $(tiff_i_)
+
+$(DD)tiffg4.dev : $(DEVS_MAK) $(libtiff_dev) $(DD)tfax.dev $(minftrsz_)\
+ $(GDEV) $(MAKEDIRS)
+ $(SETDEV2) $(DD)tiffg4 -include $(DD)tfax
+ $(ADDMOD) $(DD)tiffg4 $(tiff_i_)
+
+# Black & white, LZW compression
+
+$(DD)tifflzw.dev : $(DEVS_MAK) $(libtiff_dev) $(DD)tfax.dev $(minftrsz_)\
+ $(GDEV) $(MAKEDIRS)
+ $(SETDEV2) $(DD)tifflzw -include $(DD)tfax
+ $(ADDMOD) $(DD)tifflzw $(tiff_i_)
+
+# Black & white, PackBits compression
+
+$(DD)tiffpack.dev : $(DEVS_MAK) $(libtiff_dev) $(DD)tfax.dev $(minftrsz_)\
+ $(GDEV) $(MAKEDIRS)
+ $(SETDEV2) $(DD)tiffpack -include $(DD)tfax
+ $(ADDMOD) $(DD)tiffpack $(tiff_i_)
+
+# TIFF Gray, no compression
+
+$(DD)tiffgray.dev : $(DEVS_MAK) $(libtiff_dev) $(tiffgray_) $(DD)tiffs.dev\
+ $(minftrsz_h) $(GDEV) $(MAKEDIRS)
+ $(SETPDEV2) $(DD)tiffgray $(tiffgray_)
+ $(ADDMOD) $(DD)tiffgray -include $(DD)tiffs $(tiff_i_)
+
+$(DEVOBJ)gdevtsep.$(OBJ) : $(DEVSRC)gdevtsep.c $(PDEVH) $(stdint__h)\
+ $(gdevtifs_h) $(gdevdevn_h) $(gsequivc_h) $(stdio__h) $(ctype__h)\
+ $(gxgetbit_h) $(gdevppla_h) $(gp_h) $(gstiffio_h) $(GDEV) $(MAKEDIRS)
+ $(DEVCC) $(I_)$(TI_)$(_I) $(DEVO_)gdevtsep.$(OBJ) $(C_) $(DEVSRC)gdevtsep.c
+
+# TIFF Scaled (downscaled gray -> mono), configurable compression
+
+tiffscaled_=$(tiffsep_)
+
+$(DD)tiffscaled.dev : $(DEVS_MAK) $(libtiff_dev) $(tiffscaled_) $(DD)tiffs.dev\
+ $(minftrsz_h) $(GDEV) $(MAKEDIRS)
+ $(SETPDEV2) $(DD)tiffscaled $(tiffscaled_)
+ $(ADDMOD) $(DD)tiffscaled -include $(DD)tiffs $(tiff_i_)
+
+# TIFF Scaled 8 (downscaled gray -> gray), configurable compression
+
+tiffscaled8_=$(tiffseop_)
+
+$(DD)tiffscaled8.dev : $(DEVS_MAK) $(libtiff_dev) $(tiffscaled8_)\
+ $(DD)tiffs.dev $(minftrsz_h) $(GDEV) $(MAKEDIRS)
+ $(SETPDEV2) $(DD)tiffscaled8 $(tiffscaled8_)
+ $(ADDMOD) $(DD)tiffscaled8 -include $(DD)tiffs $(tiff_i_)
+
+# TIFF Scaled 24 (downscaled rgb -> rgb), configurable compression
+
+tiffscaled24_=$(tiffsep_)
+
+$(DD)tiffscaled24.dev : $(DEVS_MAK) $(libtiff_dev) $(tiffscaled24_)\
+ $(DD)tiffs.dev $(minftrsz_h) $(GDEV) $(MAKEDIRS)
+ $(SETPDEV2) $(DD)tiffscaled24 $(tiffscaled8_)
+ $(ADDMOD) $(DD)tiffscaled24 -include $(DD)tiffs $(tiff_i_)
+
+# TIFF Scaled 32 (downscaled cmyk -> cmyk), configurable compression
+
+tiffscaled32_=$(tiffsep_)
+
+$(DD)tiffscaled32.dev : $(DEVS_MAK) $(libtiff_dev) $(tiffscaled32_)\
+ $(DD)tiffs.dev $(minftrsz_h) $(GDEV) $(MAKEDIRS)
+ $(SETPDEV2) $(DD)tiffscaled32 $(tiffscaled8_)
+ $(ADDMOD) $(DD)tiffscaled32 -include $(DD)tiffs $(tiff_i_)
+
+# TIFF Scaled 4 (downscaled cmyk -> cmyk), configurable compression
+
+tiffscaled4_=$(tiffsep_)
+
+$(DD)tiffscaled4.dev : $(DEVS_MAK) $(libtiff_dev) $(tiffscaled4_)\
+ $(DD)tiffs.dev $(minftrsz_h) $(GDEV) $(MAKEDIRS)
+ $(SETPDEV2) $(DD)tiffscaled4 $(tiffscaled8_)
+ $(ADDMOD) $(DD)tiffscaled4 -include $(DD)tiffs $(tiff_i_)
+
+# TIFF RGB, no compression
+
+tiffrgb_=$(DEVOBJ)gdevtfnx.$(OBJ) $(DEVOBJ)minftrsz.$(OBJ)
+
+$(DD)tiff12nc.dev : $(DEVS_MAK) $(libtiff_dev) $(tiffrgb_) $(DD)tiffs.dev\
+ $(minftrsz_h) $(GDEV) $(MAKEDIRS)
+ $(SETPDEV2) $(DD)tiff12nc $(tiffrgb_)
+ $(ADDMOD) $(DD)tiff12nc -include $(DD)tiffs $(tiff_i_)
+
+$(DD)tiff24nc.dev : $(DEVS_MAK) $(libtiff_dev) $(tiffrgb_) $(DD)tiffs.dev\
+ $(minftrsz_h) $(GDEV) $(MAKEDIRS)
+ $(SETPDEV2) $(DD)tiff24nc $(tiffrgb_)
+ $(ADDMOD) $(DD)tiff24nc -include $(DD)tiffs $(tiff_i_)
+
+$(DD)tiff48nc.dev : $(DEVS_MAK) $(libtiff_dev) $(tiffrgb_) $(DD)tiffs.dev\
+ $(minftrsz_h) $(GDEV) $(MAKEDIRS)
+ $(SETPDEV2) $(DD)tiff48nc $(tiffrgb_)
+ $(ADDMOD) $(DD)tiff48nc -include $(DD)tiffs $(tiff_i_)
+
+$(DEVOBJ)gdevtfnx.$(OBJ) : $(DEVSRC)gdevtfnx.c $(PDEVH) $(stdint__h)\
+ $(gdevtifs_h) $(gscms_h) $(gstiffio_h) $(GDEV) $(MAKEDIRS)
+ $(DEVCC) $(I_)$(TI_)$(_I) $(DEVO_)gdevtfnx.$(OBJ) $(C_) $(DEVSRC)gdevtfnx.c
+
+# TIFF CMYK, no compression
+
+$(DD)tiff32nc.dev : $(DEVS_MAK) $(libtiff_dev) $(tiffgray_) $(DD)tiffs.dev\
+ $(minftrsz_h) $(GDEV) $(MAKEDIRS)
+ $(SETPDEV2) $(DD)tiff32nc $(tiffgray_)
+ $(ADDMOD) $(DD)tiff32nc -include $(DD)tiffs $(tiff_i_)
+
+$(DD)tiff64nc.dev : $(DEVS_MAK) $(libtiff_dev) $(tiffgray_) $(DD)tiffs.dev\
+ $(minftrsz_h) $(GDEV) $(MAKEDIRS)
+ $(SETPDEV2) $(DD)tiff64nc $(tiffgray_)
+ $(ADDMOD) $(DD)tiff64nc -include $(DD)tiffs $(tiff_i_)
+
+#
+# Create separation files (tiffgray) plus CMYK composite (tiff32nc)
+
+$(DD)tiffsep.dev : $(DEVS_MAK) $(libtiff_dev) $(tiffsep_) $(DD)tiffs.dev\
+ $(minftrsz_h) $(GDEV) $(MAKEDIRS)
+ $(SETPDEV2) $(DD)tiffsep $(tiffsep_)
+ $(ADDMOD) $(DD)tiffsep -include $(DD)tiffs $(tiff_i_)
+
+#
+# Create separation files (tiff 1-bit)
+
+$(DD)tiffsep1.dev : $(DEVS_MAK) $(tiffsep_) $(DD)tiffs.dev $(minftrsz_h)\
+ $(GDEV) $(MAKEDIRS)
+ $(SETPDEV2) $(DD)tiffsep1 $(tiffsep_)
+ $(ADDMOD) $(DD)tiffsep1 -include $(DD)tiffs
+
+#
+# PLANar device
+
+plan_=$(DEVOBJ)gdevplan.$(OBJ) $(DEVOBJ)gdevppla.$(OBJ) $(DEVOBJ)gdevmpla.$(OBJ)
+
+$(DEVOBJ)gdevplan.$(OBJ) : $(DEVSRC)gdevplan.c $(PDEVH)\
+ $(gdevmpla_h) $(gdevplnx_h) $(gdevppla_h)\
+ $(gscdefs_h) $(gscspace_h) $(gxgetbit_h) $(gxiparam_h) $(gxlum_h) \
+ $(MAKEDIRS)
+ $(DEVCC) $(DEVO_)gdevplan.$(OBJ) $(C_) $(DEVSRC)gdevplan.c
+
+$(DD)plan.dev : $(DEVS_MAK) $(plan_) $(GLD)page.dev $(GDEV) \
+ $(MAKEDIRS)
+ $(SETPDEV2) $(DD)plan $(plan_)
+
+$(DD)plang.dev : $(DEVS_MAK) $(plan_) $(GLD)page.dev $(GDEV) \
+ $(MAKEDIRS)
+ $(SETPDEV2) $(DD)plang $(plan_)
+
+$(DD)planm.dev : $(DEVS_MAK) $(plan_) $(GLD)page.dev $(GDEV) \
+ $(MAKEDIRS)
+ $(SETPDEV2) $(DD)planm $(plan_)
+
+$(DD)planc.dev : $(DEVS_MAK) $(plan_) $(GLD)page.dev $(GDEV) \
+ $(MAKEDIRS)
+ $(SETPDEV2) $(DD)planc $(plan_)
+
+$(DD)plank.dev : $(DEVS_MAK) $(plan_) $(GLD)page.dev $(GDEV) \
+ $(MAKEDIRS)
+ $(SETPDEV2) $(DD)plank $(plan_)
+
+#
+# PLanar Interlaced Buffer device
+
+plib_=$(DEVOBJ)gdevplib.$(OBJ) $(GLOBJ)gdevppla.$(OBJ) $(GLOBJ)gdevmpla.$(OBJ)
+
+$(DEVOBJ)gdevplib.$(OBJ) : $(DEVSRC)gdevplib.c $(PDEVH)\
+ $(gdevmpla_h) $(gdevplnx_h) $(gdevppla_h)\
+ $(gscdefs_h) $(gscspace_h) $(gxgetbit_h) $(gxiparam_h) $(gxlum_h) \
+ $(MAKEDIRS)
+ $(DEVCC) $(DEVO_)gdevplib.$(OBJ) $(C_) $(DEVSRC)gdevplib.c
+
+$(DD)plib.dev : $(DEVS_MAK) $(plib_) $(GLD)page.dev $(GDEV) $(MAKEDIRS)
+ $(SETPDEV2) $(DD)plib $(plib_)
+
+$(DD)plibg.dev : $(DEVS_MAK) $(plib_) $(GLD)page.dev $(GDEV) $(MAKEDIRS)
+ $(SETPDEV2) $(DD)plibg $(plib_)
+
+$(DD)plibm.dev : $(DEVS_MAK) $(plib_) $(GLD)page.dev $(GDEV) $(MAKEDIRS)
+ $(SETPDEV2) $(DD)plibm $(plib_)
+
+$(DD)plibc.dev : $(DEVS_MAK) $(plib_) $(GLD)page.dev $(GDEV) $(MAKEDIRS)
+ $(SETPDEV2) $(DD)plibc $(plib_)
+
+$(DD)plibk.dev : $(DEVS_MAK) $(plib_) $(GLD)page.dev $(GDEV) $(MAKEDIRS)
+ $(SETPDEV2) $(DD)plibk $(plib_)
+
+# ink coverage device -- a device that records the ink coverage
+# on each page, and discards the page.
+$(DD)inkcov.dev : $(ECHOGS_XE) $(LIB_MAK) $(DEVOBJ)gdevicov.$(OBJ) \
+ $(MAKEDIRS)
+ $(SETDEV2) $(DD)inkcov $(DEVOBJ)gdevicov.$(OBJ)
+
+$(DD)ink_cov.dev : $(ECHOGS_XE) $(LIB_MAK) $(DEVOBJ)gdevicov.$(OBJ) \
+ $(MAKEDIRS)
+ $(SETDEV2) $(DD)ink_cov $(DEVOBJ)gdevicov.$(OBJ)
+
+$(DEVOBJ)gdevicov.$(OBJ) : $(DEVSRC)gdevicov.c $(AK) \
+ $(arch_h) $(gdevprn_h) $(stdio__h) $(stdint__h) $(MAKEDIRS)
+ $(DEVCC) $(DEVO_)gdevicov.$(OBJ) $(C_) $(DEVSRC)gdevicov.c
+
+
+### ------------------------------- CUPS ------------------------------- ###
+lcups_dev=$(LCUPSGENDIR)$(D)lcups.dev
+lcupsi_dev=$(LCUPSIGENDIR)$(D)lcupsi.dev
+
+cups_=$(DEVOBJ)gdevcups.$(OBJ)
+$(DD)cups.dev : $(DEVS_MAK) $(lcups_dev) $(lcupsi_dev) $(cups_) $(GDEV) \
+ $(MAKEDIRS)
+ $(SETPDEV2) $(DD)cups $(cups_)
+ $(ADDMOD) $(DD)cups -include $(lcups_dev)
+ $(ADDMOD) $(DD)cups -include $(lcupsi_dev)
+$(DD)pwgraster.dev : $(DEVS_MAK) $(lcups_dev) $(lcupsi_dev) $(cups_) $(GDEV) \
+ $(MAKEDIRS)
+ $(SETPDEV2) $(DD)pwgraster $(cups_)
+ $(ADDMOD) $(DD)pwgraster -include $(lcups_dev)
+ $(ADDMOD) $(DD)pwgraster -include $(lcupsi_dev)
+
+$(DEVOBJ)gdevcups.$(OBJ) : $(LCUPSSRCDIR)$(D)gdevcups.c $(std_h) $(MAKEDIRS)
+ $(CUPS_CC) $(DEVO_)gdevcups.$(OBJ) $(C_) $(CFLAGS) $(CUPSCFLAGS) \
+ $(I_)$(GLSRC) \
+ $(I_)$(DEVSRC) \
+ $(I_)$(DEVOBJ) $(I_)$(LCUPSSRCDIR)$(D)libs \
+ $(LCUPSSRCDIR)$(D)gdevcups.c
+
+### ---------------------------- Tracing -------------------------------- ###
+
+# A tracing device, also an example of a high-level device.
+
+$(DEVOBJ)gdevtrac.$(OBJ) : $(DEVSRC)gdevtrac.c $(AK) $(gx_h)\
+ $(gserrors_h) $(gscspace_h)\
+ $(gxdevice_h) $(gxdht_h) $(gxfont_h) $(gxiparam_h) $(gxistate_h)\
+ $(gxpaint_h) $(gxtmap_h) $(gzcpath_h) $(gzpath_h) $(MAKEDIRS)
+ $(DEVCC) $(DEVO_)gdevtrac.$(OBJ) $(C_) $(DEVSRC)gdevtrac.c
+
+$(DD)tracedev.dev : $(DEVS_MAK) $(GDEV) $(DEVOBJ)gdevtrac.$(OBJ) \
+ $(MAKEDIRS)
+ $(SETMOD) $(DD)tracedev -dev2 tr_mono tr_rgb tr_cmyk
+ $(ADDMOD) $(DD)tracedev -obj $(DEVOBJ)gdevtrac.$(OBJ)
+
+###@@@--------------- PSDCMYKOG device --------------------------###
+
+psdcmykog_=$(DEVOBJ)gdevcmykog.$(OBJ)
+
+$(DD)psdcmykog.dev : $(DEVS_MAK) $(GDEV) $(psdcmykog_) $(DD)page.dev \
+ $(GLOBJ)gdevdevn.$(OBJ) $(MAKEDIRS)
+ $(SETPDEV) $(DD)psdcmykog $(psdcmykog_)
+
+$(DEVOBJ)gdevcmykog.$(OBJ) : $(DEVSRC)gdevcmykog.c $(GDEV) \
+ $(GDEVH) $(gdevdevn_h) $(gsequivc_h) $(gdevdevnprn_h) $(MAKEDIRS)
+ $(DEVCC) $(DEVO_)gdevcmykog.$(OBJ) $(C_) $(DEVSRC)gdevcmykog.c
diff --git a/devices/gdev3852.c b/devices/gdev3852.c
new file mode 100644
index 000000000..2c3d89539
--- /dev/null
+++ b/devices/gdev3852.c
@@ -0,0 +1,186 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+/* IBM 3852 JetPrinter color ink jet driver for Ghostscript */
+
+/*
+This driver program created by Kevin M. Gift <kgift@draper.com> in Sept. 1992.
+Modified 3/93 to correct bug in cnt_2prn size.
+Modified 3/93 to dimension page back to 8.5, which seems to
+ work better than the actual page width of 7.6, ie. it uses
+ the full printing width of the printer.
+ It was modeled after the V2.4.1 HP Paintjet driver (gdevpjet.c)
+Modified by L. Peter Deutsch <ghost@aladdin.com> 1999-01-10 to remove _ss
+ modifiers inappropriately copied from other code.
+ */
+
+#include "gdevprn.h"
+#include "gdevpcl.h"
+
+/* X_DPI and Y_DPI must be the same - use the maximum graphics resolution */
+/* for this printer */
+#define X_DPI 84
+#define Y_DPI 84
+
+/* We round up LINE_SIZE to a multiple of 8 bytes */
+/* because that's the unit of transposition from pixels to planes. */
+/* Should = 96 (KMG) */
+#define LINE_SIZE ((X_DPI * 86 / 10 + 63) / 64 * 8)
+
+/* The device descriptor */
+static dev_proc_print_page(jetp3852_print_page);
+/* Since the 'print_page' does not change the device, this device can print in the background */
+static gx_device_procs jetp3852_procs =
+ prn_color_procs(gdev_prn_open, gdev_prn_bg_output_page, gdev_prn_close,
+ gdev_pcl_3bit_map_rgb_color, gdev_pcl_3bit_map_color_rgb);
+const gx_device_printer far_data gs_jetp3852_device =
+ prn_device(jetp3852_procs, "jetp3852",
+ 86, /* width_10ths, 8.6" (?) */
+ 110, /* height_10ths, 11" */
+ X_DPI, Y_DPI,
+ 0.0, 0, 0.0, 0, /* left, bottom, right, top margins */
+ 3, jetp3852_print_page);
+
+/* ------ Internal routines ------ */
+
+/* Send the page to the printer. */
+static int
+jetp3852_print_page(gx_device_printer *pdev, FILE *prn_stream)
+{
+#define DATA_SIZE (LINE_SIZE * 8)
+
+ unsigned int cnt_2prn;
+ unsigned int count,tempcnt;
+ unsigned char vtp,cntc1,cntc2;
+ int line_size_color_plane;
+
+ byte data[DATA_SIZE];
+ byte plane_data[LINE_SIZE * 3];
+
+ /* Set initial condition for printer */
+ fputs("\033@",prn_stream);
+
+ /* Send each scan line in turn */
+ { int lnum;
+ int line_size = gdev_mem_bytes_per_scan_line((gx_device *)pdev);
+ int num_blank_lines = 0;
+ for ( lnum = 0; lnum < pdev->height; lnum++ )
+ { byte *end_data = data + line_size;
+ gdev_prn_copy_scan_lines(pdev, lnum,
+ (byte *)data, line_size);
+ /* Remove trailing 0s. */
+ while ( end_data > data && end_data[-1] == 0 )
+ end_data--;
+ if ( end_data == data )
+ { /* Blank line */
+ num_blank_lines++;
+ }
+ else
+ { int i;
+ byte *odp;
+ byte *row;
+
+ /* Pad with 0s to fill out the last */
+ /* block of 8 bytes. */
+ memset(end_data, 0, 7);
+
+ /* Transpose the data to get pixel planes. */
+ for ( i = 0, odp = plane_data; i < DATA_SIZE;
+ i += 8, odp++
+ )
+ { /* The following is for 16-bit machines */
+#define spread3(c)\
+ { 0, c, c*0x100, c*0x101, c*0x10000L, c*0x10001L, c*0x10100L, c*0x10101L }
+ static ulong spr40[8] = spread3(0x40);
+ static ulong spr8[8] = spread3(8);
+ static ulong spr2[8] = spread3(2);
+ register byte *dp = data + i;
+ register ulong pword =
+ (spr40[dp[0]] << 1) +
+ (spr40[dp[1]]) +
+ (spr40[dp[2]] >> 1) +
+ (spr8[dp[3]] << 1) +
+ (spr8[dp[4]]) +
+ (spr8[dp[5]] >> 1) +
+ (spr2[dp[6]]) +
+ (spr2[dp[7]] >> 1);
+ odp[0] = (byte)(pword >> 16);
+ odp[LINE_SIZE] = (byte)(pword >> 8);
+ odp[LINE_SIZE*2] = (byte)(pword);
+ }
+ /* Skip blank lines if any */
+ if ( num_blank_lines > 0 )
+ {
+ if (lnum == 0)
+ { /* Skip down the page from the top */
+ /* set line spacing = 1/8 inch */
+ fputs("\0330",prn_stream);
+ /* Set vertical tab */
+ vtp = (num_blank_lines / 8);
+ fprintf(prn_stream,"\033B%c\000",vtp);
+ /* Do vertical tab */
+ fputs("\013",prn_stream);
+ num_blank_lines = 0;
+ }
+ else
+ { /* Do "dot skips" */
+ while(num_blank_lines > 255)
+ {
+ fputs("\033e\377",prn_stream);
+ num_blank_lines -= 255;
+ }
+ vtp = num_blank_lines;
+ fprintf(prn_stream,"\033e%c",vtp);
+ num_blank_lines = 0;
+ }
+ }
+
+ /* Transfer raster graphics in the order R, G, B. */
+ /* Apparently it is stored in B, G, R */
+ /* Calculate the amount of data to send by what */
+ /* Ghostscript tells us the scan line_size in (bytes) */
+
+ count = line_size / 3;
+ line_size_color_plane = count / 3;
+ cnt_2prn = line_size_color_plane * 3 + 5;
+ tempcnt = cnt_2prn;
+ cntc1 = (tempcnt & 0xFF00) >> 8;
+ cntc2 = (tempcnt & 0x00FF);
+ fprintf(prn_stream, "\033[O%c%c\200\037",cntc2,cntc1);
+ fputc('\000',prn_stream);
+ fputs("\124\124",prn_stream);
+
+ for ( row = plane_data + LINE_SIZE * 2, i = 0;
+ i < 3; row -= LINE_SIZE, i++ )
+ { int jj;
+ byte ctemp;
+ odp = row;
+ /* Complement bytes */
+ for (jj=0; jj< line_size_color_plane; jj++)
+ { ctemp = *odp;
+ *odp++ = ~ctemp;
+ }
+ fwrite(row, sizeof(byte),
+ line_size_color_plane, prn_stream);
+ }
+ }
+ }
+ }
+
+ /* eject page */
+ fputs("\014", prn_stream);
+
+ return 0;
+}
diff --git a/devices/gdev3b1.c b/devices/gdev3b1.c
new file mode 100644
index 000000000..4105dcb90
--- /dev/null
+++ b/devices/gdev3b1.c
@@ -0,0 +1,794 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+/*
+ * This is a driver for the AT&T 3b1/7300/UnixPC console display.
+ *
+ * The image is built in a buffer the size of the page. Once complete,
+ * a screen-sized subset is copied to the screen, and one can scroll
+ * through the entire image (move with "vi" or arrow keys).
+ *
+ * Written by Andy Fyfe, andy@cs.caltech.edu.
+ *
+ * There are a couple of undesirable "features" that I have found no
+ * way to work around.
+ *
+ * 1) Gs attempts to save the contents of the window before using it, and
+ * then restores the contents afterward. However, if the gs window is
+ * not the current window, and there are small windows present, then
+ * the saved image is incorrect, and thus the screen will not be correctly
+ * restored. This seems to be a bug in the 3b1 window driver. Making
+ * the gs window current before saving its contents is not an acceptable
+ * solution.
+ *
+ * 2) Gs will enable the scrolling/help/cancel icons if the window has
+ * a border. Changing these border icons has the side effect of making
+ * the gs window current. This does circumvent the first problem though.
+ */
+
+/*
+ * About the ATT3B1_PERF flag (notes by Andy Fyfe):
+ *
+ * I am unable to profile gs on the 3b1, so I added ATT3B1_PERF as a
+ * quick way to find out how much time was spent in the 3b1 driver,
+ * through dynamically suppressing parts of the code at run time by
+ * setting environment variables. I can then get the time spent in
+ * those parts by comparing the results of "time gs ....".
+ *
+ * At one point this was very useful, and led to a fairly substantial
+ * speedup of the fill and copy_mono routines. It also showed that I
+ * wasn't going to get too much more, overall, by further attempts to
+ * optimize the 3b1 driver. So those parts of the code controlled by
+ * ATT3B1_PERF have really now outlived their usefulness.
+ */
+
+#include "gx.h"
+#include "gxdevice.h"
+#include "gserrors.h"
+
+#include <errno.h>
+#include <sys/window.h>
+#include <sys/termio.h>
+
+typedef struct gx_device_att3b1_s {
+ gx_device_common;
+ int fd; /* window file descriptor */
+ uchar *screen; /* pointer to screen image */
+ ushort line_size; /* size of screen line in bytes */
+ ulong screen_size; /* size of screen image in bytes */
+ int page_num; /* page number */
+#ifdef ATT3B1_PERF
+ char *no_output, *no_fill, *no_copy;
+#endif
+} gx_device_att3b1;
+#define att3b1dev ((gx_device_att3b1 *)dev)
+
+#define XDPI 100 /* to get a more-or-less square aspect ratio */
+#define YDPI 72
+#define XSIZE (8.5 * XDPI) /* 8.5 x 11 inch page, by default */
+#define YSIZE (11 * YDPI)
+
+static const ushort masks[] = { 0,
+ 0x0001, 0x0003, 0x0007, 0x000f,
+ 0x001f, 0x003f, 0x007f, 0x00ff,
+ 0x01ff, 0x03ff, 0x07ff, 0x0fff,
+ 0x1fff, 0x3fff, 0x7fff, 0xffff,
+};
+static uchar reverse_bits[256] = {
+ 0, 128, 64, 192, 32, 160, 96, 224, 16, 144, 80, 208, 48, 176, 112, 240,
+ 8, 136, 72, 200, 40, 168, 104, 232, 24, 152, 88, 216, 56, 184, 120, 248,
+ 4, 132, 68, 196, 36, 164, 100, 228, 20, 148, 84, 212, 52, 180, 116, 244,
+ 12, 140, 76, 204, 44, 172, 108, 236, 28, 156, 92, 220, 60, 188, 124, 252,
+ 2, 130, 66, 194, 34, 162, 98, 226, 18, 146, 82, 210, 50, 178, 114, 242,
+ 10, 138, 74, 202, 42, 170, 106, 234, 26, 154, 90, 218, 58, 186, 122, 250,
+ 6, 134, 70, 198, 38, 166, 102, 230, 22, 150, 86, 214, 54, 182, 118, 246,
+ 14, 142, 78, 206, 46, 174, 110, 238, 30, 158, 94, 222, 62, 190, 126, 254,
+ 1, 129, 65, 193, 33, 161, 97, 225, 17, 145, 81, 209, 49, 177, 113, 241,
+ 9, 137, 73, 201, 41, 169, 105, 233, 25, 153, 89, 217, 57, 185, 121, 249,
+ 5, 133, 69, 197, 37, 165, 101, 229, 21, 149, 85, 213, 53, 181, 117, 245,
+ 13, 141, 77, 205, 45, 173, 109, 237, 29, 157, 93, 221, 61, 189, 125, 253,
+ 3, 131, 67, 195, 35, 163, 99, 227, 19, 147, 83, 211, 51, 179, 115, 243,
+ 11, 139, 75, 203, 43, 171, 107, 235, 27, 155, 91, 219, 59, 187, 123, 251,
+ 7, 135, 71, 199, 39, 167, 103, 231, 23, 151, 87, 215, 55, 183, 119, 247,
+ 15, 143, 79, 207, 47, 175, 111, 239, 31, 159, 95, 223, 63, 191, 127, 255
+};
+
+dev_proc_open_device(att3b1_open);
+dev_proc_close_device(att3b1_close);
+dev_proc_fill_rectangle(att3b1_fill_rectangle);
+dev_proc_copy_mono(att3b1_copy_mono);
+dev_proc_output_page(att3b1_output_page);
+
+static gx_device_procs att3b1_procs = {
+ att3b1_open,
+ gx_default_get_initial_matrix,
+ gx_default_sync_output,
+ att3b1_output_page,
+ att3b1_close,
+ gx_default_map_rgb_color,
+ gx_default_map_color_rgb,
+ att3b1_fill_rectangle,
+ gx_default_tile_rectangle,
+ att3b1_copy_mono,
+ gx_default_copy_color,
+ gx_default_draw_line,
+ gx_default_get_bits
+};
+
+gx_device_att3b1 gs_att3b1_device = {
+ std_device_std_body(gx_device_att3b1, &att3b1_procs, "att3b1",
+ XSIZE, YSIZE, XDPI, YDPI),
+ { 0 }, /* std_procs */
+ -1, 0, 0, /* fd, screen, line_size, */
+ 0, 0, /* screen size, page */
+#ifdef ATT3B1_PERF
+ 0, 0, 0, /* no_output, no_fill, no_copy */
+#endif
+};
+
+int
+att3b1_open(gx_device *dev)
+{
+ struct uwdata uw;
+
+#ifdef ATT3B1_PERF
+ char *getenv(const char *);
+#endif
+
+ if (att3b1dev->fd >= 0) {
+ close(att3b1dev->fd);
+ att3b1dev->fd = -1;
+ }
+
+ if (att3b1dev->screen != NULL) {
+ gs_free(dev->memory, (char *)att3b1dev->screen,
+ att3b1dev->screen_size, 1, "att3b1_open");
+ att3b1dev->screen = 0;
+ att3b1dev->screen_size = 0;
+ }
+
+ att3b1dev->fd = open("/dev/tty", 2);
+ if (att3b1dev->fd < 0) {
+ lprintf1("att3b1_open: open /dev/tty failed [%d]\n", errno);
+ return_error(gs_error_ioerror);
+ }
+
+ /* Verify that /dev/tty is associated with a console window. */
+ if (ioctl(att3b1dev->fd, WIOCGETD, &uw) < 0) {
+ lprintf1("att3b1_open: can not obtain window data [%d]\n", errno);
+ lprintf("att3b1_open: the att3b1 device requires a console window\n");
+ att3b1_close(dev);
+ return_error(gs_error_ioerror);
+ }
+
+ /* we need an even number of bytes per line */
+ att3b1dev->line_size = ((att3b1dev->width + 15) / 16) * 2;
+ att3b1dev->screen_size = att3b1dev->line_size * att3b1dev->height;
+
+ att3b1dev->screen =
+ (uchar *)gs_malloc(dev->memory, att3b1dev->screen_size, 1, "att3b1_open");
+ if (att3b1dev->screen == NULL) {
+ att3b1_close(dev);
+ return_error(gs_error_VMerror);
+ }
+
+ att3b1dev->page_num = 1;
+
+#ifdef ATT3B1_PERF
+ att3b1dev->no_output = getenv("GS_NOOUTPUT");
+ att3b1dev->no_fill = getenv("GS_NOFILL");
+ att3b1dev->no_copy = getenv("GS_NOCOPY");
+#endif
+
+ return 0;
+}
+
+int
+att3b1_close(gx_device *dev)
+{
+ if (att3b1dev->fd >= 0) {
+ close(att3b1dev->fd);
+ att3b1dev->fd = -1;
+ }
+
+ if (att3b1dev->screen != NULL) {
+ gs_free(dev->memory, (char *)att3b1dev->screen,
+ att3b1dev->screen_size, 1, "att3b1_close");
+ att3b1dev->screen = 0;
+ att3b1dev->screen_size = 0;
+ }
+
+ return 0;
+}
+
+int
+att3b1_fill_rectangle(gx_device *dev, int x, int y, int w, int h,
+ gx_color_index colour)
+{
+ uint o, b, wl, wr, w2;
+ ushort *p, *q, maskl, maskr;
+
+#ifdef ATT3B1_PERF
+ if (att3b1dev->no_fill) return 0;
+#endif
+
+ fit_fill(dev, x, y, w, h);
+
+ /* following fit_fill, we can assume x, y, w, h are unsigned. */
+
+ p = (ushort *)&att3b1dev->screen[(ushort)y*att3b1dev->line_size] +
+ (uint)x/16;
+ o = (uint)x % 16;
+ b = 16 - o;
+ wl = ((uint)w < b) ? (uint)w : b;
+ maskl = masks[wl] << o;
+ w -= wl;
+ wr = (uint)w % 16;
+ maskr = masks[wr];
+
+ if (colour == 0) {
+ maskl = ~maskl;
+ maskr = ~maskr;
+ while (h-- > 0) {
+ q = p;
+ w2 = w;
+ *q++ &= maskl;
+ while (w2 >= 16) {
+ *q++ = 0;
+ w2 -= 16;
+ }
+ *q &= maskr;
+ p += (att3b1dev->line_size / 2);
+ }
+ }
+ else {
+ while (h-- > 0) {
+ q = p;
+ w2 = w;
+ *q++ |= maskl;
+ while (w2 >= 16) {
+ *q++ = 0xffff;
+ w2 -= 16;
+ }
+ *q |= maskr;
+ p += (att3b1dev->line_size / 2);
+ }
+ }
+
+ return 0;
+}
+
+#ifdef __GNUC__
+#define rotate(value, count) \
+ asm("ror%.l %2,%0" : "=d" (value) : "0" (value), "d" (count))
+#else
+#define rotate(value, count) \
+ value = (value >> count) | (value << (32-count))
+#endif
+
+int
+att3b1_copy_mono(gx_device *dev, const uchar *data,
+ int data_x, int raster, gx_bitmap_id id,
+ int x, int y, int width, int height,
+ gx_color_index colour0, gx_color_index colour1)
+{
+ const ushort *src_p, *src_q;
+ ushort *dst_p, *dst_q;
+ ulong bits, mask, *p;
+ uint src_o, src_b, dst_o, dst_b, op;
+ uint w1, w2;
+
+#ifdef ATT3B1_PERF
+ if (att3b1dev->no_copy) return 0;
+#endif
+
+ if (colour1 == colour0) /* vacuous case */
+ return att3b1_fill_rectangle(dev, x, y, width, height, colour0);
+
+ fit_copy(dev, data, data_x, raster, id, x, y, width, height);
+
+ /* following fit_copy, we can assume x, y, width, height are unsigned. */
+
+ /*
+ * In what follows, we're assuming that each row of the input bitmap
+ * is short-aligned, that is, that both "data" and "raster" are even.
+ */
+ src_p = ((const ushort *)data) + (uint)data_x/16;
+ src_o = (uint)data_x % 16;
+ src_b = 16 - src_o;
+
+ dst_p = (ushort *)&att3b1dev->screen[(ushort)y*att3b1dev->line_size] +
+ (uint)x/16;
+ dst_o = (uint)x % 16;
+ dst_b = 16 - dst_o;
+
+ op = (int)colour0 * 3 + (int)colour1 + 4;
+
+ while (height-- > 0) {
+ w2 = width;
+ src_q = src_p;
+ dst_q = dst_p;
+
+ while (w2 > 0) {
+ w1 = (w2 < 16) ? w2 : 16;
+ mask = masks[w1];
+ /*
+ * We are assuming that the bitmap "data" is typically aligned.
+ * Thus the test for this special case is typically a win over
+ * a 16-bit shift.
+ */
+ if (src_o == 0)
+ bits = *src_q++;
+ else {
+ bits = *((ulong *)src_q) >> src_b;
+ bits &= 0xffff;
+ src_q++;
+ }
+ if (w1 <= 8)
+ bits = reverse_bits[bits>>8];
+ else
+ bits = (reverse_bits[bits&0xff] << 8) | reverse_bits[bits>>8];
+ /*
+ * While the input bit map is assumed to be typically aligned, we
+ * assume that the place in the image is not. Thus we don't
+ * separate out the aligned case. Doing so would cost a test,
+ * and only reduce the average shift by about 1.
+ */
+ p = (ulong *)dst_q;
+ switch(op) {
+ case 1: /* not src and dst */
+ bits = ~(bits & mask);
+ rotate(bits,dst_b);
+ *p &= bits;
+ break;
+ case 2: /* src or dst */
+ bits = bits & mask;
+ rotate(bits,dst_b);
+ *p |= bits;
+ break;
+ case 3: /* src and dst */
+ bits = bits | ~mask;
+ rotate(bits,dst_b);
+ *p &= bits;
+ break;
+ case 5: /* src */
+ rotate(bits,dst_b);
+ rotate(mask,dst_b);
+ *p = (*p & ~mask) | (bits & mask);
+ break;
+ case 6: /* not src or dst */
+ bits = ~bits & mask;
+ rotate(bits,dst_b);
+ *p |= bits;
+ break;
+ case 7: /* not src */
+ rotate(bits,dst_b);
+ rotate(mask,dst_b);
+ *p = (*p & ~mask) | (~bits & mask);
+ break;
+ }
+ dst_q++;
+ w2 -= w1;
+ }
+
+ src_p += (raster / 2);
+ dst_p += (att3b1dev->line_size / 2);
+ }
+
+ return 0;
+}
+
+static int getKeyboard(gx_device *);
+
+const char *help_msg[] = {
+ "h, j, k, l, UP, DOWN, LEFT, RIGHT move the page (0.25\" h, 0.5\" v)",
+ "H, J, K, L, BEG, END move to far edge of the page",
+ "^U, ^D, ROLL UP, ROLL DOWN scroll up or down (1/2 screen height)",
+ "^F, ^B, PAGE UP, PAGE DOWN scroll up or down (full screen height)",
+ "c, C centre page horizontally, vertically",
+ "<, >, ^, _ fine movements (single pixel)",
+ "^L, ^R, r, HOME move to default position",
+ "=, MARK make current position the default",
+ "I invert the image (black <-> white)",
+ "q, x, ^C, EXIT, CANCL, n, f, NEXT,",
+ " SPACE, RETURN, ENTER end the page",
+ "?, HELP help screen",
+};
+
+static void
+do_help(gx_device *dev)
+{
+ int i;
+ struct utdata ut;
+
+ /* we would like to save the cursor position, but we can't */
+ write(att3b1dev->fd, "\033[2J\033[H", 7);
+
+ /* write help screen */
+ for (i=0; i < sizeof(help_msg)/sizeof(help_msg[0]); ++i) {
+ write(att3b1dev->fd, help_msg[i], strlen(help_msg[i]));
+ write(att3b1dev->fd, "\n", 1);
+ }
+ ut.ut_num = WTXTSLK1;
+ strcpy(ut.ut_text, "Press any key to continue");
+ ioctl(att3b1dev->fd, WIOCSETTEXT, &ut);
+
+ /* wait for keyboard input */
+ i = getKeyboard(dev);
+
+ /* clear screen and put cursor at the bottom of the screen */
+ write(att3b1dev->fd, "\033[2J\033[99;1H", 11);
+}
+
+static int
+att3b1_do_output_page(gx_device *dev, int num_copies, int flush)
+{
+ struct urdata ur;
+ struct utdata ut, ut_orig;
+ struct uwdata uw;
+ int uflags;
+ struct termio old, new;
+ int xorigin, yorigin;
+ static int def_xorigin = 0, def_yorigin = 0;
+ int screen_width, screen_height;
+ int inverted = 0;
+ int error = 0;
+ int ch;
+ ushort *p;
+ ushort save_image[WINWIDTH * WINHEIGHT / 16];
+
+#ifdef ATT3B1_PERF
+ if (att3b1dev->no_output) return 0;
+#endif
+
+ /*
+ * initialize, and save screen state
+ */
+
+ if (ioctl(att3b1dev->fd, WIOCGETD, &uw) < 0) {
+ lprintf1("att3b1_output_page: window WIOCGETD ioctl failed [%d]\n",
+ errno);
+ att3b1_close(dev);
+ return_error(gs_error_ioerror);
+ }
+
+ /*
+ * we assume, henceforth, that screen ioctl calls will succeed
+ */
+
+ write(att3b1dev->fd, "\a\033[=1C", 6);
+
+ uflags = uw.uw_uflags;
+ if (!(uflags & NBORDER)) {
+ uw.uw_uflags = BORDHSCROLL | BORDVSCROLL | BORDHELP | BORDCANCEL;
+ ioctl(att3b1dev->fd, WIOCSETD, &uw);
+ }
+
+ ut_orig.ut_num = WTXTSLK1;
+ ioctl(att3b1dev->fd, WIOCGETTEXT, &ut_orig);
+
+ /* This isn't necessary, but helps a bit when the following attempt
+ to get the current screen image fails (without any indication). */
+ memset(save_image, '\0', sizeof(save_image));
+
+ ur.ur_srcbase = 0;
+ ur.ur_srcwidth = 0;
+ ur.ur_srcx = 0;
+ ur.ur_srcy = 0;
+ ur.ur_dstbase = save_image;
+ ur.ur_dstwidth = WINWIDTH / 8;
+ ur.ur_dstx = 0;
+ ur.ur_dsty = 0;
+ ur.ur_width = uw.uw_width;
+ ur.ur_height = uw.uw_height;
+ ur.ur_srcop = SRCSRC;
+ ur.ur_dstop = DSTSRC;
+ ur.ur_pattern = 0;
+ ioctl(att3b1dev->fd, WIOCRASTOP, &ur);
+
+ ioctl(att3b1dev->fd, TCGETA, &old);
+ new = old;
+ new.c_lflag &= ~(ISIG | ICANON | ECHO | ECHOE | ECHOK | ECHONL);
+ new.c_cc[VMIN] = 1;
+ ioctl(att3b1dev->fd, TCSETAF, &new);
+
+ screen_width = (uw.uw_width < att3b1dev->width) ? uw.uw_width
+ : att3b1dev->width;
+ screen_height = (uw.uw_height < att3b1dev->height) ? uw.uw_height
+ : att3b1dev->height;
+
+ write(att3b1dev->fd, "\033[2J", 4);
+
+ ur.ur_srcwidth = att3b1dev->line_size;
+ ur.ur_width = screen_width;
+ ur.ur_height = screen_height;
+ ur.ur_dstbase = 0;
+ ur.ur_dstwidth = 0;
+
+ /*
+ * allow one to move the screen window through the entire image
+ */
+
+ xorigin = def_xorigin;
+ yorigin = def_yorigin;
+
+ while (1) {
+ /* Things go bad if ur_srcx >= 2048 */
+ ur.ur_srcbase = (ushort *)att3b1dev->screen + (xorigin >> 4);
+ ur.ur_srcx = xorigin & 15;
+ ur.ur_srcy = yorigin;
+
+ if (ioctl(att3b1dev->fd, WIOCRASTOP, &ur) < 0) {
+ lprintf1(
+ "att3b1_output_page: window WIOCRASTOP ioctl failed [%d]\n",
+ errno);
+ error = gs_error_ioerror;
+ }
+
+ ut.ut_num = WTXTSLK1;
+ gs_sprintf(ut.ut_text,
+ "%s %d, top right (%d,%d), size (%d,%d), press '?' for help.",
+ flush ? "Showpage" : "Copypage", att3b1dev->page_num, xorigin, yorigin,
+ att3b1dev->width, att3b1dev->height);
+ ioctl(att3b1dev->fd, WIOCSETTEXT, &ut);
+
+ ch = error ? 'q' : getKeyboard(dev);
+
+ switch(ch) {
+ case 'h':
+ xorigin -= ((uint)(int)att3b1dev->x_pixels_per_inch+3)/4;
+ break;
+
+ case 'k':
+ yorigin -= ((uint)(int)att3b1dev->y_pixels_per_inch+1)/2;
+ break;
+
+ case 'l':
+ xorigin += ((uint)(int)att3b1dev->x_pixels_per_inch+3)/4;
+ break;
+
+ case 'j':
+ yorigin += ((uint)(int)att3b1dev->y_pixels_per_inch+1)/2;
+ break;
+
+ case 'H':
+ xorigin = 0;
+ break;
+
+ case 'K':
+ yorigin = 0;
+ break;
+
+ case 'L':
+ xorigin = att3b1dev->width - screen_width;
+ break;
+
+ case 'J':
+ yorigin = att3b1dev->height - screen_height;
+ break;
+
+ case '<':
+ xorigin -= 1;
+ break;
+
+ case '>':
+ xorigin += 1;
+ break;
+
+ case '^':
+ yorigin -= 1;
+ break;
+
+ case '_':
+ yorigin += 1;
+ break;
+
+ case '\025': /* control-U */
+ yorigin -= screen_height/2;
+ break;
+
+ case '\004': /* control-D */
+ yorigin += screen_height/2;
+ break;
+
+ case '\002': /* control-B */
+ yorigin -= screen_height;
+ break;
+
+ case '\006': /* control-F */
+ yorigin += screen_height;
+ break;
+
+ case '\f':
+ case 'r' :
+ case '\022': /* control-R */
+ xorigin = def_xorigin;
+ yorigin = def_yorigin;
+ break;
+
+ case 'c': /* centre horizontally */
+ xorigin = (att3b1dev->width - screen_width) / 2;
+ break;
+
+ case 'C': /* centre vertically */
+ yorigin = (att3b1dev->height - screen_height) / 2;
+ break;
+
+ case '=':
+ def_xorigin = xorigin;
+ def_yorigin = yorigin;
+ break;
+
+ case 'I':
+ for (p = (ushort *)att3b1dev->screen;
+ p < (ushort *)&att3b1dev->screen[att3b1dev->screen_size]; ++p)
+ *p = ~ *p;
+ inverted = !inverted;
+ break;
+
+ case '?':
+ do_help(dev);
+ break;
+
+ case -1:
+ error = gs_error_ioerror;
+ /* fall through, for cleanup */
+
+ case 'q':
+ case 'x':
+ case '\003': /* control-C */
+ case 'n':
+ case 'f':
+ case ' ':
+ case '\n':
+ case '\r':
+ if (flush)
+ att3b1dev->page_num++;
+ else if (inverted) /* restore inverted image for copypage */
+ for (p = (ushort *)att3b1dev->screen;
+ p < (ushort *)&att3b1dev->screen[att3b1dev->screen_size]; ++p)
+ *p = ~ *p;
+ if (!(uflags & NBORDER)) {
+ ioctl(att3b1dev->fd, WIOCGETD, &uw); /*window may have moved*/
+ uw.uw_uflags = uflags;
+ ioctl(att3b1dev->fd, WIOCSETD, &uw);
+ }
+ ur.ur_srcbase = save_image;
+ ur.ur_srcwidth = WINWIDTH / 8;
+ ur.ur_width = uw.uw_width;
+ ur.ur_height = uw.uw_height;
+ ur.ur_srcx = 0;
+ ur.ur_srcy = 0;
+ ioctl(att3b1dev->fd, WIOCRASTOP, &ur);
+ ioctl(att3b1dev->fd, WIOCSETTEXT, &ut_orig);
+ ioctl(att3b1dev->fd, TCSETAF, &old);
+ write(att3b1dev->fd, "\033[=0C", 5);
+
+ if (error) {
+ att3b1_close(dev);
+ return_error(error);
+ }
+ else
+ return 0;
+ }
+
+ if (xorigin >= att3b1dev->width - screen_width)
+ xorigin = att3b1dev->width - screen_width;
+ if (xorigin < 0)
+ xorigin = 0;
+ if (yorigin >= att3b1dev->height - screen_height)
+ yorigin = att3b1dev->height - screen_height;
+ if (yorigin < 0)
+ yorigin = 0;
+ }
+}
+int
+att3b1_output_page(gx_device *dev, int num_copies, int flush)
+{
+ int code = att3b1_do_output_page(dev, num_copies, flush);
+
+ if (code >= 0)
+ code = gx_finish_output_page(dev, num_copies, flush);
+ return code;
+}
+
+static int
+get_char(gx_device *dev)
+{
+ char ch;
+ int count;
+
+ count = read(att3b1dev->fd, &ch, 1);
+ if (count == 0)
+ return 'q';
+ else if (count < 0)
+ return -1;
+ else
+ return ch;
+}
+
+static int
+getKeyboard(gx_device *dev)
+{
+ char ch;
+
+ ch = get_char(dev);
+
+ if (ch != '\033')
+ return ch;
+
+ /*
+ * If the char is escape, interpret the escape sequence and return
+ * an equivalent single character.
+ *
+ * Note that a mouse click on a window border icon is translated
+ * to the corresponding key, for example, the "up" icon generates
+ * roll-up/page-up/beg for the left/middle/right mouse button.
+ */
+
+ switch (get_char(dev)) {
+ case '[':
+ switch(get_char(dev)) {
+ case 'A': /* up arrow */
+ return 'k';
+ case 'T': /* shift up arrow (roll up) */
+ return '\025';
+ case 'B': /* down arrow */
+ return 'j';
+ case 'S': /* shift down arrow (roll down) */
+ return '\004';
+ case 'C': /* right arrow */
+ return 'l';
+ case 'D': /* left arrow */
+ return 'h';
+ case 'H': /* home */
+ return 'r';
+ case 'U': /* page down */
+ return '\006';
+ case 'V': /* page up */
+ return '\002';
+ }
+ break;
+ case 'O':
+ switch(get_char(dev)) {
+ case 'm': /* help */
+ case 'M': /* shift help */
+ return '?';
+ case 'k': /* exit */
+ case 'K': /* shift exit */
+ case 'w': /* cancl */
+ case 'W': /* shift cancl */
+ return 'q';
+ }
+ break;
+ case 'N':
+ switch(get_char(dev)) {
+ case 'h': /* next */
+ return 'f';
+ case 'i': /* mark */
+ return '=';
+ case 'L': /* shift right arrow */
+ return 'l';
+ case 'K': /* shift left arrow */
+ return 'h';
+ }
+ break;
+ case '9': /* Beg */
+ return 'K';
+ case '0': /* End */
+ return 'J';
+ }
+ return '\0';
+}
diff --git a/devices/gdev4081.c b/devices/gdev4081.c
new file mode 100644
index 000000000..c62d7fbec
--- /dev/null
+++ b/devices/gdev4081.c
@@ -0,0 +1,90 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+/* Ricoh 4081 laser printer driver */
+#include "gdevprn.h"
+
+#define X_DPI 300 /* pixels per inch */
+#define Y_DPI 300 /* pixels per inch */
+
+/* The device descriptor */
+static dev_proc_print_page(r4081_print_page);
+const gx_device_printer far_data gs_r4081_device =
+ prn_device(prn_bg_procs, "r4081", /* The print_page proc is compatible with allowing bg printing */
+ 85, /* width_10ths, 8.5" */
+ 110, /* height_10ths, 11" */
+ X_DPI, Y_DPI,
+ 0.25, 0.16, 0.25, 0.16, /* margins */
+ 1, r4081_print_page);
+
+/* ------ Internal routines ------ */
+
+/* Send the page to the printer. */
+static int
+r4081_print_page(gx_device_printer *pdev, FILE *prn_stream)
+{
+ int line_size = gdev_mem_bytes_per_scan_line((gx_device *)pdev);
+ int out_size = ((pdev->width + 7) & -8) ;
+ byte *out = (byte *)gs_malloc(pdev->memory, out_size, 1, "r4081_print_page(out)");
+ int lnum = 0;
+ int last = pdev->height;
+
+ /* Check allocations */
+ if ( out == 0 )
+ { if ( out )
+ gs_free(pdev->memory, (char *)out, out_size, 1,
+ "r4081_print_page(out)");
+ return -1;
+ }
+
+ /* find the first line which has something to print */
+ while ( lnum < last )
+ {
+ gdev_prn_copy_scan_lines(pdev, lnum, (byte *)out, line_size);
+ if ( out[0] != 0 ||
+ memcmp((char *)out, (char *)out+1, line_size-1)
+ )
+ break;
+ lnum ++;
+ }
+
+ /* find the last line which has something to print */
+ while (last > lnum) {
+ gdev_prn_copy_scan_lines(pdev, last-1, (byte *)out, line_size);
+ if ( out[0] != 0 ||
+ memcmp((char *)out, (char *)out+1, line_size-1)
+ )
+ break;
+ last --;
+ }
+
+ /* Initialize the printer and set the starting position. */
+ fprintf(prn_stream,"\033\rP\033\022YB2 \033\022G3,%d,%d,1,1,1,%d@",
+ out_size, last-lnum, (lnum+1)*720/Y_DPI);
+
+ /* Print lines of graphics */
+ while ( lnum < last )
+ {
+ gdev_prn_copy_scan_lines(pdev, lnum, (byte *)out, line_size);
+ fwrite(out, sizeof(char), line_size, prn_stream);
+ lnum ++;
+ }
+
+ /* Eject the page and reinitialize the printer */
+ fputs("\f\033\rP", prn_stream);
+
+ gs_free(pdev->memory, (char *)out, out_size, 1, "r4081_print_page(out)");
+ return 0;
+}
diff --git a/devices/gdev4693.c b/devices/gdev4693.c
new file mode 100644
index 000000000..9962fc5f8
--- /dev/null
+++ b/devices/gdev4693.c
@@ -0,0 +1,178 @@
+/*
+ * Copyright 1992 Washington State University. All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose and without fee is hereby granted.
+ * This software is provided "as is" without express or implied warranty.
+ */
+
+/* Driver for the Tektronix 4693d color plotter. */
+#include "gdevprn.h"
+#define prn_dev ((gx_device_printer *)dev) /* needed in 5.31 et seq */
+
+/* Thanks to Karl Hakimian (hakimian@yoda.eecs.wsu.edu) */
+/* for contributing this code to Aladdin Enterprises. */
+
+#define X_DPI 100
+#define Y_DPI 100
+#define WIDTH_10THS 85
+#define HEIGHT_10THS 110
+
+static dev_proc_print_page(t4693d_print_page);
+static dev_proc_map_rgb_color(gdev_t4693d_map_rgb_color);
+static dev_proc_map_color_rgb(gdev_t4693d_map_color_rgb);
+
+/* Since the print_page doesn't alter the device, this device can print in the background */
+static gx_device_procs t4693d_procs =
+ prn_color_procs(gdev_prn_open, gdev_prn_bg_output_page, gdev_prn_close,
+ gdev_t4693d_map_rgb_color, gdev_t4693d_map_color_rgb);
+
+#define t4693d_prn_device(name,depth,max_rgb) {prn_device_body( \
+ gx_device_printer,t4693d_procs,name, \
+ WIDTH_10THS, HEIGHT_10THS, X_DPI, Y_DPI, 0.25, 0.25, 0.25, 0.25, \
+ 3,depth,max_rgb,max_rgb,max_rgb + 1,max_rgb + 1, \
+ t4693d_print_page)}
+
+const gx_device_printer gs_t4693d2_device = t4693d_prn_device("t4693d2",8, 3);
+const gx_device_printer gs_t4693d4_device = t4693d_prn_device("t4693d4",16, 15);
+const gx_device_printer gs_t4693d8_device = t4693d_prn_device("t4693d8",24, 255);
+
+static gx_color_index
+gdev_t4693d_map_rgb_color(gx_device *dev, const gx_color_value cv[])
+{
+ ushort bitspercolor = prn_dev->color_info.depth / 3;
+ ulong max_value = (1 << bitspercolor) - 1;
+
+ gx_color_value r, g, b;
+ r = cv[0]; g = cv[1]; b = cv[2];
+
+ if (bitspercolor == 5) {
+ bitspercolor--;
+ max_value = (1 << bitspercolor) - 1;
+ }
+
+ return ((r*max_value/gx_max_color_value) << (bitspercolor*2)) +
+ ((g*max_value/gx_max_color_value) << bitspercolor) +
+ (b*max_value/gx_max_color_value);
+}
+
+static int
+gdev_t4693d_map_color_rgb(gx_device *dev, gx_color_index color, ushort prgb[3])
+{
+ ushort bitspercolor = prn_dev->color_info.depth / 3;
+ ulong max_value = (1 << bitspercolor) - 1;
+
+ if (bitspercolor == 5) {
+ bitspercolor--;
+ max_value = (1 << bitspercolor) - 1;
+ }
+
+ prgb[0] = (color >> (bitspercolor*2)) * gx_max_color_value / max_value;
+ prgb[1] = ((color >> bitspercolor) & max_value) * gx_max_color_value / max_value;
+ prgb[2] = (color & max_value) * gx_max_color_value / max_value;
+ return(0);
+}
+
+static int
+t4693d_print_page(gx_device_printer *dev, FILE *ps_stream)
+{
+ char header[32];
+ int depth = prn_dev->color_info.depth;
+ int line_size = gdev_mem_bytes_per_scan_line(prn_dev);
+ byte *data = (byte *)gs_malloc(dev->memory, line_size, 1, "t4693d_print_page");
+ char *p;
+ ushort data_size = line_size/prn_dev->width;
+ int checksum;
+ int lnum;
+ int i;
+#if !arch_is_big_endian
+ byte swap;
+#endif
+
+ if (data == 0) return_error(gs_error_VMerror);
+ /* build header. */
+ p = header;
+ *p++ = (char)0x14; /* Print request */
+ *p++ = (char)0xc0|20; /* Length of header */
+ *p++ = (char)0xc0 | ((prn_dev->width >> 6)&0x3f);
+ *p++ = (char)0x80 | (prn_dev->width&0x3f);
+ *p++ = (char)0xc0 | ((prn_dev->height >> 6)&0x3f);
+ *p++ = (char)0x80 | (prn_dev->height&0x3f);
+ *p++ = (char)0xc1; /* Handshake */
+ *p++ = (char)0xc0; /* Get number of prints from printer. */
+ *p++ = (char)0xc0; /* Get pixel shape from printer. */
+ *p++ = (char)(depth == 8) ? 0xcb : (depth == 16) ? 0xcc : 0xcd;
+ *p++ = (char)0xc1; /* Pixel-data order 1. */
+ *p++ = (char)0xc3; /* Interpolate to maximum size. */
+ *p++ = (char)0xc3; /* Full color range 1. */
+ *p++ = (char)0xc0; /* Color conversion from printer. */
+ *p++ = (char)0xc0; /* Color manipulation from printer. */
+ *p++ = (char)0xc0; /* B/W inversion from printer. */
+ *p++ = (char)0xc3; /* Portrait mode centered. */
+ *p++ = (char)0xc9; /* Use printer default for media and printing. */
+ *p++ = (char)0x95;
+ *p++ = (char)0x81;
+
+ for (checksum = 0, i = 0; &header[i] != p; i++)
+ checksum += header[i];
+
+ *p++ = ((checksum%128)&0x7f) | 0x80;
+ *p = 0x02; /* end of line. */
+ /* write header */
+ if (fwrite(header,1,22,ps_stream) != 22) {
+ errprintf(dev->memory, "Could not write header (t4693d).\n");
+ gs_free(dev->memory, data, line_size, 1, "t4693d_print_page");
+ return_error(gs_error_ioerror);
+ }
+
+ for (lnum = 0; lnum < prn_dev->height; lnum++) {
+ gdev_prn_copy_scan_lines(prn_dev,lnum,data,line_size);
+
+ for (i = 0; i < line_size; i += data_size) {
+
+ switch (depth) {
+ case 8:
+ data[i] &= 0x3f;
+ break;
+ case 16:
+#if arch_is_big_endian
+ data[i] &= 0x0f;
+#else
+ swap = data[i];
+ data[i] = data[i + 1]&0x0f;
+ data[i + 1] = swap;
+#endif
+ break;
+ case 24:
+ break;
+ default:
+ errprintf(dev->memory,"Bad depth (%d) t4693d.\n",depth);
+ gs_free(dev->memory, data, line_size, 1, "t4693d_print_page");
+ return_error(gs_error_rangecheck);
+ }
+
+ if (fwrite(&data[i],1,data_size,ps_stream) != data_size) {
+ errprintf(dev->memory,"Could not write pixel (t4693d).\n");
+ gs_free(dev->memory, data, line_size, 1, "t4693d_print_page");
+ return_error(gs_error_ioerror);
+ }
+
+ }
+
+ if (fputc(0x02,ps_stream) != 0x02) {
+ errprintf(dev->memory,"Could not write EOL (t4693d).\n");
+ gs_free(dev->memory, data, line_size, 1, "t4693d_print_page");
+ return_error(gs_error_ioerror);
+ }
+
+ }
+
+ if (fputc(0x01,ps_stream) != 0x01) {
+ errprintf(dev->memory,"Could not write EOT (t4693d).\n");
+ gs_free(dev->memory, data, line_size, 1, "t4693d_print_page");
+ return_error(gs_error_ioerror);
+ }
+
+ gs_free(dev->memory, data, line_size, 1, "t4693d_print_page");
+ return(0);
+}
diff --git a/devices/gdev8510.c b/devices/gdev8510.c
new file mode 100644
index 000000000..5a0940213
--- /dev/null
+++ b/devices/gdev8510.c
@@ -0,0 +1,139 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+/*
+ * C.Itoh M8510 printer driver for ghostscript.
+ *
+ * By Bob Smith <bob@snuffy.penfield.ny.us>
+ */
+
+#include "gdevprn.h"
+
+/* The device descriptor */
+static dev_proc_print_page(m8510_print_page);
+const gx_device_printer far_data gs_m8510_device =
+ prn_device(prn_bg_procs, "m8510", /* The print_page proc is compatible with allowing bg printing */
+ 85, /* width_10ths, 8.5" */
+ 110, /* height_10ths, 11" */
+ 160, /* x_dpi */
+ 144, /* y_dpi */
+ 0,0,0.5,0, /* left, bottom, right, and top margins */
+ 1, m8510_print_page);
+
+/* ------ forward declarations ------ */
+
+static void m8510_output_run(gx_device_printer *pdev,
+ byte *out, int pass, FILE *prn_stream);
+
+/* ------ internal routines ------ */
+
+/* Send the page to the printer. */
+static int
+m8510_print_page(gx_device_printer *pdev, FILE *prn_stream)
+{
+ int line_size = gdev_mem_bytes_per_scan_line((gx_device *)pdev);
+ byte *in1 = (byte *) gs_malloc(pdev->memory, 8, line_size, "m8510_print_page(in1)");
+ byte *in2 = (byte *) gs_malloc(pdev->memory, 8, line_size, "m8510_print_page(in2)");
+ byte *out = (byte *) gs_malloc(pdev->memory, 8, line_size, "m8510_print_page(out)");
+ int lnum = 0;
+ int code = 0;
+ byte *inp, *in_end, *outp;
+ int i;
+
+ if (in1 == 0 || in2 == 0 || out == 0) {
+ code = gs_note_error(gs_error_VMerror);
+ goto out;
+ }
+
+ /*
+ * Initialize the printer.
+ * NLQ mode, proportional print (160x144 dpi).
+ * and 16/144" linefeeds.
+ */
+ fwrite("\033m2\033P\033T16", 1, 9, prn_stream);
+
+ /* Transfer pixels to printer */
+ while ( lnum < pdev->height ) {
+ /* get a raster */
+ for (i = 7; i >= 0; i--) {
+ gdev_prn_copy_scan_lines(pdev, lnum, &in1[i*line_size], line_size);
+ lnum++;
+ gdev_prn_copy_scan_lines(pdev, lnum, &in2[i*line_size], line_size);
+ lnum++;
+ }
+
+ /* Transpose the 1st pass of data. */
+ in_end = in1 + line_size;
+ for (inp = in1, outp = out; inp < in_end; inp++, outp += 8)
+ gdev_prn_transpose_8x8(inp, line_size, outp, 1);
+
+ /* send the 1st line */
+ m8510_output_run(pdev, out, 0, prn_stream);
+
+ /* Transpose the 2nd pass of data. */
+ in_end = in2 + line_size;
+ for (inp = in2, outp = out; inp < in_end; inp++, outp += 8)
+ gdev_prn_transpose_8x8(inp, line_size, outp, 1);
+
+ /* send the 2nd line */
+ m8510_output_run(pdev, out, 1, prn_stream);
+ }
+
+ /* reset the printer. */
+ fwrite("\033c1", 1, 3, prn_stream);
+ fflush(prn_stream);
+
+out:;
+ if (out) gs_free(pdev->memory, (char *) out, 8, line_size, "m8510_print_page(out)");
+ if (in2) gs_free(pdev->memory, (char *) in2, 8, line_size, "m8510_print_page(in2)");
+ if (in1) gs_free(pdev->memory, (char *) in1, 8, line_size, "m8510_print_page(in1)");
+
+ return code;
+}
+
+static void
+m8510_output_run(gx_device_printer *pdev,
+ byte *out, int pass, FILE *prn_stream)
+{
+ byte *out_end = out + pdev->width;
+ char tmp[10];
+ int count;
+
+ /*
+ * Remove trailing 0s.
+ * out must be a multiple of 8 bytes.
+ */
+ while (out_end > out
+ && out_end[-1] == 0
+ && out_end[-2] == 0
+ && out_end[-3] == 0
+ && out_end[-4] == 0
+ && out_end[-5] == 0
+ && out_end[-6] == 0
+ && out_end[-7] == 0
+ && out_end[-8] == 0)
+ out_end -= 8;
+
+ /* Transfer the line of data. */
+ count = out_end - out;
+ if (count) {
+ gs_sprintf(tmp, "\033g%03d", count/8);
+ fwrite(tmp, 1, 5, prn_stream);
+ fwrite(out, 1, count, prn_stream);
+ fwrite("\r", 1, 1, prn_stream);
+ }
+
+ if (pass) fwrite("\n", 1, 1, prn_stream);
+}
diff --git a/devices/gdev8bcm.c b/devices/gdev8bcm.c
new file mode 100644
index 000000000..e40a981ec
--- /dev/null
+++ b/devices/gdev8bcm.c
@@ -0,0 +1,78 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+/* Dynamic color mapping for 8-bit displays */
+#include "gx.h"
+#include "gxdevice.h"
+#include "gdev8bcm.h"
+
+/* Initialize an 8-bit color map. */
+void
+gx_8bit_map_init(gx_8bit_color_map * pcm, int max_count)
+{
+ int i;
+
+ pcm->count = 0;
+ pcm->max_count = max_count;
+ for (i = 0; i < gx_8bit_map_size; i++)
+ pcm->map[i].rgb = gx_8bit_no_rgb;
+}
+
+/* Look up a color in an 8-bit color map. */
+/* Return <0 if not found. */
+int
+gx_8bit_map_rgb_color(const gx_8bit_color_map * pcm, gx_color_value r,
+ gx_color_value g, gx_color_value b)
+{
+ ushort rgb = gx_8bit_rgb_key(r, g, b);
+ const gx_8bit_map_entry *pme =
+ &pcm->map[(rgb * gx_8bit_map_spreader) % gx_8bit_map_size];
+
+ for (;; pme++) {
+ if (pme->rgb == rgb)
+ return pme->index;
+ else if (pme->rgb == gx_8bit_no_rgb)
+ break;
+ }
+ if (pme != &pcm->map[gx_8bit_map_size])
+ return pme - &pcm->map[gx_8bit_map_size];
+ /* We ran off the end; wrap around and continue. */
+ pme = &pcm->map[0];
+ for (;; pme++) {
+ if (pme->rgb == rgb)
+ return pme->index;
+ else if (pme->rgb == gx_8bit_no_rgb)
+ return pme - &pcm->map[gx_8bit_map_size];
+ }
+}
+
+/* Add a color to an 8-bit color map after an unsuccessful lookup, */
+/* and return its index. Return <0 if the map is full. */
+int
+gx_8bit_add_rgb_color(gx_8bit_color_map * pcm, gx_color_value r,
+ gx_color_value g, gx_color_value b)
+{
+ int index;
+ gx_8bit_map_entry *pme;
+
+ if (gx_8bit_map_is_full(pcm))
+ return -1;
+ index = gx_8bit_map_rgb_color(pcm, r, g, b);
+ if (index >= 0) /* shouldn't happen */
+ return index;
+ pme = &pcm->map[-index];
+ pme->rgb = gx_8bit_rgb_key(r, g, b);
+ return (pme->index = pcm->count++);
+}
diff --git a/devices/gdev8bcm.h b/devices/gdev8bcm.h
new file mode 100644
index 000000000..49040567b
--- /dev/null
+++ b/devices/gdev8bcm.h
@@ -0,0 +1,69 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+/* 8-bit color map support */
+/* Requires gxdevice.h (for gx_color_value) */
+
+#ifndef gdev8bcm_INCLUDED
+# define gdev8bcm_INCLUDED
+
+/*
+ * The MS-DOS, MS Windows, and X Windows drivers all use (at least on
+ * some platforms) an 8-bit color map in which some fraction is reserved
+ * for a pre-allocated cube and some or all of the remainder is
+ * allocated dynamically. Since looking up colors in this map can be
+ * a major performance bottleneck, we provide an efficient implementation
+ * that can be shared among drivers.
+ *
+ * As a performance compromise, we only look up the top 5 bits of the
+ * RGB value in the color map. This compromises color quality very little,
+ * and allows substantial optimizations.
+ */
+
+#define gx_8bit_map_size 323
+#define gx_8bit_map_spreader 123 /* approx. 323 - (1.618 * 323) */
+typedef struct gx_8bit_map_entry_s {
+ ushort rgb; /* key = 0rrrrrgggggbbbbb */
+#define gx_8bit_no_rgb ((ushort)0xffff)
+#define gx_8bit_rgb_key(r, g, b)\
+ (((r >> (gx_color_value_bits - 5)) << 10) +\
+ ((g >> (gx_color_value_bits - 5)) << 5) +\
+ (b >> (gx_color_value_bits - 5)))
+ short index; /* value */
+} gx_8bit_map_entry;
+typedef struct gx_8bit_color_map_s {
+ int count; /* # of occupied entries */
+ int max_count; /* max # of occupied entries */
+ gx_8bit_map_entry map[gx_8bit_map_size + 1];
+} gx_8bit_color_map;
+
+/* Initialize an 8-bit color map. */
+void gx_8bit_map_init(gx_8bit_color_map *, int);
+
+/* Look up a color in an 8-bit color map. */
+/* Return -1 if not found. */
+int gx_8bit_map_rgb_color(const gx_8bit_color_map *, gx_color_value,
+ gx_color_value, gx_color_value);
+
+/* Test whether an 8-bit color map has room for more entries. */
+#define gx_8bit_map_is_full(pcm)\
+ ((pcm)->count == (pcm)->max_count)
+
+/* Add a color to an 8-bit color map. */
+/* Return -1 if the map is full. */
+int gx_8bit_add_rgb_color(gx_8bit_color_map *, gx_color_value,
+ gx_color_value, gx_color_value);
+
+#endif /* gdev8bcm_INCLUDED */
diff --git a/devices/gdevadmp.c b/devices/gdevadmp.c
new file mode 100644
index 000000000..bf816c55e
--- /dev/null
+++ b/devices/gdevadmp.c
@@ -0,0 +1,401 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+/*
+ * Apple DMP / Imagewriter driver
+ *
+ * This is a modification of Mark Wedel's Apple DMP and
+ * Jonathan Luckey's Imagewriter II driver to
+ * support the Imagewriter LQ's higher resolution (320x216):
+ * appledmp: 120dpi x 72dpi is still supported (yuck)
+ * iwlo: 160dpi x 72dpi
+ * iwhi: 160dpi x 144dpi
+ * iwlq: 320dpi x 216dpi
+ *
+ * This is also my first attempt to work with gs. I have not included the LQ's
+ * ability to print in colour. Perhaps at a later date I will tackle that.
+ *
+ * BTW, to get your Imagewriter LQ serial printer to work with a PC, attach it
+ * with a nullmodem serial cable.
+ *
+ * Scott Barker (barkers@cuug.ab.ca)
+ */
+
+/*
+ * This is a modification of Mark Wedel's Apple DMP driver to
+ * support 2 higher resolutions:
+ * appledmp: 120dpi x 72dpi is still supported (yuck)
+ * iwlo: 160dpi x 72dpi
+ * iwhi: 160dpi x 144dpi
+ *
+ * The Imagewriter II is a bit odd. In pinfeed mode, it thinks its
+ * First line is 1 inch from the top of the page. If you set the top
+ * form so that it starts printing at the top of the page, and print
+ * to near the bottom, it thinks it has run onto the next page and
+ * the formfeed will skip a whole page. As a work around, I reverse
+ * the paper about a 1.5 inches at the end of the page before the
+ * formfeed to make it think its on the 'right' page. bah. hack!
+ *
+ * This is my first attempt to work with gs, so your milage may vary
+ *
+ * Jonathan Luckey (luckey@rtfm.mlb.fl.us)
+ */
+
+/* This is a bare bones driver I developed for my apple Dot Matrix Printer.
+ * This code originally was from the epson driver, but I removed a lot
+ * of stuff that was not needed.
+ *
+ * The Dot Matrix Printer was a predecessor to the apple Imagewriter. Its
+ * main difference being that it was parallel.
+ *
+ * This code should work fine on Imagewriters, as they have a superset
+ * of commands compared to the DMP printer.
+ *
+ * This driver does not produce the smalles output files possible. To
+ * do that, it should look through the output strings and find repeat
+ * occurances of characters, and use the escape sequence that allows
+ * printing repeat sequences. However, as I see it, this the limiting
+ * factor in printing is not transmission speed to the printer itself,
+ * but rather, how fast the print head can move. This is assuming the
+ * printer is set up with a reasonable speed (9600 bps)
+ *
+ * WHAT THE CODE DOES AND DOES NOT DO:
+ *
+ * To print out images, it sets the printer for unidirection printing
+ * and 15 cpi (120 dpi). IT sets line feed to 1/9 of an inch (72 dpi).
+ * When finished, it sets things back to bidirection print, 1/8" line
+ * feeds, and 12 cpi. There does not appear to be a way to reset
+ * things to initial values.
+ *
+ * This code does not set for 8 bit characters (which is required). It
+ * also assumes that carriage return/newline is needed, and not just
+ * carriage return. These are all switch settings on the DMP, and
+ * I have configured them for 8 bit data and cr only.
+ *
+ * You can search for the strings Init and Reset to find the strings
+ * that set up the printer and clear things when finished, and change
+ * them to meet your needs.
+ *
+ * Also, you need to make sure that the printer daemon (assuming unix)
+ * doesn't change the data as it is being printed. I have set my
+ * printcap file (sunos 4.1.1) with the string:
+ * ms=pass8,-opost
+ * and it works fine.
+ *
+ * Feel free to improve this code if you want. However, please make
+ * sure that the old DMP will still be supported by any changes. This
+ * may mean making an imagewriter device, and just copying this file
+ * to something like gdevimage.c.
+ *
+ * The limiting factor of the DMP is the vertical resolution. However, I
+ * see no way to do anything about this. Horizontal resolution could
+ * be increased by using 17 cpi (136 dpi). I believe the Imagewriter
+ * supports 24 cpi (192 dpi). However, the higher dpi, the slower
+ * the printing.
+ *
+ * Dot Matrix Code by Mark Wedel (master@cats.ucsc.edu)
+ */
+
+#include "gdevprn.h"
+
+/* The device descriptors */
+static dev_proc_print_page(dmp_print_page);
+
+/* Standard DMP device */
+const gx_device_printer far_data gs_appledmp_device =
+prn_device(prn_bg_procs, "appledmp", /* The print_page proc is compatible with allowing bg printing */
+ 85, /* width_10ths, 8.5" */
+ 110, /* height_10ths, 11" */
+ 120, 72, /* X_DPI, Y_DPI */
+ 0, 0.5, 0.5, 0, /* margins */
+ 1, dmp_print_page);
+
+/* lowrez Imagewriter device */
+const gx_device_printer far_data gs_iwlo_device =
+prn_device(prn_bg_procs, "iwlo", /* The print_page proc is compatible with allowing bg printing */
+ 85, /* width_10ths, 8.5" */
+ 110, /* height_10ths, 11" */
+ 160, 72, /* X_DPI, Y_DPI */
+ 0, 0.5, 0.5, 0, /* margins */
+ 1, dmp_print_page);
+
+/* hirez Imagewriter device */
+const gx_device_printer far_data gs_iwhi_device =
+prn_device(prn_bg_procs, "iwhi", /* The print_page proc is compatible with allowing bg printing */
+ 85, /* width_10ths, 8.5" */
+ 110, /* height_10ths, 11" */
+ 160, 144, /* X_DPI, Y_DPI */
+ 0, 0.5, 0.5, 0, /* margins */
+ 1, dmp_print_page);
+
+/* LQ hirez Imagewriter device */
+const gx_device_printer far_data gs_iwlq_device =
+prn_device(prn_bg_procs, "iwlq", /* The print_page proc is compatible with allowing bg printing */
+ 85, /* width_10ths, 8.5" */
+ 110, /* height_10ths, 11" */
+ 320, 216,
+ 0, 0, 0.5, 0, /* margins */
+ 1, dmp_print_page);
+
+/* ------ Internal routines ------ */
+
+#define DMP 1
+#define IWLO 2
+#define IWHI 3
+#define IWLQ 4
+
+/* Send the page to the printer. */
+static int
+dmp_print_page(gx_device_printer *pdev, FILE *prn_stream)
+{
+ int dev_type;
+
+ int line_size = gdev_mem_bytes_per_scan_line((gx_device *)pdev);
+ /* Note that in_size is a multiple of 8. */
+ int in_size = line_size * 8;
+
+ byte *buf1 = (byte *)gs_malloc(pdev->memory, in_size, 1, "dmp_print_page(buf1)");
+ byte *buf2 = (byte *)gs_malloc(pdev->memory, in_size, 1, "dmp_print_page(buf2)");
+ byte *prn = (byte *)gs_malloc(pdev->memory, 3*in_size, 1, "dmp_print_page(prn)");
+
+ byte *in = buf1;
+ byte *out = buf2;
+ int lnum = 0;
+
+ /* Check allocations */
+ if ( buf1 == 0 || buf2 == 0 || prn == 0 )
+ {
+ if ( buf1 )
+ gs_free(pdev->memory, (char *)buf1, in_size, 1,
+ "dmp_print_page(buf1)");
+ if ( buf2 )
+ gs_free(pdev->memory, (char *)buf2, in_size, 1,
+ "dmp_print_page(buf2)");
+ if ( prn )
+ gs_free(pdev->memory, (char *)prn, in_size, 1,
+ "dmp_print_page(prn)");
+ return_error(gs_error_VMerror);
+ }
+
+ if ( pdev->y_pixels_per_inch == 216 )
+ dev_type = IWLQ;
+ else if ( pdev->y_pixels_per_inch == 144 )
+ dev_type = IWHI;
+ else if ( pdev->x_pixels_per_inch == 160 )
+ dev_type = IWLO;
+ else
+ dev_type = DMP;
+
+ /* Initialize the printer and reset the margins. */
+
+ fputs("\r\n\033>\033T16", prn_stream);
+
+ switch(dev_type)
+ {
+ case IWLQ:
+ fputs("\033P\033a3", prn_stream);
+ break;
+ case IWHI:
+ case IWLO:
+ fputs("\033P", prn_stream);
+ break;
+ case DMP:
+ default:
+ fputs("\033q", prn_stream);
+ break;
+ }
+
+ /* Print lines of graphics */
+ while ( lnum < pdev->height )
+ {
+ byte *inp;
+ byte *in_end;
+ byte *out_end;
+ int lcnt,ltmp;
+ int count, passes;
+ byte *prn_blk, *prn_end, *prn_tmp;
+
+/* The apple DMP printer seems to be odd in that the bit order on
+ * each line is reverse what might be expected. Meaning, an
+ * underscore would be done as a series of 0x80, while on overscore
+ * would be done as a series of 0x01. So we get each
+ * scan line in reverse order.
+ */
+
+ switch (dev_type)
+ {
+ case IWLQ: passes = 3; break;
+ case IWHI: passes = 2; break;
+ case IWLO:
+ case DMP:
+ default: passes = 1; break;
+ }
+
+ for (count = 0; count < passes; count++)
+ {
+ for (lcnt=0; lcnt<8; lcnt++)
+ {
+ switch(dev_type)
+ {
+ case IWLQ: ltmp = lcnt + 8*count; break;
+ case IWHI: ltmp = 2*lcnt + count; break;
+ case IWLO:
+ case DMP:
+ default: ltmp = lcnt; break;
+ }
+
+ if ((lnum+ltmp)>pdev->height)
+ memset(in+lcnt*line_size,0,line_size);
+ else
+ gdev_prn_copy_scan_lines(pdev,
+ lnum+ltmp, in + line_size*(7 - lcnt),
+ line_size);
+ }
+
+ out_end = out;
+ inp = in;
+ in_end = inp + line_size;
+ for ( ; inp < in_end; inp++, out_end += 8 )
+ {
+ gdev_prn_transpose_8x8(inp, line_size,
+ out_end, 1);
+ }
+
+ out_end = out;
+
+ switch (dev_type)
+ {
+ case IWLQ: prn_end = prn + count; break;
+ case IWHI: prn_end = prn + in_size*count; break;
+ case IWLO:
+ case DMP:
+ default: prn_end = prn; break;
+ }
+
+ while ( (int)(out_end-out) < in_size)
+ {
+ *prn_end = *(out_end++);
+ if ((dev_type) == IWLQ) prn_end += 3;
+ else prn_end++;
+ }
+ }
+
+ switch (dev_type)
+ {
+ case IWLQ:
+ prn_blk = prn;
+ prn_end = prn_blk + in_size * 3;
+ while (prn_end > prn && prn_end[-1] == 0 &&
+ prn_end[-2] == 0 && prn_end[-3] == 0)
+ {
+ prn_end -= 3;
+ }
+ while (prn_blk < prn_end && prn_blk[0] == 0 &&
+ prn_blk[1] == 0 && prn_blk[2] == 0)
+ {
+ prn_blk += 3;
+ }
+ if (prn_end != prn_blk)
+ {
+ if ((prn_blk - prn) > 7)
+ fprintf(prn_stream,"\033U%04d%c%c%c",
+ (int)((prn_blk - prn)/3),
+ 0, 0, 0);
+ else
+ prn_blk = prn;
+ fprintf(prn_stream,"\033C%04d",
+ (int)((prn_end - prn_blk)/3));
+ fwrite(prn_blk, 1, (int)(prn_end - prn_blk),
+ prn_stream);
+ }
+ break;
+ case IWHI:
+ for (count = 0; count < 2; count++)
+ {
+ prn_blk = prn_tmp = prn + in_size*count;
+ prn_end = prn_blk + in_size;
+ while (prn_end > prn_blk && prn_end[-1] == 0)
+ prn_end--;
+ while (prn_blk < prn_end && prn_blk[0] == 0)
+ prn_blk++;
+ if (prn_end != prn_blk)
+ {
+ if ((prn_blk - prn_tmp) > 7)
+ fprintf(prn_stream,
+ "\033V%04d%c",
+ (int)(prn_blk-prn_tmp),
+ 0);
+ else
+ prn_blk = prn_tmp;
+ fprintf(prn_stream,"\033G%04d",
+ (int)(prn_end - prn_blk));
+ fwrite(prn_blk, 1,
+ (int)(prn_end - prn_blk),
+ prn_stream);
+ }
+ if (!count) fputs("\033T01\r\n",prn_stream);
+ }
+ fputs("\033T15",prn_stream);
+ break;
+ case IWLO:
+ case DMP:
+ default:
+ prn_blk = prn;
+ prn_end = prn_blk + in_size;
+ while (prn_end > prn_blk && prn_end[-1] == 0)
+ prn_end--;
+ while (prn_blk < prn_end && prn_blk[0] == 0)
+ prn_blk++;
+ if (prn_end != prn_blk)
+ {
+ if ((prn_blk - prn) > 7)
+ fprintf(prn_stream,"\033V%04d%c",
+ (int)(prn_blk - prn), 0);
+ else
+ prn_blk = prn;
+ fprintf(prn_stream,"\033G%04d",
+ (int)(prn_end - prn_blk));
+ fwrite(prn_blk, 1, (int)(prn_end - prn_blk),
+ prn_stream);
+ }
+ break;
+ }
+
+ fputs("\r\n",prn_stream);
+
+ switch (dev_type)
+ {
+ case IWLQ: lnum += 24 ; break;
+ case IWHI: lnum += 16 ; break;
+ case IWLO:
+ case DMP:
+ default: lnum += 8 ; break;
+ }
+ }
+
+ /* ImageWriter will skip a whole page if too close to end */
+ /* so skip back more than an inch */
+ if ( !(dev_type == DMP) )
+ fputs("\033T99\n\n\033r\n\n\n\n\033f", prn_stream);
+
+ /* Formfeed and Reset printer */
+ fputs("\033T16\f\033<\033B\033E", prn_stream);
+ fflush(prn_stream);
+
+ gs_free(pdev->memory, (char *)prn, in_size, 1, "dmp_print_page(prn)");
+ gs_free(pdev->memory, (char *)buf2, in_size, 1, "dmp_print_page(buf2)");
+ gs_free(pdev->memory, (char *)buf1, in_size, 1, "dmp_print_page(buf1)");
+ return 0;
+}
diff --git a/devices/gdevatx.c b/devices/gdevatx.c
new file mode 100644
index 000000000..2ece81f93
--- /dev/null
+++ b/devices/gdevatx.c
@@ -0,0 +1,267 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+/* Practical Automation ATX-23, -24, -38 and ITK-24i, -38 driver */
+#include "math_.h"
+#include "gdevprn.h"
+
+/*
+ * All of the ATX printers have an unprintable margin of 0.125" at the top
+ * and bottom of the page. They also have unprintable left/right margins:
+ * ATX-23 0.25"
+ * ATX-24 0.193"
+ * ATX-38 0.25"
+ * The ITK-24i does not have an unprintable left or right margin.
+ * The ITK-38 is identical to the ATX-38 for the purpose of this driver.
+ *
+ * The code below assumes that coordinates refer only to the *printable*
+ * part of each page. This is wrong and must eventually be changed.
+ */
+
+/* Define the printer commands. */
+#define ATX_SET_PAGE_LENGTH "\033f" /* + 2-byte length */
+#define ATX_VERTICAL_TAB "\033L" /* + 2-byte count */
+#define ATX_UNCOMPRESSED_DATA "\033d" /* + 2-byte count */
+#define ATX_COMPRESSED_DATA "\033x" /* + 1-byte word count */
+#define ATX_END_PAGE "\033e"
+
+/* The device descriptors */
+static dev_proc_print_page(atx23_print_page);
+static dev_proc_print_page(atx24_print_page);
+static dev_proc_print_page(atx38_print_page);
+
+#define ATX_DEVICE(dname, w10, h10, dpi, lrm, btm, print_page)\
+ prn_device_margins(prn_std_procs, dname, w10, h10, dpi, dpi, 0, 0,\
+ lrm, btm, lrm, btm, 1, print_page)
+
+const gx_device_printer gs_atx23_device = /* real width = 576 pixels */
+ATX_DEVICE("atx23", 28 /* 2.84" */, 35 /* (minimum) */,
+ 203, 0.25, 0.125, atx23_print_page);
+
+const gx_device_printer gs_atx24_device = /* real width = 832 pixels */
+ATX_DEVICE("atx24", 41 /* 4.1" */, 35 /* (minimum) */,
+ 203, 0.193, 0.125, atx24_print_page);
+
+const gx_device_printer gs_atx38_device = /* real width = 2400 pixels */
+ATX_DEVICE("atx38", 80 /* 8.0" */, 35 /* (minimum) */,
+ 300, 0.25, 0.125, atx38_print_page);
+
+const gx_device_printer gs_itk24i_device = /* real width = 832 pixels */
+ATX_DEVICE("itk24i", 41 /* 4.1" */, 35 /* (minimum) */,
+ 203, 0.0, 0.125, atx24_print_page);
+
+const gx_device_printer gs_itk38_device = /* real width = 2400 pixels */
+ATX_DEVICE("itk38", 80 /* 8.0" */, 35 /* (minimum) */,
+ 300, 0.25, 0.125, atx38_print_page);
+
+/* Output a printer command with a 2-byte, little-endian numeric argument. */
+static void
+fput_atx_command(FILE *f, const char *str, int value)
+{
+ fputs(str, f);
+ fputc((byte)value, f);
+ fputc((byte)(value >> 8), f);
+}
+
+/*
+ * Attempt to compress a scan line of data. in_size and out_size are even.
+ * Return -1 if the compressed data would exceed out_size, otherwise the
+ * size of the compressed data (always even).
+ */
+#define MIN_IN_SIZE_TO_COMPRESS 50
+#define MAX_COMPRESSED_SEGMENT_PAIRS 127
+#define MAX_UNCOMPRESSED_SEGMENT_PAIRS 255
+#define COMPRESSED_SEGMENT_COMMAND 0x80 /* + # of repeated pairs */
+#define UNCOMPRESSED_SEGMENT_COMMAND 0x7f /* followed by # of pairs */
+static int
+atx_compress(const byte *in_buf, int in_size, byte *out_buf, int out_size)
+{
+ const byte *const in_end = in_buf + in_size;
+ byte *const out_end = out_buf + out_size;
+ const byte *in = in_buf;
+ byte *out = out_buf;
+ byte *out_command;
+ int pair_count;
+
+ if (in_size < MIN_IN_SIZE_TO_COMPRESS)
+ return -1; /* not worth compressing */
+
+ /* Start a new segment. */
+ New_Segment:
+ if (in == in_end) /* end of input data */
+ return out - out_buf;
+ if (out == out_end) /* output buffer full */
+ return -1;
+ out_command = out;
+ out += 2;
+ if (in[1] == in[0]) { /* start compressed segment */
+ /* out[-2] will be compressed segment command */
+ out[-1] = in[0];
+ pair_count = 1;
+ goto Scan_Compressed_Pair;
+ } else { /* start uncompressed segment */
+ out[-2] = UNCOMPRESSED_SEGMENT_COMMAND;
+ /* out[-1] will be pair count */
+ pair_count = 0;
+ goto Scan_Uncompressed_Pair;
+ }
+
+ /* Scan compressed data. */
+ Scan_Compressed:
+ if (pair_count == MAX_COMPRESSED_SEGMENT_PAIRS ||
+ in == in_end || in[0] != in[-1] || in[1] != in[0]
+ ) { /* end the segment */
+ out_command[0] = COMPRESSED_SEGMENT_COMMAND + pair_count;
+ goto New_Segment;
+ }
+ ++pair_count;
+ Scan_Compressed_Pair:
+ in += 2;
+ goto Scan_Compressed;
+
+ /* Scan uncompressed data. */
+ Scan_Uncompressed:
+ if (pair_count == MAX_UNCOMPRESSED_SEGMENT_PAIRS ||
+ in == in_end || in[1] == in[0]
+ ) { /* end the segment */
+ out_command[1] = pair_count;
+ goto New_Segment;
+ }
+ Scan_Uncompressed_Pair:
+ if (out == out_end) /* output buffer full */
+ return -1;
+ out[0] = in[0], out[1] = in[1];
+ in += 2;
+ out += 2;
+ ++pair_count;
+ goto Scan_Uncompressed;
+
+}
+
+/* Send the page to the printer. */
+static int
+atx_print_page(gx_device_printer *pdev, FILE *f, int max_width_bytes)
+{
+ /*
+ * The page length command uses 16 bits to represent the length in
+ * units of 0.01", so the maximum representable page length is
+ * 655.35", including the unprintable top and bottom margins.
+ * Compute the maximum height of the printable area in pixels.
+ */
+ float top_bottom_skip = (pdev->HWMargins[1] + pdev->HWMargins[3]) / 72.0;
+ int max_height = (int)(pdev->HWResolution[1] * 655 - top_bottom_skip);
+ int height = min(pdev->height, max_height);
+ int page_length_100ths =
+ (int)ceil((height / pdev->HWResolution[1] + top_bottom_skip) * 100);
+ gs_memory_t *mem = pdev->memory;
+ int raster = gx_device_raster((gx_device *)pdev, true);
+ byte *buf;
+ /*
+ * ATX_COMPRESSED_DATA only takes a 1-byte (word) count.
+ * Thus no compressed scan line can take more than 510 bytes.
+ */
+ int compressed_raster = min(raster / 2, 510); /* require 50% compression */
+ byte *compressed;
+ int blank_lines, lnum;
+ int code = 0;
+
+ /* Enforce a minimum 3" page length. */
+ if (page_length_100ths < 300)
+ page_length_100ths = 300;
+ buf = gs_alloc_bytes(mem, raster, "atx_print_page(buf)");
+ compressed = gs_alloc_bytes(mem, compressed_raster,
+ "atx_print_page(compressed)");
+ if (buf == 0 || compressed == 0) {
+ code = gs_note_error(gs_error_VMerror);
+ goto done;
+ }
+ fput_atx_command(f, ATX_SET_PAGE_LENGTH, page_length_100ths);
+ for (blank_lines = 0, lnum = 0; lnum < height; ++lnum) {
+ byte *row;
+ byte *end;
+ int count;
+
+ gdev_prn_get_bits(pdev, lnum, buf, &row);
+ /* Find the end of the non-blank data. */
+ for (end = row + raster; end > row && end[-1] == 0 && end[-2] == 0; )
+ end -= 2;
+ if (end == row) { /* blank line */
+ ++blank_lines;
+ continue;
+ }
+ if (blank_lines) { /* skip vertically */
+ fput_atx_command(f, ATX_VERTICAL_TAB, blank_lines + 1);
+ blank_lines = 0;
+ }
+ /* Truncate the line to the maximum printable width. */
+ if (end - row > max_width_bytes)
+ end = row + max_width_bytes;
+ count = atx_compress(row, end - row, compressed, compressed_raster);
+ if (count >= 0) { /* compressed line */
+ /*
+ * Note that since compressed_raster can't exceed 510, count
+ * can't exceed 510 either.
+ */
+ fputs(ATX_COMPRESSED_DATA, f);
+ fputc(count / 2, f);
+ fwrite(compressed, 1, count, f);
+ } else { /* uncompressed line */
+ int num_bytes = end - row;
+
+ fput_atx_command(f, ATX_UNCOMPRESSED_DATA, num_bytes);
+ fwrite(row, 1, num_bytes, f);
+ }
+ }
+
+#if 0 /**************** MAY NOT BE NEEDED ****************/
+ /* Enforce the minimum page length, and skip any final blank lines. */
+ {
+ int paper_length = (int)(pdev->HWResolution[1] * 3 + 0.5);
+ int printed_length = height - blank_lines;
+
+ if (height > paper_length)
+ paper_length = height;
+ if (printed_length < paper_length)
+ fput_atx_command(f, ATX_VERTICAL_TAB,
+ paper_length - printed_length + 1);
+ }
+#endif
+
+ /* End the page. */
+ fputs(ATX_END_PAGE, f);
+
+ done:
+ gs_free_object(mem, compressed, "atx_print_page(compressed)");
+ gs_free_object(mem, buf, "atx_print_page(buf)");
+ return code;
+}
+
+/* Print pages with specified maximum pixel widths. */
+static int
+atx23_print_page(gx_device_printer *pdev, FILE *f)
+{
+ return atx_print_page(pdev, f, 576 / 8);
+}
+static int
+atx24_print_page(gx_device_printer *pdev, FILE *f)
+{
+ return atx_print_page(pdev, f, 832 / 8);
+}
+static int
+atx38_print_page(gx_device_printer *pdev, FILE *f)
+{
+ return atx_print_page(pdev, f, 2400 / 8);
+}
+
diff --git a/devices/gdevbit.c b/devices/gdevbit.c
new file mode 100644
index 000000000..9f6a5c06e
--- /dev/null
+++ b/devices/gdevbit.c
@@ -0,0 +1,820 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* "Plain bits" devices to measure rendering time. */
+
+#include "gdevprn.h"
+#include "gsparam.h"
+#include "gscrd.h"
+#include "gscrdp.h"
+#include "gxlum.h"
+#include "gxdcconv.h"
+#include "gdevdcrd.h"
+#include "gsutil.h" /* for bittags hack */
+
+/* Define the device parameters. */
+#ifndef X_DPI
+# define X_DPI 72
+#endif
+#ifndef Y_DPI
+# define Y_DPI 72
+#endif
+
+/* The device descriptor */
+static dev_proc_get_color_mapping_procs(bittag_get_color_mapping_procs);
+static dev_proc_map_rgb_color(bittag_rgb_map_rgb_color);
+static dev_proc_map_color_rgb(bittag_map_color_rgb);
+static dev_proc_put_params(bittag_put_params);
+static dev_proc_map_rgb_color(bit_mono_map_color);
+#if 0 /* unused */
+static dev_proc_map_rgb_color(bit_forcemono_map_rgb_color);
+#endif
+static dev_proc_map_rgb_color(bitrgb_rgb_map_rgb_color);
+static dev_proc_map_color_rgb(bit_map_color_rgb);
+static dev_proc_map_cmyk_color(bit_map_cmyk_color);
+static dev_proc_get_params(bit_get_params);
+static dev_proc_put_params(bit_put_params);
+static dev_proc_print_page(bit_print_page);
+static dev_proc_print_page(bittags_print_page);
+static dev_proc_put_image(bit_put_image);
+
+#define bit_procs(encode_color)\
+{ gdev_prn_open,\
+ gx_default_get_initial_matrix,\
+ NULL, /* sync_output */\
+ /* Since the print_page doesn't alter the device, this device can print in the background */\
+ gdev_prn_bg_output_page,\
+ gdev_prn_close,\
+ encode_color, /* map_rgb_color */\
+ bit_map_color_rgb, /* map_color_rgb */\
+ NULL, /* fill_rectangle */\
+ NULL, /* tile_rectangle */\
+ NULL, /* copy_mono */\
+ NULL, /* copy_color */\
+ NULL, /* draw_line */\
+ NULL, /* get_bits */\
+ bit_get_params,\
+ bit_put_params,\
+ encode_color, /* map_cmyk_color */\
+ NULL, /* get_xfont_procs */\
+ NULL, /* get_xfont_device */\
+ NULL, /* map_rgb_alpha_color */\
+ gx_page_device_get_page_device, /* get_page_device */\
+ NULL, /* get_alpha_bits */\
+ NULL, /* copy_alpha */\
+ NULL, /* get_band */\
+ NULL, /* copy_rop */\
+ NULL, /* fill_path */\
+ NULL, /* stroke_path */\
+ NULL, /* fill_mask */\
+ NULL, /* fill_trapezoid */\
+ NULL, /* fill_parallelogram */\
+ NULL, /* fill_triangle */\
+ NULL, /* draw_thin_line */\
+ NULL, /* begin_image */\
+ NULL, /* image_data */\
+ NULL, /* end_image */\
+ NULL, /* strip_tile_rectangle */\
+ NULL, /* strip_copy_rop */\
+ NULL, /* get_clipping_box */\
+ NULL, /* begin_typed_image */\
+ NULL, /* get_bits_rectangle */\
+ NULL, /* map_color_rgb_alpha */\
+ NULL, /* create_compositor */\
+ NULL, /* get_hardware_params */\
+ NULL, /* text_begin */\
+ NULL, /* finish_copydevice */\
+ NULL, /* begin_transparency_group */\
+ NULL, /* end_transparency_group */\
+ NULL, /* begin_transparency_mask */\
+ NULL, /* end_transparency_mask */\
+ NULL, /* discard_transparency_layer */\
+ NULL, /* get_color_mapping_procs */\
+ NULL, /* get_color_comp_index */\
+ encode_color, /* encode_color */\
+ bit_map_color_rgb /* decode_color */\
+}
+
+/*
+ * The following macro is used in get_params and put_params to determine the
+ * num_components for the current device. It works using the device name
+ * character after "bit" which is either '\0', 'r', or 'c'. Any new devices
+ * that are added to this module must modify this macro to return the
+ * correct num_components. This is needed to support the ForceMono
+ * parameter, which alters dev->num_components.
+ */
+#define REAL_NUM_COMPONENTS(dev) (dev->dname[3] == 'c' ? 4 : \
+ dev->dname[3] == 'r' ? 3 : 1)
+struct gx_device_bit_s {
+ gx_device_common;
+ gx_prn_device_common;
+ int FirstLine, LastLine; /* to allow multi-threaded rendering testing */
+};
+typedef struct gx_device_bit_s gx_device_bit;
+
+static const gx_device_procs bitmono_procs =
+bit_procs(bit_mono_map_color);
+const gx_device_bit gs_bit_device =
+{prn_device_body(gx_device_bit, bitmono_procs, "bit",
+ DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
+ X_DPI, Y_DPI,
+ 0, 0, 0, 0, /* margins */
+ 1, 1, 1, 0, 2, 1, bit_print_page)
+};
+
+static const gx_device_procs bitrgb_procs =
+bit_procs(bitrgb_rgb_map_rgb_color);
+const gx_device_bit gs_bitrgb_device =
+{prn_device_body(gx_device_bit, bitrgb_procs, "bitrgb",
+ DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
+ X_DPI, Y_DPI,
+ 0, 0, 0, 0, /* margins */
+ 3, 4, 1, 1, 2, 2, bit_print_page)
+};
+
+static const gx_device_procs bitcmyk_procs =
+bit_procs(bit_map_cmyk_color);
+const gx_device_bit gs_bitcmyk_device =
+{prn_device_body(gx_device_bit, bitcmyk_procs, "bitcmyk",
+ DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
+ X_DPI, Y_DPI,
+ 0, 0, 0, 0, /* margins */
+ 4, 4, 1, 1, 2, 2, bit_print_page)
+};
+
+static const gx_device_procs bitrgbtags_procs =
+ {
+ gdev_prn_open, /* open_device */
+ gx_default_get_initial_matrix, /* initial_matrix */
+ ((void *)0), /* sync_output */
+ gdev_prn_output_page, /* output page */
+ gdev_prn_close, /* close_device */
+ bittag_rgb_map_rgb_color, /* map rgb color */
+ bittag_map_color_rgb, /* map color rgb */
+ ((void *)0), /* fill_rectangle */
+ ((void *)0), /* tile rectangle */
+ ((void *)0), /* copy mono */
+ ((void *)0), /* copy color */
+ ((void *)0), /* obsolete draw line */
+ ((void *)0), /* get_bits */
+ gdev_prn_get_params, /* get params */
+ bittag_put_params, /* put params */
+ bittag_rgb_map_rgb_color, /* map_cmyk_color */
+ ((void *)0), /* get_xfonts */
+ ((void *)0), /* get_xfont_device */
+ ((void *)0), /* map_rgb_alpha_color */
+ gx_page_device_get_page_device, /* get_page_device */
+ ((void *)0), /* get_alpha_bits */
+ ((void *)0), /* copy_alpha */
+ ((void *)0), /* get_band */
+ ((void *)0), /* copy_rop */
+ ((void *)0), /* fill_path */
+ ((void *)0), /* stroke_path */
+ ((void *)0), /* fill_mask */
+ ((void *)0), /* fill_trapezoid */
+ ((void *)0), /* fill_parallelogram */
+ ((void *)0), /* fill_triangle */
+ ((void *)0), /* draw_thin_line */
+ ((void *)0), /* begin_image */
+ ((void *)0), /* image_data */
+ ((void *)0), /* end_image */
+ ((void *)0), /* strip_tile_rectangle */
+ ((void *)0), /* strip_copy_rop */
+ ((void *)0), /* get_clipping_box */
+ ((void *)0), /* begin_typed_image */
+ ((void *)0), /* get_bits_rectangle */
+ ((void *)0), /* map_color_rgb_alpha */
+ ((void *)0), /* create_compositor */
+ ((void *)0), /* get_hardware_params */
+ ((void *)0), /* text_begin */
+ ((void *)0), /* finish_copydevice */
+ ((void *)0), /* begin_transparency_group */
+ ((void *)0), /* end_transparency_group */
+ ((void *)0), /* begin_transparency_mask */
+ ((void *)0), /* end_transparency_mask */
+ ((void *)0), /* discard_transparency_layer */
+ bittag_get_color_mapping_procs, /* get_color_mapping_procs */
+ ((void *)0), /* get_color_comp_index */
+ bittag_rgb_map_rgb_color, /* encode_color */
+ bittag_map_color_rgb, /* decode_color */
+ ((void *)0), /* pattern_manage */
+ ((void *)0), /* fill_rectangle_hl_color */
+ ((void *)0), /* include_color_space */
+ ((void *)0), /* fill_linear_color_scanline */
+ ((void *)0), /* fill_linear_color_trapezoid */
+ ((void *)0), /* fill_linear_color_triangle */
+ ((void *)0), /* update_spot_equivalent_colors */
+ ((void *)0), /* ret_devn_params */
+ ((void *)0), /* fillpage */
+ ((void *)0), /* push_transparency_state */
+ ((void *)0), /* pop_transparency_state */
+ bit_put_image /* put_image */
+ };
+
+const gx_device_bit gs_bitrgbtags_device =
+ {
+ sizeof(gx_device_bit),
+ &bitrgbtags_procs,
+ "bitrgbtags",
+ 0 , /* memory */
+ &st_device_printer,
+ 0 , /* stype_is_dynamic */
+ 0 , /* finalize */
+ { 0 } , /* rc header */
+ 0 , /* retained */
+ 0 , /* parent */
+ 0 , /* child */
+ 0 , /* subclass data */
+ 0 , /* is open */
+ 0, /* max_fill_band */
+ { /* color infor */
+ 3, /* max_components */
+ 3, /* num_components */
+ GX_CINFO_POLARITY_ADDITIVE, /* polarity */
+ 32, /* depth */
+ GX_CINFO_COMP_NO_INDEX, /* gray index */
+ 255 , /* max_gray */
+ 255 , /* max_colors */
+ 256 , /* dither grays */
+ 256 , /* dither colors */
+ { 1, 1 } , /* antialiasing */
+ GX_CINFO_UNKNOWN_SEP_LIN, /* sep and linear */
+ { 16, 8, 0, 0 } , /* comp shift */
+ { 8, 8, 8, 8 } , /* comp bits */
+ { 0xFF0000, 0x00FF00, 0x0000FF } , /* comp mask */
+ ( "DeviceRGBT" ), /* color model name */
+ GX_CINFO_OPMODE_UNKNOWN , /* overprint mode */
+ 0, /* process comps */
+ 0 /* icc_locations */
+ },
+ {
+ ((gx_color_index)(~0)),
+ ((gx_color_index)(~0))
+ },
+ (int)((float)(85) * (X_DPI) / 10 + 0.5),
+ (int)((float)(110) * (Y_DPI) / 10 + 0.5),
+ 0, /* Pad */
+ 0, /* Align */
+ 0, /* Num planes */
+ 0,
+ {
+ (float)(((((int)((float)(85) * (X_DPI) / 10 + 0.5)) * 72.0 + 0.5) - 0.5) / (X_DPI)) ,
+ (float)(((((int)((float)(110) * (Y_DPI) / 10 + 0.5)) * 72.0 + 0.5) - 0.5) / (Y_DPI)) },
+ {
+ 0,
+ 0,
+ 0,
+ 0
+ } ,
+ 0 ,
+ { X_DPI, Y_DPI } ,
+ { X_DPI, Y_DPI },
+ {(float)(-(0) * (X_DPI)),
+ (float)(-(0) * (Y_DPI))},
+ {(float)((0) * 72.0),
+ (float)((0) * 72.0),
+ (float)((0) * 72.0),
+ (float)((0) * 72.0)},
+ 0 , /*FirstPage*/
+ 0 , /*LastPage*/
+ 0 , /*PageHandlerPushed*/
+ 0 , /*DisablePageHandler*/
+ 0 , /*ObjectFilter*/
+ 0 , /*ObjectHandlerPushed*/
+ 0 , /*PageCount*/
+ 0 , /*ShowPageCount*/
+ 1 , /*NumCopies*/
+ 0 , /*NumCopiesSet*/
+ 0 , /*IgnoreNumCopies*/
+ 0 , /*UseCIEColor*/
+ 0 , /*LockSafetyParams*/
+ 0, /*band_offset_x*/
+ 0, /*band_offset_*/
+ {false}, /*sgr*/
+ 0, /*MaxPatternBitmap*/
+ 0, /*page_uses_transparency*/
+ { MAX_BITMAP, BUFFER_SPACE,
+ { BAND_PARAMS_INITIAL_VALUES },
+ 0/*false*/, /* params_are_read_only */
+ BandingAuto /* banding_type */
+ }, /*space_params*/
+ 0, /*icc_struct*/
+ GS_UNKNOWN_TAG, /* this device supports tags */
+ {
+ gx_default_install,
+ gx_default_begin_page,
+ gx_default_end_page
+ },
+ { 0 },
+ { 0 },
+ { bittags_print_page,
+ gx_default_print_page_copies,
+ { gx_default_create_buf_device,
+ gx_default_size_buf_device,
+ gx_default_setup_buf_device,
+ gx_default_destroy_buf_device },
+ gx_default_get_space_params,
+ gx_default_start_render_thread,
+ gx_default_open_render_device,
+ gx_default_close_render_device,
+ gx_default_buffer_page },
+ { 0 },
+ 0 ,
+ 0 ,
+ 0 ,
+ -1,
+ 0 ,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0 ,
+ 0,
+ 0,
+ 0
+ };
+
+static void
+cmyk_cs_to_rgb_cm(gx_device * dev, frac c, frac m, frac y, frac k, frac out[])
+{
+ color_cmyk_to_rgb(c, m, y, k, NULL, out, dev->memory);
+}
+
+static void
+private_rgb_cs_to_rgb_cm(gx_device * dev, const gs_imager_state *pis,
+ frac r, frac g, frac b, frac out[])
+{
+ out[0] = r;
+ out[1] = g;
+ out[2] = b;
+}
+
+static void
+gray_cs_to_rgb_cm(gx_device * dev, frac gray, frac out[])
+{
+ out[0] = out[1] = out[2] = gray;
+}
+
+static const gx_cm_color_map_procs bittag_DeviceRGB_procs = {
+ gray_cs_to_rgb_cm, private_rgb_cs_to_rgb_cm, cmyk_cs_to_rgb_cm
+};
+
+static const gx_cm_color_map_procs *
+bittag_get_color_mapping_procs(const gx_device *dev)
+{
+ return &bittag_DeviceRGB_procs;
+}
+
+static gx_color_index
+bittag_rgb_map_rgb_color(gx_device * dev, const gx_color_value cv[])
+{
+ return
+ gx_color_value_to_byte(cv[2]) +
+ (((uint) gx_color_value_to_byte(cv[1])) << 8) +
+ (((ulong) gx_color_value_to_byte(cv[0])) << 16) +
+ ((ulong)(dev->graphics_type_tag & ~GS_DEVICE_ENCODES_TAGS) << 24);
+}
+
+static int
+bittag_map_color_rgb(gx_device * dev, gx_color_index color, gx_color_value cv[4])
+{
+ int depth = 24;
+ int ncomp = 3;
+ int bpc = depth / ncomp;
+ uint mask = (1 << bpc) - 1;
+
+#define cvalue(c) ((gx_color_value)((ulong)(c) * gx_max_color_value / mask))
+
+ gx_color_index cshift = color;
+ cv[2] = cvalue(cshift & mask);
+ cshift >>= bpc;
+ cv[1] = cvalue(cshift & mask);
+ cshift >>= bpc;
+ cv[0] = cvalue(cshift & mask);
+ return 0;
+#undef cvalue
+}
+
+/* Map gray to color. */
+/* Note that 1-bit monochrome is a special case. */
+static gx_color_index
+bit_mono_map_color(gx_device * dev, const gx_color_value cv[])
+{
+ int bpc = dev->color_info.depth;
+ int drop = sizeof(gx_color_value) * 8 - bpc;
+ gx_color_value gray = cv[0];
+
+ return (bpc == 1 ? gx_max_color_value - gray : gray) >> drop;
+}
+
+#if 0 /* unused */
+/* Map RGB to gray shade. */
+/* Only used in CMYK mode when put_params has set ForceMono=1 */
+static gx_color_index
+bit_forcemono_map_rgb_color(gx_device * dev, const gx_color_value cv[])
+{
+ gx_color_value color;
+ int bpc = dev->color_info.depth / 4; /* This function is used in CMYK mode */
+ int drop = sizeof(gx_color_value) * 8 - bpc;
+ gx_color_value gray, red, green, blue;
+ red = cv[0]; green = cv[1]; blue = cv[2];
+ gray = red;
+ if ((red != green) || (green != blue))
+ gray = (red * (unsigned long)lum_red_weight +
+ green * (unsigned long)lum_green_weight +
+ blue * (unsigned long)lum_blue_weight +
+ (lum_all_weights / 2))
+ / lum_all_weights;
+
+ color = (gx_max_color_value - gray) >> drop; /* color is in K channel */
+ return color;
+}
+#endif
+
+gx_color_index
+bitrgb_rgb_map_rgb_color(gx_device * dev, const gx_color_value cv[])
+{
+ if (dev->color_info.depth == 24)
+ return gx_color_value_to_byte(cv[2]) +
+ ((uint) gx_color_value_to_byte(cv[1]) << 8) +
+ ((ulong) gx_color_value_to_byte(cv[0]) << 16);
+ else {
+ COLROUND_VARS;
+ /* The following needs special handling to avoid bpc=5 when depth=16 */
+ int bpc = dev->color_info.depth == 16 ? 4 : dev->color_info.depth / 3;
+ COLROUND_SETUP(bpc);
+
+ return (((COLROUND_ROUND(cv[0]) << bpc) +
+ COLROUND_ROUND(cv[1])) << bpc) +
+ COLROUND_ROUND(cv[2]);
+ }
+}
+
+/* Map color to RGB. This has 3 separate cases, but since it is rarely */
+/* used, we do a case test rather than providing 3 separate routines. */
+static int
+bit_map_color_rgb(gx_device * dev, gx_color_index color, gx_color_value cv[4])
+{
+ int depth = dev->color_info.depth;
+ int ncomp = REAL_NUM_COMPONENTS(dev);
+ int bpc = depth / ncomp;
+ uint mask = (1 << bpc) - 1;
+
+#define cvalue(c) ((gx_color_value)((ulong)(c) * gx_max_color_value / mask))
+
+ switch (ncomp) {
+ case 1: /* gray */
+ cv[0] = cv[1] = cv[2] =
+ (depth == 1 ? (color ? 0 : gx_max_color_value) :
+ cvalue(color));
+ break;
+ case 3: /* RGB */
+ {
+ gx_color_index cshift = color;
+
+ cv[2] = cvalue(cshift & mask);
+ cshift >>= bpc;
+ cv[1] = cvalue(cshift & mask);
+ cv[0] = cvalue(cshift >> bpc);
+ }
+ break;
+ case 4: /* CMYK */
+ /* Map CMYK back to RGB. */
+ {
+ gx_color_index cshift = color;
+ uint c, m, y, k;
+
+ k = cshift & mask;
+ cshift >>= bpc;
+ y = cshift & mask;
+ cshift >>= bpc;
+ m = cshift & mask;
+ c = cshift >> bpc;
+ /* We use our improved conversion rule.... */
+ cv[0] = cvalue((mask - c) * (mask - k) / mask);
+ cv[1] = cvalue((mask - m) * (mask - k) / mask);
+ cv[2] = cvalue((mask - y) * (mask - k) / mask);
+ }
+ break;
+ }
+ return 0;
+#undef cvalue
+}
+
+/* Map CMYK to color. */
+static gx_color_index
+bit_map_cmyk_color(gx_device * dev, const gx_color_value cv[])
+{
+ int bpc = dev->color_info.depth / 4;
+ int drop = sizeof(gx_color_value) * 8 - bpc;
+ gx_color_index color =
+ (((((((gx_color_index) cv[0] >> drop) << bpc) +
+ (cv[1] >> drop)) << bpc) +
+ (cv[2] >> drop)) << bpc) +
+ (cv[3] >> drop);
+
+ return (color == gx_no_color_index ? color ^ 1 : color);
+}
+
+static int
+bittag_put_params(gx_device * pdev, gs_param_list * plist)
+{
+ pdev->graphics_type_tag |= GS_DEVICE_ENCODES_TAGS; /* the bittags devices use tags in the color */
+ return gdev_prn_put_params(pdev, plist);
+}
+/* Get parameters. We provide a default CRD. */
+static int
+bit_get_params(gx_device * pdev, gs_param_list * plist)
+{
+ int code, ecode;
+ /*
+ * The following is a hack to get the original num_components.
+ * See comment above.
+ */
+ int real_ncomps = REAL_NUM_COMPONENTS(pdev);
+ int ncomps = pdev->color_info.num_components;
+ int forcemono = (ncomps == real_ncomps ? 0 : 1);
+
+ /*
+ * Temporarily set num_components back to the "real" value to avoid
+ * confusing those that rely on it.
+ */
+ pdev->color_info.num_components = real_ncomps;
+
+ ecode = gdev_prn_get_params(pdev, plist);
+ code = sample_device_crd_get_params(pdev, plist, "CRDDefault");
+ if (code < 0)
+ ecode = code;
+ if ((code = param_write_int(plist, "ForceMono", &forcemono)) < 0) {
+ ecode = code;
+ }
+ if ((code = param_write_int(plist, "FirstLine", &((gx_device_bit *)pdev)->FirstLine)) < 0) {
+ ecode = code;
+ }
+ if ((code = param_write_int(plist, "LastLine", &((gx_device_bit *)pdev)->LastLine)) < 0) {
+ ecode = code;
+ }
+
+ /* Restore the working num_components */
+ pdev->color_info.num_components = ncomps;
+
+ return ecode;
+}
+
+/* Set parameters. We allow setting the number of bits per component. */
+/* Also, ForceMono=1 forces monochrome output from RGB/CMYK devices. */
+static int
+bit_put_params(gx_device * pdev, gs_param_list * plist)
+{
+ gx_device_color_info save_info;
+ int ncomps = pdev->color_info.num_components;
+ int real_ncomps = REAL_NUM_COMPONENTS(pdev);
+ int v;
+ int ecode = 0;
+ int code;
+ /* map to depths that we actually have memory devices to support */
+ static const byte depths[4 /* ncomps - 1 */][16 /* bpc - 1 */] = {
+ {1, 2, 0, 4, 8, 0, 0, 8, 0, 0, 0, 16, 0, 0, 0, 16}, /* ncomps = 1 */
+ {0}, /* ncomps = 2, not supported */
+ {4, 8, 0, 16, 16, 0, 0, 24, 0, 0, 0, 40, 0, 0, 0, 48}, /* ncomps = 3 (rgb) */
+ {4, 8, 0, 16, 32, 0, 0, 32, 0, 0, 0, 48, 0, 0, 0, 64} /* ncomps = 4 (cmyk) */
+ };
+ /* map back from depth to the actual bits per component */
+ static int real_bpc[17] = { 0, 1, 2, 2, 4, 4, 4, 4, 8, 8, 8, 8, 12, 12, 12, 12, 16 };
+ int bpc = real_bpc[pdev->color_info.depth / real_ncomps];
+ const char *vname;
+ int FirstLine = ((gx_device_bit *)pdev)->FirstLine;
+ int LastLine = ((gx_device_bit *)pdev)->LastLine;
+
+ /*
+ * Temporarily set num_components back to the "real" value to avoid
+ * confusing those that rely on it.
+ */
+ pdev->color_info.num_components = real_ncomps;
+
+ if ((code = param_read_int(plist, (vname = "GrayValues"), &v)) != 1 ||
+ (code = param_read_int(plist, (vname = "RedValues"), &v)) != 1 ||
+ (code = param_read_int(plist, (vname = "GreenValues"), &v)) != 1 ||
+ (code = param_read_int(plist, (vname = "BlueValues"), &v)) != 1
+ ) {
+ if (code < 0)
+ ecode = code;
+ else
+ switch (v) {
+ case 2: bpc = 1; break;
+ case 4: bpc = 2; break;
+ case 16: bpc = 4; break;
+ case 256: bpc = 8; break;
+ case 4096: bpc = 12; break;
+ case 65536: bpc = 16; break;
+ default:
+ param_signal_error(plist, vname,
+ ecode = gs_error_rangecheck);
+ }
+ }
+
+ switch (code = param_read_int(plist, (vname = "ForceMono"), &v)) {
+ case 0:
+ if (v == 1) {
+ ncomps = 1;
+ break;
+ }
+ else if (v == 0) {
+ ncomps = real_ncomps;
+ break;
+ }
+ code = gs_error_rangecheck;
+ default:
+ ecode = code;
+ param_signal_error(plist, vname, ecode);
+ case 1:
+ break;
+ }
+ if (ecode < 0)
+ return ecode;
+ switch (code = param_read_int(plist, (vname = "FirstLine"), &v)) {
+ case 0:
+ FirstLine = v;
+ break;
+ default:
+ ecode = code;
+ param_signal_error(plist, vname, ecode);
+ case 1:
+ break;
+ }
+ if (ecode < 0)
+ return ecode;
+
+ switch (code = param_read_int(plist, (vname = "LastLine"), &v)) {
+ case 0:
+ LastLine = v;
+ break;
+ default:
+ ecode = code;
+ param_signal_error(plist, vname, ecode);
+ case 1:
+ break;
+ }
+ if (ecode < 0)
+ return ecode;
+
+ /*
+ * Save the color_info in case gdev_prn_put_params fails, and for
+ * comparison. Note that depth is computed from real_ncomps.
+ */
+ save_info = pdev->color_info;
+ pdev->color_info.depth = depths[real_ncomps - 1][bpc - 1];
+ pdev->color_info.max_gray = pdev->color_info.max_color =
+ (pdev->color_info.dither_grays =
+ pdev->color_info.dither_colors =
+ (1 << bpc)) - 1;
+ ecode = gdev_prn_put_params(pdev, plist);
+ if (ecode < 0) {
+ pdev->color_info = save_info;
+ return ecode;
+ }
+ /* Now restore/change num_components. This is done after other */
+ /* processing since it is used in gx_default_put_params */
+ pdev->color_info.num_components = ncomps;
+ if (pdev->color_info.depth != save_info.depth ||
+ pdev->color_info.num_components != save_info.num_components
+ ) {
+ gs_closedevice(pdev);
+ }
+ /* Reset the map_cmyk_color procedure if appropriate. */
+ if (dev_proc(pdev, map_cmyk_color) == cmyk_1bit_map_cmyk_color ||
+ dev_proc(pdev, map_cmyk_color) == cmyk_8bit_map_cmyk_color ||
+ dev_proc(pdev, map_cmyk_color) == bit_map_cmyk_color) {
+ set_dev_proc(pdev, map_cmyk_color,
+ pdev->color_info.depth == 4 ? cmyk_1bit_map_cmyk_color :
+ pdev->color_info.depth == 32 ? cmyk_8bit_map_cmyk_color :
+ bit_map_cmyk_color);
+ }
+ /* Reset the separable and linear shift, masks, bits. */
+ set_linear_color_bits_mask_shift(pdev);
+ pdev->color_info.separable_and_linear = GX_CINFO_SEP_LIN;
+ ((gx_device_bit *)pdev)->FirstLine = FirstLine;
+ ((gx_device_bit *)pdev)->LastLine = LastLine;
+
+ return 0;
+}
+
+/* Send the page to the printer. */
+static int
+bit_print_page(gx_device_printer * pdev, FILE * prn_stream)
+{ /* Just dump the bits on the file. */
+ /* If the file is 'nul', don't even do the writes. */
+ int line_size = gdev_mem_bytes_per_scan_line((gx_device *) pdev);
+ byte *in = gs_alloc_bytes(pdev->memory, line_size, "bit_print_page(in)");
+ byte *data;
+ int nul = !strcmp(pdev->fname, "nul") || !strcmp(pdev->fname, "/dev/null");
+ int lnum = ((gx_device_bit *)pdev)->FirstLine >= pdev->height ? pdev->height - 1 :
+ ((gx_device_bit *)pdev)->FirstLine;
+ int bottom = ((gx_device_bit *)pdev)->LastLine >= pdev->height ? pdev->height - 1 :
+ ((gx_device_bit *)pdev)->LastLine;
+ int line_count = any_abs(bottom - lnum);
+ int i, step = lnum > bottom ? -1 : 1;
+
+ if (in == 0)
+ return_error(gs_error_VMerror);
+ if ((lnum == 0) && (bottom == 0))
+ line_count = pdev->height - 1; /* default when LastLine == 0, FirstLine == 0 */
+ for (i = 0; i <= line_count; i++, lnum += step) {
+ gdev_prn_get_bits(pdev, lnum, in, &data);
+ if (!nul)
+ fwrite(data, 1, line_size, prn_stream);
+ }
+ gs_free_object(pdev->memory, in, "bit_print_page(in)");
+ return 0;
+}
+
+/* For tags device go ahead and add in the size so that we can strip and create
+ proper ppm outputs for various dimensions and not be restricted to 72dpi when
+ using the tag viewer */
+static int
+bittags_print_page(gx_device_printer * pdev, FILE * prn_stream)
+{ /* Just dump the bits on the file. */
+ /* If the file is 'nul', don't even do the writes. */
+ int line_size = gdev_mem_bytes_per_scan_line((gx_device *) pdev);
+ byte *in = gs_alloc_bytes(pdev->memory, line_size, "bit_print_page(in)");
+ byte *data;
+ int nul = !strcmp(pdev->fname, "nul") || !strcmp(pdev->fname, "/dev/null");
+ int lnum = ((gx_device_bit *)pdev)->FirstLine >= pdev->height ? pdev->height - 1 :
+ ((gx_device_bit *)pdev)->FirstLine;
+ int bottom = ((gx_device_bit *)pdev)->LastLine >= pdev->height ? pdev->height - 1 :
+ ((gx_device_bit *)pdev)->LastLine;
+ int line_count = any_abs(bottom - lnum);
+ int i, step = lnum > bottom ? -1 : 1;
+
+ if (in == 0)
+ return_error(gs_error_VMerror);
+
+ fprintf(prn_stream, "P6\n%d %d\n255\n", pdev->width, pdev->height);
+ if ((lnum == 0) && (bottom == 0))
+ line_count = pdev->height - 1; /* default when LastLine == 0, FirstLine == 0 */
+ for (i = 0; i <= line_count; i++, lnum += step) {
+ gdev_prn_get_bits(pdev, lnum, in, &data);
+ if (!nul)
+ fwrite(data, 1, line_size, prn_stream);
+ }
+ gs_free_object(pdev->memory, in, "bit_print_page(in)");
+ return 0;
+}
+
+static int
+bit_put_image(gx_device *pdev, const byte *buffer, int num_chan, int xstart,
+ int ystart, int width, int height, int row_stride,
+ int plane_stride, int alpha_plane_index, int tag_plane_index)
+{
+ gx_device_memory *pmemdev = (gx_device_memory *)pdev;
+ byte *buffer_prn;
+ int yend = ystart + height;
+ int xend = xstart + width;
+ int x, y, k;
+ int src_position, des_position;
+
+ if (alpha_plane_index != 0)
+ return 0; /* we don't want alpha, return 0 to ask for the */
+ /* pdf14 device to do the alpha composition */
+ /* Eventually, the pdf14 device might be chunky pixels, punt for now */
+ if (plane_stride == 0)
+ return 0;
+ if (num_chan != 3 || tag_plane_index <= 0)
+ return_error(gs_error_unknownerror); /* can't handle these cases */
+ /* Drill down to get the appropriate memory buffer pointer */
+ buffer_prn = pmemdev->base;
+ /* Now go ahead and fill */
+ for ( y = ystart; y < yend; y++ ) {
+ src_position = (y - ystart) * row_stride;
+ des_position = y * pmemdev->raster + xstart * 4;
+ for ( x = xstart; x < xend; x++ ) {
+ /* Tag data first, then RGB */
+ buffer_prn[des_position] =
+ buffer[src_position + tag_plane_index * plane_stride];
+ des_position += 1;
+ for ( k = 0; k < 3; k++) {
+ buffer_prn[des_position] =
+ buffer[src_position + k * plane_stride];
+ des_position += 1;
+ }
+ src_position += 1;
+ }
+ }
+ return height; /* we used all of the data */
+}
diff --git a/devices/gdevbj10.c b/devices/gdevbj10.c
new file mode 100644
index 000000000..7ea73e752
--- /dev/null
+++ b/devices/gdevbj10.c
@@ -0,0 +1,449 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+/* Canon Bubble Jet BJ-10e, BJ200, and BJ300 printer driver */
+#include "gdevprn.h"
+
+/*
+ * The following is taken from the BJ200 Programmer's manual. The top
+ * margin is 3mm (0.12"), and the bottom margin is 6.4mm (0.25"). The
+ * left and right margin depend on the type of paper -- US letter or
+ * A4 -- but ultimately rest on a print width of 203.2mm (8"). For letter
+ * paper, the left margin (and hence the right) is 6.4mm (0.25"), while
+ * for A4 paper, both are 3.4mm (0.13").
+ *
+ * The bottom margin requires a bit of care. The image is printed
+ * as strips, each about 3.4mm wide. We can only attain the bottom
+ * margin if the final strip coincides with it. Note that each strip
+ * is generated using only 48 of the available 64 jets, and the absence
+ * of those bottom 16 jets makes our bottom margin, in effect, about
+ * 1.1mm (0.04") larger.
+ *
+ * The bj200 behaves, in effect, as though the origin were at the first
+ * printable position, rather than the top left corner of the page, so
+ * we add a translation to the initial matrix to compensate for this.
+ *
+ * Except for the details of getting the margins correct, the bj200 is
+ * no different from the bj10e, and uses the same routine to print each
+ * page.
+ *
+ * NOTE: The bj200 has a DIP switch called "Text scale mode" and if
+ * set, it allows the printer to get 66 lines on a letter-sized page
+ * by reducing the line spacing by a factor of 14/15. If this DIP
+ * switch is set, the page image printed by ghostscript will also be
+ * similarly squeezed. Thus text scale mode is something ghostscript
+ * would like to disable.
+ *
+ * According to the bj200 manual, which describes the bj10 commands,
+ * the printer can be reset either to the settings determined by the
+ * DIP switches, or to the factory defaults, and then some of those
+ * settings can be specifically overriden. Unfortunately, the text
+ * scale mode and horizontal print position (for letter vs A4 paper)
+ * can not be overriden. On my bj200, the factory settings are for
+ * no text scaling and letter paper, thus using the factory defaults
+ * also implies letter paper. I don't know if this is necessarily
+ * true for bj200's sold elsewhere, or for other printers that use
+ * the same command set.
+ *
+ * If your factory defaults are in fact the same, you can compile
+ * the driver with USE_FACTORY_DEFAULTS defined, in which case the
+ * printer will be reset to the factory defaults for letter paper,
+ * and reset to the DIP switch settings for A4 paper. In this case,
+ * with letter-sized paper, the text scale mode will be disabled.
+ * Further, by leaving the horizontal print position DIP switch set
+ * for A4 paper, gs will be able to print on either A4 or letter
+ * paper without changing the DIP switch. Since it's not clear that
+ * the factory defaults are universal, the default behaviour is not
+ * to define USE_FACTORY_DEFAULTS, and the printer will always be
+ * reset to the DIP switch defaults.
+ */
+
+/*
+ * According to md@duesti.fido.de (Matthias Duesterhoeft):
+
+It is possible to use the printer Canon BJ-300 (and 330) with Ghostscript if
+you use the driver for the Canon BJ-200. The Printer has to be set to
+Proprinter Mode. Although it is possible to set the print quality with a DIP
+switch, you should add the following to the already existing init-string:
+1B 5B 64 01 00 80 (all numbers in hex)
+This sets the print quality to letter quality.
+
+The minimum margins are the following:
+
+Portrait:
+B5/A4: min. left and right margin: 3.4 mm (0.13")
+Letter: min. left and right margin: 6.4 mm (0.25")
+
+Landscape:
+B4: min. left and right margin: 9.3 mm (0.37")
+A3: min. left and right margin: 37.3 mm (1.47")
+
+The recommended top margin is 12.7 mm (0.5"), although the printer is capable
+to start at 0 mm. The recommended bottom margin is 25.4 mm (1"), but 12.7 mm
+(0.5") are possible, too. If you ask me, don't use the recommended top and
+bottom margins, use 0" and 0.5".
+
+ */
+
+#define BJ200_TOP_MARGIN 0.12
+#define BJ200_BOTTOM_MARGIN 0.29
+#define BJ200_LETTER_SIDE_MARGIN 0.25
+#define BJ200_A4_SIDE_MARGIN 0.13
+
+static dev_proc_open_device(bj200_open);
+
+static dev_proc_print_page(bj10e_print_page);
+
+static gx_device_procs prn_bj200_procs =
+/* Since the print_page doesn't alter the device, this device can print in the background */
+ prn_procs(bj200_open, gdev_prn_bg_output_page, gdev_prn_close);
+
+const gx_device_printer far_data gs_bj200_device =
+ prn_device(prn_bj200_procs, "bj200",
+ DEFAULT_WIDTH_10THS,
+ DEFAULT_HEIGHT_10THS,
+ 360, /* x_dpi */
+ 360, /* y_dpi */
+ 0, 0, 0, 0, /* margins filled in by bj200_open */
+ 1, bj10e_print_page);
+
+/*
+ * (<simon@pogner.demon.co.uk>, aka <sjwright@cix.compulink.co.uk>):
+ * My bj10ex, which as far as I can tell is just like a bj10e, needs a
+ * bottom margin of 0.4" (actually, you must not print within 0.5" of
+ * the bottom; somewhere, an extra 0.1" is creeping in).
+ *
+ * (<jim.hague@acm.org>):
+ * I have a BJ10sx and the BJ10sx manual. This states that the top and
+ * bottom margins for the BJ10sx are 0.33" and 0.5". The latter may
+ * explain Simon's finding. The manual also instructs Win31 users to
+ * select 'BJ10e' as their driver, so presumably the margins will be
+ * identical and thus also correct for BJ10e. The values for the side
+ * margins given are identical to those above.
+ *
+ * As of 2nd Nov 2001 the BJ10 sx manual is at
+ * http://www.precision.com/Printer%20Manuals/Canon%20BJ-10sx%20Manual.pdf.
+ */
+
+#define BJ10E_TOP_MARGIN 0.33
+#define BJ10E_BOTTOM_MARGIN (0.50 + 0.04)
+
+static dev_proc_open_device(bj10e_open);
+
+static gx_device_procs prn_bj10e_procs =
+ prn_procs(bj10e_open, gdev_prn_output_page, gdev_prn_close);
+
+const gx_device_printer far_data gs_bj10e_device =
+ prn_device(prn_bj10e_procs, "bj10e",
+ DEFAULT_WIDTH_10THS,
+ DEFAULT_HEIGHT_10THS,
+ 360, /* x_dpi */
+ 360, /* y_dpi */
+ 0,0,0,0, /* margins */
+ 1, bj10e_print_page);
+
+/*
+ * Notes on the BJ10e/BJ200 command set.
+ *
+
+According to the BJ200 manual, the "set initial condition" sequence (ESC [
+K) has 2 bytes which can override the DIP switches -- these are the last 2
+bytes. Several bits are listed as "reserved" -- one or more may possibly
+affect the sheet feeder. The first is referred to as <P1>, with the
+following meaning:
+ 1 0
+bit 7 ignore/process P1 ignore process
+bit 6 reserved
+bit 5 alarm disabled enabled
+bit 4 automatic CR CR+LF CR
+bit 3 automatic LF CR+LF LF
+bit 2 page length 12 inches 11 inches
+bit 1 style for zero slashed not slashed
+bit 0 character set set 2 set 1
+
+The last byte is <P2>, with the following meaning:
+ 1 0
+bit 7 ignore/process P2 ignore process
+bit 6 code page 850 437
+bit 5 reserved
+bit 4 reserved
+bit 3 reserved
+bit 2 reserved
+bit 1 reserved
+bit 0 reserved
+
+The automatic CR setting is important to gs, but the rest shouldn't matter
+(gs doesn't print characters or send LF, and it explicitly sets the page
+length). The sequence ESC 5 <n> controls automatic CR -- if <n> is 0x00,
+it is turned off (CR only) and if <n> is 0x01, it is turned on (CR + LF).
+So we do following: Change the initialization string to so that the last 2
+of the 9 bytes are \200 rather than \000. Then add
+ |* Turn off automatic carriage return, otherwise we get line feeds. *|
+ fwrite("\0335\000", 1, 3, prn_stream);
+after the initialization. (Actually, instead of setting the last 2 bytes
+to \200, we suppress them altogether by changing the byte count from \004
+to \002 (the byte count is the 4th (low 8 bits) and 5th (high 8 bits) bytes
+in the initialization sequence).)
+
+*/
+
+/* ------ Internal routines ------ */
+
+/* Open the printer, and set the margins. */
+static int
+bj200_open(gx_device *pdev)
+{
+ /* Change the margins according to the paper size.
+ The top and bottom margins don't seem to depend on the
+ page length, but on the paper handling mechanism;
+ The side margins do depend on the paper width, as the
+ printer centres the 8" print line on the page. */
+
+ static const float a4_margins[4] =
+ { (float)BJ200_A4_SIDE_MARGIN, (float)BJ200_BOTTOM_MARGIN,
+ (float)BJ200_A4_SIDE_MARGIN, (float)BJ200_TOP_MARGIN
+ };
+ static const float letter_margins[4] =
+ { (float)BJ200_LETTER_SIDE_MARGIN, (float)BJ200_BOTTOM_MARGIN,
+ (float)BJ200_LETTER_SIDE_MARGIN, (float)BJ200_TOP_MARGIN
+ };
+
+ gx_device_set_margins(pdev,
+ (pdev->width / pdev->x_pixels_per_inch <= 8.4 ?
+ a4_margins : letter_margins),
+ true);
+ return gdev_prn_open(pdev);
+}
+
+static int
+bj10e_open(gx_device *pdev)
+{
+ /* See bj200_open() */
+ static const float a4_margins[4] =
+ { (float)BJ200_A4_SIDE_MARGIN, (float)BJ10E_BOTTOM_MARGIN,
+ (float)BJ200_A4_SIDE_MARGIN, (float)BJ10E_TOP_MARGIN
+ };
+ static const float letter_margins[4] =
+ { (float)BJ200_LETTER_SIDE_MARGIN, (float)BJ10E_BOTTOM_MARGIN,
+ (float)BJ200_LETTER_SIDE_MARGIN, (float)BJ10E_TOP_MARGIN
+ };
+
+ gx_device_set_margins(pdev,
+ (pdev->width / pdev->x_pixels_per_inch <= 8.4 ?
+ a4_margins : letter_margins),
+ true);
+ return gdev_prn_open(pdev);
+}
+
+/* Send the page to the printer. */
+static int
+bj10e_print_page(gx_device_printer *pdev, FILE *prn_stream)
+{ int line_size = gx_device_raster((gx_device *)pdev, 0);
+ int xres = (int)pdev->x_pixels_per_inch;
+ int yres = (int)pdev->y_pixels_per_inch;
+ int mode = (yres == 180 ?
+ (xres == 180 ? 11 : 12) :
+ (xres == 180 ? 14 : 16));
+ int bytes_per_column = (yres == 180) ? 3 : 6;
+ int bits_per_column = bytes_per_column * 8;
+ int skip_unit = bytes_per_column * 3;
+ byte *in = (byte *)gs_malloc(pdev->memory, 8, line_size, "bj10e_print_page(in)");
+ byte *out = (byte *)gs_malloc(pdev->memory, bits_per_column, line_size, "bj10e_print_page(out)");
+ int lnum = 0;
+ int skip = 0;
+ int code = 0;
+ int last_row = dev_print_scan_lines(pdev);
+ int limit = last_row - bits_per_column;
+
+ if ( in == 0 || out == 0 )
+ { code = gs_note_error(gs_error_VMerror);
+ goto fin;
+ }
+
+ /* Initialize the printer. */
+#ifdef USE_FACTORY_DEFAULTS
+ /* Check for U.S. letter vs. A4 paper. */
+ fwrite(( pdev->width / pdev->x_pixels_per_inch <= 8.4 ?
+ "\033[K\002\000\000\044" /*A4--DIP switch defaults*/ :
+ "\033[K\002\000\004\044" /*letter--factory defaults*/ ),
+ 1, 7, prn_stream);
+#else
+ fwrite("\033[K\002\000\000\044", 1, 7, prn_stream);
+#endif
+
+ /* Turn off automatic carriage return, otherwise we get line feeds. */
+ fwrite("\0335\000", 1, 3, prn_stream);
+
+ /* Set vertical spacing. */
+ fwrite("\033[\\\004\000\000\000", 1, 7, prn_stream);
+ fputc(yres & 0xff, prn_stream);
+ fputc(yres >> 8, prn_stream);
+
+ /* Set the page length. This is the printable length, in inches. */
+ fwrite("\033C\000", 1, 3, prn_stream);
+ fputc((last_row + yres - 1)/yres, prn_stream);
+
+ /* Transfer pixels to printer. The last row we can print is defined
+ by "last_row". Only the bottom of the print head can print at the
+ bottom margin, and so we align the final printing pass. The print
+ head is kept from moving below "limit", which is exactly one pass
+ above the bottom margin. Once it reaches this limit, we make our
+ final printing pass of a full "bits_per_column" rows. */
+ while ( lnum < last_row )
+ {
+ byte *in_data;
+ byte *in_end = in + line_size;
+ byte *out_beg = out;
+ byte *out_end = out + bytes_per_column * pdev->width;
+ byte *outl = out;
+ int bnum;
+
+ /* Copy 1 scan line and test for all zero. */
+ code = gdev_prn_get_bits(pdev, lnum, in, &in_data);
+ if ( code < 0 ) goto xit;
+ /* The mem... or str... functions should be faster than */
+ /* the following code, but all systems seem to implement */
+ /* them so badly that this code is faster. */
+ { register const long *zip = (const long *)in_data;
+ register int zcnt = line_size;
+ register const byte *zipb;
+ for ( ; zcnt >= 4 * sizeof(long); zip += 4, zcnt -= 4 * sizeof(long) )
+ { if ( zip[0] | zip[1] | zip[2] | zip[3] )
+ goto notz;
+ }
+ zipb = (const byte *)zip;
+ while ( --zcnt >= 0 )
+ {
+ if ( *zipb++ )
+ goto notz;
+ }
+ /* Line is all zero, skip */
+ lnum++;
+ skip++;
+ continue;
+notz: ;
+ }
+
+ /* Vertical tab to the appropriate position. Note here that
+ we make sure we don't move below limit. */
+ if ( lnum > limit )
+ { skip -= (lnum - limit);
+ lnum = limit;
+ }
+ while ( skip > 255 )
+ { fputs("\033J\377", prn_stream);
+ skip -= 255;
+ }
+ if ( skip )
+ fprintf(prn_stream, "\033J%c", skip);
+
+ /* If we've printed as far as "limit", then reset "limit"
+ to "last_row" for the final printing pass. */
+ if ( lnum == limit )
+ limit = last_row;
+ skip = 0;
+
+ /* Transpose in blocks of 8 scan lines. */
+ for ( bnum = 0; bnum < bits_per_column; bnum += 8 )
+ { int lcnt = min(8, limit - lnum);
+ byte *inp = in;
+ byte *outp = outl;
+ lcnt = gdev_prn_copy_scan_lines(pdev,
+ lnum, in, lcnt * line_size);
+ if ( lcnt < 0 )
+ { code = lcnt;
+ goto xit;
+ }
+ if ( lcnt < 8 )
+ memset(in + lcnt * line_size, 0,
+ (8 - lcnt) * line_size);
+ for ( ; inp < in_end; inp++, outp += bits_per_column )
+ { gdev_prn_transpose_8x8(inp, line_size,
+ outp, bytes_per_column);
+ }
+ outl++;
+ lnum += lcnt;
+ skip += lcnt;
+ }
+
+ /* Send the bits to the printer. We alternate horizontal
+ skips with the data. The horizontal skips are in units
+ of 1/120 inches, so we look at the data in groups of
+ 3 columns, since 3/360 = 1/120, and 3/180 = 2/120. */
+ outl = out;
+ do
+ { int count;
+ int n;
+ byte *out_ptr;
+
+ /* First look for blank groups of columns. */
+ while(outl < out_end)
+ { n = count = min(out_end - outl, skip_unit);
+ out_ptr = outl;
+ while ( --count >= 0 )
+ { if ( *out_ptr++ )
+ break;
+ }
+ if ( count >= 0 )
+ break;
+ else
+ outl = out_ptr;
+ }
+ if (outl >= out_end)
+ break;
+ if (outl > out_beg)
+ { count = (outl - out_beg) / skip_unit;
+ if ( xres == 180 ) count <<= 1;
+ fprintf(prn_stream, "\033d%c%c",
+ count & 0xff, count >> 8);
+ }
+
+ /* Next look for non-blank groups of columns. */
+ out_beg = outl;
+ outl += n;
+ while(outl < out_end)
+ { n = count = min(out_end - outl, skip_unit);
+ out_ptr = outl;
+ while ( --count >= 0 )
+ { if ( *out_ptr++ )
+ break;
+ }
+ if ( count < 0 )
+ break;
+ else
+ outl += n;
+ }
+ count = outl - out_beg + 1;
+ fprintf(prn_stream, "\033[g%c%c%c",
+ count & 0xff, count >> 8, mode);
+ fwrite(out_beg, 1, count - 1, prn_stream);
+ out_beg = outl;
+ outl += n;
+ }
+ while ( out_beg < out_end );
+
+ fputc('\r', prn_stream);
+ }
+
+ /* Eject the page */
+xit: fputc(014, prn_stream); /* form feed */
+ fflush(prn_stream);
+fin: if ( out != 0 )
+ gs_free(pdev->memory, (char *)out, bits_per_column, line_size,
+ "bj10e_print_page(out)");
+ if ( in != 0 )
+ gs_free(pdev->memory, (char *)in, 8, line_size, "bj10e_print_page(in)");
+ return code;
+}
diff --git a/devices/gdevbjc.h b/devices/gdevbjc.h
new file mode 100644
index 000000000..d5cfea2fb
--- /dev/null
+++ b/devices/gdevbjc.h
@@ -0,0 +1,287 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+#ifndef gdevbjc_INCLUDED
+# define gdevbjc_INCLUDED
+
+/*
+ * Definitions for Canon BJC printers and the associated drivers.
+ *
+ * Copyright (C) Yves Arrouye <yves.arrouye@usa.net>, 1995, 1996.
+ *
+ */
+
+/*
+ * Please do read the definitions here and change the defaults if needed.
+ *
+ * Values that can be changed are all called BJC_DEFAULT_* for generic
+ * values and BJC600_DEFAULT_* or BJC800_DEFAULT_* for specific values.
+ *
+ */
+
+#ifndef _GDEV_BJC_H
+#define _GDEV_CDJ_H
+
+/*
+ * Drivers names. I don't expect you to change them!
+ *
+ */
+
+#define BJC_BJC600 "bjc600"
+#define BJC_BJC800 "bjc800"
+
+#define BJC_BJC600_VERSION 2.1700
+#define BJC_BJC600_VERSIONSTR "2.17.00 5/23/96 Yves Arrouye"
+
+#define BJC_BJC800_VERSION 2.1700
+#define BJC_BJC800_VERSIONSTR "2.17.00 5/23/96 Yves Arrouye"
+
+/*
+ * Hardware limits. May be adjusted eventually.
+ *
+ */
+
+#define BJC_PRINT_LIMIT (3. / 25.4) /* In inches. */
+#define BJC_A3_PRINT_LIMIT (8. / 25.4) /* In inches. */
+
+#define BJC_HARD_LOWER_LIMIT (7. / 25.4) /* In inches. */
+#define BJC_USED_LOWER_LIMIT (9.54 / 25.4) /* In inches. */
+#define BJC_RECD_LOWER_LIMIT (12.7 / 25.4) /* In inches. */
+
+#ifdef USE_RECOMMENDED_MARGINS
+#define BJC_LOWER_LIMIT BJC_RECD_LOWER_LIMIT
+#undef BJC_DEFAULT_CENTEREDAREA
+#define BJC_DEFAULT_CENTEREDAREA
+#else
+#ifdef USE_TIGHT_MARGINS
+#define BJC_LOWER_LIMIT BJC_HARD_LOWER_LIMIT /* In inches. */
+#else
+#define BJC_LOWER_LIMIT BJC_USED_LOWER_LIMIT /* In inches. */
+#endif
+#endif
+
+#ifndef BJC600_MEDIAWEIGHT_THICKLIMIT
+#define BJC600_MEDIAWEIGHT_THICKLIMIT 105 /* In g/m2. */
+#endif
+#ifndef BJC800_MEDIAWEIGHT_THICKLIMIT
+#define BJC800_MEDIAWEIGHT_THICKLIMIT BJC600_MEDIAWEIGHT_THICKLIMIT
+#endif
+
+#define BJC_HEAD_ROWS 64 /* Number of heads. Do not change! */
+
+/*
+ * Margins resulting from the limits specified above.
+ *
+ * The margins are Left, Bottom, Right, Top and are expressed in inches.
+ * You should not change them, better change the limits above.
+ *
+ */
+
+#define BJC_MARGINS_LETTER \
+ (float)(6.5 / 25.4), (float)BJC_LOWER_LIMIT, \
+ (float)(6.5 / 25.4), (float)BJC_PRINT_LIMIT
+#define BJC_MARGINS_A4 \
+ (float)(3.4 / 25.4), (float)BJC_LOWER_LIMIT, \
+ (float)(3.4 / 25.4), (float)BJC_PRINT_LIMIT
+#define BJC_MARGINS_A3 \
+ (float)(4.0 / 25.4), (float)BJC_LOWER_LIMIT, \
+ (float)(4.0 / 25.4), (float)BJC_A3_PRINT_LIMIT
+
+/*
+ * Drivers options names.
+ *
+ */
+
+#define BJC_DEVINFO_VERSION "Version"
+#define BJC_DEVINFO_VERSIONSTRING "VersionString"
+
+#define BJC_DEVINFO_OUTPUTFACEUP "OutputFaceUp"
+
+#define BJC_OPTION_MANUALFEED "ManualFeed"
+#define BJC_OPTION_DITHERINGTYPE "DitheringType"
+#define BJC_OPTION_MEDIATYPE "MediaType"
+#define BJC_OPTION_MEDIAWEIGHT "MediaWeight"
+#define BJC_OPTION_PRINTQUALITY "PrintQuality"
+#define BJC_OPTION_COLORCOMPONENTS "ColorComponents"
+#define BJC_OPTION_PRINTCOLORS "PrintColors"
+#define BJC_OPTION_MONOCHROMEPRINT "MonochromePrint"
+
+/*
+ * Definitions of parameters (options) values.
+ *
+ */
+
+#define BJC_MEDIA_PLAINPAPER 0
+#define BJC_MEDIA_COATEDPAPER 1
+#define BJC_MEDIA_TRANSPARENCYFILM 2
+#define BJC_MEDIA_BACKPRINTFILM 3 /* Unused */
+#define BJC_MEDIA_ENVELOPE 8
+#define BJC_MEDIA_CARD 9
+#define BJC_MEDIA_OTHER 15
+
+#define BJC_DITHER_NONE 0
+#define BJC_DITHER_FS 1
+
+#define BJC_QUALITY_NORMAL 0
+#define BJC_QUALITY_HIGH 1
+#define BJC_QUALITY_DRAFT 2
+#define BJC_QUALITY_LOW 3
+
+#define BJC_COLOR_ALLBLACK 0
+#define BJC_COLOR_CYAN 1
+#define BJC_COLOR_MAGENTA 2
+#define BJC_COLOR_YELLOW 4
+#define BJC_COLOR_BLACK 8
+
+#define BJC_COLOR_CMY (BJC_COLOR_CYAN | BJC_COLOR_MAGENTA | BJC_COLOR_YELLOW)
+#define BJC_COLOR_CMYK (BJC_COLOR_CMY | BJC_COLOR_BLACK)
+
+/* Some compilers complain if this is a floating point value.... */
+#define BJC_RESOLUTION_BASE 90
+
+#define BJC_RESOLUTION_LOW (1 * BJC_RESOLUTION_BASE)
+#define BJC_RESOLUTION_MEDIUM (2 * BJC_RESOLUTION_BASE)
+#define BJC_RESOLUTION_NORMAL (4 * BJC_RESOLUTION_BASE)
+
+/*
+ * Default values for parameters (long).
+ *
+ * Generic values are first given, and driver-specific values are by default
+ * those generic values.
+ *
+ */
+
+#ifndef BJC_DEFAULT_MEDIATYPE
+#define BJC_DEFAULT_MEDIATYPE BJC_MEDIA_PLAINPAPER
+#endif
+#ifndef BJC_DEFAULT_PRINTQUALITY
+#define BJC_DEFAULT_PRINTQUALITY BJC_QUALITY_NORMAL
+#endif
+
+#ifndef BJC_DEFAULT_DITHERINGTYPE
+#define BJC_DEFAULT_DITHERINGTYPE BJC_DITHER_FS
+#endif
+
+#ifndef BJC_DEFAULT_MANUALFEED
+#define BJC_DEFAULT_MANUALFEED false
+#endif
+#ifndef BJC_DEFAULT_MONOCHROMEPRINT
+#define BJC_DEFAULT_MONOCHROMEPRINT false
+#endif
+
+#ifndef BJC_DEFAULT_RESOLUTION
+#define BJC_DEFAULT_RESOLUTION BJC_RESOLUTION_NORMAL
+#endif
+
+/* If you change the bits per pixel, change the color components. For
+ bpp = 1 color components = 1, bpp = 8 color components = { 1, 4},
+ bpp = { 16, 24, 32 } color components = 4, comps = { 3 }, bpp = { 24 }. */
+
+#ifndef BJC_DEFAULT_BITSPERPIXEL
+#define BJC_DEFAULT_BITSPERPIXEL 24
+#endif
+#ifndef BJC_DEFAULT_COLORCOMPONENTS
+#define BJC_DEFAULT_COLORCOMPONENTS 4
+#endif
+
+/* You should not have to change these defaults */
+
+#ifndef BJC_DEFAULT_PRINTCOLORS
+#define BJC_DEFAULT_PRINTCOLORS BJC_COLOR_CMYK
+#endif
+#ifndef BJC_DEFAULT_MONOCHROMEPRINT
+#define BJC_DEFAULT_MONOCHROMEPRINT false
+#endif
+#ifndef BJC_DEFAULT_SETMEDIAWEIGHT
+#define BJC_DEFAULT_SETMEDIAWEIGHT 0
+#endif
+#ifndef BJC_DEFAULT_MEDIAWEIGHT
+#define BJC_DEFAULT_MEDIAWEIGHT 80
+#endif
+
+/*
+ * Default values for the specific BJC drivers.
+ *
+ */
+
+#ifndef BJC600_DEFAULT_MEDIATYPE
+#define BJC600_DEFAULT_MEDIATYPE BJC_DEFAULT_MEDIATYPE
+#endif
+#ifndef BJC600_DEFAULT_PRINTQUALITY
+#define BJC600_DEFAULT_PRINTQUALITY BJC_DEFAULT_PRINTQUALITY
+#endif
+#ifndef BJC600_DEFAULT_DITHERINGTYPE
+#define BJC600_DEFAULT_DITHERINGTYPE BJC_DEFAULT_DITHERINGTYPE
+#endif
+#ifndef BJC600_DEFAULT_MANUALFEED
+#define BJC600_DEFAULT_MANUALFEED BJC_DEFAULT_MANUALFEED
+#endif
+#ifndef BJC600_DEFAULT_MONOCHROMEPRINT
+#define BJC600_DEFAULT_MONOCHROMEPRINT BJC_DEFAULT_MONOCHROMEPRINT
+#endif
+#ifndef BJC600_DEFAULT_RESOLUTION
+#define BJC600_DEFAULT_RESOLUTION BJC_DEFAULT_RESOLUTION
+#endif
+#ifndef BJC600_DEFAULT_BITSPERPIXEL
+#define BJC600_DEFAULT_BITSPERPIXEL BJC_DEFAULT_BITSPERPIXEL
+#endif
+#ifndef BJC600_DEFAULT_COLORCOMPONENTS
+#define BJC600_DEFAULT_COLORCOMPONENTS BJC_DEFAULT_COLORCOMPONENTS
+#endif
+#ifndef BJC600_DEFAULT_PRINTCOLORS
+#define BJC600_DEFAULT_PRINTCOLORS BJC_DEFAULT_PRINTCOLORS
+#endif
+#ifndef BJC600_DEFAULT_SETMEDIAWEIGHT
+#define BJC600_DEFAULT_SETMEDIAWEIGHT BJC_DEFAULT_SETMEDIAWEIGHT
+#endif
+#ifndef BJC600_DEFAULT_MEDIAWEIGHT
+#define BJC600_DEFAULT_MEDIAWEIGHT BJC_DEFAULT_MEDIAWEIGHT
+#endif
+
+#ifndef BJC800_DEFAULT_MEDIATYPE
+#define BJC800_DEFAULT_MEDIATYPE BJC_DEFAULT_MEDIATYPE
+#endif
+#ifndef BJC800_DEFAULT_PRINTQUALITY
+#define BJC800_DEFAULT_PRINTQUALITY BJC_DEFAULT_PRINTQUALITY
+#endif
+#ifndef BJC800_DEFAULT_DITHERINGTYPE
+#define BJC800_DEFAULT_DITHERINGTYPE BJC_DEFAULT_DITHERINGTYPE
+#endif
+#ifndef BJC800_DEFAULT_MANUALFEED
+#define BJC800_DEFAULT_MANUALFEED BJC_DEFAULT_MANUALFEED
+#endif
+#ifndef BJC800_DEFAULT_RESOLUTION
+#define BJC800_DEFAULT_RESOLUTION BJC_DEFAULT_RESOLUTION
+#endif
+#ifndef BJC800_DEFAULT_BITSPERPIXEL
+#define BJC800_DEFAULT_BITSPERPIXEL BJC_DEFAULT_BITSPERPIXEL
+#endif
+#ifndef BJC800_DEFAULT_COLORCOMPONENTS
+#define BJC800_DEFAULT_COLORCOMPONENTS BJC_DEFAULT_COLORCOMPONENTS
+#endif
+#ifndef BJC800_DEFAULT_PRINTCOLORS
+#define BJC800_DEFAULT_PRINTCOLORS BJC_DEFAULT_PRINTCOLORS
+#endif
+#ifndef BJC800_DEFAULT_SETMEDIAWEIGHT
+#define BJC800_DEFAULT_SETMEDIAWEIGHT BJC_DEFAULT_SETMEDIAWEIGHT
+#endif
+#ifndef BJC800_DEFAULT_MEDIAWEIGHT
+#define BJC800_DEFAULT_MEDIAWEIGHT BJC_DEFAULT_MEDIAWEIGHT
+#endif
+
+#endif /* _GDEVBJC_H */
+
+#endif /* gdevbjc_INCLUDED */
diff --git a/devices/gdevbjcl.c b/devices/gdevbjcl.c
new file mode 100644
index 000000000..86bb8b4c9
--- /dev/null
+++ b/devices/gdevbjcl.c
@@ -0,0 +1,249 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Canon BJC command generation library */
+#include "std.h"
+#include "gdevbjcl.h"
+
+/****** PRELIMINARY, SUBJECT TO CHANGE WITHOUT NOTICE. ******/
+
+/* ---------------- Utilities ---------------- */
+
+static void
+bjc_put_bytes(stream *s, const byte *data, uint count)
+{
+ uint ignore;
+
+ sputs(s, data, count, &ignore);
+}
+
+static void
+bjc_put_hi_lo(stream *s, int value)
+{
+ spputc(s, value >> 8);
+ spputc(s, value & 0xff);
+}
+
+static void
+bjc_put_lo_hi(stream *s, int value)
+{
+ spputc(s, value & 0xff);
+ spputc(s, value >> 8);
+}
+
+static void
+bjc_put_command(stream *s, int ch, int count)
+{
+ spputc(s, 033 /*ESC*/);
+ spputc(s, '(');
+ spputc(s, ch);
+ bjc_put_lo_hi(s, count);
+}
+
+/* ---------------- Commands ---------------- */
+
+/* Line feed (^J) */
+void
+bjc_put_LF(stream *s)
+{
+ spputc(s, 0x0a);
+}
+
+/* Form feed (^L) */
+void
+bjc_put_FF(stream *s)
+{
+ spputc(s, 0x0c);
+}
+
+/* Carriage return (^M) */
+void
+bjc_put_CR(stream *s)
+{
+ spputc(s, 0x0d);
+}
+
+/* Return to initial condition (ESC @) */
+void
+bjc_put_initialize(stream *s)
+{
+ bjc_put_bytes(s, (const byte *)"\033@", 2);
+}
+
+/* Set initial condition (ESC [ K <count> <init> <id> <parm1> <parm2>) */
+void
+bjc_put_set_initial(stream *s)
+{
+ bjc_put_bytes(s, (const byte *)"\033[K\002\000\000\017", 7);
+}
+
+/* Set data compression (ESC [ b <count> <state>) */
+void
+bjc_put_set_compression(stream *s, bjc_raster_compression_t compression)
+{
+ bjc_put_command(s, 'b', 1);
+ spputc(s, compression);
+}
+
+/* Select print method (ESC ( c <count> <parm1> <parm2> [<parm3>]) */
+void
+bjc_put_print_method_short(stream *s, bjc_print_color_short_t color)
+{
+ bjc_put_command(s, 'c', 1);
+ spputc(s, color);
+}
+void
+bjc_put_print_method(stream *s, bjc_print_color_t color,
+ bjc_print_media_t media, bjc_print_quality_t quality,
+ bjc_black_density_t density)
+{
+ bjc_put_command(s, 'c', 2 + (density != 0));
+ spputc(s, 0x10 | color);
+ spputc(s, (media << 4) | quality);
+ if (density)
+ spputc(s, density << 4);
+}
+
+/* Set raster resolution (ESC ( d <count> <y_res> [<x_res>]) */
+void
+bjc_put_raster_resolution(stream *s, int x_resolution, int y_resolution)
+{
+ if (x_resolution == y_resolution) {
+ bjc_put_command(s, 'd', 2);
+ } else {
+ bjc_put_command(s, 'd', 4);
+ bjc_put_hi_lo(s, y_resolution);
+ }
+ bjc_put_hi_lo(s, x_resolution);
+}
+
+/* Raster skip (ESC ( e <count> <skip>) */
+void
+bjc_put_raster_skip(stream *s, int skip)
+{
+ bjc_put_command(s, 'e', 2);
+ bjc_put_hi_lo(s, skip);
+}
+
+/* Set page margins (ESC ( g <count> <length> <lm> <rm> <top>) */
+void
+bjc_put_page_margins(stream *s, int length, int lm, int rm, int top)
+{
+ byte parms[4];
+ int count;
+
+ parms[0] = length, parms[1] = lm, parms[2] = rm, parms[3] = top;
+ count = 4; /* could be 1..3 */
+ bjc_put_command(s, 'g', count);
+ bjc_put_bytes(s, parms, count);
+}
+
+/* Set media supply method (ESC * l <count> <parm1> <parm2>) */
+void
+bjc_put_media_supply(stream *s, bjc_media_supply_t supply,
+ bjc_media_type_t type)
+{
+ bjc_put_command(s, 'l', 2);
+ spputc(s, 0x10 | supply);
+ spputc(s, type << 4);
+}
+
+/* Identify ink cartridge (ESC ( m <count> <type>) */
+void
+bjc_put_identify_cartridge(stream *s,
+ bjc_identify_cartridge_command_t command)
+{
+ bjc_put_command(s, 'm', 1);
+ spputc(s, command);
+}
+
+/* CMYK raster image (ESC ( A <count> <color>) */
+void
+bjc_put_cmyk_image(stream *s, bjc_cmyk_image_component_t component,
+ const byte *data, int count)
+{
+ bjc_put_command(s, 'A', count + 1);
+ spputc(s, component);
+ bjc_put_bytes(s, data, count);
+}
+
+/* Move by raster lines (ESC ( n <count> <lines>) */
+void
+bjc_put_move_lines(stream *s, int lines)
+{
+ bjc_put_command(s, 'n', 2);
+ bjc_put_hi_lo(s, lines);
+}
+
+/* Set unit for movement by raster lines (ESC ( o <count> <unit>) */
+void
+bjc_put_move_lines_unit(stream *s, int unit)
+{
+ bjc_put_command(s, 'o', 2);
+ bjc_put_hi_lo(s, unit);
+}
+
+/* Set extended margins (ESC ( p <count> <length60ths> <lm60ths> */
+/* <rm60ths> <top60ths>) */
+void
+bjc_put_extended_margins(stream *s, int length, int lm, int rm, int top)
+{
+ bjc_put_command(s, 'p', 8);
+ bjc_put_hi_lo(s, length);
+ bjc_put_hi_lo(s, lm);
+ bjc_put_hi_lo(s, rm);
+ bjc_put_hi_lo(s, top);
+}
+
+/* Set image format (ESC ( t <count> <depth> <format> <ink>) */
+void
+bjc_put_image_format(stream *s, int depth, bjc_image_format_t format,
+ bjc_ink_system_t ink)
+
+{
+ bjc_put_command(s, 't', 3);
+ spputc(s, depth);
+ spputc(s, format);
+ spputc(s, ink);
+}
+
+/* Page ID (ESC ( q <count> <id>) */
+void
+bjc_put_page_id(stream *s, int id)
+{
+ bjc_put_command(s, 'q', 1);
+ spputc(s, id);
+}
+
+/* Continue raster image (ESC ( F <count> <data>) */
+void
+bjc_put_continue_image(stream *s, const byte *data, int count)
+{
+ bjc_put_command(s, 'F', count);
+ bjc_put_bytes(s, data, count);
+}
+
+/* BJ indexed image (ESC ( f <count> R <dot_rows> <dot_cols> <layers> */
+/* <index>) */
+void
+bjc_put_indexed_image(stream *s, int dot_rows, int dot_cols, int layers)
+{
+ bjc_put_command(s, 'f', 5);
+ spputc(s, 'R'); /* per spec */
+ spputc(s, dot_rows);
+ spputc(s, dot_cols);
+ spputc(s, layers);
+}
diff --git a/devices/gdevbjcl.h b/devices/gdevbjcl.h
new file mode 100644
index 000000000..045cdb651
--- /dev/null
+++ b/devices/gdevbjcl.h
@@ -0,0 +1,395 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+/* Canon BJC command generation library interface */
+
+/****** PRELIMINARY, SUBJECT TO CHANGE WITHOUT NOTICE. ******/
+
+#ifndef gdevbjcl_INCLUDED
+# define gdevbjcl_INCLUDED
+
+#include <stdio.h> /* ****** PATCH FOR stream.h ****** */
+#include "stream.h"
+
+/*
+ * These procedures generate command strings for the Canon BJC family of
+ * printers. Note that not all printers support all commands.
+ */
+
+/* ---------------- Printer capabilities ---------------- */
+
+/*
+ * Different printer models implement different subsets of the command set.
+ * We define a mask bit for each capability, and a mask for each printer
+ * indicating which capabilities it supports. In some cases, a capability
+ * is a parameter value for a command rather than a separate command.
+ */
+
+/*
+ * Single-character commands.
+ *
+ * All BJC models implement CR and FF.
+ */
+#define BJC_OPT_NUL 0x00000001
+#define BJC_OPT_LF 0x00000002
+
+/*
+ * Session commands.
+ *
+ * All BJC models implement Set initial condition, Initialize,
+ * Print method, and Media Supply.
+ */
+#define BJC_OPT_IDENTIFY_CARTRIDGE 0x00000004
+#define BJC_OPT_MONOCHROME_SMOOTHING 0x00000008 /* for bjc_print_color_t */
+
+/*
+ * Page commands.
+ *
+ * All BJC models implement Page margins.
+ */
+#define BJC_OPT_EXTENDED_MARGINS 0x00000010
+#define BJC_OPT_PAGE_ID 0x00000020
+
+/*
+ * Resolution. This varies considerably from model to model.
+ * The _300 or _360 option gives the base resolution; the other options
+ * indicate which multiples of the base are available.
+ * Note that the resolution multipliers are specified as X, then Y.
+ */
+#define BJC_OPT_RESOLUTION_360 0x00000040
+#define BJC_OPT_RESOLUTION_300 0x00000080
+#define BJC_OPT_RESOLUTION_HALF 0x00000100 /* 180 or 150 */
+#define BJC_OPT_RESOLUTION_QUARTER 0x00000200 /* 90 or 75 */
+#define BJC_OPT_RESOLUTION_2X 0x00000400 /* 720 or 600 */
+#define BJC_OPT_RESOLUTION_2X_1X 0x00000800
+#define BJC_OPT_RESOLUTION_4X_2X 0x00001000
+
+/*
+ * Image commands.
+ *
+ * All BJC models implement Raster resolution, Raster skip, CMYK image,
+ * and Data compression.
+ */
+#define BJC_OPT_X_Y_RESOLUTION 0x00002000 /* for raster_resolution */
+#define BJC_OPT_MOVE_LINES 0x00004000
+#define BJC_OPT_IMAGE_FORMAT 0x00008000
+#define BJC_OPT_CONTINUE_IMAGE 0x00010000
+#define BJC_OPT_INDEXED_IMAGE 0x00020000
+#define BJC_OPT_SET_COLOR_COMPONENT 0x00040000
+
+/*
+ * Define the capabilities of the models that we know about.
+ */
+/*
+ * We don't have the documentation for the 50, but Canon says it's the
+ * same as the 80.
+ */
+#define BJC_OPT_50\
+ (BJC_OPT_NUL | BJC_OPT_LF |\
+ BJC_OPT_IDENTIFY_CARTRIDGE |\
+ BJC_OPT_EXTENDED_MARGINS | BJC_OPT_PAGE_ID |\
+ BJC_OPT_RESOLUTION_360 | BJC_OPT_RESOLUTION_HALF |\
+ BJC_OPT_RESOLUTION_QUARTER | BJC_OPT_RESOLUTION_2X_1X |\
+ BJC_OPT_X_Y_RESOLUTION | BJC_OPT_MOVE_LINES | BJC_OPT_IMAGE_FORMAT |\
+ BJC_OPT_CONTINUE_IMAGE)
+#define BJC_OPT_70\
+ (BJC_OPT_LF |\
+ BJC_OPT_IDENTIFY_CARTRIDGE | BJC_OPT_MONOCHROME_SMOOTHING |\
+ BJC_OPT_RESOLUTION_360)
+#define BJC_OPT_80\
+ BJC_OPT_50
+#define BJC_OPT_210\
+ (BJC_OPT_IDENTIFY_CARTRIDGE | BJC_OPT_MONOCHROME_SMOOTHING |\
+ BJC_OPT_EXTENDED_MARGINS |\
+ BJC_OPT_RESOLUTION_360 |\
+ BJC_OPT_X_Y_RESOLUTION | BJC_OPT_MOVE_LINES | BJC_OPT_CONTINUE_IMAGE)
+#define BJC_OPT_250\
+ (BJC_OPT_LF |\
+ BJC_OPT_IDENTIFY_CARTRIDGE | BJC_OPT_MONOCHROME_SMOOTHING |\
+ BJC_OPT_EXTENDED_MARGINS | BJC_OPT_PAGE_ID |\
+ BJC_OPT_RESOLUTION_360 | BJC_OPT_RESOLUTION_HALF |\
+ BJC_OPT_RESOLUTION_QUARTER | BJC_OPT_RESOLUTION_2X_1X |\
+ BJC_OPT_X_Y_RESOLUTION | BJC_OPT_MOVE_LINES | BJC_OPT_IMAGE_FORMAT |\
+ BJC_OPT_CONTINUE_IMAGE)
+#define BJC_OPT_610\
+ (BJC_OPT_LF |\
+ BJC_OPT_RESOLUTION_360)
+#define BJC_OPT_620\
+ BJC_OPT_610
+#define BJC_OPT_4000\
+ (BJC_OPT_LF |\
+ BJC_OPT_IDENTIFY_CARTRIDGE | BJC_OPT_MONOCHROME_SMOOTHING |\
+ BJC_OPT_RESOLUTION_360 | BJC_OPT_RESOLUTION_HALF |\
+ BJC_OPT_RESOLUTION_QUARTER)
+#define BJC_OPT_4100\
+ (BJC_OPT_IDENTIFY_CARTRIDGE |\
+ BJC_OPT_EXTENDED_MARGINS |\
+ BJC_OPT_RESOLUTION_360 |\
+ BJC_OPT_MOVE_LINES | BJC_OPT_CONTINUE_IMAGE)
+#define BJC_OPT_4200\
+ (BJC_OPT_LF |\
+ BJC_OPT_IDENTIFY_CARTRIDGE |\
+ BJC_OPT_EXTENDED_MARGINS | BJC_OPT_PAGE_ID |\
+ BJC_OPT_RESOLUTION_360 |\
+ BJC_OPT_MOVE_LINES | BJC_OPT_IMAGE_FORMAT | BJC_OPT_CONTINUE_IMAGE)
+#define BJC_OPT_4300\
+ BJC_OPT_250
+#define BJC_OPT_4550\
+ BJC_OPT_250
+#define BJC_OPT_4650\
+ BJC_OPT_250
+#define BJC_OPT_5500\
+ (BJC_OPT_IDENTIFY_CARTRIDGE | BJC_OPT_MONOCHROME_SMOOTHING |\
+ BJC_OPT_EXTENDED_MARGINS |\
+ BJC_OPT_RESOLUTION_360 |\
+ BJC_OPT_MOVE_LINES | BJC_OPT_CONTINUE_IMAGE)
+/* The 7000 is not well documented. The following is a semi-guess. */
+#define BJC_OPT_7000\
+ (BJC_OPT_NUL | BJC_OPT_LF |\
+ BJC_OPT_IDENTIFY_CARTRIDGE |\
+ BJC_OPT_EXTENDED_MARGINS | BJC_OPT_PAGE_ID |\
+ BJC_OPT_RESOLUTION_300 | BJC_OPT_RESOLUTION_2X_1X |\
+ BJC_OPT_RESOLUTION_4X_2X |\
+ BJC_OPT_MOVE_LINES | BJC_OPT_IMAGE_FORMAT | BJC_OPT_CONTINUE_IMAGE |\
+ BJC_OPT_INDEXED_IMAGE | BJC_OPT_SET_COLOR_COMPONENT)
+
+/*
+ * Enumerate the options for all the printer models we know about.
+ * m(x, y) will normally be {x, y}, to generate a table.
+ */
+#define BJC_ENUMERATE_OPTIONS(m)\
+ m(50, BJC_OPT_50)\
+ m(70, BJC_OPT_70)\
+ m(80, BJC_OPT_80)\
+ m(210, BJC_OPT_210)\
+ m(250, BJC_OPT_250)\
+ m(610, BJC_OPT_610)\
+ m(620, BJC_OPT_620)\
+ m(4000, BJC_OPT_4000)\
+ m(4100, BJC_OPT_4100)\
+ m(4200, BJC_OPT_4200)\
+ m(4300, BJC_OPT_4300)\
+ m(4550, BJC_OPT_4550)\
+ m(4650, BJC_OPT_4650)\
+ m(5500, BJC_OPT_5500)\
+ m(7000, BJC_OPT_7000)
+
+/* ---------------- Command generation ---------------- */
+
+/*
+ * Single-character commands.
+ */
+
+/* Carriage return (^M) */
+void bjc_put_CR(stream *s);
+
+/* Form feed (^L) */
+void bjc_put_FF(stream *s);
+
+/* Line feed (^J) */
+void bjc_put_LF(stream *s);
+
+/*
+ * Session commands.
+ */
+
+/* Set initial condition */
+void bjc_put_initial_condition(stream *s);
+
+/* Return to initial condition */
+void bjc_put_initialize(stream *s);
+
+/* Select print method */
+/****** DIFFERENT FOR 7000 ******/
+typedef enum {
+ BJC_PRINT_COLOR_COLOR = 0x0,
+ BJC_PRINT_COLOR_MONOCHROME = 0x1,
+ BJC_PRINT_COLOR_MONOCHROME_WITH_SMOOTHING = 0x2 /* option */
+} bjc_print_color_t;
+typedef enum {
+ BJC_PRINT_MEDIA_PLAIN_PAPER = 0x0,
+ BJC_PRINT_MEDIA_COATED_PAPER = 0x1,
+ BJC_PRINT_MEDIA_TRANSPARENCY_FILM = 0x2,
+ BJC_PRINT_MEDIA_BACK_PRINT_FILM = 0x3,
+ BJC_PRINT_MEDIA_TEXTILE_SHEET = 0x4,
+ BJC_PRINT_MEDIA_GLOSSY_PAPER = 0x5,
+ BJC_PRINT_MEDIA_HIGH_GLOSS_FILM = 0x6,
+ BJC_PRINT_MEDIA_HIGH_RESOLUTION_PAPER = 0x7 /* BJC-80 only */
+} bjc_print_media_t;
+typedef enum {
+ BJC_PRINT_QUALITY_NORMAL = 0x0,
+ BJC_PRINT_QUALITY_HIGH = 0x1,
+ BJC_PRINT_QUALITY_DRAFT = 0x2,
+ BJC_PRINT_QUALITY_COLOR_NON_BLEED = 0x8 /* not 6x0 */
+} bjc_print_quality_t;
+typedef enum {
+ /* 6x0 only */
+ BJC_BLACK_DENSITY_NORMAL = 0x0,
+ BJC_BLACK_DENSITY_HIGH = 0x1
+} bjc_black_density_t;
+void bjc_put_print_method(stream *s, bjc_print_color_t color,
+ bjc_print_media_t media,
+ bjc_print_quality_t quality,
+ bjc_black_density_t density);
+typedef enum {
+ /* 70, 4000, 4550, 4650 */
+ BJC_70_PRINT_COLOR_SHORT_FINE = 0x0, /* also 0x1, 0x2 */
+ BJC_70_PRINT_COLOR_SHORT_HQ = 0x3,
+ BJC_70_PRINT_COLOR_SHORT_ECO = 0x4,
+ /* 80, 250, 4200, 4300 */
+ BJC_80_PRINT_COLOR_SHORT_STD = 0x0,
+ BJC_80_PRINT_COLOR_SHORT_STD_SPECIALTY = 0x1,
+ BJC_80_PRINT_COLOR_SHORT_HQ_SPECIALTY = 0x2,
+ BJC_80_PRINT_COLOR_SHORT_HQ = 0x3,
+ BJC_80_PRINT_COLOR_SHORT_HIGH_SPEED = 0x4,
+ /* 210, 4100 */
+ BJC_210_PRINT_COLOR_SHORT_HQ = 0x0, /* also 0x1 */
+ BJC_210_PRINT_COLOR_SHORT_FINE = 0x2, /* also 0x3 */
+ BJC_210_PRINT_COLOR_SHORT_HIGH_SPEED = 0x4,
+ /* 5500 */
+ BJC_5500_PRINT_COLOR_SHORT_COATED = 0x0,
+ BJC_5500_PRINT_COLOR_SHORT_TRANSPARENCY = 0x1,
+ BJC_5500_PRINT_COLOR_SHORT_PLAIN = 0x2,
+ BJC_5500_PRINT_COLOR_SHORT_HQ_NON_BLEED = 0x3,
+ BJC_5500_PRINT_COLOR_SHORT_HIGH_SPEED = 0x4
+} bjc_print_color_short_t;
+void bjc_put_print_method_short(stream *s, bjc_print_color_short_t color);
+
+/* Set media supply method */
+/****** DIFFERENT FOR 7000 ******/
+typedef enum {
+ /* 70, 210, 250, 6x0, 4100 */
+ BJC_70_MEDIA_SUPPLY_MANUAL_1 = 0x0,
+ BJC_70_MEDIA_SUPPLY_MANUAL_2 = 0x1,
+ BJC_70_MEDIA_SUPPLY_ASF = 0x4,
+ /* 250, 4000, 4300, 4650, 5500 */
+ BJC_250_MEDIA_SUPPLY_CONTINUOUS_FORM = 0x2,
+ BJC_250_MEDIA_SUPPLY_ASF_BIN_2 = 0x5,
+ /* 250, 4650, 5500 */
+ BJC_250_MEDIA_SUPPLY_AUTO_SWITCH = 0xf,
+ /* 4000, 4300, 4650 */
+ BJC_4000_MEDIA_SUPPLY_CASSETTE = 0x8,
+ /* 80 */
+ BJC_80_MEDIA_SUPPLY_ASF_OFFLINE = 0x0,
+ BJC_80_MEDIA_SUPPLY_ASF_ONLINE = 0x1 /* also 0x4 */
+} bjc_media_supply_t;
+typedef enum {
+ BJC_MEDIA_TYPE_PLAIN_PAPER = 0x0,
+ BJC_MEDIA_TYPE_COATED_PAPER = 0x1,
+ BJC_MEDIA_TYPE_TRANSPARENCY_FILM = 0x2,
+ BJC_MEDIA_TYPE_BACK_PRINT_FILM = 0x3,
+ BJC_MEDIA_TYPE_PAPER_WITH_LEAD = 0x4,
+ BJC_MEDIA_TYPE_TEXTILE_SHEET = 0x5,
+ BJC_MEDIA_TYPE_GLOSSY_PAPER = 0x6,
+ BJC_MEDIA_TYPE_HIGH_GLOSS_FILM = 0x7,
+ BJC_MEDIA_TYPE_ENVELOPE = 0x8,
+ BJC_MEDIA_TYPE_CARD = 0x9,
+ BJC_MEDIA_TYPE_HIGH_RESOLUTION_6X0 = 0xa, /* 6x0 only */
+ BJC_MEDIA_TYPE_HIGH_RESOLUTION = 0xb, /* 720x720, other models */
+ BJC_MEDIA_TYPE_FULL_BLEED = 0xc,
+ BJC_MEDIA_TYPE_BANNER = 0xd
+} bjc_media_type_t;
+void bjc_put_media_supply(stream *s, bjc_media_supply_t supply,
+ bjc_media_type_t type);
+
+/* Identify ink cartridge */
+typedef enum {
+ BJC_IDENTIFY_CARTRIDGE_PREPARE = 0x0,
+ BJC_IDENTIFY_CARTRIDGE_REQUEST = 0x1
+} bjc_identify_cartridge_command_t;
+void bjc_put_identify_cartridge(stream *s,
+ bjc_identify_cartridge_command_t command);
+
+/*
+ * Page commands.
+ */
+
+/* Set page margins */
+/* Left margin is 1-origin; margins are both from left edge; indent <= 8 */
+void bjc_put_page_margins(stream *s, int length10ths, int lm10ths,
+ int rm10ths, int indent60ths);
+
+/* Set extended margins */
+/* All values are 0-origin; margins are both from left edge; indent <= 8 */
+void bjc_put_extended_margins(stream *s, int length60ths, int lm60ths,
+ int rm60ths, int indent60ths);
+
+/* Page ID */
+/* 0 <= id <= 127 */
+void bjc_put_page_id(stream *s, int id);
+
+/*
+ * Image commands.
+ */
+
+/* Set raster compression */
+typedef enum {
+ BJC_RASTER_COMPRESSION_NONE = 0x0,
+ BJC_RASTER_COMPRESSION_PACKBITS = 0x1
+} bjc_raster_compression_t;
+void bjc_put_compression(stream *s, bjc_raster_compression_t compression);
+
+/* Set raster resolution */
+void bjc_put_raster_resolution(stream *s, int x_resolution, int y_resolution);
+
+/* Raster skip */
+/* Maximum skip on 6x0 and 4000 is 0x17ff */
+void bjc_put_raster_skip(stream *s, int skip);
+
+/* CMYK raster image */
+typedef enum {
+ BJC_CMYK_IMAGE_CYAN = 'C',
+ BJC_CMYK_IMAGE_MAGENTA = 'M',
+ BJC_CMYK_IMAGE_YELLOW = 'Y',
+ BJC_CMYK_IMAGE_BLACK = 'K',
+} bjc_cmyk_image_component_t;
+void bjc_put_cmyk_image(stream *s, bjc_cmyk_image_component_t component,
+ const byte *data, int count);
+
+/* Move by raster lines */
+/* Distance must be a multiple of the raster resolution */
+void bjc_put_move_lines(stream *s, int lines);
+
+/* Set unit for movement by raster lines */
+/* unit = 360 for printers other than 7000 */
+/* unit = 300 or 600 for 7000 */
+void bjc_put_move_lines_unit(stream *s, int unit);
+
+/* Set image format */
+/* depth is 1 or 2 */
+/****** DIFFERENT FOR 7000 ******/
+typedef enum {
+ BJC_IMAGE_FORMAT_REGULAR = 0x00,
+ BJC_IMAGE_FORMAT_INDEXED = 0x80
+} bjc_image_format_t;
+typedef enum {
+ BJC_INK_SYSTEM_REGULAR = 0x01,
+ BJC_INK_SYSTEM_PHOTO = 0x02,
+ BJC_INK_SYSTEM_REGULAR_DVM = 0x09, /* drop volume modulation */
+ BJC_INK_SYSTEM_PHOTO_DVM = 0x0a /* drop volume modulation */
+} bjc_ink_system_t;
+void bjc_put_image_format(stream *s, int depth,
+ bjc_image_format_t format,
+ bjc_ink_system_t ink);
+/* 4550 only */
+void bjc_put_photo_image(stream *s, bool photo);
+
+/* Continue raster image */
+void bjc_put_continue_image(stream *s, const byte *data, int count);
+
+/* BJ indexed image */
+void bjc_put_indexed_image(stream *s, int dot_rows, int dot_cols, int layers);
+
+#endif /* gdevbjcl_INCLUDED */
diff --git a/devices/gdevbmp.c b/devices/gdevbmp.c
new file mode 100644
index 000000000..8eab4c235
--- /dev/null
+++ b/devices/gdevbmp.c
@@ -0,0 +1,223 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+/* .BMP file format output drivers */
+#include "gdevprn.h"
+#include "gdevpccm.h"
+#include "gdevbmp.h"
+
+/* ------ The device descriptors ------ */
+
+static dev_proc_print_page(bmp_print_page);
+static dev_proc_print_page(bmp_cmyk_print_page);
+
+/* Monochrome. */
+
+const gx_device_printer gs_bmpmono_device =
+prn_device(prn_bg_procs, "bmpmono", /* The print_page proc is compatible with allowing bg printing */
+ DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
+ X_DPI, Y_DPI,
+ 0, 0, 0, 0, /* margins */
+ 1, bmp_print_page);
+
+/* 8-bit (SuperVGA-style) grayscale . */
+/* (Uses a fixed palette of 256 gray levels.) */
+
+/* Since the print_page doesn't alter the device, this device can print in the background */
+static const gx_device_procs bmpgray_procs =
+prn_color_procs(gdev_prn_open, gdev_prn_bg_output_page, gdev_prn_close,
+ gx_default_gray_map_rgb_color, gx_default_gray_map_color_rgb);
+const gx_device_printer gs_bmpgray_device = {
+ prn_device_body(gx_device_printer, bmpgray_procs, "bmpgray",
+ DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
+ X_DPI, Y_DPI,
+ 0, 0, 0, 0, /* margins */
+ 1, 8, 255, 0, 256, 0, bmp_print_page)
+};
+
+/* 1-bit-per-plane separated CMYK color. */
+
+/* Since the print_page doesn't alter the device, this device can print in the background */
+#define bmp_cmyk_procs(p_map_color_rgb, p_map_cmyk_color)\
+ gdev_prn_open, NULL, NULL, gdev_prn_bg_output_page, gdev_prn_close,\
+ NULL, p_map_color_rgb, NULL, NULL, NULL, NULL, NULL, NULL,\
+ gdev_prn_get_params, gdev_prn_put_params,\
+ p_map_cmyk_color, NULL, NULL, NULL, gx_page_device_get_page_device
+
+static const gx_device_procs bmpsep1_procs = {
+ bmp_cmyk_procs(cmyk_1bit_map_color_rgb, cmyk_1bit_map_cmyk_color)
+};
+const gx_device_printer gs_bmpsep1_device = {
+ prn_device_body(gx_device_printer, bmpsep1_procs, "bmpsep1",
+ DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
+ X_DPI, Y_DPI,
+ 0,0,0,0, /* margins */
+ 4, 4, 1, 1, 2, 2, bmp_cmyk_print_page)
+};
+
+/* 8-bit-per-plane separated CMYK color. */
+
+static const gx_device_procs bmpsep8_procs = {
+ bmp_cmyk_procs(cmyk_8bit_map_color_rgb, cmyk_8bit_map_cmyk_color)
+};
+const gx_device_printer gs_bmpsep8_device = {
+ prn_device_body(gx_device_printer, bmpsep8_procs, "bmpsep8",
+ DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
+ X_DPI, Y_DPI,
+ 0,0,0,0, /* margins */
+ 4, 32, 255, 255, 256, 256, bmp_cmyk_print_page)
+};
+
+/* 4-bit planar (EGA/VGA-style) color. */
+
+/* Since the print_page doesn't alter the device, this device can print in the background */
+static const gx_device_procs bmp16_procs =
+prn_color_procs(gdev_prn_open, gdev_prn_bg_output_page, gdev_prn_close,
+ pc_4bit_map_rgb_color, pc_4bit_map_color_rgb);
+const gx_device_printer gs_bmp16_device = {
+ prn_device_body(gx_device_printer, bmp16_procs, "bmp16",
+ DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
+ X_DPI, Y_DPI,
+ 0, 0, 0, 0, /* margins */
+ 3, 4, 1, 1, 2, 2, bmp_print_page)
+};
+
+/* 8-bit (SuperVGA-style) color. */
+/* (Uses a fixed palette of 3,3,2 bits.) */
+
+/* Since the print_page doesn't alter the device, this device can print in the background */
+static const gx_device_procs bmp256_procs =
+prn_color_procs(gdev_prn_open, gdev_prn_bg_output_page, gdev_prn_close,
+ pc_8bit_map_rgb_color, pc_8bit_map_color_rgb);
+const gx_device_printer gs_bmp256_device = {
+ prn_device_body(gx_device_printer, bmp256_procs, "bmp256",
+ DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
+ X_DPI, Y_DPI,
+ 0, 0, 0, 0, /* margins */
+ 3, 8, 5, 5, 6, 6, bmp_print_page)
+};
+
+/* 24-bit color. */
+
+/* Since the print_page doesn't alter the device, this device can print in the background */
+static const gx_device_procs bmp16m_procs =
+prn_color_procs(gdev_prn_open, gdev_prn_bg_output_page, gdev_prn_close,
+ bmp_map_16m_rgb_color, bmp_map_16m_color_rgb);
+const gx_device_printer gs_bmp16m_device =
+prn_device(bmp16m_procs, "bmp16m",
+ DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
+ X_DPI, Y_DPI,
+ 0, 0, 0, 0, /* margins */
+ 24, bmp_print_page);
+
+/* 32-bit CMYK color (outside the BMP specification). */
+
+static const gx_device_procs bmp32b_procs = {
+ bmp_cmyk_procs(cmyk_8bit_map_color_rgb, gx_default_cmyk_map_cmyk_color)
+};
+const gx_device_printer gs_bmp32b_device =
+prn_device(bmp32b_procs, "bmp32b",
+ DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
+ X_DPI, Y_DPI,
+ 0, 0, 0, 0, /* margins */
+ 32, bmp_print_page);
+
+/* ------ Private definitions ------ */
+
+/* Write out a page in BMP format. */
+/* This routine is used for all non-separated formats. */
+static int
+bmp_print_page(gx_device_printer * pdev, FILE * file)
+{
+ uint raster = gdev_prn_raster(pdev);
+ /* BMP scan lines are padded to 32 bits. */
+ uint bmp_raster = raster + (-(int)raster & 3);
+ byte *row = gs_alloc_bytes(pdev->memory, bmp_raster, "bmp file buffer");
+ int y;
+ int code; /* return code */
+
+ if (row == 0) /* can't allocate row buffer */
+ return_error(gs_error_VMerror);
+ memset(row+raster, 0, bmp_raster - raster); /* clear the padding bytes */
+
+ /* Write the file header. */
+
+ code = write_bmp_header(pdev, file);
+ if (code < 0)
+ goto done;
+
+ /* Write the contents of the image. */
+ /* BMP files want the image in bottom-to-top order! */
+
+ for (y = pdev->height - 1; y >= 0; y--) {
+ gdev_prn_copy_scan_lines(pdev, y, row, raster);
+ fwrite((const char *)row, bmp_raster, 1, file);
+ }
+
+done:
+ gs_free_object(pdev->memory, row, "bmp file buffer");
+
+ return code;
+}
+
+/* Write out a page in separated CMYK format. */
+/* This routine is used for all formats. */
+static int
+bmp_cmyk_print_page(gx_device_printer * pdev, FILE * file)
+{
+ int plane_depth = pdev->color_info.depth / 4;
+ uint raster = (pdev->width * plane_depth + 7) >> 3;
+ /* BMP scan lines are padded to 32 bits. */
+ uint bmp_raster = raster + (-(int)raster & 3);
+ byte *row = gs_alloc_bytes(pdev->memory, bmp_raster, "bmp file buffer");
+ int y;
+ int code = 0; /* return code */
+ int plane;
+
+ if (row == 0) /* can't allocate row buffer */
+ return_error(gs_error_VMerror);
+ memset(row+raster, 0, bmp_raster - raster); /* clear the padding bytes */
+
+ for (plane = 0; plane <= 3; ++plane) {
+ gx_render_plane_t render_plane;
+
+ /* Write the page header. */
+
+ code = write_bmp_separated_header(pdev, file);
+ if (code < 0)
+ break;
+
+ /* Write the contents of the image. */
+ /* BMP files want the image in bottom-to-top order! */
+
+ gx_render_plane_init(&render_plane, (gx_device *)pdev, plane);
+ for (y = pdev->height - 1; y >= 0; y--) {
+ byte *actual_data;
+ uint actual_raster;
+
+ code = gdev_prn_get_lines(pdev, y, 1, row, bmp_raster,
+ &actual_data, &actual_raster,
+ &render_plane);
+ if (code < 0)
+ goto done;
+ fwrite((const char *)actual_data, bmp_raster, 1, file);
+ }
+ }
+
+done:
+ gs_free_object(pdev->memory, row, "bmp file buffer");
+
+ return code;
+}
diff --git a/devices/gdevbmp.h b/devices/gdevbmp.h
new file mode 100644
index 000000000..400fdb6dc
--- /dev/null
+++ b/devices/gdevbmp.h
@@ -0,0 +1,35 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+/* .BMP file format definitions and utility interfaces */
+
+#ifndef gdevbmp_INCLUDED
+# define gdevbmp_INCLUDED
+
+/* Define the default X and Y resolution. */
+#define X_DPI 72
+#define Y_DPI 72
+
+/* Write the BMP file header. This procedure is used for all formats. */
+int write_bmp_header(gx_device_printer *pdev, FILE *file);
+
+/* Write a BMP header for separated CMYK output. */
+int write_bmp_separated_header(gx_device_printer *pdev, FILE *file);
+
+/* 24-bit color mappers */
+dev_proc_map_rgb_color(bmp_map_16m_rgb_color);
+dev_proc_map_color_rgb(bmp_map_16m_color_rgb);
+
+#endif /* gdevbmp_INCLUDED */
diff --git a/devices/gdevbmpa.c b/devices/gdevbmpa.c
new file mode 100644
index 000000000..e7309c032
--- /dev/null
+++ b/devices/gdevbmpa.c
@@ -0,0 +1,711 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+/* .BMP file format output drivers: Demo of ASYNC rendering */
+
+/* 2000-04-20 ghost@aladdin.com - Makes device structures const, changing
+ makefile entry from DEV to DEV2. */
+/* 1998/12/29 ghost@aladdin.com - Modified to use gdev_prn_render_lines,
+ which replaces the former "overlay" calls */
+/* 1998/11/23 ghost@aladdin.com - Removed pointless restriction to
+ single-page output */
+/* 1998/7/28 ghost@aladdin.com - Factored out common BMP format code
+ to gdevbmpc.c */
+/* Initial version 2/2/98 by John Desrosiers (soho@crl.com) */
+
+#include "stdio_.h"
+#include "gserrors.h"
+#include "gdevprna.h"
+#include "gdevpccm.h"
+#include "gdevbmp.h"
+#include "gdevppla.h"
+#include "gpsync.h"
+
+/*
+ * The original version of this driver was restricted to producing a single
+ * page per file. If for some reason you want to reinstate this
+ * restriction, uncomment the next line.
+ * NOTE: Even though the logic for multi-page files is straightforward,
+ * it results in a file that most programs that process BMP format cannot
+ * handle. Most programs will only display the first page.
+ */
+/*************** #define SINGLE_PAGE ****************/
+
+/* ------ The device descriptors ------ */
+
+/* Define data type for this device based on prn_device */
+typedef struct gx_device_async_s {
+ gx_device_common;
+ gx_prn_device_common;
+ bool UsePlanarBuffer;
+ int buffered_page_exists;
+ long file_offset_to_data[4];
+} gx_device_async;
+
+/* Define initializer for device */
+#define async_device(procs, dname, w10, h10, xdpi, ydpi, lm, bm, rm, tm, color_bits, print_page)\
+{ prn_device_std_margins_body(gx_device_async, procs, dname,\
+ w10, h10, xdpi, ydpi, lm, tm, lm, bm, rm, tm, color_bits, print_page),\
+ 0, 0, { 0, 0, 0, 0 }\
+}
+
+static dev_proc_open_device(bmpa_writer_open);
+static dev_proc_open_device(bmpa_cmyk_writer_open);
+static prn_dev_proc_open_render_device(bmpa_reader_open_render_device);
+static dev_proc_print_page_copies(bmpa_reader_print_page_copies);
+/* VMS limits procedure names to 31 characters. */
+static dev_proc_print_page_copies(bmpa_cmyk_reader_print_copies);
+static prn_dev_proc_buffer_page(bmpa_reader_buffer_page);
+static prn_dev_proc_buffer_page(bmpa_cmyk_reader_buffer_page);
+static dev_proc_output_page(bmpa_reader_output_page);
+static dev_proc_get_params(bmpa_get_params);
+static dev_proc_put_params(bmpa_put_params);
+static dev_proc_get_hardware_params(bmpa_get_hardware_params);
+static prn_dev_proc_start_render_thread(bmpa_reader_start_render_thread);
+static prn_dev_proc_get_space_params(bmpa_get_space_params);
+#define default_print_page 0 /* not needed becoz print_page_copies def'd */
+
+/* Monochrome. */
+
+static const gx_device_procs bmpamono_procs =
+ prn_procs(bmpa_writer_open, gdev_prn_output_page, gdev_prn_close);
+const gx_device_async gs_bmpamono_device =
+ async_device(bmpamono_procs, "bmpamono",
+ DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
+ X_DPI, Y_DPI,
+ 0,0,0,0, /* margins */
+ 1, default_print_page);
+
+/* 1-bit-per-plane separated CMYK color. */
+
+#define bmpa_cmyk_procs(p_open, p_map_color_rgb, p_map_cmyk_color)\
+ p_open, NULL, NULL, gdev_prn_output_page, gdev_prn_close,\
+ NULL, p_map_color_rgb, NULL, NULL, NULL, NULL, NULL, NULL,\
+ bmpa_get_params, bmpa_put_params,\
+ p_map_cmyk_color, NULL, NULL, NULL, gx_page_device_get_page_device
+
+static const gx_device_procs bmpasep1_procs = {
+ bmpa_cmyk_procs(bmpa_cmyk_writer_open, cmyk_1bit_map_color_rgb,
+ cmyk_1bit_map_cmyk_color)
+};
+const gx_device_async gs_bmpasep1_device = {
+ prn_device_body(gx_device_async, bmpasep1_procs, "bmpasep1",
+ DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
+ X_DPI, Y_DPI,
+ 0,0,0,0, /* margins */
+ 4, 4, 1, 1, 2, 2, default_print_page)
+};
+
+/* 8-bit-per-plane separated CMYK color. */
+
+static const gx_device_procs bmpasep8_procs = {
+ bmpa_cmyk_procs(bmpa_cmyk_writer_open, cmyk_8bit_map_color_rgb,
+ cmyk_8bit_map_cmyk_color)
+};
+const gx_device_async gs_bmpasep8_device = {
+ prn_device_body(gx_device_async, bmpasep8_procs, "bmpasep8",
+ DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
+ X_DPI, Y_DPI,
+ 0,0,0,0, /* margins */
+ 4, 32, 255, 255, 256, 256, default_print_page)
+};
+
+/* 4-bit (EGA/VGA-style) color. */
+
+static const gx_device_procs bmpa16_procs =
+ prn_color_procs(bmpa_writer_open, gdev_prn_output_page, gdev_prn_close,
+ pc_4bit_map_rgb_color, pc_4bit_map_color_rgb);
+const gx_device_async gs_bmpa16_device =
+ async_device(bmpa16_procs, "bmpa16",
+ DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
+ X_DPI, Y_DPI,
+ 0,0,0,0, /* margins */
+ 4, default_print_page);
+
+/* 8-bit (SuperVGA-style) color. */
+/* (Uses a fixed palette of 3,3,2 bits.) */
+
+static const gx_device_procs bmpa256_procs =
+ prn_color_procs(bmpa_writer_open, gdev_prn_output_page, gdev_prn_close,
+ pc_8bit_map_rgb_color, pc_8bit_map_color_rgb);
+const gx_device_async gs_bmpa256_device =
+ async_device(bmpa256_procs, "bmpa256",
+ DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
+ X_DPI, Y_DPI,
+ 0,0,0,0, /* margins */
+ 8, default_print_page);
+
+/* 24-bit color. */
+
+static const gx_device_procs bmpa16m_procs =
+ prn_color_procs(bmpa_writer_open, gdev_prn_output_page, gdev_prn_close,
+ bmp_map_16m_rgb_color, bmp_map_16m_color_rgb);
+const gx_device_async gs_bmpa16m_device =
+ async_device(bmpa16m_procs, "bmpa16m",
+ DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
+ X_DPI, Y_DPI,
+ 0,0,0,0, /* margins */
+ 24, default_print_page);
+
+/* 32-bit CMYK color (outside the BMP specification). */
+
+static const gx_device_procs bmpa32b_procs = {
+ bmpa_cmyk_procs(bmpa_writer_open, gx_default_map_color_rgb,
+ gx_default_cmyk_map_cmyk_color)
+};
+const gx_device_async gs_bmpa32b_device =
+ async_device(bmpa32b_procs, "bmpa32b",
+ DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
+ X_DPI, Y_DPI,
+ 0, 0, 0, 0, /* margins */
+ 32, default_print_page);
+
+/* --------- Forward declarations ---------- */
+
+static void bmpa_reader_thread(void *);
+
+/* ------------ Writer Instance procedures ---------- */
+
+/* Writer's open procedure */
+static int
+bmpa_open_writer(gx_device *pdev /* Driver instance to open */,
+ dev_proc_print_page_copies((*reader_print_page_copies)),
+ prn_dev_proc_buffer_page((*reader_buffer_page)))
+{
+ gx_device_async * const pwdev = (gx_device_async *)pdev;
+ int max_width;
+ int max_raster;
+ int min_band_height;
+ int max_src_image_row;
+
+ /*
+ * Set up device's printer proc vector to point to this driver, since
+ * there are no convenient macros for setting them up in static template.
+ */
+ init_async_render_procs(pwdev, bmpa_reader_start_render_thread,
+ reader_buffer_page,
+ reader_print_page_copies);
+ set_dev_proc(pdev, get_params, bmpa_get_params); /* because not all device-init macros allow this to be defined */
+ set_dev_proc(pdev, put_params, bmpa_put_params); /* ibid. */
+ set_dev_proc(pdev, get_hardware_params, bmpa_get_hardware_params);
+ set_dev_proc(pdev, output_page, bmpa_reader_output_page); /* hack */
+ pwdev->printer_procs.get_space_params = bmpa_get_space_params;
+ pwdev->printer_procs.open_render_device =
+ bmpa_reader_open_render_device; /* Included for tutorial value */
+
+ /*
+ * Determine MAXIMUM parameters this device will have to support over
+ * lifetime. See comments for bmpa_get_space_params().
+ */
+ max_width = DEFAULT_WIDTH_10THS * 60; /* figure max wid = default @ 600dpi */
+ min_band_height = max(1, (DEFAULT_HEIGHT_10THS * 60) / 100);
+ max_raster = bitmap_raster(max_width * pwdev->color_info.depth); /* doesn't need to be super accurate */
+ max_src_image_row = max_width * 4 * 2;
+
+ /* Set to planar buffering mode if appropriate. */
+ if (pwdev->UsePlanarBuffer)
+ gdev_prn_set_procs_planar(pdev);
+
+ /* Special writer open routine for async interpretation */
+ /* Starts render thread */
+ return gdev_prn_async_write_open((gx_device_printer *)pdev,
+ max_raster, min_band_height,
+ max_src_image_row);
+}
+static int
+bmpa_writer_open(gx_device *pdev /* Driver instance to open */)
+{
+ return bmpa_open_writer(pdev, bmpa_reader_print_page_copies,
+ bmpa_reader_buffer_page);
+}
+static int
+bmpa_cmyk_writer_open(gx_device *pdev /* Driver instance to open */)
+{
+ return bmpa_open_writer(pdev, bmpa_cmyk_reader_print_copies,
+ bmpa_cmyk_reader_buffer_page);
+}
+
+/* -------------- Renderer instance procedures ----------*/
+
+/* Forward declarations */
+static int
+ bmpa_reader_buffer_planes(gx_device_printer *pdev, FILE *prn_stream,
+ int num_copies, int first_plane,
+ int last_plane, int raster);
+
+/* Thread to do rendering, started by bmpa_reader_start_render_thread */
+static void
+bmpa_reader_thread(void *params)
+{
+ gdev_prn_async_render_thread((gdev_prn_start_render_params *)params);
+}
+
+static int /* rets 0 ok, -ve error if couldn't start thread */
+bmpa_reader_start_render_thread(gdev_prn_start_render_params *params)
+{
+ return gp_create_thread(bmpa_reader_thread, params);
+}
+
+static int
+bmpa_reader_open_render_device(gx_device_printer *ppdev)
+{
+ /*
+ * Do anything that needs to be done at open time here.
+ * Since this implementation doesn't do anything, we don't need to
+ * cast the device argument to the more specific type.
+ */
+ /*gx_device_async * const prdev = (gx_device_async *)ppdev;*/
+
+ /* Cascade down to the default handler */
+ return gdev_prn_async_render_open(ppdev);
+}
+
+/* Generic routine to send the page to the printer. */
+static int
+bmpa_reader_output_page(gx_device *pdev, int num_copies, int flush)
+{
+ /*
+ * HACK: open the printer page with the positionable attribute since
+ * we need to seek back & forth to support partial rendering.
+ */
+ if ( num_copies > 0 || !flush ) {
+ int code = gdev_prn_open_printer_seekable(pdev, 1, 1);
+
+ if ( code < 0 )
+ return code;
+ }
+ return gdev_prn_output_page(pdev, num_copies, flush);
+}
+
+static int
+bmpa_reader_print_planes(gx_device_printer *pdev, FILE *prn_stream,
+ int num_copies, int first_plane, int last_plane,
+ int raster)
+{
+ gx_device_async * const prdev = (gx_device_async *)pdev;
+ /* BMP scan lines are padded to 32 bits. */
+ uint bmp_raster = raster + (-raster & 3);
+ int code = 0;
+ int y;
+ byte *row = 0;
+ byte *raster_data;
+ int plane;
+
+ /* If there's data in buffer, need to process w/overlays */
+ if (prdev->buffered_page_exists) {
+ code = bmpa_reader_buffer_planes(pdev, prn_stream, num_copies,
+ first_plane, last_plane, raster);
+ goto done;
+ }
+#ifdef SINGLE_PAGE
+ /* BMP format is single page, so discard all but 1st printable page */
+ /* Since the OutputFile may have a %d, we use ftell to determine if */
+ /* this is a zero length file, which is legal to write */
+ if (ftell(prn_stream) != 0)
+ return 0;
+#endif
+ row = gs_alloc_bytes(pdev->memory, bmp_raster, "bmp file buffer");
+ if (row == 0) /* can't allocate row buffer */
+ return_error(gs_error_VMerror);
+
+ for (plane = first_plane; plane <= last_plane; ++plane) {
+ gx_render_plane_t render_plane;
+
+ /* Write header & seek to its end */
+ code =
+ (first_plane < 0 ? write_bmp_header(pdev, prn_stream) :
+ write_bmp_separated_header(pdev, prn_stream));
+ if (code < 0)
+ goto done;
+ /* Save the file offset where data begins */
+ if ((prdev->file_offset_to_data[plane - first_plane] =
+ ftell(prn_stream)) == -1L) {
+ code = gs_note_error(gs_error_ioerror);
+ goto done;
+ }
+
+ /*
+ * Write out the bands top to bottom. Finish the job even if
+ * num_copies == 0, to avoid invalid output file.
+ */
+ if (plane >= 0)
+ gx_render_plane_init(&render_plane, (gx_device *)pdev, plane);
+ for (y = prdev->height - 1; y >= 0; y--) {
+ uint actual_raster;
+
+ code = gdev_prn_get_lines(pdev, y, 1, row, bmp_raster,
+ &raster_data, &actual_raster,
+ (plane < 0 ? NULL : &render_plane));
+ if (code < 0)
+ goto done;
+ if (fwrite((const char *)raster_data, actual_raster, 1, prn_stream) < 1) {
+ code = gs_error_ioerror;
+ goto done;
+ }
+ }
+ }
+done:
+ gs_free_object(pdev->memory, row, "bmp file buffer");
+ prdev->buffered_page_exists = 0;
+ return code;
+}
+static int
+bmpa_reader_print_page_copies(gx_device_printer *pdev, FILE *prn_stream,
+ int num_copies)
+{
+ return bmpa_reader_print_planes(pdev, prn_stream, num_copies, -1, -1,
+ gdev_prn_raster(pdev));
+}
+static int
+bmpa_cmyk_plane_raster(gx_device_printer *pdev)
+{
+ return bitmap_raster(pdev->width * (pdev->color_info.depth / 4));
+}
+static int
+bmpa_cmyk_reader_print_copies(gx_device_printer *pdev, FILE *prn_stream,
+ int num_copies)
+{
+ return bmpa_reader_print_planes(pdev, prn_stream, num_copies, 0, 3,
+ bmpa_cmyk_plane_raster(pdev));
+}
+
+/* Buffer a (partial) rasterized page & optionally print result multiple times. */
+static int
+bmpa_reader_buffer_planes(gx_device_printer *pdev, FILE *file, int num_copies,
+ int first_plane, int last_plane, int raster)
+{
+ gx_device_async * const prdev = (gx_device_async *)pdev;
+ gx_device * const dev = (gx_device *)pdev;
+ int code = 0;
+
+ /* If there's no data in buffer, no need to do any overlays */
+ if (!prdev->buffered_page_exists) {
+ code = bmpa_reader_print_planes(pdev, file, num_copies,
+ first_plane, last_plane, raster);
+ goto done;
+ }
+
+ /*
+ * Continue rendering on top of the existing file. This requires setting
+ * up a buffer of the existing bits in GS's format (except for optional
+ * extra padding bytes at the end of each scan line, provided the scan
+ * lines are still correctly memory-aligned) and then calling
+ * gdev_prn_render_lines. If the device already provides a band buffer
+ * -- which currently is always the case -- we can use it if we want;
+ * but if a device stores partially rendered pages in memory in a
+ * compatible format (e.g., a printer with a hardware page buffer), it
+ * can render directly on top of the stored bits.
+ *
+ * If we can render exactly one band (or N bands) at a time, this is
+ * more efficient, since otherwise (a) band(s) will have to be rendered
+ * more than once.
+ */
+
+ {
+ byte *raster_data;
+ gx_device_clist_reader *const crdev =
+ (gx_device_clist_reader *)pdev;
+ int raster = gx_device_raster(dev, 1);
+ int padding = -raster & 3; /* BMP scan lines are padded to 32 bits. */
+ int bmp_raster = raster + padding;
+ int plane;
+
+ /*
+ * Get the address of the renderer's band buffer. In the future,
+ * it will be possible to suppress the allocation of this buffer,
+ * and to use only buffers provided the driver itself (e.g., a
+ * hardware buffer).
+ */
+ if (!pdev->buffer_space) {
+ /* Not banding. Can't happen. */
+ code = gs_note_error(gs_error_Fatal);
+ goto done;
+ }
+ raster_data = crdev->data;
+
+ for (plane = first_plane; plane <= last_plane; ++plane) {
+ gx_render_plane_t render_plane;
+ gx_device *bdev;
+ int y, band_base_line;
+
+ /* Seek to beginning of data portion of file */
+ if (fseek(file, prdev->file_offset_to_data[plane - first_plane],
+ SEEK_SET)) {
+ code = gs_note_error(gs_error_ioerror);
+ goto done;
+ }
+
+ if (plane >= 0)
+ gx_render_plane_init(&render_plane, (gx_device *)pdev, plane);
+ else
+ render_plane.index = -1;
+
+ /* Set up the buffer device. */
+ code = gdev_create_buf_device(crdev->buf_procs.create_buf_device,
+ &bdev, crdev->target, 0, &render_plane,
+ dev->memory, NULL);
+ if (code < 0)
+ goto done;
+
+ /*
+ * Iterate thru bands from top to bottom. As noted above, we
+ * do this an entire band at a time for efficiency.
+ */
+ for (y = dev->height - 1; y >= 0; y = band_base_line - 1) {
+ int band_height =
+ dev_proc(dev, get_band)(dev, y, &band_base_line);
+ int line;
+ gs_int_rect band_rect;
+
+ /* Set up the buffer device for this band. */
+ code = crdev->buf_procs.setup_buf_device
+ (bdev, raster_data, bmp_raster, NULL, 0, band_height,
+ band_height);
+ if (code < 0)
+ goto done;
+
+ /* Fill in the buffer with a band from the BMP file. */
+ /* Need to do this backward since BMP is top to bottom. */
+ for (line = band_height - 1; line >= 0; --line)
+ if (fread(raster_data + line * bmp_raster,
+ raster, 1, file) < 1 ||
+ fseek(file, padding, SEEK_CUR)
+ ) {
+ code = gs_note_error(gs_error_ioerror);
+ goto done;
+ }
+
+ /* Continue rendering on top of the existing bits. */
+ band_rect.p.x = 0;
+ band_rect.p.y = band_base_line;
+ band_rect.q.x = pdev->width;
+ band_rect.q.y = band_base_line + band_height;
+ if ((code = clist_render_rectangle((gx_device_clist *)pdev,
+ &band_rect, bdev,
+ &render_plane, false)) < 0)
+ goto done;
+
+ /* Rewind & write out the updated buffer. */
+ if (fseek(file, -bmp_raster * band_height, SEEK_CUR)) {
+ code = gs_note_error(gs_error_ioerror);
+ goto done;
+ }
+ for (line = band_height - 1; line >= 0; --line) {
+ if (fwrite(raster_data + line * bmp_raster,
+ bmp_raster, 1, file) < 1 ||
+ fseek(file, padding, SEEK_CUR)
+ ) {
+ code = gs_note_error(gs_error_ioerror);
+ goto done;
+ }
+ }
+ }
+ crdev->buf_procs.destroy_buf_device(bdev);
+ }
+ }
+
+ done:
+ prdev->buffered_page_exists = (code >= 0);
+ return code;
+}
+static int
+bmpa_reader_buffer_page(gx_device_printer *pdev, FILE *prn_stream,
+ int num_copies)
+{
+ return bmpa_reader_buffer_planes(pdev, prn_stream, num_copies, -1, -1,
+ gdev_prn_raster(pdev));
+}
+static int
+bmpa_cmyk_reader_buffer_page(gx_device_printer *pdev, FILE *prn_stream,
+ int num_copies)
+{
+ return bmpa_reader_buffer_planes(pdev, prn_stream, num_copies, 0, 3,
+ bmpa_cmyk_plane_raster(pdev));
+}
+
+/*------------ Procedures common to writer & renderer -------- */
+
+/* Compute space parameters */
+static void
+bmpa_get_space_params(const gx_device_printer *pdev,
+ gdev_prn_space_params *space_params)
+{
+ /* Plug params into device before opening it
+ *
+ * You ask "How did you come up with these #'s?" You asked, so...
+ *
+ * To answer clearly, let me begin by recapitulating how command list
+ * (clist) device memory allocation works in the non-overlapped case:
+ * When the device is opened, a buffer is allocated. How big? For
+ * starters, it must be >= PRN_MIN_BUFFER_SPACE, and as we'll see, must
+ * be sufficient to satisfy the rest of the band params. If you don't
+ * specify a size for it in space_params.band.BandBufferSpace, the open
+ * routine will use a heuristic where it tries to use PRN_BUFFER_SPACE,
+ * then works its way down by factors of 2 if that much memory isn't
+ * available.
+ *
+ * The device proceeds to divide the buffer into several parts: one of
+ * them is used for the same thing during writing & rasterizing; the
+ * other parts are redivided and used differently writing and
+ * rasterizing. The limiting factor dictating memory requirements is the
+ * rasterizer's render buffer. This buffer needs to be able to contain
+ * a pixmap that covers an entire band. Memory consumption is whatever
+ * is needed to hold N rows of data aligned on word boundaries, +
+ * sizeof(pointer) for each of N rows. Whatever is left over in the
+ * rasterized is allocated to a tile cache. You want to make sure that
+ * cache is at least 50KB.
+ *
+ * For example, take a 600 dpi b/w device at 8.5 x 11 inches. For the
+ * whole device, that's 6600 rows @ 638 bytes = ~4.2 MB total. If the
+ * device is divided into 100 bands, each band's rasterizer buffer is
+ * 62K. Add on a 50K tile cache, and you get a 112KB (+ add a little
+ * slop) total device buffer size.
+ *
+ * Now that we've covered the rasterizer, let's switch back to the
+ * writer. The writer must have a tile cache *exactly* the same size as
+ * the reader. This means that the space to divide up for the writer is
+ * equal is size to the rasterizer's band buffer. This space is divided
+ * into 2 sections: per-band bookeeping info and a command buffer. The
+ * bookeeping info currently uses ~72 bytes for each band. The rest is
+ * the command buffer.
+ *
+ * To continue the same 112KB example, we have 62KB to slice up.
+ * We need 72 bytes * 100 bands = 7.2KB, leaving a 55K command buffer.
+ *
+ * A larger command buffer has some performance (see gxclmem.c comments)
+ * advantages in the general case, but is critical in one special case:
+ * high-level images. Whenever possible, images are transmitted across
+ * the band buffer in their original resolution and bits/pixel. The
+ * alternative fallback behavior can be very slow. Here, the relevant
+ * restriction is that at least one entire source image row must fit
+ * into the command buffer. This means that, in our example, an RGB
+ * source image would have to be <= 18K pixels wide. If the image is
+ * sampled at the same resolution as the hardware (600 dpi), that means
+ * the row would be limited to a very reasonable 30 inches. However, if
+ * the source image is sampled at 2400 dpi, that limit is only 7.5
+ * inches. The situation gets worse as bands get smaller, but the
+ * implementor must decide on the tradeoff point.
+ *
+ * The moral of the story is that you should never make a band
+ * so small that its buffer limits the command buffer excessively.
+ * Again, Max image row bytes = band buffer size - # bands * 72.
+ *
+ * In the overlapped case, everything is exactly as above, except that
+ * two identical devices, each with an identical buffer, are allocated:
+ * one for the writer, and one for the rasterizer. Because it's critical
+ * to allocate identical buffers, I *strongly* recommend setting these
+ * params in the writer's open routine:
+ * space_params.band.BandBufferSpace, .BandWidth and .BandHeight. If
+ * you don't force these values to a known value, the memory allocation
+ * heuristic may not come to the same result for both copies of the
+ * device, since the first allocation will diminish the amount of free
+ * memory.
+ *
+ * There is room for an important optimization here: allocate the
+ * writer's space with enough memory for a generous command buffer, but
+ * allocate the reader with only enough memory for a band rasterization
+ * buffer and the tile cache. To do this, observe that the space_params
+ * struct has two sizes: BufferSpace vs. BandBufferSpace. To start,
+ * BandBufferSpace is always <= BufferSpace. On the reader side,
+ * BandBufferSpace is divided between the tile cache and the rendering
+ * buffer -- that's all the memory that's needed to rasterize. On the
+ * writer's side, BandBufferSpace is divided the same way: the tile
+ * cache (which must be identical to the reader's) is carved out, and
+ * the space that would have been used for a rasterizing buffer is used
+ * as a command buffer. However, you can further increase the cmd buf
+ * further by setting BufferSize (not BandBufferSize) to a higher number
+ * than BandBufferSize. In that case, the command buffer is increased by
+ * the difference (BufferSize - BandBufferSize). There is logic in the
+ * memory allocation for printers that will automatically use BufferSize
+ * for writers (or non-async printers), and BandBufferSize for readers.
+ *
+ * Note: per the comments in gxclmem.c, the banding logic will perform
+ * better with 1MB or better for the command list.
+ */
+
+ /* This will give us a very "ungenerous" buffer. */
+ /* Here, my arbitrary rule for min image row is: twice the dest width */
+ /* in full CMYK. */
+ ulong render_space = 0;
+ ulong writer_space;
+ const int tile_cache_space = 50 * 1024;
+ const int min_image_rows = 2;
+ int min_row_space =
+ min_image_rows * ( 4 * ( pdev->width + sizeof(int) - 1 ) );
+ int min_band_height = max(1, pdev->height / 100); /* make bands >= 1% of total */
+
+ space_params->band.BandWidth = pdev->width;
+ space_params->band.BandHeight = min_band_height;
+
+ gdev_mem_data_size( (const gx_device_memory *)pdev, space_params->band.BandWidth,
+ space_params->band.BandHeight, &render_space );
+ /* need to include minimal writer requirements to satisfy rasterizer init */
+ writer_space = /* add 5K slop for good measure */
+ 5000 + (72 + 8) * ( (pdev->height / space_params->band.BandHeight) + 1 );
+ space_params->band.BandBufferSpace =
+ max(render_space, writer_space) + tile_cache_space;
+ space_params->BufferSpace =
+ max(render_space, writer_space + min_row_space) + tile_cache_space;
+ /**************** HACK HACK HACK ****************/
+ /* Override this computation to force reader & writer to match */
+ space_params->BufferSpace = space_params->band.BandBufferSpace;
+}
+
+/* Get device parameters. */
+static int
+bmpa_get_params(gx_device * pdev, gs_param_list * plist)
+{
+ gx_device_async * const bdev = (gx_device_async *)pdev;
+
+ return gdev_prn_get_params_planar(pdev, plist, &bdev->UsePlanarBuffer);
+}
+
+/* Put device parameters. */
+/* IMPORTANT: async drivers must NOT CLOSE the device while doing put_params.*/
+/* IMPORTANT: async drivers must NOT CLOSE the device while doing put_params.*/
+/* IMPORTANT: async drivers must NOT CLOSE the device while doing put_params.*/
+/* IMPORTANT: async drivers must NOT CLOSE the device while doing put_params.*/
+static int
+bmpa_put_params(gx_device *pdev, gs_param_list *plist)
+{
+ /*
+ * This driver does nothing interesting except cascade down to
+ * gdev_prn_put_params_planar, which is something it would have to do
+ * even if it did do something interesting here.
+ *
+ * Note that gdev_prn_put_params[_planar] does not close the device.
+ */
+ gx_device_async * const bdev = (gx_device_async *)pdev;
+
+ return gdev_prn_put_params_planar(pdev, plist, &bdev->UsePlanarBuffer);
+}
+
+/* Get hardware-detected parameters. */
+/* This proc defines a only one param: a useless value for testing */
+static int
+bmpa_get_hardware_params(gx_device *dev, gs_param_list *plist)
+{
+ static const char *const test_value = "Test value";
+ static const char *const test_name = "TestValue";
+ int code = 0;
+
+ if ( param_requested(plist, test_name) ) {
+ gs_param_string param_str;
+
+ param_string_from_string(param_str, test_value); /* value must be persistent to use this macro */
+ code = param_write_string(plist, test_name, &param_str);
+ }
+ return code;
+}
diff --git a/devices/gdevbmpc.c b/devices/gdevbmpc.c
new file mode 100644
index 000000000..0970a4aba
--- /dev/null
+++ b/devices/gdevbmpc.c
@@ -0,0 +1,235 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+/* .BMP file format driver utilities */
+#include "gdevprn.h"
+#include "gdevbmp.h"
+
+/*
+ * Define BMP file format structures.
+ * All multi-byte quantities are stored LSB-first!
+ */
+
+typedef ushort word;
+#if arch_sizeof_int == 4
+typedef uint dword;
+#else
+# if arch_sizeof_long == 4
+typedef ulong dword;
+# endif
+#endif
+#if arch_is_big_endian
+# define BMP_ASSIGN_WORD(a,v) a = ((v) >> 8) + ((v) << 8)
+# define BMP_ASSIGN_DWORD(a,v)\
+ a = ((v) >> 24) + (((v) >> 8) & 0xff00L) +\
+ (((dword)(v) << 8) & 0xff0000L) + ((dword)(v) << 24)
+#else
+# define BMP_ASSIGN_WORD(a,v) a = (v)
+# define BMP_ASSIGN_DWORD(a,v) a = (v)
+#endif
+
+typedef struct bmp_file_header_s {
+
+ /* BITMAPFILEHEADER */
+
+ /*
+ * This structure actually begins with two bytes
+ * containing the characters 'BM', but we must omit them,
+ * because some compilers would insert padding to force
+ * the size member to a 32- or 64-bit boundary.
+ */
+
+ /*byte typeB, typeM; *//* always 'BM' */
+ dword size; /* total size of file */
+ word reserved1;
+ word reserved2;
+ dword offBits; /* offset of bits from start of file */
+
+} bmp_file_header;
+
+#define sizeof_bmp_file_header (2 + sizeof(bmp_file_header))
+
+typedef struct bmp_info_header_s {
+
+ /* BITMAPINFOHEADER */
+
+ dword size; /* size of info header in bytes */
+ dword width; /* width in pixels */
+ dword height; /* height in pixels */
+ word planes; /* # of planes, always 1 */
+ word bitCount; /* bits per pixel */
+ dword compression; /* compression scheme, always 0 */
+ dword sizeImage; /* size of bits */
+ dword xPelsPerMeter; /* X pixels per meter */
+ dword yPelsPerMeter; /* Y pixels per meter */
+ dword clrUsed; /* # of colors used */
+ dword clrImportant; /* # of important colors */
+
+ /* This is followed by (1 << bitCount) bmp_quad structures, */
+ /* unless bitCount == 24. */
+
+} bmp_info_header;
+
+typedef struct bmp_quad_s {
+
+ /* RGBQUAD */
+
+ byte blue, green, red, reserved;
+
+} bmp_quad;
+
+/* Write the BMP file header. */
+static int
+write_bmp_depth_header(gx_device_printer *pdev, FILE *file, int depth,
+ const byte *palette /* [4 << depth] */,
+ int raster)
+{
+ /* BMP scan lines are padded to 32 bits. */
+ ulong bmp_raster = raster + (-raster & 3);
+ int height = pdev->height;
+ int quads = (depth <= 8 ? sizeof(bmp_quad) << depth : 0);
+
+ /* Write the file header. */
+
+ fputc('B', file);
+ fputc('M', file);
+ {
+ bmp_file_header fhdr;
+
+ BMP_ASSIGN_DWORD(fhdr.size,
+ sizeof_bmp_file_header +
+ sizeof(bmp_info_header) + quads +
+ bmp_raster * height);
+ BMP_ASSIGN_WORD(fhdr.reserved1, 0);
+ BMP_ASSIGN_WORD(fhdr.reserved2, 0);
+ BMP_ASSIGN_DWORD(fhdr.offBits,
+ sizeof_bmp_file_header +
+ sizeof(bmp_info_header) + quads);
+ if (fwrite((const char *)&fhdr, 1, sizeof(fhdr), file) != sizeof(fhdr))
+ return_error(gs_error_ioerror);
+ }
+
+ /* Write the info header. */
+
+ {
+ bmp_info_header ihdr;
+
+ BMP_ASSIGN_DWORD(ihdr.size, sizeof(ihdr));
+ BMP_ASSIGN_DWORD(ihdr.width, pdev->width);
+ BMP_ASSIGN_DWORD(ihdr.height, height);
+ BMP_ASSIGN_WORD(ihdr.planes, 1);
+ BMP_ASSIGN_WORD(ihdr.bitCount, depth);
+ BMP_ASSIGN_DWORD(ihdr.compression, 0);
+ BMP_ASSIGN_DWORD(ihdr.sizeImage, bmp_raster * height);
+ /*
+ * Earlier versions of this driver set the PelsPerMeter values
+ * to zero. At a user's request, we now set them correctly,
+ * but we suspect this will cause problems other places.
+ */
+#define INCHES_PER_METER (100 /*cm/meter*/ / 2.54 /*cm/inch*/)
+ BMP_ASSIGN_DWORD(ihdr.xPelsPerMeter,
+ (dword)(pdev->x_pixels_per_inch * INCHES_PER_METER + 0.5));
+ BMP_ASSIGN_DWORD(ihdr.yPelsPerMeter,
+ (dword)(pdev->y_pixels_per_inch * INCHES_PER_METER + 0.5));
+#undef INCHES_PER_METER
+ BMP_ASSIGN_DWORD(ihdr.clrUsed, 0);
+ BMP_ASSIGN_DWORD(ihdr.clrImportant, 0);
+ if (fwrite((const char *)&ihdr, 1, sizeof(ihdr), file) != sizeof(ihdr))
+ return_error(gs_error_ioerror);
+ }
+
+ /* Write the palette. */
+
+ if (depth <= 8)
+ fwrite(palette, sizeof(bmp_quad), 1 << depth, file);
+
+ return 0;
+}
+
+/* Write the BMP file header. */
+int
+write_bmp_header(gx_device_printer *pdev, FILE *file)
+{
+ int depth = pdev->color_info.depth;
+ bmp_quad palette[256];
+
+ if (depth <= 8) {
+ int i;
+ gx_color_value rgb[3];
+ bmp_quad q;
+
+ q.reserved = 0;
+ for (i = 0; i != 1 << depth; i++) {
+ /* Note that the use of map_color_rgb is deprecated in
+ favor of decode_color. This should work, though, because
+ backwards compatibility is preserved. */
+ (*dev_proc(pdev, map_color_rgb))((gx_device *)pdev,
+ (gx_color_index)i, rgb);
+ q.red = gx_color_value_to_byte(rgb[0]);
+ q.green = gx_color_value_to_byte(rgb[1]);
+ q.blue = gx_color_value_to_byte(rgb[2]);
+ palette[i] = q;
+ }
+ }
+ return write_bmp_depth_header(pdev, file, depth, (const byte *)palette,
+ gdev_prn_raster(pdev));
+}
+
+/* Write a BMP header for separated CMYK output. */
+int
+write_bmp_separated_header(gx_device_printer *pdev, FILE *file)
+{
+ int depth = pdev->color_info.depth;
+ int plane_depth = depth / 4;
+ bmp_quad palette[256];
+ bmp_quad q;
+ int i;
+
+ q.reserved = 0;
+ for (i = 0; i < 1 << plane_depth; i++) {
+ q.red = q.green = q.blue =
+ 255 - i * 255 / ((1 << plane_depth) - 1);
+ palette[i] = q;
+ }
+ return write_bmp_depth_header(pdev, file, plane_depth,
+ (const byte *)palette,
+ (pdev->width*plane_depth + 7) >> 3);
+}
+
+/* 24-bit color mappers (taken from gdevmem2.c). */
+/* Note that Windows expects RGB values in the order B,G,R. */
+
+/* Map a r-g-b color to a color index. */
+gx_color_index
+bmp_map_16m_rgb_color(gx_device * dev, const gx_color_value cv[])
+{
+
+ gx_color_value r, g, b;
+ r = cv[0]; g = cv[1]; b = cv[2];
+ return gx_color_value_to_byte(r) +
+ ((uint) gx_color_value_to_byte(g) << 8) +
+ ((ulong) gx_color_value_to_byte(b) << 16);
+}
+
+/* Map a color index to a r-g-b color. */
+int
+bmp_map_16m_color_rgb(gx_device * dev, gx_color_index color,
+ gx_color_value prgb[3])
+{
+ prgb[2] = gx_color_value_from_byte(color >> 16);
+ prgb[1] = gx_color_value_from_byte((color >> 8) & 0xff);
+ prgb[0] = gx_color_value_from_byte(color & 0xff);
+ return 0;
+}
diff --git a/devices/gdevccr.c b/devices/gdevccr.c
new file mode 100644
index 000000000..2f33680d7
--- /dev/null
+++ b/devices/gdevccr.c
@@ -0,0 +1,289 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+/* CalComp Raster Format driver */
+#include "gdevprn.h"
+
+/*
+ * Please contact the author, Ernst Muellner (ernst.muellner@oenzl.siemens.de),
+ * if you have any questions about this driver.
+ */
+
+#define CCFILESTART(p) putc(0x02, p)
+#define CCFILEEND(p) putc(0x04, p)
+#define CCNEWPASS(p) putc(0x0c, p)
+#define CCEMPTYLINE(p) putc(0x0a, p)
+#define CCLINESTART(len,p) do{ putc(0x1b,p);putc(0x4b,p);putc(len>>8,p); \
+ putc(len&0xff,p);} while(0)
+
+#define CPASS (0)
+#define MPASS (1)
+#define YPASS (2)
+#define NPASS (3)
+
+typedef struct cmyrow_s
+ {
+ int current;
+ int _cmylen[NPASS];
+ int is_used;
+ char cname[4];
+ char mname[4];
+ char yname[4];
+ unsigned char *_cmybuf[NPASS];
+ } cmyrow;
+
+#define clen _cmylen[CPASS]
+#define mlen _cmylen[MPASS]
+#define ylen _cmylen[YPASS]
+#define cmylen _cmylen
+
+#define cbuf _cmybuf[CPASS]
+#define mbuf _cmybuf[MPASS]
+#define ybuf _cmybuf[YPASS]
+#define cmybuf _cmybuf
+
+static int alloc_rb( gs_memory_t *mem, cmyrow **rb, int rows);
+static int alloc_line( gs_memory_t *mem, cmyrow *row, int cols);
+static void add_cmy8(cmyrow *rb, char c, char m, char y);
+static void write_cpass(cmyrow *buf, int rows, int pass, FILE * pstream);
+static void free_rb_line( gs_memory_t *mem, cmyrow *rbuf, int rows, int cols);
+
+struct gx_device_ccr_s {
+ gx_device_common;
+ gx_prn_device_common;
+ /* optional parameters */
+};
+typedef struct gx_device_ccr_s gx_device_ccr;
+
+#define bdev ((gx_device_ccr *)pdev)
+
+/* ------ The device descriptors ------ */
+
+/*
+ * Default X and Y resolution.
+ */
+#define X_DPI 300
+#define Y_DPI 300
+#define DEFAULT_WIDTH_10THS_A3 117
+#define DEFAULT_HEIGHT_10THS_A3 165
+
+/* Macro for generating ccr device descriptors. */
+#define ccr_prn_device(procs, dev_name, margin, num_comp, depth, max_gray, max_rgb, print_page)\
+{ prn_device_body(gx_device_ccr, procs, dev_name,\
+ DEFAULT_WIDTH_10THS_A3, DEFAULT_HEIGHT_10THS_A3, X_DPI, Y_DPI,\
+ margin, margin, margin, margin,\
+ num_comp, depth, max_gray, max_rgb, max_gray + 1, max_rgb + 1,\
+ print_page)\
+}
+
+/* For CCR, we need our own color mapping procedures. */
+static dev_proc_map_rgb_color(ccr_map_rgb_color);
+static dev_proc_map_color_rgb(ccr_map_color_rgb);
+
+/* And of course we need our own print-page routine. */
+static dev_proc_print_page(ccr_print_page);
+
+/* The device procedures */
+/* Since the print_page doesn't alter the device, this device can print in the background */
+static gx_device_procs ccr_procs =
+ prn_color_procs(gdev_prn_open, gdev_prn_bg_output_page, gdev_prn_close,
+ ccr_map_rgb_color, ccr_map_color_rgb);
+
+/* The device descriptors themselves */
+gx_device_ccr far_data gs_ccr_device =
+ ccr_prn_device(ccr_procs, "ccr", 0.2, 3, 8, 1, 1,
+ ccr_print_page);
+
+/* ------ Color mapping routines ------ */
+/* map an rgb color to a ccr cmy bitmap */
+static gx_color_index
+ccr_map_rgb_color(gx_device *pdev, const ushort cv[])
+{
+ ushort r, g, b;
+ register int shift = gx_color_value_bits - 1;
+
+ r = cv[0]; g = cv[1]; b = cv[2];
+ r>>=shift;
+ g>>=shift;
+ b>>=shift;
+
+ r=1-r; g=1-g; b=1-b; /* rgb -> cmy */
+ return r<<2 | g<<1 | b;
+}
+
+/* map an ccr cmy bitmap to a rgb color */
+static int
+ccr_map_color_rgb(gx_device *pdev, gx_color_index color, ushort rgb[3])
+{
+ rgb[2]=(1-(color >>2))*gx_max_color_value; /* r */
+ rgb[1]=(1-( (color & 0x2) >> 1))*gx_max_color_value; /* g */
+ rgb[0]=(1-(color & 0x1))*gx_max_color_value; /* b */
+ return 0;
+}
+/* ------ print page routine ------ */
+
+static int
+ccr_print_page(gx_device_printer *pdev, FILE *pstream)
+{
+ cmyrow *linebuf;
+ int line_size = gdev_prn_raster((gx_device *)pdev);
+ int pixnum = pdev->width;
+ int lnum = pdev->height;
+ int l, p, b;
+ int cmy, c, m, y;
+ byte *in;
+ byte *data;
+
+ if((in = (byte *)gs_malloc(pdev->memory, line_size, 1, "gsline")) == NULL)
+ return_error(gs_error_VMerror);
+
+ if(alloc_rb( pdev->memory, &linebuf, lnum))
+ {
+ gs_free(pdev->memory, in, line_size, 1, "gsline");
+ return_error(gs_error_VMerror);
+ }
+
+ for ( l = 0; l < lnum; l++ )
+ { gdev_prn_get_bits(pdev, l, in, &data);
+ if(alloc_line(pdev->memory, &linebuf[l], pixnum))
+ {
+ gs_free(pdev->memory, in, line_size, 1, "gsline");
+ free_rb_line( pdev->memory, linebuf, lnum, pixnum );
+ return_error(gs_error_VMerror);
+ }
+ for ( p=0; p< pixnum; p+=8)
+ {
+ c=m=y=0;
+ for(b=0; b<8; b++)
+ {
+ c <<= 1; m <<= 1; y <<= 1;
+ if(p+b < pixnum)
+ cmy = *data;
+ else
+ cmy = 0;
+
+ c |= cmy>>2;
+ m |= (cmy>>1) & 0x1;
+ y |= cmy & 0x1;
+ data++;
+ }
+ add_cmy8(&linebuf[l], c, m, y);
+ }
+ }
+CCFILESTART(pstream);
+write_cpass(linebuf, lnum, YPASS, pstream);
+CCNEWPASS(pstream);
+write_cpass(linebuf, lnum, MPASS, pstream);
+CCNEWPASS(pstream);
+write_cpass(linebuf, lnum, CPASS, pstream);
+CCFILEEND(pstream);
+
+/* clean up */
+gs_free(pdev->memory, in, line_size, 1, "gsline");
+free_rb_line( pdev->memory, linebuf, lnum, pixnum );
+return 0;
+}
+
+/* ------ Internal routines ------ */
+
+static int alloc_rb( gs_memory_t *mem, cmyrow **rb, int rows)
+ {
+ *rb = (cmyrow*) gs_malloc(mem, rows, sizeof(cmyrow), "rb");
+ if( *rb == 0)
+ return_error(gs_error_VMerror);
+ else
+ {
+ int r;
+ for(r=0; r<rows; r++)
+ {
+ gs_sprintf((*rb)[r].cname, "C%02x", r);
+ gs_sprintf((*rb)[r].mname, "M%02x", r);
+ gs_sprintf((*rb)[r].yname, "Y%02x", r);
+ (*rb)[r].is_used=0;
+ }
+ return 0;
+ }
+}
+
+static int alloc_line( gs_memory_t *mem, cmyrow *row, int cols)
+{
+ int suc;
+ suc=((row->cbuf = (unsigned char *) gs_malloc(mem, cols,1, row->cname)) &&
+ (row->mbuf = (unsigned char *) gs_malloc(mem, cols,1, row->mname)) &&
+ (row->ybuf = (unsigned char *) gs_malloc(mem, cols,1, row->yname)));
+ if(suc == 0)
+ {
+ gs_free(mem, row->cbuf, cols,1, row->cname);
+ gs_free(mem, row->mbuf, cols,1, row->mname);
+ gs_free(mem, row->ybuf, cols,1, row->yname);
+
+ return_error(gs_error_VMerror);
+ }
+ row->is_used = 1;
+ row->current = row->clen = row->mlen = row->ylen = 0;
+ return 0;
+}
+
+static void add_cmy8(cmyrow *rb, char c, char m, char y)
+{
+ int cur=rb->current;
+ rb->cbuf[cur]=c;
+ if(c)
+ rb->clen=cur+1;
+ rb->mbuf[cur]=m;
+ if(m)
+ rb->mlen=cur+1;
+ rb->ybuf[cur]=y;
+ if(y)
+ rb->ylen=cur+1;
+ rb->current++;
+ return;
+}
+
+static void write_cpass(cmyrow *buf, int rows, int pass, FILE * pstream)
+{
+ int row, len;
+ for(row=0; row<rows; row++)
+ {
+ len=buf[row].cmylen[pass];
+ if(len == 0)
+ CCEMPTYLINE(pstream);
+ else
+ {
+ CCLINESTART(len,pstream);
+ fwrite( buf[row].cmybuf[pass], len, 1, pstream);
+ }
+ }
+ return;
+}
+
+static void free_rb_line( gs_memory_t *mem, cmyrow *rbuf, int rows, int cols)
+{
+ int i;
+ for(i=0; i<rows; i++)
+ {
+ if(rbuf[i].is_used)
+ {
+ gs_free(mem, rbuf[i].cbuf, cols, 1, rbuf[i].cname);
+ gs_free(mem, rbuf[i].mbuf, cols, 1, rbuf[i].mname);
+ gs_free(mem, rbuf[i].ybuf, cols, 1, rbuf[i].yname);
+ rbuf[i].is_used = 0;
+ }
+ else
+ break;
+ }
+ gs_free( mem, rbuf, rows, sizeof(cmyrow), "rb");
+ return;
+}
diff --git a/devices/gdevcdj.c b/devices/gdevcdj.c
new file mode 100644
index 000000000..ad47cc097
--- /dev/null
+++ b/devices/gdevcdj.c
@@ -0,0 +1,3930 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+/* HP and Canon colour printer drivers */
+
+/*
+ * HP DeskJet 505J Support (dj505j.dev)
+ * -- taken from dj505j driver for Ghostscript 2.6.1 by Kazunori Asayama
+ * NEC PICTY180 (PC-PR101J/180) Support (picty180.dev)
+ * HP LaserJet 4V/4LJ Pro dither Suport (lj4dithp.dev)
+ *
+ * Norihito Ohmori <ohmori@p.chiba-u.ac.jp>
+ * April 15, 1999
+ */
+
+/****************************************************************
+ * The code in this file was contributed by the authors whose names and/or
+ * e-mail addresses appear below: Aladdin Enterprises takes no
+ * responsibility for it. In the past, we have tried to keep it working,
+ * but too many users have made too many "improvements" without regard to
+ * the overall structure; the last three "improvements" required me to spend
+ * several hours fixing each one so that the code worked again at all. For
+ * this reason, no further changes to this file will be accepted. We are
+ * planning eventually to get these drivers rewritten from scratch.
+ *
+ * L. Peter Deutsch
+ * Aladdin Enterprises
+ * February 28, 1996
+ ****************************************************************/
+
+/*
+ * Change history:
+ * 2000-08-20 Jonathan Kamens <jik@kamens.brookline.ma.us>:
+ * change to support printers with different X and Y resolution.
+ */
+
+/*
+ * Important compilation notes (YA).
+ *
+ * You may also try the cdj550cmyk driver after having defined
+ * USE_CDJ550_CMYK and added the needed definition in devs.mak. Not tried!
+ * (I have a BJC!) Also note that modes descriptions of CMYK printing
+ * is done under the BJC section of devices.doc.
+ *
+ * CMYK to RGB conversion is made a la GhostScript unless you define
+ * the preprocessor symbol USE_ADOBE_CMYK_RGB.
+ *
+ * Ghostscript: R = (1.0 - C) * (1.0 - K)
+ * Adobe: R = 1.0 - min(1.0, C + K)
+ *
+ * (and similarly for G and B). Ghostscript claims its method achieves
+ * better results.
+ *
+ * For the BJC drivers, define BJC_DEFAULT_CENTEREDAREA if you want to
+ * have the same top and bottom margins (default to use the tallest
+ * imageable area available, usually with a top margin smaller than
+ * the bottom one). Defining USE_RECOMMENDED_MARGINS has the same
+ * effect and also sets these margins to 12.4 mm. Other compilation
+ * defines are explained in devices.doc.
+ *
+ * You can also define BJC_INIT_800_AS_600 to not use BJC-800-specific code
+ * in the page initialization sequence (normally not useful to you at all,
+ * just for my debugging of the driver margins).
+ *
+ */
+
+#include "std.h" /* to stop stdlib.h redefining types */
+#include <stdlib.h> /* for rand() */
+#include "gdevprn.h"
+#include "gdevpcl.h"
+#include "gsparam.h"
+#include "gsstate.h"
+
+/* Conversion stuff. */
+#include "gxlum.h"
+
+/* Canon stuff */
+
+#include "gdevbjc.h"
+
+/***
+ *** This file contains multiple drivers. The main body of code, and all
+ *** but the DesignJet driver, were contributed by George Cameron;
+ *** please contact g.cameron@biomed.abdn.ac.uk if you have questions.
+ * 1 - cdj500: HP DeskJet 500C
+ * 2 - cdj550: HP DeskJet 550C
+ * 3 - pjxl300: HP PaintJet XL300
+ * 4 - pj: HP PaintJet
+ * 5 - pjxl: HP PaintJet XL
+ * 6 - declj250: DEC LJ250
+ *** The DesignJet 650C driver was contributed by Koert Zeilstra;
+ *** please contact koert@zen.cais.com if you have questions.
+ * 7 - dnj650c HP DesignJet 650C
+ *** The LaserJet 4 driver with dithering was contributed by Eckhard
+ *** Rueggeberg; please contact eckhard@ts.go.dlr.de if you have questions.
+ * 8 - lj4dith: HP LaserJet 4 with dithering
+ *** The ESC/P driver (for Epson ESC/P compatible printers) was written by
+ *** Yoshio Kuniyoshi <yoshio@nak.math.keio.ac.jp>, but is not maintained at
+ *** the moment.
+ * 9 - esc/p: Epson ESC/P-compatible printers
+ *** The BJC600 driver (which also works for BJC4000) was written first
+ *** by Yoshio Kuniyoshi <yoshio@nak.math.keio.ac.jp> and later modified by
+ *** Yves Arrouye <yves.arrouye@usa.net>. The current driver has been
+ *** completely rewritten by me (YA) for good color handling.
+ * 10 - bjc600: BJC 600//4000 printers
+ *** The BJC800 driver is based on the bjc600 one. By YA too.
+ * 11 - bjc800: BJC 800 printer
+ * 12 - dj505j: HP DeskJet 505J
+ * 13 - pixty180: NEC PICTY 180 (PC-PR101J/180)
+ ***/
+
+/*
+ * All of the HP-like drivers have 8-bit (monochrome), 16-bit and 24-bit
+ * (colour) and for the DJ 550C 32-bit, (colour, cmyk mode)
+ * options in addition to the usual 1-bit and 3-bit modes
+ * It is also possible to set various printer-specific parameters
+ * from the gs command line, eg.
+ *
+ * gs -sDEVICE=cdj550 -dBitsPerPixel=16 -dDepletion=1 -dShingling=2 tiger.eps
+ *
+ * Please consult the appropriate section in the devices.doc file for
+ * further details on all these drivers.
+ *
+ * All of the BJC-like drivers have 1-bit and 8-bit monochrome modes, 8-bit,
+ * 16-bit, 24-bit and 32-bit colour cmyk mode (the 8-bit monochrome mode
+ * is called "4-bit". If you want to add a CMYK printer, look at the
+ * bjc6000/bjc800 devices declarations and initialization.
+ *
+ * If you want to support different color components for the same depth
+ * on a non-CMYK printer, look how this is done for CMYK printers in
+ * cdj_set_bpp.
+ *
+ */
+
+/*
+ * This taken from gsdparam.c. I hope it will be useable directly some day.
+ *
+ */
+
+#define BEGIN_ARRAY_PARAM(pread, pname, pa, psize, e)\
+ switch ( ncode = pread(plist, (oname = pname), &pa) )\
+ {\
+ case 0:\
+ if ( pa.size != psize )\
+ code = gs_error_rangecheck;\
+ else {
+/* The body of the processing code goes here. */
+/* If it succeeds, it should do a 'break'; */
+/* if it fails, it should set ecode and fall through. */
+#define END_PARAM(pa, e)\
+ }\
+ goto e;\
+ default:\
+ code = ncode;\
+e: param_signal_error(plist, oname, code);\
+ case 1:\
+ pa.data = 0; /* mark as not filled */\
+ }
+
+static int cdj_param_check_bytes(gs_param_list *, gs_param_name, const byte *, uint, bool);
+static int cdj_param_check_float(gs_param_list *, gs_param_name, double, bool);
+#define cdj_param_check_string(plist, pname, str, is_defined)\
+ cdj_param_check_bytes(plist, pname, (const byte *)(str), strlen(str),\
+ is_defined)
+
+/*
+ * Drivers stuff.
+ *
+ */
+
+#define DESKJET_PRINT_LIMIT 0.04 /* 'real' top margin? */
+#define PAINTJET_PRINT_LIMIT 0.0 /* This is a guess.. */
+#define ESC_P_PRINT_LIMIT 0.335
+
+/* Margins are left, bottom, right, top. */
+#define DESKJET_MARGINS_LETTER (float)0.25, (float)0.50, (float)0.25, (float)0.167
+#define DESKJET_MARGINS_A4 (float)0.125, (float)0.50, (float)0.143, (float)0.167
+#define DESKJET_505J_MARGINS (float)0.125, (float)0.50, (float)0.125, (float)0.167
+#define DESKJET_505J_MARGINS_COLOR (float)0.125, (float)0.665, (float)0.125, (float)0.167
+#define LJET4_MARGINS (float)0.26, (float)0.0, (float)0.0, (float)0.0
+/* The PaintJet and DesignJet seem to have the same margins */
+/* regardless of paper size. */
+#define PAINTJET_MARGINS (float)0.167, (float)0.167, (float)0.167, (float)0.167
+#define DESIGNJET_MARGINS (float)0.167, (float)0.167, (float)0.167, (float)0.167
+
+/*
+ * With ESC/P commands, BJC-600 can print no more than 8 inches width.
+ * So it can't use full width of letter size paper. Since non printable
+ * left side area is 0.134 inch, we set up margins as follows.
+ *
+ * Note to readers: the bjc drivers series do *not* use ESC/P commands
+ * but raster ops. Configuration of these drivers can be done through
+ * the gdevbjc.h file.
+ *
+ */
+#define ESC_P_MARGINS_LETTER (float)0.134, (float)(0.276+0.2), \
+ (float)(0.366+0.01), (float)0.335
+#define ESC_P_MARGINS_A4 (float)0.134, (float)(0.276+0.2), \
+ (float)(0.166+0.01), (float)0.335
+
+/* Define bits-per-pixel for generic drivers - default is 24-bit mode */
+#ifndef BITSPERPIXEL
+# define BITSPERPIXEL 24
+#endif
+
+/*
+ * The following use of size_of rather than sizeof is required to work
+ * around a bug in Microsoft Visual C++ 5.0, which considers the THRESHOLD
+ * value (128 << SHIFT) to be unsigned because SHIFT is unsigned (because
+ * sizeof() is unsigned).
+ */
+#define W size_of(word)
+#define I size_of(int)
+
+#define invert_word(v)\
+ ((v) >> 24) + (((v) >> 8) & 0xff00L) +\
+ (((word)(v) << 8) & 0xff0000L) + ((word)(v) << 24)
+
+/* Printer types */
+#define DJ500C 0
+#define DJ550C 1
+#define DJ505J 2
+#define PJXL300 3
+#define PJ180 4
+#define PJXL180 5
+#define DECLJ250 6
+#define DNJ650C 7
+#define LJ4DITH 8
+#define ESC_P 9
+#define BJC600 10
+#define BJC800 11
+
+/* No. of ink jets (used to minimise head movements) */
+#define HEAD_ROWS_MONO 50
+#define HEAD_ROWS_COLOUR 16
+
+/* Colour mapping procedures */
+static dev_proc_map_cmyk_color (gdev_cmyk_map_cmyk_color);
+static dev_proc_map_rgb_color (gdev_cmyk_map_rgb_color);
+
+static dev_proc_map_rgb_color (gdev_pcl_map_rgb_color);
+static dev_proc_map_color_rgb (gdev_pcl_map_color_rgb);
+static dev_proc_decode_color (gdev_cmyk_map_color_cmyk);
+
+/* Print-page, parameters and miscellaneous procedures */
+static dev_proc_open_device(dj500c_open);
+static dev_proc_open_device(dj550c_open);
+static dev_proc_open_device(dj505j_open);
+static dev_proc_open_device(dnj650c_open);
+static dev_proc_open_device(lj4dith_open);
+static dev_proc_open_device(pj_open);
+static dev_proc_open_device(pjxl_open);
+static dev_proc_open_device(pjxl300_open);
+static dev_proc_open_device(escp_open);
+static dev_proc_open_device(bjc_open);
+
+static dev_proc_print_page(declj250_print_page);
+static dev_proc_print_page(dj500c_print_page);
+static dev_proc_print_page(dj550c_print_page);
+static dev_proc_print_page(dj505j_print_page);
+static dev_proc_print_page(picty180_print_page);
+static dev_proc_print_page(dnj650c_print_page);
+static dev_proc_print_page(lj4dith_print_page);
+static dev_proc_print_page(lj4dithp_print_page);
+static dev_proc_print_page(pj_print_page);
+static dev_proc_print_page(pjxl_print_page);
+static dev_proc_print_page(pjxl300_print_page);
+static dev_proc_print_page(escp_print_page);
+static dev_proc_print_page(bjc_print_page);
+
+static dev_proc_get_params(cdj_get_params);
+static dev_proc_get_params(pjxl_get_params);
+static dev_proc_get_params(bjc_get_params);
+#define ep_get_params cdj_get_params
+
+static dev_proc_put_params(cdj_put_params);
+static dev_proc_put_params(pj_put_params);
+static dev_proc_put_params(pjxl_put_params);
+static dev_proc_put_params(bjc_put_params);
+#define ep_put_params cdj_put_params
+
+/* The device descriptors */
+
+#define gx_prn_colour_device_common \
+ gx_prn_device_common; \
+ short cmyk; /* 0: not CMYK-capable, > 0: printing CMYK, */ \
+ /* < 0 : CMYK-capable, not printing CMYK */ \
+ uint default_depth; /* Used only for CMYK-capable printers now. */ \
+ uint correction
+
+typedef struct gx_device_cdj_s gx_device_cdj;
+struct gx_device_cdj_s {
+ gx_device_common;
+ gx_prn_colour_device_common;
+ int shingling; /* Interlaced, multi-pass printing */
+ int depletion; /* 'Intelligent' dot-removal */
+};
+
+typedef struct gx_device_pjxl_s gx_device_pjxl;
+struct gx_device_pjxl_s {
+ gx_device_common;
+ gx_prn_colour_device_common;
+ int printqual; /* Mechanical print quality */
+ int rendertype; /* Driver or printer dithering control */
+};
+
+typedef struct gx_device_hp_s gx_device_hp;
+struct gx_device_hp_s {
+ gx_device_common;
+ gx_prn_colour_device_common;
+};
+
+typedef struct gx_device_hp_s gx_device_pj;
+
+typedef struct gx_device_bjc600_s gx_device_bjc600;
+typedef struct gx_device_bjc800_s gx_device_bjc800;
+
+typedef struct gx_device_bjc800_s gx_device_bjc;
+
+#define bjc_params_common \
+ bool manualFeed; /* Use manual feed */ \
+ int mediaType; /* Cf. strings below */ \
+ bool mediaWeight_isSet; /* Say if weight is an integer or null */ \
+ int mediaWeight; /* Weigth of the media */ \
+ int printQuality; /* Cf. strings below */ \
+ bool ditheringType; /* Do dithering */ \
+ int colorComponents; /* The number of *desired* color comps */ \
+ int printColors /* 0: Transparent, \
+ 1: C, 2: M, 4: Y, 7: K (Color decomp). \
+ if > 8, print in black ink. */
+
+typedef struct {
+ bjc_params_common;
+
+ bool monochromePrint; /* Print with black only */
+} bjc600_params;
+
+typedef struct {
+ bjc_params_common;
+} bjc_params;
+
+typedef bjc_params bjc800_params;
+
+#define gx_bjc_device_common \
+ gx_device_common; \
+ gx_prn_colour_device_common; \
+ int ptype; \
+ float printLimit
+
+struct gx_device_bjc600_s {
+ gx_bjc_device_common;
+ bjc600_params bjc_p;
+};
+struct gx_device_bjc800_s {
+ gx_bjc_device_common;
+ bjc800_params bjc_p;
+};
+
+typedef struct {
+ gx_device_common;
+ gx_prn_colour_device_common;
+} gx_device_colour_prn;
+
+/* Use the cprn_device macro to access generic fields (like cmyk,
+ default_depth and correction), and specific macros for specific
+ devices. */
+
+#define cprn_device ((gx_device_colour_prn*) pdev)
+
+#define cdj ((gx_device_cdj *)pdev)
+#define pjxl ((gx_device_pjxl *)pdev)
+#define pj ((gx_device_pj *)pdev)
+
+#define bjc ((gx_device_bjc*) pdev)
+#define bjc600 ((gx_device_bjc600*) pdev)
+#define bjc800 ((gx_device_bjc800*) pdev)
+
+#define bjcparams (bjc->bjc_p)
+#define bjc600params (bjc600->bjc_p)
+#define bjc800params (bjc800->bjc_p)
+
+#define bjcversion(p) (((gx_device_bjc*) pdev)->ptype == BJC800 ? \
+ BJC_BJC800_VERSION : BJC_BJC600_VERSION)
+#define bjcversionstring(p) (((gx_device_bjc*) pdev)->ptype == BJC800 ? \
+ BJC_BJC800_VERSIONSTR : BJC_BJC600_VERSIONSTR)
+
+#define bjcthickpaper(l) \
+ (bjcparams.mediaWeight_isSet && bjcparams.mediaWeight > l)
+#define bjc600thickpaper() bjcthickpaper(BJC600_MEDIAWEIGHT_THICKLIMIT)
+#define bjc800thickpaper() bjcthickpaper(BJC800_MEDIAWEIGHT_THICKLIMIT)
+
+/* The basic structure for all printers. Note the presence of the cmyk, depth
+ and correct fields even if soem are not used by all printers. */
+
+#define prn_colour_device_body(dtype, procs, dname, w10, h10, xdpi, ydpi, lm, bm, rm, tm, ncomp, depth, mg, mc, dg, dc, print_page, cmyk, correct)\
+ prn_device_body(dtype, procs, dname, w10, h10, xdpi, ydpi, lm, bm, rm, tm, ncomp, depth, mg, mc, dg, dc, print_page), cmyk, depth /* default */, correct
+
+/* Note: the computation of color_info values here must match */
+/* the computation in the cdj_set_bpp procedure below. */
+
+#define prn_hp_colour_device(dtype, procs, dev_name, x_dpi, y_dpi, bpp, print_page, correct)\
+ prn_colour_device_body(dtype, procs, dev_name,\
+ DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS, x_dpi, y_dpi, 0, 0, 0, 0,\
+ (bpp == 32 ? 4 : (bpp == 1 || bpp == 8) ? 1 : 3), bpp,\
+ (bpp >= 8 ? 255 : 1), (bpp >= 8 ? 255 : bpp > 1 ? 1 : 0),\
+ (bpp >= 8 ? 256 : 2), (bpp >= 8 ? 256 : bpp > 1 ? 2 : 0),\
+ print_page, 0 /* cmyk */, correct)
+
+#define prn_cmyk_colour_device(dtype, procs, dev_name, x_dpi, y_dpi, bpp, print_page, correct)\
+ prn_colour_device_body(dtype, procs, dev_name,\
+ DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS, x_dpi, y_dpi, 0, 0, 0, 0,\
+ ((bpp == 1 || bpp == 4) ? 1 : 4), bpp,\
+ (bpp > 8 ? 255 : 1), (1 << (bpp >> 2)) - 1, /* max_gray, max_color */\
+ (bpp > 8 ? 256 : 2), (bpp > 8 ? 256 : bpp > 1 ? 2 : 0),\
+ print_page, 1 /* cmyk */, correct)
+
+#define bjc_device(dtype, p, d, x, y, b, pp, c) \
+ prn_cmyk_colour_device(dtype, p, d, x, y, b, pp, c)
+
+#define cdj_device(procs, dev_name, x_dpi, y_dpi, bpp, print_page, correction, shingling, depletion)\
+{ prn_hp_colour_device(gx_device_cdj, procs, dev_name, x_dpi, y_dpi, bpp, print_page, correction),\
+ shingling,\
+ depletion\
+}
+
+#define pjxl_device(procs, dev_name, x_dpi, y_dpi, bpp, print_page, printqual, rendertype)\
+{ prn_hp_colour_device(gx_device_pjxl, procs, dev_name, x_dpi, y_dpi, bpp, print_page, 0), \
+ printqual,\
+ rendertype\
+}
+
+#define pj_device(procs, dev_name, x_dpi, y_dpi, bpp, print_page)\
+{ prn_hp_colour_device(gx_device_pj, procs, dev_name, x_dpi, y_dpi, bpp, print_page, 0) }
+
+#define bjc600_device(procs, dev_name, x_dpi, y_dpi, bpp, print_page, t, mf, mt, mws, mw, pq, dt, cc, pc, mp) \
+{ bjc_device(gx_device_bjc600, procs, dev_name, x_dpi, y_dpi, bpp, print_page, 0),\
+ t, 0., { mf, mt, mws, mw, pq, dt, cc, pc, mp }\
+}
+#define bjc800_device(procs, dev_name, x_dpi, y_dpi, bpp, print_page, t, mf, mt, mws, mw, pq, dt, cc, pc) \
+{ bjc_device(gx_device_bjc800, procs, dev_name, x_dpi, y_dpi, bpp, print_page, 0),\
+ t, 0., { mf, mt, mws, mw, pq, dt, cc, pc }\
+}
+
+/* Since the print_page doesn't alter the device, this device can print in the background */
+#define hp_colour_procs(proc_colour_open, proc_get_params, proc_put_params) {\
+ proc_colour_open,\
+ gx_default_get_initial_matrix,\
+ gx_default_sync_output,\
+ gdev_prn_bg_output_page,\
+ gdev_prn_close,\
+ gdev_pcl_map_rgb_color,\
+ gdev_pcl_map_color_rgb,\
+ NULL, /* fill_rectangle */\
+ NULL, /* tile_rectangle */\
+ NULL, /* copy_mono */\
+ NULL, /* copy_color */\
+ NULL, /* draw_line */\
+ gx_default_get_bits,\
+ proc_get_params,\
+ proc_put_params\
+}
+
+/* Since the print_page doesn't alter the device, this device can print in the background */
+#define cmyk_colour_procs(proc_colour_open, proc_get_params, proc_put_params) {\
+ proc_colour_open,\
+ gx_default_get_initial_matrix,\
+ gx_default_sync_output,\
+ gdev_prn_bg_output_page,\
+ gdev_prn_close,\
+ NULL /* map_rgb_color */,\
+ NULL /* map_color_rgb */,\
+ NULL /* fill_rectangle */,\
+ NULL /* tile_rectangle */,\
+ NULL /* copy_mono */,\
+ NULL /* copy_color */,\
+ NULL /* draw_line */,\
+ gx_default_get_bits,\
+ proc_get_params,\
+ proc_put_params,\
+ gdev_cmyk_map_cmyk_color,\
+ NULL, /* get_xfont_procs */\
+ NULL, /* get_xfont_device */\
+ NULL, /* map_rgb_alpha_color */\
+ NULL, /* get_page_device */\
+ NULL, /* get_alpha_bits */\
+ NULL, /* copy_alpha */\
+ NULL, /* get_band */\
+ NULL, /* copy_rop */\
+ NULL, /* fill_path */\
+ NULL, /* stroke_path */\
+ NULL, /* fill_mask */\
+ NULL, /* fill_trapezoid */\
+ NULL, /* fill_parallelogram */\
+ NULL, /* fill_triangle */\
+ NULL, /* draw_thin_line */\
+ NULL, /* begin_image */\
+ NULL, /* image_data */\
+ NULL, /* end_image */\
+ NULL, /* strip_tile_rectangle */\
+ NULL, /* strip_copy_rop */\
+ NULL, /* get_clipping_box */\
+ NULL, /* begin_typed_image */\
+ NULL, /* get_bits_rectangle */\
+ NULL, /* map_color_rgb_alpha */\
+ NULL, /* create_compositor */\
+ NULL, /* get_hardware_params */\
+ NULL, /* text_begin */\
+ NULL, /* finish_copydevice */\
+ NULL, /* begin_transparency_group */\
+ NULL, /* end_transparency_group */\
+ NULL, /* begin_transparency_mask */\
+ NULL, /* end_transparency_mask */\
+ NULL, /* discard_transparency_layer */\
+ NULL, /* get_color_mapping_procs */\
+ NULL, /* get_color_comp_index */\
+ gdev_cmyk_map_cmyk_color, /* encode_color */\
+ gdev_cmyk_map_color_cmyk /* decode_color */\
+}
+
+static gx_device_procs cdj500_procs =
+hp_colour_procs(dj500c_open, cdj_get_params, cdj_put_params);
+
+static gx_device_procs cdj550_procs =
+hp_colour_procs(dj550c_open, cdj_get_params, cdj_put_params);
+
+#ifdef USE_CDJ550_CMYK
+static gx_device_procs cdj550cmyk_procs =
+cmyk_colour_procs(dj550c_open, cdj_get_params, cdj_put_params);
+#endif
+
+static gx_device_procs dj505j_procs =
+hp_colour_procs(dj505j_open, cdj_get_params, cdj_put_params);
+
+static gx_device_procs dnj650c_procs =
+hp_colour_procs(dnj650c_open, cdj_get_params, cdj_put_params);
+
+static gx_device_procs lj4dith_procs =
+hp_colour_procs(lj4dith_open, cdj_get_params, cdj_put_params);
+
+static gx_device_procs pj_procs =
+hp_colour_procs(pj_open, gdev_prn_get_params, pj_put_params);
+
+static gx_device_procs pjxl_procs =
+hp_colour_procs(pjxl_open, pjxl_get_params, pjxl_put_params);
+
+static gx_device_procs pjxl300_procs =
+hp_colour_procs(pjxl300_open, pjxl_get_params, pjxl_put_params);
+
+static gx_device_procs bjc_procs =
+cmyk_colour_procs(bjc_open, bjc_get_params, bjc_put_params);
+
+static gx_device_procs escp_procs =
+hp_colour_procs(escp_open, ep_get_params, ep_put_params);
+
+gx_device_cdj far_data gs_cdjmono_device =
+cdj_device(cdj500_procs, "cdjmono", 300, 300, 1,
+ dj500c_print_page, 4, 0, 1);
+
+gx_device_cdj far_data gs_cdeskjet_device =
+cdj_device(cdj500_procs, "cdeskjet", 300, 300, 24,
+ dj500c_print_page, 4, 2, 1);
+
+gx_device_cdj far_data gs_cdjcolor_device =
+cdj_device(cdj500_procs, "cdjcolor", 300, 300, 24,
+ dj500c_print_page, 4, 2, 1);
+
+gx_device_cdj far_data gs_cdj500_device =
+cdj_device(cdj500_procs, "cdj500", 300, 300, BITSPERPIXEL,
+ dj500c_print_page, 4, 2, 1);
+
+gx_device_cdj far_data gs_cdj550_device =
+cdj_device(cdj550_procs, "cdj550", 300, 300, BITSPERPIXEL,
+ dj550c_print_page, 0, 2, 1);
+
+#ifdef USE_CDJ550_CMYK
+gx_device_cdj far_data gs_cdj550cmyk_device = {
+ prn_cmyk_colour_device(cdj550cmyk_procs, "cdj550cmyk", 300, 300,
+ BITSPERPIXEL, dj550c_print_page, 0), 2, 1
+};
+#endif
+
+gx_device_cdj far_data gs_picty180_device =
+cdj_device(cdj550_procs, "picty180", 300, 300, BITSPERPIXEL,
+ picty180_print_page, 0, 2, 1);
+
+gx_device_cdj far_data gs_dj505j_device =
+cdj_device(dj505j_procs, "dj505j", 300, 300, 1,
+ dj505j_print_page, 4, 0, 1);
+
+gx_device_pj far_data gs_declj250_device =
+pj_device(pj_procs, "declj250", 180, 180, BITSPERPIXEL,
+ declj250_print_page);
+
+gx_device_cdj far_data gs_dnj650c_device =
+cdj_device(dnj650c_procs, "dnj650c", 300, 300, BITSPERPIXEL,
+ dnj650c_print_page, 0, 2, 1);
+
+gx_device_cdj far_data gs_lj4dith_device =
+cdj_device(lj4dith_procs, "lj4dith", 600, 600, 8,
+ lj4dith_print_page, 4, 0, 1);
+
+gx_device_cdj far_data gs_lj4dithp_device =
+cdj_device(lj4dith_procs, "lj4dithp", 600, 600, 8,
+ lj4dithp_print_page, 4, 0, 1);
+
+gx_device_pj far_data gs_pj_device =
+pj_device(pj_procs, "pj", 180, 180, BITSPERPIXEL,
+ pj_print_page);
+
+gx_device_pjxl far_data gs_pjxl_device =
+pjxl_device(pjxl_procs, "pjxl", 180, 180, BITSPERPIXEL,
+ pjxl_print_page, 0, 0);
+
+gx_device_pjxl far_data gs_pjxl300_device =
+pjxl_device(pjxl300_procs, "pjxl300", 300, 300, BITSPERPIXEL,
+ pjxl300_print_page, 0, 0);
+
+gx_device_cdj far_data gs_escp_device =
+cdj_device(escp_procs, "escp", 360, 360, 8,
+ escp_print_page, 0, 0, 1);
+
+gx_device_cdj far_data gs_escpc_device =
+cdj_device(escp_procs, "escpc", 360, 360, 24,
+ escp_print_page, 0, 0, 1);
+
+/* Args of bjc drivers are manualFeed, mediaType, printQuality, printColor,
+ mediaWeight_isSet, mediaWeight, (monochromePrint) */
+
+gx_device_bjc600 far_data gs_bjc600_device =
+ bjc600_device(
+ bjc_procs,
+ BJC_BJC600,
+ BJC600_DEFAULT_RESOLUTION,
+ BJC600_DEFAULT_RESOLUTION,
+ BJC600_DEFAULT_BITSPERPIXEL,
+ bjc_print_page,
+ BJC600,
+ BJC600_DEFAULT_MANUALFEED,
+ BJC600_DEFAULT_MEDIATYPE,
+ BJC600_DEFAULT_SETMEDIAWEIGHT,
+ BJC600_DEFAULT_MEDIAWEIGHT,
+ BJC600_DEFAULT_PRINTQUALITY,
+ BJC600_DEFAULT_DITHERINGTYPE,
+ BJC600_DEFAULT_COLORCOMPONENTS,
+ BJC600_DEFAULT_PRINTCOLORS,
+ BJC600_DEFAULT_MONOCHROMEPRINT);
+
+gx_device_bjc800 far_data gs_bjc800_device =
+ bjc800_device(
+ bjc_procs,
+ BJC_BJC800,
+ BJC800_DEFAULT_RESOLUTION,
+ BJC800_DEFAULT_RESOLUTION,
+ BJC800_DEFAULT_BITSPERPIXEL,
+ bjc_print_page,
+ BJC800,
+ BJC800_DEFAULT_MANUALFEED,
+ BJC800_DEFAULT_MEDIATYPE,
+ BJC800_DEFAULT_SETMEDIAWEIGHT,
+ BJC800_DEFAULT_MEDIAWEIGHT,
+ BJC800_DEFAULT_PRINTQUALITY,
+ BJC800_DEFAULT_DITHERINGTYPE,
+ BJC600_DEFAULT_COLORCOMPONENTS,
+ BJC800_DEFAULT_PRINTCOLORS);
+
+/* Forward references */
+static int gdev_pcl_mode1compress(const byte *, const byte *, byte *);
+static int hp_colour_open(gx_device *, int);
+static int hp_colour_print_page(gx_device_printer *, FILE *, int);
+static int cdj_put_param_int(gs_param_list *, gs_param_name, int *, int, int, int);
+static uint gdev_prn_rasterwidth(const gx_device_printer *, int);
+static int cdj_put_param_bpp(gx_device *, gs_param_list *, int, int, int);
+static int cdj_set_bpp(gx_device *, int, int);
+static void cdj_expand_line(word *, int, short, int, int);
+static int bjc_fscmyk(byte**, byte*[4][4], int**, int, int);
+
+/* String parameters manipulation */
+
+typedef struct {
+ const char* p_name;
+ int p_value;
+} stringParamDescription;
+
+static const byte* paramValueToString(const stringParamDescription*, int);
+static int paramStringValue(const stringParamDescription*,
+ const byte*, int, int*);
+
+static int put_param_string(gs_param_list*, const byte*,
+ gs_param_string*, const stringParamDescription*, int *, int);
+static int get_param_string(gs_param_list*, const byte*,
+ gs_param_string*, const stringParamDescription*, int, bool, int);
+
+/* Open the printer and set up the margins. */
+static int
+dj500c_open(gx_device *pdev)
+{ return hp_colour_open(pdev, DJ500C);
+}
+
+static int
+dj550c_open(gx_device *pdev)
+{ return hp_colour_open(pdev, DJ550C);
+}
+
+static int
+dj505j_open(gx_device *pdev)
+{ return hp_colour_open(pdev, DJ505J);
+}
+
+static int
+dnj650c_open(gx_device *pdev)
+{ return hp_colour_open(pdev, DNJ650C);
+}
+
+static int
+lj4dith_open(gx_device *pdev)
+{ return hp_colour_open(pdev, LJ4DITH);
+}
+
+static int
+pjxl300_open(gx_device *pdev)
+{ return hp_colour_open(pdev, PJXL300);
+}
+
+static int
+pj_open(gx_device *pdev)
+{ return hp_colour_open(pdev, PJ180);
+}
+
+static int
+pjxl_open(gx_device *pdev)
+{ return hp_colour_open(pdev, PJXL180);
+}
+
+static int
+escp_open(gx_device *pdev)
+{ return hp_colour_open(pdev, ESC_P);
+}
+
+static int
+bjc_open(gx_device *pdev)
+{ return hp_colour_open(pdev, bjc->ptype);
+}
+
+static int
+hp_colour_open(gx_device *pdev, int ptype)
+{ /* Change the margins if necessary. */
+ static const float dj_a4[4] = { DESKJET_MARGINS_A4 };
+ static const float dj_letter[4] = { DESKJET_MARGINS_LETTER };
+ static const float dj_505j[4] = { DESKJET_505J_MARGINS };
+ static const float dj_505jc[4] = { DESKJET_505J_MARGINS_COLOR };
+ static const float lj4_all[4] = { LJET4_MARGINS };
+ static const float pj_all[4] = { PAINTJET_MARGINS };
+ static const float dnj_all[4] = { DESIGNJET_MARGINS };
+ static const float ep_a4[4] = { ESC_P_MARGINS_A4 };
+ static const float ep_letter[4] = { ESC_P_MARGINS_LETTER };
+
+ static float bjc_a3[4] = { BJC_MARGINS_A3 }; /* Not const! */
+ static float bjc_letter[4] = { BJC_MARGINS_LETTER }; /* Not const! */
+ static float bjc_a4[4] = { BJC_MARGINS_A4 }; /* Not const! */
+
+ const float *m = (float *) 0;
+
+ /* Set up colour params if put_params has not already done so */
+ if (pdev->color_info.num_components == 0)
+ { int code = cdj_set_bpp(pdev, pdev->color_info.depth,
+ pdev->color_info.num_components);
+ if ( code < 0 )
+ return code;
+ }
+
+ switch (ptype) {
+ case DJ500C:
+ case DJ550C:
+ m = (gdev_pcl_paper_size(pdev) == PAPER_SIZE_A4 ? dj_a4 :
+ dj_letter);
+ break;
+ case DJ505J:
+ m = pdev->color_info.num_components > 1 ? dj_505jc : dj_505j;
+ break;
+ case DNJ650C:
+ m = dnj_all;
+ break;
+ case LJ4DITH:
+ m = lj4_all;
+ break;
+ case PJ180:
+ case PJXL300:
+ case PJXL180:
+ m = pj_all;
+ break;
+ case ESC_P:
+ m = (gdev_pcl_paper_size(pdev) == PAPER_SIZE_A4 ? ep_a4 :
+ ep_letter);
+ break;
+ case BJC600:
+ case BJC800:
+ switch (gdev_pcl_paper_size(pdev)) {
+ case PAPER_SIZE_LEGAL:
+ case PAPER_SIZE_LETTER:
+ m = bjc_letter;
+ break;
+
+ case PAPER_SIZE_A0:
+ case PAPER_SIZE_A1:
+ case PAPER_SIZE_A3:
+ m = bjc_a3;
+ break;
+
+ default:
+ m = bjc_a4;
+ }
+
+#ifndef USE_FIXED_MARGINS
+ if (ptype == BJC800) {
+ ((float *) m)[1] = (float)BJC_HARD_LOWER_LIMIT;
+ }
+#endif
+
+ bjc->printLimit = m[3]; /* The real hardware limit. */
+
+#ifdef BJC_DEFAULT_CENTEREDAREA
+ if (m[3] < m[1]) {
+ ((float *) m)[3] = m[1]; /* Top margin = bottom one. */
+ } else {
+ ((float *) m)[1] = m[3]; /* Bottom margin = top one. */
+ }
+#endif
+
+ break;
+
+ /*NOTREACHED*/
+
+ /*
+ * The margins must be set so that the resulting page length will be
+ * expressed exactly as a multiple of tenthes of inches.
+ *
+ */
+
+ /**/ {
+ float *bjcm = (float *) m;
+
+ byte pdimen = (byte)
+ (pdev->height / pdev->y_pixels_per_inch * 10.
+ - bjcm[3] * 10. - bjcm[1] * 10. + .5) + 1;
+ do {
+ --pdimen;
+ bjcm[1] = pdev->height / pdev->y_pixels_per_inch
+ - bjcm[3] - (float) pdimen / 10.;
+ } while (bjcm[1] < BJC_LOWER_LIMIT);
+ }
+
+ break;
+ }
+ gx_device_set_margins(pdev, m, true);
+ return gdev_prn_open(pdev);
+}
+
+/* Added parameters for DeskJet 5xxC */
+
+/* Get parameters. In addition to the standard and printer
+ * parameters, we supply shingling and depletion parameters,
+ * and control over the bits-per-pixel used in output rendering */
+static int
+cdj_get_params(gx_device *pdev, gs_param_list *plist)
+{ int code = gdev_prn_get_params(pdev, plist);
+ if ( code < 0 ||
+ (code = param_write_int(plist, "BlackCorrect", (int *)&cdj->correction)) < 0 ||
+ (code = param_write_int(plist, "Shingling", &cdj->shingling)) < 0 ||
+ (code = param_write_int(plist, "Depletion", &cdj->depletion)) < 0
+ )
+ return code;
+
+ return code;
+}
+
+/* Put parameters. */
+static int
+cdj_put_params(gx_device *pdev, gs_param_list *plist)
+{ int correction = cdj->correction;
+ int shingling = cdj->shingling;
+ int depletion = cdj->depletion;
+ int bpp = 0;
+ int code = 0;
+
+ code = cdj_put_param_int(plist, "BlackCorrect", &correction, 0, 9, code);
+ code = cdj_put_param_int(plist, "Shingling", &shingling, 0, 2, code);
+ code = cdj_put_param_int(plist, "Depletion", &depletion, 1, 3, code);
+ code = cdj_put_param_int(plist, "BitsPerPixel", &bpp, 1, 32, code);
+
+ if ( code < 0 )
+ return code;
+ code = cdj_put_param_bpp(pdev, plist, bpp, bpp, 0);
+ if ( code < 0 )
+ return code;
+
+ cdj->correction = correction;
+ cdj->shingling = shingling;
+ cdj->depletion = depletion;
+ return 0;
+}
+
+/* Added parameters for PaintJet XL and PaintJet XL300 */
+
+/* Get parameters. In addition to the standard and printer
+ * parameters, we supply print_quality and render_type
+ * parameters, together with bpp control. */
+static int
+pjxl_get_params(gx_device *pdev, gs_param_list *plist)
+{ int code = gdev_prn_get_params(pdev, plist);
+ if ( code < 0 ||
+ (code = param_write_int(plist, "PrintQuality", &pjxl->printqual)) < 0 ||
+ (code = param_write_int(plist, "RenderType", &pjxl->rendertype)) < 0
+ )
+ return code;
+
+ return code;
+}
+
+/* Put parameters. */
+static int
+pjxl_put_params(gx_device *pdev, gs_param_list *plist)
+{ int printqual = pjxl->printqual;
+ int rendertype = pjxl->rendertype;
+ int bpp = 0, real_bpp = 0;
+ int code = 0;
+
+ code = cdj_put_param_int(plist, "PrintQuality", &printqual, -1, 1, code);
+ code = cdj_put_param_int(plist, "RenderType", &rendertype, 0, 10, code);
+ code = cdj_put_param_int(plist, "BitsPerPixel", &bpp, 1, 32, code);
+
+ if ( code < 0 )
+ return code;
+ real_bpp = bpp;
+ if ( rendertype > 0 )
+ { /* If printer is doing the dithering, we must have a
+ * true-colour mode, ie. 16 or 24 bits per pixel */
+ if ( bpp > 0 && bpp < 16 )
+ real_bpp = 24;
+ }
+ code = cdj_put_param_bpp(pdev, plist, bpp, real_bpp, 0);
+ if ( code < 0 )
+ return code;
+
+ pjxl->printqual = printqual;
+ pjxl->rendertype = rendertype;
+ return 0;
+}
+
+/* Added parameters for PaintJet */
+
+/* Put parameters. In addition to the standard and printer */
+/* parameters, we allow control of the bits-per-pixel */
+static int
+pj_put_params(gx_device *pdev, gs_param_list *plist)
+{ int bpp = 0;
+ int code = cdj_put_param_int(plist, "BitsPerPixel", &bpp, 1, 32, 0);
+
+ if ( code < 0 )
+ return code;
+ return cdj_put_param_bpp(pdev, plist, bpp, bpp, 0);
+}
+
+static stringParamDescription bjc_processColorsStrings[] = {
+ { "DeviceGray", 1 },
+ { "DeviceRGB", 3 },
+ { "DeviceCMYK", 4 },
+ { 0 }
+};
+
+static stringParamDescription bjc_mediaTypeStrings[] = {
+ { "PlainPaper", BJC_MEDIA_PLAINPAPER },
+ { "CoatedPaper", BJC_MEDIA_COATEDPAPER },
+ { "TransparencyFilm", BJC_MEDIA_TRANSPARENCYFILM },
+ { "Envelope", BJC_MEDIA_ENVELOPE },
+ { "Card", BJC_MEDIA_CARD},
+ { "Other", BJC_MEDIA_OTHER },
+ { 0 }
+};
+
+static stringParamDescription bjc600_printQualityStrings[] = {
+ { "Normal", 0 },
+ { "High", 1 },
+ { "Draft", 2 },
+ { 0 }
+};
+
+static stringParamDescription bjc800_printQualityStrings[] = {
+ { "Normal", 0 },
+ { "High", 1 },
+ { "Low", 3 },
+ { "Draft", 4 },
+ { 0 },
+};
+
+static stringParamDescription bjc_ditheringTypeStrings[] = {
+ { "None", BJC_DITHER_NONE },
+ { "Floyd-Steinberg", BJC_DITHER_FS },
+ { 0 }
+};
+
+static int
+bjc_get_params(gx_device *pdev, gs_param_list *plist)
+{
+ int code = gdev_prn_get_params(pdev, plist);
+ int ncode;
+
+ gs_param_string pmedia;
+ gs_param_string pquality;
+ gs_param_string dithering;
+
+ if (code < 0) return_error(code);
+
+ if ((ncode = param_write_bool(plist, BJC_OPTION_MANUALFEED,
+ &bjcparams.manualFeed)) < 0) {
+ code = ncode;
+ }
+
+ code = get_param_string(plist, (unsigned char *)BJC_OPTION_MEDIATYPE, &pmedia,
+ bjc_mediaTypeStrings, bjcparams.mediaType, true, code);
+
+ code = get_param_string(plist, (unsigned char *)BJC_OPTION_PRINTQUALITY, &pquality,
+ (bjc->ptype == BJC800 ? bjc800_printQualityStrings :
+ bjc600_printQualityStrings), bjcparams.printQuality,
+ true, code);
+
+ code = get_param_string(plist, (unsigned char *)BJC_OPTION_DITHERINGTYPE, &dithering,
+ bjc_ditheringTypeStrings, bjcparams.ditheringType, true, code);
+
+ if ((ncode = param_write_int(plist, BJC_OPTION_PRINTCOLORS,
+ &bjcparams.printColors)) < 0) {
+ code = ncode;
+ }
+
+ if ((ncode = (bjcparams.mediaWeight_isSet ?
+ param_write_int(plist, BJC_OPTION_MEDIAWEIGHT,
+ &bjcparams.mediaWeight) :
+ param_write_null(plist, BJC_OPTION_MEDIAWEIGHT))) < 0) {
+ code = ncode;
+ }
+
+ if (bjc->ptype != BJC800) {
+ if ((ncode = param_write_bool(plist, BJC_OPTION_MONOCHROMEPRINT,
+ &bjc600params.monochromePrint)) < 0) {
+ code = ncode;
+ }
+ }
+
+ /**/ {
+ float version;
+ gs_param_string versionString;
+
+ bool bTrue = true;
+
+ version = bjcversion(pdev);
+ versionString.data = (byte *)bjcversionstring(pdev);
+
+ versionString.size = strlen((char *)versionString.data);
+ versionString.persistent = true;
+
+ if ((ncode = param_write_float(plist, BJC_DEVINFO_VERSION,
+ &version)) < 0) {
+ code = ncode;
+ }
+ if ((ncode = param_write_string(plist, BJC_DEVINFO_VERSIONSTRING,
+ &versionString)) < 0) {
+ code = ncode;
+ }
+
+ if ((ncode = param_write_bool(plist, BJC_DEVINFO_OUTPUTFACEUP,
+ &bTrue)) < 0) {
+ code = ncode;
+ }
+ }
+
+ return code;
+}
+
+/* Put properties for the bjc drivers. */
+
+static int
+bjc_put_params(gx_device *pdev, gs_param_list *plist)
+{
+ int bpp = 0, ccomps = 0;
+
+ int code = 0;
+ int ncode;
+
+ bool aBool = true;
+
+ const char* oname = (const char*) 0;
+
+ bjc600_params new600Params;
+ bjc800_params new800Params;
+
+ bjc_params* params;
+
+ gs_param_string pprocesscolors;
+ gs_param_string pmedia;
+ gs_param_string pquality;
+ gs_param_string dithering;
+
+ gs_param_float_array hwra;
+
+ if (bjc->ptype != BJC800) {
+ new600Params = bjc600params;
+ params = (bjc_params*) &new600Params;
+ } else {
+ new800Params = bjc800params;
+ params = (bjc_params*) &new800Params;
+ }
+
+ if ((code = cdj_put_param_int(plist, "BitsPerPixel",
+ &bpp, 1, 32, code)) != 1) {
+ bpp = pdev->color_info.depth;
+ }
+
+ if ((code = put_param_string(plist, (unsigned char *)"ProcessColorModel",
+ &pprocesscolors, bjc_processColorsStrings, &ccomps, code)) != 1) {
+ ccomps = pdev->color_info.num_components;
+ }
+
+ if ((ncode = param_read_bool(plist, oname = BJC_OPTION_MANUALFEED,
+ &params->manualFeed)) < 0) {
+ param_signal_error(plist, oname, code = ncode);
+ }
+
+ code = put_param_string(plist, (unsigned char *)BJC_OPTION_MEDIATYPE, &pmedia,
+ bjc_mediaTypeStrings, &params->mediaType, code);
+
+ code = cdj_put_param_int(plist, BJC_OPTION_PRINTCOLORS,
+ &params->printColors, 0, 15, code);
+
+ code = put_param_string(plist, (unsigned char *)BJC_OPTION_PRINTQUALITY, &pquality,
+ (bjc->ptype == BJC800 ? bjc800_printQualityStrings :
+ bjc600_printQualityStrings), &params->printQuality, code);
+
+ code = put_param_string(plist, (unsigned char *)BJC_OPTION_DITHERINGTYPE, &dithering,
+ bjc_ditheringTypeStrings, &params->ditheringType, code);
+
+ switch (ncode = param_read_int(plist,
+ oname = BJC_OPTION_MEDIAWEIGHT, &params->mediaWeight)) {
+ case 0:
+ if (params->mediaWeight <= 0) {
+ ncode = gs_error_rangecheck;
+ } else {
+ params->mediaWeight_isSet = 1;
+ break;
+ }
+ goto mwe;
+
+ default:
+ if ((ncode = param_read_null(plist, oname)) == 0) {
+ params->mediaWeight_isSet = 0;
+ break;
+ }
+mwe: param_signal_error(plist, oname, code = ncode);
+
+ case 1:
+ break;
+ }
+
+ if (bjc->ptype != BJC800) {
+ bjc600_params* params600 = (bjc600_params*) params;
+ if ((ncode = param_read_bool(plist,
+ oname = BJC_OPTION_MONOCHROMEPRINT,
+ &params600->monochromePrint)) < 0) {
+ param_signal_error(plist, oname, code = ncode);
+ }
+ }
+
+ if ((ncode = cdj_param_check_float(plist, BJC_DEVINFO_VERSION,
+ bjcversion(pdev), true)) < 0) {
+ code = ncode;
+ }
+ if ((ncode = cdj_param_check_string(plist, BJC_DEVINFO_VERSIONSTRING,
+ bjcversionstring(pdev), true)) < 0) {
+ code = ncode;
+ }
+
+ if ((ncode = param_read_bool(plist, oname = BJC_DEVINFO_OUTPUTFACEUP,
+ &aBool)) < 0) {
+ param_signal_error(plist, oname, code = ncode);
+ } else if (aBool != true) {
+ param_signal_error(plist, oname, code = ncode = gs_error_rangecheck);
+ }
+
+ /* Check for invalid resolution. The array macros are taken from
+ gsdparam.c and modified to use oname, ncode and code instead
+ of param_name, code and ecode respectively. */
+
+ BEGIN_ARRAY_PARAM(param_read_float_array, "HWResolution", hwra, 2, hwre)
+ if ( hwra.data[0] <= 0 || hwra.data[1] <= 0 ||
+ hwra.data[0] != hwra.data[1] )
+ ncode = gs_error_rangecheck;
+ else {
+#ifdef BJC_STRICT
+ if (hwra.data[0] != BJC_RESOLUTION_LOW &&
+ hwra.data[0] != BJC_RESOLUTION_NORMAL &&
+ hwra.data[0] != BJC_RESOLUTION_HIGH) {
+ ncode = gs_error_rangecheck;
+ }
+#else
+ /* A small hack for checking resolution without logarithms. */
+
+ /**/ {
+ int n;
+
+ for (n = 0; n < 8 * sizeof(n) / BJC_RESOLUTION_BASE; ++n) {
+ float res = (float)(BJC_RESOLUTION_BASE * (1 << n));
+
+ if (res == hwra.data[0]) break;
+
+ if (res > hwra.data[0]) {
+ ncode = gs_error_rangecheck;
+ }
+ }
+
+ if (n == 8 * sizeof(n)) {
+ ncode = gs_error_rangecheck;
+ }
+ }
+#endif
+ if (ncode < 0) {
+ code = ncode;
+ } else {
+ break;
+ }
+ }
+ END_PARAM(hwra, hwre)
+
+ if ((ncode = cdj_put_param_bpp(pdev, plist, bpp, bpp, ccomps)) < 0) {
+ code = ncode;
+ }
+
+ if (code < 0)
+ return code;
+
+ if (bpp == 1) {
+ params->ditheringType = BJC_DITHER_NONE;
+ }
+
+ /* Write values that did change */
+
+ if (bjc->ptype != BJC800) {
+ bjc600params = new600Params;
+ } else {
+ bjc800params = new800Params;
+ }
+
+ return code;
+}
+
+/* ------ Internal routines ------ */
+
+/* The DeskJet500C can compress (mode 9) */
+static int
+dj500c_print_page(gx_device_printer * pdev, FILE * prn_stream)
+{
+ return hp_colour_print_page(pdev, prn_stream, DJ500C);
+}
+
+/* The DeskJet550C can compress (mode 9) */
+static int
+dj550c_print_page(gx_device_printer * pdev, FILE * prn_stream)
+{
+ return hp_colour_print_page(pdev, prn_stream, DJ550C);
+}
+
+/* The Picty180C can compress (mode 9) */
+/* This printer need switching mode using PJL */
+static int
+picty180_print_page(gx_device_printer * pdev, FILE * prn_stream)
+{ int ret_code;
+ /* Ensure we're operating in PCL mode */
+ fputs("\033%-12345X@PJL ENTER LANGUAGE = PCLSLEEK\n", prn_stream);
+ ret_code = hp_colour_print_page(pdev, prn_stream, DJ550C);
+ /* Reenter switch-configured language */
+ fputs("\033%-12345X", prn_stream);
+ return ret_code;
+}
+
+/* The DeskJet505J can compress */
+static int
+dj505j_print_page(gx_device_printer * pdev, FILE * prn_stream)
+{
+ return hp_colour_print_page(pdev, prn_stream, DJ505J);
+}
+
+/* The DesignJet650C can compress (mode 1) */
+static int
+dnj650c_print_page(gx_device_printer * pdev, FILE * prn_stream)
+{
+ return hp_colour_print_page(pdev, prn_stream, DNJ650C);
+}
+
+static int
+lj4dith_print_page(gx_device_printer * pdev, FILE * prn_stream)
+{
+ return hp_colour_print_page(pdev, prn_stream, LJ4DITH);
+}
+
+static int
+lj4dithp_print_page(gx_device_printer * pdev, FILE * prn_stream)
+{ int ret_code;
+ /* Ensure we're operating in PCL mode */
+ fputs("\033%-12345X@PJL\r\n@PJL ENTER LANGUAGE = PCL\r\n", prn_stream);
+ ret_code = hp_colour_print_page(pdev, prn_stream, LJ4DITH);
+ /* Reenter switch-configured language */
+ fputs("\033%-12345X", prn_stream);
+ return ret_code;
+}
+
+/* The PJXL300 can compress (modes 2 & 3) */
+static int
+pjxl300_print_page(gx_device_printer * pdev, FILE * prn_stream)
+{ int ret_code;
+ /* Ensure we're operating in PCL mode */
+ fputs("\033%-12345X@PJL enter language = PCL\n", prn_stream);
+ ret_code = hp_colour_print_page(pdev, prn_stream, PJXL300);
+ /* Reenter switch-configured language */
+ fputs("\033%-12345X", prn_stream);
+ return ret_code;
+}
+
+/* The PaintJet XL can compress (modes 2 & 3) */
+static int
+pjxl_print_page(gx_device_printer * pdev, FILE * prn_stream)
+{
+ return hp_colour_print_page(pdev, prn_stream, PJXL180);
+}
+
+/* The PaintJet can compress (mode 1) */
+static int
+pj_print_page(gx_device_printer * pdev, FILE * prn_stream)
+{
+ return hp_colour_print_page(pdev, prn_stream, PJ180);
+}
+
+/* The LJ250 can compress (mode 1) */
+static int
+declj250_print_page(gx_device_printer * pdev, FILE * prn_stream)
+{ int ret_code;
+ fputs("\033%8", prn_stream); /* Enter PCL emulation mode */
+ ret_code = hp_colour_print_page(pdev, prn_stream, DECLJ250);
+ fputs("\033%@", prn_stream); /* Exit PCL emulation mode */
+ return ret_code;
+}
+
+/* The BJC-600 cannot compress w/o raster image commands. */
+static int
+escp_print_page(gx_device_printer * pdev, FILE * prn_stream)
+{
+ return hp_colour_print_page(pdev, prn_stream, ESC_P);
+}
+
+/* The BJC-600 can compress w/ raster image commands. */
+static int
+bjc_print_page(gx_device_printer * pdev, FILE * prn_stream)
+{
+ return hp_colour_print_page(pdev, prn_stream, bjc->ptype);
+}
+
+/* MACROS FOR DITHERING (we use macros for compact source and faster code) */
+/* Floyd-Steinberg dithering. Often results in a dramatic improvement in
+ * subjective image quality, but can also produce dramatic increases in
+ * amount of printer data generated and actual printing time!! Mode 9 2D
+ * compression is still useful for fairly flat colour or blank areas but its
+ * compression is much less effective in areas where the dithering has
+ * effectively randomised the dot distribution. */
+
+#define SHIFT ((I * 8) - 13)
+#define RSHIFT ((I * 8) - 16)
+#define RANDOM (((rand() << RSHIFT) % (MAXVALUE / 2)) - MAXVALUE / 4);
+#define MINVALUE 0
+#define MAXVALUE (255 << SHIFT)
+#define THRESHOLD (128 << SHIFT)
+#define C 8
+
+#define FSdither(inP, out, errP, Err, Bit, Offset, Element)\
+ oldErr = Err;\
+ Err = (errP[Element] + ((Err * 7 + C) >> 4) + ((int)inP[Element] << SHIFT));\
+ if (Err > THRESHOLD) {\
+ out |= Bit;\
+ Err -= MAXVALUE;\
+ }\
+ errP[Element + Offset] += ((Err * 3 + C) >> 4);\
+ errP[Element] = ((Err * 5 + oldErr + C) >> 4);
+
+/* Here we rely on compiler optimisation to remove lines of the form
+ * (if (1 >= 4) {...}, ie. the constant boolean expressions */
+
+/* The original code is in the #else part. Since by default NEW_DITHER
+ is not defined, the old code is used. No enhancement is visible for the
+ bjc600 drivers with the new code, anyway :-( */
+
+#ifdef NEW_DITHER
+
+#define FSDline(scan, i, j, plane_size, cErr, mErr, yErr, kErr, cP, mP, yP, kP, n)\
+{\
+ if (scan == 0) { /* going_up */\
+ for (i = 0; i < plane_size; i++) {\
+ byte c, y, m, k, bitmask;\
+ int oldErr;\
+ bitmask = 0x80;\
+ for (c = m = y = k = 0; bitmask != 0; bitmask >>= 1) {\
+ if (n >= 4) {\
+ FSdither(dp, k, ep, kErr, bitmask, -n, 0);\
+ }\
+ if (n >= 3) {\
+ FSdither(dp, c, ep, cErr, bitmask, -n, n - 3);\
+ FSdither(dp, m, ep, mErr, bitmask, -n, n - 2);\
+ }\
+ FSdither(dp, y, ep, yErr, bitmask, -n, n - 1);\
+ dp += n, ep += n;\
+ }\
+ if (n >= 4)\
+ *kP++ = k;\
+ if (n >= 3) {\
+ *cP++ = c;\
+ *mP++ = m;\
+ }\
+ *yP++ = y;\
+ }\
+ } else { /* going_down */\
+ for (i = 0; i < plane_size; i++) {\
+ byte c, y, m, k, bitmask;\
+ int oldErr;\
+ bitmask = 0x01;\
+ for (c = m = y = k = 0; bitmask != 0; bitmask <<= 1) {\
+ dp -= n, ep -= n;\
+ FSdither(dp, y, ep, yErr, bitmask, n, n - 1);\
+ if (n >= 3) {\
+ FSdither(dp, m, ep, mErr, bitmask, n, n - 2);\
+ FSdither(dp, c, ep, cErr, bitmask, n, n - 3);\
+ }\
+ if (n >= 4) {\
+ FSdither(dp, k, ep, kErr, bitmask, n, 0);\
+ }\
+ }\
+ *--yP = y;\
+ if (n >= 3)\
+ { *--mP = m;\
+ *--cP = c;\
+ }\
+ if (n >= 4)\
+ *--kP = k;\
+ }\
+ }\
+}
+
+#else
+
+#define FSDline(scan, i, j, plane_size, cErr, mErr, yErr, kErr, cP, mP, yP, kP, n)\
+{\
+ if (scan == 0) { /* going_up */\
+ for (i = 0; i < plane_size; i++) {\
+ byte c, y, m, k, bitmask;\
+ int oldErr;\
+ bitmask = 0x80;\
+ for (c = m = y = k = 0; bitmask != 0; bitmask >>= 1) {\
+ if (n >= 4) {\
+ if (*dp) {\
+ FSdither(dp, k, ep, kErr, bitmask, -n, 0);\
+ cErr = mErr = yErr = 0;\
+ } else {\
+ FSdither(dp, c, ep, cErr, bitmask, -n, n - 3);\
+ FSdither(dp, m, ep, mErr, bitmask, -n, n - 2);\
+ FSdither(dp, y, ep, yErr, bitmask, -n, n - 1);\
+ }\
+ } else {\
+ if (n >= 3) {\
+ FSdither(dp, c, ep, cErr, bitmask, -n, n - 3);\
+ FSdither(dp, m, ep, mErr, bitmask, -n, n - 2);\
+ }\
+ FSdither(dp, y, ep, yErr, bitmask, -n, n - 1);\
+ }\
+ dp += n, ep += n;\
+ }\
+ if (n >= 4)\
+ *kP++ = k;\
+ if (n >= 3) {\
+ *cP++ = c;\
+ *mP++ = m;\
+ }\
+ *yP++ = y;\
+ }\
+ } else { /* going_down */\
+ for (i = 0; i < plane_size; i++) {\
+ byte c, y, m, k, bitmask;\
+ int oldErr;\
+ bitmask = 0x01;\
+ for (c = m = y = k = 0; bitmask != 0; bitmask <<= 1) {\
+ dp -= n, ep -= n;\
+ if (n >= 4) {\
+ if (*dp) {\
+ FSdither(dp, k, ep, kErr, bitmask, n, 0);\
+ cErr = mErr = yErr = 0;\
+ } else {\
+ FSdither(dp, y, ep, yErr, bitmask, n, n - 1);\
+ FSdither(dp, m, ep, mErr, bitmask, n, n - 2);\
+ FSdither(dp, c, ep, cErr, bitmask, n, n - 3);\
+ }\
+ } else {\
+ FSdither(dp, y, ep, yErr, bitmask, n, n - 1);\
+ if (n >= 3) {\
+ FSdither(dp, m, ep, mErr, bitmask, n, n - 2);\
+ FSdither(dp, c, ep, cErr, bitmask, n, n - 3);\
+ }\
+ }\
+ }\
+ *--yP = y;\
+ if (n >= 3)\
+ { *--mP = m;\
+ *--cP = c;\
+ }\
+ if (n >= 4)\
+ *--kP = k;\
+ }\
+ }\
+}
+
+#endif
+
+/* END MACROS FOR DITHERING */
+
+#define CPbit(inP, out, Bit, Element)\
+ if (inP[Element]) {\
+ out |= Bit;\
+ }
+
+#define COPYline(scan, i, j, plane_size, cP, mP, yP, kP, n)\
+{\
+ if (scan == 0) { /* going_up */\
+ for (i = 0; i < plane_size; i++) {\
+ byte c, y, m, k, bitmask;\
+ bitmask = 0x80;\
+ for (c = m = y = k = 0; bitmask != 0; bitmask >>= 1) {\
+ if (n >= 4) {\
+ CPbit(dp, k, bitmask, 0);\
+ } \
+ if (n >= 3) {\
+ CPbit(dp, c, bitmask, n - 3);\
+ CPbit(dp, m, bitmask, n - 2);\
+ }\
+ CPbit(dp, y, bitmask, n - 1);\
+ dp += n, ep += n;\
+ }\
+ if (n >= 4)\
+ *kP++ = k;\
+ if (n >= 3) {\
+ *cP++ = c;\
+ *mP++ = m;\
+ }\
+ *yP++ = y;\
+ }\
+ } else { /* going_down */\
+ for (i = 0; i < plane_size; i++) {\
+ byte c, y, m, k, bitmask;\
+ bitmask = 0x01;\
+ for (c = m = y = k = 0; bitmask != 0; bitmask <<= 1) {\
+ dp -= n, ep -= n;\
+ if (n >= 4) {\
+ CPbit(dp, k, bitmask, 0);\
+ }\
+ if (n >= 3) {\
+ CPbit(dp, m, bitmask, n - 2);\
+ CPbit(dp, c, bitmask, n - 3);\
+ }\
+ CPbit(dp, y, bitmask, n - 1);\
+ }\
+ *--yP = y;\
+ if (n >= 3)\
+ { *--mP = m;\
+ *--cP = c;\
+ }\
+ if (n >= 4)\
+ *--kP = k;\
+ }\
+ }\
+}
+
+/* Some convenient shorthand .. */
+#define x_dpi (pdev->x_pixels_per_inch)
+#define y_dpi (pdev->y_pixels_per_inch)
+#define CONFIG_16BIT "\033*v6W\000\003\000\005\006\005"
+#define CONFIG_24BIT "\033*v6W\000\003\000\010\010\010"
+
+/* To calculate buffer size as next greater multiple of both parameter and W */
+#define calc_buffsize(a, b) (((((a) + ((b) * W) - 1) / ((b) * W))) * W)
+
+/*
+ * Miscellaneous functions for Canon BJC-600 printers in raster command mode.
+ */
+#define fputshort(n, f) fputc((n)%256,f);fputc((n)/256,f)
+
+static int
+bjc_cmd(byte cmd, int argsize, byte* arg, gx_device_printer* pdev,
+ FILE* f)
+{
+ fputs("\033(", f);
+ putc(cmd, f);
+ fputshort(argsize, f);
+ fwrite(arg, sizeof(byte), argsize, f);
+
+ return 0;
+}
+
+static int
+bjc_raster_cmd_sub(char c, int rastsize, byte* data, FILE* f)
+{
+ fputs("\033(A", f);
+ fputshort(rastsize + 1, f);
+ putc(c, f);
+ fwrite(data, sizeof(byte), rastsize, f);
+ putc('\015', f);
+
+ return 0;
+}
+
+static int
+bjc_raster_cmd(int c_id, int rastsize, byte* data, gx_device_printer* pdev,
+ FILE* f)
+{
+ if (bjcparams.printColors == BJC_COLOR_ALLBLACK) {
+ bjc_raster_cmd_sub('K', rastsize, data, f);
+ } else if (pdev->color_info.num_components == 1) {
+ if (bjcparams.printColors & BJC_COLOR_BLACK) {
+ bjc_raster_cmd_sub('K', rastsize, data, f);
+ } else {
+ if (bjcparams.printColors & BJC_COLOR_YELLOW)
+ bjc_raster_cmd_sub('Y', rastsize, data, f);
+ if (bjcparams.printColors & BJC_COLOR_MAGENTA)
+ bjc_raster_cmd_sub('M', rastsize, data, f);
+ if (bjcparams.printColors & BJC_COLOR_CYAN)
+ bjc_raster_cmd_sub('C', rastsize, data, f);
+ }
+ }else { /* Color decomposition */
+ static byte ymckCodes[] = {
+ BJC_COLOR_YELLOW,
+ BJC_COLOR_MAGENTA,
+ BJC_COLOR_CYAN,
+ BJC_COLOR_BLACK,
+ };
+
+ if (bjcparams.printColors & (int) ymckCodes[c_id]) {
+ bjc_raster_cmd_sub("YMCK"[c_id], rastsize, data, f);
+ }
+ }
+
+ return 0;
+}
+
+static int
+bjc_init_page(gx_device_printer* pdev, FILE* f)
+{
+ byte pagemargins[3], resolution[4], paperloading[2];
+
+ /* Compute page margins. */
+
+ pagemargins[0] = (byte) ((float) pdev->height / pdev->y_pixels_per_inch
+ * 10 + .5);
+ pagemargins[1] = (byte) 1;
+ pagemargins[2] = (byte) ((pdev->width / pdev->x_pixels_per_inch * 10) -
+ pdev->HWMargins[0] / 7.2 - pdev->HWMargins[2] / 7.2 + .5);
+
+ /* Cheat to keep margins into bounds (while waiting to have the right
+ margins for big papers. */
+
+ switch (bjc->ptype) {
+ case BJC800:
+ if (pagemargins[2] > 114) pagemargins[2] = 114;
+ break;
+
+ default:
+ if (pagemargins[2] > 80) pagemargins[2] = 80;
+ break;
+ }
+
+ /* Initialize resolution argument. */
+
+ resolution[0] = (byte) ((int)pdev->y_pixels_per_inch / 256);
+ resolution[1] = (byte) ((int)pdev->y_pixels_per_inch % 256);
+ resolution[2] = (byte) ((int)pdev->x_pixels_per_inch / 256);
+ resolution[3] = (byte) ((int)pdev->x_pixels_per_inch % 256);
+
+ /* Initialize paper loading argument. */
+
+ paperloading[0] = 0x10 + ((1 - bjcparams.manualFeed) << 2);
+ paperloading[1] = bjcparams.mediaType << 4;
+
+ /* Reinitialize printer in raster mode. */
+
+ fputs("\033[K", f);
+ fputshort(2, f);
+ fputc(0x00, f);
+ fputc(0x0f, f);
+
+ /* Set page mode on (ignore data at end of page) */
+
+ bjc_cmd('a', 1, (byte*) "\001", pdev, f);
+
+ /* Set page margins */
+
+ bjc_cmd('g', 3, pagemargins, pdev, f);
+
+ /* Set compression on (this is PackBits compression a la TIFF/Mac) */
+
+ bjc_cmd('b', 1, (byte*) "\001", pdev, f);
+
+ /* Set paper loading. */
+
+ bjc_cmd('l', 2, paperloading, pdev, f);
+
+ /* Set printing method. */
+
+#ifndef BJC_INIT_800_AS_600
+ if (bjc->ptype == BJC800) {
+#else
+ if (0) {
+#endif
+ byte printmode[2];
+
+ printmode[0] = bjcparams.printQuality;
+
+ /* Modes not used are 3 (CN, Color Normal) and 2 (TP+ (?)) */
+
+ switch (bjcparams.printQuality) {
+ case BJC_QUALITY_DRAFT:
+ printmode[0] = 4; /* Draft */
+ break;
+ }
+
+ printmode[1] = (bjcparams.mediaType >= BJC_MEDIA_ENVELOPE ? 1 :
+ bjc800thickpaper());
+
+ bjc_cmd('c', 2, printmode, pdev, f);
+ } else /* BJC600 */ {
+ byte printmeth[3];
+
+ printmeth[0] = 0x10 + ((1 - bjcparams.manualFeed) << 2);
+ printmeth[1] = (bjcparams.mediaType << 4) + bjcparams.printQuality;
+ printmeth[2] = (bjcparams.printQuality == BJC_QUALITY_HIGH ?
+ 0x10 : 0) + (bjcparams.mediaType >= BJC_MEDIA_ENVELOPE ? 1 :
+ bjc600thickpaper());
+
+ bjc_cmd('c', 3, printmeth, pdev, f);
+ }
+
+ /* Set raster resolution */
+
+ bjc_cmd('d', 4, resolution, pdev, f);
+
+ return 0;
+}
+
+static int
+bjc_v_skip(int n, gx_device_printer* pdev, FILE* f)
+{
+ if (n) {
+ fputs("\033(e", f);
+ putc(2, f);
+ putc(0, f);
+ putc(n / 256, f);
+ putc(n % 256, f);
+ }
+
+ return 0;
+}
+
+static int
+bjc_finish_page(gx_device_printer* pdev, FILE* f)
+{
+ bjc_cmd('a', 1, (byte*) "\000", pdev, f);
+ bjc_cmd('b', 1, (byte*) "\000", pdev, f);
+ fputc('\014', f);
+ fputs("\033@", f);
+
+ return 0;
+}
+
+/* 1D runlength compression for BJC-600
+ * this code is borrowed from gdevpcl.c:gdev_pcl_mode2compress.
+ */
+static int
+bjc_compress(const byte *row, const byte *end_row, byte *compressed)
+{
+ register const byte *exam = row;
+ register byte *cptr = compressed; /* output pointer into compressed bytes */
+
+ while ( exam < end_row ) {
+ /* Search ahead in the input looking for a run */
+ /* of at least 4 identical bytes. */
+ const byte *compr = exam;
+ const byte *end_dis;
+ const byte *next;
+ register byte test, test2;
+
+ test = *exam;
+ while ( exam < end_row ) {
+ test2 = *++exam;
+ if ( test == test2 )
+ break;
+ test = test2;
+ }
+
+ /* Find out how long the run is */
+ end_dis = exam - 1;
+ if ( exam == end_row ) { /* no run */
+ next = --end_row;
+ } else {
+
+ next = exam + 1;
+ while ( next < end_row && *next == test ) next++;
+ }
+
+ /* Now [compr..end_dis) should be encoded as dissimilar, */
+ /* and [end_dis..next) should be encoded as similar. */
+ /* Note that either of these ranges may be empty. */
+
+ for ( ; ; ) { /* Encode up to 128 dissimilar bytes */
+ uint count = end_dis - compr; /* uint for faster switch */
+ switch ( count ) { /* Use memcpy only if it's worthwhile. */
+ case 6: cptr[6] = compr[5];
+ case 5: cptr[5] = compr[4];
+ case 4: cptr[4] = compr[3];
+ case 3: cptr[3] = compr[2];
+ case 2: cptr[2] = compr[1];
+ case 1: cptr[1] = compr[0];
+ *cptr = count - 1;
+ cptr += count + 1;
+ case 0: /* all done */
+ break;
+ default:
+ if ( count > 128 ) count = 128;
+ *cptr++ = count - 1;
+ memcpy(cptr, compr, count);
+ cptr += count, compr += count;
+ continue;
+ }
+ break;
+ }
+
+ { /* Encode up to 128 similar bytes. */
+ /* Note that count may be <0 at end of row. */
+ int count = next - end_dis;
+ if (next < end_row || test != 0)
+ while ( count > 0 ) {
+
+ int this = (count > 128 ? 128 : count);
+ *cptr++ = 257 - this;
+ *cptr++ = (byte)test;
+ count -= this;
+ }
+ exam = next;
+ }
+ }
+ return cptr - compressed;
+}
+
+/*
+ * For the ESC/P mode, resolution is fixed as 360dpi and we must transform
+ * image data to serialized data.
+ */
+typedef struct {
+ word *storage;
+ uint storage_size_words;
+ byte *raster_buf[4][BJC_HEAD_ROWS];
+ byte *print_buf;
+ int num_comps;
+ int plane_size;
+ int img_rows;
+ int ln_idx;
+ int vskip1;
+ int vskip2;
+ const gs_memory_t *mem;
+} ep_globals;
+
+#define row_bytes (img_rows / 8)
+#define min_rows (32) /* for optimization of text image printing */
+
+static int
+ep_print_image(FILE *prn_stream, ep_globals *eg, char cmd, byte *data, int size)
+{
+ int i, real_rows;
+ static const char color[4] = {4,1,2,0};
+
+ switch (cmd) {
+ case 3: /* Black */
+ case 2: /* Cyan */
+ case 1: /* Magenta */
+ case 0: /* Yellow */
+ memcpy(eg->raster_buf[((int) cmd)][eg->ln_idx+eg->vskip2], data, size);
+ return 0;
+ case 'B': /* blank line skip */
+ if (!eg->ln_idx) {
+ eg->vskip1 += size;
+ } else if (size >= eg->img_rows - (eg->ln_idx+eg->vskip2) || eg->ln_idx+eg->vskip2 >= min_rows) {
+ /* The 'I' cmd must precede 'B' cmd! */
+ eg->vskip2 += size;
+ ep_print_image(prn_stream, eg, 'F', 0, 0); /* flush and reset status */
+ } else {
+ eg->vskip2 += size;
+ }
+ return 0;
+ case 'I': /* Increment index */
+ eg->ln_idx += eg->vskip2 + 1;
+ eg->vskip2 = 0;
+ if (eg->ln_idx < eg->img_rows) return 0;
+ /* if eg->raster_buf filled up, then fall through here and flush buffer */
+ case 'F': /* flush print buffer */
+ if (!eg->ln_idx) return 0; /* The end of the page. */
+
+ /* before print the image, perform vertical skip. */
+ while (eg->vskip1 >= (255*2)) {
+ fputs("\033J\377", prn_stream); /* n/180in. feeding */
+ eg->vskip1 -= (255*2);
+ }
+ if (eg->vskip1 > 255) {
+ fputs("\033J\200", prn_stream);
+ eg->vskip1 -= 256;
+ }
+ if (eg->vskip1) {
+ /* n/360in. feeding */
+ fputs("\033|J", prn_stream); putc(0, prn_stream); putc(eg->vskip1, prn_stream);
+ }
+
+ /* Optimize the number of nozzles to be used. */
+ if (eg->ln_idx > 56) { /* use 64 nozzles */
+ real_rows = 64;
+ } else if (eg->ln_idx > 48) { /* use 56 nozzles */
+ real_rows = 56;
+ } else if (eg->ln_idx > 32) { /* use 48 nozzles */
+ real_rows = 48;
+ } else { /* use 32 nozzles */
+ real_rows = 32;
+ }
+
+ for (i = 0; i < eg->num_comps; i++) {
+ int lnum, hskip, print_size, img_rows;
+ byte *p0, *p1, *p2, *p3;
+ byte *inp, *inbuf, *outp, *outbuf;
+
+ img_rows = real_rows; /* Note that this img_rows is not the one in
+ * the globals struct. */
+ outbuf = eg->print_buf;
+
+ /* Transpose raster image for serial printer image */
+ for (lnum=0; lnum < img_rows; lnum+=8, outbuf++) {
+ inbuf = inp = eg->raster_buf[i][lnum];
+ for (outp = outbuf; inp < inbuf+eg->plane_size; inp++, outp += img_rows) {
+ memflip8x8(inp, eg->plane_size, outp, row_bytes);
+ }
+ }
+
+ /* Set color */
+ if (eg->num_comps == 1) {
+ /* Don't set color (to enable user setting). */
+ putc('\015', prn_stream);
+ } else {
+ /* set color to one of CMYK. */
+ fputs("\015\033r", prn_stream);
+ putc(color[i], prn_stream);
+ }
+
+ *(outp = eg->print_buf + eg->plane_size * img_rows) = 1; /* sentinel */
+
+ p0 = p3 = eg->print_buf;
+
+ /* print image p0 to p1 and h skip p1 to p2 if p2<outp,
+ * then make p0=p2 and continue */
+ while (p0 < outp) {
+ static const word zeros[8] = {0,0,0,0,0,0,0,0};
+
+ if (p3 < outp) {
+ /* p1 is the head of running zeros. */
+ /* note that h skip unit is 1/180inch */
+ for (p1 = p3; !memcmp(p3, zeros, row_bytes*2); p3 += row_bytes*2);
+ /* p2 is the head of non zero image. */
+ p2 = p3;
+ redo:
+ for (p3 += row_bytes; memcmp(p3, zeros, row_bytes); p3 += row_bytes);
+ if (p3 < outp && memcmp(p3+row_bytes, zeros, row_bytes)) goto redo;
+ } else p1 = p2 = outp;
+
+ if (p0 < p1) { /* print the image between p0 and p1 */
+ print_size = ((p1 < outp) ? p1 : outp) - p0;
+ fputs("\033|B", prn_stream); putc(img_rows, prn_stream);
+ fputshort(print_size, prn_stream);
+ fwrite(p0, sizeof(byte), print_size, prn_stream);
+ }
+ if (p1 < p2) { /* skip running zeros from p1 to p2 */
+ hskip = (((p2 < outp) ? p2 : outp) - p1) / row_bytes / 2;
+ fputs("\033\\", prn_stream);
+ fputshort(hskip, prn_stream);
+ }
+ p0 = p2;
+ }
+ }
+ return ep_print_image(prn_stream, eg, 'R', 0, eg->vskip2 + eg->ln_idx);
+ case 'R': /* Reset status */
+ eg->ln_idx = 0;
+ eg->vskip1 = size;
+ eg->vskip2 = 0;
+ memset(eg->storage, 0, eg->storage_size_words * W);
+ return 0;
+ default: /* This should not happen */
+ errprintf(eg->mem, "ep_print_image: illegal command character `%c'.\n", cmd);
+ return 1;
+ }
+
+ /* NOT REACHED */
+}
+
+/* Send the page to the printer. Compress each scan line. */
+static int
+hp_colour_print_page(gx_device_printer * pdev, FILE * prn_stream, int ptype)
+{
+ uint raster_width = gdev_prn_rasterwidth(pdev, 1);
+/* int line_size = gdev_prn_rasterwidth(pdev, 0); */
+ int line_size = gdev_prn_raster(pdev);
+ int line_size_words = (line_size + W - 1) / W;
+ int paper_size = gdev_pcl_paper_size((gx_device *)pdev);
+ int num_comps = pdev->color_info.num_components;
+ int bits_per_pixel = pdev->color_info.depth;
+ int storage_bpp = bits_per_pixel;
+ int expanded_bpp = bits_per_pixel;
+ int plane_size, databuff_size;
+ int combined_escapes = 1;
+ int errbuff_size = 0;
+ int outbuff_size = 0;
+ int compression = 0;
+ int scan = 0;
+ int *errors[2];
+ const char *cid_string = (const char*) 0;
+ byte *data[4], *plane_data[4][4], *out_data;
+ byte *out_row, *out_row_alt;
+ word *storage;
+ uint storage_size_words;
+ ep_globals eg;
+
+ memset(&eg, 0, sizeof(eg));
+ eg.img_rows=BJC_HEAD_ROWS;
+ eg.mem=pdev->memory;
+
+ /* Tricks and cheats ... */
+ switch (ptype) {
+ case DJ550C:
+ if (num_comps == 3 && !cprn_device->cmyk)
+ num_comps = 4; /* 4-component printing */
+ break;
+ case ESC_P:
+ if (bits_per_pixel == 24) /* prefer 3-component printing for bpp=24. */
+ num_comps = 3;
+ else
+ if (num_comps != 1)
+ num_comps = 4;
+ break;
+ case PJXL300:
+ case PJXL180:
+ if (pjxl->rendertype > 0) {
+ if (bits_per_pixel < 16)
+ pjxl->rendertype = 0;
+ else {
+ /* Control codes for CID sequence */
+ cid_string = (bits_per_pixel == 16) ? CONFIG_16BIT : CONFIG_24BIT;
+ /* Pretend we're a monobit device so we send the data out unchanged */
+ bits_per_pixel = storage_bpp = expanded_bpp = 1;
+ num_comps = 1;
+ }
+ }
+ break;
+ }
+
+ if (cprn_device->cmyk <= 0) {
+ if (storage_bpp == 8 && num_comps >= 3)
+ bits_per_pixel = expanded_bpp = 3; /* Only 3 bits of each byte used */
+ }
+
+ plane_size = calc_buffsize(line_size, storage_bpp);
+ eg.plane_size = plane_size;
+
+ if (bits_per_pixel == 1) { /* Data printed direct from i/p */
+ databuff_size = 0; /* so no data buffer required, */
+ outbuff_size = plane_size * 4; /* but need separate output buffers */
+ }
+
+ if (bits_per_pixel > 4) { /* Error buffer for FS dithering */
+ storage_bpp = expanded_bpp =
+ num_comps * 8; /* 8, 24 or 32 bits */
+
+ if (cprn_device->cmyk > 0) { /* Use CMYK dithering algorithm. */
+ errbuff_size = 4 * (5 + 1 + 1 + line_size + 1 + 2) * I;
+ } else { /* Use original (RGB) dithering. */
+ errbuff_size = /* 4n extra values for line ends */
+ calc_buffsize((plane_size * expanded_bpp + num_comps * 4) * I, 1);
+ }
+ }
+
+ databuff_size = plane_size * storage_bpp;
+
+ storage_size_words = ((plane_size + plane_size) * num_comps +
+ databuff_size + errbuff_size + outbuff_size) / W;
+
+ storage = (ulong *) gs_malloc(pdev->memory, storage_size_words, W, "hp_colour_print_page");
+ eg.storage_size_words = (plane_size * (num_comps + 1)) / W * eg.img_rows
+ + 16; /* Redundant space for sentinel and aligning. */
+ eg.storage = (word *) gs_malloc(pdev->memory, eg.storage_size_words, W, "ep_print_buffer");
+
+ /*
+ * The principal data pointers are stored as pairs of values, with
+ * the selection being made by the 'scan' variable. The function of the
+ * scan variable is overloaded, as it controls both the alternating
+ * raster scan direction used in the Floyd-Steinberg dithering and also
+ * the buffer alternation required for line-difference compression.
+ *
+ * Thus, the number of pointers required is as follows:
+ *
+ * errors: 2 (scan direction only)
+ * data: 4 (scan direction and alternating buffers)
+ * plane_data: 4 (scan direction and alternating buffers)
+ */
+
+ if (storage == 0 || eg.storage == 0) /* can't allocate working area */
+ return_error(gs_error_VMerror);
+ else {
+ int i, j;
+ byte *p = out_data = out_row = (byte *)storage;
+ byte *ep_p = (byte *)eg.storage;
+ data[0] = data[1] = data[2] = p;
+ data[3] = p + databuff_size;
+ out_row_alt = out_row + plane_size * 2;
+ if (bits_per_pixel > 1) {
+ p += databuff_size;
+ }
+ if (bits_per_pixel > 4) {
+ errors[0] = (int *)p + num_comps * 2;
+ errors[1] = errors[0] + databuff_size;
+ p += errbuff_size;
+ }
+ for (i = 0; i < num_comps; i++) {
+ plane_data[0][i] = plane_data[2][i] = p;
+ p += plane_size;
+ }
+ for (i = 0; i < num_comps; i++) {
+ plane_data[1][i] = p;
+ plane_data[3][i] = p + plane_size;
+ p += plane_size;
+ }
+ if (bits_per_pixel == 1) {
+ out_data = out_row = p; /* size is outbuff_size * 4 */
+ out_row_alt = out_row + plane_size * 2;
+ data[1] += databuff_size; /* coincides with plane_data pointers */
+ data[3] += databuff_size;
+ }
+ for (i = 0; i < num_comps; i++) {
+ for (j = 0; j < eg.img_rows; j++) {
+ eg.raster_buf[i][j] = ep_p;
+ ep_p += plane_size;
+ }
+ /* Make a sentinel and align to word size. */
+ eg.print_buf = (byte *)((word)(ep_p + sizeof(word)) & ~(sizeof(word)-1));
+ }
+ eg.num_comps = num_comps;
+ }
+
+ /* Initialize printer. */
+ if (ptype == DJ505J) {
+ fputs("\033@",prn_stream); /* Reset printer */
+ fprintf(prn_stream,"\033_R%c%c", /* Set resolution */
+ (int)x_dpi & 0xff,((int)x_dpi >> 8) & 0xff);
+ } else if (ptype == BJC600 || ptype == BJC800) {
+ bjc_init_page(pdev, prn_stream);
+ } else {
+ if (ptype == LJ4DITH) {
+ fputs("\033*rB", prn_stream);
+ } else {
+ fputs("\033*rbC", prn_stream); /* End raster graphics */
+ }
+ fprintf(prn_stream, "\033*t%dR", (int)x_dpi);
+ /* Set resolution */
+ }
+
+ /* Clear temp storage */
+ memset(storage, 0, storage_size_words * W);
+
+#define DOFFSET (dev_t_margin(pdev) - DESKJET_PRINT_LIMIT) /* Print position */
+#define POFFSET (dev_t_margin(pdev) - PAINTJET_PRINT_LIMIT)
+#define EOFFSET (dev_t_margin(pdev) - ESC_P_PRINT_LIMIT)
+#define BOFFSET (dev_t_margin(pdev) - bjc->printLimit)
+ switch (ptype) {
+ case LJ4DITH:
+ /* Page size, orientation, top margin & perforation skip */
+ fprintf(prn_stream, "\033&l%dA\033&l0o0e0L\033*r0F", paper_size);
+ fprintf(prn_stream, "\033*p0x0Y" ); /* These Offsets are hacked ! */
+ fprintf(prn_stream, "\033&u600D\033*r1A" );
+ /* Select data compression */
+ compression = 3;
+ combined_escapes = 0;
+ break;
+ case DJ500C:
+ case DJ550C:
+ /* Page size, orientation, top margin & perforation skip */
+ fprintf(prn_stream, "\033&l%daolE", paper_size);
+ /* Set depletion and shingling levels */
+ fprintf(prn_stream, "\033*o%dd%dQ", cdj->depletion, cdj->shingling);
+ /* Move to top left of printed area */
+ fprintf(prn_stream, "\033*p%dY", (int)(300 * DOFFSET));
+ /* Set number of planes ((-)1 is mono, (-)3 is (cmy)rgb, -4 is cmyk),
+ * and raster width, then start raster graphics */
+ fprintf(prn_stream, "\033*r%ds-%du0A", raster_width, num_comps);
+ /* Select data compression */
+ compression = 9;
+ break;
+ case DJ505J:
+ /* Set depletion and shingling levels */
+ fprintf(prn_stream, "\033_D%c\033_E%c",
+ cdj->depletion, cdj->shingling);
+ /* Move to top left of printed area */
+ fwrite("\033_N\000", 4, 1, prn_stream);
+ fwrite("\033_J\xc4\xff", 5, 1, prn_stream);
+ /* Set number of planes ((-)1 is mono, (-)3 is (cmy)rgb, -4 is cmyk),
+ * and raster width, then start raster graphics */
+ fprintf(prn_stream, "\033_U%c%c",
+ (0xffff - num_comps + 1) & 0xff, ((0xffff - num_comps + 1) >> 8) & 0xff);
+ fprintf(prn_stream,
+ "\033_S%c%c", raster_width & 0xff, (raster_width >> 8) & 0xff);
+ /* set origin */
+ fwrite("\033_A\001", 4, 1, prn_stream);
+ compression = 1;
+ combined_escapes = 0;
+ break;
+ case DNJ650C:
+ if (pdev->x_pixels_per_inch == 600) {
+ /* set resolution to 600dpi 1st through PJL command */
+ fprintf(prn_stream,"\033%%-12345X@PJL SET RESOLUTION = 600\n");
+ }
+ fprintf (prn_stream, "\033%%0B"); /* Enter HPGL/2 mode */
+ fprintf (prn_stream, "BP5,1"); /* Turn off autorotation */
+ fprintf (prn_stream, "PS%d,%d",
+ (int)((pdev->height/pdev->y_pixels_per_inch)*1016),
+ (int)((pdev->width/pdev->x_pixels_per_inch)*1016)); /* Set length/width of page */
+ fprintf (prn_stream, "PU"); /* Pen up */
+ fprintf (prn_stream, "PA%d,%d", 0, 0); /* Move pen to upper-left */
+ fprintf (prn_stream, "\033%%1A"); /* Enter HP-RTL mode */
+ fprintf (prn_stream, "\033&a1N"); /* No negative motion - allow plotting
+ while receiving */
+ if (pdev->x_pixels_per_inch == 600)
+ fprintf (prn_stream, "\033*t600R"); /* request 600dpi via HP RTL */
+ { static const char temp[] = {
+ 033, '*', 'v', '6', 'W',
+ 000 /* color model */,
+ 000 /* pixel encoding mode */,
+ 003 /* number of bits per index */,
+ 010 /* bits red */,
+ 010 /* bits green */,
+ 010 /* bits blue */
+ };
+ fwrite (temp, 1, sizeof(temp), prn_stream);
+ }
+
+ /* Set raster width */
+ fprintf(prn_stream, "\033*r%dS", raster_width);
+ /* Start raster graphics */
+ fprintf(prn_stream, "\033*r1A");
+
+ /* Select data compression */
+ compression = 1;
+ /* No combined escapes for raster transfers */
+ combined_escapes = 0;
+ break;
+ case PJXL300:
+ /* Page size, orientation, top margin & perforation skip */
+ fprintf(prn_stream, "\033&l%daolE", paper_size);
+ /* Set no-negative-motion mode, for faster (unbuffered) printing */
+ fprintf(prn_stream, "\033&a1N");
+ /* Set print quality */
+ fprintf(prn_stream, "\033*o%dQ", pjxl->printqual);
+ /* Move to top left of printed area */
+ fprintf(prn_stream, "\033*p%dY", (int)(300 * POFFSET));
+ /* Configure colour setup */
+ if (pjxl->rendertype > 0) {
+ /* Set render type */
+ fprintf(prn_stream, "\033*t%dJ", pjxl->rendertype);
+ /* Configure image data */
+ fputs(cid_string, prn_stream);
+ /* Set raster width, then start raster graphics */
+ fprintf(prn_stream, "\033*r%ds1A", raster_width);
+ } else {
+ /* Set number of planes (1 is mono, 3 is rgb),
+ * and raster width, then start raster graphics */
+ fprintf(prn_stream, "\033*r%ds-%du0A", raster_width, num_comps);
+ }
+ /* No combined escapes for raster transfers */
+ combined_escapes = 0;
+ break;
+ case PJXL180:
+ /* Page size, orientation, top margin & perforation skip */
+ fprintf(prn_stream, "\033&l%daolE", paper_size);
+ /* Set print quality */
+ fprintf(prn_stream, "\033*o%dQ", pjxl->printqual);
+ /* Move to top left of printed area */
+ fprintf(prn_stream, "\033*p%dY", (int)(180 * POFFSET));
+ /* Configure colour setup */
+ if (pjxl->rendertype > 0) {
+ /* Set render type */
+ fprintf(prn_stream, "\033*t%dJ", pjxl->rendertype);
+ /* Configure image data */
+ fputs(cid_string, prn_stream);
+ /* Set raster width, then start raster graphics */
+ fprintf(prn_stream, "\033*r%ds1A", raster_width);
+ } else {
+ /* Set number of planes (1 is mono, 3 is rgb),
+ * and raster width, then start raster graphics */
+ fprintf(prn_stream, "\033*r%ds%du0A", raster_width, num_comps);
+ }
+ break;
+ case PJ180:
+ case DECLJ250:
+ /* Disable perforation skip */
+ fprintf(prn_stream, "\033&lL");
+ /* Move to top left of printed area */
+ fprintf(prn_stream, "\033&a%dV", (int)(720 * POFFSET));
+ /* Set number of planes (1 is mono, 3 is rgb),
+ * and raster width, then start raster graphics */
+ fprintf(prn_stream, "\033*r%ds%du0A", raster_width, num_comps);
+ if (ptype == DECLJ250) {
+ /* No combined escapes for raster transfers */
+ combined_escapes = 0;
+ /* From here on, we're a standard Paintjet .. */
+ ptype = PJ180;
+ }
+ /* Select data compression */
+ compression = 1;
+ break;
+ case ESC_P:
+ /* Move to top left of printed area (must be modified for large movement(YK))*/
+ if ((int)(EOFFSET*360)) fprintf(prn_stream, "\033|J%c%c", 0, (int)(360*EOFFSET));
+ combined_escapes = 0;
+ break;
+ case BJC600:
+ case BJC800:
+ /* Move to top left of printed area */
+ bjc_v_skip((int)(pdev->HWResolution[1] * BOFFSET), pdev, prn_stream);
+ combined_escapes = 0;
+ compression = 2; /* BJC600 uses the same method as mode 2 compression */
+ break;
+ }
+
+ /* Unfortunately, the Paintjet XL300 PCL interpreter introduces a
+ * version of the PCL language which is different to all earlier HP
+ * colour and mono inkjets, in that it loses the very useful ability
+ * to use combined escape sequences with the raster transfer
+ * commands. In this respect, it is incompatible even with the older
+ * 180 dpi PaintJet and PaintJet XL printers! Another regrettable
+ * omission is that 'mode 9' compression is not supported, as this
+ * mode can give both computational and PCL file size advantages. */
+
+ if (ptype == DJ505J) {
+ fprintf(prn_stream, "\033_M%c", compression);
+ } else if (combined_escapes) {
+ /* From now on, all escape commands start with \033*b, so we
+ * combine them (if the printer supports this). */
+ fputs("\033*b", prn_stream);
+ /* Set compression if the mode has been defined. */
+ if (compression)
+ fprintf(prn_stream, "%dm", compression);
+ }
+ else if (ptype == BJC600 || ptype == BJC800)
+ ; /* Currently, nothing to do. */
+ else
+ if (compression)
+ fprintf(prn_stream, "\033*b%dM", compression);
+
+ /* Send each scan line in turn */
+ {
+ int cErr, mErr, yErr, kErr;
+ int this_pass, lnum, i;
+ int start_rows;
+ int lend, num_blank_lines = 0;
+
+ word rmask = ~(word) 0 << ((-pdev->width * storage_bpp) & (W * 8 - 1));
+
+ lend = pdev->height -
+ (int)((dev_t_margin(pdev) + dev_b_margin(pdev)) * y_dpi);
+
+ switch (ptype) {
+ case BJC600:
+ case BJC800:
+ start_rows = BJC_HEAD_ROWS;
+ break;
+
+ /* Inhibit blank line printing for RGB-only printers, since in
+ * this case 'blank' means black! Also disabled for XL300 due to
+ * an obscure bug in the printer's firmware */
+
+ case PJ180:
+ case PJXL180:
+ case PJXL300:
+ start_rows = -1;
+ break;
+
+ default:
+ start_rows = (num_comps == 1) ? HEAD_ROWS_MONO - 1 :
+ HEAD_ROWS_COLOUR - 1;
+ break;
+ }
+
+ cErr = mErr = yErr = kErr = 0;
+
+ if (bits_per_pixel > 4) { /* Randomly seed initial error buffer */
+ if (cprn_device->cmyk > 0 && expanded_bpp == 32) {
+ bjc_fscmyk(data, plane_data, errors, plane_size, -1);
+ } else {
+ int *ep = errors[0];
+ for (i = 0; i < databuff_size; i++) {
+ *ep++ = RANDOM;
+ }
+ }
+ }
+
+ this_pass = start_rows;
+ for (lnum = 0; lnum < lend; lnum++) {
+ word *data_words = (word *)data[scan];
+ register word *end_data = data_words + line_size_words;
+
+ gdev_prn_copy_scan_lines(pdev, lnum, data[scan], line_size);
+
+ /* Mask off 1-bits beyond the line width. */
+ end_data[-1] &= rmask;
+
+ /* Remove trailing 0s. */
+ while (end_data > data_words && end_data[-1] == 0)
+ end_data--;
+ if (ptype != DNJ650C) /* DesignJet can't skip blank lines ? ? */
+ if (end_data == data_words) { /* Blank line */
+ num_blank_lines++;
+ continue;
+ }
+ /* Skip blank lines if any */
+ if (num_blank_lines > 0) {
+ if (ptype == DJ505J) {
+ fprintf(prn_stream,"\033_Y%c%c",
+ num_blank_lines & 0xff, (num_blank_lines >> 8) & 0xff);
+ } else if (ptype == ESC_P) {
+ ep_print_image(prn_stream, &eg, 'B', 0, num_blank_lines);
+ } else if (ptype == BJC600 || ptype == BJC800) {
+ bjc_v_skip(num_blank_lines, pdev, prn_stream);
+ } else if (num_blank_lines < this_pass) {
+ /* Moving down from current position
+ * causes head motion on the DeskJets, so
+ * if the number of lines is within the
+ * current pass of the print head, we're
+ * better off printing blanks. */
+ this_pass -= num_blank_lines;
+ if (combined_escapes) {
+ fputc('y', prn_stream); /* Clear current and seed rows */
+ for (; num_blank_lines; num_blank_lines--)
+ fputc('w', prn_stream);
+ } else {
+#if 0
+/**************** The following code has been proposed ****************/
+/**************** as a replacement: ****************/
+ fputs("\033*b1Y", prn_stream); /* Clear current and seed rows */
+ if ( num_blank_lines > 1 )
+ fprintf(prn_stream, "\033*b%dY", num_blank_lines - 1);
+ num_blank_lines = 0;
+#else
+ fputs("\033*bY", prn_stream); /* Clear current and seed rows */
+ if (ptype == DNJ650C) {
+ fprintf (prn_stream, "\033*b%dY", num_blank_lines);
+ num_blank_lines = 0;
+ }
+ else {
+ for (; num_blank_lines; num_blank_lines--)
+ fputs("\033*bW", prn_stream);
+ }
+#endif
+ }
+ } else {
+ if (combined_escapes)
+ fprintf(prn_stream, "%dy", num_blank_lines);
+ else
+ fprintf(prn_stream, "\033*b%dY", num_blank_lines);
+ }
+ memset(plane_data[1 - scan][0], 0, plane_size * num_comps);
+ num_blank_lines = 0;
+ this_pass = start_rows;
+ }
+ { /* Printing non-blank lines */
+ register byte *kP = plane_data[scan + 2][3];
+ register byte *cP = plane_data[scan + 2][2];
+ register byte *mP = plane_data[scan + 2][1];
+ register byte *yP = plane_data[scan + 2][0];
+ register byte *dp = data[scan + 2];
+ register int *ep = errors[scan];
+ int zero_row_count;
+ int i, j;
+ byte *odp;
+
+ if (this_pass)
+ this_pass--;
+ else
+ this_pass = start_rows;
+
+ if (expanded_bpp > bits_per_pixel) { /* Expand line if required */
+ cdj_expand_line(data_words, line_size,
+ cprn_device->cmyk,
+ bits_per_pixel, expanded_bpp);
+ }
+
+ /* In colour modes, we have some bit-shuffling to do before
+ * we can print the data; in FS mode we also have the
+ * dithering to take care of. */
+ switch (expanded_bpp) { /* Can be 1, 3, 8, 24 or 32 */
+ case 3:
+ /* Transpose the data to get pixel planes. */
+ for (i = 0, odp = plane_data[scan][0]; i < databuff_size;
+ i += 8, odp++) { /* The following is for 16-bit
+ * machines */
+#define spread3(c)\
+ { 0, c, c*0x100, c*0x101, c*0x10000L, c*0x10001L, c*0x10100L, c*0x10101L }
+ static ulong spr40[8] = spread3(0x40);
+ static ulong spr08[8] = spread3(8);
+ static ulong spr02[8] = spread3(2);
+ register byte *dp = data[scan] + i;
+ register ulong pword =
+ (spr40[dp[0]] << 1) +
+ (spr40[dp[1]]) +
+ (spr40[dp[2]] >> 1) +
+ (spr08[dp[3]] << 1) +
+ (spr08[dp[4]]) +
+ (spr08[dp[5]] >> 1) +
+ (spr02[dp[6]]) +
+ (spr02[dp[7]] >> 1);
+ odp[0] = (byte) (pword >> 16);
+ odp[plane_size] = (byte) (pword >> 8);
+ odp[plane_size * 2] = (byte) (pword);
+ }
+ break;
+
+ case 8:
+ switch (ptype) {
+ case BJC600:
+ case BJC800:
+ if (bjcparams.ditheringType == BJC_DITHER_NONE) {
+ COPYline(scan, i, j, plane_size, cP, mP, yP, kP, 1);
+ break;
+ }
+
+ default:
+ FSDline(scan, i, j, plane_size, cErr, mErr, yErr, kErr,
+ cP, mP, yP, kP, 1);
+ }
+ break;
+ case 24:
+ FSDline(scan, i, j, plane_size, cErr, mErr, yErr, kErr,
+ cP, mP, yP, kP, 3);
+ break;
+ case 32:
+ if (cprn_device->cmyk > 0) {
+ bjc_fscmyk(data, plane_data, errors, plane_size, scan);
+ } else {
+ FSDline(scan, i, j, plane_size, cErr, mErr, yErr, kErr,
+ cP, mP, yP, kP, 4);
+ }
+ break;
+
+ } /* switch(expanded_bpp) */
+
+ /* Make sure all black is in the k plane */
+
+ if (num_comps == 4 && (cprn_device->cmyk <= 0 || expanded_bpp != 32)) {
+ register word *kp = (word *)plane_data[scan][3];
+ register word *cp = (word *)plane_data[scan][2];
+ register word *mp = (word *)plane_data[scan][1];
+ register word *yp = (word *)plane_data[scan][0];
+ if (bits_per_pixel > 4) { /* Done as 4 planes */
+ for (i = 0; i < plane_size / W; i++) {
+ word bits = *cp & *mp & *yp;
+ *kp++ |= bits;
+ bits = ~bits;
+ *cp++ &= bits;
+ *mp++ &= bits;
+ *yp++ &= bits;
+ }
+ } else { /* This has really been done as 3 planes */
+ for (i = 0; i < plane_size / W; i++) {
+ word bits = *cp & *mp & *yp;
+ *kp++ = bits;
+ bits = ~bits;
+ *cp++ &= bits;
+ *mp++ &= bits;
+ *yp++ &= bits;
+ }
+ }
+ }
+
+ /* Transfer raster graphics in the order (K), C, M, Y */
+
+ for (zero_row_count = 0, i = num_comps - 1; i >= 0; i--) {
+ int output_plane = 1;
+ int out_count = 0;
+
+ switch (ptype) {
+ case DJ500C: /* Always compress using mode 9 */
+ case DJ550C:
+ out_count = gdev_pcl_mode9compress(plane_size,
+ plane_data[scan][i],
+ plane_data[1 - scan][i],
+ out_data);
+
+ /* This optimisation allows early termination of the
+ * row, but this doesn't work correctly in an alternating
+ * mode 2 / mode 3 regime, so we only use it with mode 9
+ * compression */
+ if (out_count == 0)
+ { output_plane = 0; /* No further output for this plane */
+ if (i == 0)
+ fputc('w', prn_stream);
+ else
+ zero_row_count++;
+ }
+ else
+ { for (; zero_row_count; zero_row_count--)
+ fputc('v', prn_stream);
+ }
+ break;
+ case DJ505J:
+ out_count = gdev_pcl_mode1compress((const byte *)
+ plane_data[scan][i],
+ (const byte *)
+ plane_data[scan][i] + plane_size - 1,
+ out_data);
+ break;
+ case PJ180:
+ case DNJ650C:
+ if (num_comps > 1)
+ { word *wp = (word *)plane_data[scan][i];
+ for (j = 0; j < plane_size / W; j++, wp++)
+ *wp = ~*wp;
+ }
+ out_count = gdev_pcl_mode1compress((const byte *)
+ plane_data[scan][i],
+ (const byte *)
+ plane_data[scan][i] + plane_size - 1,
+ out_data);
+ break;
+ case PJXL180: /* Need to invert data as CMY not supported */
+ if (num_comps > 1)
+ { word *wp = (word *)plane_data[scan][i];
+ for (j = 0; j < plane_size / W; j++, wp++)
+ *wp = ~*wp;
+ }
+ /* fall through .. */
+ case PJXL300: /* Compression modes 2 and 3 are both
+ * available. Try both and see which one
+ * produces the least output data. */
+ case LJ4DITH:
+ { const byte *plane = plane_data[scan][i];
+ byte *prev_plane = plane_data[1 - scan][i];
+ const word *row = (word *)plane;
+ const word *end_row = row + plane_size/W;
+ int count2 = gdev_pcl_mode2compress(row, end_row, out_row_alt);
+ int count3 = gdev_pcl_mode3compress(plane_size, plane, prev_plane, out_row);
+ int penalty = combined_escapes ? strlen("#m") : strlen("\033*b#M");
+ int penalty2 = (compression == 2 ? 0 : penalty);
+ int penalty3 = (compression == 3 ? 0 : penalty);
+
+ if (count3 + penalty3 < count2 + penalty2)
+ { if ( compression != 3 ) {
+ if (combined_escapes)
+ fputs("3m", prn_stream);
+ else
+ fputs("\033*b3M", prn_stream);
+ compression = 3;
+ }
+ out_data = out_row;
+ out_count = count3;
+ }
+ else
+ { if ( compression != 2 ) {
+ if (combined_escapes)
+ fputs("2m", prn_stream);
+ else
+ fputs("\033*b2M", prn_stream);
+ compression = 2;
+ }
+ out_data = out_row_alt;
+ out_count = count2;
+ }
+ }
+ break;
+ case BJC600:
+ case BJC800:
+ { const byte *plane = (byte *)plane_data[scan][i];
+ int count2 = bjc_compress(plane, plane + plane_size, out_row_alt);
+
+ out_data = out_row_alt;
+ out_count = count2;
+ }
+ break;
+ }
+ if (output_plane) {
+ if (ptype == DJ505J)
+ fprintf(prn_stream, "\033_%c%c%c",
+ "WVVV"[i], out_count & 0xff, (out_count >> 8) & 0xff);
+ else if (combined_escapes)
+ fprintf(prn_stream, "%d%c", out_count, "wvvv"[i]);
+ else if (ptype == BJC600 || ptype == BJC800) {
+ if (out_count)
+ bjc_raster_cmd(num_comps == 1 ? 3 : i,
+ out_count, out_data, pdev, prn_stream);
+ if (i == 0) bjc_v_skip(1, pdev, prn_stream);
+ } else if (ptype == ESC_P)
+ ep_print_image(prn_stream, &eg, (char)i, plane_data[scan][i], plane_size);
+ else
+ fprintf(prn_stream, "\033*b%d%c", out_count, "WVVV"[i]);
+ if (ptype < ESC_P)
+ fwrite(out_data, sizeof(byte), out_count, prn_stream);
+ }
+
+ } /* Transfer Raster Graphics ... */
+ if (ptype == ESC_P)
+ ep_print_image(prn_stream, &eg, 'I', 0, 0); /* increment line index */
+ scan = 1 - scan; /* toggle scan direction */
+ } /* Printing non-blank lines */
+ } /* for lnum ... */
+ } /* send each scan line in turn */
+
+ if (combined_escapes)
+ fputs("0M", prn_stream);
+
+ /* end raster graphics */
+ if (ptype == BJC600 || ptype == BJC800) {
+ bjc_finish_page(pdev, prn_stream);
+ } else if (ptype == DJ505J)
+ fputs("\033_C", prn_stream);
+ else if (ptype != ESC_P)
+ fputs("\033*rbC\033E", prn_stream);
+
+ /* eject page */
+ if (ptype == PJ180)
+ fputc('\f', prn_stream);
+ else if (ptype == DJ505J)
+ fputs("\f\033@", prn_stream);
+ else if (ptype == DNJ650C)
+ fputs ("\033*rC\033%0BPG;", prn_stream);
+ else if (ptype == BJC600 || ptype == BJC800)
+ ; /* Already done */
+ else if (ptype == ESC_P) {
+ ep_print_image(prn_stream, &eg, 'F', 0, 0); /* flush print buffer */
+ fputs("\014\033@", prn_stream); /* reset after eject page */
+ } else
+ fputs("\033&l0H", prn_stream);
+
+ /* free temporary storage */
+ gs_free(pdev->memory, (char *) eg.storage, eg.storage_size_words, W, "ep_print_buffer");
+ gs_free(pdev->memory, (char *) storage, storage_size_words, W, "hp_colour_print_page");
+
+ return 0;
+}
+
+/*
+ * Row compression for the H-P PaintJet.
+ * Compresses data from row up to end_row, storing the result
+ * starting at compressed. Returns the number of bytes stored.
+ * The compressed format consists of a byte N followed by a
+ * data byte that is to be repeated N+1 times.
+ * In the worst case, the `compressed' representation is
+ * twice as large as the input.
+ * We complement the bytes at the same time, because
+ * we accumulated the image in complemented form.
+ */
+static int
+gdev_pcl_mode1compress(const byte *row, const byte *end_row, byte *compressed)
+{ register const byte *in = row;
+ register byte *out = compressed;
+ while ( in < end_row )
+ { byte test = *in++;
+ const byte *run = in;
+ while ( in < end_row && *in == test ) in++;
+ /* Note that in - run + 1 is the repetition count. */
+ while ( in - run > 255 )
+ { *out++ = 255;
+ *out++ = test;
+ run += 256;
+ }
+ *out++ = in - run;
+ *out++ = test;
+ }
+ return out - compressed;
+}
+
+/*
+ * Map a CMYK color to a color index. We just use depth / 4 bits per color
+ * to produce the color index.
+ *
+ * Important note: CMYK values are stored in the order K, C, M, Y because of
+ * the way the HP drivers work.
+ *
+ */
+
+#define gx_color_value_to_bits(cv, b) \
+ ((cv) >> (gx_color_value_bits - (b)))
+#define gx_bits_to_color_value(cv, b) \
+ ((cv) << (gx_color_value_bits - (b)))
+
+#define gx_cmyk_value_bits(c, m, y, k, b) \
+ (((COLROUND_ROUND(k)) << (3 * (b))) | \
+ ((COLROUND_ROUND(c)) << (2 * (b))) | \
+ ((COLROUND_ROUND(m)) << (b)) | \
+ ((COLROUND_ROUND(y))))
+
+#define gx_value_cmyk_bits(v, c, m, y, k, b) \
+ (k) = COLDUP_DUP(((v) >> (3 * (b))) & ((1 << (b)) - 1)), \
+ (c) = COLDUP_DUP(((v) >> (2 * (b))) & ((1 << (b)) - 1)), \
+ (m) = COLDUP_DUP(((v) >> (b)) & ((1 << (b)) - 1)), \
+ (y) = COLDUP_DUP((v) & ((1 << (b)) - 1))
+
+static gx_color_index
+gdev_cmyk_map_cmyk_color(gx_device* pdev, const gx_color_value cv[])
+{
+ gx_color_value cyan, magenta, yellow, black;
+ gx_color_index color;
+ cyan = cv[0]; magenta = cv[1]; yellow = cv[2]; black = cv[3];
+ switch (pdev->color_info.depth) {
+ case 1:
+ color = (cyan | magenta | yellow | black) > gx_max_color_value / 2 ?
+ (gx_color_index) 1 : (gx_color_index) 0;
+ break;
+
+ default: {
+ COLROUND_VARS;
+ int nbits = pdev->color_info.depth>>2;
+ COLROUND_SETUP(nbits);
+
+ color = gx_cmyk_value_bits(cyan, magenta, yellow, black, nbits);
+ }
+ }
+
+ return color;
+}
+
+/* Mapping of RGB colors to gray values. */
+
+static gx_color_index
+gdev_cmyk_map_rgb_color(gx_device *pdev, const gx_color_value cv[])
+{
+ gx_color_value r, g, b;
+ r = cv[0]; g = cv[1]; b = cv[2];
+ if (gx_color_value_to_byte(r & g & b) == 0xff) {
+ return (gx_color_index) 0; /* White */
+ } else {
+ gx_color_value c = gx_max_color_value - r;
+ gx_color_value m = gx_max_color_value - g;
+ gx_color_value y = gx_max_color_value - b;
+
+ switch (pdev->color_info.depth) {
+ case 1:
+ return (c | m | y) > gx_max_color_value / 2 ?
+ (gx_color_index) 1 : (gx_color_index) 0;
+ /*NOTREACHED*/
+ break;
+
+ case 8:
+ return ((ulong) c * lum_red_weight * 10
+ + (ulong) m * lum_green_weight * 10
+ + (ulong) y * lum_blue_weight * 10)
+ >> (gx_color_value_bits + 2);
+ /*NOTREACHED*/
+ break;
+ }
+ }
+
+ return (gx_color_index) 0; /* This should never happen. */
+}
+
+/* Mapping of CMYK colors. */
+
+static int
+gdev_cmyk_map_color_cmyk(gx_device *pdev, gx_color_index color, gx_color_value prgb[3])
+{
+ switch (pdev->color_info.depth) {
+ case 1:
+ prgb[0] = gx_max_color_value * (1 - color);
+ break;
+
+ case 8:
+ if (pdev->color_info.num_components == 1) {
+ gx_color_value value = (gx_color_value) color ^ 0xff;
+
+ prgb[0] = (value << 8) + value;
+
+ break;
+ }
+
+ default: {
+ unsigned long bcyan, bmagenta, byellow, black;
+ int nbits = pdev->color_info.depth>>2;
+ COLDUP_VARS;
+
+ COLDUP_SETUP(nbits);
+ gx_value_cmyk_bits(color, bcyan, bmagenta, byellow, black, nbits);
+
+ prgb[0] = bcyan;
+ prgb[1] = bmagenta;
+ prgb[2] = byellow;
+ prgb[3] = black;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * Map a r-g-b color to a color index.
+ * We complement the colours, since we're using cmy anyway, and
+ * because the buffering routines expect white to be zero.
+ * Includes colour balancing, following HP recommendations, to try
+ * and correct the greenish cast resulting from an equal mix of the
+ * c, m, y, inks by reducing the cyan component to give a truer black.
+ */
+
+/* Simple black generation/under-color removal with BG(k) = UG(k) = k. YA. */
+
+#define bg_and_ucr(c, c_v, m, m_v, y, y_v, k) \
+ do { \
+ register byte cv = c_v, mv = m_v, yv = y_v, kv; \
+ \
+ kv = (cv > mv ? mv : cv); \
+ kv = (yv > k ? k : y); \
+ y = yv - kv; m = mv - kv; c = cv -kv; k = kv; \
+ } while (0)
+
+static gx_color_index
+gdev_pcl_map_rgb_color(gx_device *pdev, const gx_color_value cv[])
+{
+ gx_color_value r, g, b;
+ r = cv[0]; g = cv[1]; b = cv[2];
+ if (gx_color_value_to_byte(r & g & b) == 0xff)
+ return (gx_color_index)0; /* white */
+ else {
+ int correction = cprn_device->correction;
+ gx_color_value c = gx_max_color_value - r;
+ gx_color_value m = gx_max_color_value - g;
+ gx_color_value y = gx_max_color_value - b;
+
+ /* Colour correction for better blacks when using the colour ink
+ * cartridge (on the DeskJet 500C only). We reduce the cyan component
+ * by some fraction (eg. 4/5) to correct the slightly greenish cast
+ * resulting from an equal mix of the three inks */
+ if (correction) {
+ ulong maxval, minval, range;
+
+ maxval = c >= m ? (c >= y ? c : y) : (m >= y ? m : y);
+ if (maxval > 0) {
+ minval = c <= m ? (c <= y ? c : y) : (m <= y? m : y);
+ range = maxval - minval;
+
+#define shift (gx_color_value_bits - 12)
+ c = ((c >> shift) * (range + (maxval * correction))) /
+ ((maxval * (correction + 1)) >> shift);
+ }
+ }
+
+ switch (pdev->color_info.depth) {
+ case 1:
+ return ((c | m | y) > gx_max_color_value / 2 ?
+ (gx_color_index)1 : (gx_color_index)0);
+ case 8:
+ if (pdev->color_info.num_components >= 3)
+#define gx_color_value_to_1bit(cv) ((cv) >> (gx_color_value_bits - 1))
+ return (gx_color_value_to_1bit(c) +
+ (gx_color_value_to_1bit(m) << 1) +
+ (gx_color_value_to_1bit(y) << 2));
+ else
+#define red_weight 306
+#define green_weight 601
+#define blue_weight 117
+ return ((((ulong)c * red_weight +
+ (ulong)m * green_weight +
+ (ulong)y * blue_weight)
+ >> (gx_color_value_bits + 2)));
+ case 16:
+ /* FIXME: Simple truncation is not ideal. Should round really. */
+#define gx_color_value_to_5bits(cv) ((cv) >> (gx_color_value_bits - 5))
+#define gx_color_value_to_6bits(cv) ((cv) >> (gx_color_value_bits - 6))
+ return (gx_color_value_to_5bits(y) +
+ (gx_color_value_to_6bits(m) << 5) +
+ (gx_color_value_to_5bits(c) << 11));
+ case 24:
+ return (gx_color_value_to_byte(y) +
+ (gx_color_value_to_byte(m) << 8) +
+ ((ulong)gx_color_value_to_byte(c) << 16));
+ case 32:
+ { return ((c == m && c == y) ? ((ulong)gx_color_value_to_byte(c) << 24)
+ : (gx_color_value_to_byte(y) +
+ (gx_color_value_to_byte(m) << 8) +
+ ((ulong)gx_color_value_to_byte(c) << 16)));
+ }
+ }
+ }
+ return (gx_color_index)0; /* This never happens */
+}
+
+/* Map a color index to a r-g-b color. */
+static int
+gdev_pcl_map_color_rgb(gx_device *pdev, gx_color_index color,
+ gx_color_value prgb[3])
+{
+ /* For the moment, we simply ignore any black correction */
+ switch (pdev->color_info.depth) {
+ case 1:
+ prgb[0] = prgb[1] = prgb[2] = -((gx_color_value)color ^ 1);
+ break;
+ case 8:
+ if (pdev->color_info.num_components >= 3)
+ { gx_color_value c = (gx_color_value)color ^ 7;
+ prgb[0] = -(c & 1);
+ prgb[1] = -((c >> 1) & 1);
+ prgb[2] = -(c >> 2);
+ }
+ else
+ { gx_color_value value = (gx_color_value)color ^ 0xff;
+ prgb[0] = prgb[1] = prgb[2] = (value << 8) + value;
+ }
+ break;
+ case 16:
+ { gx_color_value c = (gx_color_value)color ^ 0xffff;
+ ushort value = c >> 11;
+ prgb[0] = ((value << 11) + (value << 6) + (value << 1) +
+ (value >> 4)) >> (16 - gx_color_value_bits);
+ value = (c >> 6) & 0x3f;
+ prgb[1] = ((value << 10) + (value << 4) + (value >> 2))
+ >> (16 - gx_color_value_bits);
+ value = c & 0x1f;
+ prgb[2] = ((value << 11) + (value << 6) + (value << 1) +
+ (value >> 4)) >> (16 - gx_color_value_bits);
+ }
+ break;
+ case 24:
+ { gx_color_index c = color ^ 0xffffff;
+ prgb[0] = gx_color_value_from_byte((gx_color_value)(c >> 16));
+ prgb[1] = gx_color_value_from_byte((gx_color_value)((c >> 8) & 0xff));
+ prgb[2] = gx_color_value_from_byte((gx_color_value)(c & 0xff));
+ }
+ break;
+ case 32:
+#define gx_maxcol gx_color_value_from_byte(gx_color_value_to_byte(gx_max_color_value))
+ { gx_color_value w = gx_maxcol - gx_color_value_from_byte(color >> 24);
+ prgb[0] = w - gx_color_value_from_byte((color >> 16) & 0xff);
+ prgb[1] = w - gx_color_value_from_byte((color >> 8) & 0xff);
+ prgb[2] = w - gx_color_value_from_byte(color & 0xff);
+ }
+ break;
+ }
+ return 0;
+}
+
+/*
+ * Convert and expand scanlines:
+ *
+ * For devices with 3 components:
+ *
+ * (a) 16 -> 24 bit (1-stage)
+ * (b) 16 -> 32 bit (2-stage)
+ * or (c) 24 -> 32 bit (1-stage)
+ *
+ * For devices with 4 components:
+ *
+ * (a) 16 -> 32 bit (1-stage)
+ * (b) 8 -> 32 bit (2-stage)
+ * or (c) 24 -> 32 bit (1-stage)
+ *
+ */
+
+static void
+cdj_expand_line(word *line, int linesize, short cmyk, int bpp, int ebpp)
+{
+ int endline = linesize;
+ byte *start = (byte *)line;
+ register byte *in, *out;
+
+ if (cmyk > 0) {
+ if (bpp == 8) {
+ in = start + endline;
+ out = start + (endline *= 2);
+
+ while (in > start) {
+ register byte b0;
+ register byte bs0, bs1, bs2, bs3;
+
+ b0 = *--in;
+
+ bs0 = b0 & 0x03;
+ bs1 = (b0 >> 2) & 0x03;
+ bs2 = (b0 >> 4) & 0x03;
+ bs3 = (b0 >> 6) & 0x03;
+
+ *--out = (bs0 << 2) + bs0 + (bs1 << 6) + (bs1 << 4);
+ *--out = (bs2 << 2) + bs2 + (bs3 << 6) + (bs3 << 4);
+ }
+ }
+
+ if (bpp == 24) {
+ endline = (endline + 2) / 3;
+
+ in = start + endline * 3;
+ out = start + endline * 4;
+
+ while (in > start) {
+ register byte b0, b1, b2;
+
+ b0 = *--in;
+ b1 = *--in;
+ b2 = *--in;
+
+ *--out = (b0 << 2) + ((b0 >> 4) & 0x03);
+ *--out = ((b1 & 0x0f) << 4) + ((b0 >> 6) << 2)
+ + ((b1 >> 2) & 0x03);
+ *--out = ((b2 & 0x03) << 6) + ((b1 >> 4) << 2) + (b2 & 0x03);
+ *--out = (b2 & 0xfc) + ((b2 >> 6) & 0x03);
+ }
+ } else if (ebpp == 32) {
+ endline = (endline + 1) / 2;
+
+ in = start + endline * 2;
+ out = start + (endline *= 4);
+
+ while (in > start) {
+ register byte b0, b1;
+
+ b0 = *--in;
+ b1 = *--in;
+
+ *--out = (b0 << 4) + ((b0 >> 4) & 0x07);
+ *--out = (b0 & 0xf0) + ((b0 >> 4) & 0xf);
+ *--out = (b1 << 4) + ((b1 >> 4) & 0x0f);
+ *--out = (b1 & 0xf0) + ((b1 >> 4) & 0xf);
+ }
+ }
+ } else /* cmyk > 0 */ {
+ if (bpp == 16) /* 16 to 24 (cmy) if required */
+ { register byte b0, b1;
+ endline = ((endline + 1) / 2);
+ in = start + endline * 2;
+ out = start + (endline *= 3);
+
+ while (in > start)
+ { b0 = *--in;
+ b1 = *--in;
+ *--out = (b0 << 3) + ((b0 >> 2) & 0x7);
+ *--out = (b1 << 5) + ((b0 >> 3) & 0x1c) + ((b1 >> 1) & 0x3);
+ *--out = (b1 & 0xf8) + (b1 >> 5);
+ }
+ }
+
+ if (ebpp == 32) /* 24/32 (cmy) to 32 (cmyk) if required */
+ { register byte c, m, y;
+ endline = ((endline + 2) / 3);
+ in = start + endline * 3;
+ out = start + endline * 4;
+
+ while (in > start)
+ {
+ y = *--in;
+ m = *--in;
+ c = *--in;
+
+ if (c == y && c == m) {
+ *--out = 0, *--out = 0, *--out = 0;
+ *--out = c;
+ } else {
+ *--out = y, *--out = m, *--out = c;
+ *--out = 0;
+ }
+ }
+ }
+ }
+}
+
+static int
+cdj_put_param_int(gs_param_list *plist, gs_param_name pname, int *pvalue,
+ int minval, int maxval, int ecode)
+{ int code, value;
+ switch ( code = param_read_int(plist, pname, &value) )
+ {
+ default:
+ return code;
+ case 1:
+ return ecode;
+ case 0:
+ if ( value < minval || value > maxval )
+ param_signal_error(plist, pname, gs_error_rangecheck);
+ *pvalue = value;
+ return (ecode < 0 ? ecode : 1);
+ }
+}
+
+static int
+cdj_set_bpp(gx_device *pdev, int bpp, int ccomps)
+{ gx_device_color_info *ci = &pdev->color_info;
+
+ if (ccomps && bpp == 0) {
+ if (cprn_device->cmyk) {
+ switch (ccomps) {
+ default:
+ return gs_error_rangecheck;
+ /*NOTREACHED*/
+ break;
+
+ case 1:
+ bpp = 1;
+ break;
+
+ case 3:
+ bpp = 24;
+ break;
+
+ case 4:
+ switch (ci->depth) {
+ case 8:
+ case 16:
+ case 24:
+ case 32:
+ break;
+
+ default:
+ bpp = cprn_device->default_depth;
+ break;
+ }
+ break;
+ }
+ }
+ }
+
+ if (bpp == 0) {
+ bpp = ci->depth; /* Use the current setting. */
+ }
+
+ if (cprn_device->cmyk < 0) {
+
+ /* Reset procedures because we may have been in another mode. */
+
+ dev_proc(pdev, encode_color) = gdev_cmyk_map_cmyk_color;
+ dev_proc(pdev, map_rgb_color) = NULL;
+ dev_proc(pdev, decode_color) = gdev_cmyk_map_color_cmyk;
+
+ if (pdev->is_open) gs_closedevice(pdev);
+ }
+
+ /* Check for valid bpp values */
+
+ switch ( bpp )
+ {
+ case 16:
+ case 32:
+ if (cprn_device->cmyk && ccomps && ccomps != 4) goto bppe;
+ break;
+
+ case 24:
+ if (!cprn_device->cmyk || ccomps == 0 || ccomps == 4) {
+ break;
+ } else if (ccomps == 1) {
+ goto bppe;
+ } else {
+
+ /* 3 components 24 bpp printing for CMYK device. */
+
+ cprn_device->cmyk = -1;
+ }
+ break;
+
+ case 8:
+ if (cprn_device->cmyk) {
+ if (ccomps) {
+ if (ccomps == 3) {
+ cprn_device->cmyk = -1;
+ bpp = 3;
+ } else if (ccomps != 1 && ccomps != 4) {
+ goto bppe;
+ }
+ }
+ if (ccomps != 1) break;
+ } else {
+ break;
+ }
+
+ case 1:
+ if (ccomps != 1) goto bppe;
+
+ if (cprn_device->cmyk && bpp != pdev->color_info.depth) {
+ dev_proc(pdev, map_cmyk_color) = NULL;
+ dev_proc(pdev, map_rgb_color) = gdev_cmyk_map_rgb_color;
+
+ if (pdev->is_open) {
+ gs_closedevice(pdev);
+ }
+ }
+ break;
+
+ case 3:
+ if (!cprn_device->cmyk) {
+ break;
+ }
+
+ default:
+bppe: return gs_error_rangecheck;
+ }
+
+ if (cprn_device->cmyk == -1) {
+ dev_proc(pdev, map_cmyk_color) = NULL;
+ dev_proc(pdev, map_rgb_color) = gdev_pcl_map_rgb_color;
+ dev_proc(pdev, map_color_rgb) = gdev_pcl_map_color_rgb;
+
+ if (pdev->is_open) {
+ gs_closedevice(pdev);
+ }
+ if (pdev->is_open) {
+ int code; /* Return code */
+ gdev_prn_space_params sp; /* Space parameter data */
+
+ /* Reallocate memory for device */
+ sp = ((gx_device_printer *)pdev)->space_params;
+
+ if ((code = gdev_prn_reallocate_memory(pdev, &sp, pdev->width,
+ pdev->height)) < 0)
+ return (code);
+ }
+ }
+
+ switch (ccomps) {
+ case 0:
+ break;
+
+ case 1:
+ if (bpp != 1 && bpp != 8) goto cce;
+ break;
+
+ case 4:
+ if (cprn_device->cmyk) {
+ if (bpp >= 8) break;
+ }
+
+ case 3:
+ if (bpp == 1 || bpp == 3 || bpp == 8 || bpp == 16
+ || bpp == 24 || bpp == 32) {
+ break;
+ }
+
+cce: default: return gs_error_rangecheck;
+ }
+
+ if (cprn_device->cmyk) {
+ if (cprn_device->cmyk > 0) {
+ ci->num_components = ccomps ? ccomps : (bpp < 8 ? 1 : 4);
+ } else {
+ ci->num_components = ccomps ? ccomps : (bpp < 8 ? 1 : 3);
+ }
+ if (bpp != 1 && ci->num_components == 1) { /* We do dithered grays. */
+ bpp = bpp < 8 ? 8 : bpp;
+ }
+
+ ci->max_color = (1 << (bpp >> 2)) - 1;
+ ci->max_gray = (bpp >= 8 ? 255 : 1);
+
+ if (ci->num_components == 1) {
+ ci->dither_grays = (bpp >= 8 ? 256 : 2);
+ ci->dither_colors = (bpp >= 8 ? 256 : bpp > 1 ? 2 : 0);
+ } else {
+ ci->dither_grays = (bpp > 8 ? 256 : 2);
+ ci->dither_colors = (bpp > 8 ? 256 : bpp > 1 ? 2 : 0);
+ }
+ } else {
+ ci->num_components = (bpp == 1 || bpp == 8 ? 1 : 3);
+ ci->max_color = (bpp >= 8 ? 255 : bpp > 1 ? 1 : 0);
+ ci->max_gray = (bpp >= 8 ? 255 : 1);
+ ci->dither_grays = (bpp >= 8 ? 256 : 2);
+ ci->dither_colors = (bpp >= 8 ? 256 : bpp > 1 ? 2 : 0);
+ }
+
+ ci->depth = ((bpp > 1) && (bpp < 8) ? 8 : bpp);
+
+ return 0;
+}
+
+/* new_bpp == save_bpp or new_bpp == 0 means don't change bpp.
+ ccomps == 0 means don't change number of color comps.
+ If new_bpp != 0, it must be the value of the BitsPerPixel element of
+ the plist; real_bpp may differ from new_bpp.
+*/
+static int
+cdj_put_param_bpp(gx_device *pdev, gs_param_list *plist, int new_bpp,
+ int real_bpp, int ccomps)
+{
+ if (new_bpp == 0 && ccomps == 0)
+ return gdev_prn_put_params(pdev, plist);
+ else
+ {
+ gx_device_color_info save_info;
+ int save_bpp;
+ int code;
+
+ save_info = pdev->color_info;
+ save_bpp = save_info.depth;
+#define save_ccomps save_info.num_components
+ if ( save_bpp == 8 && save_ccomps == 3 && !cprn_device->cmyk)
+ save_bpp = 3;
+ code = cdj_set_bpp(pdev, real_bpp, ccomps);
+ if ( code < 0 ) {
+ param_signal_error(plist, "BitsPerPixel", code);
+ param_signal_error(plist, "ProcessColorModel", code);
+ return code;
+ }
+ pdev->color_info.depth = new_bpp; /* cdj_set_bpp maps 3/6 to 8 */
+ code = gdev_prn_put_params(pdev, plist);
+ if ( code < 0 )
+ { cdj_set_bpp(pdev, save_bpp, save_ccomps);
+ return code;
+ }
+ cdj_set_bpp(pdev, real_bpp, ccomps); /* reset depth if needed */
+ if ((cdj->color_info.depth != save_bpp ||
+ (ccomps != 0 && ccomps != save_ccomps))
+ && pdev->is_open )
+ return gs_closedevice(pdev);
+ return 0;
+#undef save_ccomps
+ }
+}
+
+/* This returns either the number of pixels in a scan line, or the number
+ * of bytes required to store the line, both clipped to the page margins */
+static uint
+gdev_prn_rasterwidth(const gx_device_printer *pdev, int pixelcount)
+{
+ ulong raster_width = (ulong)(pdev->width -
+ pdev->x_pixels_per_inch * (dev_l_margin(pdev) + dev_r_margin(pdev)));
+ return (pixelcount ?
+ (uint)raster_width :
+ (uint)((raster_width * pdev->color_info.depth + 7) >> 3));
+}
+
+/* Functions for manipulation params strings */
+
+static const byte*
+paramValueToString(const stringParamDescription* params, int value)
+{
+
+ for (; params->p_name; ++params) {
+ if (params->p_value == value) {
+ return (const byte *)params->p_name;
+ }
+ }
+
+ return (const byte*) 0;
+}
+
+static int
+paramStringValue(const stringParamDescription* params,
+ const byte* name, int namelen, int* value)
+{
+
+ for (; params->p_name; ++params) {
+ if (strncmp(params->p_name, (char *)name, namelen) == 0 &&
+ params->p_name[namelen] == 0) {
+ *value = params->p_value;
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+static int
+put_param_string(gs_param_list* plist,
+ const byte* pname, gs_param_string* pstring,
+ const stringParamDescription* params, int *pvalue, int code)
+{
+
+ int ncode;
+
+ if ((ncode = param_read_string(plist, (char *)pname, pstring)) < 0) {
+ param_signal_error(plist, (char *)pname, code = ncode);
+ } else if (ncode == 1) {
+ pstring->data = 0, pstring->size = 0;
+ } else {
+ int value = 0;
+
+ if (paramStringValue(params, pstring->data, pstring->size,
+ &value) == 0) {
+ param_signal_error(plist, (char *)pname, code = gs_error_rangecheck);
+ } else {
+ *pvalue = value;
+ }
+ }
+
+ return code;
+}
+
+static int
+get_param_string(gs_param_list* plist,
+ const byte* pname, gs_param_string* pstring,
+ const stringParamDescription* params, int pvalue, bool persist, int code)
+{
+
+ int ncode;
+
+ pstring->data = paramValueToString(params, pvalue);
+
+ if (pstring->data == (byte*) 0) {
+ param_signal_error(plist, (char *)pname, ncode = gs_error_unknownerror);
+ } else {
+ pstring->size = strlen((char *)pstring->data);
+ pstring->persistent = persist;
+ }
+
+ if ((ncode = param_write_string(plist, (char *)pname, pstring)) < 0) {
+ code = ncode;
+ }
+
+ return code;
+}
+
+/*
+ * This taken from gsdparam.c. I hope it will be useable directly some day.
+ *
+ */
+
+static int
+cdj_param_check_bytes(gs_param_list *plist, gs_param_name pname,
+ const byte *str, uint size, bool is_defined)
+{ int code;
+ gs_param_string new_value;
+ switch ( code = param_read_string(plist, pname, &new_value) )
+ {
+ case 0:
+ if ( is_defined && new_value.size == size &&
+ !memcmp((const char *)str, (const char *)new_value.data,
+ size)
+ )
+ break;
+ code = gs_note_error(gs_error_rangecheck);
+ goto e;
+ default:
+ if ( param_read_null(plist, pname) == 0 )
+ return 1;
+e: param_signal_error(plist, pname, code);
+ case 1:
+ ;
+ }
+ return code;
+}
+
+/* This is original code. */
+
+static int
+cdj_param_check_float(gs_param_list *plist, gs_param_name pname, double fval,
+ bool is_defined)
+{ int code;
+ float new_value;
+ switch ( code = param_read_float(plist, pname, &new_value) )
+ {
+ case 0:
+ if ( is_defined && new_value == (float)fval)
+ break;
+ code = gs_note_error(gs_error_rangecheck);
+ goto e;
+ default:
+ if ( param_read_null(plist, pname) == 0 )
+ return 1;
+e: param_signal_error(plist, pname, code);
+ case 1:
+ ;
+ }
+ return code;
+}
+
+/* The following dithering algorithm has been kindly given to me (YA) by
+ * Klaus-Gunther Hess, I just adapted it for use with the code here. */
+
+/*
+
+(From KGH:)
+
+Just about the features of the code:
+
+ - Stored Color-Values are BYTES in the order C-M-Y-K.
+ (Indices need to change with gdevcdj.c)
+
+ - There are individual THRESHOLDs and SPOTSIZEs for
+ the color-components. The following relation should
+ be maintained:
+ SPOTSIZE = 2 * THRESHOLD + 1
+ (The internal calculation is dedicated for limiting
+ ink-density at the 720x720DpI-Resolution of the
+ Epson-Printers, without loss of dynamic color-range)
+
+ - In addition to that there are EMIN & EMAX-Values
+ for the components. The Values are computed from
+ the dithering-algorithm and can be replaced by
+ constants, if neither the implementation nor
+ THRESHOLD and SPOTSIZE can change.
+
+ - The algorithm is tuned for speed. (K-only, if gray-
+ levels are detected, with EMIN/EMAX-clipping of
+ stored CMY-Errors. [Notice: cerr, merr, yerr are
+ *not* reset to zero! Clearing them would cause
+ regular patterns & "Halos" to appear!])
+
+*/
+
+/*
+ * Macros, that represent the undisturbed dithering-algorithm
+ *
+ * FSerror: compute the desired Value
+ * FSdecide: decision based on the value computed by FSerror
+ * FSdiffuse: distribute remaining error among pixels
+ */
+
+#define FSerror(Val,Erow,Ecol) (Val + Erow + ((7 * Ecol)>>4))
+
+#define FSdecide(Error,Threshold,Spotsize,Pixel,Bit) \
+ if(Error > Threshold) {\
+ Pixel |= Bit;\
+ Error -= Spotsize;\
+ }
+
+#define FSdiffuse(Error,Erow,Ecol,Eprev)\
+ Eprev += (3 * Error + 8)>>4;\
+ Erow = (5 * Error + Ecol + 8)>>4;\
+ Ecol = Error;
+
+/*
+ * some aliases for values from the device-structure
+ */
+#define DIRECTION direction[0]
+#define CMYK_THRESHOLD(I) threshold[I]
+#define SPOTSIZE(I) spotsize[I]
+#define EMIN(I) emin[I]
+#define EMAX(I) emax[I]
+#define NPIXEL (plane_size * 8)
+
+#define IDX_C 1
+#define IDX_M 2
+#define IDX_Y 3
+#define IDX_K 0
+
+#define ODX_C 2
+#define ODX_M 1
+#define ODX_Y 0
+#define ODX_K 3
+
+static int
+bjc_fscmyk(byte** inplanes, byte* outplanes[4][4], int** errplanes,
+ int plane_size, int scan) {
+
+ byte* err = (byte*) errplanes[0];
+
+/* =========================================================== */
+ if(scan < 0) { /* scan < 0 -> initialize private buffer */
+/* =========================================================== */
+
+ int p,i,v;
+ int *direction,*threshold,*spotsize,*emin,*emax;
+ int *errv,*errc;
+/*
+ * allocate the error-buffer
+ */
+ /*KGHorig
+ i = 4 * (5 + 1 + 1 + sd->stc.prt_pixels + 1) * sizeof(errv[0]);
+ if((sd->stc.err_size < i) || (NULL == sd->stc.err)) {
+ if(NULL != sd->stc.err)
+ gs_free(sd->stc.err,sd->stc.err_size,1,"stcm/err");
+ sd->stc.err_size = i;
+ sd->stc.err = gs_malloc(sd->stc.err_size,1,"stcm/err");
+ if(sd->stc.err == NULL) return_error(gs_error_VMerror);
+ }
+ */
+
+ direction = (int *) err;
+ threshold = direction + 4;
+ spotsize = threshold + 4;
+ emin = spotsize + 4;
+ emax = emin + 4;
+ errc = emax + 4;
+ errv = errc + 2*4;
+/*
+ * compute initial values
+ */
+ DIRECTION = -1;
+ for(i = 0; i < 4; ++i) {
+ int j;
+ float maxv = 1.0;
+ /*KGHorig
+ if((sd->stc.xfer[i].size < 1) || (sd->stc.xfer[i].data == NULL)) {
+ maxv = 1.0;
+ } else {
+ maxv = 1.0/255.0;
+ for(j = 0; j < sd->stc.xfer[i].size; ++j)
+ if(maxv < sd->stc.xfer[i].data[j])
+ maxv = sd->stc.xfer[i].data[j];
+ }
+ */
+ CMYK_THRESHOLD(i) = (int)(127.0 / maxv + 0.5);
+ SPOTSIZE(i) = ((int) CMYK_THRESHOLD(i)<<1)+1;
+ j = CMYK_THRESHOLD(i); /* Maximum Error-Value */
+ errc[3] = 0;
+ FSdiffuse(CMYK_THRESHOLD(i),errv[0],errc[0],errv[-4]);
+ FSdiffuse(CMYK_THRESHOLD(i),errv[0],errc[0],errv[-4]);
+ EMAX(i) = errv[0];
+ errc[0] = 0;
+ FSdiffuse((-CMYK_THRESHOLD(i)),errv[0],errc[0],errv[-4]);
+ FSdiffuse((-CMYK_THRESHOLD(i)),errv[0],errc[0],errv[-4]);
+ EMIN(i) = errv[0];
+ }
+
+#ifdef CDJ_DEBUG_FS
+ for(i = 0; i < 4; ++i) errprintf_nomem(
+ "CMYK_THRESHOLD(%d)=%5d, spotsize(%d)=%5d, emin(%d)=%5d, emax(%d)=%5d\n",
+ i,CMYK_THRESHOLD(i),i,SPOTSIZE(i),i,EMIN(i),i,EMAX(i));
+#endif
+
+ for(i = 0; i < 4; ++i) errc[i] = 0;
+
+ for(p = 0; p < NPIXEL; ++p) {
+ for(i = 0; i < 4; ++i) {
+ /*KHGOrig
+ if(sd->stc.flags & STCDFLAG0) v = 0;
+ */
+ if (0) v = 0; /* Must provide a default for that. */
+ else v = (rand() % SPOTSIZE(i)) - CMYK_THRESHOLD(i);
+ FSdiffuse(v,errv[i],errc[i],errv[i-4]);
+ }
+ errv += i;
+ }
+
+/* =========================================================== */
+ } else { /* scan >= 0 -> scanline-processing */
+/* =========================================================== */
+
+ int w,p,dir,thedir;
+ byte *out[4],pixel[4],bit;
+ /*KGHorig
+ int *width = outplanes[scan];
+ */
+ int *direction = (int *) err;
+ int *threshold = direction + 4;
+ int *spotsize = threshold + 4;
+ int *emin = spotsize + 4;
+ int *emax = emin + 4;
+ int *errc = emax + 4;
+ int *errv = errc + 2*4;
+ int kerr,cerr,merr,yerr;
+
+ byte* in;
+
+ /*KGHorig
+ if(sd->stc.flags & STCDFLAG1) {
+ */
+ if (0) { /* Eventually will provide a flag for this. */
+ cerr = merr = yerr = kerr = 0;
+ } else {
+ cerr = errc[0];
+ merr = errc[1];
+ yerr = errc[2];
+ kerr = errc[3];
+ }
+
+ out[0] = outplanes[scan + 2][ODX_C];
+ out[1] = outplanes[scan + 2][ODX_M];
+ out[2] = outplanes[scan + 2][ODX_Y];
+ out[3] = outplanes[scan + 2][ODX_K];
+ pixel[0] = pixel[1] = pixel[2] = pixel[3] = 0;
+
+ if(DIRECTION < 0) { /* scan == 0, run backward */
+ w = NPIXEL;
+ in = inplanes[2] + 4 * (NPIXEL - 1);
+ errv += (w-1)<<2;
+ dir = -4;
+ /*KGHorig
+ if(w > 8) for(p = 0; p < 4; ++p) out[p] += (w-1)>>3;
+ */
+ thedir = -1;
+ for (p = 0; p < 4; ++p) {
+ out[p] += plane_size - 1;
+ }
+ } else { /* run forward */
+ w = 1;
+ in = inplanes[3] - 4 * NPIXEL;
+ dir = 4;
+ thedir = 1;
+ for (p = 0; p < 4; ++p) {
+ out[p] -= plane_size;
+ }
+ } /* run backward/forward */
+
+ /*KGHorig
+ if(0 == (sd->stc.flags & STCDFLAG1)) DIRECTION = -DIRECTION;
+ */
+ if (1) DIRECTION = -DIRECTION; /* Scan in other direction. */
+
+ bit = 0x80>>((w-1) & 7);
+ w = (w+7)>>3;
+
+ for(p = NPIXEL; p; --p) { /* loop over pixels */
+
+ int cmy = in[IDX_C] | in[IDX_M] | in[IDX_Y];
+ int kv = FSerror(in[IDX_K],errv[3],kerr);
+ int cv;
+
+ FSdecide(kv,CMYK_THRESHOLD(3),SPOTSIZE(3),pixel[3],bit);
+
+ if(cmy) {
+
+ if(pixel[3] & bit) { /* black known to fire */
+
+ FSdiffuse(kv,errv[3],kerr,errv[3-dir]);
+
+ cv = FSerror(in[IDX_C],errv[0],cerr);
+ cv -= SPOTSIZE(0);
+ if ((cv+CMYK_THRESHOLD(0)) < 0) cv = -CMYK_THRESHOLD(0);
+ FSdiffuse(cv,errv[0],cerr,errv[0-dir]);
+
+ cv = FSerror(in[IDX_M],errv[1],merr);
+ cv -= SPOTSIZE(1);
+ if ((cv+CMYK_THRESHOLD(1)) < 0) cv = -CMYK_THRESHOLD(1);
+
+ FSdiffuse(cv,errv[1],merr,errv[1-dir]);
+
+ cv = FSerror(in[IDX_Y],errv[2],yerr);
+ cv -= SPOTSIZE(2);
+ if ((cv+CMYK_THRESHOLD(2)) < 0) cv = -CMYK_THRESHOLD(2);
+ FSdiffuse(cv,errv[2],yerr,errv[2-dir]);
+
+ } else {
+
+ cv = FSerror(in[IDX_C],errv[0],cerr);
+ FSdecide(cv,CMYK_THRESHOLD(0),SPOTSIZE(0),pixel[0],bit);
+ FSdiffuse(cv,errv[0],cerr,errv[0-dir]);
+
+ cv = FSerror(in[IDX_M],errv[1],merr);
+ FSdecide(cv,CMYK_THRESHOLD(1),SPOTSIZE(1),pixel[1],bit);
+ FSdiffuse(cv,errv[1],merr,errv[1-dir]);
+
+ cv = FSerror(in[IDX_Y],errv[2],yerr);
+ FSdecide(cv,CMYK_THRESHOLD(2),SPOTSIZE(2),pixel[2],bit);
+ FSdiffuse(cv,errv[2],yerr,errv[2-dir]);
+
+ if(pixel[0] & pixel[1] & pixel[2] & bit) {
+ pixel[0] &= ~bit;
+ pixel[1] &= ~bit;
+ pixel[2] &= ~bit;
+ pixel[3] |= bit;
+ kv -= SPOTSIZE(3);
+ if ((kv+CMYK_THRESHOLD(3)) < 0) kv = -CMYK_THRESHOLD(0);
+ FSdiffuse(kv,errv[3],kerr,errv[3-dir]);
+ }
+ }
+
+ } else {
+
+ FSdiffuse(kv,errv[3],kerr,errv[3-dir]);
+
+ if( errv[0] > EMAX(0)) errv[0] = EMAX(0);
+ else if(errv[0] < EMIN(0)) errv[0] = EMIN(0);
+
+ if( errv[1] > EMAX(1)) errv[1] = EMAX(1);
+ else if(errv[1] < EMIN(1)) errv[1] = EMIN(1);
+
+ if( errv[2] > EMAX(2)) errv[2] = EMAX(2);
+ else if(errv[2] < EMIN(2)) errv[2] = EMIN(2);
+
+ }
+
+/*
+ * Adjust indices
+ */
+ bit = dir > 0 ? (bit>>1) : (bit<<1);
+ if(bit == 0) {
+ /*KGHorig
+ if(((*out[0] = pixel[0]) != 0) && (width[0] < w)) width[0] = w;
+ if(((*out[1] = pixel[1]) != 0) && (width[1] < w)) width[1] = w;
+ if(((*out[2] = pixel[2]) != 0) && (width[2] < w)) width[2] = w;
+ if(((*out[3] = pixel[3]) != 0) && (width[3] < w)) width[3] = w;
+ */
+ *out[0] = pixel[0];
+ *out[1] = pixel[1];
+ *out[2] = pixel[2];
+ *out[3] = pixel[3];
+ out[0] += thedir; out[1] += thedir;
+ out[2] += thedir; out[3] += thedir;
+ pixel[0] = pixel[1] = pixel[2] = pixel[3] = 0;
+
+ if(dir > 0) bit = 0x80;
+ else bit = 0x01;
+ w += dir>>2;
+ }
+
+ in += dir;
+ errv += dir;
+ } /* loop over pixels */
+
+ /*KGHorig
+ if(0 == (sd->stc.flags & STCDFLAG1)) {
+ */
+ if (1) {
+ cerr = errc[0] = cerr;
+ merr = errc[1] = merr;
+ yerr = errc[2] = yerr;
+ kerr = errc[3] = kerr;
+ }
+
+/* =========================================================== */
+ } /* initialization or scanline-Processing */
+/* =========================================================== */
+
+ return 0;
+}
diff --git a/devices/gdevcfax.c b/devices/gdevcfax.c
new file mode 100644
index 000000000..d0a993aab
--- /dev/null
+++ b/devices/gdevcfax.c
@@ -0,0 +1,232 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+/* SFF format writer for CAPI fax devices */
+#include "gdevprn.h"
+#include "strimpl.h"
+#include "scfx.h"
+#include "gdevfax.h"
+
+/* The device descriptor */
+static dev_proc_print_page(cfax_print_page);
+static dev_proc_close_device(cfax_prn_close);
+
+/* Define procedures for cfax. For sff multipage documents */
+/* a special close procedure is required because sff needs */
+/* an additional "end of document" signature after the last */
+/* "end page" signature */
+/* Since the print_page doesn't alter the device, this device can print in the background */
+static const gx_device_procs gdev_cfax_std_procs =
+ prn_params_procs(gdev_prn_open, gdev_prn_bg_output_page, cfax_prn_close,
+ gdev_fax_get_params, gdev_fax_put_params);
+
+const gx_device_fax gs_cfax_device = {
+ FAX_DEVICE_BODY(gx_device_fax, gdev_cfax_std_procs, "cfax", cfax_print_page)
+};
+
+/* ---------------- SFF output ----------------- */
+
+static void
+cfax_byte(uint c, FILE * file)
+{
+ fputc(c & 0xff, file);
+}
+
+static void
+cfax_word(ushort c, FILE * file)
+{
+ cfax_byte(c & 0xff, file);
+ cfax_byte(c >> 8, file);
+}
+
+static void
+cfax_dword(ulong c, FILE * file)
+{
+ cfax_byte(c & 0xff, file);
+ cfax_byte(c >> 8, file);
+ cfax_byte(c >> 16, file);
+ cfax_byte(c >> 24, file);
+}
+
+static void
+cfax_doc_hdr(FILE * file)
+{
+ cfax_byte('S', file);
+ cfax_byte('f', file);
+ cfax_byte('f', file);
+ cfax_byte('f', file);
+ cfax_byte(1, file);
+ cfax_byte(0, file);
+ cfax_word(0, file);
+ cfax_word(0, file);
+ cfax_word(20, file);
+ cfax_dword(0, file);
+ cfax_dword(0, file);
+}
+
+static void
+cfax_page_hdr(gx_device_printer * pdev, FILE * file)
+{
+ cfax_byte(254, file);
+ cfax_byte(16, file);
+ cfax_byte((pdev->y_pixels_per_inch < 100 ? 0 : 1), file);
+ cfax_byte(0, file);
+ cfax_byte(0, file);
+ cfax_byte(0, file);
+ cfax_word(pdev->width, file);
+ cfax_word(pdev->height, file);
+ cfax_dword(0, file);
+ cfax_dword(0, file);
+}
+
+static void
+cfax_doc_end(FILE * file)
+{
+ cfax_byte(254, file);
+ cfax_byte(0, file);
+}
+
+/* Send the page to the printer. */
+static int
+cfax_stream_print_page_width(gx_device_printer * pdev, FILE * prn_stream,
+ const stream_template * temp, stream_state * ss,
+ int width)
+{
+ gs_memory_t *mem = pdev->memory;
+ int code = 0;
+ stream_cursor_read r;
+ stream_cursor_write w;
+ int in_size = gdev_prn_raster((gx_device *) pdev);
+ /*
+ * Because of the width adjustment for fax systems, width may
+ * be different from (either greater than or less than) pdev->width.
+ * Allocate a large enough buffer to account for this.
+ */
+ int col_size = (width * pdev->color_info.depth + 7) >> 3;
+ int max_size = max(in_size, col_size);
+ int lnum, nbytes, i;
+ byte *in;
+ byte *out;
+ /* If the file is 'nul', don't even do the writes. */
+ bool nul = !strcmp(pdev->fname, "nul");
+
+ /* Initialize the common part of the encoder state. */
+ ss->templat = temp;
+ ss->memory = mem;
+
+ /* Allocate the buffers. */
+ in = gs_alloc_bytes(mem, temp->min_in_size + max_size + 1,
+ "cfax_stream_print_page(in)");
+
+#define OUT_SIZE 1000
+ out = gs_alloc_bytes(mem, OUT_SIZE, "cfax_stream_print_page(out)");
+ if (in == 0 || out == 0) {
+ code = gs_note_error(gs_error_VMerror);
+ goto done;
+ }
+
+ /* Process the image */
+ for (lnum = 0; lnum < pdev->height; lnum++) {
+ /* Initialize read and write pointer each time, because they're getting modified */
+ r.ptr = in - 1;
+ r.limit = in + col_size;
+ w.ptr = out - 1;
+ w.limit = w.ptr + OUT_SIZE;
+ /* Decoder must encode line for line, so init it for each line */
+ code = (*temp->init) (ss);
+ if (code < 0)
+ return_error(gs_error_limitcheck);
+ /* Now, get the bits and encode them */
+ gdev_prn_copy_scan_lines(pdev, lnum, in, in_size);
+ if (col_size > in_size) {
+ memset(in + in_size , 0, col_size - in_size);
+ }
+ code = (*temp->process) (ss, &r, &w, 1 /* always last line */);
+ nbytes = w.ptr - out + 1;
+ if (!nul) {
+ if (nbytes > 0) {
+ if (nbytes < 217) {
+ cfax_byte(nbytes, prn_stream);
+ for (i = 0; i < nbytes; i++)
+ cfax_byte(out[i], prn_stream);
+ } else {
+ cfax_byte(0, prn_stream);
+ cfax_word(nbytes, prn_stream);
+ for (i = 0; i < nbytes; i++)
+ cfax_byte(out[i], prn_stream);
+ }
+ } else {
+ cfax_byte(218, prn_stream);
+ }
+ }
+ if (temp->release != 0)
+ (*temp->release) (ss);
+ }
+#undef OUT_SIZE
+
+ done:
+ gs_free_object(mem, out, "cfax_stream_print_page(out)");
+ gs_free_object(mem, in, "cfax_stream_print_page(in)");
+ return code;
+}
+
+/* Begin a capi fax page. */
+static int
+cfax_begin_page(gx_device_printer * pdev, FILE * fp, int width)
+{
+ /* Patch the width to reflect fax page width adjustment. */
+ int save_width = pdev->width;
+
+ pdev->width = width;
+ if (gdev_prn_file_is_new(pdev)) {
+ cfax_doc_hdr(fp);
+ }
+ cfax_page_hdr(pdev, fp);
+
+ pdev->width = save_width;
+ return 0;
+}
+
+/* Print an capi fax (sff-encoded) page. */
+static int
+cfax_print_page(gx_device_printer * pdev, FILE * prn_stream)
+{
+ stream_CFE_state state;
+ int code;
+
+ gdev_fax_init_fax_state(&state, (gx_device_fax *)pdev);
+ state.EndOfLine = false;
+ state.EndOfBlock = false;
+ state.EncodedByteAlign = true;
+ state.FirstBitLowOrder = true;
+ state.K = 0;
+
+ cfax_begin_page(pdev, prn_stream, state.Columns);
+ code = cfax_stream_print_page_width(pdev, prn_stream,
+ &s_CFE_template, (stream_state *) &state, state.Columns);
+ return code;
+}
+
+/* Close an capi fax (sff-encoded) document. */
+static int
+cfax_prn_close(gx_device * pdev)
+{
+ gx_device_printer * const ppdev = (gx_device_printer *)pdev;
+
+ if (ppdev->file != NULL) {
+ cfax_doc_end(ppdev->file);
+ }
+ return gdev_prn_close(pdev);
+}
diff --git a/devices/gdevcif.c b/devices/gdevcif.c
new file mode 100644
index 000000000..51cf64679
--- /dev/null
+++ b/devices/gdevcif.c
@@ -0,0 +1,97 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+/*
+ CIF output driver
+
+ The `Fake bitmapped device to estimate rendering time'
+ slightly modified to produce CIF files from PostScript.
+ So anyone can put a nice logo free on its chip!
+ Frederic Petrot, petrot@masi.ibp.fr */
+
+#include "gdevprn.h"
+
+/* Define the device parameters. */
+#ifndef X_DPI
+# define X_DPI 72
+#endif
+#ifndef Y_DPI
+# define Y_DPI 72
+#endif
+
+/* The device descriptor */
+static dev_proc_print_page(cif_print_page);
+const gx_device_printer far_data gs_cif_device =
+ prn_device(prn_bg_procs, "cif", /* The print_page proc is compatible with allowing bg printing */
+ DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
+ X_DPI, Y_DPI,
+ 0,0,0,0,
+ 1, cif_print_page);
+
+/* Send the page to the output. */
+static int
+cif_print_page(gx_device_printer *pdev, FILE *prn_stream)
+{ int line_size = gdev_mem_bytes_per_scan_line((gx_device *)pdev);
+ int lnum;
+ byte *in = (byte *)gs_malloc(pdev->memory, line_size, 1, "cif_print_page(in)");
+ char *s;
+ int scanline, scanbyte;
+ int length, start; /* length is the number of successive 1 bits, */
+ /* start is the set of 1 bit start position */
+
+ if (in == 0)
+ return_error(gs_error_VMerror);
+
+ if ((s = strchr(pdev->fname, '.')) == NULL)
+ length = strlen(pdev->fname) + 1;
+ else
+ length = s - pdev->fname;
+ s = (char *)gs_malloc(pdev->memory, length, sizeof(char), "cif_print_page(s)");
+
+ strncpy(s, pdev->fname, length);
+ *(s + length) = '\0';
+ fprintf(prn_stream, "DS1 25 1;\n9 %s;\nLCP;\n", s);
+ gs_free(pdev->memory, s, length, 1, "cif_print_page(s)");
+
+ for (lnum = 0; lnum < pdev->height; lnum++) {
+ gdev_prn_copy_scan_lines(pdev, lnum, in, line_size);
+ length = 0;
+ for (scanline = 0; scanline < line_size; scanline++)
+#ifdef TILE /* original, simple, inefficient algorithm */
+ for (scanbyte = 0; scanbyte < 8; scanbyte++)
+ if (((in[scanline] >> scanbyte) & 1) != 0)
+ fprintf(prn_stream, "B4 4 %d %d;\n",
+ (scanline * 8 + (7 - scanbyte)) * 4,
+ (pdev->height - lnum) * 4);
+#else /* better algorithm */
+ for (scanbyte = 7; scanbyte >= 0; scanbyte--)
+ /* cheap linear reduction of rectangles in lines */
+ if (((in[scanline] >> scanbyte) & 1) != 0) {
+ if (length == 0)
+ start = (scanline * 8 + (7 - scanbyte));
+ length++;
+ } else {
+ if (length != 0)
+ fprintf(prn_stream, "B%d 4 %d %d;\n", length * 4,
+ start * 4 + length * 2,
+ (pdev->height - lnum) * 4);
+ length = 0;
+ }
+#endif
+ }
+ fprintf(prn_stream, "DF;\nC1;\nE\n");
+ gs_free(pdev->memory, in, line_size, 1, "cif_print_page(in)");
+ return 0;
+}
diff --git a/devices/gdevclj.c b/devices/gdevclj.c
new file mode 100644
index 000000000..1a8b664ab
--- /dev/null
+++ b/devices/gdevclj.c
@@ -0,0 +1,674 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+/*
+ * H-P Color LaserJet 5/5M device; based on the PaintJet.
+ */
+#include "math_.h"
+#include "gx.h"
+#include "gsparam.h"
+#include "gdevprn.h"
+#include "gdevpcl.h"
+
+typedef struct gx_device_clj_s gx_device_clj;
+struct gx_device_clj_s {
+ gx_device_common;
+ gx_prn_device_common;
+ bool rotated;
+};
+
+#define pclj ((gx_device_clj *)pdev)
+
+/*
+ * The HP Color LaserJet 5/5M provides a rather unexpected speed/performance
+ * tradeoff.
+ *
+ * When generating rasters, only the fixed (simple) color spaces provide
+ * reasonable performance (in this case, reasonable != good). However, in
+ * these modes, certain of the fully-saturated primary colors (cyan, blue,
+ * green, and red) are rendered differently as rasters as opposed to colored
+ * geometric objects. Hence, the color of the output will be other than what
+ * is expected.
+ *
+ * Alternatively, the direct color, 1-bit per pixel scheme can be used. This
+ * will produce the expected colors, but performance will deteriorate
+ * significantly (observed printing time will be about 3 times longer than
+ * when using the simple color mode).
+ *
+ * Note that when using the latter mode to view output from the PCL
+ * interpreter, geometric objects and raster rendered with other than
+ * geometric color spaces will have the same appearance as if sent directly
+ * to the CLJ, but rasters generated from simple color spaces will have a
+ * different appearance. To make the latter rasters match in appearance, the
+ * faster printing mode must be used (in which the case the other objects
+ * will not have the same appearance).
+ */
+#define USE_FAST_MODE
+
+/* X_DPI and Y_DPI must be the same */
+#define X_DPI 300
+#define Y_DPI 300
+
+/*
+ * Array of paper sizes, and the corresponding offsets.
+ */
+typedef struct clj_paper_size_s {
+ uint tag; /* paper type tag */
+ int orient; /* logical page orientation to use */
+ float width, height; /* in pts; +- 5 pts */
+ gs_point offsets; /* offsets in the given orientation */
+} clj_paper_size;
+
+/*
+ * The Color LaserJet prints page sizes up to 11.8" wide (A4 size) in
+ * long-edge-feed (landscape) orientation. Only executive, letter, and
+ * A4 size are supported for color, so we don't bother to list the others.
+ */
+static const clj_paper_size clj_paper_sizes[] = {
+ /* U.S. letter size comes first so it will be the default. */
+ { 2, 1, 11.00f * 72.0f, 8.50f * 72.0f, { .200f * 72.0f, 0.0 } },
+ { 1, 1, 10.50f * 72.0f, 7.25f * 72.0f, { .200f * 72.0f, 0.0 } },
+ { 26, 1, 11.69f * 72.0f, 8.27f * 72.0f, { .197f * 72.0f, 0.0 } }
+};
+
+/*
+ * The supported set of resolutions.
+ *
+ * The Color LaserJet 5/5M is actually a pseudo-contone device, with hardware
+ * capable of providing about 16 levels of intensity. The current code does
+ * not take advantage of this feature, because it is not readily controllable
+ * via PCL. Rather, the device is modeled as a bi-level device in each of
+ * three color planes. The maximum supported resolution for such an arrangement
+ * is 300 dpi.
+ *
+ * The CLJ does support raster scaling, but to invoke that scaling, even for
+ * integral factors, involves a large performance penalty. Hence, only those
+ * resolutions that can be supported without invoking raster scaling are
+ * included here. These resolutions are always the same in the fast and slow
+ * scan directions, so only a single value is listed here.
+ *
+ * All valuse are in dots per inch.
+ */
+static const float supported_resolutions[] = { 75.0, 100.0, 150.0, 300.0 };
+
+/* indicate the maximum supported resolution and scan-line length (pts) */
+#define CLJ_MAX_RES 300.0
+#define CLJ_MAX_SCANLINE (12.0 * 72.0)
+
+/*
+ * Determine a requested resolution pair is supported.
+ */
+ static bool
+is_supported_resolution(
+ const float HWResolution[2]
+)
+{
+ int i;
+
+ for (i = 0; i < countof(supported_resolutions); i++) {
+ if (HWResolution[0] == supported_resolutions[i])
+ return HWResolution[0] == HWResolution[1];
+ }
+ return false;
+}
+
+/* ---------------- Standard driver ---------------- */
+
+/*
+ * Find the paper size information corresponding to a given pair of dimensions.
+ * If rotatep != 0, *rotatep is set to true if the page must be rotated 90
+ * degrees to fit.
+ *
+ * A return value of 0 indicates the paper size is not supported.
+ *
+ * Note that for the standard driver, rotation is not allowed.
+ */
+ static const clj_paper_size *
+get_paper_size(
+ const float MediaSize[2],
+ bool * rotatep
+)
+{
+ static const float tolerance = 5.0;
+ float width = MediaSize[0];
+ float height = MediaSize[1];
+ const clj_paper_size * psize = 0;
+ int i;
+
+ for (i = 0, psize = clj_paper_sizes; i < countof(clj_paper_sizes); i++, psize++) {
+ if ( (fabs(width - psize->width) <= tolerance) &&
+ (fabs(height - psize->height) <= tolerance) ) {
+ if (rotatep != 0)
+ *rotatep = false;
+ return psize;
+ } else if ( (fabs(width - psize->height) <= tolerance) &&
+ (fabs(height - psize->width) <= tolerance) ) {
+ if (rotatep != 0)
+ *rotatep = true;
+ return psize;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * Get the (PostScript style) default matrix for the current page size.
+ *
+ * For all of the supported sizes, the page will be printed with long-edge
+ * feed (the CLJ does support some additional sizes, but only for monochrome).
+ * As will all HP laser printers, the printable region marin is 12 pts. from
+ * the edge of the physical page.
+ */
+static void
+clj_get_initial_matrix( gx_device *pdev, gs_matrix *pmat)
+{
+ double fs_res = pdev->HWResolution[0] / 72.0;
+ double ss_res = pdev->HWResolution[1] / 72.0;
+ const clj_paper_size *psize;
+
+ psize = get_paper_size(pdev->MediaSize, NULL);
+ /* if the paper size is not recognized, not much can be done */
+ /* This shouldn't be possible since clj_put_params rejects */
+ /* unknown media sizes. */
+ if (psize == 0) {
+ pmat->xx = fs_res;
+ pmat->xy = 0.0;
+ pmat->yx = 0.0;
+ pmat->yy = -ss_res;
+ pmat->tx = 0.0;
+ pmat->ty = pdev->MediaSize[1] * ss_res;
+ return;
+ }
+
+ if (pclj->rotated) {
+ pmat->xx = 0.0;
+ pmat->xy = ss_res;
+ pmat->yx = fs_res;
+ pmat->yy = 0.0;
+ pmat->tx = -psize->offsets.x * fs_res;
+ pmat->ty = -psize->offsets.y * ss_res;
+ } else {
+ pmat->xx = fs_res;
+ pmat->xy = 0.0;
+ pmat->yx = 0.0;
+ pmat->yy = -ss_res;
+ pmat->tx = -psize->offsets.x * fs_res;
+ pmat->ty = pdev->height + psize->offsets.y * ss_res;
+ }
+}
+
+/*
+ * Get parameters, including InputAttributes for all supported page sizes.
+ * We associate each page size with a different "media source", since that
+ * is currently the only way to register multiple page sizes.
+ */
+static int
+clj_get_params(gx_device *pdev, gs_param_list *plist)
+{
+ gs_param_dict mdict;
+ int code = gdev_prn_get_params(pdev, plist);
+ int ecode = code;
+ int i;
+
+ code = gdev_begin_input_media(plist, &mdict, countof(clj_paper_sizes));
+ if (code < 0)
+ ecode = code;
+ else {
+ for (i = 0; i < countof(clj_paper_sizes); ++i) {
+ code = gdev_write_input_page_size(i, &mdict,
+ clj_paper_sizes[i].width,
+ clj_paper_sizes[i].height);
+ if (code < 0)
+ ecode = code;
+ }
+ code = gdev_end_input_media(plist, &mdict);
+ if (code < 0)
+ ecode = code;
+ }
+ return ecode;
+}
+
+/*
+ * Get the media size being set by put_params, if any. Return 0 if no media
+ * size is being set, 1 (and set mediasize[]) if the size is being set, <0
+ * on error.
+ */
+static int
+clj_media_size(float mediasize[2], gs_param_list *plist)
+{
+ gs_param_float_array fres;
+ gs_param_float_array fsize;
+ gs_param_int_array hwsize;
+ int have_pagesize = 0;
+
+ if ( (param_read_float_array(plist, "HWResolution", &fres) == 0) &&
+ !is_supported_resolution(fres.data) )
+ return_error(gs_error_rangecheck);
+
+ if ( (param_read_float_array(plist, "PageSize", &fsize) == 0) ||
+ (param_read_float_array(plist, ".MediaSize", &fsize) == 0) ) {
+ mediasize[0] = fsize.data[0];
+ mediasize[1] = fsize.data[1];
+ have_pagesize = 1;
+ }
+
+ if (param_read_int_array(plist, "HWSize", &hwsize) == 0) {
+ mediasize[0] = ((float)hwsize.data[0]) * 72 / fres.data[0];
+ mediasize[1] = ((float)hwsize.data[1]) * 72 / fres.data[1];
+ have_pagesize = 1;
+ }
+
+ return have_pagesize;
+}
+
+/*
+ * Special put_params routine, to make certain the desired MediaSize and
+ * HWResolution are supported.
+ */
+ static int
+clj_put_params(
+ gx_device * pdev,
+ gs_param_list * plist
+)
+{
+ float mediasize[2];
+ bool rotate = false;
+ int have_pagesize = clj_media_size(mediasize, plist);
+
+ if (have_pagesize < 0)
+ return have_pagesize;
+ if (have_pagesize) {
+ if (get_paper_size(mediasize, &rotate) == 0 || rotate)
+ return_error(gs_error_rangecheck);
+ }
+ return gdev_prn_put_params(pdev, plist);
+}
+
+/*
+ * Pack and then compress a scanline of data. Return the size of the compressed
+ * data produced.
+ *
+ * Input is arranged with one byte per pixel, but only the three low-order bits
+ * are used. These bits are in order ymc, with yellow being the highest order
+ * bit.
+ *
+ * Output is arranged in three planes, with one bit per pixel per plane. The
+ * Color LaserJet 5/5M does support more congenial pixel encodings, but use
+ * of anything other than the fixed palettes seems to result in very poor
+ * performance.
+ *
+ * Only compresion mode 2 is used. Compression mode 1 (pure run length) has
+ * an advantage over compression mode 2 only in cases in which very long runs
+ * occur (> 128 bytes). Since both methods provide good compression in that
+ * case, it is not worth worrying about, and compression mode 2 provides much
+ * better worst-case behavior. Compression mode 3 requires considerably more
+ * effort to generate, so it is useful only when it is known a prior that
+ * scanlines repeat frequently.
+ */
+ static void
+pack_and_compress_scanline(
+ const byte * pin,
+ int in_size,
+ byte * pout[3],
+ int out_size[3]
+)
+{
+#define BUFF_SIZE \
+ ( ((int)(CLJ_MAX_RES * CLJ_MAX_SCANLINE / 72.0) + sizeof(ulong) - 1) \
+ / sizeof(ulong) )
+
+ ulong buff[3 * BUFF_SIZE];
+ byte * p_c = (byte *)buff;
+ byte * p_m = (byte *)(buff + BUFF_SIZE);
+ byte * p_y = (byte *)(buff + 2 * BUFF_SIZE);
+ ulong * ptrs[3];
+ byte c_val = 0, m_val = 0, y_val = 0;
+ ulong mask = 0x80;
+ int i;
+
+ /* pack the input for 4-bits per index */
+ for (i = 0; i < in_size; i++) {
+ uint ival = *pin++;
+
+ if (ival != 0) {
+ if ((ival & 0x4) != 0)
+ y_val |= mask;
+ if ((ival & 0x2) != 0)
+ m_val |= mask;
+ if ((ival & 0x1) != 0)
+ c_val |= mask;
+ }
+
+ if ((mask >>= 1) == 0) {
+ /* NB - write out in byte units */
+ *p_c++ = c_val;
+ c_val = 0L;
+ *p_m++ = m_val;
+ m_val = 0L;
+ *p_y++ = y_val;
+ y_val = 0L;
+ mask = 0x80;
+ }
+ }
+ if (mask != 0x80) {
+ /* NB - write out in byte units */
+ *p_c++ = c_val;
+ *p_m++ = m_val;
+ *p_y++ = y_val;
+ }
+
+ /* clear to up a longword boundary */
+ while ((((ulong)p_c) & (sizeof(ulong) - 1)) != 0) {
+ *p_c++ = 0;
+ *p_m++ = 0;
+ *p_y++ = 0;
+ }
+
+ ptrs[0] = (ulong *)p_c;
+ ptrs[1] = (ulong *)p_m;
+ ptrs[2] = (ulong *)p_y;
+
+ for (i = 0; i < 3; i++) {
+ ulong * p_start = buff + i * BUFF_SIZE;
+ ulong * p_end = ptrs[i];
+
+ /* eleminate trailing 0's */
+ while ((p_end > p_start) && (p_end[-1] == 0))
+ p_end--;
+
+ if (p_start == p_end)
+ out_size[i] = 0;
+ else
+ out_size[i] = gdev_pcl_mode2compress(p_start, p_end, pout[i]);
+ }
+
+#undef BUFF_SIZE
+}
+
+/*
+ * Send the page to the printer. Compress each scan line.
+ */
+ static int
+clj_print_page(
+ gx_device_printer * pdev,
+ FILE * prn_stream
+)
+{
+ gs_memory_t *mem = pdev->memory;
+ bool rotate;
+ const clj_paper_size * psize = get_paper_size(pdev->MediaSize, &rotate);
+ int lsize = pdev->width;
+ int clsize = (lsize + (lsize + 255) / 128) / 8;
+ byte * data = 0;
+ byte * cdata[3];
+ int blank_lines = 0;
+ int i;
+ double fs_res = pdev->HWResolution[0] / 72.0;
+ double ss_res = pdev->HWResolution[1] / 72.0;
+ int imageable_width, imageable_height;
+
+ /* no paper size at this point is a serious error */
+ if (psize == 0)
+ return_error(gs_error_unregistered);
+
+ /* allocate memory for the raw and compressed data */
+ if ((data = gs_alloc_bytes(mem, lsize, "clj_print_page(data)")) == 0)
+ return_error(gs_error_VMerror);
+ if ((cdata[0] = gs_alloc_bytes(mem, 3 * clsize, "clj_print_page(cdata)")) == 0) {
+ gs_free_object(mem, data, "clj_print_page(data)");
+ return_error(gs_error_VMerror);
+ }
+ cdata[1] = cdata[0] + clsize;
+ cdata[2] = cdata[1] + clsize;
+
+ /* Imageable area is without the margins. Note that the actual rotation
+ * of page size into pdev->width & height has been done. We just use
+ * rotate to access the correct offsets. */
+ if (pclj->rotated) {
+ imageable_width = pdev->width - (int)((2 * psize->offsets.x) * fs_res);
+ imageable_height = pdev->height - (int)((2 * psize->offsets.y) * ss_res);
+ }
+ else {
+ imageable_width = pdev->width - (int)((2 * psize->offsets.y) * ss_res);
+ imageable_height = pdev->height - (int)((2 * psize->offsets.x) * fs_res);
+ }
+
+ /* start the page. The pcl origin (0, 150 dots by default, y
+ increasing down the long edge side of the page) needs to be
+ offset such that it coincides with the offsets of the imageable
+ area. This calculation should be independant of rotation but
+ only the rotated case has been tested with a real device. */
+ fprintf( prn_stream,
+ "\033E\033&u300D\033&l%da1x%dO\033*p0x0y+50x-100Y\033*t%dR"
+#ifdef USE_FAST_MODE
+ "\033*r-3U"
+#else
+ "\033*v6W\001\002\003\001\001\001"
+#endif
+ "\033*r0f%ds%dt1A\033*b2M",
+ psize->tag,
+ pclj->rotated,
+ (int)(pdev->HWResolution[0]),
+ imageable_width,
+ imageable_height
+ );
+
+ /* process each scanline */
+ for (i = 0; i < imageable_height; i++) {
+ int clen[3];
+
+ gdev_prn_copy_scan_lines(pdev, i, data, lsize);
+
+ /* The 'lsize' bytes of data have the blank margin area at the end due */
+ /* to the 'initial_matrix' offsets that are applied. */
+ pack_and_compress_scanline(data, imageable_width, cdata, clen);
+ if ((clen[0] == 0) && (clen[1] == 0) && (clen[2] == 0))
+ ++blank_lines;
+ else {
+ if (blank_lines != 0) {
+ fprintf(prn_stream, "\033*b%dY", blank_lines);
+ blank_lines = 0;
+ }
+ fprintf(prn_stream, "\033*b%dV", clen[0]);
+ fwrite(cdata[0], sizeof(byte), clen[0], prn_stream);
+ fprintf(prn_stream, "\033*b%dV", clen[1]);
+ fwrite(cdata[1], sizeof(byte), clen[1], prn_stream);
+ fprintf(prn_stream, "\033*b%dW", clen[2]);
+ fwrite(cdata[2], sizeof(byte), clen[2], prn_stream);
+ }
+ }
+
+ /* PCL will take care of blank lines at the end */
+ fputs("\033*rC\f", prn_stream);
+
+ /* free the buffers used */
+ gs_free_object(mem, cdata[0], "clj_print_page(cdata)");
+ gs_free_object(mem, data, "clj_print_page(data)");
+
+ return 0;
+}
+
+/* CLJ device methods */
+#define CLJ_PROCS(get_params, put_params)\
+ gdev_prn_open, /* open_device */\
+ clj_get_initial_matrix, /* get_initial matrix */\
+ NULL, /* sync_output */\
+/* Since the print_page doesn't alter the device, this device can print in the background */\
+ gdev_prn_bg_output_page, /* output_page */\
+ gdev_prn_close, /* close_device */\
+ gdev_pcl_3bit_map_rgb_color, /* map_rgb_color */\
+ gdev_pcl_3bit_map_color_rgb, /* map_color_rgb */\
+ NULL, /* fill_rectangle */\
+ NULL, /* tile_rectangle */\
+ NULL, /* copy_mono */\
+ NULL, /* copy_color */\
+ NULL, /* obsolete draw_line */\
+ NULL, /* get_bits */\
+ get_params, /* get_params */\
+ put_params, /* put_params */\
+ NULL, /* map_cmyk_color */\
+ NULL, /* get_xfont_procs */\
+ NULL, /* get_xfont_device */\
+ NULL, /* map_rgb_alpha_color */\
+ gx_page_device_get_page_device /* get_page_device */
+
+static gx_device_procs cljet5_procs = {
+ CLJ_PROCS(clj_get_params, clj_put_params)
+};
+
+/* CLJ device structure */
+#define CLJ_DEVICE_BODY(procs, dname, rotated)\
+ prn_device_body(\
+ gx_device_clj,\
+ procs, /* procedures */\
+ dname, /* device name */\
+ 110, /* width - will be overridden subsequently */\
+ 85, /* height - will be overridden subsequently */\
+ X_DPI, Y_DPI, /* resolutions - current must be the same */\
+ 0.167, 0.167, /* margins (left, bottom, right, top */\
+ 0.167, 0.167,\
+ 3, /* num_components - 3 colors, 1 bit per pixel */\
+ 8, /* depth - pack into bytes */\
+ 1, 1, /* max_gray=max_component=1 */\
+ 2, 2, /* dithered_grays=dithered_components=2 */ \
+ clj_print_page /* routine to output page */\
+),\
+ rotated /* rotated - may be overridden subsequently */
+
+gx_device_clj gs_cljet5_device = {
+ CLJ_DEVICE_BODY(cljet5_procs, "cljet5", 0 /*false*/)
+};
+
+/* ---------------- Driver with page rotation ---------------- */
+
+/*
+ * For use with certain PCL interpreters, which don't implement
+ * setpagedevice, we provide a version of this driver that attempts to
+ * handle page rotation at the driver level. This version breaks an
+ * invariant that all drivers must obey, namely, that drivers are not
+ * allowed to change the parameters passed by put_params (they can only
+ * accept or reject them). Consequently, this driver must not be used in
+ * any context other than these specific PCL interpreters. We support this
+ * hack only because these PCL interpreters can't be changed to handle page
+ * rotation properly.
+ */
+
+/*
+ * Special get_params routine, to fake MediaSize, width, and height if
+ * we were in a 'rotated' state.
+ */
+static int
+clj_pr_get_params( gx_device *pdev, gs_param_list *plist )
+{
+ int code;
+
+ /* First un-rotate the MediaSize, etc. if we were in a rotated mode */
+ if (pclj->rotated) {
+ float ftmp;
+ int itmp;
+
+ ftmp = pdev->MediaSize[0];
+ pdev->MediaSize[0] = pdev->MediaSize[1];
+ pdev->MediaSize[1] = ftmp;
+ itmp = pdev->width;
+ pdev->width = pdev->height;
+ pdev->height = itmp;
+ }
+
+ /* process the parameter list */
+ code = gdev_prn_get_params(pdev, plist);
+
+ /* Now re-rotate the page size if needed */
+ if (pclj->rotated) {
+ float ftmp;
+ int itmp;
+
+ ftmp = pdev->MediaSize[0];
+ pdev->MediaSize[0] = pdev->MediaSize[1];
+ pdev->MediaSize[1] = ftmp;
+ itmp = pdev->width;
+ pdev->width = pdev->height;
+ pdev->height = itmp;
+ }
+
+ return code;
+}
+
+/*
+ * Special put_params routine, to intercept changes in the MediaSize, and to
+ * make certain the desired MediaSize and HWResolution are supported.
+ *
+ * This function will rotate MediaSize if it is needed by the device in
+ * order to print this size page.
+ */
+ static int
+clj_pr_put_params(
+ gx_device * pdev,
+ gs_param_list * plist
+)
+{
+ float mediasize[2];
+ int code = 0;
+ bool rotate = false;
+ int have_pagesize = clj_media_size(mediasize, plist);
+
+ if (have_pagesize < 0)
+ return have_pagesize;
+ if (have_pagesize) {
+ if (get_paper_size(mediasize, &rotate) == 0)
+ return_error(gs_error_rangecheck);
+ if (rotate) {
+ /* We need to rotate the requested page size, so synthesize a new */
+ /* parameter list in front of the requestor's list to force the */
+ /* rotated page size. */
+ gs_param_float_array pf_array;
+ gs_c_param_list alist;
+ float ftmp = mediasize[0];
+
+ mediasize[0] = mediasize[1];
+ mediasize[1] = ftmp;
+ pf_array.data = mediasize;
+ pf_array.size = 2;
+ pf_array.persistent = false;
+
+ gs_c_param_list_write(&alist, pdev->memory);
+ code = param_write_float_array((gs_param_list *)&alist, ".MediaSize", &pf_array);
+ gs_c_param_list_read(&alist);
+
+ /* stick this synthesized parameter on the front of the existing list */
+ gs_c_param_list_set_target(&alist, plist);
+ if ((code = gdev_prn_put_params(pdev, (gs_param_list *)&alist)) >= 0)
+ pclj->rotated = true;
+ gs_c_param_list_release(&alist);
+ } else {
+ if ((code = gdev_prn_put_params(pdev, plist)) >= 0)
+ pclj->rotated = false;
+ }
+ } else
+ code = gdev_prn_put_params(pdev, plist);
+
+ return code;
+}
+
+/* CLJ device methods -- se above for CLJ_PROCS */
+static gx_device_procs cljet5pr_procs = {
+ CLJ_PROCS(clj_pr_get_params, clj_pr_put_params)
+};
+
+/* CLJ device structure -- see above for CLJ_DEVICE_BODY */
+gx_device_clj gs_cljet5pr_device = {
+ CLJ_DEVICE_BODY(cljet5pr_procs, "cljet5pr", 1 /*true*/)
+};
diff --git a/devices/gdevcljc.c b/devices/gdevcljc.c
new file mode 100644
index 000000000..770ff9634
--- /dev/null
+++ b/devices/gdevcljc.c
@@ -0,0 +1,100 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+/*
+ * H-P Color LaserJet 5/5M contone device; based on the gdevclj.c.
+ */
+#include "math_.h"
+#include "gdevprn.h"
+#include "gdevpcl.h"
+
+/* X_DPI and Y_DPI must be the same */
+#define X_DPI 300
+#define Y_DPI 300
+
+/* Send the page to the printer. Compress each scan line. NB - the
+ * render mode as well as color parameters - bpp etc. are all
+ * hardwired.
+ */
+static int
+cljc_print_page(gx_device_printer * pdev, FILE * prn_stream)
+{
+ gs_memory_t *mem = pdev->memory;
+ uint raster = gx_device_raster((gx_device *)pdev, false);
+ int i;
+ int worst_case_comp_size = raster + (raster / 8) + 1;
+ byte *data = 0;
+ byte *cdata = 0;
+ byte *prow = 0;
+ int code = 0;
+
+ /* allocate memory for the raw data and compressed data. */
+ if (((data = gs_alloc_bytes(mem, raster, "cljc_print_page(data)")) == 0) ||
+ ((cdata = gs_alloc_bytes(mem, worst_case_comp_size, "cljc_print_page(cdata)")) == 0) ||
+ ((prow = gs_alloc_bytes(mem, worst_case_comp_size, "cljc_print_page(prow)")) == 0)) {
+ code = gs_note_error(gs_error_VMerror);
+ goto out;
+ }
+ /* send a reset and the the paper definition */
+ fprintf(prn_stream, "\033E\033&u300D\033&l%dA",
+ gdev_pcl_paper_size((gx_device *) pdev));
+ /* turn off source and pattern transparency */
+ fprintf(prn_stream, "\033*v1N\033*v1O");
+ /* set color render mode and the requested resolution */
+ fprintf(prn_stream, "\033*t4J\033*t%dR", (int)(pdev->HWResolution[0]));
+ /* set up the color model - NB currently hardwired to direct by
+ pixel which requires 8 bits per component. See PCL color
+ technical reference manual for other possible encodings. */
+ fprintf(prn_stream, "\033*v6W%c%c%c%c%c%c", 0, 3, 0, 8, 8, 8);
+ /* set up raster width and height, compression mode 3 */
+ fprintf(prn_stream, "\033&l0e-180u36Z\033*p0x0Y\033*r1A\033*b3M");
+ /* initialize the seed row */
+ memset(prow, 0, worst_case_comp_size);
+ /* process each scanline */
+ for (i = 0; i < pdev->height; i++) {
+ int compressed_size;
+
+ code = gdev_prn_copy_scan_lines(pdev, i, (byte *) data, raster);
+ if (code < 0)
+ break;
+ compressed_size = gdev_pcl_mode3compress(raster, data, prow, cdata);
+ fprintf(prn_stream, "\033*b%dW", compressed_size);
+ fwrite(cdata, sizeof(byte), compressed_size, prn_stream);
+ }
+ /* PCL will take care of blank lines at the end */
+ fputs("\033*rC\f", prn_stream);
+out:
+ gs_free_object(mem, prow, "cljc_print_page(prow)");
+ gs_free_object(mem, cdata, "cljc_print_page(cdata)");
+ gs_free_object(mem, data, "cljc_print_page(data)");
+ return code;
+}
+
+/* CLJ device methods */
+/* Since the print_page doesn't alter the device, this device can print in the background */
+static gx_device_procs cljc_procs =
+prn_color_procs(gdev_prn_open, gdev_prn_bg_output_page, gdev_prn_close,
+ gx_default_rgb_map_rgb_color, gx_default_rgb_map_color_rgb);
+
+/* the CLJ device */
+const gx_device_printer gs_cljet5c_device =
+{
+ prn_device_body(gx_device_printer, cljc_procs, "cljet5c",
+ 85, 110, X_DPI, Y_DPI,
+ 0.167, 0.167,
+ 0.167, 0.167,
+ 3, 24, 255, 255, 256, 256,
+ cljc_print_page)
+};
diff --git a/devices/gdevcmykog.c b/devices/gdevcmykog.c
new file mode 100644
index 000000000..b80839379
--- /dev/null
+++ b/devices/gdevcmykog.c
@@ -0,0 +1,805 @@
+/* Copyright (C) 2001-2013 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied, modified
+ or distributed except as expressly authorized under the terms of that
+ license. Refer to licensing information at http://www.artifex.com/
+ or contact Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134,
+ San Rafael, CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+/* psdcmykog device.
+ *
+ * This device produces 6 component psd files, Cyan, Magenta, Yellow, Key
+ * (Black), "Artifex Orange" and "Artifex Green". It renders at the given
+ * resolution, then subscales by a factor of 2 in each axis to achieve
+ * simple anti-aliasing.
+ *
+ * The primary purpose of this device is to be a testable examplar for
+ * various features added to Ghostscript during the development for 9.11.
+ * It is (hopefully) simple enough to understand.
+ *
+ * Specifically, it is 1) a planar device, 2) a device with specific
+ * raster alignment requirements, 3) a device that uses process page,
+ * 4) a device that 'adjusts' the bandheight, and 5) a device that can
+ * quickly skip processing for unused planes.
+ *
+ * To cover these in more detail:
+ *
+ * 1) The PSD file format is a planar one, that is the output requires
+ * all the C information, then all the M information, etc. Previously
+ * this has frequently been implemented within Ghostscript devices by
+ * rendering a 'chunky' image (CMYKCMYKCMYK etc) and then unpacking it
+ * afterwards. This device demonstrates the advantages of working direct
+ * in planar mode.
+ *
+ * 2) In order to swiftly perform the subsample, we would like to leverage
+ * SSE instructions (if they are available on the target CPU). To do this
+ * we require (or at least prefer) specific alignment of the buffers in
+ * which we work. The device now sets dev->pad and dev->log2_align_mod
+ * at startup, and any buffers created by the device will be allocated
+ * with this in mind.
+ *
+ * Other devices may use SSE to perform halftoning, or repacking of data
+ * for their own devices.
+ *
+ * 3) Traditionally devices have called 'get_bits' or 'get_bits_rectangle'
+ * to retrieve the rendered bits for output. A new mechanism has been
+ * added to Ghostscript whereby output devices can work on whole 'bands'
+ * at once, with "in place" access to the buffered data. The primary
+ * benefit here (over and above possibly not needing another buffer) is
+ * that when multiple rendering threads are used (-dNumRenderThreads=x)
+ * any post processing (such as the subsample stage in this device, or
+ * the compression stage in the fpng device) will be performed on these
+ * background render threads too - a potentially significant speedup.
+ *
+ * 4) The code that performs the subsampling can be much simpler if we know
+ * in advance that the heights of the rendering bands will be a multiple
+ * of two. Similarly, devices that do JPEG (or similar macroblock based
+ * compression) might be simpler if they know that the rendering bands
+ * are a multiple of 8 (or maybe 16).
+ *
+ * 5) The internal band rendering code within Ghostscript can detect (within
+ * certain limits) whether a given colorant is used or not. In the case
+ * where a colorant isn't used at all, the device can short circuit its
+ * processing of that plane.
+ */
+
+// Set USE_SSE2 if we have SSE2 available to us.
+// GS defines HAVE_SSE2 for windows builds.
+#ifdef HAVE_SSE2
+#define USE_SSE2
+#endif
+
+// Set USE_SSE3 if we have SSE3 available to us. Only has an effect if
+// USE_SSE2 is also set.
+//#define USE_SSE3
+
+// For timing we might want to disable this device from actually producing
+// any output.
+//#define NO_OUTPUT
+
+#include "std.h"
+#include "gstypes.h"
+#include "gsmemory.h"
+#include "gxdevcli.h"
+#include "gdevdevn.h"
+#include "gdevprn.h"
+#include "gsequivc.h"
+#include "gdevdevnprn.h"
+#include "gxgetbit.h"
+#include "gxdevsop.h"
+#include "gdevppla.h"
+
+/* And for the SSE code */
+#ifdef USE_SSE4
+#include <smmintrin.h> // SIMD sse4
+#endif
+#ifdef USE_SSE3
+#include <tmmintrin.h> // SIMD sse3
+#endif
+#ifdef USE_SSE2
+#include <emmintrin.h> // SIMD sse2
+#endif
+
+/* First we have the device structure definition. */
+struct gx_device_cmykog_s {
+ gx_devn_prn_device_common;
+};
+
+typedef struct gx_device_cmykog_s gx_device_cmykog;
+
+/* GC procedures
+ *
+ * In order to allow garbage collection to occur, we need to define a
+ * structure descriptor for the device structure. This is a bit of
+ * Ghostscript magic that knows how to enumerate/relocate pointers
+ * within that structure.
+ */
+
+static
+ENUM_PTRS_WITH(cmykog_device_enum_ptrs, gx_device_cmykog *pdev)
+{
+ ENUM_PREFIX(st_gx_devn_prn_device, 0);
+ /* Any extra pointers added to our device after the gx_devn_prn_device
+ * fields would be enumerated here. */
+ return 0;
+}
+ENUM_PTRS_END
+
+static RELOC_PTRS_WITH(cmykog_device_reloc_ptrs, gx_device_cmykog *pdev)
+{
+ RELOC_PREFIX(st_gx_devn_prn_device);
+ /* Any extra pointers added to our device after the gx_devn_prn_device
+ * fields would be relocated here. */
+}
+RELOC_PTRS_END
+
+/* Even though cmykog_device_finalize is the same as gx_device_finalize, */
+/* we need to implement it separately because st_composite_final */
+/* declares all 3 procedures as private. */
+static void
+cmykog_device_finalize(const gs_memory_t *cmem, void *vpdev)
+{
+ gx_devn_prn_device_finalize(cmem, vpdev);
+}
+
+gs_private_st_composite_final(st_cmykog_device, gx_device_cmykog,
+ "gx_device_cmykog", cmykog_device_enum_ptrs, cmykog_device_reloc_ptrs,
+ cmykog_device_finalize);
+
+/*--------------------------------------*/
+/* Printer Driver Functions */
+/*--------------------------------------*/
+
+/* Next, we have calls to open and close the device */
+
+/* Open the printer */
+static int
+cmykog_open(gx_device * pdev)
+{
+ gx_device_cmykog *dev = (gx_device_cmykog *)pdev;
+
+ dev->color_info.separable_and_linear = GX_CINFO_SEP_LIN;
+ dev->icc_struct->supports_devn = true;
+
+ /* Here we set the device so that any buffers set up will be aligned to a
+ * multiple of 1<<5 = 32. This is so the SSE code can safely load 32 bytes
+ * at a time from a scanline.
+ *
+ * See (2) at the top of this file. */
+ dev->pad = 0; /* No additional padding requirements */
+ dev->log2_align_mod = 5; /* But we do want lines aligned to a multiple of 32 */
+
+ /* Finally, we open the device requesting the underlying buffers to be
+ * planar, rather than chunky. See (1) at the top of this file. */
+ return gdev_prn_open_planar(pdev, true);
+}
+
+/* Close the printer */
+static int
+cmykog_close(gx_device * pdev)
+{
+ return gdev_prn_close(pdev);
+}
+
+/* Next, we hook gxdso_adjust_bandheight to ensure that every band is an even
+ * height. This takes care of 4) from the comments at the top of the file. */
+static int
+cmykog_dev_spec_op(gx_device *dev_, int op, void *data, int datasize)
+{
+ if (op == gxdso_adjust_bandheight) {
+ /* Any band height is fine, as long as it is even */
+ return datasize & ~1;
+ }
+ if (op == gxdso_get_dev_param) {
+ int code;
+ dev_param_req_t *request = (dev_param_req_t *)data;
+ code = gdev_prn_get_param(dev_, request->Param, request->list);
+ if (code != gs_error_undefined)
+ return code;
+ }
+ return gx_default_dev_spec_op(dev_, op, data, datasize);
+}
+
+/*
+ * This routine will check to see if the color component name match those
+ * that are available amoung the current device's color components.
+ *
+ * Parameters:
+ * dev - pointer to device data structure.
+ * pname - pointer to name (zero termination not required)
+ * nlength - length of the name
+ *
+ * This routine returns a positive value (0 to n) which is the device colorant
+ * number if the name is found. It returns GX_DEVICE_COLOR_MAX_COMPONENTS if
+ * the colorant is not being used due to a SeparationOrder device parameter.
+ * It returns a negative value if not found.
+ */
+static int
+cmykog_get_color_comp_index(gx_device * dev, const char * pname,
+ int name_size, int component_type)
+{
+ int index;
+ gx_device_cmykog *pdev = (gx_device_cmykog *)dev;
+
+ if (strncmp(pname, "None", name_size) == 0)
+ return -1;
+
+ index = devn_get_color_comp_index(dev,
+ &(pdev->devn_params), &(pdev->equiv_cmyk_colors),
+ pname, name_size, component_type, NO_AUTO_SPOT_COLORS);
+ return index;
+}
+
+/* Set parameters. We don't allow setting SeparationOrder, so we */
+/* throw an error if that is present. */
+static int
+cmykog_put_params(gx_device * pdev, gs_param_list * plist)
+{
+ int code;
+ gs_param_string_array sona; /* SeparationOrder names array */
+ gs_param_name param_name;
+
+ sona.data = 0;
+ switch (code = param_read_name_array(plist, (param_name = "SeparationOrder"), &sona)) {
+ default:
+ param_signal_error(plist, param_name, code);
+ return_error(code);
+ case 1:
+ sona.data = 0; /* mark as not filled */
+ case 0:
+ break; /* sona.data and sona.size were set */
+ }
+ if (sona.data != 0) {
+ param_signal_error(plist, "SeparationOrder", gs_error_undefined);
+ return_error(gs_error_undefined);
+ }
+ return gx_devn_prn_put_params(pdev, plist);
+}
+
+/* Next we have the code and structures required to support process_page
+ * operation. See 3) in the comments at the top of the file.
+ *
+ * First, we have a structure full of information. A pointer to this is
+ * passed to every process_page function:
+ */
+typedef struct cmykog_process_arg_s {
+ FILE *spot_file[GX_DEVICE_COLOR_MAX_COMPONENTS];
+ char spot_name[GX_DEVICE_COLOR_MAX_COMPONENTS][gp_file_name_sizeof];
+ int dev_raster;
+} cmykog_process_arg_t;
+
+/* Next we have a structure that describes each 'buffer' of data. There will
+ * be one of these created for each background rendering thread. The
+ * process_fn fills in the details, and the output_fn then outputs based on
+ * it. */
+typedef struct cmykog_process_buffer_s {
+ int w;
+ int h;
+ gs_get_bits_params_t params;
+ gx_color_usage_t color_usage;
+} cmykog_process_buffer_t;
+
+/* This function is called once per rendering thread to set up the buffer
+ * that will be used in future calls. */
+static int
+cmykog_init_buffer(void *arg_, gx_device *dev_, gs_memory_t *memory, int w, int h, void **bufferp)
+{
+#ifdef INCLUDE_EXAMPLE_CODE /* disabled by default to prevent compiler warnings */
+ /* arg and dev here are unused, currently, but here as an example */
+ cmykog_process_arg_t *arg = (cmykog_process_arg_t *)arg_;
+ gx_device_cmykog *dev = (gx_device_cmykog *)dev_;
+#endif
+ cmykog_process_buffer_t *buffer;
+
+ *bufferp = NULL;
+ buffer = (cmykog_process_buffer_t *)gs_alloc_bytes(memory, sizeof(*buffer), "cmykog_init_buffer");
+ if (buffer == NULL)
+ return gs_error_VMerror;
+ memset(buffer, 0, sizeof(*buffer));
+
+ *bufferp = buffer;
+ return 0;
+}
+
+/* This function is called once per rendering thread after rendering
+ * completes to free the buffer allocated in the init function above. */
+static void
+cmykog_free_buffer(void *arg_, gx_device *dev_, gs_memory_t *memory, void *buffer_)
+{
+#ifdef INCLUDE_EXAMPLE_CODE /* disabled by default to prevent compiler warnings */
+ /* arg and dev here are unused, currently, but here as an example */
+ cmykog_process_arg_t *arg = (cmykog_process_arg_t *)arg_;
+ gx_device_cmykog *dev = (gx_device_cmykog *)dev_;
+#endif
+ cmykog_process_buffer_t *buffer = (cmykog_process_buffer_t *)buffer_;
+
+ if (buffer) {
+ gs_free_object(memory, buffer, "cmykog_init_buffer");
+ }
+}
+
+/* These functions takes a plane of width*height 8 bit values, and
+ * averages groups of 4 pixels 'in-place' together to give a plane of
+ * (width>>1)*(height>>1). Also, the values are 'inverted' (each byte
+ * subtracted from 255) as the psd format requires.
+ * Both input and output share the same raster.
+ *
+ * The first is a simple C version, the second a version using SSE
+ * instructions for speed.
+ */
+#ifndef USE_SSE2
+static void average_plane(byte *image, int width, int height, int raster)
+{
+ int x, y;
+ byte *imageOut = image;
+
+ for (y = 0; y < height; y += 2) {
+ byte *in = image;
+ byte *out = imageOut;
+ for (x = 0; x < width; x += 2) {
+ int v;
+
+ v = in[raster];
+ v += *in++;
+ v += in[raster];
+ v += *in++;
+ *out++ = 255 ^ (v>>2);
+ }
+ image += raster * 2;
+ imageOut += raster;
+ }
+}
+#else
+static void average_plane(byte* image, int width, int height, int raster)
+{
+ int x, y;
+ __m128i mm0, mm1;
+ __m128i mm2, mm3;
+ __m128i invert = _mm_set1_epi8 ( -1 );
+#ifdef USE_SSE3
+ __m128i maskL, maskR;
+
+ maskL.m128i_u8[ 0] = 0x00;
+ maskL.m128i_u8[ 1] = 0x80;
+ maskL.m128i_u8[ 2] = 0x02;
+ maskL.m128i_u8[ 3] = 0x80;
+ maskL.m128i_u8[ 4] = 0x04;
+ maskL.m128i_u8[ 5] = 0x80;
+ maskL.m128i_u8[ 6] = 0x06;
+ maskL.m128i_u8[ 7] = 0x80;
+ maskL.m128i_u8[ 8] = 0x08;
+ maskL.m128i_u8[ 9] = 0x80;
+ maskL.m128i_u8[10] = 0x0A;
+ maskL.m128i_u8[11] = 0x80;
+ maskL.m128i_u8[12] = 0x0C;
+ maskL.m128i_u8[13] = 0x80;
+ maskL.m128i_u8[14] = 0x0E;
+ maskL.m128i_u8[15] = 0x80;
+ maskR.m128i_u8[ 0] = 0x01;
+ maskR.m128i_u8[ 1] = 0x80;
+ maskR.m128i_u8[ 2] = 0x03;
+ maskR.m128i_u8[ 3] = 0x80;
+ maskR.m128i_u8[ 4] = 0x05;
+ maskR.m128i_u8[ 5] = 0x80;
+ maskR.m128i_u8[ 6] = 0x07;
+ maskR.m128i_u8[ 7] = 0x80;
+ maskR.m128i_u8[ 8] = 0x09;
+ maskR.m128i_u8[ 9] = 0x80;
+ maskR.m128i_u8[10] = 0x0B;
+ maskR.m128i_u8[11] = 0x80;
+ maskR.m128i_u8[12] = 0x0D;
+ maskR.m128i_u8[13] = 0x80;
+ maskR.m128i_u8[14] = 0x0F;
+ maskR.m128i_u8[15] = 0x80;
+#else
+ __m128i mask = _mm_set1_epi16 ( 255 );
+#endif
+
+ for (y = 0; y < height; y += 2) {
+ __m128i *out = (__m128i *)(image + (y>>1) * raster);
+ const __m128i *in = (__m128i *)(image + y * raster);
+
+ for (x = 0; x < width; x += 32) {
+ mm0 = _mm_load_si128(in); //mm0 = HHhhGGggFFffEEeeDDddCCccBBbbAAaa
+#ifdef USE_SSE3
+ mm1 = _mm_shuffle_epi8(mm0, maskL);//mm1 = 00hh00gg00ff00ee00dd00cc00bb00aa
+ mm0 = _mm_shuffle_epi8(mm0, maskH);//mm0 = 00HH00GG00FF00EE00DD00CC00BB00AA
+#else
+ mm1 = _mm_and_si128(mask, mm0); //mm1 = 00hh00gg00ff00ee00dd00cc00bb00aa
+ mm0 = _mm_andnot_si128(mask, mm0); //mm0 = HH00GG00FF00EE00DD00CC00BB00AA00
+ mm0 = _mm_srli_epi16(mm0, 8); //mm0 = 00HH00GG00FF00EE00DD00CC00BB00AA
+#endif
+ mm0 = _mm_add_epi16(mm1, mm0);
+ mm3 = _mm_load_si128((__m128i*)(((char *)in) + raster));
+ //mm3 = PPppOOooNNnnMMmmLLllKKkkJJjjIIii
+ in++;
+#ifdef USE_SSE3
+ mm1 = _mm_shuffle_epi8(mm3, maskL);//mm1 = 00pp00oo00nn00mm00ll00kk00jj00ii
+ mm3 = _mm_shuffle_epi8(mm3, maskH);//mm3 = 00PP00OO00NN00MM00LL00KK00JJ00II
+#else
+ mm1 = _mm_and_si128(mask, mm3); //mm1 = 00pp00oo00nn00mm00ll00kk00jj00ii
+ mm3 = _mm_andnot_si128(mask, mm3); //mm3 = PP00OO00NN00MM00LL00KK00JJ00II00
+ mm3 = _mm_srli_epi16(mm3, 8); //mm3 = 00PP00OO00NN00MM00LL00KK00JJ00II
+#endif
+ mm3 = _mm_add_epi16(mm1, mm3);
+ mm2 = _mm_add_epi16(mm0, mm3);
+
+ mm0 = _mm_load_si128(in); //mm0 = HHhhGGggFFffEEeeDDddCCccBBbbAAaa
+#ifdef USE_SSE3
+ mm1 = _mm_shuffle_epi8(mm0, maskL);//mm1 = 00hh00gg00ff00ee00dd00cc00bb00aa
+ mm0 = _mm_shuffle_epi8(mm0, maskH);//mm0 = 00HH00GG00FF00EE00DD00CC00BB00AA
+#else
+ mm1 = _mm_and_si128(mask, mm0); //mm1 = 00hh00gg00ff00ee00dd00cc00bb00aa
+ mm0 = _mm_andnot_si128(mask, mm0); //mm0 = HH00GG00FF00EE00DD00CC00BB00AA00
+ mm0 = _mm_srli_epi16(mm0, 8); //mm0 = 00HH00GG00FF00EE00DD00CC00BB00AA
+#endif
+ mm0 = _mm_add_epi16(mm1, mm0);
+ mm3 = _mm_load_si128((__m128i*)(((char *)in) + raster));
+ //mm3 = PPppOOooNNnnMMmmLLllKKkkJJjjIIii
+ in++;
+#ifdef USE_SSE3
+ mm1 = _mm_shuffle_epi8(mm3, maskL);//mm1 = 00pp00oo00nn00mm00ll00kk00jj00ii
+ mm3 = _mm_shuffle_epi8(mm3, maskH);//mm3 = 00PP00OO00NN00MM00LL00KK00JJ00II
+#else
+ mm1 = _mm_and_si128(mask, mm3); //mm1 = 00pp00oo00nn00mm00ll00kk00jj00ii
+ mm3 = _mm_andnot_si128(mask, mm3); //mm3 = PP00OO00NN00MM00LL00KK00JJ00II00
+ mm3 = _mm_srli_epi16(mm3, 8); //mm3 = 00PP00OO00NN00MM00LL00KK00JJ00II
+#endif
+ mm3 = _mm_add_epi16(mm1, mm3);
+ mm3 = _mm_add_epi16(mm0, mm3);
+
+ mm2 = _mm_srli_epi16(mm2, 2);
+ mm3 = _mm_srli_epi16(mm3, 2);
+
+ // Recombine
+ mm2 = _mm_packus_epi16(mm2, mm3);
+
+ // Invert
+ mm2 = _mm_xor_si128(mm2, invert);
+
+ _mm_store_si128(out++, mm2);
+ }
+ }
+}
+#endif
+
+/* This is the function that does the bulk of the processing for the device.
+ * This will be called back from the process_page call after each 'band'
+ * has been drawn. */
+static int
+cmykog_process(void *arg_, gx_device *dev_, gx_device *bdev, const gs_int_rect *rect, void *buffer_)
+{
+ cmykog_process_arg_t *arg = (cmykog_process_arg_t *)arg_;
+ gx_device_cmykog *dev = (gx_device_cmykog *)dev_;
+ cmykog_process_buffer_t *buffer = (cmykog_process_buffer_t *)buffer_;
+ int code, ignore_start, i;
+ int w = rect->q.x - rect->p.x;
+ int h = rect->q.y - rect->p.y;
+ gs_int_rect my_rect;
+
+ /* We call 'get_bits_rectangle' to retrieve pointers to each plane of data
+ * for the supplied rectangle.
+ *
+ * Note that 'rect' as supplied to this function gives the position on the
+ * page, where 'my_rect' is the equivalent rectangle in the current band.
+ *
+ * No copying is done here; 6 pointers are filled in, one for each plane.
+ */
+ buffer->params.options = GB_COLORS_NATIVE | GB_ALPHA_NONE | GB_PACKING_PLANAR | GB_RETURN_POINTER | GB_ALIGN_ANY | GB_OFFSET_0 | GB_RASTER_ANY;
+ my_rect.p.x = 0;
+ my_rect.p.y = 0;
+ my_rect.q.x = w;
+ my_rect.q.y = h;
+ code = dev_proc(bdev, get_bits_rectangle)(bdev, &my_rect, &buffer->params, NULL);
+ if (code < 0)
+ return code;
+
+ /* Now we check to see which of those planes was actually used. See
+ * section 5) of the comment at the top of the file. */
+ (void)gdev_prn_color_usage((gx_device *)dev, rect->p.y, h,
+ &buffer->color_usage, &ignore_start);
+
+ /* Now, based on the pointers retrieved above, we can process the bands
+ * data. We are guaranteed that all the bands will have an even height,
+ * except possibly for the last one on the page. For simplicity we just
+ * drop any 'single' pixels on the end of rows or on the bottom of the
+ * page before trying to perform the subsampling. */
+ w &= ~1;
+ h &= ~1;
+ for (i = 0; i < dev->color_info.num_components; i++) {
+ /* Only process the plane if it's used */
+ if ((buffer->color_usage.or>>i) & 1)
+ average_plane(buffer->params.data[i], w, h, arg->dev_raster);
+ }
+
+ buffer->w = w>>1;
+ buffer->h = h>>1;
+
+ return code;
+}
+
+static void
+write_plane(FILE *file, byte *data, int w, int h, int raster)
+{
+#ifndef NO_OUTPUT
+ for (; h > 0; h--) {
+ fwrite(data, 1, w, file);
+ data += raster;
+ }
+#endif
+}
+
+static const char empty[64] = {
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
+};
+
+static void
+write_empty_plane(FILE *file, int w, int h)
+{
+#ifndef NO_OUTPUT
+ w *= h;
+ while (w > 0) {
+ h = w;
+ if (h > 64)
+ h = 64;
+ fwrite(empty, 1, h, file);
+ w -= h;
+ }
+#endif
+}
+
+/* This function is called back from process_page for each band (in order)
+ * after the process_fn has completed. All we need to do is to output
+ * the contents of the buffer. */
+static int
+cmykog_output(void *arg_, gx_device *dev_, void *buffer_)
+{
+ cmykog_process_arg_t *arg = (cmykog_process_arg_t *)arg_;
+ gx_device_cmykog *dev = (gx_device_cmykog *)dev_;
+ cmykog_process_buffer_t *buffer = (cmykog_process_buffer_t *)buffer_;
+ int i;
+ int w = buffer->w;
+ int h = buffer->h;
+ int raster = arg->dev_raster;
+
+ /* Output each plane in turn to file */
+ for (i = 0; i < dev->color_info.num_components; i++) {
+ /* The data will only have been processed if the plane is used. */
+ if ((buffer->color_usage.or>>i) & 1)
+ write_plane(arg->spot_file[i], buffer->params.data[i], w, h, raster);
+ else
+ write_empty_plane(arg->spot_file[i], w, h);
+ }
+
+ return 0;
+}
+
+/* This, finally is the main entry point that triggers printing for the
+ * device. */
+static int
+cmykog_print_page(gx_device_printer * pdev, FILE * prn_stream)
+{
+ gx_device_cmykog * pdevn = (gx_device_cmykog *) pdev;
+ int ncomp = pdevn->color_info.num_components;
+ cmykog_process_arg_t *arg;
+ gx_process_page_options_t options;
+ int code, i;
+ psd_write_ctx *psd_ctx;
+
+ if ((arg = (cmykog_process_arg_t *)gs_alloc_bytes(pdev->memory,
+ sizeof(cmykog_process_arg_t),
+ "cmykog_print_page arg")) == NULL)
+ return_error(gs_error_VMerror);
+
+ memset(arg, 0, sizeof(cmykog_process_arg_t));
+ if ((psd_ctx = (psd_write_ctx *)gs_alloc_bytes(pdev->memory,
+ sizeof(psd_write_ctx),
+ "cmykog_print_page psd_ctx")) == NULL) {
+ gs_free_object(pdev->memory, arg, "cmykog_print_page arg");
+ return_error(gs_error_VMerror);
+ }
+
+ /* Calculate the raster that will be used for each bands data;
+ * gx_device_raster_plane takes care of any alignment or padding
+ * required. */
+ arg->dev_raster = gx_device_raster_plane((gx_device *)pdev, NULL);
+
+#ifndef NO_OUTPUT
+ /* Output the psd headers */
+ code = psd_setup(psd_ctx, (gx_devn_prn_device *)pdevn,
+ prn_stream, pdev->width>>1, pdev->height>>1);
+ if (code < 0)
+ return code;
+
+ code = psd_write_header(psd_ctx, (gx_devn_prn_device *)pdevn);
+ if (code < 0)
+ return code;
+
+ /* We will output the 0th plane direct to the target file. We open
+ * temporary files here, where the data for planes 1-5 will be put.
+ * We will then copy this data into the target file at the end. */
+ arg->spot_file[0] = prn_stream;
+ for(i = 1; i < ncomp; i++) {
+ arg->spot_file[i] = gp_open_scratch_file(pdev->memory, gp_scratch_file_name_prefix, &(arg->spot_name[i][0]), "w+b");
+ if (arg->spot_file[i] == NULL) {
+ code = gs_error_invalidfileaccess;
+ goto prn_done;
+ }
+ }
+#endif
+
+ /* Kick off the actual hard work */
+ options.init_buffer_fn = cmykog_init_buffer;
+ options.free_buffer_fn = cmykog_free_buffer;
+ options.process_fn = cmykog_process;
+ options.output_fn = cmykog_output;
+ options.arg = arg;
+ options.options = 0;
+ code = dev_proc(pdev, process_page)((gx_device *)pdev, &options);
+
+#ifndef NO_OUTPUT
+ /* Now collate the temporary files. */
+ for (i = 1; i < ncomp; i++) {
+ char tmp[4096];
+ int n;
+ fseek(arg->spot_file[i], 0, SEEK_SET);
+ while (!feof(arg->spot_file[i])) {
+ n = fread(tmp, 1, 4096, arg->spot_file[i]);
+ fwrite(tmp, 1, n, prn_stream);
+ }
+ }
+ /* If Ghostscript knows that all 6 planes aren't used, it may
+ * truncate ncomp. Write the extra planes as appropriate. */
+ for (; i < pdev->color_info.max_components; i++) {
+ write_empty_plane(prn_stream, pdev->width>>1, pdev->height>>1);
+ }
+#endif
+
+prn_done:
+
+#ifndef NO_OUTPUT
+ /* Close the temporary files. */
+ for(i = 1; i < ncomp; i++) {
+ if (arg->spot_file[i] != NULL)
+ fclose(arg->spot_file[i]);
+ if(arg->spot_name[i][0])
+ unlink(arg->spot_name[i]);
+ }
+#endif
+ gs_free_object(pdev->memory, psd_ctx, "cmykog_print_page psd_ctx");
+ gs_free_object(pdev->memory, arg, "cmykog_print_page arg");
+
+ return code;
+}
+
+/* Finally, the device definition itself */
+
+#define device_procs(get_color_mapping_procs)\
+{ cmykog_open, /* open device */\
+ gx_default_get_initial_matrix, /* initialize matrix */\
+ NULL, /* sync_output */\
+ gdev_prn_bg_output_page, /* output_page */\
+ cmykog_close, /* close */\
+ NULL, /* map_rgb_color - not used */\
+ NULL, /* map_color_rgb - not used */\
+ NULL, /* fill_rectangle */\
+ NULL, /* tile_rectangle */\
+ NULL, /* copy_mono */\
+ NULL, /* copy_color */\
+ NULL, /* draw_line */\
+ NULL, /* get_bits */\
+ gx_devn_prn_get_params, /* get_params */\
+ cmykog_put_params, /* put_params */\
+ NULL, /* map_cmyk_color - not used */\
+ NULL, /* get_xfont_procs */\
+ NULL, /* get_xfont_device */\
+ NULL, /* map_rgb_alpha_color */\
+ gx_page_device_get_page_device, /* get_page_device */\
+ NULL, /* get_alpha_bits */\
+ NULL, /* copy_alpha */\
+ NULL, /* get_band */\
+ NULL, /* copy_rop */\
+ NULL, /* fill_path */\
+ NULL, /* stroke_path */\
+ NULL, /* fill_mask */\
+ NULL, /* fill_trapezoid */\
+ NULL, /* fill_parallelogram */\
+ NULL, /* fill_triangle */\
+ NULL, /* draw_thin_line */\
+ NULL, /* begin_image */\
+ NULL, /* image_data */\
+ NULL, /* end_image */\
+ NULL, /* strip_tile_rectangle */\
+ NULL, /* strip_copy_rop */\
+ NULL, /* get_clipping_box */\
+ NULL, /* begin_typed_image */\
+ NULL, /* get_bits_rectangle */\
+ NULL, /* map_color_rgb_alpha */\
+ NULL, /* create_compositor */\
+ NULL, /* get_hardware_params */\
+ NULL, /* text_begin */\
+ NULL, /* finish_copydevice */\
+ NULL, /* begin_transparency_group */\
+ NULL, /* end_transparency_group */\
+ NULL, /* begin_transparency_mask */\
+ NULL, /* end_transparency_mask */\
+ NULL, /* discard_transparency_layer */\
+ gx_devn_prn_get_color_mapping_procs,/* get_color_mapping_procs */\
+ cmykog_get_color_comp_index, /* get_color_comp_index */\
+ gx_devn_prn_encode_color, /* encode_color */\
+ gx_devn_prn_decode_color, /* decode_color */\
+ NULL, /* pattern_manage */\
+ NULL, /* fill_rectangle_hl_color */\
+ NULL, /* include_color_space */\
+ NULL, /* fill_linear_color_scanline */\
+ NULL, /* fill_linear_color_trapezoid */\
+ NULL, /* fill_linear_color_triangle */\
+ NULL, /* update_spot_equivalent_colors */\
+ gx_devn_prn_ret_devn_params, /* ret_devn_params */\
+ NULL, /* fillpage */\
+ NULL, /* push_transparency_state */\
+ NULL, /* pop_transparency_state */\
+ NULL, /* put_image */\
+ cmykog_dev_spec_op /* dev_spec_op */\
+}
+
+fixed_colorant_name DevCMYKOGComponents[] = {
+ "Cyan",
+ "Magenta",
+ "Yellow",
+ "Black",
+ "Artifex Orange",
+ "Artifex Green",
+ 0 /* List terminator */
+};
+
+#define CMYKOG_DEVICE(procs, dname, ncomp, pol, depth, mg, mc, cn, xdpi, ydpi)\
+ std_device_full_body_type_extended(gx_device_cmykog, &procs, dname,\
+ &st_cmykog_device,\
+ (int)((long)(DEFAULT_WIDTH_10THS) * (xdpi) / 10),\
+ (int)((long)(DEFAULT_HEIGHT_10THS) * (ydpi) / 10),\
+ xdpi, ydpi,\
+ ncomp, /* MaxComponents */\
+ ncomp, /* NumComp */\
+ pol, /* Polarity */\
+ depth, 0, /* Depth, GrayIndex */\
+ mg, mc, /* MaxGray, MaxColor */\
+ mg + 1, mc + 1, /* DitherGray, DitherColor */\
+ GX_CINFO_SEP_LIN, /* Linear & Separable */\
+ cn, /* Process color model name */\
+ 0, 0, /* offsets */\
+ 0, 0, 0, 0 /* margins */\
+ ),\
+ prn_device_body_rest_(cmykog_print_page)
+
+/*
+ * PSDCMYKOG 8bits
+ */
+static const gx_device_procs cmykog_procs = device_procs(get_cmykog_spot_color_mapping_procs);
+
+const gx_device_cmykog gs_psdcmykog_device =
+{
+ CMYKOG_DEVICE(cmykog_procs, "psdcmykog", 6, GX_CINFO_POLARITY_SUBTRACTIVE, 48, 255, 255, "DeviceCMYK", 600, 600),
+ /* device specific parameters */
+ { 8, /* Bits per color - must match ncomp, depth, etc. above */
+ DevCMYKOGComponents, /* Names of color model colorants */
+ 4, /* Number colorants for CMYK */
+ 6, /* MaxSeparations */
+ -1, /* PageSpotColors has not been specified */
+ {0}, /* SeparationNames */
+ 0, /* SeparationOrder names */
+ {0, 1, 2, 3, 4, 5, 6, 7 } /* Initial component SeparationOrder */
+ }
+};
diff --git a/devices/gdevcp50.c b/devices/gdevcp50.c
new file mode 100644
index 000000000..d8b242f09
--- /dev/null
+++ b/devices/gdevcp50.c
@@ -0,0 +1,224 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+/* Mitsubishi CP50 color printer driver */
+#include "gdevprn.h"
+#define ppdev ((gx_device_printer *)pdev)
+
+/***
+ *** Note: this driver was contributed by a user. Please contact
+ *** Michael Hu (michael@ximage.com) if you have questions.
+ ***/
+
+/* The value of X_PIXEL and Y_PIXEL is gained by experiment */
+#define X_PIXEL 474
+#define Y_PIXEL 800
+
+/* The value of FIRST_LINE and LAST_LINE is gained by experiment */
+/* Note: LAST-LINE - FIRST_LINE + 1 should close to Y_PIXEL */
+#define FIRST_LINE 140
+#define LAST_LINE 933
+
+/* The value of FIRST is gained by experiment */
+/* There are 60 pixel(RGB) in the right clipped margin */
+#define FIRST_COLUMN 180
+
+/* The value of X_DPI and Y_DPI is gained by experiment */
+#define X_DPI 154 /* pixels per inch */
+#define Y_DPI 187 /* pixels per inch */
+
+/* The device descriptor */
+static dev_proc_print_page(cp50_print_page);
+static dev_proc_output_page(cp50_output_page);
+
+static dev_proc_map_rgb_color(cp50_rgb_color);
+static dev_proc_map_color_rgb(cp50_color_rgb);
+
+static gx_device_procs cp50_procs =
+ prn_color_procs(gdev_prn_open, cp50_output_page, gdev_prn_close,
+ cp50_rgb_color, cp50_color_rgb);
+
+typedef struct gx_device_printer_cp50
+{
+ gx_device_common;
+ gx_prn_device_common;
+ int copies;
+} gx_device_printer_cp50;
+
+const gx_device_printer_cp50 far_data gs_cp50_device =
+{
+ prn_device_std_body(gx_device_printer_cp50, cp50_procs, "cp50",
+ 39, /* width_10ths, 100mm */
+ 59, /* height_10ths,150mm */
+ X_DPI, Y_DPI,
+ 0.39, 0.91, 0.43, 0.75, /* margins */
+ 24, cp50_print_page)
+};
+
+/* ------ Internal routines ------ */
+
+/* Send the page to the printer. */
+static int
+cp50_print_page(gx_device_printer *pdev, FILE *prn_stream)
+{
+ int line_size = gdev_mem_bytes_per_scan_line((gx_device *)pdev);
+ byte *out = (byte *)gs_malloc(pdev->memory, line_size, 1, "cp50_print_page(out)");
+ byte *r_plane = (byte *)gs_malloc(pdev->memory, X_PIXEL*Y_PIXEL, 1, "cp50_print_page(r_plane)");
+ byte *g_plane = (byte *)gs_malloc(pdev->memory, X_PIXEL*Y_PIXEL, 1, "cp50_print_page(g_plane)");
+ byte *b_plane = (byte *)gs_malloc(pdev->memory, X_PIXEL*Y_PIXEL, 1, "cp50_print_page(b_plane)");
+ byte *t_plane = (byte *)gs_malloc(pdev->memory, X_PIXEL*Y_PIXEL, 1, "cp50_print_page(t_plane)");
+ int lnum = FIRST_LINE;
+ int last = LAST_LINE;
+ int lines = X_PIXEL;
+ byte hi_lines, lo_lines;
+ byte num_copies;
+ int i,j;
+
+/*fprintf(prn_stream, "%d,%d,%d,", pdev->width, pdev->height, line_size);*/
+
+ /* Check allocations */
+ if ( out == 0 || r_plane == 0 || g_plane == 0 || b_plane == 0 ||
+ t_plane == 0)
+ { if ( out )
+ gs_free(pdev->memory, (char *)out, line_size, 1,
+ "cp50_print_page(out)");
+ if (r_plane)
+ gs_free(pdev->memory, (char *)r_plane, X_PIXEL*Y_PIXEL, 1,
+ "cp50_print_page(r_plane)");
+ if (g_plane)
+ gs_free(pdev->memory, (char *)g_plane, X_PIXEL*Y_PIXEL, 1,
+ "cp50_print_page(g_plane)");
+ if (b_plane)
+ gs_free(pdev->memory, (char *)b_plane, X_PIXEL*Y_PIXEL, 1,
+ "cp50_print_page(b_plane)");
+ if (t_plane)
+ gs_free(pdev->memory, (char *)t_plane, X_PIXEL*Y_PIXEL, 1,
+ "cp50_print_page(t_plane)");
+ return -1;
+ }
+
+ /* set each plane as white */
+ memset(r_plane, -1, X_PIXEL*Y_PIXEL);
+ memset(g_plane, -1, X_PIXEL*Y_PIXEL);
+ memset(b_plane, -1, X_PIXEL*Y_PIXEL);
+ memset(t_plane, -1, X_PIXEL*Y_PIXEL);
+
+ /* Initialize the printer */ /* see programmer manual for CP50 */
+ fprintf(prn_stream,"\033\101");
+ fprintf(prn_stream,"\033\106\010\001");
+ fprintf(prn_stream,"\033\106\010\003");
+
+ /* set number of copies */
+ fprintf(prn_stream,"\033\116");
+ num_copies = ((gx_device_printer_cp50 *)pdev)->copies & 0xFF;
+ fwrite(&num_copies, sizeof(char), 1, prn_stream);
+
+ /* download image */
+ hi_lines = lines >> 8;
+ lo_lines = lines & 0xFF;
+
+ fprintf(prn_stream,"\033\123\062");
+ fwrite(&hi_lines, sizeof(char), 1, prn_stream);
+ fwrite(&lo_lines, sizeof(char), 1, prn_stream);
+ fprintf(prn_stream,"\001"); /* dummy */
+
+ /* Print lines of graphics */
+ while ( lnum <= last )
+ {
+ int i, col;
+ gdev_prn_copy_scan_lines(pdev, lnum, (byte *)out, line_size);
+ /*fwrite(out, sizeof(char), line_size, prn_stream);*/
+ for(i=0; i<X_PIXEL; i++)
+ {
+ col = (lnum-FIRST_LINE) * X_PIXEL + i;
+ r_plane[col] = out[i*3+FIRST_COLUMN];
+ g_plane[col] = out[i*3+1+FIRST_COLUMN];
+ b_plane[col] = out[i*3+2+FIRST_COLUMN];
+ }
+ lnum ++;
+ }
+
+ /* rotate each plane and download it */
+ for(i=0;i<X_PIXEL;i++)
+ for(j=Y_PIXEL-1;j>=0;j--)
+ t_plane[(Y_PIXEL-1-j)+i*Y_PIXEL] = r_plane[i+j*X_PIXEL];
+ fwrite(t_plane, sizeof(char), X_PIXEL*Y_PIXEL, prn_stream);
+
+ for(i=0;i<X_PIXEL;i++)
+ for(j=Y_PIXEL-1;j>=0;j--)
+ t_plane[(Y_PIXEL-1-j)+i*Y_PIXEL] = g_plane[i+j*X_PIXEL];
+ fwrite(t_plane, sizeof(char), X_PIXEL*Y_PIXEL, prn_stream);
+
+ for(i=0;i<X_PIXEL;i++)
+ for(j=Y_PIXEL-1;j>=0;j--)
+ t_plane[(Y_PIXEL-1-j)+i*Y_PIXEL] = b_plane[i+j*X_PIXEL];
+ fwrite(t_plane, sizeof(char), X_PIXEL*Y_PIXEL, prn_stream);
+
+ gs_free(pdev->memory, (char *)out, line_size, 1, "cp50_print_page(out)");
+ gs_free(pdev->memory, (char *)r_plane, X_PIXEL*Y_PIXEL, 1, "cp50_print_page(r_plane)");
+ gs_free(pdev->memory, (char *)g_plane, X_PIXEL*Y_PIXEL, 1, "cp50_print_page(g_plane)");
+ gs_free(pdev->memory, (char *)b_plane, X_PIXEL*Y_PIXEL, 1, "cp50_print_page(b_plane)");
+ gs_free(pdev->memory, (char *)t_plane, X_PIXEL*Y_PIXEL, 1, "cp50_print_page(t_plane)");
+
+ return 0;
+}
+
+static int
+cp50_output_page(gx_device *pdev, int num_copies, int flush)
+{ int code, outcode, closecode;
+
+ code = gdev_prn_open_printer(pdev, 1);
+ if ( code < 0 ) return code;
+
+ ((gx_device_printer_cp50 *)pdev)->copies = num_copies; /* using global variable to pass */
+
+ /* Print the accumulated page description. */
+ outcode = (*ppdev->printer_procs.print_page)(ppdev, ppdev->file);
+
+ closecode = gdev_prn_close_printer(pdev);
+
+ if ( ppdev->buffer_space ) /* reinitialize clist for writing */
+ code = (*gs_clist_device_procs.output_page)(pdev, num_copies, flush);
+
+ if ( outcode < 0 ) return outcode;
+ if ( closecode < 0 ) return closecode;
+ if ( code < 0 ) return code;
+ return gx_finish_output_page(pdev, num_copies, flush);
+}
+
+/* 24-bit color mappers (taken from gdevmem2.c). */
+/* Note that Windows expects RGB values in the order B,G,R. */
+
+/* Map a r-g-b color to a color index. */
+static gx_color_index
+cp50_rgb_color(gx_device *dev, const gx_color_value cv[])
+{
+ gx_color_value red, green, blue;
+
+ red = cv[0]; green = cv[1]; blue = cv[2];
+ return ((ulong)gx_color_value_to_byte(red) << 16)+
+ ((uint)gx_color_value_to_byte(green) << 8) +
+ gx_color_value_to_byte(blue);
+}
+
+/* Map a color index to a r-g-b color. */
+static int
+cp50_color_rgb(gx_device *dev, gx_color_index color,
+ gx_color_value prgb[3])
+{ prgb[2] = gx_color_value_from_byte(color & 0xff);
+ prgb[1] = gx_color_value_from_byte((color >> 8) & 0xff);
+ prgb[0] = gx_color_value_from_byte(color >> 16);
+ return 0;
+}
diff --git a/devices/gdevcslw.c b/devices/gdevcslw.c
new file mode 100644
index 000000000..626badf6f
--- /dev/null
+++ b/devices/gdevcslw.c
@@ -0,0 +1,145 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+/* CoStar LabelWriter II, II Plus driver for Ghostscript */
+/* Contributed by Mike McCauley mikem@open.com.au */
+
+#include "gdevprn.h"
+
+/* We round up the LINE_SIZE to a multiple of a ulong for faster scanning. */
+typedef ulong word;
+#define W sizeof(word)
+
+/* Printer types */
+#define LW 0
+
+/* The device descriptors */
+
+static dev_proc_print_page(coslw_print_page);
+
+const gx_device_printer gs_coslw2p_device =
+prn_device(prn_bg_procs, "coslw2p", /* The print_page proc is compatible with allowing bg printing */
+ 200, 400, /* 2 inches wide */
+ 128, 128, /* 5 dots per mm */
+ 0, 0, 0, 0,
+ 1, coslw_print_page);
+
+const gx_device_printer gs_coslwxl_device =
+prn_device(prn_bg_procs, "coslwxl", /* The print_page proc is compatible with allowing bg printing */
+ 200, 400, /* 2 inches wide */
+ 204, 204, /* 8 dots per mm */
+ 0, 0, 0, 0,
+ 1, coslw_print_page);
+
+/* ------ Internal routines ------ */
+
+/* Send the page to the printer. */
+static int
+coslw_print_page(gx_device_printer * pdev, FILE * prn_stream)
+{
+ int line_size = gdev_mem_bytes_per_scan_line((gx_device *) pdev);
+ int line_size_words = (line_size + W - 1) / W;
+ uint storage_size_words = line_size_words * 8; /* data, out_row, out_row_alt, prev_row */
+ word *storage = (ulong *) gs_malloc(pdev->memory, storage_size_words, W,
+ "coslw_print_page");
+
+ word *data_words;
+#define data ((byte *)data_words)
+
+ byte *out_data;
+ int num_rows = dev_print_scan_lines(pdev);
+ int bytes_per_line = 0;
+ int out_count;
+ int code = 0;
+
+ if (storage == 0) /* can't allocate working area */
+ return_error(gs_error_VMerror);
+ data_words = storage;
+
+ /* Clear temp storage */
+ memset(data, 0, storage_size_words * W);
+
+ /* Initialize printer. */
+ if (pdev->PageCount == 0) {
+ }
+
+ /* Put out per-page initialization. */
+
+ /* End raster graphics, position cursor at top. */
+
+ /* Send each scan line in turn */
+ {
+ int lnum;
+ int num_blank_lines = 0;
+ word rmask = ~(word) 0 << (-pdev->width & (W * 8 - 1));
+
+ /* Transfer raster graphics. */
+ for (lnum = 0; lnum < num_rows; lnum++) {
+ register word *end_data =
+ data_words + line_size_words;
+
+ code = gdev_prn_copy_scan_lines(pdev, lnum,
+ (byte *) data, line_size);
+ if (code < 0)
+ break;
+ /* Mask off 1-bits beyond the line width. */
+ end_data[-1] &= rmask;
+ /* Remove trailing 0s. */
+ while (end_data > data_words && end_data[-1] == 0)
+ end_data--;
+ if (end_data == data_words) { /* Blank line */
+ num_blank_lines++;
+ continue;
+ }
+
+ /* We've reached a non-blank line. */
+ /* Put out a spacing command if necessary. */
+ while (num_blank_lines > 0)
+ {
+ int this_blank = 255;
+ if (num_blank_lines < this_blank)
+ this_blank = num_blank_lines;
+ fprintf(prn_stream, "\033f\001%c", this_blank);
+ num_blank_lines -= this_blank;
+ }
+
+ /* Perhaps add compression here later? */
+ out_data = data;
+ out_count = (byte *) end_data - data;
+
+ /* For 2 inch model, max width is 56 bytes */
+ if (out_count > 56)
+ out_count = 56;
+ /* Possible change the bytes per line */
+ if (bytes_per_line != out_count)
+ {
+ fprintf(prn_stream, "\033D%c", out_count);
+ bytes_per_line = out_count;
+ }
+
+ /* Transfer the data */
+ fputs("\026", prn_stream);
+ fwrite(out_data, sizeof(byte), out_count, prn_stream);
+ }
+ }
+
+ /* eject page */
+ fputs("\033E", prn_stream);
+
+ /* free temporary storage */
+ gs_free(pdev->memory, (char *)storage, storage_size_words, W, "coslw_print_page");
+
+ return code;
+}
diff --git a/devices/gdevdfax.c b/devices/gdevdfax.c
new file mode 100644
index 000000000..8942894ff
--- /dev/null
+++ b/devices/gdevdfax.c
@@ -0,0 +1,105 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+/* DigiBoard fax device. */
+/***
+ *** Note: this driver is maintained by a user: please contact
+ *** Rick Richardson (rick@digibd.com) if you have questions.
+ ***/
+#include "gdevprn.h"
+#include "strimpl.h"
+#include "scfx.h"
+#include "gdevfax.h"
+#include "gdevtfax.h"
+
+/* Define the device parameters. */
+#define X_DPI 204
+#define Y_DPI 196
+
+/* The device descriptors */
+
+static dev_proc_open_device(dfax_prn_open);
+static dev_proc_print_page(dfax_print_page);
+
+struct gx_device_dfax_s {
+ gx_device_common;
+ gx_prn_device_common;
+ long pageno;
+ uint iwidth; /* width of image data in pixels */
+};
+typedef struct gx_device_dfax_s gx_device_dfax;
+
+/* Since the print_page doesn't alter the device, this device can print in the background */
+static gx_device_procs dfax_procs =
+ prn_procs(dfax_prn_open, gdev_prn_bg_output_page_seekable, gdev_prn_close);
+
+gx_device_dfax far_data gs_dfaxlow_device =
+{ prn_device_std_body(gx_device_dfax, dfax_procs, "dfaxlow",
+ DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
+ X_DPI, Y_DPI/2,
+ 0,0,0,0, /* margins */
+ 1, dfax_print_page)
+};
+
+gx_device_dfax far_data gs_dfaxhigh_device =
+{ prn_device_std_body(gx_device_dfax, dfax_procs, "dfaxhigh",
+ DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
+ X_DPI, Y_DPI,
+ 0,0,0,0, /* margins */
+ 1, dfax_print_page)
+};
+
+#define dfdev ((gx_device_dfax *)dev)
+
+/* Open the device, adjusting the paper size. */
+static int
+dfax_prn_open(gx_device *dev)
+{ dfdev->pageno = 0;
+ return gdev_fax_open(dev);
+}
+
+/* Print a DigiFAX page. */
+static int
+dfax_print_page(gx_device_printer *dev, FILE *prn_stream)
+{ stream_CFE_state state;
+ static char hdr[64] = "\000PC Research, Inc\000\000\000\000\000\000";
+ int code;
+
+ gdev_fax_init_state(&state, (gx_device_fax *)dev);
+ state.EndOfLine = true;
+ state.EncodedByteAlign = true;
+
+ /* Start a page: write the header */
+ hdr[24] = 0; hdr[28] = 1;
+ hdr[26] = ++dfdev->pageno; hdr[27] = dfdev->pageno >> 8;
+ if (dev->y_pixels_per_inch == Y_DPI)
+ { hdr[45] = 0x40; hdr[29] = 1; } /* high res */
+ else
+ { hdr[45] = hdr[29] = 0; } /* low res */
+ fseek(prn_stream, 0, SEEK_END);
+ fwrite(hdr, sizeof(hdr), 1, prn_stream);
+
+ /* Write the page */
+ code = gdev_fax_print_page(dev, prn_stream, &state);
+
+ /* Fixup page count */
+ fseek(prn_stream, 24L, SEEK_SET);
+ hdr[24] = dfdev->pageno; hdr[25] = dfdev->pageno >> 8;
+ fwrite(hdr+24, 2, 1, prn_stream);
+
+ return code;
+}
+
+#undef dfdev
diff --git a/devices/gdevdjet.c b/devices/gdevdjet.c
new file mode 100644
index 000000000..23fdc1da3
--- /dev/null
+++ b/devices/gdevdjet.c
@@ -0,0 +1,608 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+/* HP LaserJet/DeskJet driver for Ghostscript */
+#include "gdevprn.h"
+#include "gdevdljm.h"
+
+/*
+ * Thanks for various improvements to:
+ * Jim Mayer (mayer@wrc.xerox.com)
+ * Jan-Mark Wams (jms@cs.vu.nl)
+ * Frans van Hoesel (hoesel@chem.rug.nl)
+ * George Cameron (g.cameron@biomed.abdn.ac.uk)
+ * Nick Duffek (nsd@bbc.com)
+ * Thanks for the FS-600 driver to:
+ * Peter Schildmann (peter.schildmann@etechnik.uni-rostock.de)
+ * Thanks for the LJIIID duplex capability to:
+ * PDP (Philip) Brown (phil@3soft-uk.com)
+ * Thanks for the OCE 9050 driver to:
+ * William Bader (wbader@EECS.Lehigh.Edu)
+ * Thanks for the LJ4D duplex capability to:
+ * Les Johnson <les@infolabs.com>
+ */
+
+/*
+ * You may select a default resolution of 75, 100, 150, 300, or
+ * (LJ4 only) 600 DPI in the makefile, or an actual resolution
+ * on the gs command line.
+ *
+ * If the preprocessor symbol A4 is defined, the default paper size is
+ * the European A4 size; otherwise it is the U.S. letter size (8.5"x11").
+ *
+ * To determine the proper "margin" settings for your printer, see the
+ * file align.ps.
+ */
+
+/* Define the default, maximum resolutions. */
+#ifdef X_DPI
+# define X_DPI2 X_DPI
+#else
+# define X_DPI 300
+# define X_DPI2 600
+#endif
+#ifdef Y_DPI
+# define Y_DPI2 Y_DPI
+#else
+# define Y_DPI 300
+# define Y_DPI2 600
+#endif
+
+/*
+ * For all DeskJet Printers:
+ *
+ * Maximum printing width = 2400 dots = 8"
+ * Maximum recommended printing height = 3100 dots = 10 1/3"
+ *
+ * All Deskjets have 1/2" unprintable bottom margin.
+ * The recommendation comes from the HP Software Developer's Guide for
+ * the DeskJet 500, DeskJet PLUS, and DeskJet printers, version C.01.00
+ * of 12/1/90.
+ *
+ * Note that the margins defined just below here apply only to the DeskJet;
+ * the paper size, width and height apply to the LaserJet as well.
+ */
+
+/* Margins are left, bottom, right, top. */
+/* from Frans van Hoesel hoesel@rugr86.rug.nl. */
+/* A4 has a left margin of 1/8 inch and at a printing width of
+ * 8 inch this give a right margin of 0.143. The 0.09 top margin is
+ * not the actual margin - which is 0.07 - but compensates for the
+ * inexact paperlength which is set to 117 10ths.
+ * Somebody should check for letter sized paper. I left it at 0.07".
+ */
+#define DESKJET_MARGINS_LETTER (float)0.2, (float)0.45, (float)0.3, (float)0.05
+#define DESKJET_MARGINS_A4 (float)0.125, (float)0.5, (float)0.143, (float)0.09
+/* Similar margins for the LaserJet. */
+/* These are defined in the PCL 5 Technical Reference Manual. */
+/* Note that for PCL 5 printers, we get the printer to translate the */
+/* coordinate system: the margins only define the unprintable area. */
+#define LASERJET_MARGINS_A4 (float)0.167, (float)0.167, (float)0.167, (float)0.167
+#define LASERJET_MARGINS_LETTER (float)0.167, (float)0.167, (float)0.167, (float)0.167
+
+/* See gdevdljm.h for the definitions of the PCL_ features. */
+
+/* The device descriptors */
+static dev_proc_open_device(hpjet_open);
+static dev_proc_close_device(hpjet_close);
+static dev_proc_close_device(ljet4pjl_close);
+static dev_proc_print_page_copies(djet_print_page_copies);
+static dev_proc_print_page_copies(djet500_print_page_copies);
+static dev_proc_print_page_copies(fs600_print_page_copies);
+static dev_proc_print_page_copies(ljet_print_page_copies);
+static dev_proc_print_page_copies(ljetplus_print_page_copies);
+static dev_proc_print_page_copies(ljet2p_print_page_copies);
+static dev_proc_print_page_copies(ljet3_print_page_copies);
+static dev_proc_print_page_copies(ljet3d_print_page_copies);
+static dev_proc_print_page_copies(ljet4_print_page_copies);
+static dev_proc_print_page_copies(ljet4d_print_page_copies);
+static dev_proc_print_page_copies(lp2563_print_page_copies);
+static dev_proc_print_page_copies(oce9050_print_page_copies);
+static dev_proc_print_page_copies(ljet4pjl_print_page_copies);
+static dev_proc_get_params(hpjet_get_params);
+static dev_proc_put_params(hpjet_put_params);
+
+/* Since the print_page doesn't alter the device, this device can print in the background */
+static const gx_device_procs prn_hp_procs =
+prn_params_procs(hpjet_open, gdev_prn_bg_output_page, hpjet_close,
+ hpjet_get_params, hpjet_put_params);
+
+/* Since the print_page doesn't alter the device, this device can print in the background */
+static gx_device_procs prn_ljet4pjl_procs =
+prn_params_procs(hpjet_open, gdev_prn_bg_output_page, ljet4pjl_close,
+ gdev_prn_get_params, gdev_prn_put_params);
+
+typedef struct gx_device_hpjet_s gx_device_hpjet;
+
+struct gx_device_hpjet_s {
+ gx_device_common;
+ gx_prn_device_common;
+ int MediaPosition;
+ bool MediaPosition_set;
+ bool ManualFeed;
+ bool ManualFeed_set;
+ bool Tumble;
+};
+
+#define HPJET_DEVICE(procs, dname, w10, h10, xdpi, ydpi, lm, bm, rm, tm, color_bits, print_page_copies)\
+ { prn_device_std_margins_body_copies(gx_device_hpjet, procs, dname, \
+ w10, h10, xdpi, ydpi, lm, tm, lm, bm, rm, tm, color_bits, \
+ print_page_copies), \
+ 0, false, false, false, false }
+
+const gx_device_hpjet gs_deskjet_device =
+HPJET_DEVICE(prn_hp_procs, "deskjet",
+ DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
+ X_DPI, Y_DPI,
+ 0, 0, 0, 0, /* margins filled in by hpjet_open */
+ 1, djet_print_page_copies);
+
+const gx_device_hpjet gs_djet500_device =
+HPJET_DEVICE(prn_hp_procs, "djet500",
+ DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
+ X_DPI, Y_DPI,
+ 0, 0, 0, 0, /* margins filled in by hpjet_open */
+ 1, djet500_print_page_copies);
+
+const gx_device_hpjet gs_fs600_device =
+HPJET_DEVICE(prn_hp_procs, "fs600",
+ DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
+ X_DPI2, Y_DPI2,
+ 0.23, 0.0, 0.23, 0.04, /* margins */
+ 1, fs600_print_page_copies);
+
+const gx_device_hpjet gs_laserjet_device =
+HPJET_DEVICE(prn_hp_procs, "laserjet",
+ DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
+ X_DPI, Y_DPI,
+ 0.05, 0.25, 0.55, 0.25, /* margins */
+ 1, ljet_print_page_copies);
+
+const gx_device_hpjet gs_ljetplus_device =
+HPJET_DEVICE(prn_hp_procs, "ljetplus",
+ DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
+ X_DPI, Y_DPI,
+ 0.05, 0.25, 0.55, 0.25, /* margins */
+ 1, ljetplus_print_page_copies);
+
+const gx_device_hpjet gs_ljet2p_device =
+HPJET_DEVICE(prn_hp_procs, "ljet2p",
+ DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
+ X_DPI, Y_DPI,
+ 0.25, 0.25, 0.25, 0.0, /* margins */
+ 1, ljet2p_print_page_copies);
+
+const gx_device_hpjet gs_ljet3_device =
+HPJET_DEVICE(prn_hp_procs, "ljet3",
+ DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
+ X_DPI, Y_DPI,
+ 0.20, 0.25, 0.25, 0.25, /* margins */
+ 1, ljet3_print_page_copies);
+
+const gx_device_hpjet gs_ljet3d_device =
+HPJET_DEVICE(prn_hp_procs, "ljet3d",
+ DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
+ X_DPI, Y_DPI,
+ 0.20, 0.25, 0.25, 0.25, /* margins */
+ 1, ljet3d_print_page_copies);
+
+const gx_device_hpjet gs_ljet4_device =
+HPJET_DEVICE(prn_hp_procs, "ljet4",
+ DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
+ X_DPI2, Y_DPI2,
+ 0, 0, 0, 0, /* margins */
+ 1, ljet4_print_page_copies);
+
+const gx_device_hpjet gs_ljet4d_device =
+HPJET_DEVICE(prn_hp_procs, "ljet4d",
+ DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
+ X_DPI2, Y_DPI2,
+ 0, 0, 0, 0, /* margins */
+ 1, ljet4d_print_page_copies);
+
+const gx_device_hpjet gs_lp2563_device =
+HPJET_DEVICE(prn_hp_procs, "lp2563",
+ DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
+ X_DPI, Y_DPI,
+ 0, 0, 0, 0, /* margins */
+ 1, lp2563_print_page_copies);
+
+const gx_device_hpjet gs_oce9050_device =
+HPJET_DEVICE(prn_hp_procs, "oce9050",
+ 24 * 10, 24 * 10, /* 24 inch roll (can print 32" also) */
+ 400, 400, /* 400 dpi */
+ 0, 0, 0, 0, /* margins */
+ 1, oce9050_print_page_copies);
+
+const gx_device_printer gs_ljet4pjl_device =
+prn_device_copies(prn_ljet4pjl_procs, "ljet4pjl",
+ DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
+ X_DPI2, Y_DPI2,
+ 0, 0, 0, 0, /* margins */
+ 1, ljet4pjl_print_page_copies);
+
+/* Open the printer, adjusting the margins if necessary. */
+static int
+hpjet_open(gx_device * pdev)
+{ /* Change the margins if necessary. */
+ gx_device_printer *const ppdev = (gx_device_printer *)pdev;
+ const float *m = 0;
+ bool move_origin = true;
+
+ if (ppdev->printer_procs.print_page_copies == djet_print_page_copies ||
+ ppdev->printer_procs.print_page_copies == djet500_print_page_copies
+ ) {
+ static const float m_a4[4] =
+ {DESKJET_MARGINS_A4};
+ static const float m_letter[4] =
+ {DESKJET_MARGINS_LETTER};
+
+ m = (gdev_pcl_paper_size(pdev) == PAPER_SIZE_A4 ? m_a4 :
+ m_letter);
+ } else if (ppdev->printer_procs.print_page_copies == oce9050_print_page_copies ||
+ ppdev->printer_procs.print_page_copies == lp2563_print_page_copies
+ );
+ else { /* LaserJet */
+ static const float m_a4[4] =
+ {LASERJET_MARGINS_A4};
+ static const float m_letter[4] =
+ {LASERJET_MARGINS_LETTER};
+
+ m = (gdev_pcl_paper_size(pdev) == PAPER_SIZE_A4 ? m_a4 :
+ m_letter);
+ move_origin = false;
+ }
+ if (m != 0)
+ gx_device_set_margins(pdev, m, move_origin);
+ /* If this is a LJIIID, enable Duplex. */
+ if (ppdev->printer_procs.print_page_copies == ljet3d_print_page_copies)
+ ppdev->Duplex = true, ppdev->Duplex_set = 0;
+ if (ppdev->printer_procs.print_page_copies == ljet4d_print_page_copies)
+ ppdev->Duplex = true, ppdev->Duplex_set = 0;
+ return gdev_prn_open(pdev);
+}
+
+/* hpjet_close is only here to eject odd numbered pages in duplex mode, */
+/* and to reset the printer so the ink cartridge doesn't clog up. */
+static int
+hpjet_close(gx_device * pdev)
+{
+ gx_device_printer *const ppdev = (gx_device_printer *)pdev;
+ int code = gdev_prn_open_printer(pdev, 1);
+
+ if (code < 0)
+ return code;
+ if (ppdev->PageCount > 0) {
+ if (ppdev->Duplex_set >= 0 && ppdev->Duplex)
+ fputs("\033&l0H", ppdev->file);
+
+ fputs("\033E", ppdev->file);
+ }
+
+ return gdev_prn_close(pdev);
+}
+
+static int
+ljet4pjl_close(gx_device *pdev)
+{
+ gx_device_printer *const ppdev = (gx_device_printer *)pdev;
+ int code = gdev_prn_open_printer(pdev, 1);
+
+ if (code < 0)
+ return code;
+ if ( ppdev->Duplex_set >= 0 && ppdev->Duplex ) {
+ gdev_prn_open_printer(pdev, 1);
+ fputs("\033&l0H", ppdev->file) ;
+ }
+ fputs("\033%-12345X", ppdev->file);
+ return gdev_prn_close(pdev);
+}
+
+/* ------ Internal routines ------ */
+
+/* Make an init string that contains paper tray selection. The resulting
+ init string is stored in buf, so make sure that buf is at least 5
+ bytes larger than str. */
+static void
+hpjet_make_init(gx_device_printer *pdev, char *buf, const char *str)
+{
+ gx_device_hpjet *dev = (gx_device_hpjet *)pdev;
+ int paper_source = -1;
+
+ if (dev->ManualFeed_set && dev->ManualFeed) paper_source = 2;
+ else if (dev->MediaPosition_set && dev->MediaPosition >= 0)
+ paper_source = dev->MediaPosition;
+ if (paper_source >= 0)
+ gs_sprintf(buf, "%s\033&l%dH", str, paper_source);
+ else
+ gs_sprintf(buf, "%s", str);
+}
+
+/* The DeskJet can compress (mode 2) */
+static int
+djet_print_page_copies(gx_device_printer * pdev, FILE * prn_stream,
+ int num_copies)
+{
+ char init[80];
+
+ hpjet_make_init(pdev, init, "\033&k1W\033*b2M");
+ return dljet_mono_print_page_copies(pdev, prn_stream, num_copies,
+ 300, PCL_DJ_FEATURES, init, init, false);
+}
+/* The DeskJet500 can compress (modes 2&3) */
+static int
+djet500_print_page_copies(gx_device_printer * pdev, FILE * prn_stream,
+ int num_copies)
+{
+ char init[80];
+
+ hpjet_make_init(pdev, init, "\033&k1W");
+ return dljet_mono_print_page_copies(pdev, prn_stream, num_copies,
+ 300, PCL_DJ500_FEATURES, init, init, false);
+}
+/* The Kyocera FS-600 laser printer (and perhaps other printers */
+/* which use the PeerlessPrint5 firmware) doesn't handle */
+/* ESC&l#u and ESC&l#Z correctly. */
+static int
+fs600_print_page_copies(gx_device_printer * pdev, FILE * prn_stream,
+ int num_copies)
+{
+ int dots_per_inch = (int)pdev->y_pixels_per_inch;
+ char base_init[60];
+ char init[80];
+
+ gs_sprintf(base_init, "\033*r0F\033&u%dD", dots_per_inch);
+ hpjet_make_init(pdev, init, base_init);
+ return dljet_mono_print_page_copies(pdev, prn_stream, num_copies,
+ dots_per_inch, PCL_FS600_FEATURES,
+ init, init, false);
+}
+/* The LaserJet series II can't compress */
+static int
+ljet_print_page_copies(gx_device_printer * pdev, FILE * prn_stream,
+ int num_copies)
+{
+ char init[80];
+
+ hpjet_make_init(pdev, init, "\033*b0M");
+ return dljet_mono_print_page_copies(pdev, prn_stream, num_copies,
+ 300, PCL_LJ_FEATURES, init, init, false);
+}
+/* The LaserJet Plus can't compress */
+static int
+ljetplus_print_page_copies(gx_device_printer * pdev, FILE * prn_stream,
+ int num_copies)
+{
+ char init[80];
+
+ hpjet_make_init(pdev, init, "\033*b0M");
+ return dljet_mono_print_page_copies(pdev, prn_stream, num_copies,
+ 300, PCL_LJplus_FEATURES, init, init, false);
+}
+/* LaserJet series IIp & IId compress (mode 2) */
+/* but don't support *p+ or *b vertical spacing. */
+static int
+ljet2p_print_page_copies(gx_device_printer * pdev, FILE * prn_stream,
+ int num_copies)
+{
+ char init[80];
+
+ hpjet_make_init(pdev, init, "\033*r0F\033*b2M");
+ return dljet_mono_print_page_copies(pdev, prn_stream, num_copies,
+ 300, PCL_LJ2p_FEATURES, init, init, false);
+}
+/* All LaserJet series IIIs (III,IIId,IIIp,IIIsi) compress (modes 2&3) */
+/* They also need their coordinate system translated slightly. */
+static int
+ljet3_print_page_copies(gx_device_printer * pdev, FILE * prn_stream,
+ int num_copies)
+{
+ char init[80];
+
+ hpjet_make_init(pdev, init, "\033&l-180u36Z\033*r0F");
+ return dljet_mono_print_page_copies(pdev, prn_stream, num_copies,
+ 300, PCL_LJ3_FEATURES, init, init, false);
+}
+/* LaserJet IIId is same as LaserJet III, except for duplex */
+static int
+ljet3d_print_page_copies(gx_device_printer * pdev, FILE * prn_stream,
+ int num_copies)
+{
+ char init[80];
+ char even_init[80];
+
+ gx_device_hpjet *dev = (gx_device_hpjet *)pdev;
+ bool tumble=dev->Tumble;
+
+ hpjet_make_init(pdev, init, "\033&l-180u36Z\033*r0F");
+ gs_sprintf(even_init, "\033&l180u36Z\033*r0F");
+ return dljet_mono_print_page_copies(pdev, prn_stream, num_copies,
+ 300, PCL_LJ3D_FEATURES, init, even_init, tumble);
+}
+/* LaserJet 4 series compresses, and it needs a special sequence to */
+/* allow it to specify coordinates at 600 dpi. */
+/* It too needs its coordinate system translated slightly. */
+static int
+ljet4_print_page_copies(gx_device_printer * pdev, FILE * prn_stream,
+ int num_copies)
+{
+ int dots_per_inch = (int)pdev->y_pixels_per_inch;
+ char base_init[60];
+ char init[80];
+
+ gs_sprintf(base_init, "\033&l-180u36Z\033*r0F\033&u%dD", dots_per_inch);
+ if (gdev_pcl_page_orientation((gx_device *) pdev) == PAGE_ORIENTATION_LANDSCAPE)
+ gs_sprintf(base_init, "\033&l0u140Z\033*r0F\033&u%dD", dots_per_inch);
+ hpjet_make_init(pdev, init, base_init);
+
+ return dljet_mono_print_page_copies(pdev, prn_stream, num_copies,
+ dots_per_inch, PCL_LJ4_FEATURES,
+ init, init, false);
+}
+static int
+ljet4d_print_page_copies(gx_device_printer * pdev, FILE * prn_stream,
+ int num_copies)
+{
+ int dots_per_inch = (int)pdev->y_pixels_per_inch;
+ char base_init[60];
+ char init[80];
+ char even_init[80];
+
+ gx_device_hpjet *dev = (gx_device_hpjet *)pdev;
+ bool tumble=dev->Tumble;
+
+ /* Put out per-page initialization. */
+ /*
+ Modified by karsten@sengebusch.de
+ in duplex mode the sheet is alread in process, so there are some
+ commands which must not be sent to the printer for the 2nd page,
+ as this commands will cause the printer to eject the sheet with
+ only the 1st page printed. This commands are:
+ \033&l%dA (setting paper size)
+ \033&l%dH (setting paper tray)
+ in simplex mode we set this parameters for each page,
+ in duplex mode we set this parameters for each odd page
+ (paper tray is set by "hpjet_make_init")
+ */
+ gs_sprintf(base_init, "\033&l-180u36Z\033*r0F\033&u%dD", dots_per_inch);
+ hpjet_make_init(pdev, init, base_init);
+ gs_sprintf(even_init, "\033&l180u36Z\033*r0F\033&u%dD", dots_per_inch);
+ return dljet_mono_print_page_copies(pdev, prn_stream, num_copies,
+ dots_per_inch, PCL_LJ4D_FEATURES,
+ init,even_init,tumble);
+}
+
+/* LaserJet 4 series compresses, and it needs a special sequence to */
+/* allow it to specify coordinates at 600 dpi. */
+/* It too needs its coordinate system translated slightly. */
+static int
+ljet4pjl_print_page_copies(gx_device_printer *pdev, FILE *prn_stream,
+ int num_copies)
+{ int dots_per_inch = (int)pdev->y_pixels_per_inch;
+ char real_init[60];
+
+ gs_sprintf(real_init, "\033&l-180u36Z\033*r0F\033&u%dD", dots_per_inch);
+ return dljet_mono_print_page_copies(pdev, prn_stream, num_copies,
+ dots_per_inch, PCL_LJ4PJL_FEATURES,
+ real_init, real_init, false);
+}
+
+/* The 2563B line printer can't compress */
+/* and doesn't support *p+ or *b vertical spacing. */
+static int
+lp2563_print_page_copies(gx_device_printer * pdev, FILE * prn_stream,
+ int num_copies)
+{
+ char init[80];
+
+ hpjet_make_init(pdev, init, "\033*b0M");
+ return dljet_mono_print_page_copies(pdev, prn_stream, num_copies,
+ 300, PCL_LP2563B_FEATURES, init, init, false);
+}
+/* The Oce line printer has TIFF compression */
+/* and doesn't support *p+ or *b vertical spacing. */
+static int
+oce9050_print_page_copies(gx_device_printer * pdev, FILE * prn_stream,
+ int num_copies)
+{
+ int code;
+ char init[80];
+
+ /* Switch to HP_RTL. */
+ fputs("\033%1B", prn_stream); /* Enter HPGL/2 mode */
+ fputs("BP", prn_stream); /* Begin Plot */
+ fputs("IN;", prn_stream); /* Initialize (start plot) */
+ fputs("\033%1A", prn_stream); /* Enter PCL mode */
+
+ hpjet_make_init(pdev, init, "\033*b0M");
+
+ code = dljet_mono_print_page_copies(pdev, prn_stream, num_copies,
+ 400, PCL_OCE9050_FEATURES, init, init, false);
+
+ /* Return to HPGL/2 mode. */
+ fputs("\033%1B", prn_stream); /* Enter HPGL/2 mode */
+ if (code == 0) {
+ fputs("PU", prn_stream); /* Pen Up */
+ fputs("SP0", prn_stream); /* Pen Select */
+ fputs("PG;", prn_stream); /* Advance Full Page */
+ fputs("\033E", prn_stream); /* Reset */
+ }
+ return code;
+}
+
+static int
+hpjet_get_params(gx_device *pdev, gs_param_list *plist)
+{
+ gx_device_hpjet *dev = (gx_device_hpjet *)pdev;
+ int code = gdev_prn_get_params(pdev, plist);
+
+ if (code >= 0)
+ {
+ code = param_write_bool(plist, "ManualFeed", &dev->ManualFeed);
+ }
+ if (code >= 0)
+ {
+ code = param_write_int(plist, "MediaPosition", &dev->MediaPosition);
+ }
+ if (code >=0)
+ code = param_write_bool(plist, "Tumble", &dev->Tumble);
+ return code;
+}
+
+static int
+hpjet_put_params(gx_device *pdev, gs_param_list *plist)
+{
+ gx_device_hpjet *dev = (gx_device_hpjet *)pdev;
+ int code;
+ bool ManualFeed;
+ bool ManualFeed_set = false;
+ int MediaPosition;
+ bool MediaPosition_set = false;
+ bool Tumble;
+
+ code = param_read_bool(plist, "ManualFeed", &ManualFeed);
+ if (code == 0) ManualFeed_set = true;
+ if (code >= 0) {
+ code = param_read_int(plist, "MediaPosition", &MediaPosition);
+ if (code == 0) MediaPosition_set = true;
+ else if (code < 0) {
+ if (param_read_null(plist, "MediaPosition") == 0) {
+ code = 0;
+ }
+ }
+ }
+ if (code>=0)
+ {
+ code=param_read_bool(plist,"Tumble",&Tumble);
+ if (code != 0) Tumble = false; /* default: no tumble */
+ }
+
+ if (code >= 0)
+ code = gdev_prn_put_params(pdev, plist);
+
+ if (code >= 0) {
+ dev->Tumble=Tumble;
+ if (ManualFeed_set) {
+ dev->ManualFeed = ManualFeed;
+ dev->ManualFeed_set = true;
+ }
+ if (MediaPosition_set) {
+ dev->MediaPosition = MediaPosition;
+ dev->MediaPosition_set = true;
+ }
+ }
+
+ return code;
+}
diff --git a/devices/gdevdjtc.c b/devices/gdevdjtc.c
new file mode 100644
index 000000000..a94672fa9
--- /dev/null
+++ b/devices/gdevdjtc.c
@@ -0,0 +1,274 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+/* HP DeskJet 500C driver */
+#include "gdevprn.h"
+#include "gdevpcl.h"
+#include "malloc_.h"
+
+/***
+ *** Note: this driver was contributed by a user, Alfred Kayser:
+ *** please contact AKayser@et.tudelft.nl if you have questions.
+ ***/
+
+#ifndef SHINGLING /* Interlaced, multi-pass printing */
+#define SHINGLING 1 /* 0 = none, 1 = 50%, 2 = 25%, 2 is best & slowest */
+#endif
+
+#ifndef DEPLETION /* 'Intelligent' dot-removal */
+#define DEPLETION 1 /* 0 = none, 1 = 25%, 2 = 50%, 1 best for graphics? */
+#endif /* Use 0 for transparencies */
+
+#define X_DPI 300
+#define Y_DPI 300
+/* bytes per line for DeskJet Color */
+#define LINE_SIZE ((X_DPI * 85 / 10 + 63) / 64 * 8)
+
+/* The device descriptors */
+static dev_proc_print_page(djet500c_print_page);
+
+/* Since the print_page doesn't alter the device, this device can print in the background */
+static gx_device_procs djet500c_procs =
+ prn_color_procs(gdev_prn_open, gdev_prn_bg_output_page, gdev_prn_close,
+ gdev_pcl_3bit_map_rgb_color, gdev_pcl_3bit_map_color_rgb);
+
+const gx_device_printer far_data gs_djet500c_device =
+ prn_device(djet500c_procs, "djet500c",
+ 85, /* width_10ths, 8.5" */
+ 120, /* height_10ths, 12" */
+ X_DPI, Y_DPI,
+ 0.25, 0.25, 0.25, 0.25, /* margins */
+ 3, djet500c_print_page);
+
+/* Forward references */
+static int djet500c_print_page(gx_device_printer *, FILE *);
+
+static int mode2compress(byte *row, byte *end_row, byte *compressed);
+
+/* The DeskJet 500C uses additive colors in separate planes. */
+/* We only keep one bit of color, with 1 = R, 2 = G, 4 = B. */
+/* Because the buffering routines assume 0 = white, */
+/* we complement all the color components. */
+
+/* Send the page to the printer. For speed, compress each scan line, */
+/* since computer-to-printer communication time is often a bottleneck. */
+/* The DeskJet Color can compress (mode 2) */
+
+static int
+djet500c_print_page(gx_device_printer *pdev, FILE *fprn)
+{
+ byte *bitData=NULL;
+ byte *plane1=NULL;
+ byte *plane2=NULL;
+ byte *plane3=NULL;
+ int bitSize=0;
+ int planeSize=0;
+
+ /* select the most compressed mode available & clear tmp storage */
+ /* put printer in known state */
+ fputs("\033E",fprn);
+
+ /* ends raster graphics to set raster graphics resolution */
+ fputs("\033*rbC", fprn); /* was \033*rB */
+
+ /* set raster graphics resolution -- 300 dpi */
+ fputs("\033*t300R", fprn);
+
+ /* A4, skip perf, def. paper tray */
+ fputs("\033&l26a0l1H", fprn);
+
+ /* RGB Mode */
+ fputs("\033*r3U", fprn);
+
+ /* set depletion level */
+ fprintf(fprn, "\033*o%dD", DEPLETION);
+
+ /* set shingling level */
+ fprintf(fprn, "\033*o%dQ", SHINGLING);
+
+ /* move to top left of page & set current position */
+ fputs("\033*p0x0Y", fprn); /* cursor pos: 0,0 */
+
+ fputs("\033*b2M", fprn); /* mode 2 compression for now */
+
+ fputs("\033*r0A", fprn); /* start graf. left */
+
+ /* Send each scan line in turn */
+ { int lnum;
+ int num_blank_lines = 0;
+ int lineSize = gdev_mem_bytes_per_scan_line((gx_device *)pdev);
+ if (lineSize>bitSize)
+ {
+ if (bitData) free(bitData);
+ bitSize=lineSize;
+ bitData=(byte*)malloc(bitSize+16);
+ }
+ for (lnum=0; lnum<pdev->height; lnum++)
+ {
+ byte *endData;
+
+ gdev_prn_copy_scan_lines(pdev, lnum, bitData, lineSize);
+
+ /* Identify and skip blank lines */
+ endData = bitData + lineSize;
+ while ( (endData>bitData) && (endData[-1] == 0) )
+ endData--;
+ if (endData == bitData)
+ num_blank_lines++;
+ else
+ {
+ int count, k, i, lineLen;
+ endData = bitData + lineSize;
+
+ /* Pad with 0s to fill out the last */
+ /* block of 8 bytes. */
+ memset(endData, 0, 7);
+
+ lineLen=((endData-bitData)+7)/8; /* Round to next 8multiple */
+ if (planeSize<lineLen)
+ {
+ if (plane1) free(plane1);
+ if (plane2) free(plane2);
+ if (plane3) free(plane3);
+ planeSize=lineLen;
+ plane1=(byte*)malloc(planeSize+8);
+ plane2=(byte*)malloc(planeSize+8);
+ plane3=(byte*)malloc(planeSize+8);
+ }
+ /* Transpose the data to get pixel planes. */
+ for (k=i=0; k<lineLen; i+=8, k++)
+ {
+ register ushort t, c;
+
+ /* Three smaller loops are better optimizable and use less
+ vars, so most of them can be in registers even on pc's */
+ for (c=t=0;t<8;t++)
+ c = (c<<1) | (bitData[t+i]&4);
+ plane3[k] = ~(byte)(c>>2);
+ for (c=t=0;t<8;t++)
+ c = (c<<1) | (bitData[t+i]&2);
+ plane2[k] = ~(byte)(c>>1);
+ for (c=t=0;t<8;t++)
+ c = (c<<1) | (bitData[t+i]&1);
+ plane1[k] = ~(byte)(c);
+ }
+
+ /* Skip blank lines if any */
+ if (num_blank_lines > 0)
+ { /* move down from current position */
+ fprintf(fprn, "\033*b%dY", num_blank_lines);
+ num_blank_lines = 0;
+ }
+
+ /* Transfer raster graphics */
+ /* in the order R, G, B. */
+ /* lineLen is at least bitSize/8, so bitData can easily be used to store
+ lineLen of bytes */
+ /* P.s. mode9 compression is akward(??) to use, because the lineLenght's
+ are different, so we are stuck with mode 2, which is good enough */
+
+ /* set the line width */
+ fprintf(fprn, "\033*r%dS", lineLen*8);
+
+ count = mode2compress(plane1, plane1 + lineLen, bitData);
+ fprintf(fprn, "\033*b%dV", count);
+ fwrite(bitData, sizeof(byte), count, fprn);
+ count = mode2compress(plane2, plane2 + lineLen, bitData);
+ fprintf(fprn, "\033*b%dV", count);
+ fwrite(bitData, sizeof(byte), count, fprn);
+ count = mode2compress(plane3, plane3 + lineLen, bitData);
+ fprintf(fprn, "\033*b%dW", count);
+ fwrite(bitData, sizeof(byte), count, fprn);
+ }
+ }
+ }
+ /* end raster graphics */
+ fputs("\033*rbC", fprn); /* was \033*rB */
+ fputs("\033*r1U", fprn); /* back to 1 plane */
+
+ /* put printer in known state */
+ fputs("\033E",fprn);
+
+ /* eject page */
+ fputs("\033&l0H", fprn);
+
+ /* release allocated memory */
+ if (bitData) free(bitData);
+ if (plane1) free(plane1);
+ if (plane2) free(plane2);
+ if (plane3) free(plane3);
+
+ return 0;
+}
+
+/*
+ * Mode 2 Row compression routine for the HP DeskJet & LaserJet IIp.
+ * Compresses data from row up to end_row, storing the result
+ * starting at compressed. Returns the number of bytes stored.
+ * Runs of K<=127 literal bytes are encoded as K-1 followed by
+ * the bytes; runs of 2<=K<=127 identical bytes are encoded as
+ * 257-K followed by the byte.
+ * In the worst case, the result is N+(N/127)+1 bytes long,
+ * where N is the original byte count (end_row - row).
+ * I can't use the general pcl version, because it assume even linelength's
+ */
+
+static int
+mode2compress(byte *row, byte *end_row, byte *compressed)
+{
+ register byte *exam; /* word being examined in the row to compress */
+ register byte *cptr = compressed; /* output pointer into compressed bytes */
+ int i, count, len;
+ byte test;
+
+ exam = row;
+ while (1)
+ {
+ test = *exam++;
+ /* Advance exam until test==*exam or exam==end_row */
+ while ((test != *exam) && (exam < end_row))
+ test = *exam++;
+ /* row points to start of differing bytes,
+ exam points to start of consequtive series
+ or to end of row */
+ if (exam<end_row) exam--;
+ len=exam-row;
+ while (len>0)
+ {
+ count=len;
+ if (count>127) count=127;
+ *cptr++=count-1;
+ for (i=0;i<count;i++) *cptr++ = *row++;
+ len-=count;
+ }
+ if (exam>=end_row) break; /* done */
+ exam++; /* skip first same byte */
+ while ((test == *exam) && (exam < end_row)) /* skip all same bytes */
+ exam++;
+ /* exam points now first different word or to end of data */
+ len = exam-row;
+ while (len>0)
+ {
+ count=len;
+ if (count>127) count=127;
+ *cptr++=(257-count);
+ *cptr++=test;
+ len-=count;
+ }
+ if (exam>=end_row) break; /* end of data */
+ row = exam; /* row points to first dissimular byte */
+ }
+ return (cptr-compressed);
+}
diff --git a/devices/gdevdljm.c b/devices/gdevdljm.c
new file mode 100644
index 000000000..093f90d1a
--- /dev/null
+++ b/devices/gdevdljm.c
@@ -0,0 +1,329 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+/* Generic monochrome H-P DeskJet/LaserJet driver */
+#include "gdevprn.h"
+#include "gdevdljm.h"
+
+/*
+ * Thanks for various improvements to:
+ * Jim Mayer (mayer@wrc.xerox.com)
+ * Jan-Mark Wams (jms@cs.vu.nl)
+ * Frans van Hoesel (hoesel@chem.rug.nl)
+ * George Cameron (g.cameron@biomed.abdn.ac.uk)
+ * Nick Duffek (nsd@bbc.com)
+ * Thanks for the FS-600 driver to:
+ * Peter Schildmann (peter.schildmann@etechnik.uni-rostock.de)
+ * Thanks for the LJIIID duplex capability to:
+ * PDP (Philip) Brown (phil@3soft-uk.com)
+ * Thanks for the OCE 9050 driver to:
+ * William Bader (wbader@EECS.Lehigh.Edu)
+ * Thanks for the LJ4D duplex capability to:
+ * Les Johnson <les@infolabs.com>
+ */
+
+/* See gdevdljm.h for the definitions of the PCL_ features. */
+
+/* The number of blank lines that make it worthwhile to reposition */
+/* the cursor. */
+#define MIN_SKIP_LINES 7
+
+/* We round up the LINE_SIZE to a multiple of a ulong for faster scanning. */
+#define W sizeof(word)
+
+/* Send a page to the printer. */
+int
+dljet_mono_print_page(gx_device_printer * pdev, FILE * prn_stream,
+ int dots_per_inch, int features, const char *page_init)
+{
+ return dljet_mono_print_page_copies(pdev, prn_stream, 1, dots_per_inch,
+ features, page_init, page_init, false);
+}
+int
+dljet_mono_print_page_copies(gx_device_printer * pdev, FILE * prn_stream,
+ int num_copies, int dots_per_inch, int features,
+ const char *odd_page_init, const char *even_page_init, bool tumble)
+{
+ int line_size = gdev_mem_bytes_per_scan_line((gx_device *) pdev);
+ int line_size_words = (line_size + W - 1) / W;
+ uint storage_size_words = line_size_words * 8; /* data, out_row, out_row_alt, prev_row */
+ word *storage;
+ word
+ *data_words,
+ *out_row_words,
+ *out_row_alt_words,
+ *prev_row_words;
+#define data ((byte *)data_words)
+#define out_row ((byte *)out_row_words)
+#define out_row_alt ((byte *)out_row_alt_words)
+#define prev_row ((byte *)prev_row_words)
+ byte *out_data;
+ int x_dpi = (int)pdev->x_pixels_per_inch;
+ int y_dpi = (int)pdev->y_pixels_per_inch;
+ int y_dots_per_pixel = dots_per_inch / y_dpi;
+ int num_rows = dev_print_scan_lines(pdev);
+
+ int out_count;
+ int compression = -1;
+ static const char *const from2to3 = "\033*b3M";
+ static const char *const from3to2 = "\033*b2M";
+ int penalty_from2to3 = strlen(from2to3);
+ int penalty_from3to2 = strlen(from3to2);
+ int paper_size = gdev_pcl_paper_size((gx_device *) pdev);
+ int page_orientation = gdev_pcl_page_orientation((gx_device *) pdev);
+ int code = 0;
+ bool dup = pdev->Duplex;
+ bool dupset = pdev->Duplex_set >= 0;
+
+ if (num_copies != 1 && !(features & PCL_CAN_PRINT_COPIES))
+ return gx_default_print_page_copies(pdev, prn_stream, num_copies);
+ storage =
+ (ulong *)gs_alloc_byte_array(pdev->memory, storage_size_words, W,
+ "hpjet_print_page");
+ if (storage == 0) /* can't allocate working area */
+ return_error(gs_error_VMerror);
+ data_words = storage;
+ out_row_words = data_words + (line_size_words * 2);
+ out_row_alt_words = out_row_words + (line_size_words * 2);
+ prev_row_words = out_row_alt_words + (line_size_words * 2);
+ /* Clear temp storage */
+ memset(data, 0, storage_size_words * W);
+
+ /* Initialize printer. */
+ if (pdev->PageCount == 0) {
+ if (features & HACK__IS_A_LJET4PJL) {
+ fputs("\033%-12345X@PJL\r\n@PJL ENTER LANGUAGE = PCL\r\n",
+ prn_stream);
+ }
+ fputs("\033E", prn_stream); /* reset printer */
+ /* If the printer supports it, set the paper size */
+ /* based on the actual requested size. */
+ fprintf(prn_stream, "\033&l%dO", page_orientation);
+ if (features & PCL_CAN_SET_PAPER_SIZE) {
+ fprintf(prn_stream, "\033&l%dA", paper_size);
+ }
+ /* If printer can duplex, set duplex mode appropriately. */
+ if (features & PCL_HAS_DUPLEX) {
+ if (dupset && dup && !tumble)
+ fputs("\033&l1S", prn_stream);
+ else if (dupset && dup && tumble)
+ fputs("\033&l2S", prn_stream);
+ else if (dupset && !dup)
+ fputs("\033&l0S", prn_stream);
+ else /* default to duplex for this printer */
+ fputs("\033&l1S", prn_stream);
+ }
+ }
+ /* Put out per-page initialization. */
+ /*
+ Modified by karsten@sengebusch.de
+ in duplex mode the sheet is alread in process, so there are some
+ commands which must not be sent to the printer for the 2nd page,
+ as this commands will cause the printer to eject the sheet with
+ only the 1st page printed. This commands are:
+ \033&l%dA (setting paper size)
+ \033&l%dH (setting paper tray)
+ in simplex mode we set this parameters for each page,
+ in duplex mode we set this parameters for each odd page
+ */
+
+ if ((features & PCL_HAS_DUPLEX) && dupset && dup) {
+ /* We are printing duplex, so change margins as needed */
+ if (( (pdev->PageCount/num_copies)%2)==0) {
+ fprintf(prn_stream, "\033&l%dO", page_orientation);
+ if (features & PCL_CAN_SET_PAPER_SIZE) {
+ fprintf(prn_stream, "\033&l%dA", paper_size);
+ }
+ fputs("\033&l0l0E", prn_stream);
+ fputs(odd_page_init, prn_stream);
+ } else
+ fputs(even_page_init, prn_stream);
+ } else {
+ fprintf(prn_stream, "\033&l%dO", page_orientation);
+ if (features & PCL_CAN_SET_PAPER_SIZE){
+ fprintf(prn_stream, "\033&l%dA", paper_size);
+ }
+ fputs("\033&l0l0E", prn_stream);
+ fputs(odd_page_init, prn_stream);
+ }
+
+ fprintf(prn_stream, "\033&l%dX", num_copies); /* # of copies */
+
+ /* End raster graphics, position cursor at top. */
+ fputs("\033*rB\033*p0x0Y", prn_stream);
+
+ /* The DeskJet and DeskJet Plus reset everything upon */
+ /* receiving \033*rB, so we must reinitialize graphics mode. */
+ if (features & PCL_END_GRAPHICS_DOES_RESET) {
+ fputs(odd_page_init, prn_stream); /* Assume this does the right thing */
+ fprintf(prn_stream, "\033&l%dX", num_copies); /* # of copies */
+ }
+
+ /* Set resolution. */
+ fprintf(prn_stream, "\033*t%dR", x_dpi);
+
+ /* Send each scan line in turn */
+ {
+ int lnum;
+ int num_blank_lines = 0;
+ word rmask = ~(word) 0 << (-pdev->width & (W * 8 - 1));
+
+ /* Transfer raster graphics. */
+ for (lnum = 0; lnum < num_rows; lnum++) {
+ register word *end_data =
+ data_words + line_size_words;
+
+ code = gdev_prn_copy_scan_lines(pdev, lnum,
+ (byte *) data, line_size);
+ if (code < 0)
+ break;
+ /* Mask off 1-bits beyond the line width. */
+ end_data[-1] &= rmask;
+ /* Remove trailing 0s. */
+ while (end_data > data_words && end_data[-1] == 0)
+ end_data--;
+ if (end_data == data_words) { /* Blank line */
+ num_blank_lines++;
+ continue;
+ }
+ /* We've reached a non-blank line. */
+ /* Put out a spacing command if necessary. */
+ if (num_blank_lines == lnum) {
+ /* We're at the top of a page. */
+ if (features & PCL_ANY_SPACING) {
+ if (num_blank_lines > 0)
+ fprintf(prn_stream, "\033*p+%dY",
+ num_blank_lines * y_dots_per_pixel);
+ /* Start raster graphics. */
+ fputs("\033*r1A", prn_stream);
+ } else if (features & PCL_MODE_3_COMPRESSION) {
+ /* Start raster graphics. */
+ fputs("\033*r1A", prn_stream);
+#if 1 /* don't waste paper */
+ if (num_blank_lines > 0)
+ fputs("\033*b0W", prn_stream);
+ num_blank_lines = 0;
+#else
+ for (; num_blank_lines; num_blank_lines--)
+ fputs("\033*b0W", prn_stream);
+#endif
+ } else {
+ /* Start raster graphics. */
+ fputs("\033*r1A", prn_stream);
+ for (; num_blank_lines; num_blank_lines--)
+ fputs("\033*bW", prn_stream);
+ }
+ }
+ /* Skip blank lines if any */
+ else if (num_blank_lines != 0) {
+ /*
+ * Moving down from current position causes head motion
+ * on the DeskJet, so if the number of lines is small,
+ * we're better off printing blanks.
+ */
+ /*
+ * For Canon LBP4i and some others, <ESC>*b<n>Y doesn't
+ * properly clear the seed row if we are in compression mode
+ * 3.
+ */
+ if ((num_blank_lines < MIN_SKIP_LINES && compression != 3) ||
+ !(features & PCL_ANY_SPACING)
+ ) {
+ bool mode_3ns =
+ (features & PCL_MODE_3_COMPRESSION) &&
+ !(features & PCL_ANY_SPACING);
+
+ if (mode_3ns && compression != 2) {
+ /* Switch to mode 2 */
+ fputs(from3to2, prn_stream);
+ compression = 2;
+ }
+ if (features & PCL_MODE_3_COMPRESSION) {
+ /* Must clear the seed row. */
+ fputs("\033*b1Y", prn_stream);
+ num_blank_lines--;
+ }
+ if (mode_3ns) {
+ for (; num_blank_lines; num_blank_lines--)
+ fputs("\033*b0W", prn_stream);
+ } else {
+ for (; num_blank_lines; num_blank_lines--)
+ fputs("\033*bW", prn_stream);
+ }
+ } else if (features & PCL3_SPACING) {
+ fprintf(prn_stream, "\033*p+%dY",
+ num_blank_lines * y_dots_per_pixel);
+ } else {
+ fprintf(prn_stream, "\033*b%dY",
+ num_blank_lines);
+ }
+ /* Clear the seed row (only matters for */
+ /* mode 3 compression). */
+ memset(prev_row, 0, line_size);
+ }
+ num_blank_lines = 0;
+
+ /* Choose the best compression mode */
+ /* for this particular line. */
+ if (features & PCL_MODE_3_COMPRESSION) {
+ /* Compression modes 2 and 3 are both */
+ /* available. Try both and see which one */
+ /* produces the least output data. */
+ int count3 = gdev_pcl_mode3compress(line_size, data,
+ prev_row, out_row);
+ int count2 = gdev_pcl_mode2compress(data_words, end_data,
+ out_row_alt);
+ int penalty3 =
+ (compression == 3 ? 0 : penalty_from2to3);
+ int penalty2 =
+ (compression == 2 ? 0 : penalty_from3to2);
+
+ if (count3 + penalty3 < count2 + penalty2) {
+ if (compression != 3)
+ fputs(from2to3, prn_stream);
+ compression = 3;
+ out_data = out_row;
+ out_count = count3;
+ } else {
+ if (compression != 2)
+ fputs(from3to2, prn_stream);
+ compression = 2;
+ out_data = out_row_alt;
+ out_count = count2;
+ }
+ } else if (features & PCL_MODE_2_COMPRESSION) {
+ out_data = out_row;
+ out_count = gdev_pcl_mode2compress(data_words, end_data,
+ out_row);
+ } else {
+ out_data = data;
+ out_count = (byte *) end_data - data;
+ }
+
+ /* Transfer the data */
+ fprintf(prn_stream, "\033*b%dW", out_count);
+ fwrite(out_data, sizeof(byte), out_count,
+ prn_stream);
+ }
+ }
+
+ /* end raster graphics and eject page */
+ fputs("\033*rB\f", prn_stream);
+
+ /* free temporary storage */
+ gs_free_object(pdev->memory, storage, "hpjet_print_page");
+
+ return code;
+}
diff --git a/devices/gdevdljm.h b/devices/gdevdljm.h
new file mode 100644
index 000000000..87325ddee
--- /dev/null
+++ b/devices/gdevdljm.h
@@ -0,0 +1,149 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+/* Interface to generic monochrome H-P DeskJet/LaserJet driver */
+
+#ifndef gdevdljm_INCLUDED
+# define gdevdljm_INCLUDED
+
+#include "gdevpcl.h"
+
+/*
+ * The notion that there is such a thing as a "PCL printer" is a fiction: no
+ * two "PCL" printers, even at the same PCL level, have identical command
+ * sets. (The H-P documentation isn't fully accurate either; for example,
+ * it doesn't reveal that the DeskJet printers implement anything beyond PCL
+ * 3.)
+ *
+ * This file contains feature definitions for a generic monochrome PCL
+ * driver (gdevdljm.c), and the specific feature values for all such
+ * printers that Ghostscript currently supports.
+ */
+
+/* Printer spacing capabilities. Include at most one of these. */
+#define PCL_NO_SPACING 0 /* no vertical spacing capability, must be 0 */
+#define PCL3_SPACING 1 /* <ESC>*p+<n>Y (PCL 3) */
+#define PCL4_SPACING 2 /* <ESC>*b<n>Y (PCL 4) */
+#define PCL5_SPACING 4 /* <ESC>*b<n>Y and clear seed row (PCL 5) */
+/* The following is only used internally. */
+#define PCL_ANY_SPACING\
+ (PCL3_SPACING | PCL4_SPACING | PCL5_SPACING)
+
+/* Individual printer properties. Any subset of these may be included. */
+#define PCL_MODE_2_COMPRESSION 8 /* compression mode 2 supported */
+ /* (PCL 4) */
+#define PCL_MODE_3_COMPRESSION 16 /* compression modes 2 & 3 supported */
+ /* (PCL 5) */
+#define PCL_END_GRAPHICS_DOES_RESET 32 /* <esc>*rB resets all parameters */
+#define PCL_HAS_DUPLEX 64 /* <esc>&l<duplex>S supported */
+#define PCL_CAN_SET_PAPER_SIZE 128 /* <esc>&l<sizecode>A supported */
+#define PCL_CAN_PRINT_COPIES 256 /* <esc>&l<copies>X supported */
+#define HACK__IS_A_LJET4PJL 512
+
+/* Shorthands for the most common spacing/compression combinations. */
+#define PCL_MODE0 PCL3_SPACING
+#define PCL_MODE0NS PCL_NO_SPACING
+#define PCL_MODE2 (PCL4_SPACING | PCL_MODE_2_COMPRESSION)
+#define PCL_MODE2P (PCL_NO_SPACING | PCL_MODE_2_COMPRESSION)
+#define PCL_MODE3 (PCL5_SPACING | PCL_MODE_3_COMPRESSION)
+#define PCL_MODE3NS (PCL_NO_SPACING | PCL_MODE_3_COMPRESSION)
+
+/* Parameters for the printers we know about. */
+
+ /* H-P DeskJet */
+#define PCL_DJ_FEATURES\
+ (PCL_MODE2 |\
+ PCL_END_GRAPHICS_DOES_RESET | PCL_CAN_SET_PAPER_SIZE)
+
+ /* H-P DeskJet 500 */
+#define PCL_DJ500_FEATURES\
+ (PCL_MODE3 |\
+ PCL_END_GRAPHICS_DOES_RESET | PCL_CAN_SET_PAPER_SIZE)
+
+ /* Kyocera FS-600 */
+#define PCL_FS600_FEATURES\
+ (PCL_MODE3 |\
+ PCL_CAN_SET_PAPER_SIZE | PCL_CAN_PRINT_COPIES)
+
+ /* H-P original LaserJet */
+#define PCL_LJ_FEATURES\
+ (PCL_MODE0)
+
+ /* H-P LaserJet Plus */
+#define PCL_LJplus_FEATURES\
+ (PCL_MODE0)
+
+ /* H-P LaserJet IIp, IId */
+#define PCL_LJ2p_FEATURES\
+ (PCL_MODE2P |\
+ PCL_CAN_SET_PAPER_SIZE)
+
+ /* H-P LaserJet III* */
+#define PCL_LJ3_FEATURES\
+ (PCL_MODE3 |\
+ PCL_CAN_SET_PAPER_SIZE | PCL_CAN_PRINT_COPIES)
+
+ /* H-P LaserJet IIId */
+#define PCL_LJ3D_FEATURES\
+ (PCL_MODE3 |\
+ PCL_HAS_DUPLEX | PCL_CAN_SET_PAPER_SIZE | PCL_CAN_PRINT_COPIES)
+
+ /* H-P LaserJet 4 */
+#define PCL_LJ4_FEATURES\
+ (PCL_MODE3 |\
+ PCL_CAN_SET_PAPER_SIZE | PCL_CAN_PRINT_COPIES)
+
+ /* H-P LaserJet 4 PL */
+#define PCL_LJ4PJL_FEATURES\
+ (PCL_MODE3 |\
+ PCL_CAN_SET_PAPER_SIZE | PCL_CAN_PRINT_COPIES | HACK__IS_A_LJET4PJL)
+
+ /* H-P LaserJet 4d */
+#define PCL_LJ4D_FEATURES\
+ (PCL_MODE3 |\
+ PCL_HAS_DUPLEX | PCL_CAN_SET_PAPER_SIZE | PCL_CAN_PRINT_COPIES)
+
+ /* H-P 2563B line printer */
+#define PCL_LP2563B_FEATURES\
+ (PCL_MODE0NS |\
+ PCL_CAN_SET_PAPER_SIZE)
+
+ /* OCE 9050 line printer */
+#define PCL_OCE9050_FEATURES\
+ (PCL_MODE3NS |\
+ PCL_CAN_SET_PAPER_SIZE)
+
+/* ---------------- Procedures ---------------- */
+
+/* Send a page to the printer. */
+int dljet_mono_print_page(
+ gx_device_printer * pdev, /* from device-specific _print_page */
+ FILE * prn_stream, /* ibid. */
+ int dots_per_inch, /* may be a multiple of y resolution */
+ int features, /* as defined above */
+ const char *page_init /* page initialization string */
+ );
+int dljet_mono_print_page_copies(
+ gx_device_printer * pdev,
+ FILE * prn_stream,
+ int num_copies,
+ int dots_per_inch,
+ int features,
+ const char *odd_page_init,
+ const char *even_page_init,
+ bool tumble
+ );
+
+#endif /* gdevdljm_INCLUDED */
diff --git a/devices/gdevdm24.c b/devices/gdevdm24.c
new file mode 100644
index 000000000..34a2011dc
--- /dev/null
+++ b/devices/gdevdm24.c
@@ -0,0 +1,280 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+/* High-res 24Dot-matrix printer driver */
+
+/* Supported printers
+ * NEC P6 and similar, implemented by Andreas Schwab (schwab@ls5.informatik.uni-dortmund.de)
+ * Epson LQ850, implemented by Christian Felsch (felsch@tu-harburg.d400.de)
+ */
+
+#include "gdevprn.h"
+
+/* Driver for NEC P6 */
+static dev_proc_print_page (necp6_print_page);
+const gx_device_printer far_data gs_necp6_device =
+ prn_device (prn_bg_procs, "necp6", /* The print_page proc is compatible with allowing bg printing */
+ DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
+ 360, 360,
+ 0, 0, 0.5, 0, /* margins */
+ 1, necp6_print_page);
+
+/* Driver for Epson LQ850 */
+/* I've tested this driver on a BJ300 with LQ850 emulation and there it produce correct 360x360dpi output. */
+static dev_proc_print_page (lq850_print_page);
+const gx_device_printer gs_lq850_device =
+ prn_device (prn_bg_procs, "lq850", /* The print_page proc is compatible with allowing bg printing */
+ DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
+ 360, 360,
+ 0, 0, 0.5, 0, /* margins */
+ 1, lq850_print_page);
+
+/* ------ Internal routines ------ */
+
+/* Forward references */
+static void dot24_output_run (byte *, int, int, FILE *);
+static void dot24_improve_bitmap (byte *, int);
+
+/* Send the page to the printer. */
+static int
+dot24_print_page (gx_device_printer *pdev, FILE *prn_stream, char *init_string, int init_len)
+{
+ int xres = (int)pdev->x_pixels_per_inch;
+ int yres = (int)pdev->y_pixels_per_inch;
+ int x_high = (xres == 360);
+ int y_high = (yres == 360);
+ int bits_per_column = (y_high ? 48 : 24);
+ uint line_size = gdev_prn_raster (pdev);
+ uint in_size = line_size * bits_per_column;
+ byte *in = (byte *) gs_malloc (pdev->memory, in_size, 1, "dot24_print_page (in)");
+ uint out_size = ((pdev->width + 7) & -8) * 3;
+ byte *out = (byte *) gs_malloc (pdev->memory, out_size, 1, "dot24_print_page (out)");
+ int y_passes = (y_high ? 2 : 1);
+ int dots_per_space = xres / 10; /* pica space = 1/10" */
+ int bytes_per_space = dots_per_space * 3;
+ int skip = 0, lnum = 0, ypass;
+
+ /* Check allocations */
+ if (in == 0 || out == 0)
+ {
+ if (out)
+ gs_free (pdev->memory, (char *) out, out_size, 1, "dot24_print_page (out)");
+ if (in)
+ gs_free (pdev->memory, (char *) in, in_size, 1, "dot24_print_page (in)");
+ return_error (gs_error_VMerror);
+ }
+
+ /* Initialize the printer and reset the margins. */
+ fwrite (init_string, init_len - 1, sizeof (char), prn_stream);
+ fputc ((int) (pdev->width / pdev->x_pixels_per_inch * 10) + 2,
+ prn_stream);
+
+ /* Print lines of graphics */
+ while (lnum < pdev->height)
+ {
+ byte *inp;
+ byte *in_end;
+ byte *out_end;
+ byte *out_blk;
+ register byte *outp;
+ int lcnt;
+
+ /* Copy 1 scan line and test for all zero. */
+ gdev_prn_copy_scan_lines (pdev, lnum, in, line_size);
+ if (in[0] == 0
+ && !memcmp ((char *) in, (char *) in + 1, line_size - 1))
+ {
+ lnum++;
+ skip += 2 - y_high;
+ continue;
+ }
+
+ /* Vertical tab to the appropriate position. */
+ while ((skip >> 1) > 255)
+ {
+ fputs ("\033J\377", prn_stream);
+ skip -= 255 * 2;
+ }
+
+ if (skip)
+ {
+ if (skip >> 1)
+ fprintf (prn_stream, "\033J%c", skip >> 1);
+ if (skip & 1)
+ fputc ('\n', prn_stream);
+ }
+
+ /* Copy the rest of the scan lines. */
+ if (y_high)
+ {
+ inp = in + line_size;
+ for (lcnt = 1; lcnt < 24; lcnt++, inp += line_size)
+ if (!gdev_prn_copy_scan_lines (pdev, lnum + lcnt * 2, inp,
+ line_size))
+ {
+ memset (inp, 0, (24 - lcnt) * line_size);
+ break;
+ }
+ inp = in + line_size * 24;
+ for (lcnt = 0; lcnt < 24; lcnt++, inp += line_size)
+ if (!gdev_prn_copy_scan_lines (pdev, lnum + lcnt * 2 + 1, inp,
+ line_size))
+ {
+ memset (inp, 0, (24 - lcnt) * line_size);
+ break;
+ }
+ }
+ else
+ {
+ lcnt = 1 + gdev_prn_copy_scan_lines (pdev, lnum + 1, in + line_size,
+ in_size - line_size);
+ if (lcnt < 24)
+ /* Pad with lines of zeros. */
+ memset (in + lcnt * line_size, 0, in_size - lcnt * line_size);
+ }
+
+ for (ypass = 0; ypass < y_passes; ypass++)
+ {
+ out_end = out;
+ inp = in;
+ if (ypass)
+ inp += line_size * 24;
+ in_end = inp + line_size;
+
+ for (; inp < in_end; inp++, out_end += 24)
+ {
+ memflip8x8 (inp, line_size, out_end, 3);
+ memflip8x8 (inp + line_size * 8, line_size, out_end + 1, 3);
+ memflip8x8 (inp + line_size * 16, line_size, out_end + 2, 3);
+ }
+ /* Remove trailing 0s. */
+ while (out_end - 3 >= out && out_end[-1] == 0
+ && out_end[-2] == 0 && out_end[-3] == 0)
+ out_end -= 3;
+
+ for (out_blk = outp = out; outp < out_end;)
+ {
+ /* Skip a run of leading 0s. */
+ /* At least 10 are needed to make tabbing worth it. */
+
+ if (outp[0] == 0 && outp + 12 <= out_end
+ && outp[1] == 0 && outp[2] == 0
+ && outp[3] == 0 && outp[4] == 0 && outp[5] == 0
+ && outp[6] == 0 && outp[7] == 0 && outp[8] == 0
+ && outp[9] == 0 && outp[10] == 0 && outp[11] == 0)
+ {
+ byte *zp = outp;
+ int tpos;
+ byte *newp;
+ outp += 12;
+ while (outp + 3 <= out_end
+ && outp[0] == 0 && outp[1] == 0 && outp[2] == 0)
+ outp += 3;
+ tpos = (outp - out) / bytes_per_space;
+ newp = out + tpos * bytes_per_space;
+ if (newp > zp + 10)
+ {
+ /* Output preceding bit data. */
+ /* only false at beginning of line */
+ if (zp > out_blk)
+ {
+ if (x_high)
+ dot24_improve_bitmap (out_blk, (int) (zp - out_blk));
+ dot24_output_run (out_blk, (int) (zp - out_blk),
+ x_high, prn_stream);
+ }
+ /* Tab over to the appropriate position. */
+ fprintf (prn_stream, "\033D%c%c\t", tpos, 0);
+ out_blk = outp = newp;
+ }
+ }
+ else
+ outp += 3;
+ }
+ if (outp > out_blk)
+ {
+ if (x_high)
+ dot24_improve_bitmap (out_blk, (int) (outp - out_blk));
+ dot24_output_run (out_blk, (int) (outp - out_blk), x_high,
+ prn_stream);
+ }
+
+ fputc ('\r', prn_stream);
+ if (ypass < y_passes - 1)
+ fputc ('\n', prn_stream);
+ }
+ skip = 48 - y_high;
+ lnum += bits_per_column;
+ }
+
+ /* Eject the page and reinitialize the printer */
+ fputs ("\f\033@", prn_stream);
+ fflush (prn_stream);
+
+ gs_free (pdev->memory, (char *) out, out_size, 1, "dot24_print_page (out)");
+ gs_free (pdev->memory, (char *) in, in_size, 1, "dot24_print_page (in)");
+
+ return 0;
+}
+
+/* Output a single graphics command. */
+static void
+dot24_output_run (byte *data, int count, int x_high, FILE *prn_stream)
+{
+ int xcount = count / 3;
+ fputc (033, prn_stream);
+ fputc ('*', prn_stream);
+ fputc ((x_high ? 40 : 39), prn_stream);
+ fputc (xcount & 0xff, prn_stream);
+ fputc (xcount >> 8, prn_stream);
+ fwrite (data, 1, count, prn_stream);
+}
+
+/* If xdpi == 360, the P6 / LQ850 cannot print adjacent pixels. Clear the
+ second last pixel of every run of set pixels, so that the last pixel
+ is always printed. */
+static void
+dot24_improve_bitmap (byte *data, int count)
+{
+ int i;
+ register byte *p = data + 6;
+
+ for (i = 6; i < count; i += 3, p += 3)
+ {
+ p[-6] &= ~(~p[0] & p[-3]);
+ p[-5] &= ~(~p[1] & p[-2]);
+ p[-4] &= ~(~p[2] & p[-1]);
+ }
+ p[-6] &= ~p[-3];
+ p[-5] &= ~p[-2];
+ p[-4] &= ~p[-1];
+
+}
+
+static int
+necp6_print_page(gx_device_printer *pdev, FILE *prn_stream)
+{
+ char necp6_init_string [] = "\033@\033P\033l\000\r\034\063\001\033Q";
+
+ return dot24_print_page(pdev, prn_stream, necp6_init_string, sizeof(necp6_init_string));
+}
+
+static int
+lq850_print_page(gx_device_printer *pdev, FILE *prn_stream)
+{
+ char lq850_init_string [] = "\033@\033P\033l\000\r\033\053\001\033Q";
+
+ return dot24_print_page(pdev, prn_stream, lq850_init_string, sizeof(lq850_init_string));
+}
diff --git a/devices/gdevdsp.c b/devices/gdevdsp.c
new file mode 100644
index 000000000..6925f0913
--- /dev/null
+++ b/devices/gdevdsp.c
@@ -0,0 +1,1914 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+/* gdevdsp.c */
+
+/*
+ * DLL based display device driver.
+ *
+ * by Russell Lang, Ghostgum Software Pty Ltd
+ *
+ * This device is intended to be used for displays when
+ * Ghostscript is loaded as a DLL/shared library/static library.
+ * It is intended to work for Windows, OS/2, Linux, Mac OS 9 and
+ * hopefully others.
+ *
+ * Before this device is opened, the address of a structure must
+ * be provided using gsapi_set_display_callback(minst, callback);
+ * This structure contains callback functions to notify the
+ * caller when the device is opened, closed, resized, showpage etc.
+ * The structure is defined in gdevdsp.h.
+ *
+ * Not all combinations of display formats have been tested.
+ * At the end of this file is some example code showing which
+ * formats have been tested.
+ */
+
+#include "string_.h"
+#include "gx.h"
+#include "gserrors.h"
+#include "gsdevice.h" /* for gs_copydevice */
+#include "gxdevice.h"
+
+#include "gp.h"
+#include "gpcheck.h"
+#include "gsparam.h"
+
+#include "gdevpccm.h" /* 4-bit PC color */
+#include "gxdevmem.h"
+#include "gdevdevn.h"
+#include "gsequivc.h"
+#include "gdevdsp.h"
+#include "gdevdsp2.h"
+
+#include "gdevkrnlsclass.h" /* 'standard' built in subclasses, currently First/Last Page and obejct filter */
+
+/* Initial values for width and height */
+#define INITIAL_RESOLUTION 96
+#define INITIAL_WIDTH ((INITIAL_RESOLUTION * 85 + 5) / 10)
+#define INITIAL_HEIGHT ((INITIAL_RESOLUTION * 110 + 5) / 10)
+
+/* Device procedures */
+
+/* See gxdevice.h for the definitions of the procedures. */
+static dev_proc_open_device(display_open);
+static dev_proc_get_initial_matrix(display_get_initial_matrix);
+static dev_proc_sync_output(display_sync_output);
+static dev_proc_output_page(display_output_page);
+static dev_proc_close_device(display_close);
+
+static dev_proc_map_rgb_color(display_map_rgb_color_device4);
+static dev_proc_map_color_rgb(display_map_color_rgb_device4);
+static dev_proc_encode_color(display_encode_color_device8);
+static dev_proc_decode_color(display_decode_color_device8);
+static dev_proc_map_rgb_color(display_map_rgb_color_device16);
+static dev_proc_map_color_rgb(display_map_color_rgb_device16);
+static dev_proc_map_rgb_color(display_map_rgb_color_rgb);
+static dev_proc_map_color_rgb(display_map_color_rgb_rgb);
+static dev_proc_map_rgb_color(display_map_rgb_color_bgr24);
+static dev_proc_map_color_rgb(display_map_color_rgb_bgr24);
+
+static dev_proc_fill_rectangle(display_fill_rectangle);
+static dev_proc_copy_mono(display_copy_mono);
+static dev_proc_copy_color(display_copy_color);
+static dev_proc_get_bits(display_get_bits);
+static dev_proc_get_params(display_get_params);
+static dev_proc_put_params(display_put_params);
+static dev_proc_finish_copydevice(display_finish_copydevice);
+
+static dev_proc_get_color_mapping_procs(display_separation_get_color_mapping_procs);
+static dev_proc_get_color_comp_index(display_separation_get_color_comp_index);
+static dev_proc_encode_color(display_separation_encode_color);
+static dev_proc_decode_color(display_separation_decode_color);
+static dev_proc_update_spot_equivalent_colors(display_update_spot_equivalent_colors);
+static dev_proc_ret_devn_params(display_ret_devn_params);
+
+static const gx_device_procs display_procs =
+{
+ display_open,
+ display_get_initial_matrix,
+ display_sync_output,
+ display_output_page,
+ display_close,
+ gx_default_w_b_map_rgb_color,
+ gx_default_w_b_map_color_rgb,
+ display_fill_rectangle,
+ NULL, /* tile rectangle */
+ display_copy_mono,
+ display_copy_color,
+ NULL, /* draw line */
+ display_get_bits,
+ display_get_params,
+ display_put_params,
+ gx_default_cmyk_map_cmyk_color, /* map_cmyk_color */
+ gx_default_get_xfont_procs,
+ NULL, /* get_xfont_device */
+ NULL, /* map_rgb_alpha_color */
+ gx_page_device_get_page_device,
+ /* extra entries */
+ NULL, /* get_alpha_bits */
+ NULL, /* copy_alpha */
+ NULL, /* get_band */
+ NULL, /* copy_rop */
+ NULL, /* fill_path */
+ NULL, /* stroke_path */
+ NULL, /* fill_mask */
+ NULL, /* fill_trapezoid */
+ NULL, /* fill_parallelogram */
+ NULL, /* fill_triangle */
+ NULL, /* draw_thin_line */
+ NULL, /* begin_image */
+ NULL, /* image_data */
+ NULL, /* end_image */
+ NULL, /* strip_tile_rectangle */
+ NULL, /* strip_copy_rop */
+ NULL, /* get_clipping_box */
+ NULL, /* begin_typed_image */
+ NULL, /* get_bits_rectangle */
+ NULL, /* map_color_rgb_alpha */
+ NULL, /* create_compositor */
+ NULL, /* get_hardware_params */
+ NULL, /* text_begin */
+ display_finish_copydevice, /* finish_copydevice */
+ NULL, /* begin_transparency_group */
+ NULL, /* end_transparency_group */
+ NULL, /* begin_transparency_mask */
+ NULL, /* end_transparency_mask */
+ NULL, /* discard_transparency_layer */
+ NULL, /* get_color_mapping_procs */
+ NULL, /* get_color_comp_index */
+ NULL, /* encode_color */
+ NULL, /* decode_color */
+ NULL, /* pattern_manage */
+ NULL, /* fill_rectangle_hl_color */\
+ NULL, /* include_color_space */\
+ NULL, /* fill_linear_color_scanline */\
+ NULL, /* fill_linear_color_trapezoid */\
+ NULL, /* fill_linear_color_triangle */\
+ display_update_spot_equivalent_colors, /* update_spot_equivalent_colors */
+ display_ret_devn_params /* ret_devn_params */\
+};
+
+/* GC descriptor */
+public_st_device_display();
+
+static
+ENUM_PTRS_WITH(display_enum_ptrs, gx_device_display *ddev)
+ if (index == 0) {
+ if (ddev->mdev) {
+ return ENUM_OBJ(gx_device_enum_ptr((gx_device *)ddev->mdev));
+ }
+ return 0;
+ }
+ else if (index-1 < ddev->devn_params.separations.num_separations)
+ ENUM_RETURN(ddev->devn_params.separations.names[index-1].data);
+ else
+ return 0;
+ENUM_PTRS_END
+
+static
+RELOC_PTRS_WITH(display_reloc_ptrs, gx_device_display *ddev)
+ if (ddev->mdev) {
+ ddev->mdev = (gx_device_memory *)
+ gx_device_reloc_ptr((gx_device *)ddev->mdev, gcst);
+ }
+ { int i;
+ for (i = 0; i < ddev->devn_params.separations.num_separations; ++i) {
+ RELOC_PTR(gx_device_display, devn_params.separations.names[i].data);
+ }
+ }
+RELOC_PTRS_END
+
+const gx_device_display gs_display_device =
+{
+ std_device_std_body_type(gx_device_display, &display_procs, "display",
+ &st_device_display,
+ INITIAL_WIDTH, INITIAL_HEIGHT,
+ INITIAL_RESOLUTION, INITIAL_RESOLUTION),
+ {0}, /* std_procs */
+ NULL, /* mdev */
+ NULL, /* callback */
+ NULL, /* pHandle */
+ 0, /* nFormat */
+ NULL, /* pBitmap */
+ 0, /* ulBitmapSize */
+ 0, /* HWResolution_set */
+
+ { /* devn_params specific parameters */
+ 8, /* Bits per color - must match ncomp, depth, etc. */
+ DeviceCMYKComponents, /* Names of color model colorants */
+ 4, /* Number of colorants for CMYK */
+ 0, /* MaxSeparations has not been specified */
+ -1, /* PageSpotColors has not been specified */
+ {0}, /* SeparationNames */
+ 0, /* Number of SeparationOrder names */
+ {0, 1, 2, 3, 4, 5, 6, 7 } /* Initial component SeparationOrder */
+ },
+ { true } /* equivalent CMYK colors for spot colors */
+};
+
+/* prototypes for internal procedures */
+static int display_check_structure(gx_device_display *dev);
+static void display_free_bitmap(gx_device_display * dev);
+static int display_alloc_bitmap(gx_device_display *, gx_device *);
+static int display_set_color_format(gx_device_display *dev, int nFormat);
+static int display_set_separations(gx_device_display *dev);
+static int display_raster(gx_device_display *dev);
+
+/* Open the display driver. */
+static int
+display_open(gx_device * dev)
+{
+ gx_device_display *ddev = (gx_device_display *) dev;
+ int ccode;
+
+ /* Erase these, in case we are opening a copied device. */
+ ddev->mdev = NULL;
+ ddev->pBitmap = NULL;
+ ddev->ulBitmapSize = 0;
+
+ /* Allow device to be opened "disabled" without a callback. */
+ /* The callback will be set later and the device re-opened. */
+ if (ddev->callback == NULL)
+ return 0;
+ ccode = install_internal_subclass_devices((gx_device **)&ddev, NULL);
+ if (ccode < 0)
+ return ccode;
+ dev = ddev;
+
+ /* Make sure we have been passed a valid callback structure. */
+ if ((ccode = display_check_structure(ddev)) < 0)
+ return_error(ccode);
+
+ /* set color info */
+ if ((ccode = display_set_color_format(ddev, ddev->nFormat)) < 0)
+ return_error(ccode);
+
+ /* Tell caller that the device is open. */
+ /* This is always the first callback */
+ ccode = (*(ddev->callback->display_open))(ddev->pHandle, dev);
+ if (ccode < 0)
+ return_error(ccode);
+
+ /* Tell caller the proposed device parameters */
+ ccode = (*(ddev->callback->display_presize)) (ddev->pHandle, dev,
+ dev->width, dev->height, display_raster(ddev), ddev->nFormat);
+ if (ccode < 0) {
+ (*(ddev->callback->display_close))(ddev->pHandle, dev);
+ return_error(ccode);
+ }
+
+ /* allocate the image */
+ ccode = display_alloc_bitmap(ddev, dev);
+ if (ccode < 0) {
+ (*(ddev->callback->display_close))(ddev->pHandle, dev);
+ return_error(ccode);
+ }
+
+ /* Tell caller the device parameters */
+ ccode = (*(ddev->callback->display_size)) (ddev->pHandle, dev,
+ dev->width, dev->height, display_raster(ddev), ddev->nFormat,
+ ddev->mdev->base);
+ if (ccode < 0) {
+ display_free_bitmap(ddev);
+ (*(ddev->callback->display_close))(ddev->pHandle, dev);
+ return_error(ccode);
+ }
+
+ return 0;
+}
+
+static void
+display_get_initial_matrix(gx_device * dev, gs_matrix * pmat)
+{
+ gx_device_display *ddev = (gx_device_display *) dev;
+ if ((ddev->nFormat & DISPLAY_FIRSTROW_MASK) == DISPLAY_TOPFIRST)
+ gx_default_get_initial_matrix(dev, pmat);
+ else
+ gx_upright_get_initial_matrix(dev, pmat); /* Windows / OS/2 */
+}
+
+/* Update the display. */
+int
+display_sync_output(gx_device * dev)
+{
+ gx_device_display *ddev = (gx_device_display *) dev;
+ if (ddev->callback == NULL)
+ return 0;
+ display_set_separations(ddev);
+
+ (*(ddev->callback->display_sync))(ddev->pHandle, dev);
+ return (0);
+}
+
+/* Update the display, bring to foreground. */
+/* If you want to pause on showpage, delay your return from callback */
+int
+display_output_page(gx_device * dev, int copies, int flush)
+{
+ gx_device_display *ddev = (gx_device_display *) dev;
+ int code;
+ if (ddev->callback == NULL)
+ return 0;
+ display_set_separations(ddev);
+
+ code = (*(ddev->callback->display_page))
+ (ddev->pHandle, dev, copies, flush);
+
+ if (code >= 0)
+ code = gx_finish_output_page(dev, copies, flush);
+ return code;
+}
+
+/* Close the display driver */
+static int
+display_close(gx_device * dev)
+{
+ gx_device_display *ddev = (gx_device_display *) dev;
+ if (ddev->callback == NULL)
+ return 0;
+
+ /* Tell caller that device is about to be closed. */
+ (*(ddev->callback->display_preclose))(ddev->pHandle, dev);
+
+ /* Release memory. */
+ display_free_bitmap(ddev);
+
+ /* Tell caller that device is closed. */
+ /* This is always the last callback */
+ (*(ddev->callback->display_close))(ddev->pHandle, dev);
+
+ return 0;
+}
+
+/*
+ * This routine will encode a 1 Black on white color.
+ */
+static gx_color_index
+gx_b_w_gray_encode(gx_device * dev, const gx_color_value cv[])
+{
+ return 1 - (cv[0] >> (gx_color_value_bits - 1));
+}
+
+/* DISPLAY_COLORS_NATIVE, 4bit/pixel */
+/* Map a r-g-b color to a color code */
+static gx_color_index
+display_map_rgb_color_device4(gx_device * dev, const gx_color_value cv[])
+{
+ return pc_4bit_map_rgb_color(dev, cv);
+}
+
+/* Map a color code to r-g-b. */
+static int
+display_map_color_rgb_device4(gx_device * dev, gx_color_index color,
+ gx_color_value prgb[3])
+{
+ pc_4bit_map_color_rgb(dev, color, prgb);
+ return 0;
+}
+
+/* DISPLAY_COLORS_NATIVE, 8bit/pixel */
+/* Map a r-g-b-k color to a color code */
+static gx_color_index
+display_encode_color_device8(gx_device * dev, const gx_color_value cv[])
+{
+ /* palette of 96 colors */
+ /* 0->63 = 00RRGGBB, 64->95 = 010YYYYY */
+ gx_color_value r = cv[0];
+ gx_color_value g = cv[1];
+ gx_color_value b = cv[2];
+ gx_color_value k = cv[3]; /* 0 = black */
+ if ((r == 0) && (g == 0) && (b == 0)) {
+ k = ((k >> (gx_color_value_bits - 6)) + 1) >> 1;
+ if (k > 0x1f)
+ k = 0x1f;
+ return (k + 0x40);
+ }
+ if (k > 0) {
+ /* The RGB->RGBK color mapping shouldn't generate this. */
+ r = ((r+k) > gx_max_color_value) ? gx_max_color_value :
+ (gx_color_value)(r+k);
+ g = ((g+k) > gx_max_color_value) ? gx_max_color_value :
+ (gx_color_value)(g+k);
+ b = ((b+k) > gx_max_color_value) ? gx_max_color_value :
+ (gx_color_value)(b+k);
+ }
+ r = ((r >> (gx_color_value_bits - 3)) + 1) >> 1;
+ if (r > 0x3)
+ r = 0x3;
+ g = ((g >> (gx_color_value_bits - 3)) + 1) >> 1;
+ if (g > 0x3)
+ g = 0x3;
+ b = ((b >> (gx_color_value_bits - 3)) + 1) >> 1;
+ if (b > 0x3)
+ b = 0x3;
+ return (r << 4) + (g << 2) + b;
+}
+
+/* Map a color code to r-g-b-k. */
+static int
+display_decode_color_device8(gx_device * dev, gx_color_index color,
+ gx_color_value prgb[4])
+{
+ gx_color_value one;
+ /* palette of 96 colors */
+ /* 0->63 = 00RRGGBB, 64->95 = 010YYYYY */
+ if (color < 64) {
+ one = (gx_color_value) (gx_max_color_value / 3);
+ prgb[0] = (gx_color_value) (((color >> 4) & 3) * one);
+ prgb[1] = (gx_color_value) (((color >> 2) & 3) * one);
+ prgb[2] = (gx_color_value) (((color) & 3) * one);
+ prgb[3] = 0;
+ }
+ else if (color < 96) {
+ one = (gx_color_value) (gx_max_color_value / 31);
+ prgb[0] = prgb[1] = prgb[2] = 0;
+ prgb[3] = (gx_color_value) ((color & 0x1f) * one);
+ }
+ else {
+ prgb[0] = prgb[1] = prgb[2] = prgb[3] = 0;
+ }
+ return 0;
+}
+
+/* DISPLAY_COLORS_NATIVE, 16bit/pixel */
+/* Map a r-g-b color to a color code */
+static gx_color_index
+display_map_rgb_color_device16(gx_device * dev, const gx_color_value cv[])
+{
+ gx_device_display *ddev = (gx_device_display *) dev;
+ gx_color_value r = cv[0];
+ gx_color_value g = cv[1];
+ gx_color_value b = cv[2];
+ /* FIXME: Simple truncation isn't ideal. Should round really. */
+ if ((ddev->nFormat & DISPLAY_ENDIAN_MASK) == DISPLAY_BIGENDIAN) {
+ if ((ddev->nFormat & DISPLAY_555_MASK) == DISPLAY_NATIVE_555)
+ /* byte0=0RRRRRGG byte1=GGGBBBBB */
+ return ((r >> (gx_color_value_bits - 5)) << 10) +
+ ((g >> (gx_color_value_bits - 5)) << 5) +
+ (b >> (gx_color_value_bits - 5));
+ else
+ /* byte0=RRRRRGGG byte1=GGGBBBBB */
+ return ((r >> (gx_color_value_bits - 5)) << 11) +
+ ((g >> (gx_color_value_bits - 6)) << 5) +
+ (b >> (gx_color_value_bits - 5));
+ }
+
+ if ((ddev->nFormat & DISPLAY_555_MASK) == DISPLAY_NATIVE_555)
+ /* byte0=GGGBBBBB byte1=0RRRRRGG */
+ return ((r >> (gx_color_value_bits - 5)) << 2) +
+ (((g >> (gx_color_value_bits - 5)) & 0x7) << 13) +
+ (((g >> (gx_color_value_bits - 5)) & 0x18) >> 3) +
+ ((b >> (gx_color_value_bits - 5)) << 8);
+
+ /* byte0=GGGBBBBB byte1=RRRRRGGG */
+ return ((r >> (gx_color_value_bits - 5)) << 3) +
+ (((g >> (gx_color_value_bits - 6)) & 0x7) << 13) +
+ (((g >> (gx_color_value_bits - 6)) & 0x38) >> 3) +
+ ((b >> (gx_color_value_bits - 5)) << 8);
+}
+
+/* Map a color code to r-g-b. */
+static int
+display_map_color_rgb_device16(gx_device * dev, gx_color_index color,
+ gx_color_value prgb[3])
+{
+ gx_device_display *ddev = (gx_device_display *) dev;
+ ushort value;
+
+ if ((ddev->nFormat & DISPLAY_ENDIAN_MASK) == DISPLAY_BIGENDIAN) {
+ if ((ddev->nFormat & DISPLAY_555_MASK) == DISPLAY_NATIVE_555) {
+ /* byte0=0RRRRRGG byte1=GGGBBBBB */
+ value = (ushort) (color >> 10);
+ prgb[0] = (gx_color_value)
+ (((value << 11) + (value << 6) + (value << 1) +
+ (value >> 4)) >> (16 - gx_color_value_bits));
+ value = (ushort) ((color >> 5) & 0x1f);
+ prgb[1] = (gx_color_value)
+ (((value << 11) + (value << 6) + (value << 1) +
+ (value >> 4)) >> (16 - gx_color_value_bits));
+ value = (ushort) (color & 0x1f);
+ prgb[2] = (gx_color_value)
+ (((value << 11) + (value << 6) + (value << 1) +
+ (value >> 4)) >> (16 - gx_color_value_bits));
+ }
+ else {
+ /* byte0=RRRRRGGG byte1=GGGBBBBB */
+ value = (ushort) (color >> 11);
+ prgb[0] = ((value << 11) + (value << 6) + (value << 1) +
+ (value >> 4)) >> (16 - gx_color_value_bits);
+ value = (ushort) ((color >> 5) & 0x3f);
+ prgb[1] = (gx_color_value)
+ ((value << 10) + (value << 4) + (value >> 2))
+ >> (16 - gx_color_value_bits);
+ value = (ushort) (color & 0x1f);
+ prgb[2] = (gx_color_value)
+ ((value << 11) + (value << 6) + (value << 1) +
+ (value >> 4)) >> (16 - gx_color_value_bits);
+ }
+ }
+ else {
+ if ((ddev->nFormat & DISPLAY_555_MASK) == DISPLAY_NATIVE_555) {
+ /* byte0=GGGBBBBB byte1=0RRRRRGG */
+ value = (ushort) ((color >> 2) & 0x1f);
+ prgb[0] = (gx_color_value)
+ ((value << 11) + (value << 6) + (value << 1) +
+ (value >> 4)) >> (16 - gx_color_value_bits);
+ value = (ushort)
+ (((color << 3) & 0x18) + ((color >> 13) & 0x7));
+ prgb[1] = (gx_color_value)
+ ((value << 11) + (value << 6) + (value << 1) +
+ (value >> 4)) >> (16 - gx_color_value_bits);
+ value = (ushort) ((color >> 8) & 0x1f);
+ prgb[2] = (gx_color_value)
+ ((value << 11) + (value << 6) + (value << 1) +
+ (value >> 4)) >> (16 - gx_color_value_bits);
+ }
+ else {
+ /* byte0=GGGBBBBB byte1=RRRRRGGG */
+ value = (ushort) ((color >> 3) & 0x1f);
+ prgb[0] = (gx_color_value)
+ (((value << 11) + (value << 6) + (value << 1) +
+ (value >> 4)) >> (16 - gx_color_value_bits));
+ value = (ushort)
+ (((color << 3) & 0x38) + ((color >> 13) & 0x7));
+ prgb[1] = (gx_color_value)
+ (((value << 10) + (value << 4) + (value >> 2))
+ >> (16 - gx_color_value_bits));
+ value = (ushort) ((color >> 8) & 0x1f);
+ prgb[2] = (gx_color_value)
+ (((value << 11) + (value << 6) + (value << 1) +
+ (value >> 4)) >> (16 - gx_color_value_bits));
+ }
+ }
+ return 0;
+}
+
+/* Map a r-g-b color to a color code */
+static gx_color_index
+display_map_rgb_color_rgb(gx_device * dev, const gx_color_value cv[])
+{
+ gx_device_display *ddev = (gx_device_display *) dev;
+ gx_color_value r = cv[0];
+ gx_color_value g = cv[1];
+ gx_color_value b = cv[2];
+ int drop = gx_color_value_bits - 8;
+ gx_color_value red, green, blue;
+
+ red = r >> drop;
+ green = g >> drop;
+ blue = b >> drop;
+
+ switch (ddev->nFormat & DISPLAY_ALPHA_MASK) {
+ case DISPLAY_ALPHA_NONE:
+ if ((ddev->nFormat & DISPLAY_ENDIAN_MASK) == DISPLAY_BIGENDIAN) {
+ gx_color_value rgb[3];
+ rgb[0] = r; rgb[1] = g; rgb[2] = b;
+ return gx_default_rgb_map_rgb_color(dev, rgb); /* RGB */
+ }
+ else
+ return (blue<<16) + (green<<8) + red; /* BGR */
+ case DISPLAY_ALPHA_FIRST:
+ case DISPLAY_UNUSED_FIRST:
+ if ((ddev->nFormat & DISPLAY_ENDIAN_MASK) == DISPLAY_BIGENDIAN)
+ return ((gx_color_index)red<<16) + (green<<8) + blue; /* xRGB */
+ else
+ return ((gx_color_index)blue<<16) + (green<<8) + red; /* xBGR */
+ case DISPLAY_ALPHA_LAST:
+ case DISPLAY_UNUSED_LAST:
+ if ((ddev->nFormat & DISPLAY_ENDIAN_MASK) == DISPLAY_BIGENDIAN)
+ return ((gx_color_index)red<<24) + (green<<16) + (blue<<8); /* RGBx */
+ else
+ return ((gx_color_index)blue<<24) + (green<<16) + (red<<8); /* BGRx */
+ }
+ return 0;
+}
+
+/* Map a color code to r-g-b. */
+static int
+display_map_color_rgb_rgb(gx_device * dev, gx_color_index color,
+ gx_color_value prgb[3])
+{
+ gx_device_display *ddev = (gx_device_display *) dev;
+ uint bits_per_color = 8;
+ uint color_mask;
+
+ color_mask = (1 << bits_per_color) - 1;
+
+ switch (ddev->nFormat & DISPLAY_ALPHA_MASK) {
+ case DISPLAY_ALPHA_NONE:
+ if ((ddev->nFormat & DISPLAY_ENDIAN_MASK) == DISPLAY_BIGENDIAN)
+ return gx_default_rgb_map_color_rgb(dev, color, prgb); /* RGB */
+ else {
+ /* BGR */
+ prgb[0] = (gx_color_value) (((color) & color_mask) *
+ (ulong) gx_max_color_value / color_mask);
+ prgb[1] = (gx_color_value)
+ (((color >> bits_per_color) & color_mask) *
+ (ulong) gx_max_color_value / color_mask);
+ prgb[2] = (gx_color_value)
+ (((color >> 2*bits_per_color) & color_mask) *
+ (ulong) gx_max_color_value / color_mask);
+ }
+ break;
+ case DISPLAY_ALPHA_FIRST:
+ case DISPLAY_UNUSED_FIRST:
+ if ((ddev->nFormat & DISPLAY_ENDIAN_MASK) == DISPLAY_BIGENDIAN) {
+ /* xRGB */
+ prgb[0] = (gx_color_value)
+ (((color >> 2*bits_per_color) & color_mask) *
+ (ulong) gx_max_color_value / color_mask);
+ prgb[1] = (gx_color_value)
+ (((color >> bits_per_color) & color_mask) *
+ (ulong) gx_max_color_value / color_mask);
+ prgb[2] = (gx_color_value) (((color) & color_mask) *
+ (ulong) gx_max_color_value / color_mask);
+ }
+ else {
+ /* xBGR */
+ prgb[0] = (gx_color_value)
+ (((color) & color_mask) *
+ (ulong) gx_max_color_value / color_mask);
+ prgb[1] = (gx_color_value)
+ (((color >> bits_per_color) & color_mask) *
+ (ulong) gx_max_color_value / color_mask);
+ prgb[2] = (gx_color_value)
+ (((color >> 2*bits_per_color) & color_mask) *
+ (ulong) gx_max_color_value / color_mask);
+ }
+ break;
+ case DISPLAY_ALPHA_LAST:
+ case DISPLAY_UNUSED_LAST:
+ if ((ddev->nFormat & DISPLAY_ENDIAN_MASK) == DISPLAY_BIGENDIAN) {
+ /* RGBx */
+ prgb[0] = (gx_color_value)
+ (((color >> 3*bits_per_color) & color_mask) *
+ (ulong) gx_max_color_value / color_mask);
+ prgb[1] = (gx_color_value)
+ (((color >> 2*bits_per_color) & color_mask) *
+ (ulong) gx_max_color_value / color_mask);
+ prgb[2] = (gx_color_value)
+ (((color >> bits_per_color) & color_mask) *
+ (ulong) gx_max_color_value / color_mask);
+ }
+ else {
+ /* BGRx */
+ prgb[0] = (gx_color_value)
+ (((color >> bits_per_color) & color_mask) *
+ (ulong) gx_max_color_value / color_mask);
+ prgb[1] = (gx_color_value)
+ (((color >> 2*bits_per_color) & color_mask) *
+ (ulong) gx_max_color_value / color_mask);
+ prgb[2] = (gx_color_value)
+ (((color >> 3*bits_per_color) & color_mask) *
+ (ulong) gx_max_color_value / color_mask);
+ }
+ }
+ return 0;
+}
+
+/* Map a r-g-b color to a color code */
+static gx_color_index
+display_map_rgb_color_bgr24(gx_device * dev, const gx_color_value cv[])
+{
+ gx_color_value r = cv[0];
+ gx_color_value g = cv[1];
+ gx_color_value b = cv[2];
+ return (gx_color_value_to_byte(b)<<16) +
+ (gx_color_value_to_byte(g)<<8) +
+ gx_color_value_to_byte(r);
+}
+
+/* Map a color code to r-g-b. */
+static int
+display_map_color_rgb_bgr24(gx_device * dev, gx_color_index color,
+ gx_color_value prgb[3])
+{
+ prgb[0] = gx_color_value_from_byte(color & 0xff);
+ prgb[1] = gx_color_value_from_byte((color >> 8) & 0xff);
+ prgb[2] = gx_color_value_from_byte((color >> 16) & 0xff);
+ return 0;
+}
+
+/* Fill a rectangle */
+static int
+display_fill_rectangle(gx_device * dev, int x, int y, int w, int h,
+ gx_color_index color)
+{
+ gx_device_display *ddev = (gx_device_display *) dev;
+ if (ddev->callback == NULL)
+ return 0;
+ dev_proc(ddev->mdev, fill_rectangle)((gx_device *)ddev->mdev,
+ x, y, w, h, color);
+ if (ddev->callback->display_update)
+ (*(ddev->callback->display_update))(ddev->pHandle, dev, x, y, w, h);
+ return 0;
+}
+
+/* Copy a monochrome bitmap */
+static int
+display_copy_mono(gx_device * dev,
+ const byte * base, int sourcex, int raster, gx_bitmap_id id,
+ int x, int y, int w, int h,
+ gx_color_index zero, gx_color_index one)
+{
+ gx_device_display *ddev = (gx_device_display *) dev;
+ if (ddev->callback == NULL)
+ return 0;
+ dev_proc(ddev->mdev, copy_mono)((gx_device *)ddev->mdev,
+ base, sourcex, raster, id, x, y, w, h, zero, one);
+ if (ddev->callback->display_update)
+ (*(ddev->callback->display_update))(ddev->pHandle, dev, x, y, w, h);
+ return 0;
+}
+
+/* Copy a color pixel map */
+static int
+display_copy_color(gx_device * dev,
+ const byte * base, int sourcex, int raster, gx_bitmap_id id,
+ int x, int y, int w, int h)
+{
+ gx_device_display *ddev = (gx_device_display *) dev;
+ if (ddev->callback == NULL)
+ return 0;
+ dev_proc(ddev->mdev, copy_color)((gx_device *)ddev->mdev,
+ base, sourcex, raster, id, x, y, w, h);
+ if (ddev->callback->display_update)
+ (*(ddev->callback->display_update))(ddev->pHandle, dev, x, y, w, h);
+ return 0;
+}
+
+static int
+display_get_bits(gx_device * dev, int y, byte * str, byte ** actual_data)
+{
+ gx_device_display *ddev = (gx_device_display *) dev;
+ if (ddev->callback == NULL)
+ return 0;
+ return dev_proc(ddev->mdev, get_bits)((gx_device *)ddev->mdev,
+ y, str, actual_data);
+}
+
+static int
+display_get_params(gx_device * dev, gs_param_list * plist)
+{
+ gx_device_display *ddev = (gx_device_display *) dev;
+ int code;
+ gs_param_string dhandle;
+ int idx;
+ int val;
+ int i = 0;
+ size_t dptr;
+ char buf[64];
+
+ idx = ((int)sizeof(size_t)) * 8 - 4;
+ buf[i++] = '1';
+ buf[i++] = '6';
+ buf[i++] = '#';
+ dptr = (size_t)(ddev->pHandle);
+ while (idx >= 0) {
+ val = (int)(dptr >> idx) & 0xf;
+ if (val <= 9)
+ buf[i++] = '0' + val;
+ else
+ buf[i++] = 'a' - 10 + val;
+ idx -= 4;
+ }
+ buf[i] = '\0';
+
+ param_string_from_transient_string(dhandle, buf);
+
+ code = gx_default_get_params(dev, plist);
+ (void)(code < 0 ||
+ (code = param_write_string(plist,
+ "DisplayHandle", &dhandle)) < 0 ||
+ (code = param_write_int(plist,
+ "DisplayFormat", &ddev->nFormat)) < 0 ||
+ (code = param_write_float(plist,
+ "DisplayResolution", &ddev->HWResolution[1])) < 0);
+ if (code >= 0 &&
+ (ddev->nFormat & DISPLAY_COLORS_MASK) == DISPLAY_COLORS_SEPARATION)
+ code = devn_get_params(dev, plist, &ddev->devn_params,
+ &ddev->equiv_cmyk_colors);
+ return code;
+}
+
+/* Put parameters. */
+/* The parameters "DisplayHandle" and "DisplayFormat"
+ * can be changed when the device is closed, but not when open.
+ * The device width and height can be changed when open.
+ */
+static int
+display_put_params(gx_device * dev, gs_param_list * plist)
+{
+ gx_device_display *ddev = (gx_device_display *) dev;
+ int ecode = 0, code;
+ bool is_open = dev->is_open;
+ gs_param_float_array hwra;
+ float dispres = 0.0;
+
+ int old_width = dev->width;
+ int old_height = dev->height;
+ int old_format = ddev->nFormat;
+ void *old_handle = ddev->pHandle;
+
+ gs_devn_params *pdevn_params = &ddev->devn_params;
+ equivalent_cmyk_color_params *pequiv_colors = &ddev->equiv_cmyk_colors;
+ /* Save current data in case we have a problem */
+ gs_devn_params saved_devn_params = *pdevn_params;
+ equivalent_cmyk_color_params saved_equiv_colors = *pequiv_colors;
+
+ int format;
+ void *handle;
+ int found_string_handle = 0;
+ gs_param_string dh = { 0 };
+
+ /* Handle extra parameters */
+
+ switch (code = param_read_int(plist, "DisplayFormat", &format)) {
+ case 0:
+ if (dev->is_open) {
+ if (ddev->nFormat != format)
+ ecode = gs_error_rangecheck;
+ else
+ break;
+ }
+ else {
+ code = display_set_color_format(ddev, format);
+ if (code < 0)
+ ecode = code;
+ else
+ break;
+ }
+ goto cfe;
+ default:
+ ecode = code;
+ cfe:param_signal_error(plist, "DisplayFormat", ecode);
+ case 1:
+ break;
+ }
+
+ /* 64-bit systems need to use DisplayHandle as a string */
+ switch (code = param_read_string(plist, "DisplayHandle", &dh)) {
+ case 0:
+ found_string_handle = 1;
+ break;
+ default:
+ if ((code == gs_error_typecheck) && (sizeof(size_t) <= 4)) {
+ /* 32-bit systems can use the older long type */
+ switch (code = param_read_long(plist, "DisplayHandle",
+ (long *)(&handle))) {
+ case 0:
+ if (dev->is_open) {
+ if (ddev->pHandle != handle)
+ ecode = gs_error_rangecheck;
+ else
+ break;
+ }
+ else {
+ ddev->pHandle = handle;
+ break;
+ }
+ goto hdle;
+ default:
+ ecode = code;
+ hdle:param_signal_error(plist, "DisplayHandle", ecode);
+ case 1:
+ break;
+ }
+ break;
+ }
+ ecode = code;
+ param_signal_error(plist, "DisplayHandle", ecode);
+ /* fall through */
+ case 1:
+ dh.data = 0;
+ break;
+ }
+ if (found_string_handle) {
+ /*
+ * Convert from a string to a pointer.
+ * It is assumed that size_t has the same size as a pointer.
+ * Allow formats (1234), (10#1234) or (16#04d2).
+ */
+ size_t ptr = 0;
+ int i;
+ int base = 10;
+ int val;
+ code = 0;
+ for (i=0; i<dh.size; i++) {
+ val = dh.data[i];
+ if ((val >= '0') && (val <= '9'))
+ val = val - '0';
+ else if ((val >= 'A') && (val <= 'F'))
+ val = val - 'A' + 10;
+ else if ((val >= 'a') && (val <= 'f'))
+ val = val - 'a' + 10;
+ else if (val == '#') {
+ base = (int)ptr;
+ ptr = 0;
+ if ((base != 10) && (base != 16)) {
+ code = gs_error_rangecheck;
+ break;
+ }
+ continue;
+ }
+ else {
+ code = gs_error_rangecheck;
+ break;
+ }
+
+ if (base == 10)
+ ptr = ptr * 10 + val;
+ else if (base == 16)
+ ptr = ptr * 16 + val;
+ else {
+ code = gs_error_rangecheck;
+ break;
+ }
+ }
+ if (code == 0) {
+ if (dev->is_open) {
+ if (ddev->pHandle != (void *)ptr)
+ code = gs_error_rangecheck;
+ }
+ else
+ ddev->pHandle = (void *)ptr;
+ }
+ if (code < 0) {
+ ecode = code;
+ param_signal_error(plist, "DisplayHandle", ecode);
+ }
+ }
+
+ /*
+ * Set the initial display resolution.
+ * If HWResolution is explicitly set, e.g. using -rDPI on the
+ * command line, then use that. Otherwise, use DisplayResolution
+ * which is typically set by the client to the display
+ * logical resolution. Once either of these have been
+ * used, ignore all further DisplayResolution parameters.
+ */
+ if (param_read_float_array(plist, "HWResolution", &hwra) == 0)
+ ddev->HWResolution_set = 1;
+
+ switch (code = param_read_float(plist, "DisplayResolution", &dispres)) {
+ case 0:
+ if (!ddev->HWResolution_set) {
+ gx_device_set_resolution(dev, dispres, dispres);
+ ddev->HWResolution_set = 1;
+ }
+ break;
+ default:
+ ecode = code;
+ param_signal_error(plist, "DisplayResolution", ecode);
+ case 1:
+ break;
+ }
+
+ if (ecode >= 0 &&
+ (ddev->nFormat & DISPLAY_COLORS_MASK) == DISPLAY_COLORS_SEPARATION) {
+ /* Use utility routine to handle devn parameters */
+ ecode = devn_put_params(dev, plist, pdevn_params, pequiv_colors);
+ /*
+ * Setting MaxSeparations changes color_info.depth in
+ * devn_put_params, but we always use 64bpp,
+ * so reset it to the the correct value.
+ */
+ dev->color_info.depth = arch_sizeof_color_index * 8;
+ }
+
+ if (ecode >= 0) {
+ /* Prevent gx_default_put_params from closing the device. */
+ dev->is_open = false;
+ ecode = gx_default_put_params(dev, plist);
+ dev->is_open = is_open;
+ }
+ if (ecode < 0) {
+ /* If we have an error then restore original data. */
+ *pdevn_params = saved_devn_params;
+ *pequiv_colors = saved_equiv_colors;
+ if (format != old_format)
+ display_set_color_format(ddev, old_format);
+ ddev->pHandle = old_handle;
+ dev->width = old_width;
+ dev->height = old_height;
+ return ecode;
+ }
+
+ if ( is_open && ddev->callback &&
+ ((old_width != dev->width) || (old_height != dev->height)) ) {
+ /* We can resize this device while it is open, but we cannot
+ * change the color format or handle.
+ */
+ /* Tell caller we are about to change the device parameters */
+ if ((*ddev->callback->display_presize)(ddev->pHandle, dev,
+ dev->width, dev->height, display_raster(ddev),
+ ddev->nFormat) < 0) {
+ /* caller won't let us change the size */
+ /* restore parameters then return an error */
+ *pdevn_params = saved_devn_params;
+ *pequiv_colors = saved_equiv_colors;
+ display_set_color_format(ddev, old_format);
+ ddev->nFormat = old_format;
+ ddev->pHandle = old_handle;
+ dev->width = old_width;
+ dev->height = old_height;
+ return_error(gs_error_rangecheck);
+ }
+
+ display_free_bitmap(ddev);
+
+ code = display_alloc_bitmap(ddev, dev);
+ if (code < 0) {
+ /* No bitmap, so tell the caller it is zero size */
+ (*ddev->callback->display_size)(ddev->pHandle, dev,
+ 0, 0, 0, ddev->nFormat, NULL);
+ return_error(code);
+ }
+
+ /* tell caller about the new size */
+ if ((*ddev->callback->display_size)(ddev->pHandle, dev,
+ dev->width, dev->height, display_raster(ddev),
+ ddev->nFormat, ddev->mdev->base) < 0)
+ return_error(gs_error_rangecheck);
+ }
+
+ return 0;
+}
+
+/* Clean up the instance after making a copy. */
+int
+display_finish_copydevice(gx_device *dev, const gx_device *from_dev)
+{
+ gx_device_display *ddev = (gx_device_display *) dev;
+
+ /* Mark the new instance as closed. */
+ ddev->is_open = false;
+
+ /* Clear pointers */
+ ddev->mdev = NULL;
+ ddev->pBitmap = NULL;
+ ddev->ulBitmapSize = 0;
+
+ return 0;
+}
+
+/*
+ * The following procedures are used to map the standard color spaces into
+ * the separation color components for the display device.
+ */
+static void
+display_separation_gray_cs_to_cmyk_cm(gx_device * dev, frac gray, frac out[])
+{
+ int * map =
+ (int *)(&((gx_device_display *) dev)->devn_params.separation_order_map);
+
+ gray_cs_to_devn_cm(dev, map, gray, out);
+}
+
+static void
+display_separation_rgb_cs_to_cmyk_cm(gx_device * dev,
+ const gs_imager_state *pis, frac r, frac g, frac b, frac out[])
+{
+ int * map =
+ (int *)(&((gx_device_display *) dev)->devn_params.separation_order_map);
+
+ rgb_cs_to_devn_cm(dev, map, pis, r, g, b, out);
+}
+
+static void
+display_separation_cmyk_cs_to_cmyk_cm(gx_device * dev,
+ frac c, frac m, frac y, frac k, frac out[])
+{
+ const int * map =
+ (int *)(&((gx_device_display *) dev)->devn_params.separation_order_map);
+
+ cmyk_cs_to_devn_cm(dev, map, c, m, y, k, out);
+}
+
+static const gx_cm_color_map_procs display_separation_cm_procs = {
+ display_separation_gray_cs_to_cmyk_cm,
+ display_separation_rgb_cs_to_cmyk_cm,
+ display_separation_cmyk_cs_to_cmyk_cm
+};
+
+static const gx_cm_color_map_procs *
+display_separation_get_color_mapping_procs(const gx_device * dev)
+{
+ return &display_separation_cm_procs;
+}
+
+/*
+ * Encode a list of colorant values into a gx_color_index_value.
+ */
+static gx_color_index
+display_separation_encode_color(gx_device *dev, const gx_color_value colors[])
+{
+ int bpc = ((gx_device_display *)dev)->devn_params.bitspercomponent;
+ int drop = sizeof(gx_color_value) * 8 - bpc;
+ gx_color_index color = 0;
+ int i = 0;
+ int ncomp = dev->color_info.num_components;
+ COLROUND_VARS;
+
+ COLROUND_SETUP(bpc);
+ for (; i<ncomp; i++) {
+ color <<= bpc;
+ color |= COLROUND_ROUND(colors[i]);
+ }
+ if (bpc*ncomp < arch_sizeof_color_index * 8)
+ color <<= (arch_sizeof_color_index * 8 - ncomp * bpc);
+ return (color == gx_no_color_index ? color ^ 1 : color);
+}
+
+/*
+ * Decode a gx_color_index value back to a list of colorant values.
+ */
+static int
+display_separation_decode_color(gx_device * dev, gx_color_index color,
+ gx_color_value * out)
+{
+ int bpc = ((gx_device_display *)dev)->devn_params.bitspercomponent;
+ int drop = sizeof(gx_color_value) * 8 - bpc;
+ int mask = (1 << bpc) - 1;
+ int i = 0;
+ int ncomp = dev->color_info.num_components;
+ COLDUP_VARS;
+
+ COLDUP_SETUP(bpc);
+ if (bpc*ncomp < arch_sizeof_color_index * 8)
+ color >>= (arch_sizeof_color_index * 8 - ncomp * bpc);
+ for (; i<ncomp; i++) {
+ out[ncomp - i - 1] = COLDUP_DUP(color & mask);
+ color >>= bpc;
+ }
+ return 0;
+}
+
+/*
+ * Device proc for updating the equivalent CMYK color for spot colors.
+ */
+static int
+display_update_spot_equivalent_colors(gx_device * dev, const gs_state * pgs)
+{
+ gx_device_display * ddev = (gx_device_display *)dev;
+
+ if ((ddev->nFormat & DISPLAY_COLORS_MASK) == DISPLAY_COLORS_SEPARATION)
+ update_spot_equivalent_cmyk_colors(dev, pgs,
+ &ddev->devn_params, &ddev->equiv_cmyk_colors);
+ return 0;
+}
+
+/*
+ * Device proc for returning a pointer to DeviceN parameter structure
+ */
+static gs_devn_params *
+display_ret_devn_params(gx_device * dev)
+{
+ gx_device_display * pdev = (gx_device_display *)dev;
+
+ return &pdev->devn_params;
+}
+
+/*
+ * This routine will check to see if the color component name match those
+ * that are available amoung the current device's color components.
+ *
+ * Parameters:
+ * dev - pointer to device data structure.
+ * pname - pointer to name (zero termination not required)
+ * nlength - length of the name
+ *
+ * This routine returns a positive value (0 to n) which is the device colorant
+ * number if the name is found. It returns GX_DEVICE_COLOR_MAX_COMPONENTS if
+ * the colorant is not being used due to a SeparationOrder device parameter.
+ * It returns a negative value if not found.
+ */
+static int
+display_separation_get_color_comp_index(gx_device * dev,
+ const char * pname, int name_size, int component_type)
+{
+ return devn_get_color_comp_index(dev,
+ &(((gx_device_display *)dev)->devn_params),
+ &(((gx_device_display *)dev)->equiv_cmyk_colors),
+ pname, name_size, component_type, ENABLE_AUTO_SPOT_COLORS);
+}
+
+/* ------ Internal routines ------ */
+
+/* Make sure we have been given a valid structure */
+/* Return 0 on success, gs_error_rangecheck on failure */
+static int display_check_structure(gx_device_display *ddev)
+{
+ if (ddev->callback == 0)
+ return_error(gs_error_rangecheck);
+
+ if (ddev->callback->size == sizeof(struct display_callback_v1_s)) {
+ /* Original V1 structure */
+ if (ddev->callback->version_major != DISPLAY_VERSION_MAJOR_V1)
+ return_error(gs_error_rangecheck);
+
+ /* complain if caller asks for newer features */
+ if (ddev->callback->version_minor > DISPLAY_VERSION_MINOR_V1)
+ return_error(gs_error_rangecheck);
+ }
+ else {
+ /* V2 structure with added display_separation callback */
+ if (ddev->callback->size != sizeof(display_callback))
+ return_error(gs_error_rangecheck);
+
+ if (ddev->callback->version_major != DISPLAY_VERSION_MAJOR)
+ return_error(gs_error_rangecheck);
+
+ /* complain if caller asks for newer features */
+ if (ddev->callback->version_minor > DISPLAY_VERSION_MINOR)
+ return_error(gs_error_rangecheck);
+ }
+
+ if ((ddev->callback->display_open == NULL) ||
+ (ddev->callback->display_close == NULL) ||
+ (ddev->callback->display_presize == NULL) ||
+ (ddev->callback->display_size == NULL) ||
+ (ddev->callback->display_sync == NULL) ||
+ (ddev->callback->display_page == NULL))
+ return_error(gs_error_rangecheck);
+
+ /* Don't test display_update, display_memalloc or display_memfree
+ * since these may be NULL if not provided.
+ * Don't test display_separation, since this may be NULL if
+ * separation format is not supported.
+ */
+
+ return 0;
+}
+
+static void
+display_free_bitmap(gx_device_display * ddev)
+{
+ if (ddev->callback == NULL)
+ return;
+ if (ddev->pBitmap) {
+ if (ddev->callback->display_memalloc
+ && ddev->callback->display_memfree
+ && ddev->pBitmap) {
+ (*ddev->callback->display_memfree)(ddev->pHandle, ddev,
+ ddev->pBitmap);
+ }
+ else {
+ gs_free_object(ddev->memory->non_gc_memory,
+ ddev->pBitmap, "display_free_bitmap");
+ }
+ ddev->pBitmap = NULL;
+ if (ddev->mdev)
+ ddev->mdev->base = NULL;
+ }
+ if (ddev->mdev) {
+ dev_proc(ddev->mdev, close_device)((gx_device *)ddev->mdev);
+ gx_device_retain((gx_device *)(ddev->mdev), false);
+ ddev->mdev = NULL;
+ }
+}
+
+/* calculate byte length of a row */
+static int
+display_raster(gx_device_display *dev)
+{
+ int align = 0;
+ int bytewidth = ((dev->width * dev->color_info.depth) + 7) /8;
+ switch (dev->nFormat & DISPLAY_ROW_ALIGN_MASK) {
+ case DISPLAY_ROW_ALIGN_4:
+ align = 4;
+ break;
+ case DISPLAY_ROW_ALIGN_8:
+ align = 8;
+ break;
+ case DISPLAY_ROW_ALIGN_16:
+ align = 16;
+ break;
+ case DISPLAY_ROW_ALIGN_32:
+ align = 32;
+ break;
+ case DISPLAY_ROW_ALIGN_64:
+ align = 64;
+ break;
+ }
+ if (align < ARCH_ALIGN_PTR_MOD)
+ align = ARCH_ALIGN_PTR_MOD;
+ align -= 1;
+ bytewidth = (bytewidth + align) & (~align);
+ return bytewidth;
+}
+
+/* Allocate the backing bitmap. */
+static int
+display_alloc_bitmap(gx_device_display * ddev, gx_device * param_dev)
+{
+ int ccode;
+ const gx_device_memory *mdproto;
+ if (ddev->callback == NULL)
+ return 0;
+
+ /* free old bitmap (if any) */
+ display_free_bitmap(ddev);
+
+ /* allocate a memory device for rendering */
+ mdproto = gdev_mem_device_for_bits(ddev->color_info.depth);
+ if (mdproto == 0)
+ return_error(gs_error_rangecheck);
+
+ ddev->mdev = gs_alloc_struct(gs_memory_stable(ddev->memory),
+ gx_device_memory, &st_device_memory, "display_memory_device");
+ if (ddev->mdev == 0)
+ return_error(gs_error_VMerror);
+
+ gs_make_mem_device(ddev->mdev, mdproto, gs_memory_stable(ddev->memory),
+ 0, (gx_device *) NULL);
+ check_device_separable((gx_device *)(ddev->mdev));
+ gx_device_fill_in_procs((gx_device *)(ddev->mdev));
+ /* Mark the memory device as retained. When the bitmap is closed,
+ * we will clear this and the memory device will be then be freed.
+ */
+ gx_device_retain((gx_device *)(ddev->mdev), true);
+
+ /* Memory device width may be larger than device width
+ * if row alignment is not 4.
+ */
+ ddev->mdev->width = param_dev->width;
+ ddev->mdev->width = display_raster(ddev) * 8 / ddev->color_info.depth;
+ ddev->mdev->height = param_dev->height;
+
+ /* Tell the memory device to allocate the line pointers separately
+ * so we can place the bitmap in special memory.
+ */
+ ddev->mdev->line_pointer_memory = ddev->mdev->memory;
+ if (gdev_mem_bits_size(ddev->mdev, ddev->mdev->width, ddev->mdev->height,
+ &(ddev->ulBitmapSize)) < 0)
+ return_error(gs_error_VMerror);
+
+ /* allocate bitmap using an allocator not subject to GC */
+ if (ddev->callback->display_memalloc
+ && ddev->callback->display_memfree) {
+ ddev->pBitmap = (*ddev->callback->display_memalloc)(ddev->pHandle,
+ ddev, ddev->ulBitmapSize);
+ }
+ else {
+ ddev->pBitmap = gs_alloc_byte_array_immovable(ddev->memory->non_gc_memory,
+ (uint)ddev->ulBitmapSize, 1, "display_alloc_bitmap");
+ }
+
+ if (ddev->pBitmap == NULL) {
+ ddev->mdev->width = 0;
+ ddev->mdev->height = 0;
+ return_error(gs_error_VMerror);
+ }
+
+ ddev->mdev->base = (byte *) ddev->pBitmap;
+ ddev->mdev->foreign_bits = true;
+
+ ccode = dev_proc(ddev->mdev, open_device)((gx_device *)ddev->mdev);
+ if (ccode < 0)
+ display_free_bitmap(ddev);
+
+ /* erase bitmap - before display gets redrawn */
+ if (ccode == 0) {
+ int i;
+ gx_color_value cv[GX_DEVICE_COLOR_MAX_COMPONENTS];
+ for (i=0; i<GX_DEVICE_COLOR_MAX_COMPONENTS; i++)
+ cv[i] = (ddev->color_info.polarity == GX_CINFO_POLARITY_ADDITIVE)
+ ? gx_max_color_value : 0;
+ dev_proc(ddev, fill_rectangle)((gx_device *)ddev,
+ 0, 0, ddev->width, ddev->height,
+ ddev->procs.encode_color((gx_device *)ddev, cv));
+ }
+
+ return ccode;
+}
+
+static int
+display_set_separations(gx_device_display *dev)
+{
+ if (((dev->nFormat & DISPLAY_COLORS_MASK) == DISPLAY_COLORS_SEPARATION) &&
+ (dev->callback->version_major > DISPLAY_VERSION_MAJOR_V1) &&
+ (dev->callback->display_separation != NULL)) {
+ /* Tell the client about the separation to composite mapping */
+ char name[64];
+ int num_spot = dev->devn_params.separations.num_separations;
+ int num_std_colorants = dev->devn_params.num_std_colorant_names;
+ int num_comp = num_std_colorants + num_spot;
+ int comp_map[GX_DEVICE_COLOR_MAX_COMPONENTS];
+ int comp_num;
+ int sep_num;
+ int sep_name_size;
+ unsigned int c, m, y, k;
+
+ /* Map the separation numbers to component numbers */
+ memset(comp_map, 0, sizeof(comp_map));
+ for (sep_num = 0; sep_num < num_comp; sep_num++) {
+ comp_num = dev->devn_params.separation_order_map[sep_num];
+ if (comp_num >= 0 && comp_num < GX_DEVICE_COLOR_MAX_COMPONENTS)
+ comp_map[comp_num] = sep_num;
+ }
+ /* For each component, tell the client the separation mapping */
+ for (comp_num = 0; comp_num < num_comp; comp_num++) {
+ c = y = m = k = 0;
+ sep_num = comp_map[comp_num];
+ /* Get the CMYK equivalent */
+ if (sep_num < dev->devn_params.num_std_colorant_names) {
+ sep_name_size =
+ strlen(dev->devn_params.std_colorant_names[sep_num]);
+ if (sep_name_size > sizeof(name)-2)
+ sep_name_size = sizeof(name)-1;
+ memcpy(name, dev->devn_params.std_colorant_names[sep_num],
+ sep_name_size);
+ name[sep_name_size] = '\0';
+ switch (sep_num) {
+ case 0: c = 65535; break;
+ case 1: m = 65535; break;
+ case 2: y = 65535; break;
+ case 3: k = 65535; break;
+ }
+ }
+ else {
+ sep_num -= dev->devn_params.num_std_colorant_names;
+ sep_name_size =
+ dev->devn_params.separations.names[sep_num].size;
+ if (sep_name_size > sizeof(name)-2)
+ sep_name_size = sizeof(name)-1;
+ memcpy(name, dev->devn_params.separations.names[sep_num].data,
+ sep_name_size);
+ name[sep_name_size] = '\0';
+ if (dev->equiv_cmyk_colors.color[sep_num].color_info_valid) {
+ c = dev->equiv_cmyk_colors.color[sep_num].c
+ * 65535 / frac_1;
+ m = dev->equiv_cmyk_colors.color[sep_num].m
+ * 65535 / frac_1;
+ y = dev->equiv_cmyk_colors.color[sep_num].y
+ * 65535 / frac_1;
+ k = dev->equiv_cmyk_colors.color[sep_num].k
+ * 65535 / frac_1;
+ }
+ }
+ (*dev->callback->display_separation)(dev->pHandle, dev,
+ comp_num, name,
+ (unsigned short)c, (unsigned short)m,
+ (unsigned short)y, (unsigned short)k);
+ }
+ }
+ return 0;
+}
+
+typedef enum DISPLAY_MODEL_e {
+ DISPLAY_MODEL_GRAY=0,
+ DISPLAY_MODEL_RGB=1,
+ DISPLAY_MODEL_RGBK=2,
+ DISPLAY_MODEL_CMYK=3,
+ DISPLAY_MODEL_SEP=4
+} DISPLAY_MODEL;
+
+/*
+ * This is a utility routine to build the display device's color_info
+ * structure (except for the anti alias info).
+ */
+static void
+set_color_info(gx_device_color_info * pdci, DISPLAY_MODEL model,
+ int nc, int depth, int maxgray, int maxcolor)
+{
+ pdci->num_components = pdci->max_components = nc;
+ pdci->depth = depth;
+ pdci->gray_index = 0;
+ pdci->max_gray = maxgray;
+ pdci->max_color = maxcolor;
+ pdci->dither_grays = maxgray + 1;
+ pdci->dither_colors = maxcolor + 1;
+ pdci->separable_and_linear = GX_CINFO_UNKNOWN_SEP_LIN;
+ switch (model) {
+ case DISPLAY_MODEL_GRAY:
+ pdci->polarity = GX_CINFO_POLARITY_ADDITIVE;
+ pdci->cm_name = "DeviceGray";
+ pdci->gray_index = 0;
+ break;
+ case DISPLAY_MODEL_RGB:
+ pdci->polarity = GX_CINFO_POLARITY_ADDITIVE;
+ pdci->cm_name = "DeviceRGB";
+ pdci->gray_index = GX_CINFO_COMP_NO_INDEX;
+ break;
+ case DISPLAY_MODEL_RGBK:
+ pdci->polarity = GX_CINFO_POLARITY_ADDITIVE;
+ pdci->cm_name = "DeviceRGBK";
+ pdci->gray_index = 3;
+ break;
+ case DISPLAY_MODEL_CMYK:
+ pdci->polarity = GX_CINFO_POLARITY_SUBTRACTIVE;
+ pdci->cm_name = "DeviceCMYK";
+ pdci->gray_index = 3;
+ break;
+ default:
+ case DISPLAY_MODEL_SEP:
+ /* Anything else is separations */
+ pdci->polarity = GX_CINFO_POLARITY_SUBTRACTIVE;
+ pdci->cm_name = "DeviceCMYK";
+ pdci->gray_index = GX_CINFO_COMP_NO_INDEX; /* may not have K */
+ break;
+ }
+}
+
+/*
+ * This is an utility routine to set up the color procs for the display
+ * device. The display device can change its setup.
+ */
+static void
+set_color_procs(gx_device * pdev,
+ dev_t_proc_encode_color((*encode_color), gx_device),
+ dev_t_proc_decode_color((*decode_color), gx_device),
+ dev_t_proc_get_color_mapping_procs((*get_color_mapping_procs), gx_device),
+ dev_t_proc_get_color_comp_index((*get_color_comp_index), gx_device))
+{
+#if 0 /* These procs are no longer used */
+ pdev->procs.map_rgb_color = encode_color;
+ pdev->procs.map_color_rgb = decode_color;
+#endif
+ pdev->procs.get_color_mapping_procs = get_color_mapping_procs;
+ pdev->procs.get_color_comp_index = get_color_comp_index;
+ pdev->procs.encode_color = encode_color;
+ pdev->procs.decode_color = decode_color;
+}
+
+/*
+ * This is an utility routine to set up the color procs for the display
+ * device. This routine is used when the display device is Gray.
+ */
+static void
+set_gray_color_procs(gx_device * pdev,
+ dev_t_proc_encode_color((*encode_color), gx_device),
+ dev_t_proc_decode_color((*decode_color), gx_device))
+{
+ set_color_procs(pdev, encode_color, decode_color,
+ gx_default_DevGray_get_color_mapping_procs,
+ gx_default_DevGray_get_color_comp_index);
+}
+
+/*
+ * This is an utility routine to set up the color procs for the display
+ * device. This routine is used when the display device is RGB.
+ */
+static void
+set_rgb_color_procs(gx_device * pdev,
+ dev_t_proc_encode_color((*encode_color), gx_device),
+ dev_t_proc_decode_color((*decode_color), gx_device))
+{
+ set_color_procs(pdev, encode_color, decode_color,
+ gx_default_DevRGB_get_color_mapping_procs,
+ gx_default_DevRGB_get_color_comp_index);
+}
+
+/*
+ * This is an utility routine to set up the color procs for the display
+ * device. This routine is used when the display device is RGBK.
+ */
+static void
+set_rgbk_color_procs(gx_device * pdev,
+ dev_t_proc_encode_color((*encode_color), gx_device),
+ dev_t_proc_decode_color((*decode_color), gx_device))
+{
+ set_color_procs(pdev, encode_color, decode_color,
+ gx_default_DevRGBK_get_color_mapping_procs,
+ gx_default_DevRGBK_get_color_comp_index);
+}
+
+/*
+ * This is an utility routine to set up the color procs for the display
+ * device. This routine is used when the display device is CMYK.
+ */
+static void
+set_cmyk_color_procs(gx_device * pdev,
+ dev_t_proc_encode_color((*encode_color), gx_device),
+ dev_t_proc_decode_color((*decode_color), gx_device))
+{
+ set_color_procs(pdev, encode_color, decode_color,
+ gx_default_DevCMYK_get_color_mapping_procs,
+ gx_default_DevCMYK_get_color_comp_index);
+}
+
+/* Set the color_info and mapping functions for this instance of the device */
+static int
+display_set_color_format(gx_device_display *ddev, int nFormat)
+{
+ gx_device * pdev = (gx_device *) ddev;
+ gx_device_color_info dci = ddev->color_info;
+ int bpc; /* bits per component */
+ int bpp; /* bits per pixel */
+ int maxvalue;
+ int align;
+
+ switch (nFormat & DISPLAY_DEPTH_MASK) {
+ case DISPLAY_DEPTH_1:
+ bpc = 1;
+ break;
+ case DISPLAY_DEPTH_2:
+ bpc = 2;
+ break;
+ case DISPLAY_DEPTH_4:
+ bpc = 4;
+ break;
+ case DISPLAY_DEPTH_8:
+ bpc = 8;
+ break;
+ case DISPLAY_DEPTH_12:
+ bpc = 12;
+ break;
+ case DISPLAY_DEPTH_16:
+ bpc = 16;
+ break;
+ default:
+ return_error(gs_error_rangecheck);
+ }
+ maxvalue = (1 << bpc) - 1;
+ ddev->devn_params.bitspercomponent = bpc;
+
+ switch (ddev->nFormat & DISPLAY_ROW_ALIGN_MASK) {
+ case DISPLAY_ROW_ALIGN_DEFAULT:
+ align = ARCH_ALIGN_PTR_MOD;
+ break;
+ case DISPLAY_ROW_ALIGN_4:
+ align = 4;
+ break;
+ case DISPLAY_ROW_ALIGN_8:
+ align = 8;
+ break;
+ case DISPLAY_ROW_ALIGN_16:
+ align = 16;
+ break;
+ case DISPLAY_ROW_ALIGN_32:
+ align = 32;
+ break;
+ case DISPLAY_ROW_ALIGN_64:
+ align = 64;
+ break;
+ default:
+ align = 0; /* not permitted */
+ }
+ if (align < ARCH_ALIGN_PTR_MOD)
+ return_error(gs_error_rangecheck);
+
+ switch (ddev->nFormat & DISPLAY_ALPHA_MASK) {
+ case DISPLAY_ALPHA_FIRST:
+ case DISPLAY_ALPHA_LAST:
+ /* Not implemented and unlikely to ever be implemented
+ * because they would interact with linear_and_separable
+ */
+ return_error(gs_error_rangecheck);
+ }
+
+ switch (nFormat & DISPLAY_COLORS_MASK) {
+ case DISPLAY_COLORS_NATIVE:
+ switch (nFormat & DISPLAY_DEPTH_MASK) {
+ case DISPLAY_DEPTH_1:
+ /* 1bit/pixel, black is 1, white is 0 */
+ set_color_info(&dci, DISPLAY_MODEL_GRAY, 1, 1, 1, 0);
+ dci.separable_and_linear = GX_CINFO_SEP_LIN_NONE;
+ set_gray_color_procs(pdev, gx_b_w_gray_encode,
+ gx_default_b_w_map_color_rgb);
+ break;
+ case DISPLAY_DEPTH_4:
+ /* 4bit/pixel VGA color */
+ set_color_info(&dci, DISPLAY_MODEL_RGB, 3, 4, 3, 2);
+ dci.separable_and_linear = GX_CINFO_SEP_LIN_NONE;
+ set_rgb_color_procs(pdev, display_map_rgb_color_device4,
+ display_map_color_rgb_device4);
+ break;
+ case DISPLAY_DEPTH_8:
+ /* 8bit/pixel 96 color palette */
+ set_color_info(&dci, DISPLAY_MODEL_RGBK, 4, 8, 31, 3);
+ dci.separable_and_linear = GX_CINFO_SEP_LIN_NONE;
+ set_rgbk_color_procs(pdev, display_encode_color_device8,
+ display_decode_color_device8);
+ break;
+ case DISPLAY_DEPTH_16:
+ /* Windows 16-bit display */
+ /* Is maxgray = maxcolor = 63 correct? */
+ if ((ddev->nFormat & DISPLAY_555_MASK)
+ == DISPLAY_NATIVE_555)
+ set_color_info(&dci, DISPLAY_MODEL_RGB, 3, 16, 31, 31);
+ else
+ set_color_info(&dci, DISPLAY_MODEL_RGB, 3, 16, 63, 63);
+ set_rgb_color_procs(pdev, display_map_rgb_color_device16,
+ display_map_color_rgb_device16);
+ break;
+ default:
+ return_error(gs_error_rangecheck);
+ }
+ dci.gray_index = GX_CINFO_COMP_NO_INDEX;
+ break;
+ case DISPLAY_COLORS_GRAY:
+ set_color_info(&dci, DISPLAY_MODEL_GRAY, 1, bpc, maxvalue, 0);
+ if (bpc == 1)
+ set_gray_color_procs(pdev, gx_default_gray_encode,
+ gx_default_w_b_map_color_rgb);
+ else
+ set_gray_color_procs(pdev, gx_default_gray_encode,
+ gx_default_gray_map_color_rgb);
+ break;
+ case DISPLAY_COLORS_RGB:
+ if ((nFormat & DISPLAY_ALPHA_MASK) == DISPLAY_ALPHA_NONE)
+ bpp = bpc * 3;
+ else
+ bpp = bpc * 4;
+ set_color_info(&dci, DISPLAY_MODEL_RGB, 3, bpp, maxvalue, maxvalue);
+ if (((nFormat & DISPLAY_DEPTH_MASK) == DISPLAY_DEPTH_8) &&
+ ((nFormat & DISPLAY_ALPHA_MASK) == DISPLAY_ALPHA_NONE)) {
+ if ((nFormat & DISPLAY_ENDIAN_MASK) == DISPLAY_BIGENDIAN)
+ set_rgb_color_procs(pdev, gx_default_rgb_map_rgb_color,
+ gx_default_rgb_map_color_rgb);
+ else
+ set_rgb_color_procs(pdev, display_map_rgb_color_bgr24,
+ display_map_color_rgb_bgr24);
+ }
+ else {
+ /* slower flexible functions for alpha/unused component */
+ set_rgb_color_procs(pdev, display_map_rgb_color_rgb,
+ display_map_color_rgb_rgb);
+ }
+ break;
+ case DISPLAY_COLORS_CMYK:
+ bpp = bpc * 4;
+ set_color_info(&dci, DISPLAY_MODEL_CMYK, 4, bpp, maxvalue, maxvalue);
+ if ((nFormat & DISPLAY_ALPHA_MASK) != DISPLAY_ALPHA_NONE)
+ return_error(gs_error_rangecheck);
+ if ((nFormat & DISPLAY_ENDIAN_MASK) != DISPLAY_BIGENDIAN)
+ return_error(gs_error_rangecheck);
+
+ if ((nFormat & DISPLAY_DEPTH_MASK) == DISPLAY_DEPTH_1)
+ set_cmyk_color_procs(pdev, cmyk_1bit_map_cmyk_color,
+ cmyk_1bit_map_color_cmyk);
+ else if ((nFormat & DISPLAY_DEPTH_MASK) == DISPLAY_DEPTH_8)
+ set_cmyk_color_procs(pdev, cmyk_8bit_map_cmyk_color,
+ cmyk_8bit_map_color_cmyk);
+ else
+ return_error(gs_error_rangecheck);
+ break;
+ case DISPLAY_COLORS_SEPARATION:
+ if ((nFormat & DISPLAY_ENDIAN_MASK) != DISPLAY_BIGENDIAN)
+ return_error(gs_error_rangecheck);
+ bpp = arch_sizeof_color_index * 8;
+ set_color_info(&dci, DISPLAY_MODEL_SEP, bpp/bpc, bpp,
+ maxvalue, maxvalue);
+ if ((nFormat & DISPLAY_DEPTH_MASK) == DISPLAY_DEPTH_8) {
+ ddev->devn_params.bitspercomponent = bpc;
+ set_color_procs(pdev,
+ display_separation_encode_color,
+ display_separation_decode_color,
+ display_separation_get_color_mapping_procs,
+ display_separation_get_color_comp_index);
+ }
+ else
+ return_error(gs_error_rangecheck);
+ break;
+ default:
+ return_error(gs_error_rangecheck);
+ }
+
+ /* restore old anti_alias info */
+ dci.anti_alias = ddev->color_info.anti_alias;
+ ddev->color_info = dci;
+ check_device_separable(pdev);
+ switch (nFormat & DISPLAY_COLORS_MASK) {
+ case DISPLAY_COLORS_NATIVE:
+ ddev->color_info.gray_index = GX_CINFO_COMP_NO_INDEX;
+ if ((nFormat & DISPLAY_DEPTH_MASK) == DISPLAY_DEPTH_1)
+ ddev->color_info.gray_index = 0;
+ else if ((nFormat & DISPLAY_DEPTH_MASK) == DISPLAY_DEPTH_8)
+ ddev->color_info.gray_index = 3;
+ break;
+ case DISPLAY_COLORS_RGB:
+ ddev->color_info.gray_index = GX_CINFO_COMP_NO_INDEX;
+ break;
+ case DISPLAY_COLORS_GRAY:
+ ddev->color_info.gray_index = 0;
+ break;
+ case DISPLAY_COLORS_CMYK:
+ ddev->color_info.gray_index = 3;
+ break;
+ case DISPLAY_COLORS_SEPARATION:
+ ddev->color_info.gray_index = GX_CINFO_COMP_NO_INDEX;
+ break;
+ }
+ ddev->nFormat = nFormat;
+
+ return 0;
+}
+
+/* ------ Begin Test Code ------ */
+
+/*********************************************************************
+typedef struct test_mode_s test_mode;
+struct test_mode_s {
+ char *name;
+ unsigned int format;
+};
+
+test_mode test_modes[] = {
+ {"1bit/pixel native, black is 1, Windows",
+ DISPLAY_COLORS_NATIVE | DISPLAY_ALPHA_NONE | DISPLAY_DEPTH_1 |
+ DISPLAY_LITTLEENDIAN | DISPLAY_BOTTOMFIRST},
+ {"4bit/pixel native, Windows VGA 16 color palette",
+ DISPLAY_COLORS_NATIVE | DISPLAY_ALPHA_NONE | DISPLAY_DEPTH_4 |
+ DISPLAY_LITTLEENDIAN | DISPLAY_BOTTOMFIRST},
+ {"8bit/pixel native, Windows SVGA 96 color palette",
+ DISPLAY_COLORS_NATIVE | DISPLAY_ALPHA_NONE | DISPLAY_DEPTH_8 |
+ DISPLAY_LITTLEENDIAN | DISPLAY_BOTTOMFIRST},
+ {"16bit/pixel native, Windows BGR555",
+ DISPLAY_COLORS_NATIVE | DISPLAY_ALPHA_NONE | DISPLAY_DEPTH_16 |
+ DISPLAY_LITTLEENDIAN | DISPLAY_BOTTOMFIRST | DISPLAY_NATIVE_555},
+ {"16bit/pixel native, Windows BGR565",
+ DISPLAY_COLORS_NATIVE | DISPLAY_ALPHA_NONE | DISPLAY_DEPTH_16 |
+ DISPLAY_LITTLEENDIAN | DISPLAY_BOTTOMFIRST | DISPLAY_NATIVE_565},
+ {"1bit/pixel gray, black is 0, topfirst",
+ DISPLAY_COLORS_GRAY | DISPLAY_ALPHA_NONE | DISPLAY_DEPTH_1 |
+ DISPLAY_BIGENDIAN | DISPLAY_TOPFIRST},
+ {"4bit/pixel gray, bottom first",
+ DISPLAY_COLORS_GRAY | DISPLAY_ALPHA_NONE | DISPLAY_DEPTH_4 |
+ DISPLAY_BIGENDIAN | DISPLAY_BOTTOMFIRST},
+ {"8bit/pixel gray, bottom first",
+ DISPLAY_COLORS_GRAY | DISPLAY_ALPHA_NONE | DISPLAY_DEPTH_8 |
+ DISPLAY_BIGENDIAN | DISPLAY_BOTTOMFIRST},
+ {"24bit/pixel color, bottom first, Windows BGR24",
+ DISPLAY_COLORS_RGB | DISPLAY_ALPHA_NONE | DISPLAY_DEPTH_8 |
+ DISPLAY_LITTLEENDIAN | DISPLAY_BOTTOMFIRST},
+ {"24bit/pixel color, bottom first, RGB24",
+ DISPLAY_COLORS_RGB | DISPLAY_ALPHA_NONE | DISPLAY_DEPTH_8 |
+ DISPLAY_BIGENDIAN | DISPLAY_BOTTOMFIRST},
+ {"24bit/pixel color, top first, GdkRgb RGB24",
+ DISPLAY_COLORS_RGB | DISPLAY_ALPHA_NONE | DISPLAY_DEPTH_8 |
+ DISPLAY_BIGENDIAN | DISPLAY_TOPFIRST},
+ {"32bit/pixel color, top first, Macintosh xRGB",
+ DISPLAY_COLORS_RGB | DISPLAY_UNUSED_FIRST | DISPLAY_DEPTH_8 |
+ DISPLAY_BIGENDIAN | DISPLAY_TOPFIRST},
+ {"32bit/pixel color, bottom first, xBGR",
+ DISPLAY_COLORS_RGB | DISPLAY_UNUSED_FIRST | DISPLAY_DEPTH_8 |
+ DISPLAY_LITTLEENDIAN | DISPLAY_BOTTOMFIRST},
+ {"32bit/pixel color, bottom first, Windows BGRx",
+ DISPLAY_COLORS_RGB | DISPLAY_UNUSED_LAST | DISPLAY_DEPTH_8 |
+ DISPLAY_LITTLEENDIAN | DISPLAY_BOTTOMFIRST},
+ {"32bit/pixel color, bottom first, RGBx",
+ DISPLAY_COLORS_RGB | DISPLAY_UNUSED_LAST | DISPLAY_DEPTH_8 |
+ DISPLAY_BIGENDIAN | DISPLAY_BOTTOMFIRST},
+ {"32bit/pixel CMYK, bottom first",
+ DISPLAY_COLORS_CMYK | DISPLAY_ALPHA_NONE | DISPLAY_DEPTH_8 |
+ DISPLAY_BIGENDIAN | DISPLAY_BOTTOMFIRST},
+ {"64bit/pixel separations, bottom first",
+ DISPLAY_COLORS_SEPARATIONS | DISPLAY_ALPHA_NONE | DISPLAY_DEPTH_8 |
+ DISPLAY_BIGENDIAN | DISPLAY_BOTTOMFIRST},
+ {"4bit/pixel CMYK, bottom first",
+ DISPLAY_COLORS_CMYK | DISPLAY_ALPHA_NONE | DISPLAY_DEPTH_1 |
+ DISPLAY_BIGENDIAN | DISPLAY_BOTTOMFIRST},
+ {"1bit/pixel native, black is 1, 8 byte alignment",
+ DISPLAY_COLORS_NATIVE | DISPLAY_ALPHA_NONE | DISPLAY_DEPTH_1 |
+ DISPLAY_LITTLEENDIAN | DISPLAY_BOTTOMFIRST | DISPLAY_ROW_ALIGN_8},
+ {"24bit/pixel color, bottom first, BGR24, 64 byte alignment",
+ DISPLAY_COLORS_RGB | DISPLAY_ALPHA_NONE | DISPLAY_DEPTH_8 |
+ DISPLAY_LITTLEENDIAN | DISPLAY_BOTTOMFIRST | DISPLAY_ROW_ALIGN_64}
+};
+
+void
+test(int index)
+{
+ char buf[1024];
+ sprintf(buf, "gs -dDisplayFormat=16#%x examples/colorcir.ps -c quit", test_modes[index].format);
+ system(buf);
+}
+
+int main(int argc, char *argv[])
+{
+ int i;
+ int dotest = 0;
+ if (argc >=2) {
+ if (strcmp(argv[1], "-t") == 0)
+ dotest = 1;
+ else {
+ fprintf(stdout, "To show modes: disp\nTo run test: disp -t\n");
+ return 1;
+ }
+ }
+ for (i=0; i < sizeof(test_modes)/sizeof(test_mode); i++) {
+ fprintf(stdout, "16#%x or %d: %s\n", test_modes[i].format,
+ test_modes[i].format, test_modes[i].name);
+ if (dotest)
+ test(i);
+ }
+ return 0;
+}
+*********************************************************************/
+
+/* ------ End Test Code ------ */
diff --git a/devices/gdevdsp.h b/devices/gdevdsp.h
new file mode 100644
index 000000000..268c455ac
--- /dev/null
+++ b/devices/gdevdsp.h
@@ -0,0 +1,265 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+/* gdevdsp.h - callback structure for DLL based display device */
+
+#ifndef gdevdsp_INCLUDED
+# define gdevdsp_INCLUDED
+
+/*
+ * The callback structure must be provided by calling the
+ * Ghostscript APIs in the following order:
+ * gsapi_new_instance(&minst);
+ * gsapi_set_display_callback(minst, callback);
+ * gsapi_init_with_args(minst, argc, argv);
+ *
+ * Supported parameters and default values are:
+ * -sDisplayHandle=16#04d2 or 1234 string
+ * Caller supplied handle as a decimal or hexadecimal number
+ * in a string. On 32-bit platforms, it may be set
+ * using -dDisplayHandle=1234 for backward compatibility.
+ * Included as first parameter of all callback functions.
+ *
+ * -dDisplayFormat=0 long
+ * Color format specified using bitfields below.
+ * Included as argument of display_size() and display_presize()
+ * These can only be changed when the device is closed.
+ *
+ * The second parameter of all callback functions "void *device"
+ * is the address of the Ghostscript display device instance.
+ * The arguments "void *handle" and "void *device" together
+ * uniquely identify an instance of the display device.
+ *
+ * A typical sequence of callbacks would be
+ * open, presize, memalloc, size, sync, page
+ * presize, memfree, memalloc, size, sync, page
+ * preclose, memfree, close
+ * The caller should not access the image buffer:
+ * - before the first sync
+ * - between presize and size
+ * - after preclose
+ * If opening the device fails, you might see the following:
+ * open, presize, memalloc, memfree, close
+ *
+ */
+
+#define DISPLAY_VERSION_MAJOR 2
+#define DISPLAY_VERSION_MINOR 0
+
+#define DISPLAY_VERSION_MAJOR_V1 1 /* before separation format was added */
+#define DISPLAY_VERSION_MINOR_V1 0
+
+/* The display format is set by a combination of the following bitfields */
+
+/* Define the color space alternatives */
+typedef enum {
+ DISPLAY_COLORS_NATIVE = (1<<0),
+ DISPLAY_COLORS_GRAY = (1<<1),
+ DISPLAY_COLORS_RGB = (1<<2),
+ DISPLAY_COLORS_CMYK = (1<<3),
+ DISPLAY_COLORS_SEPARATION = (1<<19)
+} DISPLAY_FORMAT_COLOR;
+#define DISPLAY_COLORS_MASK 0x8000fL
+
+/* Define whether alpha information, or an extra unused bytes is included */
+/* DISPLAY_ALPHA_FIRST and DISPLAY_ALPHA_LAST are not implemented */
+typedef enum {
+ DISPLAY_ALPHA_NONE = (0<<4),
+ DISPLAY_ALPHA_FIRST = (1<<4),
+ DISPLAY_ALPHA_LAST = (1<<5),
+ DISPLAY_UNUSED_FIRST = (1<<6), /* e.g. Mac xRGB */
+ DISPLAY_UNUSED_LAST = (1<<7) /* e.g. Windows BGRx */
+} DISPLAY_FORMAT_ALPHA;
+#define DISPLAY_ALPHA_MASK 0x00f0L
+
+/* Define the depth per component for DISPLAY_COLORS_GRAY,
+ * DISPLAY_COLORS_RGB and DISPLAY_COLORS_CMYK,
+ * or the depth per pixel for DISPLAY_COLORS_NATIVE
+ * DISPLAY_DEPTH_2 and DISPLAY_DEPTH_12 have not been tested.
+ */
+typedef enum {
+ DISPLAY_DEPTH_1 = (1<<8),
+ DISPLAY_DEPTH_2 = (1<<9),
+ DISPLAY_DEPTH_4 = (1<<10),
+ DISPLAY_DEPTH_8 = (1<<11),
+ DISPLAY_DEPTH_12 = (1<<12),
+ DISPLAY_DEPTH_16 = (1<<13)
+ /* unused (1<<14) */
+ /* unused (1<<15) */
+} DISPLAY_FORMAT_DEPTH;
+#define DISPLAY_DEPTH_MASK 0xff00L
+
+/* Define whether Red/Cyan should come first,
+ * or whether Blue/Black should come first
+ */
+typedef enum {
+ DISPLAY_BIGENDIAN = (0<<16), /* Red/Cyan first */
+ DISPLAY_LITTLEENDIAN = (1<<16) /* Blue/Black first */
+} DISPLAY_FORMAT_ENDIAN;
+#define DISPLAY_ENDIAN_MASK 0x00010000L
+
+/* Define whether the raster starts at the top or bottom of the bitmap */
+typedef enum {
+ DISPLAY_TOPFIRST = (0<<17), /* Unix, Mac */
+ DISPLAY_BOTTOMFIRST = (1<<17) /* Windows */
+} DISPLAY_FORMAT_FIRSTROW;
+#define DISPLAY_FIRSTROW_MASK 0x00020000L
+
+/* Define whether packing RGB in 16-bits should use 555
+ * or 565 (extra bit for green)
+ */
+typedef enum {
+ DISPLAY_NATIVE_555 = (0<<18),
+ DISPLAY_NATIVE_565 = (1<<18)
+} DISPLAY_FORMAT_555;
+#define DISPLAY_555_MASK 0x00040000L
+
+/* Define the row alignment, which must be equal to or greater than
+ * the size of a pointer.
+ * The default (DISPLAY_ROW_ALIGN_DEFAULT) is the size of a pointer,
+ * 4 bytes (DISPLAY_ROW_ALIGN_4) on 32-bit systems or 8 bytes
+ * (DISPLAY_ROW_ALIGN_8) on 64-bit systems.
+ */
+typedef enum {
+ DISPLAY_ROW_ALIGN_DEFAULT = (0<<20),
+ /* DISPLAY_ROW_ALIGN_1 = (1<<20), */ /* not currently possible */
+ /* DISPLAY_ROW_ALIGN_2 = (2<<20), */ /* not currently possible */
+ DISPLAY_ROW_ALIGN_4 = (3<<20),
+ DISPLAY_ROW_ALIGN_8 = (4<<20),
+ DISPLAY_ROW_ALIGN_16 = (5<<20),
+ DISPLAY_ROW_ALIGN_32 = (6<<20),
+ DISPLAY_ROW_ALIGN_64 = (7<<20)
+} DISPLAY_FORMAT_ROW_ALIGN;
+#define DISPLAY_ROW_ALIGN_MASK 0x00700000L
+
+#ifndef display_callback_DEFINED
+#define display_callback_DEFINED
+typedef struct display_callback_s display_callback;
+#endif
+
+/*
+ * Note that for Windows, the display callback functions are
+ * cdecl, not stdcall. This differs from those in iapi.h.
+ */
+
+struct display_callback_s {
+ /* Size of this structure */
+ /* Used for checking if we have been handed a valid structure */
+ int size;
+
+ /* Major version of this structure */
+ /* The major version number will change if this structure changes. */
+ int version_major;
+
+ /* Minor version of this structure */
+ /* The minor version number will change if new features are added
+ * without changes to this structure. For example, a new color
+ * format.
+ */
+ int version_minor;
+
+ /* New device has been opened */
+ /* This is the first event from this device. */
+ int (*display_open)(void *handle, void *device);
+
+ /* Device is about to be closed. */
+ /* Device will not be closed until this function returns. */
+ int (*display_preclose)(void *handle, void *device);
+
+ /* Device has been closed. */
+ /* This is the last event from this device. */
+ int (*display_close)(void *handle, void *device);
+
+ /* Device is about to be resized. */
+ /* Resize will only occur if this function returns 0. */
+ /* raster is byte count of a row. */
+ int (*display_presize)(void *handle, void *device,
+ int width, int height, int raster, unsigned int format);
+
+ /* Device has been resized. */
+ /* New pointer to raster returned in pimage */
+ int (*display_size)(void *handle, void *device, int width, int height,
+ int raster, unsigned int format, unsigned char *pimage);
+
+ /* flushpage */
+ int (*display_sync)(void *handle, void *device);
+
+ /* showpage */
+ /* If you want to pause on showpage, then don't return immediately */
+ int (*display_page)(void *handle, void *device, int copies, int flush);
+
+ /* Notify the caller whenever a portion of the raster is updated. */
+ /* This can be used for cooperative multitasking or for
+ * progressive update of the display.
+ * This function pointer may be set to NULL if not required.
+ */
+ int (*display_update)(void *handle, void *device, int x, int y,
+ int w, int h);
+
+ /* Allocate memory for bitmap */
+ /* This is provided in case you need to create memory in a special
+ * way, e.g. shared. If this is NULL, the Ghostscript memory device
+ * allocates the bitmap. This will only called to allocate the
+ * image buffer. The first row will be placed at the address
+ * returned by display_memalloc.
+ */
+ void *(*display_memalloc)(void *handle, void *device, unsigned long size);
+
+ /* Free memory for bitmap */
+ /* If this is NULL, the Ghostscript memory device will free the bitmap */
+ int (*display_memfree)(void *handle, void *device, void *mem);
+
+ /* Added in V2 */
+ /* When using separation color space (DISPLAY_COLORS_SEPARATION),
+ * give a mapping for one separation component.
+ * This is called for each new component found.
+ * It may be called multiple times for each component.
+ * It may be called at any time between display_size
+ * and display_close.
+ * The client uses this to map from the separations to CMYK
+ * and hence to RGB for display.
+ * GS must only use this callback if version_major >= 2.
+ * The unsigned short c,m,y,k values are 65535 = 1.0.
+ * This function pointer may be set to NULL if not required.
+ */
+ int (*display_separation)(void *handle, void *device,
+ int component, const char *component_name,
+ unsigned short c, unsigned short m,
+ unsigned short y, unsigned short k);
+};
+
+/* This is the V1 structure, before separation format was added */
+struct display_callback_v1_s {
+ int size;
+ int version_major;
+ int version_minor;
+ int (*display_open)(void *handle, void *device);
+ int (*display_preclose)(void *handle, void *device);
+ int (*display_close)(void *handle, void *device);
+ int (*display_presize)(void *handle, void *device,
+ int width, int height, int raster, unsigned int format);
+ int (*display_size)(void *handle, void *device, int width, int height,
+ int raster, unsigned int format, unsigned char *pimage);
+ int (*display_sync)(void *handle, void *device);
+ int (*display_page)(void *handle, void *device, int copies, int flush);
+ int (*display_update)(void *handle, void *device, int x, int y,
+ int w, int h);
+ void *(*display_memalloc)(void *handle, void *device, unsigned long size);
+ int (*display_memfree)(void *handle, void *device, void *mem);
+};
+
+#define DISPLAY_CALLBACK_V1_SIZEOF sizeof(struct display_callback_v1_s)
+
+#endif /* gdevdsp_INCLUDED */
diff --git a/devices/gdevdsp2.h b/devices/gdevdsp2.h
new file mode 100644
index 000000000..762ad30c2
--- /dev/null
+++ b/devices/gdevdsp2.h
@@ -0,0 +1,46 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+/* gdevdsp2.c */
+
+#ifndef gdevdsp2_INCLUDED
+# define gdevdsp2_INCLUDED
+
+typedef struct gx_device_display_s gx_device_display;
+
+#define gx_device_display_common\
+ gx_device_memory *mdev;\
+ display_callback *callback;\
+ void *pHandle;\
+ int nFormat;\
+ void *pBitmap;\
+ unsigned long ulBitmapSize;\
+ int HWResolution_set;\
+ gs_devn_params devn_params;\
+ equivalent_cmyk_color_params equiv_cmyk_colors
+
+/* The device descriptor */
+struct gx_device_display_s {
+ gx_device_common;
+ gx_device_display_common;
+};
+
+extern_st(st_device_display);
+#define public_st_device_display() /* in gdevdsp.c */\
+ gs_public_st_composite_use_final(st_device_display, gx_device_display,\
+ "gx_device_display", display_enum_ptrs, display_reloc_ptrs,\
+ gx_device_finalize)
+
+#endif /* gdevdsp2_INCLUDED */
diff --git a/devices/gdevepsc.c b/devices/gdevepsc.c
new file mode 100644
index 000000000..7eb4b6fd8
--- /dev/null
+++ b/devices/gdevepsc.c
@@ -0,0 +1,453 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+/* Epson color dot-matrix printer driver by dave@exlog.com */
+#include "gdevprn.h"
+
+/*
+ * For 9-pin printers, you may select
+ * X_DPI = 60, 120, or 240
+ * Y_DPI = 60 or 72
+ * For 24-pin printers, you may select
+ * X_DPI = 60, 120, 180, 240, or 360
+ * Y_DPI = 60, 72, 180, or 216
+ * Note that a given printer implements *either* Y_DPI = 60 | 180 *or*
+ * Y_DPI = 72 | 216; no attempt is made to check this here.
+ * Note that X_DPI = 180 or 360 requires Y_DPI > 100;
+ * this isn't checked either. Finally, note that X_DPI=240 and
+ * X_DPI=360 are double-density modes requiring two passes to print.
+ *
+ * The values of X_DPI and Y_DPI may be set at compile time:
+ * see gdevs.mak.
+ *
+ * At some time in the future, we could simulate 24-bit output on
+ * 9-pin printers by using fractional vertical positioning;
+ * we could even implement an X_DPI=360 mode by using the
+ * ESC++ command that spaces vertically in units of 1/360"
+ * (not supported on many printers.)
+ */
+
+#ifndef X_DPI
+# define X_DPI 180 /* pixels per inch */
+#endif
+#ifndef Y_DPI
+# define Y_DPI 180 /* pixels per inch */
+#endif
+
+/*
+** Colors for EPSON LQ-2550.
+**
+** We map VIOLET to BLUE since this is the best we can do.
+*/
+#define BLACK 0
+#define MAGENTA 1
+#define CYAN 2
+#define VIOLET 3
+#define YELLOW 4
+#define RED 5
+#define GREEN 6
+#define WHITE 7
+
+/*
+** The offset in this array correspond to
+** the ESC-r n value
+*/
+static char rgb_color[2][2][2] = {
+ {{BLACK, VIOLET}, {GREEN, CYAN}},
+ {{RED, MAGENTA}, {YELLOW, WHITE}}
+ };
+
+/* Map an RGB color to a printer color. */
+#define cv_shift (sizeof(gx_color_value) * 8 - 1)
+static gx_color_index
+epson_map_rgb_color(gx_device *dev, const gx_color_value cv[])
+{
+
+ gx_color_value r = cv[0];
+ gx_color_value g = cv[1];
+ gx_color_value b = cv[2];
+
+ if (gx_device_has_color(dev))
+/* use ^7 so WHITE is 0 for internal calculations */
+ return (gx_color_index)rgb_color[r >> cv_shift][g >> cv_shift][b >> cv_shift] ^ 7;
+ else
+ return gx_default_map_rgb_color(dev, cv);
+}
+
+/* Map the printer color back to RGB. */
+static int
+epson_map_color_rgb(gx_device *dev, gx_color_index color,
+ gx_color_value prgb[3])
+{
+#define c1 gx_max_color_value
+if (gx_device_has_color(dev))
+ switch ((ushort)color ^ 7)
+ {
+ case BLACK:
+ prgb[0] = 0; prgb[1] = 0; prgb[2] = 0; break;
+ case VIOLET:
+ prgb[0] = 0; prgb[1] = 0; prgb[2] = c1; break;
+ case GREEN:
+ prgb[0] = 0; prgb[1] = c1; prgb[2] = 0; break;
+ case CYAN:
+ prgb[0] = 0; prgb[1] = c1; prgb[2] = c1; break;
+ case RED:
+ prgb[0] = c1; prgb[1] = 0; prgb[2] = 0; break;
+ case MAGENTA:
+ prgb[0] = c1; prgb[1] = 0; prgb[2] = c1; break;
+ case YELLOW:
+ prgb[0] = c1; prgb[1] = c1; prgb[2] = 0; break;
+ case WHITE:
+ prgb[0] = c1; prgb[1] = c1; prgb[2] = c1; break;
+ }
+ else
+ return gx_default_map_color_rgb(dev, color, prgb);
+ return 0;
+}
+
+/* The device descriptor */
+static dev_proc_print_page(epsc_print_page);
+
+/* Since the print_page doesn't alter the device, this device can print in the background */
+static gx_device_procs epson_procs =
+ prn_color_procs(gdev_prn_open, gdev_prn_bg_output_page, gdev_prn_close,
+ epson_map_rgb_color, epson_map_color_rgb);
+
+const gx_device_printer far_data gs_epsonc_device =
+ prn_device(epson_procs, "epsonc",
+ DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
+ X_DPI, Y_DPI,
+ 0, 0, 0.25, 0, /* margins */
+ 3, epsc_print_page);
+
+/* ------ Internal routines ------ */
+
+/* Forward references */
+static void epsc_output_run(byte *, int, int, char, FILE *, int);
+
+/* Send the page to the printer. */
+#define DD 0x80 /* double density flag */
+static int
+epsc_print_page(gx_device_printer *pdev, FILE *prn_stream)
+{ static int graphics_modes_9[5] =
+ { -1, 0 /*60*/, 1 /*120*/, -1, DD+3 /*240*/
+ };
+ static int graphics_modes_24[7] =
+ { -1, 32 /*60*/, 33 /*120*/, 39 /*180*/,
+ -1, -1, DD+40 /*360*/
+ };
+ int y_24pin = pdev->y_pixels_per_inch > 72;
+ int y_mult = (y_24pin ? 3 : 1);
+ int line_size = (pdev->width + 7) >> 3; /* always mono */
+ int in_size = line_size * (8 * y_mult);
+ byte *in = (byte *)gs_malloc(pdev->memory, in_size+1, 1, "epsc_print_page(in)");
+ int out_size = ((pdev->width + 7) & -8) * y_mult;
+ byte *out = (byte *)gs_malloc(pdev->memory, out_size+1, 1, "epsc_print_page(out)");
+ int x_dpi = (int)pdev->x_pixels_per_inch;
+ char start_graphics = (char)
+ ((y_24pin ? graphics_modes_24 : graphics_modes_9)[x_dpi / 60]);
+ int first_pass = (start_graphics & DD ? 1 : 0);
+ int last_pass = first_pass * 2;
+ int dots_per_space = x_dpi / 10; /* pica space = 1/10" */
+ int bytes_per_space = dots_per_space * y_mult;
+ int skip = 0, lnum = 0, pass;
+/* declare color buffer and related vars */
+ byte *color_in;
+ int color_line_size, color_in_size;
+ int spare_bits = (pdev->width % 8); /* left over bits to go to margin */
+ int whole_bits = pdev->width - spare_bits;
+
+ /* Check allocations */
+ if ( in == 0 || out == 0 )
+ { if ( in ) gs_free(pdev->memory, (char *)in, in_size+1, 1, "epsc_print_page(in)");
+ if ( out ) gs_free(pdev->memory, (char *)out, out_size+1, 1, "epsc_print_page(out)");
+ return -1;
+ }
+
+ /* Initialize the printer and reset the margins. */
+ fwrite("\033@\033P\033l\000\033Q\377\033U\001\r", 1, 14, prn_stream);
+
+/* Create color buffer */
+ if (gx_device_has_color(pdev))
+ {
+ color_line_size = gdev_mem_bytes_per_scan_line((gx_device *)pdev);
+ color_in_size = color_line_size * (8 * y_mult);
+ if((color_in = (byte *)gs_malloc(pdev->memory, color_in_size+1, 1,
+ "epsc_print_page(color)")) == 0)
+ {
+ gs_free(pdev->memory, (char *)in, in_size+1, 1, "epsc_print_page(in)");
+ gs_free(pdev->memory, (char *)out, out_size+1, 1, "epsc_print_page(out)");
+ return(-1);
+ }
+ }
+ else
+ {
+ color_in = in;
+ color_in_size = in_size;
+ color_line_size = line_size;
+ }
+
+ /* Print lines of graphics */
+ while ( lnum < pdev->height )
+ {
+ int lcnt;
+ byte *nextcolor = NULL; /* position where next color appears */
+ byte *nextmono = NULL; /* position to map next color */
+
+ /* Copy 1 scan line and test for all zero. */
+ gdev_prn_copy_scan_lines(pdev, lnum, color_in, color_line_size);
+
+ if ( color_in[0] == 0 &&
+ !memcmp((char *)color_in, (char *)color_in + 1, color_line_size - 1)
+ )
+ { lnum++;
+ skip += 3 / y_mult;
+ continue;
+ }
+
+ /* Vertical tab to the appropriate position. */
+ while ( skip > 255 )
+ { fputs("\033J\377", prn_stream);
+ skip -= 255;
+ }
+ if ( skip )
+ fprintf(prn_stream, "\033J%c", skip);
+
+ /* Copy the rest of the scan lines. */
+ lcnt = 1 + gdev_prn_copy_scan_lines(pdev, lnum + 1,
+ color_in + color_line_size, color_in_size - color_line_size);
+
+ if ( lcnt < 8 * y_mult )
+ {
+ memset((char *)(color_in + lcnt * color_line_size), 0,
+ color_in_size - lcnt * color_line_size);
+ if (gx_device_has_color(pdev)) /* clear the work buffer */
+ memset((char *)(in + lcnt * line_size), 0,
+ in_size - lcnt * line_size);
+ }
+
+/*
+** We need to create a normal epson scan line from our color scan line
+** We do this by setting a bit in the "in" buffer if the pixel byte is set
+** to any color. We then search for any more pixels of that color, setting
+** "in" accordingly. If any other color is found, we save it for the next
+** pass. There may be up to 7 passes.
+** In the future, we should make the passes so as to maximize the
+** life of the color ribbon (i.e. go lightest to darkest).
+*/
+ do
+ {
+ byte *inp = in;
+ byte *in_end = in + line_size;
+ byte *out_end = out;
+ byte *out_blk;
+ register byte *outp;
+
+ if (gx_device_has_color(pdev))
+ {
+ register int i,j;
+ register byte *outbuf, *realbuf;
+ byte current_color;
+ int end_next_bits = whole_bits;
+ int lastbits;
+
+/* Move to the point in the scanline that has a new color */
+ if (nextcolor)
+ {
+ realbuf = nextcolor;
+ outbuf = nextmono;
+ memset((char *)in, 0, (nextmono - in));
+ i = nextcolor - color_in;
+ nextcolor = NULL;
+ end_next_bits = (i / color_line_size) * color_line_size
+ + whole_bits;
+ }
+ else
+ {
+ i = 0;
+ realbuf = color_in;
+ outbuf = in;
+ nextcolor = NULL;
+ }
+/* move thru the color buffer, turning on the appropriate
+** bit in the "mono" buffer", setting pointers to the next
+** color and changing the color output of the epson
+*/
+ for (current_color = 0; i <= color_in_size && outbuf < in + in_size; outbuf++)
+ {
+/* Remember, line_size is rounded up to next whole byte
+** whereas color_line_size is the proper length
+** We only want to set the proper bits in the last line_size byte.
+*/
+ if (spare_bits && i == end_next_bits)
+ {
+ end_next_bits = whole_bits + i + spare_bits;
+ lastbits = 8 - spare_bits;
+ }
+ else
+ lastbits = 0;
+
+ for (*outbuf = 0, j = 8; --j >= lastbits && i <= color_in_size;
+ realbuf++,i++)
+ {
+ if (*realbuf)
+ {
+ if (current_color > 0)
+ {
+ if (*realbuf == current_color)
+ {
+ *outbuf |= 1 << j;
+ *realbuf = 0; /* throw this byte away */
+ }
+ /* save this location for next pass */
+ else if (nextcolor == NULL)
+ {
+ nextcolor = realbuf - (7 - j);
+ nextmono = outbuf;
+ }
+ }
+ else
+ {
+ *outbuf |= 1 << j;
+ current_color = *realbuf; /* set color */
+ *realbuf = 0;
+ }
+ }
+ }
+ }
+ *outbuf = 0; /* zero the end, for safe keeping */
+/* Change color on the EPSON, current_color must be set
+** but lets check anyway
+*/
+ if (current_color)
+ fprintf(prn_stream,"\033r%c",current_color ^ 7);
+ }
+
+ /* We have to 'transpose' blocks of 8 pixels x 8 lines, */
+ /* because that's how the printer wants the data. */
+ /* If we are in a 24-pin mode, we have to transpose */
+ /* groups of 3 lines at a time. */
+
+ if ( y_24pin )
+ { for ( ; inp < in_end; inp++, out_end += 24 )
+ { gdev_prn_transpose_8x8(inp, line_size, out_end, 3);
+ gdev_prn_transpose_8x8(inp + line_size * 8, line_size, out_end + 1, 3);
+ gdev_prn_transpose_8x8(inp + line_size * 16, line_size, out_end + 2, 3);
+ }
+ /* Remove trailing 0s. */
+ while ( out_end > out && out_end[-1] == 0 &&
+ out_end[-2] == 0 && out_end[-3] == 0
+ )
+ out_end -= 3;
+ }
+ else
+ { for ( ; inp < in_end; inp++, out_end += 8 )
+ { gdev_prn_transpose_8x8(inp, line_size, out_end, 1);
+ }
+ /* Remove trailing 0s. */
+ while ( out_end > out && out_end[-1] == 0 )
+ out_end--;
+ }
+
+ for ( pass = first_pass; pass <= last_pass; pass++ )
+ {
+ for ( out_blk = outp = out; outp < out_end; )
+ { /* Skip a run of leading 0s. */
+ /* At least 10 are needed to make tabbing worth it. */
+ /* We do everything by 3's to avoid having to make */
+ /* different cases for 9- and 24-pin. */
+
+ if ( *outp == 0 && outp + 12 <= out_end &&
+ outp[1] == 0 && outp[2] == 0 &&
+ (outp[3] | outp[4] | outp[5]) == 0 &&
+ (outp[6] | outp[7] | outp[8]) == 0 &&
+ (outp[9] | outp[10] | outp[11]) == 0
+ )
+ { byte *zp = outp;
+ int tpos;
+ byte *newp;
+ outp += 12;
+ while ( outp + 3 <= out_end && *outp == 0 &&
+ outp[1] == 0 && outp[2] == 0
+ )
+ outp += 3;
+ tpos = (outp - out) / bytes_per_space;
+ newp = out + tpos * bytes_per_space;
+ if ( newp > zp + 10 )
+ { /* Output preceding bit data. */
+ if ( zp > out_blk ) /* only false at */
+ /* beginning of line */
+ epsc_output_run(out_blk, (int)(zp - out_blk),
+ y_mult, start_graphics,
+ prn_stream, pass);
+ /* Tab over to the appropriate position. */
+ fprintf(prn_stream, "\033D%c%c\t", tpos, 0);
+ out_blk = outp = newp;
+ }
+ }
+ else
+ outp += y_mult;
+ }
+ if ( outp > out_blk )
+ epsc_output_run(out_blk, (int)(outp - out_blk),
+ y_mult, start_graphics,
+ prn_stream, pass);
+
+ fputc('\r', prn_stream);
+ }
+ } while (nextcolor);
+ skip = 24;
+ lnum += 8 * y_mult;
+ }
+
+ /* Eject the page and reinitialize the printer */
+ fputs("\f\033@", prn_stream);
+
+ gs_free(pdev->memory, (char *)out, out_size+1, 1, "epsc_print_page(out)");
+ gs_free(pdev->memory, (char *)in, in_size+1, 1, "epsc_print_page(in)");
+ if (gx_device_has_color(pdev))
+ gs_free(pdev->memory, (char *)color_in, color_in_size+1, 1, "epsc_print_page(rin)");
+ return 0;
+}
+
+/* Output a single graphics command. */
+/* pass=0 for all columns, 1 for even columns, 2 for odd columns. */
+static void
+epsc_output_run(byte *data, int count, int y_mult,
+ char start_graphics, FILE *prn_stream, int pass)
+{ int xcount = count / y_mult;
+ fputc(033, prn_stream);
+ if ( !(start_graphics & ~3) )
+ { fputc("KLYZ"[(int)start_graphics], prn_stream);
+ }
+ else
+ { fputc('*', prn_stream);
+ fputc(start_graphics & ~DD, prn_stream);
+ }
+ fputc(xcount & 0xff, prn_stream);
+ fputc(xcount >> 8, prn_stream);
+ if ( !pass )
+ fwrite((char *)data, 1, count, prn_stream);
+ else
+ { /* Only write every other column of y_mult bytes. */
+ int which = pass;
+ byte *dp = data;
+ register int i, j;
+ for ( i = 0; i < xcount; i++, which++ )
+ for ( j = 0; j < y_mult; j++, dp++ )
+ { putc(((which & 1) ? *dp : 0), prn_stream);
+ }
+ }
+}
diff --git a/devices/gdevepsn.c b/devices/gdevepsn.c
new file mode 100644
index 000000000..537072068
--- /dev/null
+++ b/devices/gdevepsn.c
@@ -0,0 +1,496 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/*
+ * Epson (and similar) dot-matrix printer driver for Ghostscript.
+ *
+ * Four devices are defined here: 'epson', 'eps9mid', 'eps9high', and 'ibmpro'.
+ * The 'epson' device is the generic device, for 9-pin and 24-pin printers.
+ * 'eps9high' is a special mode for 9-pin printers where scan lines are
+ * interleaved in multiple passes to produce high vertical resolution at
+ * the expense of several passes of the print head. 'eps9mid' is a special
+ * mode for 9 pin printers too, scan lines are interleaved but with next
+ * vertical line. 'ibmpro' is for the IBM ProPrinter, which has slightly
+ * (but only slightly) different control codes.
+ *
+ * Thanks to:
+ * David Wexelblat (dwex@mtgzfs3.att.com) for the 'eps9high' code;
+ * Guenther Thomsen (thomsen@cs.tu-berlin.de) for the 'eps9mid' code;
+ * James W. Birdsall (jwbirdsa@picarefy.picarefy.com) for the
+ * 'ibmpro' modifications;
+ * Russell J. Lang (rjl@aladdin.com) for the 180x60 and 240x180 dpi
+ * enhancements.
+ */
+#include "gdevprn.h"
+
+/*
+ * Define whether the printer is archaic -- so old that it doesn't
+ * support settable tabs, pitch, or left margin. (This should be a
+ * run-time property....) Note: the IBM ProPrinter is archaic.
+ */
+/*#define ARCHAIC 1*/
+
+/*
+ * Define whether the printer is a Panasonic 9-pin printer,
+ * which sometimes doesn't recognize a horizontal tab command
+ * when a line contains a lot of graphics commands,
+ * requiring a "backspace, space" sequence before a tab.
+ */
+/*#define TAB_HICCUP 1*/
+
+/*
+ * Define the minimum distance for which it's worth converting white space
+ * into a tab. This can be specified in pixels (to save transmission time),
+ * in tenths of an inch (for printers where tabs provoke actual head motion),
+ * or both. The distance must meet BOTH criteria for the driver to tab,
+ * so an irrelevant criterion should be set to 0 rather than infinite.
+ */
+#define MIN_TAB_PIXELS 10
+#define MIN_TAB_10THS 15
+
+/*
+ * Valid values for X_DPI:
+ *
+ * For 9-pin printers: 60, 120, 240
+ * For 24-pin printers: 60, 120, 180, 240, 360
+ *
+ * The value specified at compile time is the default value used if the
+ * user does not specify a resolution at runtime.
+ */
+#ifndef X_DPI
+# define X_DPI 240
+#endif
+
+/*
+ * For Y_DPI, a given printer will support a base resolution of 60 or 72;
+ * check the printer manual. The Y_DPI value must be a multiple of this
+ * base resolution. Valid values for Y_DPI:
+ *
+ * For 9-pin printers: 1*base_res
+ * For 24-pin printers: 1*base_res, 3*base_res
+ *
+ * The value specified at compile time is the default value used if the
+ * user does not specify a resolution at runtime.
+ */
+
+#ifndef Y_BASERES
+# define Y_BASERES 72
+#endif
+#ifndef Y_DPI
+# define Y_DPI (1*Y_BASERES)
+#endif
+
+/* The device descriptors */
+static dev_proc_print_page(epson_print_page);
+static dev_proc_print_page(eps9mid_print_page);
+static dev_proc_print_page(eps9high_print_page);
+static dev_proc_print_page(ibmpro_print_page);
+
+/* Standard Epson device */
+const gx_device_printer far_data gs_epson_device =
+ prn_device(prn_bg_procs, "epson", /* The print_page proc is compatible with allowing bg printing */
+ DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
+ X_DPI, Y_DPI,
+ 0.25, 0.02, 0.25, 0.4, /* margins */
+ 1, epson_print_page);
+
+/* Mid-res (interleaved, 1 pass per line) 9-pin device */
+const gx_device_printer far_data gs_eps9mid_device =
+ prn_device(prn_bg_procs, "eps9mid", /* The print_page proc is compatible with allowing bg printing */
+ DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
+ X_DPI, 3*Y_BASERES,
+ 0.2, 0.0, 0, 0.0, /* margins */
+ 1, eps9mid_print_page);
+
+/* High-res (interleaved) 9-pin device */
+const gx_device_printer far_data gs_eps9high_device =
+ prn_device(prn_bg_procs, "eps9high", /* The print_page proc is compatible with allowing bg printing */
+ DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
+ X_DPI, 3*Y_BASERES,
+ 0.2, 0.0, 0.0, 0.0, /* margins */
+ 1, eps9high_print_page);
+
+/* IBM ProPrinter device */
+const gx_device_printer far_data gs_ibmpro_device =
+ prn_device(prn_bg_procs, "ibmpro", /* The print_page proc is compatible with allowing bg printing */
+ DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
+ X_DPI, Y_DPI,
+ 0.2, 0.0, 0.0, 0.0, /* margins */
+ 1, ibmpro_print_page);
+
+/* ------ Driver procedures ------ */
+
+/* Forward references */
+static void eps_output_run(byte *, int, int, char, FILE *, int);
+
+/* Send the page to the printer. */
+#define DD 0x40 /* double density flag */
+static int
+eps_print_page(gx_device_printer *pdev, FILE *prn_stream, int y_9pin_high,
+ const char *init_string, int init_length, const char *end_string,
+ int archaic, int tab_hiccup)
+{
+ static const char graphics_modes_9[5] =
+ {
+ -1, 0 /*60*/, 1 /*120*/, 7 /*180*/, DD+3 /*240*/
+ };
+
+ static const char graphics_modes_24[7] =
+ {
+ -1, 32 /*60*/, 33 /*120*/, 39 /*180*/,
+ DD+35 /*240*/, -1, DD+40 /*360*/
+ };
+
+ int y_24pin = (y_9pin_high ? 0 : pdev->y_pixels_per_inch > 72);
+ int in_y_mult = ((y_24pin | y_9pin_high) ? 3 : 1);
+ int line_size = gdev_mem_bytes_per_scan_line((gx_device *)pdev);
+ /* Note that in_size is a multiple of 8. */
+ int in_size = line_size * (8 * in_y_mult);
+ byte *buf1 = (byte *)gs_malloc(pdev->memory, in_size, 1, "eps_print_page(buf1)");
+ byte *buf2 = (byte *)gs_malloc(pdev->memory, in_size, 1, "eps_print_page(buf2)");
+ byte *in = buf1;
+ byte *out = buf2;
+ int out_y_mult = (y_24pin ? 3 : 1);
+ int x_dpi = (int)pdev->x_pixels_per_inch;
+ char start_graphics =
+ (y_24pin ? graphics_modes_24 : graphics_modes_9)[x_dpi / 60];
+ int first_pass = (start_graphics & DD ? 1 : 0);
+ int last_pass = first_pass * (y_9pin_high == 2 ? 1 : 2);
+ int y_passes = (y_9pin_high ? 3 : 1);
+ int dots_per_space = x_dpi / 10; /* pica space = 1/10" */
+ int bytes_per_space = dots_per_space * out_y_mult;
+ int tab_min_pixels = x_dpi * MIN_TAB_10THS / 10;
+ int skip = 0, lnum = 0, pass, ypass;
+
+ /* Check allocations */
+ if ( buf1 == 0 || buf2 == 0 )
+ { if ( buf1 )
+ gs_free(pdev->memory, (char *)buf1, in_size, 1, "eps_print_page(buf1)");
+ if ( buf2 )
+ gs_free(pdev->memory, (char *)buf2, in_size, 1, "eps_print_page(buf2)");
+ return_error(gs_error_VMerror);
+ }
+
+ /* Initialize the printer and reset the margins. */
+ fwrite(init_string, 1, init_length, prn_stream);
+ if ( init_string[init_length - 1] == 'Q' )
+ fputc((int)(pdev->width / pdev->x_pixels_per_inch * 10) + 2,
+ prn_stream);
+
+ /* Calculate the minimum tab distance. */
+ if ( tab_min_pixels < max(MIN_TAB_PIXELS, 3) )
+ tab_min_pixels = max(MIN_TAB_PIXELS, 3);
+ tab_min_pixels -= tab_min_pixels % 3; /* simplify life */
+
+ /* Print lines of graphics */
+ while ( lnum < pdev->height )
+ {
+ byte *in_data;
+ byte *inp;
+ byte *in_end;
+ byte *out_end;
+ byte *out_blk;
+ register byte *outp;
+ int lcnt;
+
+ /* Copy 1 scan line and test for all zero. */
+ gdev_prn_get_bits(pdev, lnum, in, &in_data);
+ if ( in_data[0] == 0 &&
+ !memcmp((char *)in_data, (char *)in_data + 1, line_size - 1)
+ )
+ {
+ lnum++;
+ skip += 3 / in_y_mult;
+ continue;
+ }
+
+ /* Vertical tab to the appropriate position. */
+ while ( skip > 255 )
+ {
+ fputs("\033J\377", prn_stream);
+ skip -= 255;
+ }
+ if ( skip )
+ {
+ fprintf(prn_stream, "\033J%c", skip);
+ }
+
+ /* Copy the the scan lines. */
+ lcnt = gdev_prn_copy_scan_lines(pdev, lnum, in, in_size);
+ if ( lcnt < 8 * in_y_mult )
+ { /* Pad with lines of zeros. */
+ memset(in + lcnt * line_size, 0,
+ in_size - lcnt * line_size);
+ }
+
+ if ( y_9pin_high == 2 )
+ { /* Force printing of every dot in one pass */
+ /* by reducing vertical resolution */
+ /* (ORing with the next line of data). */
+ /* This is necessary because some Epson compatibles */
+ /* can't print neighboring dots. */
+ int i;
+ for ( i = 0; i < line_size * in_y_mult; ++i )
+ in_data[i] |= in_data[i + line_size];
+ }
+
+ if ( y_9pin_high )
+ { /* Shuffle the scan lines */
+ byte *p;
+ int i;
+ static const char index[] =
+ { 0, 8, 16, 1, 9, 17,
+ 2, 10, 18, 3, 11, 19,
+ 4, 12, 20, 5, 13, 21,
+ 6, 14, 22, 7, 15, 23
+ };
+
+ for ( i = 0; i < 24; i++ )
+ {
+ memcpy(out+(index[i]*line_size),
+ in+(i*line_size), line_size);
+ }
+ p = in;
+ in = out;
+ out = p;
+ }
+
+ for ( ypass = 0; ypass < y_passes; ypass++ )
+ {
+ for ( pass = first_pass; pass <= last_pass; pass++ )
+ {
+ /* We have to 'transpose' blocks of 8 pixels x 8 lines, */
+ /* because that's how the printer wants the data. */
+ /* If we are in a 24-pin mode, we have to transpose */
+ /* groups of 3 lines at a time. */
+
+ if ( pass == first_pass )
+ {
+ out_end = out;
+ inp = in;
+ in_end = inp + line_size;
+
+ if ( y_24pin )
+ {
+ for ( ; inp < in_end; inp++, out_end += 24 )
+ {
+ gdev_prn_transpose_8x8(inp, line_size, out_end, 3);
+ gdev_prn_transpose_8x8(inp + line_size * 8,
+ line_size, out_end + 1, 3);
+ gdev_prn_transpose_8x8(inp + line_size * 16,
+ line_size, out_end + 2, 3);
+ }
+ /* Remove trailing 0s. */
+ while ( out_end > out && out_end[-1] == 0 &&
+ out_end[-2] == 0 && out_end[-3] == 0)
+ {
+ out_end -= 3;
+ }
+ }
+ else
+ {
+ for ( ; inp < in_end; inp++, out_end += 8 )
+ {
+ gdev_prn_transpose_8x8(inp + (ypass * 8*line_size),
+ line_size, out_end, 1);
+ }
+ /* Remove trailing 0s. */
+ while ( out_end > out && out_end[-1] == 0 )
+ {
+ out_end--;
+ }
+ }
+ }
+
+ for ( out_blk = outp = out; outp < out_end; )
+ {
+ /* Skip a run of leading 0s. At least */
+ /* tab_min_pixels are needed to make tabbing */
+ /* worth it. We do everything by 3's to */
+ /* avoid having to make different cases */
+ /* for 9- and 24-pin. */
+ if ( !archaic &&
+ *outp == 0 && out_end - outp >= tab_min_pixels &&
+ (outp[1] | outp[2]) == 0 &&
+ !memcmp((char *)outp, (char *)outp + 3,
+ tab_min_pixels - 3)
+ )
+ {
+ byte *zp = outp;
+ int tpos;
+ byte *newp;
+
+ outp += tab_min_pixels;
+ while ( outp + 3 <= out_end &&
+ *outp == 0 &&
+ outp[1] == 0 && outp[2] == 0 )
+ {
+ outp += 3;
+ }
+ tpos = (outp - out) / bytes_per_space;
+ newp = out + tpos * bytes_per_space;
+ if ( newp > zp + 10 )
+ {
+ /* Output preceding bit data.*/
+ if ( zp > out_blk )
+ {
+ /* only false at beginning of line */
+ eps_output_run(out_blk, (int)(zp - out_blk),
+ out_y_mult, start_graphics,
+ prn_stream,
+ (y_9pin_high == 2 ?
+ (1 + ypass) & 1 : pass));
+ }
+ /* Tab over to the appropriate position. */
+ if ( tab_hiccup )
+ fputs("\010 ", prn_stream); /* bksp, space */
+ /* The following statement is broken up */
+ /* to work around a bug in emx/gcc. */
+ fprintf(prn_stream, "\033D%c", tpos);
+ fputc(0, prn_stream);
+ fputc('\t', prn_stream);
+ out_blk = outp = newp;
+ }
+ }
+ else
+ {
+ outp += out_y_mult;
+ }
+ }
+ if ( outp > out_blk )
+ {
+ eps_output_run(out_blk, (int)(outp - out_blk),
+ out_y_mult, start_graphics,
+ prn_stream,
+ (y_9pin_high == 2 ? (1 + ypass) & 1 : pass));
+ }
+
+ fputc('\r', prn_stream);
+ }
+ if ( ypass < y_passes - 1 )
+ fputs("\033J\001", prn_stream);
+ }
+ skip = 24 - y_passes + 1; /* no skip on last Y pass */
+ lnum += 8 * in_y_mult;
+ }
+
+ /* Eject the page and reinitialize the printer */
+ fputs(end_string, prn_stream);
+ fflush(prn_stream);
+
+ gs_free(pdev->memory, (char *)buf2, in_size, 1, "eps_print_page(buf2)");
+ gs_free(pdev->memory, (char *)buf1, in_size, 1, "eps_print_page(buf1)");
+ return 0;
+}
+
+/* Output a single graphics command. */
+/* pass=0 for all columns, 1 for even columns, 2 for odd columns. */
+static void
+eps_output_run(byte *data, int count, int y_mult,
+ char start_graphics, FILE *prn_stream, int pass)
+{
+ int xcount = count / y_mult;
+
+ fputc(033, prn_stream);
+ if ( !(start_graphics & ~3) )
+ {
+ fputc("KLYZ"[(int)start_graphics], prn_stream);
+ }
+ else
+ {
+ fputc('*', prn_stream);
+ fputc(start_graphics & ~DD, prn_stream);
+ }
+ fputc(xcount & 0xff, prn_stream);
+ fputc(xcount >> 8, prn_stream);
+ if ( !pass )
+ {
+ fwrite(data, 1, count, prn_stream);
+ }
+ else
+ {
+ /* Only write every other column of y_mult bytes. */
+ int which = pass;
+ register byte *dp = data;
+ register int i, j;
+
+ for ( i = 0; i < xcount; i++, which++ )
+ {
+ for ( j = 0; j < y_mult; j++, dp++ )
+ {
+ putc(((which & 1) ? *dp : 0), prn_stream);
+ }
+ }
+ }
+}
+
+/* The print_page procedures are here, to avoid a forward reference. */
+#ifndef ARCHAIC
+# define ARCHAIC 0
+#endif
+#ifndef TAB_HICCUP
+# define TAB_HICCUP 0
+#endif
+
+#define ESC 0x1b
+static const char eps_init_string[] = {
+#if ARCHAIC
+ ESC, '@', 022 /*^R*/, ESC, 'Q'
+#else
+ ESC, '@', ESC, 'P', ESC, 'l', 0, '\r', ESC, 'Q'
+#endif
+};
+
+static int
+epson_print_page(gx_device_printer *pdev, FILE *prn_stream)
+{
+ return eps_print_page(pdev, prn_stream, 0, eps_init_string,
+ sizeof(eps_init_string), "\f\033@",
+ ARCHAIC, TAB_HICCUP);
+}
+
+static int
+eps9high_print_page(gx_device_printer *pdev, FILE *prn_stream)
+{
+ return eps_print_page(pdev, prn_stream, 1, eps_init_string,
+ sizeof(eps_init_string), "\f\033@",
+ ARCHAIC, TAB_HICCUP);
+}
+
+static int
+eps9mid_print_page(gx_device_printer *pdev, FILE *prn_stream)
+{
+ return eps_print_page(pdev, prn_stream, 2, eps_init_string,
+ sizeof(eps_init_string), "\f\033@",
+ ARCHAIC, TAB_HICCUP);
+}
+
+static int
+ibmpro_print_page(gx_device_printer *pdev, FILE *prn_stream)
+{
+ /*
+ * IBM Proprinter Guide to Operations, p. 4-5: "DC1: Select Printer: Sets
+ * the printer to accept data from your computer." Prevents printer from
+ * interpreting first characters as literal text.
+ */
+#define DC1 0x11
+ static const char ibmpro_init_string[] = {
+ DC1, ESC, '3', 0x30
+ };
+#undef DC1
+ return eps_print_page(pdev, prn_stream, 0, ibmpro_init_string,
+ sizeof(ibmpro_init_string), "\f", 1, 0);
+}
diff --git a/devices/gdevescp.c b/devices/gdevescp.c
new file mode 100644
index 000000000..b7b73ba69
--- /dev/null
+++ b/devices/gdevescp.c
@@ -0,0 +1,412 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+/*
+ * Epson 'ESC/P 2' language printer driver.
+ *
+ * This driver uses the ESC/P2 language raster graphics commands with
+ * compression. The driver skips vertical white space, provided that
+ * the white space is >= 24/band_size (<~ 1.7mm @ 360dpi!) high. There
+ * is no attempt to skip horizontal white space, but the compression
+ * greatly reduces the significance of this (a nearly blank line would
+ * take about 45 bytes). The driver compresses the data one scan line at
+ * a time, even though this is not enforced by the hardware. The reason
+ * I have done this is that, since the driver skips data outside the
+ * margins, we would have to set up a extra pointers to keep track of
+ * the data from the previous scan line. Doing this would add extra
+ * complexity at a small saving of disk space.
+ *
+ * These are the only possible optimisations that remain, and would
+ * greatly increase the complexity of the driver. At this point, I don't
+ * consider them necessary, but I might consider implementing them if
+ * enough people encourage me to do so.
+ *
+ * Richard Brown (rab@tauon.ph.unimelb.edu.au)
+ *
+ */
+
+#include "gdevprn.h"
+
+/*
+ * Valid values for X_DPI and Y_DPI: 180, 360
+ *
+ * The value specified at compile time is the default value used if the
+ * user does not specify a resolution at runtime.
+ */
+#ifndef X_DPI
+# define X_DPI 360
+#endif
+
+#ifndef Y_DPI
+# define Y_DPI 360
+#endif
+
+/*
+ * Margin definitions: Stylus 800 printer driver:
+ *
+ * The commented margins are from the User's Manual.
+ *
+ * The values actually used here are more accurate for my printer.
+ * The Stylus paper handling is quite sensitive to these settings.
+ * If you find that the printer uses an extra page after every real
+ * page, you'll need to increase the top and/or bottom margin.
+ */
+
+#define STYLUS_L_MARGIN 0.13 /*0.12*/
+#define STYLUS_B_MARGIN 0.56 /*0.51*/
+#define STYLUS_T_MARGIN 0.34 /*0.12*/
+#ifdef A4
+# define STYLUS_R_MARGIN 0.18 /*0.15*/
+#else
+# define STYLUS_R_MARGIN 0.38
+#endif
+
+/*
+ * Epson AP3250 Margins:
+ */
+
+#define AP3250_L_MARGIN 0.18
+#define AP3250_B_MARGIN 0.51
+#define AP3250_T_MARGIN 0.34
+#define AP3250_R_MARGIN 0.28 /* US paper */
+
+/* The device descriptor */
+static dev_proc_print_page(escp2_print_page);
+
+/* Stylus 800 device */
+const gx_device_printer far_data gs_st800_device =
+ prn_device(prn_bg_procs, "st800", /* The print_page proc is compatible with allowing bg printing */
+ DEFAULT_WIDTH_10THS,
+ DEFAULT_HEIGHT_10THS,
+ X_DPI, Y_DPI,
+ STYLUS_L_MARGIN, STYLUS_B_MARGIN, STYLUS_R_MARGIN, STYLUS_T_MARGIN,
+ 1, escp2_print_page);
+
+/* AP3250 device */
+const gx_device_printer far_data gs_ap3250_device =
+ prn_device(prn_bg_procs, "ap3250", /* The print_page proc is compatible with allowing bg printing */
+ DEFAULT_WIDTH_10THS,
+ DEFAULT_HEIGHT_10THS,
+ X_DPI, Y_DPI,
+ AP3250_L_MARGIN, AP3250_B_MARGIN, AP3250_R_MARGIN, AP3250_T_MARGIN,
+ 1, escp2_print_page);
+
+/* ------ Internal routines ------ */
+
+/* Send the page to the printer. */
+static int
+escp2_print_page(gx_device_printer *pdev, FILE *prn_stream)
+{
+ int line_size = gdev_prn_raster((gx_device_printer *)pdev);
+ int band_size = 24; /* 1, 8, or 24 */
+ int in_size = line_size * band_size;
+
+ byte *buf1 = (byte *)gs_malloc(pdev->memory, in_size, 1, "escp2_print_page(buf1)");
+ byte *buf2 = (byte *)gs_malloc(pdev->memory, in_size, 1, "escp2_print_page(buf2)");
+ byte *in = buf1;
+ byte *out = buf2;
+
+ int skip, lnum, top, bottom, left, width;
+ int auto_feed = 1;
+ int count, i;
+
+ /*
+ ** Check for valid resolution:
+ **
+ ** XDPI YDPI
+ ** 360 360
+ ** 360 180
+ ** 180 180
+ */
+
+ if( !( (pdev->x_pixels_per_inch == 180 &&
+ pdev->y_pixels_per_inch == 180) ||
+ (pdev->x_pixels_per_inch == 360 &&
+ (pdev->y_pixels_per_inch == 360 ||
+ pdev->y_pixels_per_inch == 180) )) )
+ return_error(gs_error_rangecheck);
+
+ /*
+ ** Check buffer allocations:
+ */
+
+ if ( buf1 == 0 || buf2 == 0 )
+ { if ( buf1 )
+ gs_free(pdev->memory, (char *)buf1, in_size, 1, "escp2_print_page(buf1)");
+ if ( buf2 )
+ gs_free(pdev->memory, (char *)buf2, in_size, 1, "escp2_print_page(buf2)");
+ return_error(gs_error_VMerror);
+ }
+
+ /*
+ ** Reset printer, enter graphics mode:
+ */
+
+ fwrite("\033@\033(G\001\000\001", 1, 8, prn_stream);
+
+#ifdef A4
+ /*
+ ** After reset, the Stylus is set up for US letter paper.
+ ** We need to set the page size appropriately for A4 paper.
+ ** For some bizarre reason the ESC/P2 language wants the bottom
+ ** margin measured from the *top* of the page:
+ */
+
+ fwrite("\033(U\001\0\n\033(C\002\0t\020\033(c\004\0\0\0t\020",
+ 1, 22, prn_stream);
+#endif
+
+ /*
+ ** Set the line spacing to match the band height:
+ */
+
+ if( pdev->y_pixels_per_inch == 360 )
+ fwrite("\033(U\001\0\012\033+\030", 1, 9, prn_stream);
+ else
+ fwrite("\033(U\001\0\024\033+\060", 1, 9, prn_stream);
+
+ /*
+ ** If the printer has automatic page feeding, then the paper
+ ** will already be positioned at the top margin value, so we
+ ** start printing the image from there. Similarly, we must not
+ ** try to print or even line feed past the bottom margin, since
+ ** the printer will automatically load a new page.
+ ** Printers without this feature may actually need to be told
+ ** to skip past the top margin.
+ */
+
+ if( auto_feed ) {
+ top = (int)(dev_t_margin(pdev) * pdev->y_pixels_per_inch);
+ bottom = (int)(pdev->height -
+ dev_b_margin(pdev) * pdev->y_pixels_per_inch);
+ } else {
+ top = 0;
+ bottom = pdev->height;
+ }
+
+ /*
+ ** Make left margin and width sit on byte boundaries:
+ */
+
+ left = ( (int) (dev_l_margin(pdev) * pdev->x_pixels_per_inch) ) >> 3;
+
+ width = ((pdev->width - (int)(dev_r_margin(pdev) * pdev->x_pixels_per_inch)) >> 3) - left;
+
+ /*
+ ** Print the page:
+ */
+
+ for ( lnum = top, skip = 0 ; lnum < bottom ; )
+ {
+ byte *in_data;
+ byte *inp;
+ byte *in_end;
+ byte *outp;
+ register byte *p, *q;
+ int lcnt;
+
+ /*
+ ** Check buffer for 0 data. We can't do this mid-band
+ */
+
+ gdev_prn_get_bits(pdev, lnum, in, &in_data);
+ while ( in_data[0] == 0 &&
+ !memcmp((char *)in_data, (char *)in_data + 1, line_size - 1) &&
+ lnum < bottom )
+ {
+ lnum++;
+ skip++;
+ gdev_prn_get_bits(pdev, lnum, in, &in_data);
+ }
+
+ if(lnum == bottom ) break; /* finished with this page */
+
+ /*
+ ** Skip blank lines if we need to:
+ */
+
+ if( skip ) {
+ fwrite("\033(v\002\000", 1, 5, prn_stream);
+ fputc(skip & 0xff, prn_stream);
+ fputc(skip >> 8, prn_stream);
+ skip = 0;
+ }
+
+ lcnt = gdev_prn_copy_scan_lines(pdev, lnum, in, in_size);
+
+ /*
+ ** Check to see if we don't have enough data to fill an entire
+ ** band. Padding here seems to work (the printer doesn't jump
+ ** to the next (blank) page), although the ideal behaviour
+ ** would probably be to reduce the band height.
+ **
+ ** Pad with nulls:
+ */
+
+ if( lcnt < band_size )
+ memset(in + lcnt * line_size, 0, in_size - lcnt * line_size);
+
+ /*
+ ** Now we have a band of data: try to compress it:
+ */
+
+ for( outp = out, i = 0 ; i < band_size ; i++ ) {
+
+ /*
+ ** Take margins into account:
+ */
+
+ inp = in + i * line_size + left;
+ in_end = inp + width;
+
+ /*
+ ** walk through input buffer, looking for repeated data:
+ ** Since we need more than 2 repeats to make the compression
+ ** worth it, we can compare pairs, since it doesn't matter if we
+ **
+ */
+
+ for( p = inp, q = inp + 1 ; q < in_end ; ) {
+
+ if( *p != *q ) {
+
+ p += 2;
+ q += 2;
+
+ } else {
+
+ /*
+ ** Check behind us, just in case:
+ */
+
+ if( p > inp && *p == *(p-1) )
+ p--;
+
+ /*
+ ** walk forward, looking for matches:
+ */
+
+ for( q++ ; *q == *p && q < in_end ; q++ ) {
+ if( (q-p) >= 128 ) {
+ if( p > inp ) {
+ count = p - inp;
+ while( count > 128 ) {
+ *outp++ = '\177';
+ memcpy(outp, inp, 128); /* data */
+ inp += 128;
+ outp += 128;
+ count -= 128;
+ }
+ *outp++ = (char) (count - 1); /* count */
+ memcpy(outp, inp, count); /* data */
+ outp += count;
+ }
+ *outp++ = '\201'; /* Repeat 128 times */
+ *outp++ = *p;
+ p += 128;
+ inp = p;
+ }
+ }
+
+ if( (q - p) > 2 ) { /* output this sequence */
+ if( p > inp ) {
+ count = p - inp;
+ while( count > 128 ) {
+ *outp++ = '\177';
+ memcpy(outp, inp, 128); /* data */
+ inp += 128;
+ outp += 128;
+ count -= 128;
+ }
+ *outp++ = (char) (count - 1); /* byte count */
+ memcpy(outp, inp, count); /* data */
+ outp += count;
+ }
+ count = q - p;
+ *outp++ = (char) (256 - count + 1);
+ *outp++ = *p;
+ p += count;
+ inp = p;
+ } else /* add to non-repeating data list */
+ p = q;
+ if( q < in_end )
+ q++;
+ }
+ }
+
+ /*
+ ** copy remaining part of line:
+ */
+
+ if( inp < in_end ) {
+
+ count = in_end - inp;
+
+ /*
+ ** If we've had a long run of varying data followed by a
+ ** sequence of repeated data and then hit the end of line,
+ ** it's possible to get data counts > 128.
+ */
+
+ while( count > 128 ) {
+ *outp++ = '\177';
+ memcpy(outp, inp, 128); /* data */
+ inp += 128;
+ outp += 128;
+ count -= 128;
+ }
+
+ *outp++ = (char) (count - 1); /* byte count */
+ memcpy(outp, inp, count); /* data */
+ outp += count;
+ }
+ }
+
+ /*
+ ** Output data:
+ */
+
+ fwrite("\033.\001", 1, 3, prn_stream);
+
+ if(pdev->y_pixels_per_inch == 360)
+ fputc('\012', prn_stream);
+ else
+ fputc('\024', prn_stream);
+
+ if(pdev->x_pixels_per_inch == 360)
+ fputc('\012', prn_stream);
+ else
+ fputc('\024', prn_stream);
+
+ fputc(band_size, prn_stream);
+
+ fputc((width << 3) & 0xff, prn_stream);
+ fputc( width >> 5, prn_stream);
+
+ fwrite(out, 1, (outp - out), prn_stream);
+
+ fwrite("\r\n", 1, 2, prn_stream);
+ lnum += band_size;
+ }
+
+ /* Eject the page and reinitialize the printer */
+
+ fputs("\f\033@", prn_stream);
+ fflush(prn_stream);
+
+ gs_free(pdev->memory, (char *)buf2, in_size, 1, "escp2_print_page(buf2)");
+ gs_free(pdev->memory, (char *)buf1, in_size, 1, "escp2_print_page(buf1)");
+ return 0;
+}
diff --git a/devices/gdevevga.c b/devices/gdevevga.c
new file mode 100644
index 000000000..483b684df
--- /dev/null
+++ b/devices/gdevevga.c
@@ -0,0 +1,114 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+/* IBM PC EGA and VGA display drivers */
+/* All of the real code is in gdevpcfb.c. */
+#include "memory_.h"
+#include "gx.h"
+#include "gserrors.h"
+#include "gxdevice.h"
+#include "gdevpcfb.h"
+
+/* ------ Internal routines ------ */
+
+/* We can't catch signals.... */
+void
+pcfb_set_signals(gx_device * dev)
+{
+}
+
+/* Read the device state */
+void
+pcfb_get_state(pcfb_bios_state * pbs)
+{
+ registers regs;
+
+ regs.h.ah = 0xf;
+ int86(0x10, &regs, &regs);
+ pbs->display_mode = regs.h.al;
+ pbs->text_page = regs.h.bh;
+ regs.h.ah = 0x3;
+ int86(0x10, &regs, &regs);
+ pbs->text_cursor_mode = regs.rshort.cx;
+ regs.rshort.ax = 0x1130;
+ regs.h.bh = 0;
+ int86(0x10, &regs, &regs);
+ switch (regs.rshort.cx) {
+ case 0x08:
+ pbs->text_font = 0x1112;
+ break; /* 8 x 8 */
+ case 0x10:
+ pbs->text_font = 0x1114;
+ break; /* 8 x 16 */
+ default:
+ pbs->text_font = 0x1111; /* 8 x 14 */
+ }
+ regs.h.ah = 0x8;
+ regs.h.bh = pbs->text_page;
+ int86(0x10, &regs, &regs);
+ pbs->text_attribute = regs.h.ah;
+ pbs->border_color = (regs.h.ah >> 4);
+ regs.rshort.ax = 0x1a00;
+ int86(0x10, &regs, &regs);
+ if (regs.h.al == 0x1a && regs.h.bl == 0x8) {
+ regs.rshort.ax = 0x1008;
+ int86(0x10, &regs, &regs);
+ pbs->border_color = regs.h.bh;
+ }
+ if (pbs->display_mode != 3) {
+ pbs->display_mode = 3;
+ pbs->text_font = 0x1112;
+ pbs->text_cursor_mode = 0x0607;
+ pbs->text_attribute = 7;
+ pbs->text_page = 0;
+ }
+}
+
+/* Set the device mode */
+void
+pcfb_set_mode(int mode)
+{
+ registers regs;
+
+ regs.h.ah = 0;
+ regs.h.al = mode;
+ int86(0x10, &regs, &regs);
+}
+
+/* Restore the device state */
+void
+pcfb_set_state(const pcfb_bios_state * pbs)
+{
+ registers regs;
+
+ pcfb_set_mode(pbs->display_mode);
+ regs.rshort.ax = 0x500; /* force display of page 0 */
+ int86(0x10, &regs, &regs);
+ regs.rshort.ax = pbs->text_font;
+ regs.h.bl = 0;
+ int86(0x10, &regs, &regs);
+ regs.h.ah = 0x3;
+ regs.h.bh = 0;
+ int86(0x10, &regs, &regs); /* Get cursor to reset MCGA */
+ regs.h.al = pbs->text_page;
+ regs.h.ah = 0x5;
+ int86(0x10, &regs, &regs);
+ regs.rshort.cx = pbs->text_cursor_mode;
+ regs.h.ah = 0x1;
+ int86(0x10, &regs, &regs);
+ regs.rshort.ax = 0x1001;
+ regs.h.bh = pbs->border_color;
+ int86(0x10, &regs, &regs);
+}
diff --git a/devices/gdevfax.c b/devices/gdevfax.c
new file mode 100644
index 000000000..143b57418
--- /dev/null
+++ b/devices/gdevfax.c
@@ -0,0 +1,314 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+/* Fax devices */
+#include "gdevprn.h"
+#include "strimpl.h"
+#include "scfx.h"
+#include "gdevfax.h"
+#include "minftrsz.h"
+
+/* The device descriptors */
+static dev_proc_print_page(faxg3_print_page);
+static dev_proc_print_page(faxg32d_print_page);
+static dev_proc_print_page(faxg4_print_page);
+
+/* Define procedures that adjust the paper size. */
+/* Since the print_page doesn't alter the device, this device can print in the background */
+const gx_device_procs gdev_fax_std_procs =
+ prn_params_procs(gdev_prn_open, gdev_prn_bg_output_page, gdev_prn_close,
+ gdev_fax_get_params, gdev_fax_put_params);
+
+#define FAX_DEVICE(dname, print_page)\
+{\
+ FAX_DEVICE_BODY(gx_device_fax, gdev_fax_std_procs, dname, print_page)\
+}
+
+const gx_device_fax gs_faxg3_device =
+ FAX_DEVICE("faxg3", faxg3_print_page);
+
+const gx_device_fax gs_faxg32d_device =
+ FAX_DEVICE("faxg32d", faxg32d_print_page);
+
+const gx_device_fax gs_faxg4_device =
+ FAX_DEVICE("faxg4", faxg4_print_page);
+
+/* Open the device. */
+/* This is no longer needed: we retain it for client backward compatibility. */
+int
+gdev_fax_open(gx_device * dev)
+{
+ return gdev_prn_open(dev);
+}
+
+/* Get/put the fax parameters: AdjustWidth and MinFeatureSize */
+int
+gdev_fax_get_params(gx_device * dev, gs_param_list * plist)
+{
+ gx_device_fax *const fdev = (gx_device_fax *)dev;
+ int code = gdev_prn_get_params(dev, plist);
+ int ecode = code;
+
+ if ((code = param_write_int(plist, "AdjustWidth", &fdev->AdjustWidth)) < 0)
+ ecode = code;
+ if ((code = param_write_int(plist, "MinFeatureSize", &fdev->MinFeatureSize)) < 0)
+ ecode = code;
+ return ecode;
+}
+int
+gdev_fax_put_params(gx_device * dev, gs_param_list * plist)
+{
+ gx_device_fax *const fdev = (gx_device_fax *)dev;
+ int ecode = 0;
+ int code;
+ int aw = fdev->AdjustWidth;
+ int mfs = fdev->MinFeatureSize;
+ const char *param_name;
+
+ switch (code = param_read_int(plist, (param_name = "AdjustWidth"), &aw)) {
+ case 0:
+ if (aw >= 0)
+ break;
+ code = gs_error_rangecheck;
+ default:
+ ecode = code;
+ param_signal_error(plist, param_name, ecode);
+ case 1:
+ break;
+ }
+
+ switch (code = param_read_int(plist, (param_name = "MinFeatureSize"), &mfs)) {
+ case 0:
+ if (mfs >= 0 && mfs <= 4)
+ break;
+ code = gs_error_rangecheck;
+ default:
+ ecode = code;
+ param_signal_error(plist, param_name, ecode);
+ case 1:
+ break;
+ }
+
+ if (ecode < 0)
+ return ecode;
+ code = gdev_prn_put_params(dev, plist);
+ if (code < 0)
+ return code;
+
+ fdev->AdjustWidth = aw;
+ fdev->MinFeatureSize = mfs;
+ return code;
+}
+
+/* Initialize the stream state with a set of default parameters. */
+/* These select the same defaults as the CCITTFaxEncode filter, */
+/* except we set BlackIs1 = true. */
+static void
+gdev_fax_init_state_adjust(stream_CFE_state *ss,
+ const gx_device_fax *fdev,
+ int adjust_width)
+{
+ s_CFE_template.set_defaults((stream_state *)ss);
+ ss->Columns = fdev->width;
+ ss->Rows = fdev->height;
+ ss->BlackIs1 = true;
+ ss->Columns = fax_adjusted_width(ss->Columns, adjust_width);
+}
+
+void
+gdev_fax_init_state(stream_CFE_state *ss, const gx_device_fax *fdev)
+{
+ gdev_fax_init_state_adjust(ss, fdev, 1);
+}
+void
+gdev_fax_init_fax_state(stream_CFE_state *ss, const gx_device_fax *fdev)
+{
+ gdev_fax_init_state_adjust(ss, fdev, fdev->AdjustWidth);
+}
+
+/*
+ * Print one strip with fax compression. Fax devices call this once per
+ * page; TIFF devices call this once per strip.
+ */
+int
+gdev_fax_print_strip(gx_device_printer * pdev, FILE * prn_stream,
+ const stream_template * temp, stream_state * ss,
+ int width, int row_first, int row_end /* last + 1 */)
+{
+ gs_memory_t *mem = pdev->memory;
+ int code;
+ stream_cursor_read r;
+ stream_cursor_write w;
+ int in_size = gdev_mem_bytes_per_scan_line((gx_device *) pdev);
+ /*
+ * Because of the width adjustment for fax systems, width may
+ * be different from (either greater than or less than) pdev->width.
+ * Allocate a large enough buffer to account for this.
+ */
+ int col_size = (width * pdev->color_info.depth + 7) >> 3;
+ int max_size = max(in_size, col_size);
+ int lnum = 0;
+ int row_in = row_first;
+ byte *in;
+ byte *out;
+ void *min_feature_data = NULL;
+ /* If the file is 'nul', don't even do the writes. */
+ bool nul = !strcmp(pdev->fname, "nul");
+ int lnum_in = row_in;
+ int min_feature_size = ((gx_device_fax *const)pdev)->MinFeatureSize;
+
+ /* Initialize the common part of the encoder state. */
+ ss->templat = temp;
+ ss->memory = mem;
+ /* Now initialize the encoder. */
+ code = temp->init(ss);
+ if (code < 0)
+ return_error(gs_error_limitcheck); /* bogus, but as good as any */
+
+ /* Allocate the buffers. */
+ in = gs_alloc_bytes(mem, temp->min_in_size + max_size + 1,
+ "gdev_stream_print_page(in)");
+#define OUT_SIZE 1000
+ out = gs_alloc_bytes(mem, OUT_SIZE, "gdev_stream_print_page(out)");
+ if (in == 0 || out == 0) {
+ code = gs_note_error(gs_error_VMerror);
+ goto done;
+ }
+ /* Init the min_feature_size expansion for entire image (not strip) */
+ if ((min_feature_size > 1) && (row_first == 0))
+ code = min_feature_size_init(mem, min_feature_size,
+ width, pdev->height, &min_feature_data);
+ if (min_feature_size > 1)
+ row_in = max(0, row_first-min_feature_size); /* need some data before this row */
+
+ /* Set up the processing loop. */
+ r.ptr = r.limit = in - 1;
+ w.ptr = out - 1;
+ w.limit = w.ptr + OUT_SIZE;
+#undef OUT_SIZE
+
+ /* Process the image. */
+ for (lnum = row_in; ;) {
+ int status;
+
+ if_debug7m('w', mem,
+ "[w]lnum=%d r=0x%lx,0x%lx,0x%lx w=0x%lx,0x%lx,0x%lx\n", lnum,
+ (ulong)in, (ulong)r.ptr, (ulong)r.limit,
+ (ulong)out, (ulong)w.ptr, (ulong)w.limit);
+ status = temp->process(ss, &r, &w, lnum == row_end);
+ if_debug7m('w', mem,
+ "...%d, r=0x%lx,0x%lx,0x%lx w=0x%lx,0x%lx,0x%lx\n", status,
+ (ulong)in, (ulong)r.ptr, (ulong)r.limit,
+ (ulong)out, (ulong)w.ptr, (ulong)w.limit);
+ switch (status) {
+ case 0: /* need more input data */
+ if (lnum == row_end)
+ goto ok;
+ {
+ uint left = r.limit - r.ptr;
+ int filtered_count = in_size;
+
+ memcpy(in, r.ptr + 1, left);
+ do {
+ if (lnum_in < row_end)
+ {
+ code = gdev_prn_copy_scan_lines(pdev, lnum_in++, in + left, in_size);
+ if (code < 0) {
+ code = gs_note_error(code);
+ goto done;
+ }
+ }
+ if (min_feature_size > 1)
+ filtered_count =
+ min_feature_size_process(in+left, min_feature_data);
+ } while (filtered_count == 0);
+ /* Note: we use col_size here, not in_size. */
+ lnum++;
+ if (col_size > in_size) {
+ memset(in + left + in_size, 0, col_size - in_size);
+ }
+ r.limit = in + left + col_size - 1;
+ r.ptr = in - 1;
+ }
+ break;
+ case 1: /* need to write output */
+ if (!nul)
+ fwrite(out, 1, w.ptr + 1 - out, prn_stream);
+ w.ptr = out - 1;
+ break;
+ }
+ }
+
+ ok:
+ /* Write out any remaining output. */
+ if (!nul)
+ fwrite(out, 1, w.ptr + 1 - out, prn_stream);
+
+ done:
+ if ((min_feature_size > 1) && (lnum == pdev->height))
+ min_feature_size_dnit(min_feature_data);
+ gs_free_object(mem, out, "gdev_stream_print_page(out)");
+ gs_free_object(mem, in, "gdev_stream_print_page(in)");
+ if (temp->release)
+ temp->release(ss);
+ return code;
+}
+
+/* Print a fax page. Other fax drivers use this. */
+int
+gdev_fax_print_page(gx_device_printer * pdev, FILE * prn_stream,
+ stream_CFE_state * ss)
+{
+ return gdev_fax_print_strip(pdev, prn_stream, &s_CFE_template,
+ (stream_state *)ss, ss->Columns,
+ 0, pdev->height);
+}
+
+/* Print a 1-D Group 3 page. */
+static int
+faxg3_print_page(gx_device_printer * pdev, FILE * prn_stream)
+{
+ stream_CFE_state state;
+
+ gdev_fax_init_fax_state(&state, (gx_device_fax *)pdev);
+ state.EndOfLine = true;
+ state.EndOfBlock = false;
+ return gdev_fax_print_page(pdev, prn_stream, &state);
+}
+
+/* Print a 2-D Group 3 page. */
+static int
+faxg32d_print_page(gx_device_printer * pdev, FILE * prn_stream)
+{
+ stream_CFE_state state;
+
+ gdev_fax_init_fax_state(&state, (gx_device_fax *)pdev);
+ state.K = (pdev->y_pixels_per_inch < 100 ? 2 : 4);
+ state.EndOfLine = true;
+ state.EndOfBlock = false;
+ return gdev_fax_print_page(pdev, prn_stream, &state);
+}
+
+/* Print a Group 4 page. */
+static int
+faxg4_print_page(gx_device_printer * pdev, FILE * prn_stream)
+{
+ stream_CFE_state state;
+
+ gdev_fax_init_fax_state(&state, (gx_device_fax *)pdev);
+ state.K = -1;
+ state.EndOfBlock = false;
+ return gdev_fax_print_page(pdev, prn_stream, &state);
+}
diff --git a/devices/gdevfax.h b/devices/gdevfax.h
new file mode 100644
index 000000000..1aeea75f5
--- /dev/null
+++ b/devices/gdevfax.h
@@ -0,0 +1,64 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+/* Definitions and interface for fax devices */
+
+#ifndef gdevfax_INCLUDED
+# define gdevfax_INCLUDED
+
+/* Define the default device parameters. */
+#define X_DPI 204
+#define Y_DPI 196
+
+/* Define the structure for fax devices. */
+/* Precede this by gx_device_common and gx_prn_device_common. */
+#define gx_fax_device_common\
+ int AdjustWidth; /* 0 = no adjust, 1 = adjust to fax values */\
+ int MinFeatureSize /* < 2 == no darkening */
+typedef struct gx_device_fax_s {
+ gx_device_common;
+ gx_prn_device_common;
+ gx_fax_device_common;
+} gx_device_fax;
+
+#define FAX_DEVICE_BODY(dtype, procs, dname, print_page)\
+ prn_device_std_body(dtype, procs, dname,\
+ DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,\
+ X_DPI, Y_DPI,\
+ 0, 0, 0, 0, /* margins */\
+ 1, print_page),\
+ 1, /* AdjustWidth */\
+ 0 /* MinFeatureSize */
+
+/* Procedures defined in gdevfax.c */
+
+/* Driver procedures */
+dev_proc_open_device(gdev_fax_open);
+dev_proc_get_params(gdev_fax_get_params); /* adds AdjustWidth, MinFeatureSize */
+dev_proc_put_params(gdev_fax_put_params); /* adds AdjustWidth, MinFeatureSize */
+extern const gx_device_procs gdev_fax_std_procs;
+
+/* Other procedures */
+void gdev_fax_init_state(stream_CFE_state *ss, const gx_device_fax *fdev);
+void gdev_fax_init_fax_state(stream_CFE_state *ss,
+ const gx_device_fax *fdev);
+int gdev_fax_print_strip(gx_device_printer * pdev, FILE * prn_stream,
+ const stream_template * temp, stream_state * ss,
+ int width, int row_first,
+ int row_end /* last + 1 */);
+int gdev_fax_print_page(gx_device_printer *pdev, FILE *prn_stream,
+ stream_CFE_state *ss);
+
+#endif /* gdevfax_INCLUDED */
diff --git a/devices/gdevfpng.c b/devices/gdevfpng.c
new file mode 100644
index 000000000..84cb68ab1
--- /dev/null
+++ b/devices/gdevfpng.c
@@ -0,0 +1,457 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* PNG (Portable Network Graphics) Format. Pronounced "ping". */
+
+#include "gdevprn.h"
+#include "gdevmem.h"
+#include "gscdefs.h"
+#include "gxgetbit.h"
+#include "zlib.h"
+#include "gxdownscale.h"
+#include "gxdevsop.h"
+
+/* ------ The device descriptors ------ */
+
+/*
+ * Default X and Y resolution.
+ */
+#define X_DPI 72
+#define Y_DPI 72
+
+static dev_proc_print_page(fpng_print_page);
+
+typedef struct gx_device_fpng_s gx_device_fpng;
+struct gx_device_fpng_s {
+ gx_device_common;
+ gx_prn_device_common;
+ int downscale_factor;
+};
+
+static int
+fpng_get_param(gx_device *dev, char *Param, void *list)
+{
+ gx_device_fpng *pdev = (gx_device_fpng *)dev;
+ gs_param_list * plist = (gs_param_list *)list;
+
+ if (strcmp(Param, "DownScaleFactor") == 0) {
+ if (pdev->downscale_factor < 1)
+ pdev->downscale_factor = 1;
+ return param_write_int(plist, "DownScaleFactor", &pdev->downscale_factor);
+ }
+ return gdev_prn_get_param(dev, Param, list);
+}
+
+static int
+fpng_get_params(gx_device * dev, gs_param_list * plist)
+{
+ gx_device_fpng *pdev = (gx_device_fpng *)dev;
+ int code, ecode;
+
+ ecode = 0;
+ if (pdev->downscale_factor < 1)
+ pdev->downscale_factor = 1;
+ if ((code = param_write_int(plist, "DownScaleFactor", &pdev->downscale_factor)) < 0)
+ ecode = code;
+
+ code = gdev_prn_get_params(dev, plist);
+ if (code < 0)
+ ecode = code;
+
+ return ecode;
+}
+
+static int
+fpng_put_params(gx_device *dev, gs_param_list *plist)
+{
+ gx_device_fpng *pdev = (gx_device_fpng *)dev;
+ int code, ecode;
+ int dsf = pdev->downscale_factor;
+ const char *param_name;
+
+ ecode = 0;
+ switch (code = param_read_int(plist, (param_name = "DownScaleFactor"), &dsf)) {
+ case 0:
+ if (dsf >= 1)
+ break;
+ code = gs_error_rangecheck;
+ default:
+ ecode = code;
+ param_signal_error(plist, param_name, ecode);
+ case 1:
+ break;
+ }
+
+ code = gdev_prn_put_params(dev, plist);
+ if (code < 0)
+ ecode = code;
+
+ pdev->downscale_factor = dsf;
+
+ return ecode;
+}
+
+static int
+fpng_dev_spec_op(gx_device *pdev, int dev_spec_op, void *data, int size)
+{
+ gx_device_fpng *fdev = (gx_device_fpng *)pdev;
+
+ if (dev_spec_op == gxdso_adjust_bandheight)
+ return gx_downscaler_adjust_bandheight(fdev->downscale_factor, size);
+
+ if (dev_spec_op == gxdso_get_dev_param) {
+ int code;
+ dev_param_req_t *request = (dev_param_req_t *)data;
+ code = fpng_get_param(pdev, request->Param, request->list);
+ if (code != gs_error_undefined)
+ return code;
+ }
+
+ return gdev_prn_dev_spec_op(pdev, dev_spec_op, data, size);
+}
+
+/* 24-bit color. */
+
+/* Since the print_page doesn't alter the device, this device can print in the background */
+static const gx_device_procs fpng_procs =
+{
+ gdev_prn_open,
+ NULL, /* get_initial_matrix */
+ NULL, /* sync_output */
+ gdev_prn_bg_output_page,
+ gdev_prn_close,
+ gx_default_rgb_map_rgb_color,
+ gx_default_rgb_map_color_rgb,
+ NULL, /* fill_rectangle */
+ NULL, /* tile_rectangle */
+ NULL, /* copy_mono */
+ NULL, /* copy_color */
+ NULL, /* draw_line */
+ NULL, /* get_bits */
+ fpng_get_params,
+ fpng_put_params,
+ NULL, /* map_cmyk_color */
+ NULL, /* get_xfont_procs */
+ NULL, /* get_xfont_device */
+ NULL, /* map_rgb_alpha_color */
+ gx_page_device_get_page_device,
+ NULL, /* get_alpha_bits */
+ NULL, /* copy_alpha */
+ NULL, /* get_band */
+ NULL, /* copy_rop */
+ NULL, /* fill_path */
+ NULL, /* stroke_path */
+ NULL, /* fill_mask */
+ NULL, /* fill_trapezoid */
+ NULL, /* fill_parallelogram */
+ NULL, /* fill_triangle */
+ NULL, /* draw_thin_line */
+ NULL, /* begin_image */
+ NULL, /* image_data */
+ NULL, /* end_image */
+ NULL, /* strip_tile_rectangle */
+ NULL, /* strip_copy_rop, */
+ NULL, /* get_clipping_box */
+ NULL, /* begin_typed_image */
+ NULL, /* get_bits_rectangle */
+ NULL, /* map_color_rgb_alpha */
+ NULL, /* create_compositor */
+ NULL, /* get_hardware_params */
+ NULL, /* text_begin */
+ NULL, /* finish_copydevice */
+ NULL, /* begin_transparency_group */
+ NULL, /* end_transparency_group */
+ NULL, /* begin_transparency_mask */
+ NULL, /* end_transparency_mask */
+ NULL, /* discard_transparency_layer */
+ NULL, /* get_color_mapping_procs */
+ NULL, /* get_color_comp_index */
+ NULL, /* encode_color */
+ NULL, /* decode_color */
+ NULL, /* pattern_manage */
+ NULL, /* fill_rectangle_hl_color */
+ NULL, /* include_color_space */
+ NULL, /* fill_linear_color_scanline */
+ NULL, /* fill_linear_color_trapezoid */
+ NULL, /* fill_linear_color_triangle */
+ NULL, /* update_spot_equivalent_colors */
+ NULL, /* ret_devn_params */
+ NULL, /* fillpage */
+ NULL, /* push_transparency_state */
+ NULL, /* pop_transparency_state */
+ NULL, /* put_image */
+ fpng_dev_spec_op, /* dev_spec_op */
+ NULL, /* copy plane */
+ gx_default_get_profile, /* get_profile */
+ gx_default_set_graphics_type_tag /* set_graphics_type_tag */
+};
+const gx_device_fpng gs_fpng_device =
+{prn_device_body(gx_device_fpng, fpng_procs, "fpng",
+ DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
+ X_DPI, Y_DPI,
+ 0, 0, 0, 0, /* margins */
+ 3, 24, 255, 255, 256, 256, fpng_print_page),
+};
+
+/* ------ Private definitions ------ */
+
+typedef struct fpng_buffer_s {
+ int size;
+ int compressed;
+ unsigned char data[1];
+} fpng_buffer_t;
+
+static int fpng_init_buffer(void *arg, gx_device *dev, gs_memory_t *mem, int w, int h, void **pbuffer)
+{
+ /* Currently, we allocate a "worst case" buffer per band - this is
+ * slightly larger than the band itself. We do this, so that in banded
+ * mode, we never need to reallocate the buffer during operation.
+ * For paged mode, we don't care so much about not reallocating - we
+ * could spot paged mode by noting that w and h match that of the
+ * device (or that of the device after downscaling at least), and then
+ * allocate a smaller initial buffer. We could even output as we go
+ * in paged mode. For now we leave this as an exercise for the reader.
+ */
+ fpng_buffer_t *buffer;
+ int size = deflateBound(NULL, (w*3+1)*h);
+ buffer = (fpng_buffer_t *)gs_alloc_bytes(mem, sizeof(fpng_buffer_t) + size, "fpng_init_buffer");
+ *pbuffer = (void *)buffer;
+ if (buffer == NULL)
+ return gs_error_VMerror;
+ buffer->size = size;
+ buffer->compressed = 0;
+ return 0;
+}
+
+static void fpng_free_buffer(void *arg, gx_device *dev, gs_memory_t *mem, void *buffer)
+{
+ gs_free_object(mem, buffer, "fpng_init_buffer");
+}
+
+static void big32(unsigned char *buf, unsigned int v)
+{
+ buf[0] = (v >> 24) & 0xff;
+ buf[1] = (v >> 16) & 0xff;
+ buf[2] = (v >> 8) & 0xff;
+ buf[3] = (v) & 0xff;
+}
+
+static void write_big32(int v, FILE *file)
+{
+ fputc(v>>24, file);
+ fputc(v>>16, file);
+ fputc(v>>8, file);
+ fputc(v>>0, file);
+}
+
+static void putchunk(const char *tag, const unsigned char *data, int size, FILE *file)
+{
+ unsigned int sum;
+ write_big32(size, file);
+ fwrite(tag, 1, 4, file);
+ fwrite(data, 1, size, file);
+ sum = crc32(0, NULL, 0);
+ sum = crc32(sum, (const unsigned char*)tag, 4);
+ sum = crc32(sum, data, size);
+ write_big32(sum, file);
+}
+
+static void *zalloc(void *mem_, unsigned int items, unsigned int size)
+{
+ gs_memory_t *mem = (gs_memory_t *)mem_;
+
+ return gs_alloc_bytes(mem, items * size, "zalloc (fpng_process)");
+}
+
+static void zfree(void *mem_, void *address)
+{
+ gs_memory_t *mem = (gs_memory_t *)mem_;
+
+ gs_free_object(mem, address, "zfree (fpng_process)");
+}
+
+static inline int paeth_predict(const unsigned char *d, int raster)
+{
+ int a = d[-3]; /* Left */
+ int b = d[-raster]; /* Above */
+ int c = d[-3-raster]; /* Above left */
+ int p = a + b - c;
+ int pa, pb, pc;
+ pa = p - a;
+ if (pa < 0)
+ pa = -pa;
+ pb = p - b;
+ if (pb < 0)
+ pb = -pb;
+ pc = p - c;
+ if (pc < 0)
+ pc = -pc;
+ if (pa <= pb && pa <= pc)
+ return a;
+ if (pb <= pc)
+ return b;
+ return c;
+}
+
+static int fpng_process(void *arg, gx_device *dev, gx_device *bdev, const gs_int_rect *rect, void *buffer_)
+{
+ int code;
+ gx_device_fpng *fdev = (gx_device_fpng *)dev;
+ gs_get_bits_params_t params;
+ int w = rect->q.x - rect->p.x;
+ int raster = bitmap_raster(bdev->width * 3 * 8);
+ int h = rect->q.y - rect->p.y;
+ int x, y;
+ unsigned char *p;
+ unsigned char sub = 1;
+ unsigned char paeth = 4;
+ int firstband = (rect->p.y == 0);
+ int lastband;
+ gs_int_rect my_rect;
+ z_stream stream;
+ int err;
+ int page_height = gx_downscaler_scale_rounded(dev->height, fdev->downscale_factor);
+ fpng_buffer_t *buffer = (fpng_buffer_t *)buffer_;
+
+ if (h <= 0 || w <= 0)
+ return 0;
+
+ lastband = (rect->q.y == page_height-1);
+
+ params.options = GB_COLORS_NATIVE | GB_ALPHA_NONE | GB_PACKING_CHUNKY | GB_RETURN_POINTER | GB_ALIGN_ANY | GB_OFFSET_0 | GB_RASTER_ANY;
+ my_rect.p.x = 0;
+ my_rect.p.y = 0;
+ my_rect.q.x = w;
+ my_rect.q.y = h;
+ code = dev_proc(bdev, get_bits_rectangle)(bdev, &my_rect, &params, NULL);
+ if (code < 0)
+ return code;
+
+ /* Apply the paeth prediction filter to the buffered data */
+ p = params.data[0];
+ /* Paeth for lines h-1 to 1 */
+ p += raster*(h-1);
+ for (y = h-1; y > 0; y--)
+ {
+ p += 3*(w-1);
+ for (x = w-1; x > 0; x--)
+ {
+ p[0] -= paeth_predict(p+0, raster);
+ p[1] -= paeth_predict(p+1, raster);
+ p[2] -= paeth_predict(p+2, raster);
+ p -= 3;
+ }
+ p[0] -= p[-raster];
+ p[1] -= p[1-raster];
+ p[2] -= p[2-raster];
+ p -= raster;
+ }
+ /* Sub for the first line */
+ {
+ p += 3*(w-1);
+ for (x = w-1; x > 0; x--)
+ {
+ p[2] -= p[-1];
+ p[1] -= p[-2];
+ p[0] -= p[-3];
+ p -= 3;
+ }
+ }
+
+ /* Compress the data */
+ stream.zalloc = zalloc;
+ stream.zfree = zfree;
+ stream.opaque = bdev->memory;
+ err = deflateInit(&stream, Z_DEFAULT_COMPRESSION);
+ if (err != Z_OK)
+ return gs_error_VMerror;
+ p = params.data[0];
+ stream.next_out = &buffer->data[0];
+ stream.avail_out = buffer->size;
+
+ /* Nasty zlib hackery here. Zlib always outputs a 'start of stream'
+ * marker at the beginning. We just want a block, so for all blocks
+ * except the first one, we compress a single byte and flush it. This
+ * takes care of the 'start of stream' marker. Throw this deflate block
+ * away, and start compression again. */
+ if (!firstband)
+ {
+ stream.next_in = &sub;
+ stream.avail_in = 1;
+ deflate(&stream, Z_FULL_FLUSH);
+ stream.next_out = &buffer->data[0];
+ stream.avail_out = buffer->size;
+ stream.total_out = 0;
+ }
+
+ stream.next_in = &sub;
+ for (y = h-1; y >= 0; y--)
+ {
+ stream.avail_in = 1;
+ deflate(&stream, Z_NO_FLUSH);
+ stream.next_in = p;
+ stream.avail_in = w*3;
+ deflate(&stream, (y == 0 ? (lastband ? Z_FINISH : Z_FULL_FLUSH) : Z_NO_FLUSH));
+ p += raster;
+ stream.next_in = &paeth;
+ }
+ /* Ignore errors given here */
+ deflateEnd(&stream);
+
+ buffer->compressed = stream.total_out;
+
+ return code;
+}
+
+static int fpng_output(void *arg, gx_device *dev, void *buffer_)
+{
+ FILE *file = (FILE *)arg;
+ fpng_buffer_t *buffer = (fpng_buffer_t *)buffer_;
+
+ putchunk("IDAT", &buffer->data[0], buffer->compressed, file);
+
+ return 0;
+}
+
+/* Write out a page in PNG format. */
+static int
+fpng_print_page(gx_device_printer *pdev, FILE *file)
+{
+ gx_device_fpng *fdev = (gx_device_fpng *)pdev;
+ static const unsigned char pngsig[8] = { 137, 80, 78, 71, 13, 10, 26, 10 };
+ unsigned char head[13];
+ gx_process_page_options_t process = { 0 };
+
+ fwrite(pngsig, 1, 8, file); /* Signature */
+
+ /* IHDR chunk */
+ big32(&head[0], gx_downscaler_scale_rounded(pdev->width, fdev->downscale_factor));
+ big32(&head[4], gx_downscaler_scale_rounded(pdev->height, fdev->downscale_factor));
+ head[8] = 8; /* 8bpc */
+ head[9] = 2; /* rgb */
+ head[10] = 0; /* compression */
+ head[11] = 0; /* filter */
+ head[12] = 0; /* interlace */
+ putchunk("IHDR", head, 13, file);
+
+ process.init_buffer_fn = fpng_init_buffer;
+ process.free_buffer_fn = fpng_free_buffer;
+ process.process_fn = fpng_process;
+ process.output_fn = fpng_output;
+ process.arg = file;
+
+ return gx_downscaler_process_page((gx_device *)pdev, &process, fdev->downscale_factor);
+}
diff --git a/devices/gdevherc.c b/devices/gdevherc.c
new file mode 100644
index 000000000..f02cefe64
--- /dev/null
+++ b/devices/gdevherc.c
@@ -0,0 +1,473 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+/* IBM PC-compatible Hercules Graphics display driver */
+/* using direct access to frame buffer */
+
+#define FB_RASTER 90
+#define SCREEN_HEIGHT 350
+#define SCREEN_ASPECT_RATIO (54.0/35.0)
+#define VIDEO_MODE 0x07
+#define regen 0xb0000000L
+
+#define interrupt /* patch ANSI incompatibility */
+#include "dos_.h"
+typedef union REGS registers;
+#include "gx.h"
+#include "gsmatrix.h" /* for gxdevice.h */
+#include "gxbitmap.h"
+#include "gxdevice.h"
+
+/* outportb is defined in dos_.h */
+#define outport2(port, index, data)\
+ (outportb(port, index), outportb((port)+1, data))
+/* Define the nominal page height in inches. */
+#ifdef A4
+# define PAGE_HEIGHT_INCHES 11.69
+#else
+# define PAGE_HEIGHT_INCHES 11.0
+#endif
+
+/* Dimensions of screen */
+#define screen_size_x (FB_RASTER * 8)
+#define screen_size_y SCREEN_HEIGHT
+/* Other display parameters */
+#define raster_x FB_RASTER
+#define aspect_ratio SCREEN_ASPECT_RATIO
+#define graphics_video_mode VIDEO_MODE
+
+/* Procedures */
+
+ /* See gxdevice.h for the definitions of the procedures. */
+
+dev_proc_open_device(herc_open);
+dev_proc_close_device(herc_close);
+dev_proc_fill_rectangle(herc_fill_rectangle);
+dev_proc_copy_mono(herc_copy_mono);
+dev_proc_copy_color(herc_copy_color);
+
+/* The device descriptor */
+static gx_device_procs herc_procs = {
+ herc_open,
+ gx_default_get_initial_matrix,
+ gx_default_sync_output,
+ gx_default_output_page,
+ herc_close,
+ gx_default_map_rgb_color,
+ gx_default_map_color_rgb,
+ herc_fill_rectangle,
+ gx_default_tile_rectangle,
+ herc_copy_mono,
+ herc_copy_color
+};
+
+gx_device far_data gs_herc_device = {
+ std_device_std_body(gx_device, &herc_procs, "herc",
+ screen_size_x, screen_size_y,
+ /* The following parameters map an appropriate fraction of */
+ /* the screen to a full-page coordinate space. */
+ /* This may or may not be what is desired! */
+ (screen_size_y * aspect_ratio) / PAGE_HEIGHT_INCHES, /* x dpi */
+ screen_size_y / PAGE_HEIGHT_INCHES /* y dpi */
+ )
+};
+
+/* Forward declarations */
+static int herc_get_mode(void);
+static void herc_set_mode(int);
+
+/* Save the HERC mode */
+static int herc_save_mode = -1;
+
+/* Reinitialize the herc for text mode */
+int
+herc_close(gx_device *dev)
+{ if ( herc_save_mode >= 0 ) herc_set_mode(herc_save_mode);
+ return 0;
+}
+
+/* ------ Internal routines ------ */
+
+/* Read the device mode */
+static int
+herc_get_mode(void)
+{ registers regs;
+ regs.h.ah = 0xf;
+ int86(0x10, &regs, &regs);
+ return regs.h.al;
+}
+
+/* Set the device mode */
+static void
+herc_set_mode(int mode)
+{ registers regs;
+ regs.h.ah = 0;
+ regs.h.al = mode;
+ int86(0x10, &regs, &regs);
+}
+
+/****************************************************************/
+/* Hercules graphics card functions */
+/* */
+/* -- Taken from Jan/Feb 1988 issue of Micro Cornucopia #39 */
+/* */
+/* --rewritten for MSC 5.1 on 02/18/91 by Phillip Conrad */
+/****************************************************************/
+
+static const char paramg[12] = {0x35, 0x2d, 0x2e, 0x07, 0x5b, 0x02,
+ 0x57, 0x57, 0x02, 0x03, 0x00, 0x00};
+/* (Never used)
+static const char paramt[12] = {0x61, 0x50, 0x52, 0x0f, 0x19, 0x06,
+ 0x19, 0x19, 0x02, 0x0d, 0x0b, 0x0c};
+*/
+
+/* Type and macro for frame buffer pointers. */
+/*** Intimately tied to the 80x86 (x<2) addressing architecture. ***/
+typedef byte far *fb_ptr;
+# define mk_fb_ptr(x, y)\
+ (fb_ptr)((regen) + ((0x2000 * ((y) % 4) + (90 * ((y) >> 2))) + ((int)(x) >> 3)))
+
+/* Structure for operation parameters. */
+/* Note that this structure is known to assembly code. */
+/* Not all parameters are used for every operation. */
+typedef struct rop_params_s {
+ fb_ptr dest; /* pointer to frame buffer */
+ int draster; /* raster of frame buffer */
+ const byte far *src; /* pointer to source data */
+ int sraster; /* source raster */
+ int width; /* width in bytes */
+ int height; /* height in scan lines */
+ int shift; /* amount to right shift source */
+ int invert; /* 0 or -1 to invert source */
+ int data; /* data for fill */
+ int x_pos; /*>>added--2/24/91 */
+ int y_pos;
+} rop_params;
+
+/* Define the device port and register numbers, and the regen map base */
+#define seq_addr 0x3b4 /* changed for HERC card (6845 ports)*/
+#define graph_mode 0x3b8
+#define graph_stat 0x3ba
+#define graph_config 0x3bf
+
+#ifndef regen
+#define regen 0xa0000000L
+#endif
+
+/* Initialize the display for Hercules graphics mode */
+int
+herc_open(gx_device *dev)
+{ int i;
+ if ( herc_save_mode < 0 ) herc_save_mode = herc_get_mode();
+/* herc_set_mode(graphics_video_mode); */
+ outportb(graph_config,3);
+ for(i=0;i<sizeof(paramg);i++)
+ {
+ outport2(seq_addr,i,paramg[i]);
+
+ }
+ outportb(graph_mode,0x0a); /* set page 0 */
+ for(i=0;i<0x3FFFL;i++) /* clear the screen */
+ {
+ int far *loc = (int far *)( regen +(2L*i));
+ *loc = 0;
+ }
+
+ return 0;
+}
+
+/* Macro for testing bit-inclusion */
+#define bit_included_in(x,y) !((x)&~(y))
+
+/* Copy a monochrome bitmap. The colors are given explicitly. */
+/* Color = gx_no_color_index means transparent (no effect on the image). */
+int
+herc_copy_mono(gx_device *dev,
+ const byte *base, int sourcex, int raster, gx_bitmap_id id,
+ int x, int y, int w, int h, gx_color_index izero, gx_color_index ione)
+{ rop_params params;
+#define czero (int)izero
+#define cone (int)ione
+ int dleft, sleft, count;
+ int invert, zmask, omask;
+ byte mask, rmask;
+
+ if ( cone == czero ) /* vacuous case */
+ return herc_fill_rectangle(dev, x, y, w, h, izero);
+
+ /* clip */
+ fit_copy(dev, base, sourcex, raster, id, x, y, w, h);
+ params.dest = mk_fb_ptr(x, y);
+ params.draster = raster_x;
+ params.src = base + (sourcex >> 3);
+ params.sraster = raster;
+ params.height = h;
+ params.shift = (x - sourcex) & 7;
+ params.y_pos = y;
+ params.x_pos = x;
+ params.width = w;
+
+ if(czero > cone) params.invert = -1;
+
+ /* Macros for writing partial bytes. */
+ /* bits has already been inverted by xor'ing with invert. */
+
+#define write_byte_masked(ptr, bits, mask)\
+ *ptr = ((bits | ~mask | zmask) & (*ptr | (bits & mask & omask)))
+
+#define write_byte(ptr, bits)\
+ *ptr = ((bits | zmask) & (*ptr | (bits & omask)))
+
+ invert = (czero == 1 || cone == 0 ? -1 : 0);
+/* invert = (czero == 1 || cone == 1 ? -1 : 0); */
+ zmask = (czero == 0 || cone == 0 ? 0 : -1);
+ omask = (czero == 1 || cone == 1 ? -1 : 0);
+
+#undef czero
+#undef cone
+
+ /* Actually copy the bits. */
+
+ sleft = 8 - (sourcex & 7);
+ dleft = 8 - (x & 7);
+ mask = 0xff >> (8 - dleft);
+ count = w;
+ if ( w < dleft )
+ mask -= mask >> w,
+ rmask = 0;
+ else
+ rmask = 0xff00 >> ((w - dleft) & 7);
+
+ if (sleft == dleft) /* optimize the aligned case */
+ {
+ w -= dleft;
+ while ( --h >= 0 )
+ {
+ register const byte *bptr = params.src;
+ register byte *optr = mk_fb_ptr(params.x_pos,params.y_pos);
+ register int bits = *bptr ^ invert; /* first partial byte */
+
+ count = w;
+
+ write_byte_masked(optr, bits, mask);
+
+ /* Do full bytes. */
+
+ while ((count -= 8) >= 0)
+ {
+ bits = *++bptr ^ invert;
+ params.x_pos += 8;
+ optr = mk_fb_ptr(params.x_pos,params.y_pos);
+ write_byte(optr, bits);
+ }
+ /* Do last byte */
+
+ if (count > -8)
+ {
+ bits = *++bptr ^ invert;
+ params.x_pos += 8;
+ optr = mk_fb_ptr(params.x_pos,params.y_pos);
+ write_byte_masked(optr, bits, rmask);
+ }
+/* dest += BPL; */
+ params.y_pos++;
+ params.x_pos = x;
+ params.src += raster;
+ }
+ }
+ else
+ {
+ int skew = (sleft - dleft) & 7;
+ int cskew = 8 - skew;
+
+ while (--h >= 0)
+ {
+ const byte *bptr = params.src;
+ byte *optr = mk_fb_ptr(params.x_pos,params.y_pos);
+ register int bits;
+
+ count = w;
+
+ /* Do the first partial byte */
+
+ if (sleft >= dleft)
+ {
+ bits = *bptr >> skew;
+ }
+ else /* ( sleft < dleft ) */
+ {
+ bits = *bptr++ << cskew;
+ if (count > sleft)
+ bits += *bptr >> skew;
+ }
+ bits ^= invert;
+ write_byte_masked(optr, bits, mask);
+ count -= dleft;
+ params.x_pos += 8;
+ optr = mk_fb_ptr(params.x_pos,params.y_pos);
+
+ /* Do full bytes. */
+
+ while ( count >= 8 )
+ {
+ bits = *bptr++ << cskew;
+ bits += *bptr >> skew;
+ bits ^= invert;
+ write_byte(optr, bits);
+ count -= 8;
+ params.x_pos += 8;
+ optr = mk_fb_ptr(params.x_pos,params.y_pos);
+ }
+
+ /* Do last byte */
+
+ if (count > 0)
+ {
+ bits = *bptr++ << cskew;
+ if (count > skew)
+ bits += *bptr >> skew;
+ bits ^= invert;
+ write_byte_masked(optr, bits, rmask);
+ }
+/* dest += BPL;
+ line += raster;
+*/
+ params.y_pos++;
+ params.x_pos = x;
+ params.src += raster;
+ }
+ }
+ return 0;
+}
+
+/* Copy a color pixelmap. This is just like a bitmap, */
+int
+herc_copy_color(gx_device *dev,
+ const byte *base, int sourcex, int raster, gx_bitmap_id id,
+ int x, int y, int w, int h)
+{ return herc_copy_mono(dev, base, sourcex, raster, id,
+ x, y, w, h,(gx_color_index)0, (gx_color_index)1);
+}
+
+# define mk_fb_yptr(x, y)\
+ (fb_ptr)((regen) + ((0x2000 * ((y) % 4) + (90 * ((y) >> 2))) + x))
+
+/* Fill a rectangle. */
+int
+herc_fill_rectangle(gx_device *dev, int x, int y, int w, int h,
+ gx_color_index color)
+{ rop_params params;
+
+ int x2, y2, xlen;
+ byte led, red, d;
+ byte far *ptr;
+ int xloc;
+
+ fit_fill(dev, x, y, w, h);
+
+ params.dest = mk_fb_ptr(x, y);
+ params.y_pos = y;
+ params.x_pos = x;
+
+ x2 = x + w - 1;
+ y2 = y + h - 1;
+
+ xlen = (x2 >> 3) - (x >> 3) - 1;
+ led = 0xff >> (x & 7);
+ red = 0xff << (7 - (x2 & 7));
+
+ ptr = mk_fb_ptr(x,y);
+
+ if (color)
+ {
+ /* here to set pixels */
+
+ if (xlen == -1)
+ {
+ /* special for rectangles that fit in a byte */
+
+ d = led & red;
+ for(; h >= 0; h--, ptr = mk_fb_ptr(x,params.y_pos))
+ {
+ *ptr |= d;
+ params.y_pos++;
+ }
+ return 0;
+ }
+
+ /* normal fill */
+
+ xloc = params.x_pos >> 3;
+ for(; h >= 0; h--, ptr = mk_fb_ptr(x,params.y_pos))
+ { register int x_count = xlen;
+ register byte far *p = ptr;
+ *p |= led;
+/* params.x_pos += 8; */
+ xloc++;
+ p = mk_fb_yptr(xloc,params.y_pos);
+ while ( x_count-- ) {
+ *p = 0xff;
+/* params.x_pos += 8; */
+ xloc++;
+ p = mk_fb_yptr(xloc,params.y_pos);
+ }
+ *p |= red;
+/* params.x_pos = x; */
+ xloc = params.x_pos >> 3;
+ params.y_pos++;
+ }
+ }
+
+ /* here to clear pixels */
+
+ led = ~led;
+ red = ~red;
+
+ if (xlen == -1)
+ {
+ /* special for rectangles that fit in a byte */
+
+ d = led | red;
+ for(; h >= 0; h--, ptr = mk_fb_ptr(x,params.y_pos))
+ {
+ *ptr &= d;
+ params.y_pos++;
+ }
+ return 0;
+ }
+
+ /* normal fill */
+
+ xloc = x >> 3;
+ for(; h >= 0; h--, ptr = mk_fb_ptr(x,params.y_pos))
+ { register int x_count = xlen;
+ register byte far *p = ptr;
+ *p &= led;
+/* params.x_pos += 8; */
+ xloc++;
+ p = mk_fb_yptr(xloc,params.y_pos);
+ while ( x_count-- ) {
+ *p = 0x00;
+/* params.x_pos += 8; */
+ xloc++;
+ p = mk_fb_yptr(xloc,params.y_pos);
+ }
+ *p &= red;
+/* params.x_pos = x; */
+ xloc = params.x_pos >> 3;
+ params.y_pos++;
+ }
+ return 0;
+}
diff --git a/devices/gdevhl7x.c b/devices/gdevhl7x.c
new file mode 100644
index 000000000..9992355a8
--- /dev/null
+++ b/devices/gdevhl7x.c
@@ -0,0 +1,1016 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+/*
+ * Brother HL 720 and 730 driver for Ghostscript
+ *
+ * Note: for the HL 760, use the HP driver.
+ *
+ * The original code was borrowed from the
+ * HP LaserJet/DeskJet driver for Ghostscript.
+ * The code specific to the Brother HL 720 was written by :
+ * Pierre-Olivier Gaillard (pierre.gaillard@hol.fr)
+ * Thanks to the documentation kindly provided by :
+ * Richard Thomas <RICHARDT@brother.co.uk>
+ *
+ * Removal of compression code on 1/17/00 by Ross Martin
+ * (ross@ross.interwrx.com, martin@walnut.eas.asu.edu)
+ * enables this driver to correctly print tiger.eps on a
+ * Brother MFC6550MC Fax Machine. Change to the Horizontal
+ * Offset fixes incorrect page alignment at 300dpi in
+ * Landscape mode with a2ps.
+ */
+#include "gdevprn.h"
+/* The following line is used though these printers are not PCL printers*/
+/* This is because we want the paper size access function */
+/* (The 720 is a simple GDI printer) */
+#include "gdevpcl.h"
+
+/*
+ * You may select a default resolution of 150 (for 730), 300, or
+ * 600 DPI in the makefile, or an actual resolution on
+ * the gs command line.
+ *
+ * If the preprocessor symbol A4 is defined, the default paper size is
+ * the European A4 size; otherwise it is the U.S. letter size (8.5"x11").
+ *
+ * You may find the following test page useful in determining the exact
+ * margin settings on your printer. It prints four big arrows which
+ * point exactly to the for corners of an A4 sized paper. Of course the
+ * arrows cannot appear in full on the paper, and they are truncated by
+ * the margins. The margins measured on the testpage must match those
+ * in gdevdjet.c. So the testpage indicates two facts: 1) the page is
+ * not printed in the right position 2) the page is truncated too much
+ * because the margins are wrong. Setting wrong margins in gdevdjet.c
+ * will also move the page, so both facts should be matched with the
+ * real world.
+
+%!
+ newpath
+ 0 0 moveto 144 72 lineto 72 144 lineto
+ closepath fill stroke 0 0 moveto 144 144 lineto stroke
+
+ 595.27 841.88 moveto 451.27 769.88 lineto 523.27 697.88 lineto
+ closepath fill stroke 595.27 841.88 moveto 451.27 697.88 lineto stroke
+
+ 0 841.88 moveto 144 769.88 lineto 72 697.88 lineto
+ closepath fill stroke 0 841.88 moveto 144 697.88 lineto stroke
+
+ 595.27 0 moveto 451.27 72 lineto 523.27 144 lineto
+ closepath fill stroke 595.27 0 moveto 451.27 144 lineto stroke
+
+ /Helvetica findfont
+ 14 scalefont setfont
+ 100 600 moveto
+ (This is an A4 testpage. The arrows should point exactly to the) show
+ 100 580 moveto
+ (corners and the margins should match those given in gdev*.c) show
+ showpage
+
+ */
+
+#define USE_POSSIBLY_FLAWED_COMPRESSION 1
+
+/* Type definitions */
+typedef struct {
+ short width; /* physical width of the paper */
+ short height; /* physical height of the paper */
+} PaperFormat; /* Rep. of the charateristics of a sheet of paper */
+
+typedef unsigned char Byte; /* Rep. of elementary data unit */
+
+/*
+ * Definition of a Helper structure to handle a list of commands
+ */
+typedef struct {
+ Byte * data;
+ short maxSize;
+ short current;
+
+} ByteList;
+
+/*
+ * Type for representing a summary of the previous lines
+ *
+ */
+
+typedef struct {
+ short previousSize;
+ Byte previousData[1500]; /* Size bigger than any possible line */
+ short nbBlankLines;
+ short nbLinesSent;
+ short pageWidth;
+ short pageHeight;
+ short horizontalOffset;
+ short resolution;
+} Summary;
+
+/* Constants */
+
+/* We need a boolean : true , we got it from gdevprn.h */
+
+/* Other constants */
+static const int DumpFinished = 0;
+static const int DumpContinue = 1;
+static const int HL7X0_LENGTH = 5; /* Length of a command to tell the size of the data to be sent to the printer*/
+static void makeCommandsForSequence(Byte * pSource,
+ short length,
+ ByteList * pCommandList,
+ short offset,
+ Byte * pCommandCount,
+ short rest);
+
+/* Auxiliary Functions */
+
+static int dumpPage(gx_device_printer * pSource,
+ Byte * pLineTmp,
+ ByteList * pCommandList,
+ Summary * pSummary
+ );
+static void initSummary(Summary * s,short pw, short ph, short resolution);
+
+static void resetPreviousData(Summary * s);
+
+static void makeFullLine( Byte * pCurrentLine,
+ Byte * pPreviousLine,
+ short lineWidth,
+ ByteList * commandsList,
+ short horizontalOffset
+ );
+
+/*
+ * Initialize a list of Bytes structure
+ */
+static void initByteList(ByteList *list, Byte *array, short maxSize,short initCurrent);
+static void addByte(ByteList *list,Byte value );
+static void addArray(ByteList *list, Byte *source, short nb);
+static void addNBytes(ByteList * list, Byte value, short nb);
+static Byte * currentPosition(ByteList * list);
+static void addCodedNumber(ByteList * list, short number);
+static int isThereEnoughRoom(ByteList * list, short biggest);
+static short roomLeft(ByteList * list);
+static void dumpToPrinter(ByteList * list,FILE * printStream);
+
+/* Real Print function */
+
+static int hl7x0_print_page(gx_device_printer *, FILE *, int, int, ByteList *);
+
+/* Define the default, maximum resolutions. */
+#ifdef X_DPI
+# define X_DPI2 X_DPI
+#else
+# define X_DPI 300
+# define X_DPI2 600
+#endif
+#ifdef Y_DPI
+# define Y_DPI2 Y_DPI
+#else
+# define Y_DPI 300
+# define Y_DPI2 600
+#endif
+
+#define LETTER_WIDTH 5100
+#define LEFT_MARGIN 30
+/* The following table is not actually used.... */
+static const PaperFormat tableOfFormats[] = {
+ /* 0 P LETTER */ { 2550, 3300 },
+ /* 1 P LEGAL */ { 2550, 4200 },
+ /* 2 P EXEC */ { 2175, 3150 },
+ /* 3 P A4(78) */ { 2480, 3507 },
+ /* 4 P B5 */ { 2078, 2953 },
+ /* 5 P A5 */ { 1754, 2480 },
+ /* 6 P MONARC */ { 1162, 2250 },
+ /* 7 P COM10 */ { 1237, 2850 },
+ /* 8 P DL */ { 1299, 2598 },
+ /* 9 P C5 */ { 1913, 2704 },
+ /* 10 P A4Long */ { 2480, 4783 },
+
+ /* 11 L LETTER */ { 3300, 2550 },
+ /* 12 L LEGAL */ { 4200, 2550 },
+ /* 13 L EXEC */ { 3150, 2175 },
+ /* 14 L A4 */ { 3507, 2480 },
+ /* 15 L B5 */ { 2952, 2078 },
+ /* 16 L A5 */ { 2480, 1754 },
+ /* 17 L MONARC */ { 2250, 1162 },
+ /* 18 L COM10 */ { 2850, 1237 },
+ /* 19 L DL */ { 2598, 1299 },
+ /* 20 L C5 */ { 2704, 1913 },
+ /* 21 L A4Long */ { 4783, 2480 }
+};
+
+/* Compute the maximum length of a compressed line */
+static short MaxLineLength(short resolution){
+return (((156 * resolution / 150 ) * 5 )/4) + 8;
+}
+
+/* Margins are left, bottom, right, top. */
+/* Quotation from original gdevdjet.c */
+/* from Frans van Hoesel hoesel@rugr86.rug.nl. */
+/* A4 has a left margin of 1/8 inch and at a printing width of
+ * 8 inch this give a right margin of 0.143. The 0.09 top margin is
+ * not the actual margin - which is 0.07 - but compensates for the
+ * inexact paperlength which is set to 117 10ths.
+ * Somebody should check for letter sized paper. I left it at 0.07".
+ */
+
+/* The A4 margins are almost good */
+/* The one for Letter are those of the gdevdjet.c file... */
+#define HL7X0_MARGINS_A4 0.1, 0.15, 0.07, 0.05
+#define HL7X0_MARGINS_LETTER 0.275, 0.20, 0.25, 0.07
+
+/* We round up the LINE_SIZE to a multiple of a ulong for faster scanning. */
+#define W sizeof(word)
+
+/* Printer types */
+
+#define HL720 0
+#define HL730 0 /* No difference */
+
+/* The device descriptors */
+static dev_proc_open_device(hl7x0_open);
+static dev_proc_close_device(hl7x0_close);
+static dev_proc_print_page(hl720_print_page);
+static dev_proc_print_page(hl730_print_page);
+
+/* Since the print_page doesn't alter the device, this device can print in the background */
+static const gx_device_procs prn_hl_procs =
+ prn_params_procs(hl7x0_open, gdev_prn_bg_output_page, hl7x0_close,
+ gdev_prn_get_params, gdev_prn_put_params);
+
+const gx_device_printer far_data gs_hl7x0_device =
+ prn_device(prn_hl_procs, "hl7x0",
+ DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
+ X_DPI, Y_DPI,
+ 0, 0, 0, 0, /* margins filled in by hl7x0_open */
+ 1, hl720_print_page); /* The hl720 and hl730 can both use the same print method */
+
+/* Open the printer, adjusting the margins if necessary. */
+
+static int
+hl7x0_open(gx_device *pdev)
+{ /* Change the margins if necessary. */
+ static const float m_a4[4] = { HL7X0_MARGINS_A4 };
+ static const float m_letter[4] = { HL7X0_MARGINS_LETTER };
+ const float *m =
+ (gdev_pcl_paper_size(pdev) == PAPER_SIZE_A4 ? m_a4 : m_letter);
+
+ gx_device_set_margins(pdev, m, true);
+ return gdev_prn_open(pdev);
+}
+
+/* The orders sent are those provided in the Brother DOS example */
+static int
+hl7x0_close(gx_device *pdev)
+{
+ gx_device_printer *const ppdev = (gx_device_printer *)pdev;
+ int code = gdev_prn_open_printer(pdev, 1);
+
+ if (code < 0)
+ return code;
+ fputs("@N@N@N@N@X", ppdev->file) ;
+ return gdev_prn_close_printer(pdev);
+}
+
+/* ------ Internal routines ------ */
+
+/* The HL 720 can compress*/
+static int
+hl720_print_page(gx_device_printer *pdev, FILE *prn_stream)
+{
+ Byte prefix[] ={
+ 0x1B,'%','-','1','2','3','4','5','X'
+ ,'@','P','J','L',0x0A /* set PJL mode */
+ ,'@','P','J','L',' ','E','N','T','E','R',' '
+ ,'L','A','N','G','U','A','G','E'
+ ,' ','=',' ','H','B','P',0x0A /* set GDI Printer mode */
+ ,'@','L', 0x0
+ };
+ ByteList initCommand;
+ int x_dpi = pdev->x_pixels_per_inch;
+ initByteList(&initCommand,
+ prefix, /* Array */
+ sizeof(prefix), /* Total size */
+ sizeof(prefix) - 1); /* Leave one byte free since*/
+ /* we need to add the following order at the end */
+ addByte(&initCommand, (Byte) ((((600/x_dpi) >> 1) \
+ | (((600/x_dpi) >> 1) << 2))));
+ /* Put the value of the used resolution into the init string */
+
+ return hl7x0_print_page(pdev, prn_stream, HL720, 300,
+ &initCommand);
+}
+/* The HL 730 can compress */
+static int
+hl730_print_page(gx_device_printer *pdev, FILE *prn_stream)
+{ return hl720_print_page(pdev, prn_stream);
+}
+
+/* Send the page to the printer. For speed, compress each scan line, */
+/* since computer-to-printer communication time is often a bottleneck. */
+static int
+hl7x0_print_page(gx_device_printer *pdev, FILE *printStream, int ptype,
+ int dots_per_inch, ByteList *initCommand)
+{
+ /* UTILE*/
+ /* Command for a formFeed (we can't use strings because of the zeroes...)*/
+ Byte FormFeed[] = {'@','G',0x00,0x00,0x01,0xFF,'@','F'};
+ ByteList formFeedCommand;
+ /* Main characteristics of the page */
+ int line_size = gdev_mem_bytes_per_scan_line((gx_device *)pdev);
+ int x_dpi = pdev->x_pixels_per_inch;
+ /* int y_dpi = pdev->y_pixels_per_inch; */
+ int num_rows = dev_print_scan_lines(pdev);
+ int result;
+ int sizeOfBuffer = MaxLineLength(x_dpi) + 30;
+ Byte * storage = (Byte *) gs_malloc(pdev->memory,
+ sizeOfBuffer + line_size,
+ 1,
+ "hl7x0_print_page");
+ /* bool dup = pdev->Duplex; */
+ /* bool dupset = pdev->Duplex_set >= 0; */
+ Summary pageSummary;
+ ByteList commandsBuffer;
+ initSummary(&pageSummary,
+ line_size,
+ num_rows,
+ x_dpi);
+ if ( storage == 0 ) /* can't allocate working area */
+ return_error(gs_error_VMerror);
+ initByteList(&commandsBuffer, storage, sizeOfBuffer,0 );
+ /* PLUS A MOI */
+ if ( pdev->PageCount == 0 )
+ {
+ /* Put out init string before first page. */
+ dumpToPrinter(initCommand, printStream); /* send init to printer */
+
+ }
+
+ do {
+ result = dumpPage(pdev,
+ storage + sizeOfBuffer, /* The line buffer is after the dump buffer */
+ &commandsBuffer,
+ &pageSummary);
+ dumpToPrinter(&commandsBuffer,printStream);
+
+ } while (result == DumpContinue);
+
+ /* end raster graphics and eject page */
+ initByteList(&formFeedCommand,
+ FormFeed, /* Array */
+ sizeof(FormFeed), /* Size in bytes */
+ sizeof(FormFeed)); /* First free byte */
+ dumpToPrinter(&formFeedCommand, printStream);
+
+ /* free temporary storage */
+ gs_free(pdev->memory, (char *)storage, storage_size_words, 1, "hl7X0_print_page");
+
+ return 0; /* If we reach this line, it means there was no error */
+}
+
+/*
+ * Useful auxiliary declarations
+ *
+ */
+
+static short stripTrailingBlanks(Byte * line, short length){
+ short positionOfFirstZero = length - 1;
+ while (positionOfFirstZero > 0) {
+ if (line[positionOfFirstZero] != 0) {
+ return positionOfFirstZero + 1;
+ }
+ positionOfFirstZero -- ;
+ }
+ return 0;
+}
+
+/*
+ * Changed the horizontalOffset function 1/17/00 Ross Martin.
+ * ross@ross.interwrx.com or martin@walnut.eas.asu.edu
+ *
+ * The equation used to muliply pixWidth by resolution/600
+ * also. This didn't work right at resolution 300; it caused
+ * landscape pages produced by a2ps to be half off the
+ * page, when they were not at 600dpi or on other
+ * devices. I'm not sure the equation below is exactly
+ * correct, but it now looks to be pretty close visually,
+ * and works correctly at 600dpi and 300dpi.
+ */
+static short horizontalOffset(short pixWidth,
+ short pixOffset,
+ short resolution){
+return (((LETTER_WIDTH * resolution/600 - pixWidth) + pixOffset * 2) + 7) / 8;
+
+}
+
+/*
+ * First values in a Summary
+ */
+static void initSummary(Summary * s,short pw, short ph, short resolution){
+ s->previousSize = -1 ;
+ s->nbBlankLines = 1;
+ s->nbLinesSent = 0;
+ s->pageWidth = pw; /* In Bytes */
+ s->pageHeight = ph;
+ s->horizontalOffset = horizontalOffset( pw * 8,LEFT_MARGIN, resolution) ;
+ s->resolution = resolution;
+}
+
+/*
+ * The previous line was blank, so we need to clean the corresponding array
+ */
+static void resetPreviousData(Summary * s){
+ memset(s->previousData,0,s->pageWidth);
+}
+
+/*
+ * dumpPage :
+ *
+ */
+static int dumpPage(gx_device_printer * pSource,
+ Byte * pLineTmp,
+ ByteList * pCommandList,
+ Summary * pSummary
+ ){
+
+ /* Declarations */
+ Byte * pSaveCommandStart;
+ short lineNB;
+ short usefulLength;
+ short tmpLength;
+ /* Initializations */
+ /* Make room for size of commands buffer */
+ pSaveCommandStart = currentPosition(pCommandList);
+ addNBytes(pCommandList,0,HL7X0_LENGTH);
+ /* pSource += pSummary->nbLinesSent * pSummary->pageWidth;*/
+ /* Process all possible Lines */
+ for (lineNB = pSummary->nbLinesSent /*ERROR? + nbBlankLines */ ;
+ lineNB < pSummary->pageHeight ; lineNB ++ ) {
+ /* Fetch the line and put it into the buffer */
+ gdev_prn_copy_scan_lines(pSource,
+ lineNB,
+ pLineTmp,
+ pSummary->pageWidth);
+
+ usefulLength = stripTrailingBlanks(pLineTmp,pSummary->pageWidth);
+ if (usefulLength != 0) {
+
+ /* The line is not blank */
+ /* Get rid of the precedent blank lines */
+ if (pSummary->nbBlankLines != 0) {
+ if ( isThereEnoughRoom( pCommandList, pSummary->nbBlankLines ) ) {
+
+ addNBytes(pCommandList,0xff,pSummary->nbBlankLines);
+ pSummary->nbBlankLines = 0;
+
+ }
+ else {
+
+ short availableRoom = roomLeft(pCommandList);
+ addNBytes(pCommandList,0xff,availableRoom);
+ pSummary->nbBlankLines -= availableRoom;
+
+ break ; /* We have no more room */
+
+ }
+
+ resetPreviousData(pSummary); /* Make sure there are zeroes for the previous line */
+ pSummary->previousSize = 0; /* The previous line was empty */
+
+ }
+
+ /* Deal with the current line */
+ if (!isThereEnoughRoom(pCommandList,MaxLineLength(pSummary->resolution))){
+ break; /* We can process this line */
+ }
+
+ if (pSummary->previousSize > usefulLength){
+ tmpLength = pSummary->previousSize;
+ }
+ else {
+ tmpLength = usefulLength;
+ }
+
+ if (pSummary->previousSize == -1 ) {/* This is the first line */
+
+ Byte *save = currentPosition(pCommandList);
+ addByte(pCommandList,0); /* One byte for the number of commands */
+
+ makeCommandsForSequence(pLineTmp,
+ tmpLength,
+ pCommandList,
+ pSummary->horizontalOffset,
+ save,
+ 0);
+ }
+ else { /*There is a previous line */
+
+ makeFullLine(pLineTmp,
+ pSummary->previousData,
+ tmpLength,
+ pCommandList,
+ pSummary->horizontalOffset);
+ }
+ /* The present line will soon be considered as "previous" */
+ pSummary->previousSize = tmpLength;
+ /* Update the data representing the line will soon be the "previous line" */
+ memcpy(pSummary->previousData,pLineTmp,tmpLength);
+
+ }
+ else { /* the current line is blank */
+ pSummary->nbBlankLines++;
+ }
+
+ /* And one more line */
+ pSummary->nbLinesSent ++;
+ }
+
+ if (pCommandList->current > HL7X0_LENGTH){
+ short size = pCommandList->current - HL7X0_LENGTH;
+ *(pSaveCommandStart++) = '@';
+ *(pSaveCommandStart++) = 'G';
+ *(pSaveCommandStart++) = (Byte) (size >> 16);
+ *(pSaveCommandStart++) = (Byte) (size >> 8);
+ *(pSaveCommandStart++) = (Byte) (size);
+ }
+ else { /* We only met blank lines and reached the end of the page */
+ pCommandList->current = 0;
+ }
+ if (lineNB == pSummary->pageHeight){
+ return DumpFinished;
+ }
+ else {
+ return DumpContinue;
+ }
+}
+
+/*
+ * makeFullLine :
+ * process an arbitrary line for which a former line is available
+ * The line will be split in sequences that are different from the
+ * corresponding ones of the previous line. These sequences will be processed
+ * by makeCommandsOfSequence.
+ */
+
+static void makeFullLine( Byte * pCurrentLine,
+ Byte * pPreviousLine,
+ short lineWidth,
+ ByteList * commandsList,
+ short horizontalOffset
+ ){
+ /* Declarations */
+ Byte *pPreviousTmp;
+ Byte *pCurrentTmp;
+ Byte *pNumberOfCommands;
+ int loopCounter;
+ short remainingWidth;
+ Byte *pStartOfSequence;
+ /*****************/
+ /* Special cases */
+ /*****************/
+
+ /* I believe this situation to be impossible */
+ if (lineWidth <= 0) {
+ addByte(commandsList,0xff);
+ return;
+ }
+
+ /*******************/
+ /* Initializations */
+ /*******************/
+
+ pNumberOfCommands = currentPosition(commandsList); /* Keep a pointer to the number of commands */
+ addByte(commandsList,0); /* At the moment there are 0 commands */
+
+ pPreviousTmp = pPreviousLine;
+ pCurrentTmp = pCurrentLine;
+
+ /* Build vector of differences with a Xor */
+
+ for (loopCounter = lineWidth ; 0 < loopCounter ; loopCounter -- )
+ *pPreviousTmp++ ^= *pCurrentTmp++;
+
+ /* Find sequences that are different from the corresponding (i.e. vertically aligned)
+ * one of the previous line. Make commands for them.
+ */
+
+ pStartOfSequence = pPreviousLine;
+ remainingWidth = lineWidth;
+
+ while (true) {
+
+ /*
+ * Disabled line-to-line compression, 1/17/00 Ross Martin
+ * ross@ross.interwrx.com and/or martin@walnut.eas.asu.edu
+ *
+ * The compression here causes problems printing tiger.eps.
+ * The problem is vertical streaks. The printer I'm printing
+ * to is a Brother MFC6550MC Fax Machine, which may be
+ * slightly different from the hl720 and hl730. Note that
+ * this fax machine does support HP LaserJet 2p emulation,
+ * but in order to enable it I believe one needs special
+ * setup from a DOS program included with the printer. Thus,
+ * the hl7x0 driver seems a better choice. In any case,
+ * on the MFC6550MC, some files print fine with compression
+ * turned on, but others such as tiger.eps print with streaks.
+ * disabling the compression fixes the problem, so I haven't
+ * looked any further at the cause. It may be that the
+ * compression is correct for the hl720 and hl730, and only
+ * different for the MFC6550MC, or it may be that tiger.eps
+ * won't print correctly with compression enabled on any
+ * of these. It may be that the problem is only with color
+ * and/or grayscale prints. YMMV. I don't think it likely
+ * that turning off compression will cause problems with
+ * other printers, except that they may possibly print slower.
+ */
+
+#ifdef USE_POSSIBLY_FLAWED_COMPRESSION
+ /* Count and skip bytes that are not "new" */
+ while (true) {
+ if (remainingWidth == 0) /* There is nothing left to do */
+ {
+ return;
+ }
+ if (*pStartOfSequence != 0)
+ break;
+ pStartOfSequence ++;
+ horizontalOffset ++; /* the offset takes count of the bytes that are not "new" */
+ --remainingWidth;
+ }
+#endif
+
+ pPreviousTmp = pStartOfSequence + 1; /* The sequence contains at least this byte */
+ --remainingWidth;
+
+ /* Find the end of the sequence of "new" bytes */
+
+#ifdef USE_POSSIBLY_FLAWED_COMPRESSION
+ while (remainingWidth != 0 && *pPreviousTmp != 0) {
+ ++pPreviousTmp; /* Enlarge the sequence Of new bytes */
+ --remainingWidth;
+ }
+#else
+ pPreviousTmp += remainingWidth;
+ remainingWidth = 0;
+#endif
+
+ makeCommandsForSequence(pCurrentLine + (pStartOfSequence - pPreviousLine),
+ pPreviousTmp - pStartOfSequence,
+ commandsList,
+ horizontalOffset,
+ pNumberOfCommands,
+ remainingWidth);
+ if (*pNumberOfCommands == 0xfe /* If the number of commands has reached the maximum value */
+ || /* or */
+ remainingWidth == 0 ) /* There is nothing left to process */
+ {
+ return;
+ }
+
+ pStartOfSequence = pPreviousTmp + 1; /* We go on right after the sequence of "new" bytes */
+ horizontalOffset = 1;
+ --remainingWidth;
+ } /* End of While */
+
+} /* End of makeFullLine */
+
+/*
+ * Declarations of functions that are defined further in the file
+ */
+static void makeSequenceWithoutRepeat(
+ Byte * pSequence,
+ short lengthOfSequence,
+ ByteList * pCommandList,
+ short offset );
+
+static void makeSequenceWithRepeat(
+ Byte * pSequence,
+ short lengthOfSequence,
+ ByteList * pCommandList,
+ short offset );
+
+/*
+ * makeCommandsForSequence :
+ * Process a sequence of new bytes (i.e. different from the ones on the former line)
+ */
+
+static void makeCommandsForSequence(Byte * pSource,
+ short length,
+ ByteList * pCommandList,
+ short offset,
+ Byte * pNumberOfCommands,
+ short rest) {
+ /* Declarations */
+ Byte * pStartOfSequence;
+ Byte * pEndOfSequence;
+ short remainingLength = length - 1;
+
+ pStartOfSequence = pSource;
+ pEndOfSequence = pStartOfSequence + 1;
+ /*
+ * Process the whole "new" Sequence that is divided into
+ * repetitive and non-repetitive sequences.
+ */
+ while (true) {
+
+ /* If we have already stored too many commands, make one last command with
+ * everything that is left in the line and return.
+ */
+ if (*pNumberOfCommands == 0xfd) {
+ makeSequenceWithoutRepeat(pStartOfSequence,
+ 1 + remainingLength + rest,
+ pCommandList,
+ offset);
+ ++*pNumberOfCommands;
+ return;
+ }
+
+ /* Start with a sub-sequence without byte-repetition */
+ while (true) {
+ /* If we have completed the last subsequence */
+ if (remainingLength == 0) {
+ makeSequenceWithoutRepeat(pStartOfSequence,
+ pEndOfSequence - pStartOfSequence,
+ pCommandList,
+ offset);
+ ++*pNumberOfCommands;
+ return;
+ }
+ /* If we have discovered a repetition */
+ if (*pEndOfSequence == *(pEndOfSequence - 1)) {
+ break;
+ }
+ ++ pEndOfSequence; /* The subsequence is bigger*/
+ --remainingLength;
+ }
+ /* If this is a sequence without repetition */
+ if (pStartOfSequence != pEndOfSequence - 1) {
+ makeSequenceWithoutRepeat(pStartOfSequence,
+ (pEndOfSequence - 1) - pStartOfSequence,
+ pCommandList,
+ offset);
+ ++*pNumberOfCommands;
+ offset = 0;
+ pStartOfSequence = pEndOfSequence - 1;
+
+ /* If we have too many commands */
+ if (*pNumberOfCommands == 0xfd) {
+ makeSequenceWithoutRepeat(pStartOfSequence,
+ 1 + remainingLength + rest,
+ pCommandList,
+ offset);
+ ++*pNumberOfCommands;
+ return;
+ }
+ } /* End If */
+
+ /*
+ * Process a subsequence that repeats the same byte
+ */
+ while (true) {
+ /* If there is nothing left to process */
+ if (remainingLength == 0) {
+ makeSequenceWithRepeat(pStartOfSequence,
+ pEndOfSequence - pStartOfSequence,
+ pCommandList,
+ offset);
+ ++*pNumberOfCommands;
+ return;
+ }
+ /* If we find a different byte */
+ if (*pEndOfSequence != *pStartOfSequence){
+ break;
+ }
+ ++pEndOfSequence; /* The subsequence is yet bigger */
+ --remainingLength;
+ } /* End of While */
+ makeSequenceWithRepeat(pStartOfSequence,
+ pEndOfSequence - pStartOfSequence,
+ pCommandList,
+ offset);
+ ++*pNumberOfCommands;
+ offset = 0; /* The relative offset between two subsequences is 0 */
+ pStartOfSequence = pEndOfSequence ++ ; /* we loop again from the end of this subsequence */
+ --remainingLength;
+
+ } /* End of While */
+
+} /* End makeCommandsForSequence */
+
+/*
+ * makeSequenceWithoutRepeat
+ */
+static void makeSequenceWithoutRepeat(
+ Byte * pSequence,
+ short lengthOfSequence,
+ ByteList * pCommandList,
+ short offset ){
+ /*
+ * Constant definitions
+ */
+ static const short MAX_OFFSET = 15;
+ static const short POSITION_OF_OFFSET = 3;
+ static const short MAX_LENGTH = 7;
+
+ Byte tmpFirstByte = 0;
+ Byte * pSaveFirstByte;
+ short reducedLength = lengthOfSequence - 1; /* Length is alway higher than 1
+ Therefore a reduced value is stored
+ */
+ /* Initialization */
+
+ pSaveFirstByte = currentPosition(pCommandList);
+ addByte( pCommandList, 0 /* Dummy value */);
+
+ /* Computations */
+
+ if (offset >= MAX_OFFSET) {
+ addCodedNumber(pCommandList,offset - MAX_OFFSET);
+ tmpFirstByte |= MAX_OFFSET << POSITION_OF_OFFSET;
+ }
+ else
+ tmpFirstByte |= offset << POSITION_OF_OFFSET;
+
+ if (reducedLength >= MAX_LENGTH) {
+ addCodedNumber(pCommandList,reducedLength - MAX_LENGTH);
+ tmpFirstByte |= MAX_LENGTH ;
+ }
+ else
+ tmpFirstByte |= reducedLength ;
+ /* Add a copy of the source sequence */
+
+ addArray(pCommandList, pSequence, lengthOfSequence);
+
+ /* Store the computed value of the first byte */
+
+ *pSaveFirstByte = tmpFirstByte;
+
+ return ;
+} /* End of makeSequenceWithoutRepeat */
+
+/*
+ * makeSequenceWithRepeat
+ */
+static void makeSequenceWithRepeat(
+ Byte * pSequence,
+ short lengthOfSequence,
+ ByteList * pCommandList,
+ short offset ){
+ /*
+ * Constant definitions
+ */
+ static const short MAX_OFFSET = 3;
+ static const short POSITION_OF_OFFSET = 5;
+ static const short MAX_LENGTH = 31;
+
+ Byte tmpFirstByte = 0x80;
+ Byte * pSaveFirstByte;
+ short reducedLength = lengthOfSequence - 2; /* Length is always higher than 2
+ Therefore a reduced value is stored
+ */
+ /* Initialization */
+
+ pSaveFirstByte = currentPosition(pCommandList);
+ addByte( pCommandList, 0 /* Dummy value */);
+
+ /* Computations */
+
+ if (offset >= MAX_OFFSET) {
+ addCodedNumber(pCommandList, offset - MAX_OFFSET);
+ tmpFirstByte |= MAX_OFFSET << POSITION_OF_OFFSET;
+ }
+ else
+ tmpFirstByte |= offset << POSITION_OF_OFFSET;
+
+ if (reducedLength >= MAX_LENGTH) {
+ addCodedNumber(pCommandList,reducedLength - MAX_LENGTH);
+ tmpFirstByte |= MAX_LENGTH ;
+ }
+ else
+ tmpFirstByte |= reducedLength ;
+ /* Add a copy the byte that is repeated throughout the sequence */
+
+ addByte(pCommandList, *pSequence );
+
+ /* Store the computed value of the first byte */
+
+ *pSaveFirstByte = tmpFirstByte;
+
+ return ;
+} /* End of makeSequenceWithRepeat*/
+
+/*
+ * Initialize a list of Bytes structure
+ */
+static void initByteList(ByteList *list, Byte *array, short maxSize, short initCurrent) {
+ list->current = initCurrent;
+ list->maxSize = maxSize;
+ list->data = array;
+}
+
+/*
+ * Add a Byte to a list of Bytes
+ */
+static void addByte(ByteList *list,Byte value ) {
+ if (list->current < list->maxSize)
+ list->data[list->current++] = value;
+ else
+ eprintf("Could not add byte to command\n");
+}
+
+/*
+ * Add a copy of an array to a list of Bytes
+ */
+
+static void addArray(ByteList *list, Byte *source, short nb){
+ if (list->current <= list->maxSize - nb)
+ {
+ memcpy(list->data + list->current, source , (size_t) nb);
+ list->current += nb;
+ }
+ else
+ eprintf("Could not add byte array to command\n");
+}
+
+/*
+ * Add N bytes to a list of Bytes
+ */
+
+static void addNBytes(ByteList * list, Byte value, short nb){
+ int i;
+ if (list->current <= list->maxSize - nb)
+ {
+ for (i = list->current ; i < (list->current + nb) ; i++)
+ {
+ list->data[i] = value;
+ }
+ list->current += nb;
+ }
+ else
+ eprintf1("Could not add %d bytes to command\n",nb);
+}
+
+/*
+ * Get pointer to the current byte
+ */
+static Byte * currentPosition(ByteList * list) {
+ return &(list->data[list->current]);
+}
+
+/*
+ * add a number coded in the following way :
+ * q bytes with 0xff value
+ * 1 byte with r value
+ * where q is the quotient of the number divided by 0xff and r is the
+ * remainder.
+ */
+static void addCodedNumber(ByteList * list, short number){
+ short q = number / 0xff;
+ short r = number % 0xff;
+
+ addNBytes(list, 0xff, q);
+ addByte(list,r);
+
+}
+
+/*
+ * See if there is enough room for a set of commands of size biggest
+ *
+ */
+
+static int isThereEnoughRoom(ByteList * list, short biggest){
+ return ((list->maxSize-list->current) >= biggest);
+}
+/*
+ * Tell how much room is left
+ */
+static short roomLeft(ByteList * list){
+ return list->maxSize - list->current;
+}
+/*
+ * Dump all commands to the printer and reset the structure
+ *
+ */
+static void dumpToPrinter(ByteList * list,FILE * printStream){
+ short loopCounter;
+ /* Actual dump */
+ /* Please note that current is the first empty byte */
+ for (loopCounter = 0; loopCounter < list->current; loopCounter++)
+ {
+ fputc(list->data[loopCounter],printStream);
+ }
+
+ /* Reset of the ByteList */
+ list->current = 0;
+}
diff --git a/devices/gdevicov.c b/devices/gdevicov.c
new file mode 100644
index 000000000..1b8d4b815
--- /dev/null
+++ b/devices/gdevicov.c
@@ -0,0 +1,203 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+
+/* inkcov: compute ink coverage of the document being rendered.
+ * originally copyright 2011 Sebastian Kapfer <sebastian.kapfer@physik.uni-erlangen.de>
+ * but assigned to Artifex Software, Inc. (see http://bugs.ghostscript.com/show_bug.cgi?id=692665)
+ *
+ * output is plain text; one line per page.
+ * columns 1 through 4 give the fraction of pixels containing
+ * c, m, y and black ink.
+ * column 5 is the string 'CMYK'.
+ * column 6 is 'OK' if everything went fine, 'ERROR' if there
+ * was a problem.
+ *
+ * the resolution defaults to 75 dpi (which gives good-enough estimates)
+ * but can be changed via the -r flag to Ghostscript.
+ */
+
+#include "stdint_.h"
+#include "stdio_.h"
+#include "gdevprn.h"
+
+static int
+cov_write_page(gx_device_printer *pdev, FILE *file)
+{
+ int code = 0;
+ int raster = gdev_prn_raster(pdev);
+ int height = pdev->height;
+ byte *line = gs_alloc_bytes(pdev->memory, raster, "ink coverage plugin buffer");
+ int y;
+ uint64_t c_pix = 0, m_pix = 0, y_pix = 0, k_pix = 0, total_pix = 0;
+
+ for (y = 0; y < height; y++) {
+ byte *row, *end;
+
+ code = gdev_prn_get_bits(pdev, y, line, &row);
+ if (code < 0)
+ break;
+ end = row + raster;
+
+ for (; row < end; row += 4) {
+ c_pix += !!row[0];
+ m_pix += !!row[1];
+ y_pix += !!row[2];
+ k_pix += !!row[3];
+ ++total_pix;
+ }
+ }
+
+ if (pdev->width * height != total_pix)
+ code = 1;
+
+ gs_free_object(pdev->memory, line, "ink coverage plugin buffer");
+
+ {
+ double c = -1., m = -1., y = -1., k = -1.;
+ if (code == 0) {
+ c = (double)c_pix / total_pix;
+ m = (double)m_pix / total_pix;
+ y = (double)y_pix / total_pix;
+ k = (double)k_pix / total_pix;
+ }
+
+
+ if (IS_LIBCTX_STDOUT(pdev->memory, file)) {
+ outprintf(pdev->memory, "%8.5f %8.5f %8.5f %8.5f CMYK %s\n",
+ c, m, y, k, code ? "ERROR" : "OK");
+ }
+ else if (IS_LIBCTX_STDERR(pdev->memory, file)) {
+ errprintf(pdev->memory, "%8.5f %8.5f %8.5f %8.5f CMYK %s\n",
+ c, m, y, k, code ? "ERROR" : "OK");
+ }
+ else {
+ fprintf (file, "%8.5f %8.5f %8.5f %8.5f CMYK %s\n",
+ c, m, y, k, code ? "ERROR" : "OK");
+ }
+ }
+
+ return 0;
+}
+
+/* cov_write_page2 gave ink coverage values not ratecoverage */
+
+static int cov_write_page_ink(gx_device_printer *pdev, FILE *file)
+{
+ int code = 0;
+ int raster = gdev_prn_raster(pdev);
+ int height = pdev->height;
+ double dc_pix=0;
+ double dm_pix=0;
+ double dy_pix=0;
+ double dk_pix=0;
+
+ byte *line = gs_alloc_bytes(pdev->memory, raster, "ink coverage plugin buffer");
+ int y;
+ uint64_t total_pix = 0;
+
+ for (y = 0; y < height; y++) {
+ byte *row, *end;
+
+ code = gdev_prn_get_bits(pdev, y, line, &row);
+ if (code < 0)
+ break;
+ end = row + raster;
+
+ for (; row < end; row += 4) {
+
+ dc_pix += row[0];
+
+ dm_pix += row[1];
+
+ dy_pix += row[2];
+
+ dk_pix += row[3];
+
+ ++total_pix;
+ }
+ }
+
+ if (pdev->width * height != total_pix)
+ code = 1;
+
+ gs_free_object(pdev->memory, line, "ink coverage plugin buffer");
+
+ {
+ double c = -1., m = -1., y = -1., k = -1.;
+ if (code == 0) {
+ c = (dc_pix*100) / (total_pix*255);
+ m = (dm_pix*100) / (total_pix*255);
+ y = (dy_pix*100) / (total_pix*255);
+ k = (dk_pix*100) / (total_pix*255);
+ }
+
+ if (IS_LIBCTX_STDOUT(pdev->memory, file)) {
+ outprintf(pdev->memory, "%8.5f %8.5f %8.5f %8.5f CMYK %s\n",
+ c, m, y, k, code ? "ERROR" : "OK");
+ }
+ else if (IS_LIBCTX_STDERR(pdev->memory, file)) {
+ errprintf(pdev->memory, "%8.5f %8.5f %8.5f %8.5f CMYK %s\n",
+ c, m, y, k, code ? "ERROR" : "OK");
+ }
+ else {
+ fprintf (file, "%8.5f %8.5f %8.5f %8.5f CMYK %s\n",
+ c, m, y, k, code ? "ERROR" : "OK");
+ }
+ }
+
+ return 0;
+}
+
+static const gx_device_procs cov_procs =
+{
+ gdev_prn_open,
+ NULL, /* get_initial_matrix */
+ NULL, /* sync_output */
+ /* Since the print_page doesn't alter the device, this device can print in the background */
+ gdev_prn_bg_output_page,
+ gdev_prn_close,
+ NULL, /* map_rgb_color */
+ cmyk_8bit_map_color_rgb,
+ NULL, /* fill_rectangle */
+ NULL, /* tile_rectangle */
+ NULL, /* copy_mono */
+ NULL, /* copy_color */
+ NULL, /* draw_line */
+ NULL, /* get_bits */
+ gdev_prn_get_params,
+ gdev_prn_put_params,
+ cmyk_8bit_map_cmyk_color,
+ NULL, /* get_xfont_procs */
+ NULL, /* get_xfont_device */
+ NULL, /* map_rgb_alpha_color */
+ gx_page_device_get_page_device
+};
+
+const gx_device_printer gs_inkcov_device = prn_device(
+ cov_procs, "inkcov",
+ DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
+ 75, 75, /* dpi */
+ 0, 0, 0, 0, /* margins */
+ 32, cov_write_page);
+
+const gx_device_printer gs_ink_cov_device = prn_device(
+ cov_procs, "ink_cov",
+ DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
+ 75, 75, /* dpi */
+ 0, 0, 0, 0, /* margins */
+ 32, cov_write_page_ink);
+
diff --git a/devices/gdevifno.c b/devices/gdevifno.c
new file mode 100644
index 000000000..0982623c9
--- /dev/null
+++ b/devices/gdevifno.c
@@ -0,0 +1,811 @@
+/*
+ * Copyright (c) 1998 by Lucent Technologies.
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose without fee is hereby granted, provided that this entire notice
+ * is included in all copies of any software which is or includes a copy
+ * or modification of this software.
+ *
+ * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY
+ * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
+ * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
+ */
+
+/*
+ * gs device to generate inferno bitmaps
+ *
+ * Russ Cox <rsc@plan9.bell-labs.com>, 3/25/98
+ * Updated to fit in the standard GS distribution, 5/14/98
+ * Comments edited for automatic TOC generation, 11/4/99
+ */
+
+#include "gdevprn.h"
+#include "gsparam.h"
+
+#define nil ((void*)0)
+
+/*
+ * ERROR
+ * is used to go up the stack and
+ * eventually return_error(gs_error_Fatal) to gs.
+ */
+#define ERROR (-2)
+
+typedef struct WImage WImage;
+typedef struct Rectangle Rectangle;
+typedef struct Point Point;
+
+struct Point {
+ int x;
+ int y;
+};
+
+struct Rectangle {
+ Point min;
+ Point max;
+};
+static const Point ZP = { 0, 0 };
+
+static WImage* initwriteimage(FILE *f, Rectangle r, int ldepth, gs_memory_t *mem);
+static int writeimageblock(WImage *w, uchar *data, int ndata, gs_memory_t *mem);
+static int bytesperline(Rectangle, int);
+static int rgb2cmap(int, int, int);
+/* static long cmap2rgb(int); */ /* not currently used */
+
+void init_p9color(ulong *p9color);
+
+#define X_DPI 100
+#define Y_DPI 100
+
+static dev_proc_map_rgb_color(inferno_rgb2cmap);
+static dev_proc_map_color_rgb(inferno_cmap2rgb);
+static dev_proc_open_device(inferno_open);
+static dev_proc_close_device(inferno_close);
+static dev_proc_print_page(inferno_print_page);
+
+typedef struct inferno_device_s {
+ gx_device_common;
+ gx_prn_device_common;
+ int ldepth;
+ int lastldepth;
+ int color, gray;
+ int cmapcall;
+ int nbits;
+ ulong *p9color; /* index blue most sig, red least sig */
+} inferno_device;
+
+/* structure descriptor for the garbage collector */
+/* we must use the final version because gx_device_common requires
+ a finalisation call, but such procedures aren't inherited. */
+gs_private_st_suffix_add1_final(st_inferno_device, inferno_device,
+ "inferno_device", inferno_device_enum_ptrs, inferno_device_reloc_ptrs,
+ gx_device_finalize, st_device_printer, p9color);
+
+static const gx_device_procs inferno_procs =
+ prn_color_params_procs(inferno_open, gdev_prn_output_page, inferno_close,
+ inferno_rgb2cmap, inferno_cmap2rgb,
+ gdev_prn_get_params, gdev_prn_put_params);
+
+inferno_device far_data gs_inferno_device =
+{ prn_device_stype_body(inferno_device, inferno_procs, "inferno",
+ &st_inferno_device,
+ DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
+ X_DPI, Y_DPI,
+ 0,0,0,0, /* margins */
+ 3, /* 3 = RGB, 1 = gray, 4 = CMYK */
+ 16, /* # of bits per pixel -- 16 is easier to handle than 12 */
+ 255, /* # of distinct gray levels. >=31 to fool gs into asking us*/
+ 255, /* # of distinct color levels. >= 31 to fool gs */
+ 0, /* dither gray ramp size. hopefully not used */
+ 0, /* dither color ramp size. hopefully not used */
+ inferno_print_page)
+};
+
+/*
+ * ghostscript asks us how to convert between
+ * rgb and color map entries
+ */
+static gx_color_index
+inferno_rgb2cmap(gx_device *dev, const gx_color_value cv[]) {
+ int shift;
+ inferno_device *bdev = (inferno_device*) dev;
+ int nbits = bdev->nbits;
+ int mask = (1<<nbits)-1;
+ gx_color_value red, green, blue;
+
+ red = cv[0]; green = cv[1]; blue = cv[2];
+ /* make the colors the size we want */
+ if(gx_color_value_bits > nbits) {
+ shift = gx_color_value_bits - nbits;
+ red >>= shift;
+ green >>= shift;
+ blue >>= shift;
+ } else if(gx_color_value_bits < nbits) {
+ shift = nbits - gx_color_value_bits;
+ red <<= shift;
+ green <<= shift;
+ blue <<= shift;
+ }
+
+ /* mask them off to be just nbits */
+ red &= mask;
+ green &= mask;
+ blue &= mask;
+
+ /*
+ * we keep track of what ldepth bitmap this is by watching
+ * what colors gs asks for.
+ *
+ * one catch: sometimes print_page gets called more than one
+ * per page (for multiple copies) without cmap calls inbetween.
+ * if bdev->cmapcall is 0 when print_page gets called, it uses
+ * the ldepth of the last page.
+ */
+ if(red == green && green == blue && red != 0 && red != mask) {
+ if(red == 5 || red == 10) {
+ if(bdev->ldepth < 1)
+ bdev->ldepth = 1;
+ } else {
+ if(bdev->ldepth < 2)
+ bdev->ldepth = 2;
+ }
+ } else
+ bdev->ldepth = 3;
+
+ bdev->cmapcall = 1;
+ return ((((blue<<4)|green)<<4)|red);
+}
+
+static int
+inferno_cmap2rgb(gx_device *dev, gx_color_index color,
+ gx_color_value rgb[3]) {
+ int shift, i;
+ inferno_device *bdev = (inferno_device*) dev;
+ int nbits = bdev->nbits;
+ int mask = (1<<nbits)-1;
+
+ if(color < 0 || color > 255)
+ return_error(gs_error_rangecheck);
+
+ rgb[2] = (color >> (2*nbits)) & mask;
+ rgb[1] = (color >> nbits) & mask;
+ rgb[0] = color & mask;
+ if(gx_color_value_bits > nbits) {
+ shift = gx_color_value_bits - nbits;
+ for(i=0; i<3; i++)
+ rgb[i] <<= shift;
+ } else if(gx_color_value_bits < nbits) {
+ shift = nbits - gx_color_value_bits;
+ for(i=0; i<3; i++)
+ rgb[i] >>= shift;
+ }
+
+ return 0;
+}
+
+/*
+ * dithering tables courtesy of john hobby
+ */
+/* The following constants and tables define the mapping from fine-grained RGB
+ triplets to 8-bit values based on the standard Plan 9 color map.
+*/
+#define Rlevs 16 /* number of levels to cut red value into */
+#define Glevs 16
+#define Blevs 16
+#define Mlevs 16
+#define Rfactor 1 /* multiple of red level in e cp9color[] index */
+#define Gfactor Rlevs
+#define Bfactor (Rlevs*Glevs)
+#define p9color_size (sizeof(ulong)*Rlevs*Glevs*Blevs)
+
+void init_p9color(ulong *p9color) /* init at run time since p9color[] is so big */
+{
+ int r, g, b, o;
+ ulong* cur = p9color;
+ for (b=0; b<16; b++) {
+ for (g=0; g<16; g++) {
+ int m0 = (b>g) ? b : g;
+ for (r=0; r<16; r++) {
+ int V, M, rM, gM, bM, m;
+ int m1 = (r>m0) ? r : m0;
+ V=m1&3; M=(m1-V)<<1;
+ if (m1==0) m1=1;
+ m = m1 << 3;
+ rM=r*M; gM=g*M; bM=b*M;
+ *cur = 0;
+ for (o=7*m1; o>0; o-=2*m1) {
+ int rr=(rM+o)/m, gg=(gM+o)/m, bb=(bM+o)/m;
+ int ij = (rr<<6) + (V<<4) + ((V-rr+(gg<<2)+bb)&15);
+ *cur = (*cur << 8) + 255-ij;
+ }
+ cur++;
+ }
+ }
+ }
+}
+
+/*
+ * inferno_open() is supposed to initialize the device.
+ * there's not much to do.
+ */
+static int
+inferno_open(gx_device *dev)
+{
+ inferno_device *bdev = (inferno_device*) dev;
+ bdev->color = bdev->gray = 0;
+ bdev->cmapcall = 0;
+ bdev->ldepth = 3;
+ bdev->nbits = 4; /* 4 bits per color per pixel (12 bpp, then we dither) */
+ /* if you change this, change the entry in gs_inferno_device */
+ bdev->p9color = (ulong *)gs_alloc_bytes(bdev->memory, p9color_size, "plan 9 colour cube");
+ if (bdev->p9color == NULL)
+ return_error(gs_error_VMerror);
+ init_p9color(bdev->p9color);
+ return gdev_prn_open(dev);
+}
+
+/*
+ * inferno_close() is called at the end, once everything
+ * is finished. we have nothing to do.
+ */
+static int
+inferno_close(gx_device *dev)
+{
+ inferno_device *bdev = (inferno_device*) dev;
+ int code;
+
+ gs_free_object(dev->memory, bdev->p9color, "plan 9 colour cube");
+
+ code = gdev_prn_close(dev);
+ if(code < 0)
+ return_error(code);
+ return 0;
+}
+
+/*
+ * inferno_print_page() is called once for each page
+ * (actually once for each copy of each page, but we won't
+ * worry about that).
+ */
+static int
+inferno_print_page(gx_device_printer *pdev, FILE *f)
+{
+ uchar *buf;
+ uchar *p;
+ WImage *w;
+ int bpl, y;
+ int x, xmod;
+ int ldepth;
+ int ppb[] = {8, 4, 2, 1}; /* pixels per byte */
+ int bpp[] = {1, 2, 4, 8}; /* bits per pixel */
+ int gsbpl;
+ ulong u;
+ ushort us;
+
+ inferno_device *bdev = (inferno_device *) pdev;
+ Rectangle r;
+
+ gsbpl = gdev_prn_raster(pdev);
+ if(gsbpl > 16384) { /* == 8192 dots across */
+ emprintf(pdev->memory, "bitmap far too wide for inferno\n");
+ return_error(gs_error_Fatal);
+ }
+
+ if(bdev->cmapcall) {
+ bdev->lastldepth = bdev->ldepth;
+ bdev->ldepth = 0;
+ bdev->cmapcall = 0;
+ }
+ ldepth = bdev->lastldepth;
+
+ r.min = ZP;
+ r.max.x = pdev->width;
+ r.max.y = pdev->height;
+ bpl = bytesperline(r, ldepth);
+ w = initwriteimage(f, r, ldepth, bdev->memory);
+ if(w == nil) {
+ emprintf(pdev->memory, "initwriteimage failed\n");
+ return_error(gs_error_Fatal);
+ }
+
+ buf = gs_alloc_bytes(bdev->memory, gsbpl, "inferno line buffer");
+ if(buf == NULL) {
+ emprintf(pdev->memory, "couldn't allocate line buffer\n");
+ return_error(gs_error_VMerror);
+ }
+
+ /*
+ * i wonder if it is faster to put the switch around the for loops
+ * to save all the ldepth lookups.
+ */
+ for(y=0; y<pdev->height; y++) {
+ gdev_prn_get_bits(pdev, y, buf, &p);
+ for(x=0; x<pdev->width; x++) {
+ us = (p[2*x]<<8) | p[2*x+1];
+ switch(ldepth) {
+ case 3:
+ if(0){
+ int r, g, b;
+ r = us & 0xf;
+ g = (us>>4)&0xf;
+ b = (us>>8)&0xf;
+ r<<=4;
+ g<<=4;
+ b<<=4;
+ p[x] = rgb2cmap(r,g,b);
+ }
+ if(1){
+ u = bdev->p9color[us];
+ /* the ulong in p9color is a 2x2 matrix. pull the entry
+ * u[x%2][y%2], more or less.
+ */
+ p[x] = u >> (8*((y%2)+2*(x%2)));
+ }
+ break;
+ case 2:
+ us = ~us;
+ if((x%2) == 0)
+ p[x/2] = us & 0xf;
+ else
+ p[x/2] = (p[x/2]<<4)|(us&0xf);
+ break;
+ case 0:
+ us = ~us;
+ if((x%8) == 0)
+ p[x/8] = us & 0x1;
+ else
+ p[x/8] = (p[x/8]<<1)|(us&0x1);
+ break;
+ }
+ }
+
+ /* pad last byte over if we didn't fill it */
+ xmod = pdev->width % ppb[ldepth];
+ if(xmod)
+ p[(x-1)/ppb[ldepth]] <<= ((ppb[ldepth]-xmod)*bpp[ldepth]);
+ if(writeimageblock(w, p, bpl, bdev->memory) == ERROR) {
+ gs_free_object(bdev->memory, buf, "inferno line buffer");
+ /* w leaks here */
+ return_error(gs_error_Fatal);
+ }
+ }
+ gs_free_object(bdev->memory, buf, "inferno line buffer");
+ if(writeimageblock(w, nil, 0, bdev->memory) == ERROR) {
+ return_error(gs_error_Fatal);
+ }
+
+ return 0;
+}
+
+/*
+ * this is a modified version of the image compressor
+ * from fb/bit2enc. it is modified only in that it
+ * now compiles as part of gs.
+ * some updates have been made to use dynamic memory.
+ */
+
+/*
+ * Compressed image file parameters
+ */
+#define NMATCH 3 /* shortest match possible */
+#define NRUN (NMATCH+31) /* longest match possible */
+#define NMEM 1024 /* window size */
+#define NDUMP 128 /* maximum length of dump */
+#define NCBLOCK 6000 /* size of compressed blocks */
+
+#define HSHIFT 3 /* HSHIFT==5 runs slightly faster, but hash table is 64x bigger */
+#define NHASH (1<<(HSHIFT*NMATCH))
+#define HMASK (NHASH-1)
+#define hupdate(h, c) ((((h)<<HSHIFT)^(c))&HMASK)
+
+typedef struct Dump Dump;
+typedef struct Hlist Hlist;
+
+struct Hlist{
+ ulong p;
+ Hlist *next, *prev;
+};
+
+struct Dump {
+ int ndump;
+ uchar *dumpbuf;
+ uchar buf[1+NDUMP];
+};
+
+struct WImage {
+ FILE *f;
+
+ /* image attributes */
+ Rectangle origr, r;
+ int bpl;
+
+ /* output buffer */
+ uchar outbuf[NCBLOCK], *outp, *eout, *loutp;
+
+ /* sliding input window */
+ /*
+ * ibase is the pointer to where the beginning of
+ * the input "is" in memory. whenever we "slide" the
+ * buffer N bytes, what we are actually doing is
+ * decrementing ibase by N.
+ * the ulongs in the Hlist structures are just
+ * pointers relative to ibase.
+ */
+ uchar *inbuf; /* inbuf should be at least NMEM+NRUN+NMATCH long */
+ uchar *ibase;
+ int minbuf; /* size of inbuf (malloc'ed bytes) */
+ int ninbuf; /* size of inbuf (filled bytes) */
+ ulong line; /* the beginning of the line we are currently encoding,
+ * relative to inbuf (NOT relative to ibase) */
+
+ /* raw dump buffer */
+ Dump dump;
+
+ /* hash tables */
+ Hlist hash[NHASH];
+ Hlist chain[NMEM], *cp;
+ int h;
+ int needhash;
+};
+
+static void
+zerohash(WImage *w)
+{
+ memset(w->hash, 0, sizeof(w->hash));
+ memset(w->chain, 0, sizeof(w->chain));
+ w->cp=w->chain;
+ w->needhash = 1;
+}
+
+static int
+addbuf(WImage *w, uchar *buf, int nbuf)
+{
+ int n;
+ if(buf == nil || w->outp+nbuf > w->eout) {
+ if(w->loutp==w->outbuf){ /* can't really happen -- we checked line length above */
+ eprintf("buffer too small for line\n");
+ return ERROR;
+ }
+ n=w->loutp-w->outbuf;
+ fprintf(w->f, "%11d %11d ", w->r.max.y, n);
+ fwrite(w->outbuf, 1, n, w->f);
+ w->r.min.y=w->r.max.y;
+ w->outp=w->outbuf;
+ w->loutp=w->outbuf;
+ zerohash(w);
+ return -1;
+ }
+
+ memmove(w->outp, buf, nbuf);
+ w->outp += nbuf;
+ return nbuf;
+}
+
+/* return 0 on success, -1 if buffer is full */
+static int
+flushdump(WImage *w)
+{
+ int n = w->dump.ndump;
+
+ if(n == 0)
+ return 0;
+
+ w->dump.buf[0] = 0x80|(n-1);
+ if((n=addbuf(w, w->dump.buf, n+1)) == ERROR)
+ return ERROR;
+ if(n < 0)
+ return -1;
+ w->dump.ndump = 0;
+ return 0;
+}
+
+static void
+updatehash(WImage *w, uchar *p, uchar *ep)
+{
+ uchar *q;
+ Hlist *cp;
+ Hlist *hash;
+ int h;
+
+ hash = w->hash;
+ cp = w->cp;
+ h = w->h;
+ for(q=p; q<ep; q++) {
+ if(cp->prev)
+ cp->prev->next = cp->next;
+ cp->next = hash[h].next;
+ cp->prev = &hash[h];
+ cp->prev->next = cp;
+ if(cp->next)
+ cp->next->prev = cp;
+ cp->p = q - w->ibase;
+ if(++cp == w->chain+NMEM)
+ cp = w->chain;
+ if(&q[NMATCH] < &w->inbuf[w->ninbuf])
+ h = hupdate(h, q[NMATCH]);
+ }
+ w->cp = cp;
+ w->h = h;
+}
+
+/*
+ * attempt to process a line of input,
+ * returning the number of bytes actually processed.
+ *
+ * if the output buffer needs to be flushed, we flush
+ * the buffer and return 0.
+ * otherwise we return bpl
+ */
+static int
+gobbleline(WImage *w)
+{
+ int runlen, n, offs;
+ uchar *eline, *es, *best, *p, *s, *t;
+ Hlist *hp;
+ uchar buf[2];
+ int rv;
+
+ if(w->needhash) {
+ w->h = 0;
+ for(n=0; n!=NMATCH; n++)
+ w->h = hupdate(w->h, w->inbuf[w->line+n]);
+ w->needhash = 0;
+ }
+ w->dump.ndump=0;
+ eline=w->inbuf+w->line+w->bpl;
+ for(p=w->inbuf+w->line;p!=eline;){
+ es = (eline < p+NRUN) ? eline : p+NRUN;
+
+ best=nil;
+ runlen=0;
+ /* hash table lookup */
+ for(hp=w->hash[w->h].next;hp;hp=hp->next){
+ /*
+ * the next block is an optimization of
+ * for(s=p, t=w->ibase+hp->p; s<es && *s == *t; s++, t++)
+ * ;
+ */
+
+ { uchar *ss, *tt;
+ s = p+runlen;
+ t = w->ibase+hp->p+runlen;
+ for(ss=s, tt=t; ss>=p && *ss == *tt; ss--, tt--)
+ ;
+ if(ss < p)
+ while(s<es && *s == *t)
+ s++, t++;
+ }
+
+ n = s-p;
+
+ if(n > runlen) {
+ runlen = n;
+ best = w->ibase+hp->p;
+ if(p+runlen == es)
+ break;
+ }
+ }
+
+ /*
+ * if we didn't find a long enough run, append to
+ * the raw dump buffer
+ */
+ if(runlen<NMATCH){
+ if(w->dump.ndump==NDUMP) {
+ if((rv = flushdump(w)) == ERROR)
+ return ERROR;
+ if(rv < 0)
+ return 0;
+ }
+ w->dump.dumpbuf[w->dump.ndump++]=*p;
+ runlen=1;
+ }else{
+ /*
+ * otherwise, assuming the dump buffer is empty,
+ * add the compressed rep.
+ */
+ if((rv = flushdump(w)) == ERROR)
+ return ERROR;
+ if(rv < 0)
+ return 0;
+ offs=p-best-1;
+ buf[0] = ((runlen-NMATCH)<<2)|(offs>>8);
+ buf[1] = offs&0xff;
+ if(addbuf(w, buf, 2) < 0)
+ return 0;
+ }
+
+ /*
+ * add to hash tables what we just encoded
+ */
+ updatehash(w, p, p+runlen);
+ p += runlen;
+ }
+
+ if((rv = flushdump(w)) == ERROR)
+ return ERROR;
+ if(rv < 0)
+ return 0;
+ w->line += w->bpl;
+ w->loutp=w->outp;
+ w->r.max.y++;
+ return w->bpl;
+}
+
+static uchar*
+shiftwindow(WImage *w, uchar *data, uchar *edata)
+{
+ int n, m;
+
+ /* shift window over */
+ if(w->line > NMEM) {
+ n = w->line-NMEM;
+ memmove(w->inbuf, w->inbuf+n, w->ninbuf-n);
+ w->line -= n;
+ w->ibase -= n;
+ w->ninbuf -= n;
+ }
+
+ /* fill right with data if available */
+ if(w->minbuf > w->ninbuf && edata > data) {
+ m = w->minbuf - w->ninbuf;
+ if(edata-data < m)
+ m = edata-data;
+ memmove(w->inbuf+w->ninbuf, data, m);
+ data += m;
+ w->ninbuf += m;
+ }
+
+ return data;
+}
+
+static WImage*
+initwriteimage(FILE *f, Rectangle r, int ldepth, gs_memory_t *mem)
+{
+ WImage *w;
+ int n, bpl;
+
+ bpl = bytesperline(r, ldepth);
+ if(r.max.y <= r.min.y || r.max.x <= r.min.x || bpl <= 0) {
+ emprintf(mem, "bad rectangle, ldepth");
+ return nil;
+ }
+
+ n = NMEM+NMATCH+NRUN+bpl*2;
+ w = (WImage*)gs_alloc_bytes(mem, n+sizeof(*w), "inferno image");
+ if(w == nil)
+ return nil;
+ w->inbuf = (uchar*) &w[1];
+ w->ibase = w->inbuf;
+ w->line = 0;
+ w->minbuf = n;
+ w->ninbuf = 0;
+ w->origr = r;
+ w->r = r;
+ w->r.max.y = w->r.min.y;
+ w->eout = w->outbuf+sizeof(w->outbuf);
+ w->outp = w->loutp = w->outbuf;
+ w->bpl = bpl;
+ w->f = f;
+ w->dump.dumpbuf = w->dump.buf+1;
+ w->dump.ndump = 0;
+ zerohash(w);
+
+ fprintf(f, "compressed\n%11d %11d %11d %11d %11d ",
+ ldepth, r.min.x, r.min.y, r.max.x, r.max.y);
+ return w;
+}
+
+static int
+writeimageblock(WImage *w, uchar *data, int ndata, gs_memory_t *mem)
+{
+ uchar *edata;
+
+ if(data == nil) { /* end of data, flush everything */
+ while(w->line < w->ninbuf)
+ if(gobbleline(w) == ERROR)
+ return ERROR;
+ addbuf(w, nil, 0);
+ if(w->r.min.y != w->origr.max.y) {
+ emprintf(mem, "not enough data supplied to writeimage\n");
+ }
+ gs_free_object(mem, w, "inferno image");
+ return 0;
+ }
+
+ edata = data+ndata;
+ data = shiftwindow(w, data, edata);
+ while(w->ninbuf >= w->line+w->bpl+NMATCH) {
+ if(gobbleline(w) == ERROR)
+ return ERROR;
+ data = shiftwindow(w, data, edata);
+ }
+ if(data != edata) {
+ fprintf(w->f, "data != edata. uh oh\n");
+ return ERROR; /* can't happen */
+ }
+ return 0;
+}
+
+/*
+ * functions from the Plan9/Brazil drawing libraries
+ */
+static int
+bytesperline(Rectangle r, int ld)
+{
+ ulong ws, l, t;
+ int bits = 8;
+
+ ws = bits>>ld; /* pixels per unit */
+ if(r.min.x >= 0){
+ l = (r.max.x+ws-1)/ws;
+ l -= r.min.x/ws;
+ }else{ /* make positive before divide */
+ t = (-r.min.x)+ws-1;
+ t = (t/ws)*ws;
+ l = (t+r.max.x+ws-1)/ws;
+ }
+ return l;
+}
+
+static int
+rgb2cmap(int cr, int cg, int cb)
+{
+ int r, g, b, v, cv;
+
+ if(cr < 0)
+ cr = 0;
+ else if(cr > 255)
+ cr = 255;
+ if(cg < 0)
+ cg = 0;
+ else if(cg > 255)
+ cg = 255;
+ if(cb < 0)
+ cb = 0;
+ else if(cb > 255)
+ cb = 255;
+ r = cr>>6;
+ g = cg>>6;
+ b = cb>>6;
+ cv = cr;
+ if(cg > cv)
+ cv = cg;
+ if(cb > cv)
+ cv = cb;
+ v = (cv>>4)&3;
+ return 255-((((r<<2)+v)<<4)+(((g<<2)+b+v-r)&15));
+}
+
+/*
+ * go the other way; not currently used.
+ *
+static long
+cmap2rgb(int c)
+{
+ int j, num, den, r, g, b, v, rgb;
+
+ c = 255-c;
+ r = c>>6;
+ v = (c>>4)&3;
+ j = (c-v+r)&15;
+ g = j>>2;
+ b = j&3;
+ den=r;
+ if(g>den)
+ den=g;
+ if(b>den)
+ den=b;
+ if(den==0) {
+ v *= 17;
+ rgb = (v<<16)|(v<<8)|v;
+ }
+ else{
+ num=17*(4*den+v);
+ rgb = ((r*num/den)<<16)|((g*num/den)<<8)|(b*num/den);
+ }
+ return rgb;
+}
+ *
+ *
+ */
diff --git a/devices/gdevijs.c b/devices/gdevijs.c
new file mode 100644
index 000000000..5520716ad
--- /dev/null
+++ b/devices/gdevijs.c
@@ -0,0 +1,1462 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+/*
+ * IJS device for Ghostscript.
+ * Intended to work with any IJS compliant inkjet driver, including
+ * hpijs 1.0 and later, an IJS-enhanced gimp-print driver, and
+ * the IJS Windows GDI server (ijsmswin.exe).
+ *
+ * DRAFT
+ *
+ * WARNING: The ijs server can be selected on the gs command line
+ * which is a security risk, since any program can be run.
+ * You should use -dSAFER which sets .LockSafetyParams to true
+ * before opening this device.
+ *
+ * 11/26/03 David Suffield (gdevijs-krgb-1.0.patch)
+ * (c) 2003-2004 Copyright Hewlett-Packard Development Company, LP
+ *
+ * 1. Removed hpijs 1.0-1.0.2 workarounds, use hpijs 1.0.3 or higher.
+ * 2. Added krgb support.
+ *
+ * 02/21/05 David Suffield (gdevijs-krgb-1.1.patch)
+ * 1. Fixed segfault issue with 1-bit color space.
+ * 2. Fixed z-order issue with colored text on black rectangle.
+ *
+ * 02/22/06 David Suffield (gdevijs-krgb-1.2.patch)
+ * 1. Fixed krgb buffer overflow issue with out-of-band data in fill_rectangle and copy_mono.
+ * This buffer overflow condition occurred with fullbleed print jobs that had k-band images.
+ * 2. Added Dan Coby (artifex) fix for gsijs_read_string_malloc gs_free *str memory leak.
+ *
+ * 06/02/06 David Suffield (gdevijs-krgb-1.3.patch)
+ * 1. Revisited the krgb buffer overflow issue with out-of-band data in fill_rectangle and
+ * copy_mono. Changed the fill_rectangle and copy_mono to an inner loop buffer check
+ * instead of a outer loop x/y extent check.
+ * 2. As requested by Ralph Giles, added K 1-bit and 8-bit support for krgb, but only 1-bit is
+ * implemented for now.
+ *
+ * KRGB definition:
+ * 1. K=1-bit or 8-bit black plane, RGB=24 bit color raster.
+ * 2. K-plane will only contain objects that are black text and black line drawings.
+ * 3. RGB raster will not contain K-plane objects.
+ * 4. K resolution and RGB resolution will be equal.
+ * 5. K-plane will be byte aligned.
+ * 6. K-plane 1-bit definition; 1=black, 0=nothing (KRGB).
+ * 7. K-plane 8-bit definition; 255=black, 0=nothing (KxRGB).
+ *
+ * 1/15/08 David Suffield (gdevijs-krgb-1.5.patch)
+ * 1. Added checks for null forward device in the graphic procedures.
+ * 2. Corrected the "force banding" code in gsijs_open. Needed for small images (IE: hagaki in landscape).
+ *
+ */
+
+#include "unistd_.h" /* for dup() */
+#include <stdlib.h>
+#include <fcntl.h>
+#include "gdevprn.h"
+#include "gp.h"
+#include "ijs/ijs.h"
+#include "ijs/ijs_client.h"
+
+#if 0
+#define KRGB_DEBUG
+#endif
+
+/* This should go into gdevprn.h, or, better yet, gdevprn should
+ acquire an API for changing resolution. */
+int gdev_prn_maybe_realloc_memory(gx_device_printer *pdev,
+ gdev_prn_space_params *old_space,
+ int old_width, int old_height,
+ bool old_page_uses_transparency);
+
+/* Device procedures */
+
+/* See gxdevice.h for the definitions of the procedures. */
+static dev_proc_open_device(gsijs_open);
+static dev_proc_close_device(gsijs_close);
+static dev_proc_output_page(gsijs_output_page);
+static dev_proc_get_params(gsijs_get_params);
+static dev_proc_put_params(gsijs_put_params);
+static dev_proc_finish_copydevice(gsijs_finish_copydevice);
+
+/* Following definitions are for krgb support. */
+static dev_proc_create_buf_device(gsijs_create_buf_device);
+static dev_proc_fill_rectangle(gsijs_fill_rectangle);
+static dev_proc_copy_mono(gsijs_copy_mono);
+static dev_proc_fill_mask(gsijs_fill_mask);
+static dev_proc_fill_path(gsijs_fill_path);
+static dev_proc_stroke_path(gsijs_stroke_path);
+
+static const gx_device_procs gsijs_procs = {
+ gsijs_open,
+ NULL, /* get_initial_matrix */
+ NULL, /* sync_output */
+ gsijs_output_page,
+ gsijs_close,
+ gx_default_rgb_map_rgb_color,
+ gx_default_rgb_map_color_rgb,
+ NULL, /* fill_rectangle */
+ NULL, /* tile_rectangle */
+ NULL, /* copy_mono */
+ NULL, /* copy_color */
+ NULL, /* draw_line */
+ NULL, /* get_bits */
+ gsijs_get_params,
+ gsijs_put_params,
+ NULL, /* map_cmyk_color */
+ NULL, /* get_xfont_procs */
+ NULL, /* get_xfont_device */
+ NULL, /* map_rgb_alpha_color */
+ gx_page_device_get_page_device,
+ NULL, /* get_alpha_bits */
+ NULL, /* copy_alpha */
+ NULL, /* get_band */
+ NULL, /* copy_rop */
+ NULL, /* fill_path */
+ NULL, /* stroke_path */
+ NULL, /* fill_mask */
+ NULL, /* fill_trapezoid */
+ NULL, /* fill_parallelogram */
+ NULL, /* fill_triangle */
+ NULL, /* draw_thin_line */
+ NULL, /* begin_image */
+ NULL, /* image_data */
+ NULL, /* end_image */
+ NULL, /* strip_tile_rectangle */
+ NULL, /* strip_copy_rop, */
+ NULL, /* get_clipping_box */
+ NULL, /* begin_typed_image */
+ NULL, /* get_bits_rectangle */
+ NULL, /* map_color_rgb_alpha */
+ NULL, /* create_compositor */
+ NULL, /* get_hardware_params */
+ NULL, /* text_begin */
+ gsijs_finish_copydevice
+};
+
+typedef struct gx_device_ijs_s gx_device_ijs;
+
+/* The device descriptor */
+struct gx_device_ijs_s {
+ gx_device_common;
+ gx_prn_device_common;
+ bool IjsUseOutputFD;
+ char IjsServer[gp_file_name_sizeof]; /* name of executable ijs server */
+ char *ColorSpace;
+ int ColorSpace_size;
+ int BitsPerSample;
+ char *DeviceManufacturer;
+ int DeviceManufacturer_size;
+ char *DeviceModel;
+ int DeviceModel_size;
+ char *IjsParams;
+ int IjsParams_size;
+
+ /* Common setpagedevice parameters supported by ijs but not
+ currently parsed by gx_prn_device. We prefix these with Ijs to
+ avoid namespace collision if they do get added to gx_prn_device.
+ */
+ bool IjsTumble;
+ bool IjsTumble_set;
+
+ IjsClientCtx *ctx;
+ int ijs_version;
+
+ /* Additional parameters for krgb support. */
+ int krgb_mode; /* 0=false, 1=true */
+ int k_bits; /* number of bits in k plane, 1 or 8 */
+ int k_path; /* k plane path, 0=false, 1=true */
+ int k_width; /* k plane width in pixels */
+ int k_band_size; /* k plane buffer size in bytes, byte aligned */
+ unsigned char *k_band; /* k plane buffer */
+ gx_device_procs prn_procs; /* banding playback procedures */
+};
+
+#define DEFAULT_DPI 74 /* See gsijs_set_resolution() below. */
+
+gx_device_ijs gs_ijs_device =
+{
+ prn_device_std_body(gx_device_ijs, gsijs_procs, "ijs",
+ DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
+ DEFAULT_DPI, DEFAULT_DPI,
+ 0, 0, 0, 0,
+ 24 /* depth */, NULL /* print page */),
+ FALSE, /* IjsUseOutputFD */
+ "", /* IjsServer */
+ NULL, /* ColorSpace */
+ 0, /* ColorSpace_size */
+ 8, /* BitsPerSample */
+ NULL, /* DeviceManufacturer */
+ 0, /* DeviceManufacturer_size */
+ NULL, /* DeviceModel */
+ 0, /* DeviceModel_size */
+ NULL, /* IjsParams */
+ 0, /* IjsParams_size */
+
+ FALSE, /* Tumble */
+ FALSE, /* Tumble_set */
+
+ NULL, /* IjsClient *ctx */
+ 0, /* ijs_version */
+ 0, /* krgb_mode */
+ 0, /* k_bits */
+ 0, /* k_path */
+ 0, /* k_width */
+ 0, /* k_band_size */
+ NULL /* k_band buffer */
+};
+
+static int gsijs_client_set_param(gx_device_ijs *ijsdev, const char *key,
+ const char *value);
+static int gsijs_set_color_format(gx_device_ijs *ijsdev);
+static int gsijs_read_int(gs_param_list *plist, gs_param_name pname,
+ int *pval, int min_value, int max_value, bool only_when_closed);
+static int gsijs_read_bool(gs_param_list *plist, gs_param_name pname,
+ bool *pval, bool only_when_closed);
+static int gsijs_read_string(gs_param_list * plist, gs_param_name pname,
+ char * str, uint size, bool safety, bool only_when_closed);
+
+/**************************************************************************/
+
+/* ---------------- Low-level graphic procedures ---------------- */
+
+static unsigned char xmask[] =
+{
+ 0x80, /* x=0 */
+ 0x40, /* 1 */
+ 0x20, /* 2 */
+ 0x10, /* 3 */
+ 0x08, /* 4 */
+ 0x04, /* 5 */
+ 0x02, /* 6 */
+ 0x01 /* 7 */
+};
+
+static int gsijs_fill_rectangle(gx_device * dev, int x, int y, int w, int h,
+ gx_color_index color)
+{
+ gx_device_ijs *ijsdev = (gx_device_ijs *)((gx_device_forward *)dev)->target;
+
+ if (!ijsdev)
+ return 0; /* no forward device, bug?? */
+
+ if (ijsdev->krgb_mode && ijsdev->k_path && y >= 0 && x >= 0)
+ {
+ int raster = (ijsdev->k_width+7) >> 3;
+ register unsigned char *dest;
+ int dest_start_bit;
+ int band_height = ijsdev->k_band_size/raster;
+ int i,j;
+ unsigned char *beg = ijsdev->k_band;
+ unsigned char *end = ijsdev->k_band+ijsdev->k_band_size;
+ unsigned char *p;
+
+ if (h <= 0 || w <= 0)
+ return 0;
+
+ /* Check for out-of-band graphic. */
+ if (x >= ijsdev->k_width || y >= band_height)
+ return 0; /* out-of-band */
+
+ dest_start_bit = x & 7;
+ dest=ijsdev->k_band+(raster*y)+(x >> 3);
+
+ /* Note x,y orgin 0,0 is stored first byte 0 left to right. */
+
+ if (color==0x0)
+ {
+ /* Color is black, store in k plane band instead of regular band. */
+ for (j=0; j<h; j++)
+ {
+ for (i=0; i<w; i++)
+ {
+ p = &dest[(dest_start_bit+i)>>3];
+ if (p >= beg && p <= end)
+ *p |= xmask[(dest_start_bit+i)&7];
+ }
+ dest+=raster;
+ }
+ return 0;
+ }
+ else
+ {
+ /* Color is not black, remove any k plane bits for z-order dependencies, store in regular band. */
+ for (j=0; j<h; j++)
+ {
+ for (i=0; i<w; i++)
+ {
+ p = &dest[(dest_start_bit+i)>>3];
+ if (p >= beg && p <= end)
+ *p &= ~xmask[(dest_start_bit+i)&7];
+ }
+ dest+=raster;
+ }
+ }
+ }
+
+ return (*ijsdev->prn_procs.fill_rectangle)(dev, x, y, w, h, color);
+}
+
+static int gsijs_copy_mono(gx_device * dev, const byte * data,
+ int dx, int draster, gx_bitmap_id id,
+ int x, int y, int w, int height, gx_color_index zero, gx_color_index one)
+{
+ gx_device_ijs *ijsdev = (gx_device_ijs *)((gx_device_forward *)dev)->target;
+
+ if (!ijsdev)
+ return 0; /* no forward device, bug?? */
+
+ /* if (ijsdev->krgb_mode && ijsdev->k_path && one==0x0) */
+ if (ijsdev->krgb_mode && ijsdev->k_path)
+ {
+ /* Store in k plane band instead of regular band. */
+ int raster = (ijsdev->k_width+7) >> 3; /* raster width in bytes, byte aligned */
+ register unsigned char *dest;
+ register const unsigned char *scan;
+ int dest_start_bit;
+ int scan_start_bit;
+ int band_height = ijsdev->k_band_size/raster;
+ int i,h=height;
+ unsigned char *beg = ijsdev->k_band;
+ unsigned char *end = ijsdev->k_band+ijsdev->k_band_size;
+ unsigned char *p;
+
+ if (h <= 0 || w <= 0)
+ return 0;
+
+ /* Check for out-of-band graphic. */
+ if (x >= ijsdev->k_width || y >= band_height)
+ return 0; /* out-of-band */
+
+ scan=data+(dx >> 3);
+ dest_start_bit = x & 7;
+ scan_start_bit = dx & 7;
+ dest=ijsdev->k_band+(raster*y)+(x >> 3);
+
+ if (one==0x0)
+ {
+ /* Color is black, store in k plane band instead of regular band. */
+ while (h-- > 0)
+ {
+ for (i=0; i<w; i++)
+ {
+ if (scan[(scan_start_bit+i)>>3] & xmask[(scan_start_bit+i)&7])
+ {
+ p = &dest[(dest_start_bit+i)>>3];
+ if (p >= beg && p <= end)
+ *p |= xmask[(dest_start_bit+i)&7];
+ }
+ }
+ scan+=draster;
+ dest+=raster;
+ }
+ return 0;
+ }
+ else
+ {
+ /* Color is not black, remove any k plane bits for z-order dependencies, store in regular band. */
+ while (h-- > 0)
+ {
+ for (i=0; i<w; i++)
+ {
+ if (scan[(scan_start_bit+i)>>3] & xmask[(scan_start_bit+i)&7])
+ {
+ p = &dest[(dest_start_bit+i)>>3];
+ if (p >= beg && p <= end)
+ *p &= ~xmask[(dest_start_bit+i)&7];
+ }
+ }
+ scan+=draster;
+ dest+=raster;
+ }
+ }
+ }
+
+ return (*ijsdev->prn_procs.copy_mono)(dev, data, dx, draster, id, x, y, w, height, zero, one);
+}
+
+/* ---------------- High-level graphic procedures ---------------- */
+
+static int gsijs_fill_mask(gx_device * dev,
+ const byte * data, int dx, int raster, gx_bitmap_id id,
+ int x, int y, int w, int h,
+ const gx_drawing_color * pdcolor, int depth,
+ gs_logical_operation_t lop, const gx_clip_path * pcpath)
+{
+ gx_device_ijs *ijsdev = (gx_device_ijs *)((gx_device_forward *)dev)->target;
+ int code;
+
+ if (!ijsdev)
+ return 0; /* no forward device, bug?? */
+
+ ijsdev->k_path = 1;
+
+ code = (*ijsdev->prn_procs.fill_mask)(dev, data, dx, raster, id, x, y, w, h, pdcolor, depth, lop, pcpath);
+
+ ijsdev->k_path = 0;
+
+ return code;
+}
+
+static int gsijs_fill_path(gx_device * dev, const gs_imager_state * pis,
+ gx_path * ppath, const gx_fill_params * params,
+ const gx_drawing_color * pdcolor,
+ const gx_clip_path * pcpath)
+{
+ gx_device_ijs *ijsdev = (gx_device_ijs *)((gx_device_forward *)dev)->target;
+ int code;
+
+ if (!ijsdev)
+ return 0; /* no forward device, bug?? */
+
+ ijsdev->k_path = 1;
+
+ code = (*ijsdev->prn_procs.fill_path)(dev, pis, ppath, params, pdcolor, pcpath);
+
+ ijsdev->k_path = 0;
+
+ return 0;
+}
+
+static int gsijs_stroke_path(gx_device * dev, const gs_imager_state * pis,
+ gx_path * ppath, const gx_stroke_params * params,
+ const gx_drawing_color * pdcolor,
+ const gx_clip_path * pcpath)
+{
+ gx_device_ijs *ijsdev = (gx_device_ijs *)((gx_device_forward *)dev)->target;
+ int code;
+
+ if (!ijsdev)
+ return 0; /* no forward device, bug?? */
+
+ ijsdev->k_path = 1;
+
+ code = (*ijsdev->prn_procs.stroke_path)(dev, pis, ppath, params, pdcolor, pcpath);
+
+ ijsdev->k_path = 0;
+
+ return code;
+}
+
+/* ---------------- krgb banding playback procedures ---------------- */
+
+static int gsijs_get_bits(gx_device_printer * pdev, int y, byte * str, byte ** actual_data)
+{
+ gx_device_ijs *ijsdev = (gx_device_ijs *)pdev;
+ gx_device_clist_common *cdev = (gx_device_clist_common *)pdev;
+ int band_height = cdev->page_info.band_params.BandHeight;
+ int band_number = y/band_height;
+ int raster = (ijsdev->k_width+7) >> 3; /* raster width in bytes, byte aligned */
+ int y1=raster*(y-(band_height*band_number));
+
+ if (y1 == 0)
+ {
+ /* First raster for band, clear k_band. Banding playback occurs on first raster. */
+ memset(ijsdev->k_band, 0, ijsdev->k_band_size);
+ }
+
+ return gdev_prn_get_bits(pdev, y, str, actual_data); /* get raster from regular band */
+}
+
+static int gsijs_k_get_bits(gx_device_printer * pdev, int y, byte ** actual_data)
+{
+ gx_device_ijs *ijsdev = (gx_device_ijs *)pdev;
+ gx_device_clist_common *cdev = (gx_device_clist_common *)pdev;
+ int band_height = cdev->page_info.band_params.BandHeight;
+ int band_number = y/band_height;
+ int raster = (ijsdev->k_width+7) >> 3; /* raster width in bytes, byte aligned */
+ int y1=raster*(y-(band_height*band_number));
+
+ *actual_data = ijsdev->k_band+y1;
+
+ return 0;
+}
+
+static int gsijs_create_buf_device(gx_device **pbdev, gx_device *target, int y,
+ const gx_render_plane_t *render_plane, gs_memory_t *mem, gx_color_usage_t *for_band)
+{
+ gx_device_ijs *ijsdev = (gx_device_ijs *)target;
+ int n_chan = ijsdev->color_info.num_components;
+ int code = gx_default_create_buf_device(pbdev, target, y, render_plane, mem, for_band);
+ if (code < 0 || n_chan != 3)
+ return code;
+
+ /* Save buffer (vector) procedures so that we can hook them during banding playback. */
+ ijsdev->prn_procs = (*pbdev)->procs;
+
+ /* Replace buffer procedures with krgb procedures. */
+ set_dev_proc(*pbdev, fill_rectangle, gsijs_fill_rectangle);
+ set_dev_proc(*pbdev, copy_mono, gsijs_copy_mono);
+ set_dev_proc(*pbdev, fill_mask, gsijs_fill_mask);
+ set_dev_proc(*pbdev, fill_path, gsijs_fill_path);
+ set_dev_proc(*pbdev, stroke_path, gsijs_stroke_path);
+
+ return code;
+}
+
+/* See if IJS server supports krgb. */
+static int
+gsijs_set_krgb_mode(gx_device_ijs *ijsdev)
+{
+ char buf[256];
+ int n_chan = ijsdev->color_info.num_components;
+ int code;
+
+ ijsdev->krgb_mode = 0; /* default is no krgb */
+
+ if (n_chan != 3)
+ return 0; /* no krgb support, not RGB colorspace */
+
+ buf[0] = 0;
+ code = ijs_client_enum_param(ijsdev->ctx, 0, "ColorSpace", buf, sizeof(buf)-1);
+ if (code >= 0)
+ buf[code] = 0;
+ if (strstr(buf, "KRGB") != NULL)
+ {
+ ijsdev->krgb_mode = 1; /* yes KRGB is supported */
+ ijsdev->k_bits = 1; /* KRGB = 1x8x8x8 */
+ }
+ else if (strstr(buf, "KxRGB") != NULL)
+ {
+ ijsdev->krgb_mode = 1; /* yes KRGB is supported */
+ ijsdev->k_bits = 8; /* KRGB = 8x8x8x8 */
+ }
+
+ return 0;
+}
+
+/* ------ Private definitions ------ */
+
+static int
+gsijs_parse_wxh (const char *val, int size, double *pw, double *ph)
+{
+ char buf[256];
+ char *tail;
+ int i;
+
+ for (i = 0; i < size; i++)
+ if (val[i] == 'x')
+ break;
+
+ if (i + 1 >= size)
+ return IJS_ESYNTAX;
+
+ if (i >= sizeof(buf))
+ return IJS_EBUF;
+
+ memcpy (buf, val, i);
+ buf[i] = 0;
+ *pw = strtod (buf, &tail);
+ if (tail == buf)
+ return IJS_ESYNTAX;
+
+ if (size - i > sizeof(buf))
+ return IJS_EBUF;
+
+ memcpy (buf, val + i + 1, size - i - 1);
+ buf[size - i - 1] = 0;
+ *ph = strtod (buf, &tail);
+ if (tail == buf)
+ return IJS_ESYNTAX;
+
+ return 0;
+}
+
+/**
+ * gsijs_set_generic_params: Set generic IJS parameters.
+ **/
+static int
+gsijs_set_generic_params(gx_device_ijs *ijsdev)
+{
+ char buf[256];
+ int code = 0;
+ int i, j;
+ char *value;
+
+ /* Split IjsParams into separate parameters and send to ijs server */
+ value = NULL;
+ for (i=0, j=0; (j < ijsdev->IjsParams_size) && (i < sizeof(buf)-1); j++) {
+ char ch = ijsdev->IjsParams[j];
+ if (ch == '\\') {
+ j++;
+ buf[i++] = ijsdev->IjsParams[j];
+ }
+ else {
+ if (ch == '=') {
+ buf[i++] = '\0';
+ value = &buf[i];
+ }
+ else
+ buf[i++] = ch;
+ if (ch == ',') {
+ buf[i-1] = '\0';
+ if (value)
+ gsijs_client_set_param(ijsdev, buf, value);
+ i = 0;
+ value = NULL;
+ }
+ }
+ }
+ if (value)
+ code = gsijs_client_set_param(ijsdev, buf, value);
+
+ if (code == 0 && ijsdev->Duplex_set) {
+ code = gsijs_client_set_param(ijsdev, "PS:Duplex",
+ ijsdev->Duplex ? "true" : "false");
+ }
+ if (code == 0 && ijsdev->IjsTumble_set) {
+ code = gsijs_client_set_param(ijsdev, "PS:Tumble",
+ ijsdev->IjsTumble ? "true" :
+ "false");
+ }
+ return code;
+}
+
+/**
+ * gsijs_set_margin_params: Do margin negotiation with IJS server.
+ **/
+static int
+gsijs_set_margin_params(gx_device_ijs *ijsdev)
+{
+ char buf[256];
+ int code = 0;
+ int i, j;
+ char *value;
+
+ /* Split IjsParams into separate parameters and send to ijs server */
+ value = NULL;
+ for (i=0, j=0; (j < ijsdev->IjsParams_size) && (i < sizeof(buf)-1); j++) {
+ char ch = ijsdev->IjsParams[j];
+ if (ch == '\\') {
+ j++;
+ buf[i++] = ijsdev->IjsParams[j];
+ }
+ else {
+ if (ch == '=') {
+ buf[i++] = '\0';
+ value = &buf[i];
+ }
+ else
+ buf[i++] = ch;
+ if (ch == ',') {
+ buf[i-1] = '\0';
+ if (value)
+ gsijs_client_set_param(ijsdev, buf, value);
+ i = 0;
+ value = NULL;
+ }
+ }
+ }
+ if (value)
+ code = gsijs_client_set_param(ijsdev, buf, value);
+
+ if (code == 0 && ijsdev->Duplex_set) {
+ code = gsijs_client_set_param(ijsdev, "Duplex",
+ ijsdev->Duplex ? "true" : "false");
+ }
+ if (code == 0 && ijsdev->IjsTumble_set) {
+ code = gsijs_client_set_param(ijsdev, "Tumble",
+ ijsdev->IjsTumble ? "true" :
+ "false");
+ }
+
+ if (code == 0) {
+ gs_sprintf (buf, "%gx%g", ijsdev->MediaSize[0] * (1.0 / 72),
+ ijsdev->MediaSize[1] * (1.0 / 72));
+ code = ijs_client_set_param(ijsdev->ctx, 0, "PaperSize",
+ buf, strlen(buf));
+ }
+
+ if (code == 0) {
+ double printable_width, printable_height;
+ double printable_left, printable_top;
+ float m[4];
+
+ code = ijs_client_get_param(ijsdev->ctx, 0, "PrintableArea",
+ buf, sizeof(buf));
+ if (code == IJS_EUNKPARAM)
+ /* IJS server doesn't support margin negotiations.
+ That's ok. */
+ return 0;
+ else if (code >= 0) {
+ code = gsijs_parse_wxh (buf, code,
+ &printable_width, &printable_height);
+ }
+
+ if (code == 0) {
+ code = ijs_client_get_param(ijsdev->ctx, 0, "PrintableTopLeft",
+ buf, sizeof(buf));
+ if (code == IJS_EUNKPARAM)
+ return 0;
+ else if (code >= 0) {
+ code = gsijs_parse_wxh(buf, code,
+ &printable_left, &printable_top);
+ }
+ }
+
+ if (code == 0) {
+ m[0] = printable_left;
+ m[3] = printable_top;
+ m[2] = ijsdev->MediaSize[0] * (1.0 / 72) -
+ printable_left - printable_width;
+ m[1] = ijsdev->MediaSize[1] * (1.0 / 72) -
+ printable_top - printable_height;
+ gx_device_set_margins((gx_device *)ijsdev, m, true);
+ gs_sprintf (buf, "%gx%g", printable_left, printable_top);
+ code = ijs_client_set_param(ijsdev->ctx, 0, "TopLeft",
+ buf, strlen(buf));
+ }
+ }
+
+ return code;
+}
+
+/**
+ * gsijs_set_resolution: Set resolution.
+ *
+ * The priority order, highest first, is: commandline -r switch,
+ * if specified; IJS get_param of Dpi; 72 dpi default.
+ *
+ * Because Ghostscript doesn't have a really good way to detect
+ * whether resolution was set on the command line, we set a
+ * low-probability resolution (DEFAULT_DPI) in the static
+ * initialization of the device, then detect whether it has been
+ * changed from that. This causes a minor infelicity: if DEFAULT_DPI
+ * is set on the command line, it is changed to the default here.
+ **/
+static int
+gsijs_set_resolution(gx_device_ijs *ijsdev)
+{
+ char buf[256];
+ int code;
+ double x_dpi, y_dpi;
+ int width = ijsdev->width;
+ int height = ijsdev->height;
+ bool save_is_open = ijsdev->is_open;
+
+ if (ijsdev->HWResolution[0] != DEFAULT_DPI ||
+ ijsdev->HWResolution[1] != DEFAULT_DPI) {
+ /* Resolution has been set on command line. */
+ return 0;
+ }
+ code = ijs_client_get_param(ijsdev->ctx, 0, "Dpi",
+ buf, sizeof(buf));
+ if (code >= 0) {
+ int i;
+
+ for (i = 0; i < code; i++)
+ if (buf[i] == 'x')
+ break;
+ if (i == code) {
+ char *tail;
+
+ if (i == sizeof(buf))
+ code = IJS_EBUF;
+ buf[i] = 0;
+ x_dpi = y_dpi = strtod (buf, &tail);
+ if (tail == buf)
+ code = IJS_ESYNTAX;
+ } else {
+ double x, y;
+
+ code = gsijs_parse_wxh(buf, code, &x, &y);
+ x_dpi = x;
+ y_dpi = y;
+ }
+ }
+
+ if (code < 0) {
+ x_dpi = 72.0;
+ y_dpi = 72.0;
+ }
+
+ gx_device_set_resolution((gx_device *)ijsdev, x_dpi, y_dpi);
+
+ ijsdev->is_open = true;
+ code = gdev_prn_maybe_realloc_memory((gx_device_printer *)ijsdev,
+ &ijsdev->space_params, width, height,
+ ijsdev->page_uses_transparency);
+ ijsdev->is_open = save_is_open;
+ return code;
+}
+
+/* Open the gsijs driver */
+static int
+gsijs_open(gx_device *dev)
+{
+ gx_device_ijs *ijsdev = (gx_device_ijs *)dev;
+ int code;
+ char buf[256];
+ bool use_outputfd;
+ int fd = -1;
+
+ if (strlen(ijsdev->IjsServer) == 0) {
+ emprintf(dev->memory, "ijs server not specified\n");
+ return gs_note_error(gs_error_ioerror);
+ }
+
+ ijsdev->space_params.banding_type = BandingAlways; /* always force banding */
+
+ /* Set create_buf_device in printer device, so that we can hook the banding playback procedures. */
+ ijsdev->printer_procs.buf_procs.create_buf_device = gsijs_create_buf_device;
+
+ /* Decide whether to use OutputFile or OutputFD. Note: how to
+ determine this is a tricky question, so we just allow the
+ user to set it.
+ */
+ use_outputfd = ijsdev->IjsUseOutputFD;
+
+ /* If using OutputFilename, we don't want to open the output file.
+ Leave that to the ijs server. */
+ ijsdev->OpenOutputFile = use_outputfd;
+
+ code = gdev_prn_open(dev);
+ if (code < 0)
+ return code;
+
+ if (use_outputfd) {
+ /* Note: dup() may not be portable to all interesting IJS
+ platforms. In that case, this branch should be #ifdef'ed out.
+ */
+ fd = dup(fileno(ijsdev->file));
+ if (fd < 0) {
+ emprintf(ijsdev->memory, "dup() failed\n");
+ return gs_note_error(gs_error_ioerror);
+ }
+ }
+
+ /* WARNING: Ghostscript should be run with -dSAFER to stop
+ * someone changing the ijs server (e.g. running a shell).
+ */
+ ijsdev->ctx = ijs_invoke_server(ijsdev->IjsServer);
+ if (ijsdev->ctx == (IjsClientCtx *)NULL) {
+ emprintf1(ijsdev->memory,
+ "Can't start ijs server \042%s\042\n", ijsdev->IjsServer);
+ return gs_note_error(gs_error_ioerror);
+ }
+
+ ijsdev->ijs_version = ijs_client_get_version (ijsdev->ctx);
+
+ if (ijs_client_open(ijsdev->ctx) < 0) {
+ emprintf(ijsdev->memory, "Can't open ijs\n");
+ return gs_note_error(gs_error_ioerror);
+ }
+ if (ijs_client_begin_job(ijsdev->ctx, 0) < 0) {
+ emprintf(ijsdev->memory, "Can't begin ijs job 0\n");
+ ijs_client_close(ijsdev->ctx);
+ return gs_note_error(gs_error_ioerror);
+ }
+
+ if (use_outputfd) {
+ /* Note: dup() may not be portable to all interesting IJS
+ platforms. In that case, this branch should be #ifdef'ed out.
+ */
+ gs_sprintf(buf, "%d", fd);
+ ijs_client_set_param(ijsdev->ctx, 0, "OutputFD", buf, strlen(buf));
+ close(fd);
+ } else {
+ ijs_client_set_param(ijsdev->ctx, 0, "OutputFile",
+ ijsdev->fname, strlen(ijsdev->fname));
+ }
+
+ if (code >= 0 && ijsdev->DeviceManufacturer)
+ code = ijs_client_set_param(ijsdev->ctx, 0, "DeviceManufacturer",
+ ijsdev->DeviceManufacturer,
+ strlen(ijsdev->DeviceManufacturer));
+
+ if (code >= 0 && ijsdev->DeviceModel)
+ code = ijs_client_set_param(ijsdev->ctx, 0, "DeviceModel",
+ ijsdev->DeviceModel,
+ strlen(ijsdev->DeviceModel));
+
+ if (code >= 0)
+ code = gsijs_set_generic_params(ijsdev);
+
+ if (code >= 0)
+ code = gsijs_set_resolution(ijsdev);
+
+ if (code >= 0)
+ code = gsijs_set_margin_params(ijsdev);
+
+ if (code >= 0)
+ code = gsijs_set_krgb_mode(ijsdev);
+
+ return code;
+}
+
+/* Finish device initialization. */
+static int
+gsijs_finish_copydevice(gx_device *dev, const gx_device *from_dev)
+{
+ int code;
+ static const char rgb[] = "DeviceRGB";
+ gx_device_ijs *ijsdev = (gx_device_ijs *)dev;
+
+ code = gx_default_finish_copydevice(dev, from_dev);
+ if(code < 0)
+ return code;
+
+ if (!ijsdev->ColorSpace) {
+ ijsdev->ColorSpace = gs_malloc(ijsdev->memory, sizeof(rgb), 1,
+ "gsijs_finish_copydevice");
+ if (!ijsdev->ColorSpace)
+ return gs_note_error(gs_error_VMerror);
+ ijsdev->ColorSpace_size = sizeof(rgb);
+ memcpy(ijsdev->ColorSpace, rgb, sizeof(rgb));
+ }
+ return code;
+}
+
+/* Close the gsijs driver */
+static int
+gsijs_close(gx_device *dev)
+{
+ gx_device_ijs *ijsdev = (gx_device_ijs *)dev;
+ int code;
+
+ /* ignore ijs errors on close */
+ ijs_client_end_job(ijsdev->ctx, 0);
+ ijs_client_close(ijsdev->ctx);
+ ijs_client_begin_cmd(ijsdev->ctx, IJS_CMD_EXIT);
+ ijs_client_send_cmd_wait(ijsdev->ctx);
+
+ code = gdev_prn_close(dev);
+ if (ijsdev->IjsParams)
+ gs_free(dev->memory, ijsdev->IjsParams,
+ ijsdev->IjsParams_size, 1, "gsijs_read_string_malloc");
+ if (ijsdev->ColorSpace)
+ gs_free(dev->memory, ijsdev->ColorSpace,
+ ijsdev->ColorSpace_size, 1, "gsijs_read_string_malloc");
+ if (ijsdev->DeviceManufacturer)
+ gs_free(dev->memory, ijsdev->DeviceManufacturer,
+ ijsdev->DeviceManufacturer_size, 1, "gsijs_read_string_malloc");
+ if (ijsdev->DeviceModel)
+ gs_free(dev->memory, ijsdev->DeviceModel,
+ ijsdev->DeviceModel_size, 1, "gsijs_read_string_malloc");
+ ijsdev->IjsParams = NULL;
+ ijsdev->IjsParams_size = 0;
+ ijsdev->DeviceManufacturer = NULL;
+ ijsdev->DeviceManufacturer_size = 0;
+ ijsdev->DeviceModel = NULL;
+ ijsdev->DeviceModel_size = 0;
+ return code;
+}
+
+/* This routine is entirely analagous to gdev_prn_print_scan_lines(),
+ but computes width instead of height. It is not specific to IJS,
+ and a strong case could be made for moving it into gdevprn.c. */
+static int
+gsijs_raster_width(gx_device *pdev)
+{
+ int width = pdev->width;
+ gs_matrix imat;
+ float xscale;
+ int right, offset, end;
+
+ (*dev_proc(pdev, get_initial_matrix)) (pdev, &imat);
+ xscale = imat.xx * 72.0;
+ right = (int)(dev_r_margin(pdev) * xscale);
+ offset = (int)(dev_x_offset(pdev) * xscale);
+ end = offset + width - right;
+ return min(width, end);
+}
+
+/* Print a page. Don't use normal printer gdev_prn_output_page
+ * because it opens the output file.
+ */
+static int
+gsijs_output_page(gx_device *dev, int num_copies, int flush)
+{
+ gx_device_ijs *ijsdev = (gx_device_ijs *)dev;
+ gx_device_printer *pdev = (gx_device_printer *)dev;
+ int raster = gdev_prn_raster(pdev);
+ int ijs_width, ijs_height;
+ int row_bytes, k_row_bytes=0;
+ int n_chan = pdev->color_info.num_components;
+ int krgb_mode = ijsdev->krgb_mode;
+ int k_bits = ijsdev->k_bits;
+ unsigned char *data;
+ char buf[256];
+ double xres = pdev->HWResolution[0];
+ double yres = pdev->HWResolution[1];
+ int code = 0;
+ int endcode = 0;
+ int status = 0;
+ int i, y;
+
+ if ((data = gs_alloc_bytes(pdev->memory, raster, "gsijs_output_page"))
+ == (unsigned char *)NULL)
+ return gs_note_error(gs_error_VMerror);
+
+ /* Determine bitmap width and height */
+ ijs_height = gdev_prn_print_scan_lines(dev);
+ ijs_width = gsijs_raster_width(dev);
+
+ row_bytes = (ijs_width * pdev->color_info.depth + 7) >> 3;
+
+ if (krgb_mode)
+ {
+ gx_device_clist_common *cdev = (gx_device_clist_common *)dev;
+ int band_height = cdev->page_info.band_params.BandHeight;
+ k_row_bytes = (ijs_width + 7) >> 3;
+
+ /* Create banding buffer for k plane. */
+ ijsdev->k_width = ijs_width;
+ ijsdev->k_band_size = band_height * k_row_bytes;
+ if ((ijsdev->k_band = gs_malloc(pdev->memory, ijsdev->k_band_size, 1, "gsijs_output_page")) == (unsigned char *)NULL)
+ return gs_note_error(gs_error_VMerror);
+ }
+
+ /* Required page parameters */
+ gs_sprintf(buf, "%d", n_chan);
+ gsijs_client_set_param(ijsdev, "NumChan", buf);
+ gs_sprintf(buf, "%d", ijsdev->BitsPerSample);
+ gsijs_client_set_param(ijsdev, "BitsPerSample", buf);
+
+ /* This needs to become more sophisticated for DeviceN. */
+ strcpy(buf, (n_chan == 4) ? "DeviceCMYK" :
+ ((n_chan == 3) ? (krgb_mode ? ((k_bits == 1) ? "KRGB" : "KxRGB") : "DeviceRGB") : "DeviceGray"));
+ gsijs_client_set_param(ijsdev, "ColorSpace", buf);
+
+ gs_sprintf(buf, "%d", ijs_width);
+ gsijs_client_set_param(ijsdev, "Width", buf);
+ gs_sprintf(buf, "%d", ijs_height);
+ gsijs_client_set_param(ijsdev, "Height", buf);
+
+ gs_sprintf(buf, "%gx%g", xres, yres);
+ gsijs_client_set_param(ijsdev, "Dpi", buf);
+
+#ifdef KRGB_DEBUG
+ int kfd, rgbfd;
+ char sz[128];
+ kfd = open("/tmp/k.pbm", O_CREAT | O_TRUNC | O_RDWR, 0644);
+ rgbfd = open("/tmp/rgb.ppm", O_CREAT | O_TRUNC | O_RDWR, 0644);
+ snprintf(sz, sizeof(sz), "P4\n#gdevijs test\n%d\n%d\n", ijs_width, ijs_height);
+ write(kfd, sz, strlen(sz));
+ snprintf(sz, sizeof(sz), "P6\n#gdevijs test\n%d\n%d\n255\n", ijs_width, ijs_height);
+ write(rgbfd, sz, strlen(sz));
+#endif
+
+ for (i=0; i<num_copies; i++) {
+ unsigned char *actual_data;
+ ijs_client_begin_cmd (ijsdev->ctx, IJS_CMD_BEGIN_PAGE);
+ status = ijs_client_send_cmd_wait(ijsdev->ctx);
+
+ for (y = 0; y < ijs_height; y++) {
+ if (krgb_mode)
+ code = gsijs_get_bits(pdev, y, data, &actual_data);
+ else
+ code = gdev_prn_get_bits(pdev, y, data, &actual_data);
+ if (code < 0)
+ break;
+#ifdef KRGB_DEBUG
+ write(rgbfd, actual_data, row_bytes);
+#endif
+ status = ijs_client_send_data_wait(ijsdev->ctx, 0, (char *)actual_data, row_bytes);
+ if (status)
+ break;
+
+ if (krgb_mode) {
+ code = gsijs_k_get_bits(pdev, y, &actual_data);
+ if (code < 0)
+ break;
+#ifdef KRGB_DEBUG
+ write(kfd, actual_data, k_row_bytes);
+#endif
+ status = ijs_client_send_data_wait(ijsdev->ctx, 0, (char *)actual_data, k_row_bytes);
+ if (status)
+ break;
+ }
+ }
+ ijs_client_begin_cmd(ijsdev->ctx, IJS_CMD_END_PAGE);
+ status = ijs_client_send_cmd_wait(ijsdev->ctx);
+ }
+
+#ifdef KRGB_DEBUG
+ close(kfd);
+ close(rgbfd);
+#endif
+
+ if(krgb_mode)
+ gs_free(pdev->memory, ijsdev->k_band, ijsdev->k_band_size, 1, "gsijs_output_page");
+
+ gs_free_object(pdev->memory, data, "gsijs_output_page");
+
+ endcode = (PRINTER_IS_CLIST(pdev) &&
+ !((gx_device_clist_common *)pdev)->do_not_open_or_close_bandfiles ?
+ clist_finish_page((gx_device *)pdev, flush) : 0);
+
+ if (endcode < 0)
+ return endcode;
+
+ if (code < 0)
+ return endcode;
+
+ if (status < 0)
+ return gs_note_error(gs_error_ioerror);
+
+ code = gx_finish_output_page(dev, num_copies, flush);
+ return code;
+}
+
+/**************************************************************************/
+
+/* Get device parameters */
+static int
+gsijs_get_params(gx_device *dev, gs_param_list *plist)
+{
+ gx_device_ijs *ijsdev = (gx_device_ijs *)dev;
+ gs_param_string gps;
+ int code = gdev_prn_get_params(dev, plist);
+
+ if (code >= 0) {
+ param_string_from_transient_string(gps, ijsdev->IjsServer);
+ code = param_write_string(plist, "IjsServer", &gps);
+ }
+
+ if (code >= 0) {
+ if (ijsdev->DeviceManufacturer) {
+ param_string_from_transient_string(gps,
+ ijsdev->DeviceManufacturer);
+ code = param_write_string(plist, "DeviceManufacturer", &gps);
+ } else {
+ code = param_write_null(plist, "DeviceManufacturer");
+ }
+ }
+
+ if (code >= 0) {
+ if (ijsdev->DeviceModel) {
+ param_string_from_transient_string(gps, ijsdev->DeviceModel);
+ code = param_write_string(plist, "DeviceModel", &gps);
+ } else {
+ code = param_write_null(plist, "DeviceModel");
+ }
+ }
+
+ if (code >= 0) {
+ if (ijsdev->IjsParams) {
+ param_string_from_transient_string(gps, ijsdev->IjsParams);
+ code = param_write_string(plist, "IjsParams", &gps);
+ } else {
+ code = param_write_null(plist, "IjsParams");
+ }
+ }
+
+ if (code >= 0)
+ code = param_write_int(plist, "BitsPerSample", &ijsdev->BitsPerSample);
+
+ if (code >= 0)
+ code = param_write_bool(plist, "IjsUseOutputFD",
+ &ijsdev->IjsUseOutputFD);
+
+ if (code >= 0) {
+ if (ijsdev->IjsTumble_set) {
+ code = param_write_bool(plist, "Tumble", &ijsdev->IjsTumble);
+ } else {
+ code = param_write_null(plist, "Tumble");
+ }
+ }
+
+ return code;
+}
+
+static int
+gsijs_read_int(gs_param_list *plist, gs_param_name pname, int *pval,
+ int min_value, int max_value, bool only_when_closed)
+{
+ int code = 0;
+ int new_value;
+
+ switch (code = param_read_int(plist, pname, &new_value)) {
+ case 0:
+ if (only_when_closed && (new_value != *pval)) {
+ code = gs_error_rangecheck;
+ goto e;
+ }
+ if ((new_value >= min_value) && (new_value <= max_value)) {
+ *pval = new_value;
+ break;
+ }
+ code = gs_note_error(gs_error_rangecheck);
+ goto e;
+ default:
+ if (param_read_null(plist, pname) == 0)
+ return 1;
+ e:param_signal_error(plist, pname, code);
+ case 1:
+ ;
+ }
+ return code;
+}
+
+static int
+gsijs_read_bool(gs_param_list *plist, gs_param_name pname, bool *pval,
+ bool only_when_closed)
+{
+ int code = 0;
+ bool new_value;
+
+ switch (code = param_read_bool(plist, pname, &new_value)) {
+ case 0:
+ if (only_when_closed && (new_value != *pval)) {
+ code = gs_error_rangecheck;
+ goto e;
+ }
+ *pval = new_value;
+ break;
+ default:
+ if (param_read_null(plist, pname) == 0) {
+ return 1;
+ }
+ e:param_signal_error(plist, pname, code);
+ case 1:
+ ;
+ }
+ return code;
+}
+
+static int
+gsijs_read_string(gs_param_list *plist, gs_param_name pname, char *str,
+ uint size, bool safety, bool only_when_closed)
+{
+ int code;
+ gs_param_string new_value;
+ int differs;
+
+ switch (code = param_read_string(plist, pname, &new_value)) {
+ case 0:
+ differs = bytes_compare(new_value.data, new_value.size,
+ (const byte *)str, strlen(str));
+ if (safety && differs) {
+ code = gs_error_invalidaccess;
+ goto e;
+ }
+ if (only_when_closed && differs) {
+ code = gs_error_rangecheck;
+ goto e;
+ }
+ if (new_value.size < size) {
+ strncpy(str, (const char *)new_value.data, new_value.size);
+ str[new_value.size+1] = '\0';
+ break;
+ }
+ code = gs_note_error(gs_error_rangecheck);
+ goto e;
+ default:
+ if (param_read_null(plist, pname) == 0)
+ return 1;
+ e:param_signal_error(plist, pname, code);
+ case 1:
+ ;
+ }
+ return code;
+}
+
+static int
+gsijs_read_string_malloc(gs_param_list *plist, gs_param_name pname, char **str,
+ int *size, bool only_when_closed)
+{
+ int code;
+ gs_param_string new_value;
+ int differs;
+
+ switch (code = param_read_string(plist, pname, &new_value)) {
+ case 0:
+ differs = bytes_compare(new_value.data, new_value.size,
+ (const byte *)(*str ? *str : ""),
+ *str ? strlen(*str) : 0);
+ if (only_when_closed && differs) {
+ code = gs_error_rangecheck;
+ goto e;
+ }
+ if (new_value.size + 1 != *size) {
+ if (*str)
+ gs_free(plist->memory, *str, *size, 1,
+ "gsijs_read_string_malloc");
+ *str = NULL;
+ *size = 0;
+ }
+ if (*str == NULL)
+ *str = gs_malloc(plist->memory, new_value.size + 1, 1,
+ "gsijs_read_string_malloc");
+ if (*str == NULL) {
+ code = gs_note_error(gs_error_VMerror);
+ goto e;
+ }
+ *size = new_value.size + 1;
+ strncpy(*str, (const char *)new_value.data, new_value.size);
+ (*str)[new_value.size] = '\0';
+ break;
+ default:
+ if (param_read_null(plist, pname) == 0)
+ return 1;
+ e:param_signal_error(plist, pname, code);
+ case 1:
+ ;
+ }
+ return code;
+}
+
+static int
+gsijs_put_params(gx_device *dev, gs_param_list *plist)
+{
+ gx_device_ijs *ijsdev = (gx_device_ijs *)dev;
+ int code = 0;
+ bool is_open = dev->is_open;
+
+ /* We allow duplex to be set in all cases. At some point, it may
+ be worthwhile to query the device to see if it supports
+ duplex. Note also that this code will get called even before
+ the device has been opened, which is when the -DDuplex
+ command line is processed. */
+ if (ijsdev->Duplex_set < 0) {
+ ijsdev->Duplex = 1;
+ ijsdev->Duplex_set = 0;
+ }
+
+ /* If a parameter must not be changed after the device is open,
+ * the last parameter of gsijs_read_xxx() is is_open.
+ * If a parameter may be changed at any time, it is false.
+ */
+ if (code >= 0)
+ code = gsijs_read_string(plist, "IjsServer",
+ ijsdev->IjsServer, sizeof(ijsdev->IjsServer),
+ dev->LockSafetyParams, is_open);
+
+ if (code >= 0)
+ code = gsijs_read_string_malloc(plist, "DeviceManufacturer",
+ &ijsdev->DeviceManufacturer, &ijsdev->DeviceManufacturer_size,
+ is_open);
+
+ if (code >= 0)
+ code = gsijs_read_string_malloc(plist, "DeviceModel",
+ &ijsdev->DeviceModel, &ijsdev->DeviceModel_size,
+ is_open);
+
+ if (code >= 0)
+ code = gsijs_read_string_malloc(plist, "IjsParams",
+ &(ijsdev->IjsParams), &(ijsdev->IjsParams_size), is_open);
+
+ if (code >= 0)
+ code = gsijs_read_int(plist, "BitsPerSample", &ijsdev->BitsPerSample,
+ 1, 16, is_open);
+
+ if (code >= 0)
+ code = gsijs_read_bool(plist, "IjsUseOutputFD",
+ &ijsdev->IjsUseOutputFD, is_open);
+
+ if (code >= 0) {
+ code = gsijs_read_string_malloc(plist, "ProcessColorModel",
+ &ijsdev->ColorSpace, &ijsdev->ColorSpace_size, is_open);
+ }
+
+ if (code >= 0) {
+ code = gsijs_read_bool(plist, "Tumble", &ijsdev->IjsTumble, false);
+ if (code == 0)
+ ijsdev->IjsTumble_set = true;
+ }
+
+ if (code >= 0)
+ code = gsijs_set_color_format(ijsdev);
+
+ if (code >= 0)
+ code = gdev_prn_put_params(dev, plist);
+
+ if (code >= 0 && is_open) {
+ code = gsijs_set_generic_params(ijsdev);
+ if (code >= 0)
+ code = gsijs_set_margin_params(ijsdev);
+ if (code < 0)
+ return gs_note_error(gs_error_ioerror);
+ }
+
+ return code;
+}
+
+static int
+gsijs_client_set_param(gx_device_ijs *ijsdev, const char *key,
+ const char *value)
+{
+ int code = ijs_client_set_param(ijsdev->ctx, 0 /* job id */,
+ key, value, strlen(value));
+ if (code < 0)
+ dmprintf2(ijsdev->memory, "ijs: Can't set parameter %s=%s\n", key, value);
+ return code;
+}
+
+static int
+gsijs_set_color_format(gx_device_ijs *ijsdev)
+{
+ gx_device_color_info dci = ijsdev->color_info;
+ int components; /* 1=gray, 3=RGB, 4=CMYK */
+ int bpc = ijsdev->BitsPerSample; /* bits per component */
+ int maxvalue;
+ const char *ColorSpace = ijsdev->ColorSpace;
+
+ if (ColorSpace == NULL)
+ ColorSpace = "DeviceRGB";
+
+ if (!strcmp (ColorSpace, "DeviceGray")) {
+ components = 1;
+ if (bpc == 1) {
+ ijsdev->procs.map_rgb_color = gx_default_w_b_map_rgb_color;
+ ijsdev->procs.map_color_rgb = gx_default_w_b_map_color_rgb;
+ } else {
+ ijsdev->procs.map_rgb_color = gx_default_gray_map_rgb_color;
+ ijsdev->procs.map_color_rgb = gx_default_gray_map_color_rgb;
+ }
+ ijsdev->procs.encode_color = gx_default_gray_fast_encode;
+ ijsdev->procs.decode_color = gx_default_decode_color;
+ dci.polarity = GX_CINFO_POLARITY_ADDITIVE;
+ dci.gray_index = 0;
+ } else if (!strcmp (ColorSpace, "DeviceRGB")) {
+ components = 3;
+ ijsdev->procs.map_rgb_color = gx_default_rgb_map_rgb_color;
+ ijsdev->procs.map_color_rgb = gx_default_rgb_map_color_rgb;
+ ijsdev->procs.encode_color = gx_default_rgb_map_rgb_color;
+ ijsdev->procs.decode_color = gx_default_rgb_map_color_rgb;
+ dci.polarity = GX_CINFO_POLARITY_ADDITIVE;
+ dci.gray_index = GX_CINFO_COMP_NO_INDEX;
+ } else if (!strcmp (ColorSpace, "DeviceCMYK")) {
+ components = 4;
+ ijsdev->procs.map_cmyk_color = cmyk_8bit_map_cmyk_color;
+ ijsdev->procs.map_color_rgb = cmyk_8bit_map_color_rgb;
+ ijsdev->procs.encode_color = cmyk_8bit_map_cmyk_color;
+ ijsdev->procs.decode_color = gx_default_decode_color;
+ dci.polarity = GX_CINFO_POLARITY_SUBTRACTIVE;
+ dci.gray_index = 3;
+ } else {
+ return -1;
+ }
+
+ maxvalue = (1 << bpc) - 1;
+ dci.max_components = components;
+ dci.num_components = components;
+ dci.depth = bpc * components;
+ dci.max_gray = maxvalue;
+ dci.max_color = components > 1 ? maxvalue : 0;
+ dci.dither_grays = maxvalue+1;
+ dci.dither_colors = components > 1 ? maxvalue+1 : 0;
+
+ dci.separable_and_linear = GX_CINFO_SEP_LIN;
+ dci.cm_name = ColorSpace;
+
+ ijsdev->color_info = dci;
+
+ set_linear_color_bits_mask_shift((gx_device *)ijsdev);
+
+ return 0;
+}
diff --git a/devices/gdevimgn.c b/devices/gdevimgn.c
new file mode 100644
index 000000000..8562f109f
--- /dev/null
+++ b/devices/gdevimgn.c
@@ -0,0 +1,569 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+/*
+ * Imagen ImPRESS printer driver - version 1.4
+ *
+ * This driver uses the Impress bitmap operation to print the page image.
+ */
+
+/* Written by Alan Millar (AMillar@bolis.sf-bay.org) August 4 1992.
+ Basic bitmap dump. */
+/* Updated by Alan Millar Sept 21 1992. Added resolution handling
+ for 75, 150, and 300 dpi. */
+/* Updated by Alan Millar June 05 1993. General cleanup for
+ beta test release. */
+/* Updated by Alan Millar June 21 1993. v1.3. Combined multipage
+ output into single imPress document. Quote fewer special
+ chars in byte stream mode. imPress document header options
+ can be set from environment variable IMPRESSHEADER */
+/* Updated by Alan Millar July 04 1993. v1.4.
+ New makefile option USE_BYTE_STREAM instead of changing source.
+ Swatch output redone to eliminate ALL blank swatches (swatchMap).
+ Buffer copying changed to multi-byte operations (BIGTYPE).
+ Page margins and A4 paper settings fixed, at least for Canon CX.
+ */
+
+/* -------------------------------------------------------- */
+/* Instructions:
+
+ - Add "imagen.dev" to DEVICE_DEVS in the makefile. For example:
+ DEVICE_DEVS2=laserjet.dev imagen.dev
+
+ - Include or exclude USE_BYTE_STREAM in makefile as appropriate
+ If you are compiling on Unix, re-run "tar_cat" to update the makefile
+ from devs.mak
+
+ - At run time, specify the resolution on the GS command line
+ by using -r300 or -r150 or -r75
+ - At run time, specify any imPress document options in
+ the IMPRESSHEADER environment variable.
+ */
+
+/* -------------------------------------------------------- */
+/* Hardware/software combinations tested:
+ - ImageStation IP3 8/300 with parallel byte-stream interface,
+ using GS 2.6.1 on Linux with GCC 2.3.3;
+ earlier using GS 2.5.1 on MS-Dos with Turbo C++ 1.0
+ - Sequenced-packet-protocol interface untested.
+ */
+/* -------------------------------------------------------- */
+/* Bugs/Enhancements:
+ - Driver does not use any Impress language features for
+ drawing lines/arcs
+ - Driver does not use resident or downloadable fonts.
+ - Buffer output instead of system call for each byte?
+ */
+
+/* -------------------------------------------------------- */
+#include "gdevprn.h"
+#include "gxobj.h"
+/* #include <stdio.h> should not be used in drivers */
+#include <stdlib.h>
+
+/* -------------------------------------------------------- */
+/* Working Constants */
+
+/* Byte stream quoting: convert special characters to hex.
+ Specify by including/excluding -DUSE_BYTE_STREAM in makefile.
+ This should match printer's hardware interface configuration.
+ If printer interface is serial with sequenced-packet-protocol
+ spooler software (ImageStation config# 11 = 01), then don't use it.
+ Imagen "ipr" spooler software should not use byte stream.
+ If printer interface is Centronics parallel byte stream,
+ (ImageStation config# 11 = 03), then use byte stream. */
+
+#ifdef USE_BYTE_STREAM
+# define BYTE_STREAM 1
+#else
+# define BYTE_STREAM 0
+#endif
+
+/* Byte stream quote character (ImageStation config# 15).
+ Only needed when using byte stream */
+#define QUOTE_CHAR (char) 0x02
+/* Byte stream end-of-file character (ImageStation config# 14). */
+#define EOF_CHAR (char) 0x04
+/* Other special characters to quote. Put them here if spooler or
+ hardware uses flow control, etc. If not needed, set to
+ a redundant value such as EOF_CHAR */
+#define EXTRA_QUOTE1 (char) 0x11 /* ^Q */
+#define EXTRA_QUOTE2 (char) 0x13 /* ^S */
+#define EXTRA_QUOTE3 EOF_CHAR
+#define EXTRA_QUOTE4 EOF_CHAR
+
+/* -------------------------------------------------------- */
+/* imPress header default options.
+ Can be overridden at run-time with IMPRESSHEADER env variable */
+
+#define IMPRESSHEADER "jobheader onerror, prerasterization off"
+
+/* -------------------------------------------------------- */
+
+#define CANON_CX
+
+/* Printer engine max resolution. 300 for Canon CX models such as
+ ImageStation IP3. Others (240?) unverified */
+#ifdef CANON_CX
+# define MAX_DPI 300
+#endif
+#ifndef MAX_DPI
+# define MAX_DPI 300
+#endif
+
+/* Determine imPress scaling factor from GS resolution.
+ Magnify can be 0, 1, or 2.
+ 0 = MAX_DPI, 1 = MAX_DPI / 2, 2 = MAX_DPI / 4
+ Assuming MAX_DPI is 300, you can specify -r75 or -r150
+ or -r300 on the GS command line */
+#define getMagnification ( \
+ ( pdev->x_pixels_per_inch > (MAX_DPI >> 1) ) ? 0 : \
+ ( pdev->x_pixels_per_inch > (MAX_DPI >> 2) ) ? 1 : \
+ 2 )
+
+/* Page dimensions from gdevprn.h - specify -DA4 in makefile for A4 paper */
+#define WIDTH_10THS DEFAULT_WIDTH_10THS
+#define HEIGHT_10THS DEFAULT_HEIGHT_10THS
+
+/* Width in inches of unprintable edge of paper. May need fine tuning.
+ Canon CX engine in ImageStation IP3 8/300 will only print 8 inches
+ wide on any paper size. May vary for other engines */
+
+#ifdef CANON_CX
+# define MARG_L 0.15
+# define MARG_R ( (float)WIDTH_10THS / 10.0 - 8.0 - MARG_L)
+#endif
+#ifndef MARG_L
+# define MARG_L 0.2
+#endif
+#ifndef MARG_R
+# define MARG_R 0.2
+#endif
+#define MARG_T 0.1
+#define MARG_B 0.2
+
+/* Flag for displaying debug messages at run-time. Higher
+ number = higher detail */
+#define IM_DEBUG 0
+#define DebugMsg(Level,P1,P2) if (Level<=IM_DEBUG) {errprintf_nomem(P1,P2 );}
+
+/*-------------------------------------------*/
+ /* Impress bitmaps are made up of 32x32 bit swatches.
+ A swatch is four bytes (32 bits) wide by 32 bytes high,
+ totalling 128 bytes. */
+#define HorzBytesPerSw 4
+#define HorzBitsPerSw (HorzBytesPerSw * 8)
+#define VertBytesPerSw 32
+#define TotalBytesPerSw (HorzBytesPerSw * VertBytesPerSw)
+
+/*-------------------------------------------*/
+/* Attempt at optimization to something faster than byte-by-byte copying.
+ imPress swatches are 4 bytes wide, so type must align on a 4-byte
+ boundary. Swatch interleaving restricts the copy to 4 bytes in a row.
+ Type must be numeric where value is zero when all bytes in it are zero. */
+#if arch_sizeof_long == 4
+# define BIGTYPE unsigned long int
+#else
+# if arch_sizeof_short == 4
+# define BIGTYPE unsigned short int
+# else
+# if arch_sizeof_short == 2
+# define BIGTYPE unsigned short
+# endif
+# endif
+#endif
+#ifndef BIGTYPE
+#define BIGTYPE byte
+#endif
+
+#define BIGSIZE ( sizeof( BIGTYPE ) )
+
+/*-------------------------------------------*/
+/* IMAGEN imPress Command opcodes */
+/* from DVIIMP.C */
+#define iSP 128 /* advance one space */
+#define iSP1 129 /* advance one space + 1 pixel */
+#define iMPLUS 131 /* Move one pixel forward */
+#define iMMINUS 132 /* Move one pixel back */
+#define iMMOVE 133 /* Move in main advance direction */
+#define iSMOVE 134 /* Move in secondary advance direction */
+
+#define iABS_H 135 /* Move to H position */
+#define iREL_H 136 /* Move in H direction */
+#define iABS_V 137 /* Move to V position */
+#define iREL_V 138 /* Move in V direction */
+
+#define iCRLF 197 /* move to beginning of next line */
+
+#define iSET_HV_SYSTEM 205 /* Define new coordinate system */
+#define iSET_ADV_DIRS 206 /* Define advance directions */
+
+#define iPAGE 213 /* Set H and V to 0 */
+#define iENDPAGE 219 /* print the current page */
+
+#define iBITMAP 235 /* Print a full bitmap */
+#define iSET_MAGNIFICATION 236
+ /* magnify the page by 1, 2, 4 */
+#define iNOOP 254 /* no operation */
+#define iEOF 255 /* end of impress document */
+
+/*-------------------------------------------*/
+/*-------------------------------------------*/
+/* The device descriptor */
+
+static dev_proc_print_page(imagen_print_page);
+static dev_proc_open_device(imagen_prn_open);
+static dev_proc_close_device(imagen_prn_close);
+
+/* Since the print_page doesn't alter the device, this device can print in the background */
+gx_device_procs imagen_procs =
+ prn_procs(imagen_prn_open, gdev_prn_bg_output_page, imagen_prn_close);
+
+#define ppdev ((gx_device_printer *)pdev)
+
+/*-------------------------------------------*/
+const gx_device_printer far_data gs_imagen_device =
+ prn_device(/*prn_std_procs*/ imagen_procs,
+ "imagen",
+ WIDTH_10THS,
+ HEIGHT_10THS,
+ MAX_DPI, /* x_dpi */
+ MAX_DPI, /* y_dpi */
+ MARG_L,MARG_R,MARG_T,MARG_B, /* margins */
+ 1, imagen_print_page);
+
+/*-------------------------------------------*/
+
+/*-------------------------------------------*/
+static void
+iWrite(FILE *Out, byte Val)
+{ /* iWrite */
+ const char *hexList = "0123456789ABCDEF";
+
+ /* if we are doing byte-stream, quote characters that would otherwise
+ match EOF and QUOTE itself, or other special chars */
+ /* Imagen quoting takes one character and writes out the QUOTE
+ character followed by the hex digits of the quoted character */
+ if (BYTE_STREAM &&
+ ( Val == QUOTE_CHAR || Val == EOF_CHAR
+ || Val == EXTRA_QUOTE1 || Val == EXTRA_QUOTE2
+ || Val == EXTRA_QUOTE3 || Val == EXTRA_QUOTE4 ) ) {
+ fputc (QUOTE_CHAR, Out);
+ fputc ((char) hexList[Val / 0x10], Out);
+ fputc ((char) hexList[Val % 0x10], Out);
+ } else { /* quoted char */
+ /* Not doing quoting, just send it out */
+ fputc(Val, Out);
+ } /* quoted char */
+} /* iWrite */
+
+/* Write out 16bit, high byte first */
+static void
+iWrite2(FILE *Out, int Val)
+{ /* iWrite2 */
+ iWrite(Out,(byte) (Val >> 8) & 0x00FF );
+ iWrite(Out,(byte) Val & 0x00FF );
+} /* iWrite2 */
+
+/* --------------------------------------------------------- */
+
+static int
+imagen_prn_open(gx_device *pdev)
+{ /* imagen_prn_open */
+ int code;
+
+ const char *impHeader;
+
+ /* ----------------------------------------- */
+ DebugMsg(1,"%s\n","Start of imagen_prn_open");
+ DebugMsg(2,"BIGSIZE = %ld \n",BIGSIZE);
+
+ code = gdev_prn_open(pdev);
+ if ( code < 0 ) return code;
+
+ /* ----------------------------------------- */
+
+ DebugMsg(2,"opening file: %s\n",ppdev->fname);
+ code = gdev_prn_open_printer(pdev, 1);
+ if ( code < 0 ) return code;
+
+ impHeader = getenv("IMPRESSHEADER");
+ if (impHeader == NULL ) {
+ impHeader = IMPRESSHEADER ;
+ } /* if impHeader */
+
+ fprintf(ppdev->file,"@document(language impress, %s)",impHeader);
+
+ code = gdev_prn_close_printer(pdev);
+ if ( code < 0 ) return code;
+
+ /* ----------------------------------------- */
+ DebugMsg(1,"%s\n","End of imagen_prn_open");
+
+ return code;
+} /* imagen_prn_open */
+
+static int
+imagen_prn_close(gx_device *pdev)
+{ /* imagen_prn_close */
+ int code;
+
+ /* ----------------------------------------- */
+ DebugMsg(1,"%s\n","Start of imagen_prn_close");
+
+ code = gdev_prn_open_printer(pdev, 1);
+ if ( code < 0 ) return code;
+
+ /* Write imPress end of document marker */
+ iWrite(ppdev->file,iEOF);
+
+ /* And byte stream end of file */
+ if (BYTE_STREAM) {
+ /* DON'T use iWrite because actual EOF should not be quoted! */
+ fputc(EOF_CHAR,ppdev->file);
+ } /* if byte stream */
+
+ fflush(ppdev->file);
+
+ code = gdev_prn_close_printer(pdev);
+ if ( code < 0 ) return code;
+
+ code = gdev_prn_close(pdev);
+
+ DebugMsg(1,"%s\n","End of imagen_prn_close");
+
+ return(code);
+} /* imagen_prn_close */
+
+/*-------------------------------------------*/
+/* Send the page to the printer. */
+static int
+imagen_print_page(gx_device_printer *pdev, FILE *prn_stream)
+{
+ int line_size = gdev_mem_bytes_per_scan_line((gx_device *)pdev);
+ const int align_size = obj_align_round((line_size/BIGSIZE)+1);
+ /* input buffer: one line of bytes rasterized by gs */
+ byte *in = (byte *)gs_malloc(pdev->memory, BIGSIZE, align_size, "imagen_print_page(in)");
+ /* output buffer: 32 lines, interleaved into imPress swatches */
+ byte *out;
+ /* working pointer into output buffer */
+ byte *swatch;
+ byte *temp;
+ /* map of which swatches in a row are completely blank, or are non-blank */
+ byte *swatchMap;
+ /* starting line number on page of a row of swatches */
+ int lnum ;
+ /* line number within a row of swatches */
+ int swatchLine;
+ /* ending line number of row of swatches */
+ int lastLine;
+ /* how many swatches can fit on a row */
+ int swatchCount;
+ /* index into row of non-blank swatch */
+ int startSwatch;
+ int endSwatch;
+ /* Scaling factor for resolution */
+ int Magnify;
+ /* page totals */
+ int totalBlankSwatches;
+ int totalGreySwatches;
+
+ /* ----------------------------------------- */
+ /* Start of routine */
+ /* ----------------------------------------- */
+
+ DebugMsg(1,"%s\n","Start of imagen_print_page");
+
+ /* ----------------------------------------- */
+ Magnify = getMagnification ;
+
+ /* Impress bitmaps are made up of 32x32 bit swatches.
+ A swatch is four bytes wide by 32 bytes high.
+ See how many swatches will fit horizontally. */
+
+ swatchCount = (line_size + HorzBytesPerSw - 1) / HorzBytesPerSw;
+
+ totalBlankSwatches = 0 ;
+ totalGreySwatches = 0 ;
+ DebugMsg(2,"Swatch count = %d\n",swatchCount);
+ DebugMsg(2,"Line size = %d\n",line_size );
+
+ out = (byte *)gs_malloc(pdev->memory, TotalBytesPerSw , swatchCount + 1,
+ "imagen_print_page(out)");
+
+ swatchMap = (byte *)gs_malloc(pdev->memory, BIGSIZE,swatchCount / BIGSIZE + 1,
+ "imagen_print_page(swatchMap)" );
+
+ if ( in == 0 || out == 0 )
+ return -1;
+
+ /* Initialize the page */
+ iWrite(prn_stream,iPAGE);
+
+ /* Tell ImPress what resolution we will be using */
+ iWrite(prn_stream,iSET_MAGNIFICATION);
+ iWrite(prn_stream,Magnify);
+
+ /*------------------------------------------------------*/
+ /* main loop down page */
+ lnum = 0;
+ while (lnum <= pdev->height) {
+
+ /* erase swatch map. */
+ for (swatch = swatchMap; swatch < swatchMap + swatchCount ;
+ swatch += BIGSIZE ) {
+ * (BIGTYPE *)swatch = (BIGTYPE) 0;
+ } /* for */
+
+ /* get scan lines to fill swatches */
+ swatchLine = 0;
+ lastLine = VertBytesPerSw - 1;
+
+ /* Check if we don't have a full-height row of swatches at end of page */
+ if (lnum + lastLine > pdev->height ) {
+ /* back up last row so it overlaps with previous. Not a problem
+ on a laser printer, because the overlapping part will be identical */
+ lnum = pdev->height - lastLine ;
+ }; /* not full height */
+
+ DebugMsg (3,"lnum = %d \n",lnum);
+
+ /* ------------------------------------------------------- */
+ /* get 32 lines and interleave into a row of swatches */
+ for (swatchLine = 0 ; swatchLine <= lastLine; swatchLine++) {
+
+ /* blank out end of buffer for BIGSIZE overlap */
+ for (temp = in + line_size; temp < in + align_size*BIGSIZE; temp++){
+ *temp = 0;
+ } /* for temp */
+
+ /* get one line */
+ gdev_prn_copy_scan_lines(pdev, lnum + swatchLine, in, line_size);
+ DebugMsg(5,"Got scan line %d ", lnum + swatchLine);
+ DebugMsg(5,"line %d \n", swatchLine);
+
+ /* interleave scan line into swatch buffer */
+ /* a swatch is a 4 byte * 32 byte square. Swatches are placed
+ next to each other. The first scan line maps into the first
+ four bytes of the first swatch, then the first four of the second
+ swatch, etc.
+ To get this on the page:
+ A1 A1 A1 A1 B1 B1 B1 B1 C1 C1 C1 C1
+ A2 A2 A2 A2 B2 B2 B2 B2 C2 C2 C2 C2
+ ...
+ A32 A32 A32 A32 B32 B32 B32 B32 C32 C32 C32 C32
+ You have to send it as:
+ A1 A1 A1 A1 A2 ... A32 B1 B1 .. B32 C1 C1 ... C32 */
+
+ /* set initial offset into swatch buffer based on which
+ line in the swatch we are processing */
+ swatch = out + swatchLine * HorzBytesPerSw;
+ DebugMsg(5,"offset: swatch = %d \n",(int) (swatch - out) );
+ temp = in;
+ while ( temp < in + line_size ) {
+ /* copy multi-byte to swatch buffer */
+ * (BIGTYPE *)swatch = * (BIGTYPE *)temp;
+ if ( * (BIGTYPE *)temp ) {
+ /* mark map if not blank */
+ swatchMap[(swatch - out)/TotalBytesPerSw] = (byte) 1 ;
+ } /* if not zero */
+
+ temp += (BIGSIZE > HorzBytesPerSw) ? HorzBytesPerSw : BIGSIZE ;
+ swatch += (BIGSIZE > HorzBytesPerSw) ? HorzBytesPerSw : BIGSIZE ;
+
+ /* if we copied four bytes, skip to next swatch */
+ if ( ((temp - in) % HorzBytesPerSw ) == 0 ) {
+ swatch += (TotalBytesPerSw - HorzBytesPerSw) ;
+ } /* if need to skip */
+ } /* while < line_size */
+
+ } /* for swatchLine */
+
+ /* ------------------------------------------------- */
+ /* we now have full swatches. */
+ /* Send to printer */
+
+ /* go through swatch map to find non-blank swatches.
+ Skip over completely blank swatches */
+ startSwatch = 0;
+ while (startSwatch < swatchCount ) {
+ if (swatchMap[startSwatch] == 0 ) {
+ /* skip blank swatch */
+ DebugMsg(6,"Skip blank %d \n",startSwatch);
+ totalBlankSwatches++;
+ startSwatch++;
+ } else { /* if swatch == 0 */
+ /* we hit a non-blank swatch. */
+ totalGreySwatches++;
+
+ /* See how many there are in a row */
+ endSwatch = startSwatch;
+ while ( (endSwatch < swatchCount) && swatchMap[endSwatch] ) {
+ endSwatch++;
+ totalGreySwatches++;
+ } /* while */
+ /* endSwatch is one past last non-blank swatch */
+ DebugMsg(6,"Grey swatches %d ",startSwatch);
+ DebugMsg(6,"until %d \n",endSwatch);
+
+ /* vertical position: scan line, shifted for magnification */
+ iWrite(prn_stream, iABS_V);
+ iWrite2(prn_stream, lnum << Magnify);
+
+ /* horizontal position = swatch number * 32 bits/swatch */
+ iWrite(prn_stream,iABS_H);
+ iWrite2(prn_stream, startSwatch * HorzBitsPerSw << Magnify );
+ iWrite(prn_stream,iBITMAP); /* start bitmap */
+ iWrite(prn_stream,0x07); /* bit OR with page */
+ iWrite(prn_stream,(endSwatch - startSwatch)); /* horizontal
+ number of swatches */
+ iWrite(prn_stream, 1) ; /* vertical number of swatches */
+ /* write out swatch buffer */
+ for (swatch = out + startSwatch * TotalBytesPerSw;
+ swatch < out + endSwatch * TotalBytesPerSw; swatch++) {
+ iWrite(prn_stream,*swatch);
+ } /* for swatch */
+
+ /* swatches have been printed, see if there are still
+ more in this row */
+ startSwatch = endSwatch;
+ } /* if swatch == 0 */
+
+ } /* while startSwatch */
+
+ /* Whole row of swatches is done. Go on to next row of swatches */
+ lnum += lastLine + 1;
+
+ } /* while lnum */
+
+ /* Eject the page */
+ iWrite(prn_stream,iENDPAGE);
+
+ fflush(prn_stream);
+
+ gs_free(pdev->memory, (char *)out, TotalBytesPerSw, swatchCount+1, "imagen_print_page(out)");
+ gs_free(pdev->memory, (char *)swatchMap, BIGSIZE, swatchCount / BIGSIZE + 1,
+ "imagen_print_page(swatchMap)" );
+ gs_free(pdev->memory, (char *)in, BIGSIZE, align_size, "imagen_print_page(in)");
+ /* ----------------------------------------- */
+
+ DebugMsg(1,"Debug: Grey: %d \n",totalGreySwatches);
+ DebugMsg(1,"Debug: Blank: %d \n",totalBlankSwatches );
+ DebugMsg(1,"%s\n","End of imagen_print_page");
+
+ /* ----------------------------------------- */
+ return 0;
+
+} /* imagen_print_page */
diff --git a/devices/gdevjbig2.c b/devices/gdevjbig2.c
new file mode 100644
index 000000000..ac7205346
--- /dev/null
+++ b/devices/gdevjbig2.c
@@ -0,0 +1,126 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* $Id: gdevjbig2.c 6300 2005-12-28 19:56:24Z giles $ */
+/* JBIG2 encode filter test device */
+
+#include "gdevprn.h"
+#include "stream.h"
+#include "strimpl.h"
+#include "sjbig2_luratech.h"
+
+/* Structure for the JBIG2-writing device. */
+typedef struct gx_device_jbig2_s {
+ gx_device_common;
+ gx_prn_device_common;
+} gx_device_jbig2;
+
+/* ------ The device descriptors ------ */
+
+/* Default X and Y resolution. */
+#ifndef X_DPI
+# define X_DPI 72
+#endif
+#ifndef Y_DPI
+# define Y_DPI 72
+#endif
+
+static dev_proc_print_page(jbig2_print_page);
+
+/* Monochrome only */
+
+const gx_device_printer gs_gdevjbig2_device =
+prn_device(prn_bg_procs, "jbig2", /* The print_page proc is compatible with allowing bg printing */
+ DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
+ X_DPI, Y_DPI, /* resolution */
+ 0, 0, 0, 0, /* margins */
+ 1, jbig2_print_page);
+
+/* Send the page to the file. */
+static int
+jbig2_print_page(gx_device_printer * pdev, FILE * prn_stream)
+{
+ gx_device_jbig2 *jdev = (gx_device_jbig2 *) pdev;
+ gs_memory_t *mem = jdev->memory;
+ int line_size = gdev_mem_bytes_per_scan_line((gx_device *) pdev);
+ byte *in = gs_alloc_bytes(mem, line_size, "jbig2_print_page(in)");
+ byte *fbuf = 0;
+ uint fbuf_size;
+ byte *jbuf = 0;
+ uint jbuf_size;
+ int lnum;
+ int code = 0;
+ stream_jbig2encode_state state;
+ stream fstrm, cstrm;
+
+ if (in == 0) {
+ code = gs_note_error(gs_error_VMerror);
+ goto fail;
+ }
+ /* Create the jbig2encode state. */
+ s_init_state((stream_state *)&state, &s_jbig2encode_template, 0);
+ if (state.templat->set_defaults)
+ (*state.templat->set_defaults) ((stream_state *) & state);
+ state.width = jdev->width;
+ state.height = jdev->height;
+ /* Set up the streams. */
+ fbuf_size = max(512 /* arbitrary */ , state.templat->min_out_size);
+ jbuf_size = state.templat->min_in_size;
+ if ((fbuf = gs_alloc_bytes(mem, fbuf_size, "jbig2_print_page(fbuf)")) == 0 ||
+ (jbuf = gs_alloc_bytes(mem, jbuf_size, "jbig2_print_page(jbuf)")) == 0
+ ) {
+ code = gs_note_error(gs_error_VMerror);
+ goto done;
+ }
+ s_init(&fstrm, mem);
+ swrite_file(&fstrm, prn_stream, fbuf, fbuf_size);
+ s_init(&cstrm, mem);
+ s_std_init(&cstrm, jbuf, jbuf_size, &s_filter_write_procs,
+ s_mode_write);
+ cstrm.state = (stream_state *) & state;
+ cstrm.procs.process = state.templat->process;
+ cstrm.strm = &fstrm;
+ if (state.templat->init)
+ (*state.templat->init) (cstrm.state);
+
+ state.jb2_encode = true;
+
+ /* Copy the data to the output. */
+ for (lnum = 0; lnum < jdev->height; ++lnum) {
+ byte *data;
+ uint ignore_used;
+ int i;
+
+ if (cstrm.end_status) {
+ code = gs_note_error(gs_error_ioerror);
+ goto done;
+ }
+ gdev_prn_get_bits(pdev, lnum, in, &data);
+ sputs(&cstrm, data, state.stride, &ignore_used);
+ }
+
+ /* Wrap up. */
+ sclose(&cstrm);
+ sflush(&fstrm);
+ done:
+ gs_free_object(mem, jbuf, "jbig2_print_page(jbuf)");
+ gs_free_object(mem, fbuf, "jbig2_print_page(fbuf)");
+ gs_free_object(mem, in, "jbig2_print_page(in)");
+ return code;
+ fail:
+ gs_free_object(mem, in, "jbig2_print_page(in)");
+ return code;
+}
diff --git a/devices/gdevjpeg.c b/devices/gdevjpeg.c
new file mode 100644
index 000000000..43e3654c2
--- /dev/null
+++ b/devices/gdevjpeg.c
@@ -0,0 +1,556 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+/* JPEG output driver */
+#include "stdio_.h" /* for jpeglib.h */
+#include "jpeglib_.h"
+#include "gdevprn.h"
+#include "stream.h"
+#include "strimpl.h"
+#include "sdct.h"
+#include "sjpeg.h"
+
+/* Structure for the JPEG-writing device. */
+typedef struct gx_device_jpeg_s {
+ gx_device_common;
+ gx_prn_device_common;
+ /* Additional parameters */
+ int JPEGQ; /* quality on IJG scale */
+ float QFactor; /* quality per DCTEncode conventions */
+ /* JPEGQ overrides QFactor if both are specified. */
+
+ /** 1.0 default 2.0 is twice as big
+ */
+ gs_point ViewScale;
+
+ /** translation needs to have scalefactor multiplied in.
+ */
+ gs_point ViewTrans;
+
+} gx_device_jpeg;
+
+/* The device descriptor */
+static dev_proc_get_params(jpeg_get_params);
+static dev_proc_get_initial_matrix(jpeg_get_initial_matrix);
+static dev_proc_put_params(jpeg_put_params);
+static dev_proc_print_page(jpeg_print_page);
+static dev_proc_map_color_rgb(jpegcmyk_map_color_rgb);
+static dev_proc_map_cmyk_color(jpegcmyk_map_cmyk_color);
+
+/* ------ The device descriptors ------ */
+
+/* Default X and Y resolution. */
+#ifndef X_DPI
+# define X_DPI 72
+#endif
+#ifndef Y_DPI
+# define Y_DPI 72
+#endif
+
+/* 24-bit color */
+
+static const gx_device_procs jpeg_procs =
+{
+ gdev_prn_open,
+ jpeg_get_initial_matrix, /* get_initial_matrix */
+ NULL, /* sync_output */
+/* Since the print_page doesn't alter the device, this device can print in the background */
+ gdev_prn_bg_output_page,
+ gdev_prn_close,
+ gx_default_rgb_map_rgb_color,/* map_rgb_color */
+ gx_default_rgb_map_color_rgb,
+ NULL, /* fill_rectangle */
+ NULL, /* tile_rectangle */
+ NULL, /* copy_mono */
+ NULL, /* copy_color */
+ NULL, /* draw_line */
+ NULL, /* get_bits */
+ jpeg_get_params,
+ jpeg_put_params,
+ NULL,
+ NULL, /* get_xfont_procs */
+ NULL, /* get_xfont_device */
+ NULL, /* map_rgb_alpha_color */
+ gx_page_device_get_page_device
+};
+
+const gx_device_jpeg gs_jpeg_device =
+{prn_device_std_body(gx_device_jpeg, jpeg_procs, "jpeg",
+ DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
+ X_DPI, Y_DPI, 0, 0, 0, 0, 24, jpeg_print_page),
+ 0, /* JPEGQ: 0 indicates not specified */
+ 0.0, /* QFactor: 0 indicates not specified */
+ { 1.0, 1.0 }, /* ViewScale 1 to 1 */
+ { 0.0, 0.0 } /* translation 0 */
+};
+
+/* 8-bit gray */
+
+static const gx_device_procs jpeggray_procs =
+{
+ gdev_prn_open,
+ jpeg_get_initial_matrix, /* get_initial_matrix */
+ NULL, /* sync_output */
+/* Since the print_page doesn't alter the device, this device can print in the background */
+ gdev_prn_bg_output_page,
+ gdev_prn_close,
+ gx_default_gray_map_rgb_color,/* map_rgb_color */
+ gx_default_gray_map_color_rgb,
+ NULL, /* fill_rectangle */
+ NULL, /* tile_rectangle */
+ NULL, /* copy_mono */
+ NULL, /* copy_color */
+ NULL, /* draw_line */
+ NULL, /* get_bits */
+ jpeg_get_params,
+ jpeg_put_params,
+ NULL,
+ NULL, /* get_xfont_procs */
+ NULL, /* get_xfont_device */
+ NULL, /* map_rgb_alpha_color */
+ gx_page_device_get_page_device
+};
+
+const gx_device_jpeg gs_jpeggray_device =
+{prn_device_body(gx_device_jpeg, jpeggray_procs, "jpeggray",
+ DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
+ X_DPI, Y_DPI, 0, 0, 0, 0,
+ 1, 8, 255, 0, 256, 0,
+ jpeg_print_page),
+ 0, /* JPEGQ: 0 indicates not specified */
+ 0.0, /* QFactor: 0 indicates not specified */
+ { 1.0, 1.0 }, /* ViewScale 1 to 1 */
+ { 0.0, 0.0 } /* translation 0 */
+};
+/* 32-bit CMYK */
+
+static const gx_device_procs jpegcmyk_procs =
+{ gdev_prn_open,
+ gx_default_get_initial_matrix,
+/* Since the print_page doesn't alter the device, this device can print in the background */
+ NULL, /* sync_output */
+ gdev_prn_bg_output_page,
+ gdev_prn_close,
+ NULL,
+ jpegcmyk_map_color_rgb,
+ NULL, /* fill_rectangle */
+ NULL, /* tile_rectangle */
+ NULL, /* copy_mono */
+ NULL, /* copy_color */
+ NULL, /* draw_line */
+ NULL, /* get_bits */
+ jpeg_get_params,
+ jpeg_put_params,
+ jpegcmyk_map_cmyk_color,
+ NULL, /* get_xfont_procs */
+ NULL, /* get_xfont_device */
+ NULL, /* map_rgb_alpha_color */
+ gx_page_device_get_page_device /* get_page_device */
+};
+
+const gx_device_jpeg gs_jpegcmyk_device =
+{prn_device_std_body(gx_device_jpeg, jpegcmyk_procs, "jpegcmyk",
+ DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
+ X_DPI, Y_DPI, 0, 0, 0, 0, 32, jpeg_print_page),
+ 0, /* JPEGQ: 0 indicates not specified */
+ 0.0, /* QFactor: 0 indicates not specified */
+ { 1.0, 1.0 }, /* ViewScale 1 to 1 */
+ { 0.0, 0.0 } /* translation 0 */
+};
+
+/* Apparently Adobe Photoshop and some other applications that */
+/* accept JPEG CMYK images expect color values to be inverted. */
+static int
+jpegcmyk_map_color_rgb(gx_device * dev, gx_color_index color,
+ gx_color_value prgb[3])
+{
+ int
+ not_k = color & 0xff,
+ r = not_k - ~(color >> 24),
+ g = not_k - ~((color >> 16) & 0xff),
+ b = not_k - ~((color >> 8) & 0xff);
+
+ prgb[0] = (r < 0 ? 0 : gx_color_value_from_byte(r));
+ prgb[1] = (g < 0 ? 0 : gx_color_value_from_byte(g));
+ prgb[2] = (b < 0 ? 0 : gx_color_value_from_byte(b));
+ return 0;
+}
+
+static gx_color_index
+jpegcmyk_map_cmyk_color(gx_device * dev, const gx_color_value cv[])
+{
+ gx_color_index color = ~(
+ gx_color_value_to_byte(cv[3]) +
+ ((uint)gx_color_value_to_byte(cv[2]) << 8) +
+ ((uint)gx_color_value_to_byte(cv[1]) << 16) +
+ ((uint)gx_color_value_to_byte(cv[0]) << 24));
+
+ return (color == gx_no_color_index ? color ^ 1 : color);
+}
+
+/* Get parameters. */
+static int
+jpeg_get_params(gx_device * dev, gs_param_list * plist)
+{
+ gx_device_jpeg *jdev = (gx_device_jpeg *) dev;
+ int code = gdev_prn_get_params(dev, plist);
+ int ecode;
+ float float2double;
+ if (code < 0)
+ return code;
+
+ if ((ecode = param_write_int(plist, "JPEGQ", &jdev->JPEGQ)) < 0)
+ code = ecode;
+ if ((ecode = param_write_float(plist, "QFactor", &jdev->QFactor)) < 0)
+ code = ecode;
+ float2double = jdev->ViewScale.x;
+ if ((ecode = param_write_float(plist, "ViewScaleX", &float2double)) < 0)
+ code = ecode;
+ float2double = jdev->ViewScale.y;
+ if ((ecode = param_write_float(plist, "ViewScaleY", &float2double)) < 0)
+ code = ecode;
+ float2double = jdev->ViewTrans.x;
+ if ((ecode = param_write_float(plist, "ViewTransX", &float2double)) < 0)
+ code = ecode;
+ float2double = jdev->ViewTrans.y;
+ if ((ecode = param_write_float(plist, "ViewTransY", &float2double)) < 0)
+ code = ecode;
+
+ return code;
+}
+
+/* Put parameters. */
+static int
+jpeg_put_params(gx_device * dev, gs_param_list * plist)
+{
+ gx_device_jpeg *jdev = (gx_device_jpeg *) dev;
+ int ecode = 0;
+ int code;
+ gs_param_name param_name;
+ int jq = jdev->JPEGQ;
+ float qf = jdev->QFactor;
+ float fparam;
+
+ switch (code = param_read_int(plist, (param_name = "JPEGQ"), &jq)) {
+ case 0:
+ if (jq < 0 || jq > 100)
+ ecode = gs_error_limitcheck;
+ else
+ break;
+ goto jqe;
+ default:
+ ecode = code;
+ jqe:param_signal_error(plist, param_name, ecode);
+ case 1:
+ break;
+ }
+
+ switch (code = param_read_float(plist, (param_name = "QFactor"), &qf)) {
+ case 0:
+ if (qf < 0.0 || qf > 1.0e6)
+ ecode = gs_error_limitcheck;
+ else
+ break;
+ goto qfe;
+ default:
+ ecode = code;
+ qfe:param_signal_error(plist, param_name, ecode);
+ case 1:
+ break;
+ }
+
+ code = param_read_float(plist, (param_name = "ViewScaleX"), &fparam);
+ if ( code == 0 ) {
+ if (fparam < 1.0)
+ param_signal_error(plist, param_name, gs_error_limitcheck);
+ else
+ jdev->ViewScale.x = fparam;
+ }
+ else if ( code < 1 ) {
+ ecode = code;
+ param_signal_error(plist, param_name, code);
+ }
+
+ code = param_read_float(plist, (param_name = "ViewScaleY"), &fparam);
+ if ( code == 0 ) {
+ if (fparam < 1.0)
+ param_signal_error(plist, param_name, gs_error_limitcheck);
+ else
+ jdev->ViewScale.y = fparam;
+ }
+ else if ( code < 1 ) {
+ ecode = code;
+ param_signal_error(plist, param_name, code);
+ }
+
+ /* pixels in desired dpi, auto negative ( moves up and left ) */
+ code = param_read_float(plist, (param_name = "ViewTransX"), &fparam);
+ if ( code == 0 ) {
+ jdev->ViewTrans.x = fparam;
+ }
+ else if ( code < 1 ) {
+ ecode = code;
+ param_signal_error(plist, param_name, code);
+ }
+
+ code = param_read_float(plist, (param_name = "ViewTransY"), &fparam);
+ if ( code == 0 ) {
+ jdev->ViewTrans.y = fparam;
+ }
+ else if ( code < 1 ) {
+ ecode = code;
+ param_signal_error(plist, param_name, code);
+ }
+ code = gdev_prn_put_params(dev, plist);
+ if (code < 0)
+ return code;
+
+ if (ecode < 0)
+ return ecode;
+
+ jdev->JPEGQ = jq;
+ jdev->QFactor = qf;
+ return 0;
+}
+
+/******************************************************************
+ This device supports translation and scaling.
+
+0123456
+
+0PPPPPPP 0 is origin
+PPPPPPPP 1 is x1,y1 (2,2)
+PP1vvvPP 2 is x2,y2 (6,6)
+PPvvvvPP v is viewport, P is original page
+PPvvvvPP
+PPPPPP2P
+PPPPPPPP
+
+Given a view port in pixels starting at x1,y1
+where x1 < width, y1 < height in pixels
+
+ViewScaleX = desired Resolution / HWResolution ; 1.0 default
+ViewScaleY = desired Resolution / HWResolution
+
+HWResolutionX = desired dpi at 1:1 scaling ; 72dpi default
+HWResolutionY = desired dpi at 1:1 scaling
+
+ViewTransX = x1 * ViewScaleX ; 0.0 default
+ViewTransY = y1 * ViewScaleY
+
+if initial matrix multiplies ViewScaleX in then translation is limited to
+multiples of the HWResolution.
+
+***************************************************************************/
+
+static void
+jpeg_get_initial_matrix(gx_device *dev, gs_matrix *pmat)
+{
+ gx_device_jpeg *pdev = (gx_device_jpeg *)dev;
+ double fs_res = (dev->HWResolution[0] / 72.0) * pdev->ViewScale.x;
+ double ss_res = (dev->HWResolution[1] / 72.0) * pdev->ViewScale.y;
+
+ /* NB this device has no paper margins */
+
+ switch(pdev->LeadingEdge) {
+ case 1:
+ pmat->xx = 0;
+ pmat->xy = -ss_res;
+ pmat->yx = -fs_res;
+ pmat->yy = 0;
+ pmat->tx = (pdev->width * pdev->ViewScale.x) - pdev->ViewTrans.x;
+ pmat->ty = (pdev->height * pdev->ViewScale.y) - pdev->ViewTrans.y;
+ break;
+ case 2:
+ pmat->xx = -fs_res;
+ pmat->xy = 0;
+ pmat->yx = 0;
+ pmat->yy = ss_res;
+ pmat->tx = (pdev->width * pdev->ViewScale.x) - pdev->ViewTrans.x;
+ pmat->ty = -pdev->ViewTrans.x;
+ break;
+ case 3:
+ pmat->xx = 0;
+ pmat->xy = ss_res;
+ pmat->yx = fs_res;
+ pmat->yy = 0;
+ pmat->tx = -pdev->ViewTrans.x;
+ pmat->ty = -pdev->ViewTrans.y;
+ break;
+ default:
+ case 0:
+ pmat->xx = fs_res;
+ pmat->xy = 0;
+ pmat->yx = 0;
+ pmat->yy = -ss_res;
+ pmat->tx = -pdev->ViewTrans.x;
+ pmat->ty = (pdev->height * pdev->ViewScale.y) - pdev->ViewTrans.y;
+ break;
+ }
+
+}
+
+/* Send the page to the file. */
+static int
+jpeg_print_page(gx_device_printer * pdev, FILE * prn_stream)
+{
+ gx_device_jpeg *jdev = (gx_device_jpeg *) pdev;
+ gs_memory_t *mem = pdev->memory;
+ int line_size = gdev_mem_bytes_per_scan_line((gx_device *) pdev);
+ byte *in = gs_alloc_bytes(mem, line_size, "jpeg_print_page(in)");
+ jpeg_compress_data *jcdp = gs_alloc_struct_immovable(mem, jpeg_compress_data,
+ &st_jpeg_compress_data, "jpeg_print_page(jpeg_compress_data)");
+ byte *fbuf = 0;
+ uint fbuf_size;
+ byte *jbuf = 0;
+ uint jbuf_size;
+ int lnum;
+ int code;
+ stream_DCT_state state;
+ stream fstrm, jstrm;
+
+ if (jcdp == 0 || in == 0) {
+ code = gs_note_error(gs_error_VMerror);
+ goto fail;
+ }
+ /* Create the DCT encoder state. */
+ jcdp->templat = s_DCTE_template;
+ s_init_state((stream_state *)&state, &jcdp->templat, 0);
+ if (state.templat->set_defaults) {
+ state.memory = mem;
+ (*state.templat->set_defaults) ((stream_state *) & state);
+ state.memory = NULL;
+ }
+ state.QFactor = 1.0; /* disable quality adjustment in zfdcte.c */
+ state.ColorTransform = 1; /* default for RGB */
+ /* We insert no markers, allowing the IJG library to emit */
+ /* the format it thinks best. */
+ state.NoMarker = true; /* do not insert our own Adobe marker */
+ state.Markers.data = 0;
+ state.Markers.size = 0;
+ state.data.compress = jcdp;
+ /* Add in ICC profile */
+ state.icc_profile = NULL; /* In case it is not set here */
+ if (pdev->icc_struct != NULL && pdev->icc_struct->device_profile[0] != NULL) {
+ cmm_profile_t *icc_profile = pdev->icc_struct->device_profile[0];
+ if (icc_profile->num_comps == pdev->color_info.num_components &&
+ !(pdev->icc_struct->usefastcolor)) {
+ state.icc_profile = icc_profile;
+ }
+ }
+ /* We need state.memory for gs_jpeg_create_compress().... */
+ jcdp->memory = state.jpeg_memory = state.memory = mem;
+ if ((code = gs_jpeg_create_compress(&state)) < 0)
+ goto fail;
+ /* ....but we need it to be NULL so we don't try to free
+ * the stack based state...
+ */
+ state.memory = NULL;
+ jcdp->cinfo.image_width = pdev->width;
+ jcdp->cinfo.image_height = pdev->height;
+ switch (pdev->color_info.depth) {
+ case 32:
+ jcdp->cinfo.input_components = 4;
+ jcdp->cinfo.in_color_space = JCS_CMYK;
+ break;
+ case 24:
+ jcdp->cinfo.input_components = 3;
+ jcdp->cinfo.in_color_space = JCS_RGB;
+ break;
+ case 8:
+ jcdp->cinfo.input_components = 1;
+ jcdp->cinfo.in_color_space = JCS_GRAYSCALE;
+ break;
+ }
+ /* Set compression parameters. */
+ if ((code = gs_jpeg_set_defaults(&state)) < 0)
+ goto done;
+ if (jdev->JPEGQ > 0) {
+ code = gs_jpeg_set_quality(&state, jdev->JPEGQ, TRUE);
+ if (code < 0)
+ goto done;
+ } else if (jdev->QFactor > 0.0) {
+ code = gs_jpeg_set_linear_quality(&state,
+ (int)(min(jdev->QFactor, 100.0)
+ * 100.0 + 0.5),
+ TRUE);
+ if (code < 0)
+ goto done;
+ }
+ jcdp->cinfo.restart_interval = 0;
+ jcdp->cinfo.density_unit = 1; /* dots/inch (no #define or enum) */
+ jcdp->cinfo.X_density = (UINT16)pdev->HWResolution[0];
+ jcdp->cinfo.Y_density = (UINT16)pdev->HWResolution[1];
+ /* Create the filter. */
+ /* Make sure we get at least a full scan line of input. */
+ state.scan_line_size = jcdp->cinfo.input_components *
+ jcdp->cinfo.image_width;
+ jcdp->templat.min_in_size =
+ max(s_DCTE_template.min_in_size, state.scan_line_size);
+ /* Make sure we can write the user markers in a single go. */
+ jcdp->templat.min_out_size =
+ max(s_DCTE_template.min_out_size, state.Markers.size);
+
+ /* Set up the streams. */
+ fbuf_size = max(512 /* arbitrary */ , jcdp->templat.min_out_size);
+ jbuf_size = jcdp->templat.min_in_size;
+ if ((fbuf = gs_alloc_bytes(mem, fbuf_size, "jpeg_print_page(fbuf)")) == 0 ||
+ (jbuf = gs_alloc_bytes(mem, jbuf_size, "jpeg_print_page(jbuf)")) == 0
+ ) {
+ code = gs_note_error(gs_error_VMerror);
+ goto done;
+ }
+ s_init(&fstrm, mem);
+ swrite_file(&fstrm, prn_stream, fbuf, fbuf_size);
+ s_init(&jstrm, mem);
+ s_std_init(&jstrm, jbuf, jbuf_size, &s_filter_write_procs,
+ s_mode_write);
+ jstrm.state = (stream_state *) & state;
+ jstrm.procs.process = state.templat->process;
+ jstrm.strm = &fstrm;
+ if (state.templat->init)
+ (*state.templat->init) (jstrm.state);
+
+ /* Copy the data to the output. */
+ for (lnum = 0; lnum < pdev->height; ++lnum) {
+ byte *data;
+ uint ignore_used;
+
+ if (jstrm.end_status) {
+ code = gs_note_error(gs_error_ioerror);
+ goto done;
+ }
+ gdev_prn_get_bits(pdev, lnum, in, &data);
+ sputs(&jstrm, data, state.scan_line_size, &ignore_used);
+ }
+
+ /* Wrap up. */
+ sclose(&jstrm);
+ sflush(&fstrm);
+ jcdp = 0;
+ done:
+ gs_free_object(mem, jbuf, "jpeg_print_page(jbuf)");
+ gs_free_object(mem, fbuf, "jpeg_print_page(fbuf)");
+ if (jcdp)
+ gs_jpeg_destroy(&state); /* frees *jcdp */
+ gs_free_object(mem, in, "jpeg_print_page(in)");
+ return code;
+ fail:
+ if (jcdp)
+ gs_free_object(mem, jcdp, "jpeg_print_page(jpeg_compress_data)");
+ gs_free_object(mem, in, "jpeg_print_page(in)");
+ return code;
+}
+
diff --git a/devices/gdevjpx.c b/devices/gdevjpx.c
new file mode 100644
index 000000000..bcae51d4b
--- /dev/null
+++ b/devices/gdevjpx.c
@@ -0,0 +1,227 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* $Id: gdevjpx.c 6300 2005-12-28 19:56:24Z giles $ */
+/* JPX encode filter test device */
+
+#include "gdevprn.h"
+#include "stream.h"
+#include "strimpl.h"
+#include "sjpx_luratech.h"
+
+/* Structure for the JPX-writing device. */
+typedef struct gx_device_jpx_s {
+ gx_device_common;
+ gx_prn_device_common;
+} gx_device_jpx;
+
+/* The device descriptor */
+static dev_proc_print_page(jpx_print_page);
+
+/* ------ The device descriptors ------ */
+
+/* Default X and Y resolution. */
+#ifndef X_DPI
+# define X_DPI 72
+#endif
+#ifndef Y_DPI
+# define Y_DPI 72
+#endif
+
+static dev_proc_print_page(jpx_print_page);
+
+/* 24 bit RGB default */
+/* Since the print_page doesn't alter the device, this device can print in the background */
+static const gx_device_procs jpxrgb_procs =
+prn_color_procs(gdev_prn_open, gdev_prn_bg_output_page, gdev_prn_close,
+ gx_default_rgb_map_rgb_color,
+ gx_default_rgb_map_color_rgb);
+const gx_device_printer gs_jpxrgb_device = {
+ prn_device_std_body(gx_device_jpx, jpxrgb_procs, "jpx",
+ DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
+ X_DPI, Y_DPI, /* resolution */
+ 0, 0, 0, 0, /* margins */
+ 24, /* bits per pixel */
+ jpx_print_page)
+};
+
+/* 8 bit Grayscale */
+/* Since the print_page doesn't alter the device, this device can print in the background */
+static const gx_device_procs jpxgray_procs =
+prn_color_procs(gdev_prn_open, gdev_prn_bg_output_page, gdev_prn_close,
+ gx_default_gray_map_rgb_color,
+ gx_default_gray_map_color_rgb);
+const gx_device_printer gs_jpxgray_device = {
+ prn_device_body(gx_device_jpx, jpxgray_procs, "jpxgray",
+ DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
+ X_DPI, Y_DPI, /* resolution */
+ 0, 0, 0, 0, /* margins */
+ 1, 8, 255, 0, 256, 0, /* components, depth and min/max values */
+ jpx_print_page)
+};
+
+/* 32 bit CMKY */
+static dev_proc_map_color_rgb(jpx_cmyk_map_color_rgb);
+static dev_proc_map_cmyk_color(jpx_cmyk_map_cmyk_color);
+static const gx_device_procs jpxcmyk_procs =
+{ gdev_prn_open,
+ gx_default_get_initial_matrix,
+/* Since the print_page doesn't alter the device, this device can print in the background */
+ NULL, /* sync_output */
+ gdev_prn_bg_output_page,
+ gdev_prn_close,
+ NULL,
+ jpx_cmyk_map_color_rgb,
+ NULL, /* fill_rectangle */
+ NULL, /* tile_rectangle */
+ NULL, /* copy_mono */
+ NULL, /* copy_color */
+ NULL, /* draw_line */
+ NULL, /* get_bits */
+ gdev_prn_get_params,
+ gdev_prn_put_params,
+ jpx_cmyk_map_cmyk_color,
+ NULL, /* get_xfont_procs */
+ NULL, /* get_xfont_device */
+ NULL, /* map_rgb_alpha_color */
+ gx_page_device_get_page_device /* get_page_device */
+};
+const gx_device_printer gs_jpxcmyk_device = {
+ prn_device_std_body(gx_device_jpx, jpxcmyk_procs, "jpxcmyk",
+ DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
+ X_DPI, Y_DPI, /* resolution */
+ 0, 0, 0, 0, /* margins */
+ 32, /* bits per pixel */
+ jpx_print_page)
+};
+
+/* private color conversion routines;
+ we don't seem to have defaults for cmyk. */
+static int
+jpx_cmyk_map_color_rgb(gx_device * dev, gx_color_index color,
+ gx_color_value prgb[3])
+{
+ int not_k = color & 0xff,
+ r = not_k - ~(color >> 24),
+ g = not_k - ~((color >> 16) & 0xff),
+ b = not_k - ~((color >> 8) & 0xff);
+
+ prgb[0] = (r < 0 ? 0 : gx_color_value_from_byte(r));
+ prgb[1] = (g < 0 ? 0 : gx_color_value_from_byte(g));
+ prgb[2] = (b < 0 ? 0 : gx_color_value_from_byte(b));
+ return 0;
+}
+
+static gx_color_index
+jpx_cmyk_map_cmyk_color(gx_device * dev, const gx_color_value cv[])
+{
+ gx_color_index color = ~(
+ gx_color_value_to_byte(cv[3]) +
+ ((uint)gx_color_value_to_byte(cv[2]) << 8) +
+ ((uint)gx_color_value_to_byte(cv[1]) << 16) +
+ ((uint)gx_color_value_to_byte(cv[0]) << 24));
+
+ return (color == gx_no_color_index ? color ^ 1 : color);
+}
+
+/* Send the page to the file. */
+static int
+jpx_print_page(gx_device_printer * pdev, FILE * prn_stream)
+{
+ gx_device_jpx *jdev = (gx_device_jpx *) pdev;
+ gs_memory_t *mem = jdev->memory;
+ int line_size = gdev_mem_bytes_per_scan_line((gx_device *) pdev);
+ byte *in = gs_alloc_bytes(mem, line_size, "jpx_print_page(in)");
+ byte *fbuf = 0;
+ uint fbuf_size;
+ byte *jbuf = 0;
+ uint jbuf_size;
+ int lnum;
+ int code = 0;
+ stream_jpxe_state state;
+ stream fstrm, cstrm;
+
+ if (in == 0) {
+ code = gs_note_error(gs_error_VMerror);
+ goto fail;
+ }
+ /* Create the jpx encoder state. */
+ s_init_state((stream_state *)&state, &s_jpxe_template, 0);
+ if (state.templat->set_defaults)
+ (*state.templat->set_defaults) ((stream_state *) & state);
+ state.width = jdev->width;
+ state.height = jdev->height;
+ switch (jdev->color_info.depth) {
+ case 32: state.colorspace = gs_jpx_cs_cmyk; break;
+ case 24: state.colorspace = gs_jpx_cs_rgb; break;
+ case 8: state.colorspace = gs_jpx_cs_gray; break;
+ default:
+ state.colorspace = gs_jpx_cs_gray; /* safest option */
+ dmlprintf1(mem, "unexpected color_info depth %d\n",
+ jdev->color_info.depth);
+ }
+ state.bpc = 8; /* currently only 8 bits per component is supported */
+
+ /* ask for lossless encoding */
+ /* state.lossless = 1; */
+ /* or, set the quality level different from the default */
+ /* state.quality = 35; */
+
+ /* Set up the streams. */
+ fbuf_size = max(512 /* arbitrary */ , state.templat->min_out_size);
+ jbuf_size = state.templat->min_in_size;
+ if ((fbuf = gs_alloc_bytes(mem, fbuf_size, "jpx_print_page(fbuf)")) == 0 ||
+ (jbuf = gs_alloc_bytes(mem, jbuf_size, "jpx_print_page(jbuf)")) == 0
+ ) {
+ code = gs_note_error(gs_error_VMerror);
+ goto done;
+ }
+ s_init(&fstrm, mem);
+ swrite_file(&fstrm, prn_stream, fbuf, fbuf_size);
+ s_init(&cstrm, mem);
+ s_std_init(&cstrm, jbuf, jbuf_size, &s_filter_write_procs,
+ s_mode_write);
+ cstrm.state = (stream_state *) & state;
+ cstrm.procs.process = state.templat->process;
+ cstrm.strm = &fstrm;
+ if (state.templat->init)
+ (*state.templat->init) (cstrm.state);
+
+ /* Copy the data to the output. */
+ for (lnum = 0; lnum < jdev->height; ++lnum) {
+ byte *data;
+ uint ignore_used;
+
+ if (cstrm.end_status) {
+ code = gs_note_error(gs_error_ioerror);
+ goto done;
+ }
+ gdev_prn_get_bits(pdev, lnum, in, &data);
+ sputs(&cstrm, data, state.stride, &ignore_used);
+ }
+
+ /* Wrap up. */
+ sclose(&cstrm);
+ sflush(&fstrm);
+ done:
+ gs_free_object(mem, jbuf, "jpx_print_page(jbuf)");
+ gs_free_object(mem, fbuf, "jpx_print_page(fbuf)");
+ gs_free_object(mem, in, "jpx_print_page(in)");
+ return code;
+ fail:
+ gs_free_object(mem, in, "jpx_print_page(in)");
+ return code;
+}
diff --git a/devices/gdevl256.c b/devices/gdevl256.c
new file mode 100644
index 000000000..3eb591c9b
--- /dev/null
+++ b/devices/gdevl256.c
@@ -0,0 +1,314 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+/* Ghostscript driver for 256-color VGA modes with Linux and vgalib */
+/* This Driver was derived from the BGI-Driver. It was written
+ only for my own purpose. I never planned to release it or send
+ it to others. So, if something doesn't work, you may send
+ me a short note, but don't expect me to correct it. I will
+ try my very best, but i have some work to do.
+
+ Ludger Kunz | ____________|Tel.: 02371/566-230
+ FernUniversitaet Hagen| /| / / \ |FAX: 02371/52212
+ Lehrgebiet ES | / |/ /_ \ |EMAIL:
+ Frauenstuhlweg 31 | / |\ / \ |ludger.kunz@fernuni-hagen.de
+ 58644 Iserlohn |/___|_\/_______\|
+ */
+#include "memory_.h"
+#include "gx.h"
+#include "gxdevice.h"
+#include "gserrors.h"
+
+#include <errno.h>
+#include <vga.h>
+#include <vgagl.h>
+
+/* The color map for dynamically assignable colors. */
+#define first_dc_index 64
+static int next_dc_index;
+
+#define dc_hash_size 293 /* prime, >num_dc */
+typedef struct {
+ ushort rgb, index;
+} dc_entry;
+static dc_entry dynamic_colors[dc_hash_size + 1];
+
+#define XDPI 60 /* to get a more-or-less square aspect ratio */
+#define YDPI 60
+
+#ifndef A4 /*Letter size */
+#define YSIZE (20.0 * YDPI / 2.5)
+#define XSIZE (8.5 / 11)*YSIZE /* 8.5 x 11 inch page, by default */
+#else /* A4 paper */
+#define XSIZE 8.3 /*8.27 */
+#define YSIZE 11.7 /*11.69 */
+#endif
+
+/* The device descriptor */
+typedef struct gx_device_lvga256 {
+ gx_device_common;
+} gx_device_lvga256;
+
+#define lvga256dev ((gx_device_lvga256 *)dev)
+
+static dev_proc_open_device(lvga256_open);
+static dev_proc_close_device(lvga256_close);
+static dev_proc_map_rgb_color(lvga256_map_rgb_color);
+static dev_proc_map_color_rgb(lvga256_map_color_rgb);
+static dev_proc_fill_rectangle(lvga256_fill_rectangle);
+static dev_proc_tile_rectangle(lvga256_tile_rectangle);
+static dev_proc_copy_mono(lvga256_copy_mono);
+static dev_proc_copy_color(lvga256_copy_color);
+static dev_proc_draw_line(lvga256_draw_line);
+
+static gx_device_procs lvga256_procs =
+{
+ lvga256_open,
+ NULL, /* get_initial_matrix */
+ NULL, /* sync_output */
+ NULL, /* output_page */
+ lvga256_close,
+ lvga256_map_rgb_color,
+ lvga256_map_color_rgb,
+ lvga256_fill_rectangle,
+ lvga256_tile_rectangle,
+ lvga256_copy_mono,
+ lvga256_copy_color,
+ lvga256_draw_line
+};
+
+gx_device_lvga256 far_data gs_lvga256_device =
+{std_device_color_body(gx_device_lvga256, &lvga256_procs, "lvga256",
+ 0, 0, /* width and height are set in lvga256_open */
+ 1, 1, /* density is set in lvga256_open */
+ /*dci_color( */ 8, 31, 4 /*) */ )
+};
+
+/* Open the LINUX driver for graphics mode */
+int
+lvga256_open(gx_device * dev)
+{
+ int vgamode;
+ int width, height;
+
+ vga_init();
+ vgamode = vga_getdefaultmode();
+ if (vgamode == -1)
+ vgamode = G320x200x256;
+ vga_setmode(vgamode);
+ gl_setcontextvga(vgamode);
+ width = vga_getxdim();
+ height = vga_getydim();
+ dev->y_pixels_per_inch = height / 12.0;
+ dev->x_pixels_per_inch = dev->y_pixels_per_inch;
+ gx_device_set_width_height(dev, width, height);
+ {
+ int c;
+
+ for (c = 0; c < 64; c++) {
+ static const byte c2[10] =
+ {0, 42, 0, 0, 0, 0, 0, 0, 21, 63};
+
+ gl_setpalettecolor(c, c2[(c >> 2) & 9], c2[(c >> 1) & 9], c2[c & 9]);
+ }
+ }
+ /* Initialize the dynamic color table. */
+ memset(dynamic_colors, 0, (dc_hash_size + 1) * sizeof(dc_entry));
+ next_dc_index = first_dc_index;
+
+ return 0;
+}
+
+/* Close the LINUX driver */
+int
+lvga256_close(gx_device * dev)
+{
+ vga_setmode(TEXT);
+ return 0;
+}
+
+/* Map a r-g-b color to a palette index. */
+/* The first 64 entries of the color map are set */
+/* for compatibility with the older display modes: */
+/* these are indexed as 0.0.R0.G0.B0.R1.G1.B1. */
+gx_color_index
+lvga256_map_rgb_color(gx_device * dev, const gx_color_value cv[])
+{
+ gx_color_value r, g, b;
+ r = cv[0]; g = cv[1]; b = cv[2];
+#define cv_bits(v,n) (v >> (gx_color_value_bits - n))
+ ushort r5 = cv_bits(r, 5), g5 = cv_bits(g, 5), b5 = cv_bits(b, 5);
+ static const byte cube_bits[32] =
+ {0, 128, 128, 128, 128, 128, 128, 128, 128, 128,
+ 8, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,
+ 1, 128, 128, 128, 128, 128, 128, 128, 128, 128,
+ 9
+ };
+ uint cx = ((uint) cube_bits[r5] << 2) + ((uint) cube_bits[g5] << 1) +
+ (uint) cube_bits[b5];
+ ushort rgb;
+ register dc_entry *pdc;
+
+ /* Check for a color on the cube. */
+ if (cx < 64)
+ return (gx_color_index) cx;
+ /* Not on the cube, check the dynamic color table. */
+ rgb = (r5 << 10) + (g5 << 5) + b5;
+ for (pdc = &dynamic_colors[rgb % dc_hash_size]; pdc->rgb != 0; pdc++) {
+ if (pdc->rgb == rgb)
+ return (gx_color_index) (pdc->index);
+ }
+ if (pdc == &dynamic_colors[dc_hash_size]) { /* Wraparound */
+ for (pdc = &dynamic_colors[0]; pdc->rgb != 0; pdc++) {
+ if (pdc->rgb == rgb)
+ return (gx_color_index) (pdc->index);
+ }
+ }
+ if (next_dc_index == 256) { /* No space left, report failure. */
+ return gx_no_color_index;
+ }
+ /* Not on the cube, and not in the dynamic table. */
+ /* Put in the dynamic table if space available. */
+ {
+ int i = next_dc_index++;
+
+ pdc->rgb = rgb;
+ pdc->index = i;
+ gl_setpalettecolor(i, cv_bits(r, 6), cv_bits(g, 6), cv_bits(b, 6));
+ return (gx_color_index) i;
+ }
+}
+
+int
+lvga256_map_color_rgb(gx_device * dev, gx_color_index color,
+ unsigned short prgb[3])
+{
+/* gl_getpalettecolor (color,(int *)&prgb[0],(int *)&prgb[1],(int *)&prgb[2]); */
+ prgb[0] = gx_max_color_value;
+ prgb[1] = gx_max_color_value;
+ prgb[2] = gx_max_color_value;
+ return 0;
+}
+
+/* Copy a monochrome bitmap. The colors are given explicitly. */
+/* Color = gx_no_color_index means transparent (no effect on the image). */
+int
+lvga256_copy_mono(gx_device * dev,
+ const byte * base, int sourcex, int raster, gx_bitmap_id id,
+ int x, int y, int w, int h,
+ gx_color_index zero, gx_color_index one)
+{
+ const byte *ptr_line = base + (sourcex >> 3);
+ int left_bit = 0x80 >> (sourcex & 7);
+ int dest_y = y, end_x = x + w;
+ int invert = 0;
+ int color;
+
+ fit_copy(dev, base, sourcex, raster, id, x, y, w, h);
+ if (zero == gx_no_color_index) {
+ if (one == gx_no_color_index)
+ return 0;
+ color = (int)one;
+ } else {
+ if (one == gx_no_color_index) {
+ color = (int)zero;
+ invert = -1;
+ } else { /* Pre-clear the rectangle to zero */
+ gl_fillbox(x, y, w, h, 0);
+ color = (int)one;
+ }
+ }
+ while (h--) { /* for each line */
+ const byte *ptr_source = ptr_line;
+ register int dest_x = x;
+ register int bit = left_bit;
+
+ while (dest_x < end_x) { /* for each bit in the line */
+ if ((*ptr_source ^ invert) & bit) {
+ gl_setpixel(dest_x, dest_y, color);
+ }
+ dest_x++;
+ if ((bit >>= 1) == 0)
+ bit = 0x80, ptr_source++;
+ }
+ dest_y++;
+ ptr_line += raster;
+ }
+ return 0;
+}
+
+/* Copy a color pixel map. This is just like a bitmap, except that */
+/* each pixel takes 4 bits instead of 1 when device driver has color. */
+int
+lvga256_copy_color(gx_device * dev,
+ const byte * base, int sourcex, int raster, gx_bitmap_id id,
+ int x, int y, int w, int h)
+{
+ fit_copy(dev, base, sourcex, raster, id, x, y, w, h);
+ if (gx_device_has_color(dev)) { /* color device, four bits per pixel */
+ const byte *line = base + sourcex;
+
+ gl_putbox(x, y, w, h, line);
+ } else { /* monochrome device: one bit per pixel */
+ /* bit map is the same as lvga256_copy_mono: one bit per pixel */
+ lvga256_copy_mono(dev, base, sourcex, raster, id, x, y, w, h,
+ (gx_color_index) 0, (gx_color_index) 255);
+ }
+ return 0;
+}
+
+/* Fill a rectangle. */
+int
+lvga256_fill_rectangle(gx_device * dev, int x, int y, int w, int h,
+ gx_color_index color)
+{
+ fit_fill(dev, x, y, w, h);
+ gl_fillbox(x, y, w, h, color);
+ return 0;
+}
+
+/* Tile a rectangle. If neither color is transparent, */
+/* pre-clear the rectangle to color0 and just tile with color1. */
+/* This is faster because of how lvga256_copy_mono is implemented. */
+/* Note that this also does the right thing for colored tiles. */
+int
+lvga256_tile_rectangle(gx_device * dev, const gx_tile_bitmap * tile,
+ int x, int y, int w, int h, gx_color_index czero, gx_color_index cone,
+ int px, int py)
+{
+ if (czero != gx_no_color_index && cone != gx_no_color_index) {
+ lvga256_fill_rectangle(dev, x, y, w, h, czero);
+ czero = gx_no_color_index;
+ }
+ return gx_default_tile_rectangle(dev, tile, x, y, w, h, czero, cone, px, py);
+}
+
+/* Draw a line */
+int
+lvga256_draw_line(gx_device * dev, int x0, int y0, int x1, int y1,
+ gx_color_index color)
+{
+ gl_line(x0, y0, x1, y1, color);
+ return 0;
+}
+
+#ifdef GS_DEVS_SHARED
+extern void gs_lib_register_device(const gx_device *dev);
+void
+gs_shared_init(void)
+{
+ gs_lib_register_device(&gs_lvga256_device);
+}
+#endif
diff --git a/devices/gdevl31s.c b/devices/gdevl31s.c
new file mode 100644
index 000000000..e232bf343
--- /dev/null
+++ b/devices/gdevl31s.c
@@ -0,0 +1,281 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+/*
+ * H-P LaserJet 3100 driver
+ *
+ * This is a driver for use with the H-P LaserJet 3100 Software.
+ * It requires installed H-P LaserJet 3100 Software to print.
+ * It can be used with smbclient to print from an UNIX box
+ * to a LaserJet 3100 printer attached to a MS-Windows box.
+ *
+ * Written by Ulrich Schmid, uschmid@mail.hh.provi.de.
+ */
+
+#include "gdevprn.h"
+#include "gdevmeds.h"
+
+#define XCORRECTION 0.11
+#define YCORRECTION 0.12
+
+/* order matters! 0 1 2 3 4 5 6 7 8 */
+const char *media[10] = {"a4", "letter", "legal", "com10", "c5", "dl", "b5", "monarch", "executive", 0};
+const int height[2][10] = {{3447, 3240, 4140, 5587, 2644, 5083, 2975, 4387, 3090, 0},
+ {6894, 6480, 8280, 11167, 5288, 10159, 5950, 8767, 6180, 0}};
+const int width[2] = {2528,
+ 5056};
+#define LARGEST_MEDIUM 2 /* legal */
+
+/* These codes correspond to sequences of pixels with the same color.
+ * After the code for a sequence < 64 pixels the color changes.
+ * After the code for a sequence with 64 pixels the previous color continues. */
+static struct {
+ uint bits;
+ uint length; /* number of valid bits */
+} code[2][65] =
+/* White */
+{{{0x0ac, 8}, {0x038, 6}, {0x00e, 4}, {0x001, 4}, {0x00d, 4}, {0x003, 4}, {0x007, 4}, {0x00f, 4},
+ {0x019, 5}, {0x005, 5}, {0x01c, 5}, {0x002, 5}, {0x004, 6}, {0x030, 6}, {0x00b, 6}, {0x02b, 6},
+ {0x015, 6}, {0x035, 6}, {0x072, 7}, {0x018, 7}, {0x008, 7}, {0x074, 7}, {0x060, 7}, {0x010, 7},
+ {0x00a, 7}, {0x06a, 7}, {0x064, 7}, {0x012, 7}, {0x00c, 7}, {0x040, 8}, {0x0c0, 8}, {0x058, 8},
+ {0x0d8, 8}, {0x048, 8}, {0x0c8, 8}, {0x028, 8}, {0x0a8, 8}, {0x068, 8}, {0x0e8, 8}, {0x014, 8},
+ {0x094, 8}, {0x054, 8}, {0x0d4, 8}, {0x034, 8}, {0x0b4, 8}, {0x020, 8}, {0x0a0, 8}, {0x050, 8},
+ {0x0d0, 8}, {0x04a, 8}, {0x0ca, 8}, {0x02a, 8}, {0x0aa, 8}, {0x024, 8}, {0x0a4, 8}, {0x01a, 8},
+ {0x09a, 8}, {0x05a, 8}, {0x0da, 8}, {0x052, 8}, {0x0d2, 8}, {0x04c, 8}, {0x0cc, 8}, {0x02c, 8},
+ {0x01b, 5}},
+/* Black */
+ {{0x3b0, 10}, {0x002, 3}, {0x003, 2}, {0x001, 2}, {0x006, 3}, {0x00c, 4}, {0x004, 4}, {0x018, 5},
+ {0x028, 6}, {0x008, 6}, {0x010, 7}, {0x050, 7}, {0x070, 7}, {0x020, 8}, {0x0e0, 8}, {0x030, 9},
+ {0x3a0, 10}, {0x060, 10}, {0x040, 10}, {0x730, 11}, {0x0b0, 11}, {0x1b0, 11}, {0x760, 11}, {0x0a0, 11},
+ {0x740, 11}, {0x0c0, 11}, {0x530, 12}, {0xd30, 12}, {0x330, 12}, {0xb30, 12}, {0x160, 12}, {0x960, 12},
+ {0x560, 12}, {0xd60, 12}, {0x4b0, 12}, {0xcb0, 12}, {0x2b0, 12}, {0xab0, 12}, {0x6b0, 12}, {0xeb0, 12},
+ {0x360, 12}, {0xb60, 12}, {0x5b0, 12}, {0xdb0, 12}, {0x2a0, 12}, {0xaa0, 12}, {0x6a0, 12}, {0xea0, 12},
+ {0x260, 12}, {0xa60, 12}, {0x4a0, 12}, {0xca0, 12}, {0x240, 12}, {0xec0, 12}, {0x1c0, 12}, {0xe40, 12},
+ {0x140, 12}, {0x1a0, 12}, {0x9a0, 12}, {0xd40, 12}, {0x340, 12}, {0x5a0, 12}, {0x660, 12}, {0xe60, 12},
+ {0x3c0, 10}}};
+
+/* Define the default, maximum resolutions. */
+#ifndef X_DPI
+# define X_DPI 600
+#endif
+#ifndef Y_DPI
+# define Y_DPI 600
+#endif
+
+/* The device descriptors */
+static dev_proc_print_page_copies(lj3100sw_print_page_copies);
+static dev_proc_close_device(lj3100sw_close);
+
+/* Since the print_page doesn't alter the device, this device can print in the background */
+static gx_device_procs prn_lj3100sw_procs =
+ prn_params_procs(gdev_prn_open, gdev_prn_bg_output_page, lj3100sw_close,
+ gdev_prn_get_params, gdev_prn_put_params);
+
+/* workaround to emulate the missing prn_device_margins_copies macro */
+#define gx_default_print_page_copies lj3100sw_print_page_copies
+gx_device_printer far_data gs_lj3100sw_device =
+ prn_device_margins/*_copies*/(prn_lj3100sw_procs, "lj3100sw",
+ DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
+ X_DPI, Y_DPI,
+ XCORRECTION, YCORRECTION,
+ 0.25, 0.2, 0.25, 0.2,
+ 1, 0 /* lj3100sw_print_page_copies */);
+#undef gx_default_print_page_copies
+
+#define ppdev ((gx_device_printer *)pdev)
+
+#define BUFFERSIZE 0x1000
+
+static void
+lj3100sw_output_section_header(FILE *prn_stream, int type, int arg1, int arg2)
+{
+ fputc(type & 0xff, prn_stream);
+ fputc(type >> 8 & 0xff, prn_stream);
+ fputc(arg1 & 0xff, prn_stream);
+ fputc(arg1 >> 8 & 0xff, prn_stream);
+ fputc(arg2 & 0xff, prn_stream);
+ fputc(arg2 >> 8 & 0xff, prn_stream);
+}
+
+static void
+lj3100sw_flush_buffer(FILE *prn_stream, char *buffer, char **pptr)
+{
+ int size = *pptr - buffer;
+ if (size) {
+ lj3100sw_output_section_header(prn_stream, 0, size, 0);
+ fwrite(buffer, 1, size, prn_stream);
+ *pptr = buffer;
+ }
+}
+
+static void
+lj3100sw_output_data_byte(FILE *prn_stream, char *buffer, char **pptr, int val)
+{
+ if (*pptr >= buffer + BUFFERSIZE)
+ lj3100sw_flush_buffer(prn_stream, buffer, pptr);
+ *(*pptr)++ = val;
+}
+
+static void
+lj3100sw_output_repeated_data_bytes(FILE *prn_stream, char *buffer, char **pptr, int val, int num)
+{
+ int size;
+ while (num) {
+ if (*pptr >= buffer + BUFFERSIZE)
+ lj3100sw_flush_buffer(prn_stream, buffer, pptr);
+ size = min(num, buffer + BUFFERSIZE - *pptr);
+ memset(*pptr, val, size);
+ *pptr += size;
+ num -= size;
+ }
+}
+
+static void
+lj3100sw_output_newline(FILE *prn_stream, char *buffer, char **pptr)
+{
+ lj3100sw_output_data_byte(prn_stream, buffer, pptr, 0);
+ lj3100sw_output_data_byte(prn_stream, buffer, pptr, 0);
+ lj3100sw_output_data_byte(prn_stream, buffer, pptr, 0x80);
+}
+
+static void
+lj3100sw_output_empty_line(FILE *prn_stream, char *buffer, char **pptr, bool high_resolution)
+{
+ if (high_resolution) {
+ lj3100sw_output_data_byte(prn_stream, buffer, pptr, 0x80);
+ lj3100sw_output_data_byte(prn_stream, buffer, pptr, 0x0f);
+ lj3100sw_output_data_byte(prn_stream, buffer, pptr, 0x78);
+ lj3100sw_output_data_byte(prn_stream, buffer, pptr, 0xac);
+ } else {
+ lj3100sw_output_data_byte(prn_stream, buffer, pptr, 0x80);
+ lj3100sw_output_data_byte(prn_stream, buffer, pptr, 0x87);
+ lj3100sw_output_data_byte(prn_stream, buffer, pptr, 0x0d);
+ }
+}
+
+static int
+lj3100sw_print_page_copies(gx_device_printer *pdev, FILE *prn_stream, int num_copies /* ignored */)
+{
+ int i, j;
+ char buffer[BUFFERSIZE], *ptr = buffer;
+ int medium_index = select_medium(pdev, media, LARGEST_MEDIUM);
+ bool high_resolution = (pdev->x_pixels_per_inch > 300);
+ int printer_height = height[high_resolution ? 1 : 0][medium_index];
+ int printer_width = width[high_resolution ? 1 : 0];
+ int paper_height = pdev->height;
+ int paper_width = pdev->width;
+ int line_size = gdev_prn_raster(pdev);
+ gs_memory_t *mem = pdev->memory;
+ byte *in = (byte *)gs_malloc(mem, line_size, 1, "lj3100sw_print_page");
+ byte *data;
+ if (in == 0)
+ return_error(gs_error_VMerror);
+ if (gdev_prn_file_is_new(pdev)) {
+ lj3100sw_output_section_header(prn_stream, 1, 0, 0);
+ lj3100sw_output_repeated_data_bytes(prn_stream, buffer, &ptr, 0x1b, 12);
+ ptr += gs_sprintf(ptr, "\r\nBD");
+ lj3100sw_output_repeated_data_bytes(prn_stream, buffer, &ptr, 0, 5520);
+ ptr += gs_sprintf(ptr, "%s\r\n%s %d\r\n%s %d\r\n%s %d\r\n%s %d\r\n%s %d\r\n%s %d\r\n",
+ "NJ",
+ "PQ", -1,
+ "RE", high_resolution ? 6 : 2,
+ "SL", printer_width,
+ "LM", 0,
+ "PS", medium_index,
+ "PC", 0);
+ lj3100sw_flush_buffer(prn_stream, buffer, &ptr);
+ }
+
+ lj3100sw_output_section_header(prn_stream, 3, ppdev->NumCopies, 0);
+ ptr += gs_sprintf(ptr, "%s %d\r\n%s\r\n",
+ "CM", 1,
+ "PD");
+ *ptr++ = 0;
+ lj3100sw_output_newline(prn_stream, buffer, &ptr);
+
+ for (i = 0; i < printer_height; i++) {
+ if (i < paper_height) {
+ int color = 0; /* white */
+ int count = 0;
+ int bit_index = 0;
+ uint tmp = 0;
+ gdev_prn_get_bits(pdev, i, in, &data);
+ for (j = 0; j <= printer_width; j++) {
+ int xoffset = (printer_width - paper_width) / 2;
+ int newcolor = 0;
+ if (j >= xoffset && j < xoffset + paper_width)
+ newcolor = (data[(j - xoffset) / 8] >> (7 - (j - xoffset) % 8)) & 1;
+ if (j == printer_width)
+ newcolor = !color; /* force output */
+ if (newcolor == color)
+ count++;
+ else if (count == printer_width && color == 0) /* implies j == printer_width */
+ lj3100sw_output_empty_line(prn_stream, buffer, &ptr, high_resolution);
+ else /* print a sequence of pixels with a uniform color */
+ while (newcolor != color) {
+ int size = min(count, 64);
+ tmp |= code[color][size].bits << bit_index;
+ bit_index += code[color][size].length;
+ while (bit_index >= 8) {
+ lj3100sw_output_data_byte(prn_stream, buffer, &ptr, tmp & 0xff);
+ tmp >>= 8;
+ bit_index -= 8;
+ }
+ if (size == 64)
+ count -= 64;
+ else {
+ color = newcolor;
+ count = 1;
+ }
+ }
+ }
+ if (bit_index)
+ lj3100sw_output_data_byte(prn_stream, buffer, &ptr, tmp & 0xff);
+ }
+ else
+ lj3100sw_output_empty_line(prn_stream, buffer, &ptr, high_resolution);
+ lj3100sw_output_newline(prn_stream, buffer, &ptr);
+ }
+
+ for (i = 0; i < 3; i++ ) {
+ lj3100sw_output_data_byte(prn_stream, buffer, &ptr, 0x00);
+ lj3100sw_output_data_byte(prn_stream, buffer, &ptr, 0x08);
+ lj3100sw_output_data_byte(prn_stream, buffer, &ptr, 0x80);
+ }
+ lj3100sw_output_repeated_data_bytes(prn_stream, buffer, &ptr, 0, 520);
+ lj3100sw_flush_buffer(prn_stream, buffer, &ptr);
+
+ lj3100sw_output_section_header(prn_stream, 4, 0, 0);
+ for (i = 0; i < 4 * ppdev->NumCopies; i++)
+ lj3100sw_output_section_header(prn_stream, 54, 0, 0);
+
+ gs_free(mem, (char *)in, line_size, 1, "lj3100sw_print_page");
+ return 0;
+}
+
+static int
+lj3100sw_close(gx_device *pdev)
+{
+ int i;
+ FILE *prn_stream = ((gx_device_printer *)pdev)->file;
+
+ lj3100sw_output_section_header(prn_stream, 0, 4, 0);
+ fputs("XX\r\n", prn_stream);
+ for (i = 0; i < 4 * ppdev->NumCopies; i++)
+ lj3100sw_output_section_header(prn_stream, 54, 0, 0);
+ lj3100sw_output_section_header(prn_stream, 2, 0, 0);
+
+ return gdev_prn_close(pdev);
+}
diff --git a/devices/gdevlbp8.c b/devices/gdevlbp8.c
new file mode 100644
index 000000000..5f6ff17b3
--- /dev/null
+++ b/devices/gdevlbp8.c
@@ -0,0 +1,215 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+/* Canon LBP-8II and LIPS III driver */
+#include "gdevprn.h"
+
+/*
+ Modifications:
+ 2.2.97 Lauri Paatero
+ Changed CSI command into ESC [. DCS commands may still need to be changed
+ (to ESC P).
+ 4.9.96 Lauri Paatero
+ Corrected LBP-8II margins again. Real problem was that (0,0) is NOT
+ in upper left corner.
+ Now using relative addressing for vertical addressing. This avoids
+problems
+ when printing to paper with wrong size.
+ 18.6.96 Lauri Paatero, lauri.paatero@paatero.pp.fi
+ Corrected LBP-8II margins.
+ Added logic to recognize (and optimize away) long strings of 00's in data.
+ For LBP-8II removed use of 8-bit CSI (this does not work if 8-bit character
+ set has been configured in LBP-8II. (Perhaps this should also be done
+ for LBP-8III?)
+ Original versions:
+ LBP8 driver: Tom Quinn (trq@prg.oxford.ac.uk)
+ LIPS III driver: Kenji Okamoto (okamoto@okamoto.cias.osakafu-u.ac.jp)
+*/
+
+#define X_DPI 300
+#define Y_DPI 300
+#define LINE_SIZE ((X_DPI * 85 / 10 + 7) / 8) /* bytes per line */
+
+/* The device descriptors */
+static dev_proc_print_page(lbp8_print_page);
+#ifdef NOCONTRIB
+static dev_proc_print_page(lips3_print_page);
+#endif
+
+const gx_device_printer far_data gs_lbp8_device =
+ prn_device(prn_bg_procs, "lbp8", /* The print_page proc is compatible with allowing bg printing */
+ DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
+ X_DPI, Y_DPI,
+ 0.16, 0.2, 0.32, 0.21, /* margins: left, bottom, right, top */
+ 1, lbp8_print_page);
+
+#ifdef NOCONTRIB
+const gx_device_printer far_data gs_lips3_device =
+ prn_device(prn_bg_procs, "lips3", /* The print_page proc is compatible with allowing bg printing */
+ 82, /* width_10ths, 8.3" */
+ 117, /* height_10ths, 11.7" */
+ X_DPI, Y_DPI,
+ 0.16, 0.27, 0.23, 0.27, /* margins */
+ 1, lips3_print_page);
+#endif
+
+/* ------ Internal routines ------ */
+
+#define ESC (char)0x1b
+#define CSI '\233'
+#define DCS '\220'
+#define ST '\234'
+
+static const char lbp8_init[] = {
+ ESC, ';', ESC, 'c', ESC, ';', /* reset, ISO */
+ ESC, '[', '2', '&', 'z', /* fullpaint mode */
+ ESC, '[', '1', '4', 'p', /* select page type (A4) */
+ ESC, '[', '1', '1', 'h', /* set mode */
+ ESC, '[', '7', ' ', 'I', /* select unit size (300dpi)*/
+ ESC, '[', '6', '3', 'k', /* Move 63 dots up (to top of printable area) */
+};
+
+#ifdef NOCONTRIB
+static const char lips3_init[] = {
+ ESC, '<', /* soft reset */
+ DCS, '0', 'J', ST, /* JOB END */
+ DCS, '3', '1', ';', '3', '0', '0', ';', '2', 'J', ST, /* 300dpi, LIPS3 JOB START */
+ ESC, '<', /* soft reset */
+ DCS, '2', 'y', 'P', 'r', 'i', 'n', 't', 'i', 'n', 'g', '(', 'g', 's', ')', ST, /* Printing (gs) display */
+ ESC, '[', '?', '1', 'l', /* auto cr-lf disable */
+ ESC, '[', '?', '2', 'h', /* auto ff disable */
+ ESC, '[', '1', '1', 'h', /* set mode */
+ ESC, '[', '7', ' ', 'I', /* select unit size (300dpi)*/
+ ESC, '[', 'f' /* move to home position */
+};
+
+static const char lips3_end[] = {
+ DCS, '0', 'J', ST /* JOB END */
+};
+#endif
+
+/* Send the page to the printer. */
+static int
+can_print_page(gx_device_printer *pdev, FILE *prn_stream,
+ const char *init, int init_size, const char *end, int end_size)
+{
+ char data[LINE_SIZE*2];
+ char *out_data;
+ int last_line_nro = 0;
+
+ fwrite(init, init_size, 1, prn_stream); /* initialize */
+
+ /* Send each scan line in turn */
+ {
+ int lnum;
+ int line_size = gdev_mem_bytes_per_scan_line((gx_device *)pdev);
+ byte rmask = (byte)(0xff << (-pdev->width & 7));
+
+ for ( lnum = 0; lnum < pdev->height; lnum++ ) {
+ char *end_data = data + LINE_SIZE;
+ gdev_prn_copy_scan_lines(pdev, lnum,
+ (byte *)data, line_size);
+ /* Mask off 1-bits beyond the line width. */
+ end_data[-1] &= rmask;
+ /* Remove trailing 0s. */
+ while ( end_data > data && end_data[-1] == 0 )
+ end_data--;
+ if ( end_data != data ) {
+ int num_cols = 0;
+ int out_count;
+ int zero_count;
+ out_data = data;
+
+ /* move down */
+ fprintf(prn_stream, "%c[%de",
+ ESC, lnum-last_line_nro );
+ last_line_nro = lnum;
+
+ while (out_data < end_data) {
+ /* Remove leading 0s*/
+ while(out_data < end_data && *out_data == 0) {
+ num_cols += 8;
+ out_data++;
+ }
+
+ out_count = end_data - out_data;
+ zero_count = 0;
+
+ /* if there is a lot data, find if there is sequence of zeros */
+ if (out_count>22) {
+
+ out_count = 1;
+
+ while(out_data+out_count+zero_count < end_data) {
+ if (out_data[zero_count+out_count] != 0) {
+ out_count += 1+zero_count;
+ zero_count = 0;
+ }
+ else {
+ zero_count++;
+ if (zero_count>20)
+ break;
+ }
+ }
+
+ }
+
+ if (out_count==0)
+ break;
+
+ /* move down and across*/
+ fprintf(prn_stream, "%c[%d`",
+ ESC, num_cols );
+ /* transfer raster graphic command */
+ fprintf(prn_stream, "%c[%d;%d;300;.r",
+ ESC, out_count, out_count);
+
+ /* send the row */
+ fwrite(out_data, sizeof(char),
+ out_count, prn_stream);
+
+ out_data += out_count+zero_count;
+ num_cols += 8*(out_count+zero_count);
+ }
+ }
+ }
+ }
+
+ /* eject page */
+ fprintf(prn_stream, "%c=", ESC);
+
+ /* terminate */
+ if (end != NULL)
+ (void)fwrite(end, end_size, 1, prn_stream);
+
+ return 0;
+}
+
+/* Print an LBP-8 page. */
+static int
+lbp8_print_page(gx_device_printer *pdev, FILE *prn_stream)
+{
+ return can_print_page(pdev, prn_stream, lbp8_init, sizeof(lbp8_init),
+ NULL, 0);
+}
+
+#ifdef NOCONTRIB
+/* Print a LIPS III page. */
+static int
+lips3_print_page(gx_device_printer *pdev, FILE *prn_stream)
+{ return can_print_page(pdev, prn_stream, lips3_init, sizeof(lips3_init),
+ lips3_end, sizeof(lips3_end));
+}
+#endif
diff --git a/devices/gdevlj56.c b/devices/gdevlj56.c
new file mode 100644
index 000000000..41ab75fa4
--- /dev/null
+++ b/devices/gdevlj56.c
@@ -0,0 +1,213 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+/* H-P LaserJet 5 & 6 drivers for Ghostscript */
+#include "gdevprn.h"
+#include "stream.h"
+#include "gdevpcl.h"
+#include "gdevpxat.h"
+#include "gdevpxen.h"
+#include "gdevpxop.h"
+#include "gdevpxut.h"
+
+/* Define the default resolution. */
+#ifndef X_DPI
+# define X_DPI 600
+#endif
+#ifndef Y_DPI
+# define Y_DPI 600
+#endif
+
+/* Define the number of blank lines that make it worthwhile to */
+/* start a new image. */
+#define MIN_SKIP_LINES 2
+
+/* We round up the LINE_SIZE to a multiple of a ulong for faster scanning. */
+#define W sizeof(word)
+
+static dev_proc_open_device(ljet5_open);
+static dev_proc_close_device(ljet5_close);
+static dev_proc_print_page(ljet5_print_page);
+
+/* Since the print_page doesn't alter the device, this device can print in the background */
+static const gx_device_procs ljet5_procs =
+prn_procs(ljet5_open, gdev_prn_bg_output_page, ljet5_close);
+
+const gx_device_printer gs_lj5mono_device =
+prn_device(ljet5_procs, "lj5mono",
+ DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
+ X_DPI, Y_DPI,
+ 0, 0, 0, 0,
+ 1, ljet5_print_page);
+
+/* Since the print_page doesn't alter the device, this device can print in the background */
+static const gx_device_procs lj5gray_procs =
+prn_color_procs(ljet5_open, gdev_prn_bg_output_page, ljet5_close,
+ gx_default_gray_map_rgb_color,
+ gx_default_gray_map_color_rgb);
+
+const gx_device_printer gs_lj5gray_device = {
+ prn_device_body(gx_device_printer, lj5gray_procs, "lj5gray",
+ DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
+ X_DPI, Y_DPI,
+ 0, 0, 0, 0,
+ 1, 8, 255, 0, 256, 1, ljet5_print_page)
+};
+
+/* Open the printer, writing the stream header. */
+static int
+ljet5_open(gx_device * pdev)
+{
+ int code = gdev_prn_open(pdev);
+
+ if (code < 0)
+ return code;
+ code = gdev_prn_open_printer(pdev, true);
+ if (code < 0)
+ return code;
+ {
+ gx_device_printer *const ppdev = (gx_device_printer *)pdev;
+ stream fs;
+ stream *const s = &fs;
+ byte buf[50]; /* arbitrary */
+
+ s_init(s, pdev->memory);
+ swrite_file(s, ppdev->file, buf, sizeof(buf));
+ px_write_file_header(s, pdev);
+ sflush(s); /* don't close */
+ }
+ return 0;
+}
+
+/* Close the printer, writing the stream trailer. */
+static int
+ljet5_close(gx_device * pdev)
+{
+ gx_device_printer *const ppdev = (gx_device_printer *)pdev;
+ int code = gdev_prn_open_printer(pdev, true);
+
+ if (code < 0)
+ return code;
+ px_write_file_trailer(ppdev->file);
+ return gdev_prn_close(pdev);
+}
+
+/* Send the page to the printer. For now, just send the whole image. */
+static int
+ljet5_print_page(gx_device_printer * pdev, FILE * prn_stream)
+{
+ gs_memory_t *mem = pdev->memory;
+ uint line_size = gdev_mem_bytes_per_scan_line((gx_device *) pdev);
+ uint line_size_words = (line_size + W - 1) / W;
+ uint out_size = line_size + (line_size / 127) + 1;
+ word *line = (word *)gs_alloc_byte_array(mem, line_size_words, W, "ljet5(line)");
+ byte *out = gs_alloc_bytes(mem, out_size, "ljet5(out)");
+ int code = 0;
+ int lnum;
+ stream fs;
+ stream *const s = &fs;
+ byte buf[200]; /* arbitrary */
+
+ if (line == 0 || out == 0) {
+ code = gs_note_error(gs_error_VMerror);
+ goto done;
+ }
+ s_init(s, mem);
+ swrite_file(s, prn_stream, buf, sizeof(buf));
+
+ /* Write the page header. */
+ {
+ static const byte page_header[] = {
+ pxtBeginPage,
+ DUSP(0, 0), DA(pxaPoint),
+ pxtSetCursor
+ };
+ static const byte mono_header[] = {
+ DUB(eGray), DA(pxaColorSpace),
+ DUB(e8Bit), DA(pxaPaletteDepth),
+ pxt_ubyte_array, pxt_ubyte, 2, 0xff, 0x00, DA(pxaPaletteData),
+ pxtSetColorSpace
+ };
+ static const byte gray_header[] = {
+ DUB(eGray), DA(pxaColorSpace),
+ pxtSetColorSpace
+ };
+
+ px_write_page_header(s, (gx_device *)pdev);
+ px_write_select_media(s, (gx_device *)pdev, NULL, NULL, 0, false, false, 0, NULL);
+ PX_PUT_LIT(s, page_header);
+ if (pdev->color_info.depth == 1)
+ PX_PUT_LIT(s, mono_header);
+ else
+ PX_PUT_LIT(s, gray_header);
+ }
+
+ /* Write the image header. */
+ {
+ static const byte mono_image_header[] = {
+ DA(pxaDestinationSize),
+ DUB(eIndexedPixel), DA(pxaColorMapping),
+ DUB(e1Bit), DA(pxaColorDepth),
+ pxtBeginImage
+ };
+ static const byte gray_image_header[] = {
+ DA(pxaDestinationSize),
+ DUB(eDirectPixel), DA(pxaColorMapping),
+ DUB(e8Bit), DA(pxaColorDepth),
+ pxtBeginImage
+ };
+
+ px_put_us(s, pdev->width);
+ px_put_a(s, pxaSourceWidth);
+ px_put_us(s, pdev->height);
+ px_put_a(s, pxaSourceHeight);
+ px_put_usp(s, pdev->width, pdev->height);
+ if (pdev->color_info.depth == 1)
+ PX_PUT_LIT(s, mono_image_header);
+ else
+ PX_PUT_LIT(s, gray_image_header);
+ }
+
+ /* Write the image data, compressing each line. */
+ for (lnum = 0; lnum < pdev->height; ++lnum) {
+ int ncompr;
+ static const byte line_header[] = {
+ DA(pxaStartLine),
+ DUS(1), DA(pxaBlockHeight),
+ DUB(eRLECompression), DA(pxaCompressMode),
+ pxtReadImage
+ };
+
+ code = gdev_prn_copy_scan_lines(pdev, lnum, (byte *) line, line_size);
+ if (code < 0)
+ goto fin;
+ px_put_us(s, lnum);
+ PX_PUT_LIT(s, line_header);
+ ncompr = gdev_pcl_mode2compress_padded(line, line + line_size_words,
+ out, true);
+ px_put_data_length(s, ncompr);
+ px_put_bytes(s, out, ncompr);
+ }
+
+ /* Finish up. */
+ fin:
+ spputc(s, pxtEndImage);
+ spputc(s, pxtEndPage);
+ sflush(s);
+ done:
+ gs_free_object(mem, out, "ljet5(out)");
+ gs_free_object(mem, line, "ljet5(line)");
+ return code;
+}
diff --git a/devices/gdevlp8k.c b/devices/gdevlp8k.c
new file mode 100644
index 000000000..2e6d178cc
--- /dev/null
+++ b/devices/gdevlp8k.c
@@ -0,0 +1,397 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* EPSON LP-8000 ESC-sequence Laser Printer driver for Ghostscript.
+
+This driver structure is most close to that of the Epson 'ESC/P 2' language
+printer driver "gdevescp.c" contributed by Richard Brown, but all the control
+sequences and data formats are totally different.
+
+The main driver strategy is as follows. The driver scans lines, skips empty
+ones, removes leading and trailing zeros for other lines, compresses the
+non-zero rest of each line and finally outputs the data.
+
+At the moment the driver supports only 300x300 DPI resolution. If somebody
+needs 240x240, another valid value for LP-8000 printer, he or she can try to
+play with the corresponding values in initialization and termination
+strings. Or I shall spend some extra time for hacking, if enough people
+encourage me to do it. (The only available in our laboratory "Operation
+guide" in Japanese does not contain any information about it. And LP-8000
+driver for Japanese Windows does not support this mode either.)
+
+The output data format is the following.
+
+1. Initialization string, pretty long and sophisticated, I don't know why it
+was necessary.
+
+2. Data bits for each line. The most general format includes both starting X
+and Y values as well as data type (simple or compressed).
+
+3. Termination string.
+
+ DATA FORMATS
+
+1. A simple (non-compressed) data format. By evident reasons it is NOT
+SUPPORTED by the driver and is discussed here just as a starting point for
+the future explanations. "\035" here is an alias for 0x1d ESC-character :
+
+"\035" "Starting X point in ASCII format" "X"
+"\035" "Starting Y point in ASCII format" "Y"
+"\035" "Number of data BYTES for this printer line in ASCII format" ";"
+"Number of POINTS to print in this line (equals to the
+(Number of BYTES)*8)" ";"
+"1;obi{I" "data BYTES for this line in BINARY format"
+
+Both X and Y printer coordinates are 60 pixels shifted from the corresponding
+coordinates of the Ghostscript display, that is X = x - 60, Y = y - 60. For
+example, 1 inch left margin requires the value of 300 - 60 = 240 for
+starting X printer coordinate. Similar, 1.5 inch top margin requires Y
+values to start from 300*1.5 - 60 = 390.
+
+The shortest possible abbreviation for the simple data format string is
+
+"\035" "Starting Y point in ASCII format" "Y"
+"\035" "Number of data BYTES for this printer line in ASCII format" ";"
+"Number of POINTS to print in this line (equals to the
+(Number of BYTES)*8)" ";"
+"1;obi{I" "data BYTES for this line in BINARY format"
+
+In this case the value of the starting X point is assumed to be equal to
+that for the previous line.
+
+An example of the data output for 2 printer lines
+
+"\035"315X"\035"240Y"\035"2;16;1;obi{I"0ff0""\035"241Y"\035"3;24;1;obi{I"0f000f"
+
+Here "0ff0" is an alias for 0x0f 0xf0 binary data, etc. The first line of the
+above example starts from X=315, Y=240 and consists of 2 data bytes
+resulting in 4 blank (white) points followed by 8 black points followed by 4
+white points on the paper. The second line starts from X=315, Y=241 and
+contains 3 data bytes resulting in output of 4 white, 4 black, 12 white and
+finally 4 black points.
+
+2. Compressed data format (SUPPORTED BY THE DRIVER).
+
+General description is as follows.
+
+"\035" "Starting X point in ASCII format" "X"
+"\035" "Starting Y point in ASCII format" "Y"
+"\035" "3bcI"
+"\035" "Total number of compressed BYTES in ASCII format" ";"
+"Number of POINTS to print in this line" ";"
+"1;obi{I" "compressed data BYTES for this line in BINARY format"
+"\035" "0bcI"
+
+Additional ESC-sequences "\035" "3bcI" and "\035" "0bcI" mean start and end
+of the compressed data format, respectively. As in the discussed above case
+of a non-compressed data format, the shortest abbreviation has the form of
+
+"\035" "Starting Y point in ASCII format" "Y"
+"\035" "Total number of compressed BYTES in ASCII format" ";"
+"Number of POINTS to print in this line" ";"
+"1;obi{I" "compressed data BYTES for this line in BINARY format"
+
+COMPRESSED DATA BYTES FORMAT has the form of
+
+"d1 d2 d3 d4 d4 count_d4 d5 d6 d6 count_d6 ... d(n-1) d(n-1) count_d(n-1) dn"
+
+Here dx (x = 1 ... n) means data in a BINARY format. Any 2 repeated bytes
+MUST follow by the count, otherwise the printer will interpret the next
+data byte as a counter. The count value indicates how many bytes of the
+same value should be INSERTED after the repeated ones. So, the total number of
+repeated bytes is (count + 2), not count. If there are only 2 equal data
+bytes somewhere in the data stream, they MUST follow by zero.
+
+Example of 2 compressed data strings.
+
+"\035"105X"\035"320Y"\035"3bcI"\035"3;2048;1;obi{I"0000fe"
+"\035"105X"\035"321Y"\035"11;2048;1;obi{I"0000021fffffe5fc000011"
+
+The first one containing 3 bytes of compressed data will result in empty
+(zero) line of 2048 blank points started from X=105, Y=320. The second one
+containing 11 compressed data bytes will produce the picture of 4*8 + 3 = 35
+white points followed by 5 + 16 + 0xe5*8 + 6 = 1859 black points followed by
+2 + 8*19 = 154 white points (total 2048 points) started from X=105, Y=321.
+
+Strictly speaking, it was not necessary to adjust the number of points to
+the byte boundary. I did it for the sake of simplicity. One more argument in
+favor of this step is that the error of positioning does not exceed (7 /
+300) inches or (7 / 118) cm, that is 0.6 mm, which is negligible, I guess.
+
+ADDITIONAL INFORMATION
+
+It is also possible to use LP-8000 printer with 180x180 DPI resolution as an
+"ibmpro" device from gdevepsn.c The only thing which should be corrected, is
+the value 0x30 in static const char ibmpro_init_string[]. Decimal 36
+fixes the 1,5 times elongation along the vertical axis. It is also
+recommended to choose the appropriate values for all margins. In my case it
+was 0.2, 0.6, 0, 0.3 in the device descriptor instead of the 0.2, 0.95, 0,
+1.0
+
+Nevertheless, typical Latex file looked so ugly after printing in this mode,
+that I preferred to spend several days for hacking the format of the Japanese
+Windows printer output for 300 DPI resolution and create my own driver.
+
+Any suggestions, corrections, critical comments, etc. are welcome!
+
+Oleg Fat'yanov <faty1@rlem.titech.ac.jp>
+
+*/
+
+#include "gdevprn.h"
+
+#ifndef X_DPI
+#define X_DPI 300
+#endif
+
+#ifndef Y_DPI
+#define Y_DPI 300
+#endif
+
+#define L_MARGIN 0.25
+#define B_MARGIN 0.25
+#define R_MARGIN 0.25
+#define T_MARGIN 0.25
+
+static dev_proc_print_page(lp8000_print_page);
+
+gx_device_printer far_data gs_lp8000_device =
+ prn_device(prn_bg_procs, "lp8000", /* The print_page proc is compatible with allowing bg printing */
+ DEFAULT_WIDTH_10THS,
+ DEFAULT_HEIGHT_10THS,
+ X_DPI, Y_DPI,
+ L_MARGIN, B_MARGIN, R_MARGIN, T_MARGIN,
+ 1, lp8000_print_page);
+
+static int
+lp8000_print_page(gx_device_printer *pdev, FILE *prn_stream)
+{
+
+ int line_size = gdev_mem_bytes_per_scan_line((gx_device *)pdev);
+ int in_size = line_size;
+
+ byte *buf1 = (byte *)gs_malloc(pdev->memory, in_size, 1, "lp8000_print_page(buf1)");
+ byte *buf2 = (byte *)gs_malloc(pdev->memory, in_size, 1, "lp8000_print_page(buf2)");
+ byte *in = buf1;
+ byte *out = buf2;
+
+ int lnum, top, bottom, left, width;
+ int count, left1, left2, left0;
+
+/* Check memory allocations */
+
+ if ( buf1 == 0 || buf2 == 0 )
+ { if ( buf1 )
+ gs_free(pdev->memory, (char *)buf1, in_size, 1, "lp8000_print_page(buf1)");
+
+ if ( buf2 )
+ gs_free(pdev->memory, (char *)buf2, in_size, 1, "lp8000_print_page(buf2)");
+
+ return_error(gs_error_VMerror);
+ }
+
+/* Initialize the printer */
+
+ fwrite("\033\001@EJL \n",1,8,prn_stream);
+ fwrite("@EJL EN LA=ESC/PAGE\n",1,20,prn_stream);
+ fwrite("\035rhE\033\001@EJL \n",1,12,prn_stream);
+ fwrite("@EJL SE LA=ESC/PAGE\n",1,20,prn_stream);
+ fwrite("@EJL SET PU=1 PS=A4 ZO=OFF\n",1,27,prn_stream);
+ fwrite("@EJL EN LA=ESC/PAGE\n",1,20,prn_stream);
+ fwrite("\0350;0.24muE\0352;300;300drE",1,23,prn_stream);
+ fwrite("\0350;300;300drE\0351tsE\0351mmE",1,23,prn_stream);
+ fwrite("\0357isE\0355iaF\0355ipP\03514psE\0350poE",1,26,prn_stream);
+ fwrite("\03560;60loE\0350X\0350Y",1,15,prn_stream);
+ fwrite("\0350;0;2360;3388caE",1,17,prn_stream);
+ fwrite("\0351cmE\0350alfP",1,11,prn_stream);
+ fwrite("\0350affP\0350boP\0350abP",1,16,prn_stream);
+ fwrite("\0354ilG\0350bcI\0350sarG",1,16,prn_stream);
+ fwrite("\0351;0;100spE\0352owE",1,16,prn_stream);
+
+/* Here the common part of the initialization string ends */
+
+/* Calculate the PRINTER_LEFT_MARGIN = device_left_margin - 60 adjusted to
+the byte boundary. Save this value for future comparison and set the
+starting X value of the printer line.
+*/
+ left1 = (int) (L_MARGIN * pdev->x_pixels_per_inch) - 60;
+ left1 = (left1 >> 3) << 3;
+ left0 = left1;
+
+ fwrite("\035",1,1,prn_stream);
+ fprintf(prn_stream,"%d",left1);
+ fwrite("X",1,1,prn_stream);
+
+ /* Set the compressed data format */
+ fwrite("\0353bcI",1,5,prn_stream);
+
+ top = T_MARGIN * pdev->y_pixels_per_inch;
+ bottom = pdev->height - B_MARGIN * pdev->y_pixels_per_inch;
+
+ left = ( (int) (L_MARGIN * pdev->x_pixels_per_inch) ) >> 3 ;
+ width = ((pdev->width - (int)(R_MARGIN * pdev->x_pixels_per_inch)) >> 3) - left;
+
+ /*
+ ** Print the page:
+ */
+
+ for ( lnum = top; lnum < bottom ; )
+
+ {
+ byte *in_data;
+ byte *inp;
+ byte *in_end;
+ byte *outp;
+ register byte *p, *q;
+ int lcnt;
+
+ /*
+ ** Check buffer for 0 data.
+ */
+
+ gdev_prn_get_bits(pdev, lnum, in, &in_data);
+ while ( in_data[0] == 0 &&
+ !memcmp((char *)in_data, (char *)in_data + 1, line_size - 1) &&
+ lnum < bottom )
+ {
+ lnum++;
+ gdev_prn_get_bits(pdev, lnum, in, &in_data);
+ }
+
+ if(lnum == bottom ) break;
+ /* finished with this page */
+
+ lcnt = gdev_prn_copy_scan_lines(pdev, lnum, in, in_size);
+
+ inp = in + left;
+ in_end = inp + width;
+
+/* Remove trailing 0s form the scan line data */
+
+ while (in_end > inp && in_end[-1] == 0)
+ {
+ in_end--;
+ }
+
+/* Remove leading 0s form the scan line data */
+
+ for(left2 = 0; inp < in_end && inp[0] == 0; inp++,left2++);
+
+/* Recalculate starting X value */
+
+ left2 = left1 + (left2 << 3);
+
+/* Compress non-zero data for this line*/
+
+ outp = out;
+
+ for( p = inp, q = inp + 1 ; q < in_end ; )
+ {
+ if( *p != *q++ )
+ {
+ /*
+ Copy non-repeated bytes
+ to the output buffer
+ */
+ *outp++ = *p++;
+ }
+ else
+ {
+ for (count = 2; ( *p == *q ) && (q < in_end); q++, count++);
+
+ /*
+ Copy repeated bytes and counts to the output buffer.
+ As long as count is <= 255, additional step is necessary
+ for a long repeated sequence
+ */
+
+ while (count > 257)
+ {
+ *outp++ = *p;
+ *outp++ = *p;
+ *outp++ = 255;
+ p += 257;
+ count -=257;
+ }
+ *outp++ = *p;
+ *outp++ = *p;
+ *outp++ = count - 2;
+ p += count;
+ q = p+1;
+ }
+ }
+
+/* The next line is necessary just in case of a single non-repeated byte at
+the end of the input buffer */
+
+if (p == (in_end - 1)) *outp++ = *p;
+
+/* End of the compression procedure */
+
+/* Set a new value of the starting X point, if necessary */
+
+if (left2 != left0)
+ {
+ left0 = left2;
+ fwrite("\035",1,1,prn_stream);
+ fprintf(prn_stream,"%d",left2);
+ fwrite("X",1,1,prn_stream);
+ }
+
+/* Output the data string to the printer.
+Y coordinate of the printer equals (lnum - 60)
+*/
+
+ fwrite("\035",1,1,prn_stream);
+ fprintf(prn_stream,"%d",lnum-60);
+ fwrite("Y\035",1,2,prn_stream);
+ fprintf(prn_stream,"%d;",(outp - out));
+ fprintf(prn_stream,"%d;",(in_end - inp) << 3);
+ fwrite("1;0bi{I",1,7,prn_stream);
+ fwrite(out,1,(outp - out),prn_stream);
+
+ lnum++;
+
+ }
+
+/* Send the termination string */
+
+ fwrite("\0350bcI",1,5,prn_stream);
+ fwrite("\0351coO",1,5,prn_stream);
+ fwrite("\035rhE",1,4,prn_stream);
+
+ fwrite("\033\001@EJL \n",1,8,prn_stream);
+ fwrite("@EJL SE LA=ESC/PAGE\n",1,20,prn_stream);
+ fwrite("@EJL SET PU=1 PS=A4 ZO=OFF\n",1,27,prn_stream);
+ fwrite("@EJL EN LA=ESC/PAGE\n",1,20,prn_stream);
+ fwrite("\0350;0.24muE\0352;300;300drE",1,23,prn_stream);
+ fwrite("\0350;300;300drE\0351tsE\0351mmE",1,23,prn_stream);
+ fwrite("\0357isE\0355iaF\0355ipP\03514psE\0350poE",1,26,prn_stream);
+ fwrite("\03560;60loE\0350X\0350Y",1,15,prn_stream);
+ fwrite("\0350;0;2360;3388caE",1,17,prn_stream);
+ fwrite("\0351cmE\0350alfP",1,11,prn_stream);
+ fwrite("\0350affP\0350boP\0350abP",1,16,prn_stream);
+ fwrite("\0354ilG\0350bcI\0350sarG",1,16,prn_stream);
+ fwrite("\035rhE",1,4,prn_stream);
+ fwrite("\033\001@EJL \n",1,8,prn_stream);
+ fwrite("\033\001@EJL \n",1,8,prn_stream);
+
+ fflush(prn_stream);
+
+ gs_free(pdev->memory, (char *)buf2, in_size, 1, "lp8000_print_page(buf2)");
+ gs_free(pdev->memory, (char *)buf1, in_size, 1, "lp8000_print_page(buf1)");
+ return 0;
+}
diff --git a/devices/gdevlxm.c b/devices/gdevlxm.c
new file mode 100644
index 000000000..f78f3b1b1
--- /dev/null
+++ b/devices/gdevlxm.c
@@ -0,0 +1,417 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+/*
+ * Lexmark 5700 ink-jet printer driver for Ghostscript
+ *
+ * defines the lxm5700m device for printing in black-and-white at 1200 dpi
+ * doesn't handle color or any other resolution.
+ * Native resolution appears to be 600 x 1200, but print bands are overlapped.
+ *
+ * I use the command
+ * gs -sOutputFile=/dev/lp0 -sDevice=lxm5700m -dHeadSeparation=15 file.ps
+ *
+ * where HeadSeparation varies from print-cartridge to print-cartridge and
+ * 16 (the default) usually works fine.
+ *
+ * Stephen Taylor setaylor@ma.ultranet.com staylor@cs.wpi.edu
+ */
+
+#include "gdevprn.h"
+#include "gsparams.h"
+
+/* The procedure descriptors */
+/* declare functions */
+static dev_proc_print_page(lxm5700m_print_page);
+static dev_proc_get_params(lxm_get_params);
+static dev_proc_put_params(lxm_put_params);
+
+/* set up dispatch table. I follow gdevdjet in using gdev_prn_output_page */
+/* Since the print_page doesn't alter the device, this device can print in the background */
+static const gx_device_procs lxm5700m_procs =
+ prn_params_procs(gdev_prn_open, gdev_prn_bg_output_page, gdev_prn_close,
+ lxm_get_params, lxm_put_params);
+
+/* The device descriptors */
+
+/* define a subclass with useful state in it. */
+typedef struct lxm_device_s { /* a sub-class of gx_device_printer */
+ gx_device_common;
+ gx_prn_device_common;
+ int headSeparation;
+} lxm_device;
+
+/* Standard lxm5700m device */
+lxm_device far_data gs_lxm5700m_device = {
+ prn_device_std_body(lxm_device, lxm5700m_procs, "lxm5700m",
+ DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
+ 600, 600, /* x dpi, y dpi */
+ 0.2, 0.0, 0.0, 0.0, /* margins */
+ 1, lxm5700m_print_page),
+ 16 /* default headSeparation value */
+};
+
+/* I don't know the whole protocol for the printer, but let me tell
+ * you about the fraction I use.
+ * Each page begins with a header, which I describe as init1, init2, init3 --
+ * (I separate them, because I've seen output in which those sections
+ * seemed to appear independently, but I've deleted the only code I
+ * had that actually used them separately.)
+ * Then there are a number of swipe commands, each of which describes one
+ * swipe of the printhead. Each swipe command begins with a fixed
+ * header, which gives the number of bytes in the command,
+ * left and right margins,
+ * a vertical offset, some noise characters I don't know the meaning of,
+ * and the table of bits. The bits are given one column at a time, using a
+ * simple compression scheme: a directory, consisting of two bytes, tells
+ * which sixteen-bit intervals of the 208-bit column contain 1-bits; then
+ * the appropriate number of sixteen-bit bit-maps follow. (A zero-bit in the
+ * directory indicates that a bitmap for that sector follows.) In the worst case,
+ * this scheme would be bigger than the uncompressed bitmap, but it seems to
+ * usually save 80-90%. The biggest complication of the bitmap scheme is this:
+ * There are two print-heads on the black cartridge, and they are 16 pixels
+ * (or headSeparation) apart. Odd columns of the swipe address one printhead,
+ * and even columns the other. On the following swipe, the printheads
+ * addressed by even and odd columns are reversed. I think the printheads might be
+ * staggered, but the output I've seen staggers things in software;
+ * adjacent vertical bits on the same head are not addressed; the missing
+ * bits are written in by the second head when it passes (16 columns later.)
+ * In my code, I call the state of addressing one head or the other "direction".
+ * Originally I thought that the printhead was writing on both leftward and
+ * rightward motion, and that which head was addressed by odd columns changed
+ * accordingly. I'm no longer sure this is true, but the head addressed by the
+ * even columns does alternate with each swipe.
+ */
+/*
+ * various output shorthands
+ */
+
+#define init1() \
+ top(), \
+ 0xA5,0, 3, 0x40,4,5, \
+ 0xA5,0, 3, 0x40,4,6, \
+ 0xA5,0, 3, 0x40,4,7, \
+ 0xA5,0, 3, 0x40,4,8, \
+ 0xA5,0, 4, 0x40,0xe0,0x0b, 3
+
+#define init2() \
+ 0xA5,0, 11, 0x40,0xe0,0x41, 0,0,0,0,0,0,0, 2, \
+ 0xA5,0, 6, 0x40, 5, 0,0,0x80,0 \
+
+#define init3() \
+ 0x1b,'*', 7,0x73,0x30, \
+ 0x1b,'*', 'm', 0, 0x14, 3, 0x84, 2, 0, 1, 0xf4, \
+ 0x1b,'*', 7,0x63, \
+ 0x1b,'*', 'm', 0, 0x42, 0, 0, \
+ 0xA5,0, 5, 0x40,0xe0,0x80, 8, 7, \
+ 0x1b,'*', 'm', 0, 0x40, 0x15, 7, 0x0f, 0x0f \
+
+#define top() \
+ 0xA5,0, 6, 0x40, 3,3,0xc0,0x0f,0x0f \
+
+#define fin() \
+ 0x1b,'*', 7, 0x65 \
+
+#define outByte(b) putc(b, prn_stream)
+
+#define RIGHTWARD 0
+#define LEFTWARD 1
+/* number of pixels between even columns in output and odd ones*/
+/* #define headSeparation 16 */
+/* overlap between successive swipes of the print head */
+#define overLap 104
+/* height of printhead in pixels */
+#define swipeHeight 208
+/* number of shorts described by each column directory */
+#define directorySize 13
+
+/* ------ Driver procedures ------ */
+
+/* Send the page to the printer. */
+static int
+lxm5700m_print_page(gx_device_printer *pdev, FILE *prn_stream)
+{
+ int lnum,minX, maxX, i, l, highestX, leastX, extent;
+ int direction = RIGHTWARD;
+ int lastY = 0;
+
+ int line_size = gdev_mem_bytes_per_scan_line((gx_device *)pdev);
+ /* Note that in_size is a multiple of 8. */
+ int in_size = line_size * (swipeHeight);
+ int swipeBuf_size = in_size;
+ byte *buf1 = (byte *)gs_malloc(pdev->memory, in_size, 1, "lxm_print_page(buf1)");
+ byte *swipeBuf =
+ (byte *)gs_malloc(pdev->memory, swipeBuf_size, 1, "lxm_print_page(swipeBuf)");
+ byte *in = buf1;
+
+ /* Check allocations */
+ if ( buf1 == 0 || swipeBuf == 0 ) {
+ if ( buf1 )
+quit_ignomiously: /* and a goto into an if statement is pretty ignomious! */
+ gs_free(pdev->memory, (char *)buf1, in_size, 1, "lxm_print_page(buf1)");
+ if ( swipeBuf )
+ gs_free(pdev->memory, (char *)swipeBuf, swipeBuf_size, 1, "lxm_print_page(swipeBuf)");
+ return_error(gs_error_VMerror);
+ }
+
+ { /* Initialize the printer and reset the margins. */
+ static const char init_string[] = {
+ init1(),
+ init2(),
+ init3()
+ };
+ fwrite(init_string, 1, sizeof(init_string), prn_stream);
+ }
+ /* Print lines of graphics */
+ for (lnum=0; lnum < pdev->height-swipeHeight ; ) { /* increment in body */
+ byte *in_data;
+ register byte *outp;
+ int lcnt;
+
+ { /* test for blank scan lines. We maintain the */
+ /* loop invariant lnum <pdev->height, but modify lnum */
+ int l;
+
+ for (l=lnum; l<pdev->height; l++) {
+ /* Copy 1 scan line and test for all zero. */
+ gdev_prn_get_bits(pdev, l, in, &in_data);
+ if ( in_data[0] != 0 ||
+ memcmp((char *)in_data, (char *)in_data + 1, line_size - 1)
+ ) {
+ break;
+ }
+ }/* end for l */
+
+ /* now l is the next non-blank scan line */
+ if (l >= pdev->height) {/* if there are no more bits on this page */
+ lnum = l;
+ break; /* end the loop and eject the page*/
+ }
+
+ /* leave room for following swipe to reinforce these bits */
+ if (l-lnum > overLap) lnum = l - overLap;
+
+ /* if the first non-blank near bottom of page */
+ if (lnum >=pdev->height - swipeHeight) {
+ /* don't move the printhead over empty air*/
+ lnum = pdev->height - swipeHeight;
+ }
+ }
+
+ /* Copy the the scan lines. */
+ lcnt = gdev_prn_copy_scan_lines(pdev, lnum, in, in_size);
+ if ( lcnt < swipeHeight ) {
+ /* Pad with lines of zeros. */
+ memset(in + lcnt * line_size, 0,
+ in_size - lcnt * line_size);
+ }
+
+ /* compute right and left margin for this swipe */
+ minX = line_size;
+ maxX = 0;
+ for (l=0; l<swipeHeight; l++) {/* for each line of swipe */
+ for (i=0; i<minX; i++) {/* look for left-most non-zero byte*/
+ if (in[l*line_size+i] !=0) {
+ minX = i;
+ break;
+ }
+ }
+ for (i=line_size-1; i>=maxX; i--) {/* look for right-most */
+ if (in[l*line_size+i] !=0) {
+ maxX = i;
+ break;
+ }
+ }
+ }
+ minX = (minX&(-2)); /* truncate to even */
+ maxX = (maxX+3)&-2; /* raise to even */
+
+ highestX = maxX*8-1;
+ leastX = minX*8;
+ extent = highestX -leastX +1;
+
+ outp = swipeBuf;
+
+ /* macro, not fcn call. Space penalty is modest, speed helps */
+#define buffer_store(x) if(outp-swipeBuf>=swipeBuf_size) {\
+ gs_free(pdev->memory, (char *)swipeBuf, swipeBuf_size, 1, "lxm_print_page(swipeBuf)");\
+ swipeBuf_size*=2;\
+ swipeBuf = (byte *)gs_malloc(pdev->memory, swipeBuf_size, 1, "lxm_print_page(swipeBuf)");\
+ if (swipeBuf == 0) goto quit_ignomiously;\
+ break;}\
+ else *outp++ = (x)
+
+ {/* work out the bytes to store for this swipe*/
+
+ int sx, sxBy8, sxMask;
+ int words[directorySize];
+ bool f, sum;
+ int retval=0;
+ int j,c,y;
+ int j1,c1;
+ int i,b,x, directory ;
+
+ /* want to set up pointers for (upto two) stripes covered by the output*/
+
+ /* now for each column covered by output: */
+ for (x=leastX; x<=highestX; x++) {
+ for (i=0; i<directorySize; i++) {
+ words[i] = 0;
+ }
+ directory = 0x2000; /* empty directory != 0 */
+
+ /* prime loops: make comparisons here */
+ switch (direction) {
+ case(RIGHTWARD):
+ sx = (x&1)==1 ? x : x-(((lxm_device*)pdev)->headSeparation);
+ j1 = (x&1); /* even if x even, odd if x odd */
+ break;
+ default: /* shouldn't happen ... but compilation checks */
+ case(LEFTWARD):
+ sx = (x&1)==0 ? x : x-((lxm_device*)pdev)->headSeparation;
+ j1 = 1-(x&1); /* odd if x even, even if x odd */
+ }
+ c1 = 0x8000 >> j1;
+
+ sxBy8 = sx/8;
+ sxMask = 0x80>>(sx%8);
+
+ /* loop through all the swipeHeight bits of this column */
+ for (i = 0, b=1, y= sxBy8+j1*line_size; i < directorySize; i++,b<<=1) {
+ sum = false;
+ for (j=j1,c=c1 /*,y=i*16*line_size+sxBy8*/; j<16; j+=2, y+=2*line_size, c>>=2) {
+ f = (in[y]&sxMask);
+ if (f) {
+ words[i] |= c;
+ sum |= f;
+ }
+ }
+ if (!sum) directory |=b;
+ }
+ retval+=2;
+ buffer_store(directory>>8); buffer_store(directory&0xff);
+ if (directory != 0x3fff) {
+ for (i=0; i<directorySize; i++) {
+ if (words[i] !=0) {
+ buffer_store(words[i]>>8) ; buffer_store(words[i]&0xff);
+ retval += 2;
+ }
+ }
+ }
+ }
+#undef buffer_store
+ }
+ {/* now write out header, then buffered bits */
+ int leastY = lnum;
+
+ /* compute size of swipe, needed for header */
+ int sz = 0x1a + outp - swipeBuf;
+
+ /* put out header*/
+ int deltaY = 2*(leastY - lastY); /* vert coordinates here are 1200 dpi */
+ lastY = leastY;
+ outByte(0x1b); outByte('*'); outByte(3);
+ outByte(deltaY>>8); outByte(deltaY&0xff);
+ outByte(0x1b); outByte('*'); outByte(4); outByte(0); outByte(0);
+ outByte(sz>>8); outByte(sz&0xff); outByte(0); outByte(3);
+ outByte(1); outByte(1); outByte(0x1a);
+ outByte(0);
+ outByte(extent>>8); outByte(extent&0xff);
+ outByte(leastX>>8); outByte(leastX&0xff);
+ outByte(highestX>>8); outByte(highestX&0xff);
+ outByte(0); outByte(0);
+ outByte(0x22); outByte(0x33); outByte(0x44);
+ outByte(0x55); outByte(1);
+ /* put out bytes */
+ fwrite(swipeBuf,1,outp-swipeBuf,prn_stream);
+ }
+ lnum += overLap;
+ direction ^= 1;
+ }/* ends the loop for swipes of the print head.*/
+
+ /* Eject the page and reinitialize the printer */
+ {
+ static const char bottom[] = {
+ fin() /*, looks like I can get away with only this much ...
+ init1(),
+ init3(),
+ fin() ,
+ top(),
+ fin() */
+ };
+ fwrite(bottom, 1, sizeof(bottom), prn_stream);
+ }
+ fflush(prn_stream);
+
+ gs_free(pdev->memory, (char *)swipeBuf, swipeBuf_size, 1, "lxm_print_page(swipeBuf)");
+ gs_free(pdev->memory, (char *)buf1, in_size, 1, "lxm_print_page(buf1)");
+ return 0;
+}
+
+/*
+ * There are a number of parameters which can differ between ink cartridges.
+ * The Windows driver asks you to recalibrate every time you load a new
+ * cartridge.
+ * most of the parameters adjusted there relate to color, and so this
+ * monotone driver doesn't need them. However, the Lexmark 5700 black
+ * cartridge has two columns of dots, separated by about 16 pixels.
+ * This `head separation' distance can vary between cartridges, so
+ * we provide a parameter to set it. In my small experience I've not
+ * set the corresponding parameter in windows to anything greater than 17
+ * or smaller than 15, but it would seem that it can vary from 1 to 32,
+ * based on the calibration choices offered.
+ *
+ * As I understand the rules laid out in gsparams.h,
+ * lxm_get_params is supposed to return the current values of parameters
+ * and lxm_put_params is supposed to set up values in the lxm_device
+ * structure which can be used by the lxm5700m_print_page routine.
+ * I've copied my routines from gdevcdj.c
+ */
+
+static int
+lxm_get_params(gx_device *pdev, gs_param_list *plist)
+{
+ lxm_device* const ldev = (lxm_device*)pdev;
+ int code = gdev_prn_get_params(pdev, plist);
+
+ if ( code < 0 ) return code;
+ code = param_write_int(plist,
+ "HeadSeparation",
+ (int *)&(ldev->headSeparation));
+
+ return code;
+}
+
+/* put_params is supposed to check all the parameters before setting any. */
+static int
+lxm_put_params(gx_device *pdev, gs_param_list *plist)
+{
+ int ecode;
+ lxm_device* const ldev = (lxm_device*)pdev;
+ int trialHeadSeparation=ldev->headSeparation;
+ int code = param_read_int(plist, "HeadSeparation", &trialHeadSeparation);
+
+ if ( trialHeadSeparation < 1 || trialHeadSeparation > 32 )
+ param_signal_error(plist, "HeadSeparation", gs_error_rangecheck);
+ /* looks like param_signal_error is not expected to return */
+ ecode = gdev_prn_put_params(pdev, plist); /* call super class put_params */
+ if ( code < 0 ) return code;
+ if (ecode < 0) return ecode;
+
+ /* looks like everything okay; go ahead and set headSeparation */
+ ldev->headSeparation = trialHeadSeparation;
+ if ( code == 1) return ecode; /* I guess this means there is no "HeadSeparation" parameter */
+ return 0;
+}
diff --git a/devices/gdevmac.c b/devices/gdevmac.c
new file mode 100644
index 000000000..04ee6b75f
--- /dev/null
+++ b/devices/gdevmac.c
@@ -0,0 +1,780 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* MacOS bitmap output device. This code is superceeded by
+ the newer gsapi_* interface and the DISPLAY device. Please
+ use that instead. See doc/API.htm for more information */
+
+#include "gdevmac.h"
+#include "gsparam.h"
+#include "gsdll.h"
+
+/* The device descriptor */
+
+gx_device_procs gs_mac_procs = {
+ mac_open, /* open_device */
+ mac_get_initial_matrix, /* get_initial_matrix */
+ mac_sync_output, /* sync_output */
+ mac_output_page, /* output_page */
+ mac_close, /* close_device */
+ gx_default_rgb_map_rgb_color, /* map_rgb_color */
+ gx_default_rgb_map_color_rgb, /* map_color_rgb */
+ mac_fill_rectangle, /* fill_rectangle */
+ NULL, /* tile_rectangle */
+ mac_copy_mono, /* copy_mono */
+ NULL,// mac_copy_color, /* copy_color */
+ mac_draw_line, /* draw_line */
+ NULL, /* get_bits */
+ mac_get_params, /* get_params */
+ mac_put_params, /* put_params */
+ NULL, /* map_cmyk_color */
+ NULL, /* get_xfont_procs */
+ NULL, /* get_xfont_device */
+ NULL, /* map_rgb_alpha_color */
+ gx_page_device_get_page_device, /* get_page_device */
+ gx_default_get_alpha_bits, /* get_alpha_bits */
+ mac_copy_alpha, /* copy_alpha */
+ NULL, /* get_band */
+ NULL, /* copy_rop */
+ NULL, /* fill_path */
+ NULL, /* stroke_path */
+ NULL, /* fill_mask */
+ NULL, /* fill_trapezoid */
+ NULL, /* fill_parallelogram */
+ NULL, /* fill_triangle */
+ NULL, /* draw_thin_line */
+ NULL, /* begin_image */
+ NULL, /* image_data */
+ NULL, /* end_image */
+ NULL //mac_strip_tile_rectangle /* strip_tile_rectangle */
+};
+
+/* The instance is public. */
+
+gx_device_macos gs_macos_device = {
+ std_device_color_body(gx_device_macos,
+ &gs_mac_procs,
+ DEV_MAC_NAME,
+ DEFAULT_DEV_WIDTH, /* x and y extent (nominal) */
+ DEFAULT_DEV_HEIGHT,
+ DEFAULT_DEV_DPI, /* x and y density (nominal) */
+ DEFAULT_DEV_DPI,
+ /*dci_color(*/8, 255, 256/*)*/),
+ { 0 }, /* std_procs */
+ "", /* Output Filename */
+ NULL, /* Output File */
+ NULL, /* PicHandle to "draw" into */
+ NULL, /* PicPtr */
+ false, /* outputPage */
+ -1, /* lastFontFace */
+ -1, /* lastFontSize */
+ -1, /* lastFontID */
+ 0 /* numUsedFonts */
+};
+
+static int
+mac_open(register gx_device *dev)
+{
+ gx_device_macos * mdev = (gx_device_macos *)dev;
+
+ static short picHeader[42] = { 0x0000, // picture size
+ 0x0000, 0x0000, 0x0318, 0x0264, // bounding rect at 72dpi
+ 0x0011, 0x02ff, 0x0c00, 0xfffe, 0x0000, // version/header opcodes
+ 0x0048, 0x0000, // best x resolution
+ 0x0048, 0x0000, // best y resolution
+ 0x0000, 0x0000, 0x0318, 0x0264, // optimal src rect at 72dpi
+ 0x0000, // reserved
+
+ 0x0000, 0x001e, // DefHilite
+ 0x0008, 0x0048, // PenMode
+ 0x001a, 0x0000, 0x0000, 0x0000, // RGBFgCol = Black
+ 0x001b, 0xFFFF, 0xFFFF, 0xFFFF, // RGBBkCol = White
+
+ 0x0001, 0x000A, // set clipping
+ 0x0000, 0x0000, 0x0318, 0x0264, // clipping rect
+ 0x0032, 0x0000, 0x0000, 0x0318, 0x0264 // erase rect
+ };
+
+ mac_set_colordepth(dev, mdev->color_info.depth);
+
+ mdev->numUsedFonts = 0;
+ mdev->lastFontFace = -1;
+ mdev->lastFontSize = -1;
+ mdev->lastFontID = -1;
+
+ mdev->pic = (PicHandle) NewHandle(500000);
+ if (mdev->pic == 0) // error, not enough memory
+ return gs_error_VMerror;
+
+ HLockHi((Handle) mdev->pic); // move handle high and lock it
+
+ mdev->currPicPos = (short*) *mdev->pic;
+ memcpy(mdev->currPicPos, picHeader, 42*2);
+ mdev->currPicPos += 42;
+
+ // enter correct dimensions and resolutions
+ ((short*)(*mdev->pic))[ 3] = mdev->MediaSize[1];
+ ((short*)(*mdev->pic))[ 4] = mdev->MediaSize[0];
+
+ ((short*)(*mdev->pic))[16] = ((short*)(*mdev->pic))[35] = ((short*)(*mdev->pic))[40] = mdev->height;
+ ((short*)(*mdev->pic))[17] = ((short*)(*mdev->pic))[36] = ((short*)(*mdev->pic))[41] = mdev->width;
+
+ ((short*)(*mdev->pic))[10] = (((long) X2Fix( mdev->x_pixels_per_inch )) & 0xFFFF0000) >> 16;
+ ((short*)(*mdev->pic))[11] = ((long) X2Fix( mdev->x_pixels_per_inch )) & 0x0000FFFF;
+ ((short*)(*mdev->pic))[12] = (((long) X2Fix( mdev->y_pixels_per_inch )) & 0xFFFF0000) >> 16;
+ ((short*)(*mdev->pic))[13] = ((long) X2Fix( mdev->y_pixels_per_inch )) & 0x0000FFFF;
+
+ // finish picture, but dont increment pointer, we want to go on drawing
+ *mdev->currPicPos = 0x00ff;
+
+ // notify the caller that a new device was opened
+ if (pgsdll_callback)
+ (*pgsdll_callback) (GSDLL_DEVICE, (char *)mdev, 1);
+
+ return 0;
+}
+
+static void
+mac_get_initial_matrix(register gx_device *dev, register gs_matrix *pmat)
+{
+ pmat->xx = dev->x_pixels_per_inch / 72.0;
+ pmat->xy = 0;
+ pmat->yx = 0;
+ pmat->yy = dev->y_pixels_per_inch / -72.0;
+ pmat->tx = 0;
+ pmat->ty = dev->height;
+}
+
+/* Make the output appear on the screen. */
+int
+mac_sync_output(gx_device * dev)
+{
+ gx_device_macos * mdev = (gx_device_macos *)dev;
+
+ // finish picture, but dont increment pointer, we want to go on drawing
+ *mdev->currPicPos = 0x00ff;
+
+ // tell the caller to sync
+ if (pgsdll_callback)
+ (*pgsdll_callback) (GSDLL_SYNC, (char *)mdev, 0);
+
+ return (0);
+}
+
+int
+mac_output_page(gx_device * dev, int copies, int flush)
+{
+ gx_device_macos * mdev = (gx_device_macos *)dev;
+ int code = 0;
+
+ mdev->outputPage = true;
+
+ if (strcmp(mdev->outputFileName, "")) {
+ // save file
+ code = mac_save_pict(dev);
+ }
+
+ // tell the caller that the page is done
+ if (pgsdll_callback)
+ (*pgsdll_callback) (GSDLL_PAGE, (char *)mdev, 0);
+
+ gx_finish_output_page(dev, copies, flush);
+
+ return code;
+}
+
+static int
+mac_save_pict(gx_device * dev)
+{
+ gx_device_macos * mdev = (gx_device_macos *)dev;
+ int code = 0;
+ int i;
+
+ if (mdev->outputFile == NULL) {
+ code = gx_device_open_output_file(dev, mdev->outputFileName, true, true, &(mdev->outputFile));
+ if (code < 0) return code;
+ }
+
+ for (i=0; i<512; i++) fputc(0, mdev->outputFile);
+ fwrite(*(mdev->pic), sizeof(char), ((long) mdev->currPicPos - (long) *mdev->pic + 2), mdev->outputFile);
+
+ gx_device_close_output_file(dev, mdev->outputFileName, mdev->outputFile);
+ mdev->outputFile = NULL;
+
+ return code;
+}
+
+/* Close the device. */
+static int
+mac_close(register gx_device *dev)
+{
+ gx_device_macos * mdev = (gx_device_macos *)dev;
+
+ long len;
+
+ HUnlock((Handle) mdev->pic); // no more changes in the pict -> unlock handle
+ if (strcmp(mdev->outputFileName, "")) {
+ DisposeHandle((Handle) mdev->pic);
+ gx_device_close_output_file(dev, mdev->outputFileName, mdev->outputFile);
+ mdev->outputFile = 0;
+ } else {
+ len = (long)mdev->currPicPos - (long)*mdev->pic;
+ SetHandleSize((Handle) mdev->pic, len + 10); // +10 just for the case
+ }
+
+ // notify the caller that the device was closed
+ // it has to dispose the PICT handle when it is ready!
+ if (pgsdll_callback)
+ (*pgsdll_callback) (GSDLL_DEVICE, (char *)mdev, 0);
+
+ return 0;
+}
+
+/* Fill a rectangle with a color. */
+static int
+mac_fill_rectangle(register gx_device *dev,
+ int x, int y, int w, int h,
+ gx_color_index color)
+{
+ gx_device_macos * mdev = (gx_device_macos *)dev;
+
+ /* ignore a fullpage rect directly after an output_page, this would clear the pict */
+ if (mdev->outputPage &&
+ (x == 0) && (y == 0) && (w == mdev->width) && (h == mdev->height)) {
+ return 0;
+ }
+
+ CheckMem(1024, 100*1024);
+ ResetPage();
+
+ GSSetFgCol(dev, mdev->currPicPos, color);
+ PICT_fillRect(mdev->currPicPos, x, y, w, h);
+
+ PICT_OpEndPicGoOn(mdev->currPicPos);
+
+ return 0;
+}
+
+/* Draw a line */
+static int
+mac_draw_line (register gx_device *dev,
+ int x0, int y0,
+ int x1, int y1,
+ gx_color_index color)
+{
+ gx_device_macos * mdev = (gx_device_macos *)dev;
+
+ CheckMem(1024, 100*1024);
+ ResetPage();
+
+ GSSetFgCol(dev, mdev->currPicPos, color);
+ PICT_Line(mdev->currPicPos, x0, y0, x1, y1);
+
+ PICT_OpEndPicGoOn(mdev->currPicPos);
+
+ return 0;
+}
+
+/* Copy a monochrome bitmap. */
+static int
+mac_copy_mono (register gx_device *dev,
+ const unsigned char *base, int data_x, int raster, gx_bitmap_id id,
+ int x, int y, int w, int h,
+ gx_color_index color_0, gx_color_index color_1)
+{
+ gx_device_macos * mdev = (gx_device_macos *)dev;
+
+ int byteCount = raster * h;
+ short copyMode;
+
+ // this case doesn't change the picture -> return without wasting time
+ if (color_0 == gx_no_color_index && color_1 == gx_no_color_index)
+ return 0;
+
+ fit_copy(dev, base, data_x, raster, id, x, y, w, h);
+
+ CheckMem(10*1024 + byteCount*10, 100*1024 + byteCount*10);
+ ResetPage();
+
+ if (color_0 == gx_no_color_index)
+ copyMode = srcOr;
+ else if (color_1 == gx_no_color_index)
+ copyMode = notSrcBic; // this mode is untested ! (no file found which is using it)
+ else
+ copyMode = srcCopy;
+
+ copyMode += ditherCopy;
+
+ GSSetBkCol(dev, mdev->currPicPos, color_0);
+ GSSetFgCol(dev, mdev->currPicPos, color_1);
+
+ PICTWriteOpcode(mdev->currPicPos, 0x0098);
+ PICTWriteInt(mdev->currPicPos, raster);
+ PICTWriteRect(mdev->currPicPos, 0, 0, raster*8, h);
+ PICTWriteRect(mdev->currPicPos, data_x, 0, w, h);
+ PICTWriteRect(mdev->currPicPos, x, y, w, h);
+ PICTWriteInt(mdev->currPicPos, copyMode);
+ PICTWriteDataPackBits(mdev->currPicPos, base, raster, h);
+
+ PICT_OpEndPicGoOn(mdev->currPicPos);
+
+ return 0;
+}
+
+/* Fill a region with a color and apply a per-pixel alpha-value */
+/* alpha value is simulated by changed the value to white. Full transparency means white color */
+/* that's why this will only work on a fully white background!!!! */
+static int
+mac_copy_alpha(gx_device *dev, const unsigned char *base, int data_x,
+ int raster, gx_bitmap_id id, int x, int y, int w, int h,
+ gx_color_index color, int depth)
+{
+ gx_device_macos * mdev = (gx_device_macos *)dev;
+
+ ColorSpec *colorTable;
+ short copyMode, shade, maxShade = (1 << depth) - 1, byteCount = raster * h;
+ gx_color_value rgb[3];
+ colorHSV colHSV;
+ colorRGB colRGB;
+ float saturation, value;
+
+ fit_copy(dev, base, data_x, raster, id, x, y, w, h);
+
+ CheckMem( byteCount*4 + 200*1024, byteCount*4 + 500*1024 );
+ ResetPage();
+
+ colorTable = (ColorSpec*) malloc(sizeof(ColorSpec) * (maxShade+1));
+ if (colorTable == NULL)
+ return gs_error_VMerror;
+
+ (*dev_proc(dev, map_color_rgb))(dev, color, rgb);
+ colRGB.red = rgb[0];
+ colRGB.green = rgb[1];
+ colRGB.blue = rgb[2];
+ mac_convert_rgb_hsv(&colRGB, &colHSV);
+ saturation = colHSV.s;
+ value = colHSV.v;
+
+ for (shade=0; shade <= maxShade; shade++) {
+ colorTable[shade].value = maxShade - shade;
+
+ colHSV.s = saturation * (1.0 - (float)shade/(float)maxShade);
+ colHSV.v = value + ((1.0 - value) * (float)shade/(float)maxShade);
+
+ mac_convert_hsv_rgb(&colHSV, &colRGB);
+ colorTable[shade].rgb.red = colRGB.red;
+ colorTable[shade].rgb.green = colRGB.green;
+ colorTable[shade].rgb.blue = colRGB.blue;
+ }
+ copyMode = srcCopy + ditherCopy;
+
+ GSSetStdCol(mdev->currPicPos);
+
+ if (raster < 8) {
+ PICTWriteOpcode(mdev->currPicPos, 0x0090);
+ } else {
+ PICTWriteOpcode(mdev->currPicPos, 0x0098);
+ }
+ PICTWritePixMap(mdev->currPicPos, 0, 0, raster*8/depth, h, raster, 0, 0,
+ X2Fix(mdev->x_pixels_per_inch), X2Fix(mdev->y_pixels_per_inch), depth);
+ PICTWriteColorTable(mdev->currPicPos, 0, maxShade+1, colorTable);
+ PICTWriteRect(mdev->currPicPos, data_x, 0, w, h);
+ PICTWriteRect(mdev->currPicPos, x, y, w, h);
+ PICTWriteInt(mdev->currPicPos, copyMode);
+ PICTWriteDataPackBits(mdev->currPicPos, base, raster, h);
+
+ PICT_OpEndPicGoOn(mdev->currPicPos);
+
+ free(colorTable);
+
+ return 0;
+}
+
+void
+mac_convert_rgb_hsv(colorRGB *inRGB, colorHSV *HSV)
+{
+#define NORMALIZE_RGB(col) ((float)col/(float)0xFFFF)
+
+ float min = 1.0, temp;
+ float r = NORMALIZE_RGB(inRGB->red),
+ g = NORMALIZE_RGB(inRGB->green),
+ b = NORMALIZE_RGB(inRGB->blue);
+
+ HSV->h = 0;
+
+ HSV->v = r;
+ if (g > HSV->v) HSV->v = g;
+ if (b > HSV->v) HSV->v = b;
+
+ min = r;
+ if (g < min) min = g;
+ if (b < min) min = b;
+
+ temp = HSV->v - min;
+
+ if (HSV->v > 0)
+ HSV->s = temp / HSV->v;
+ else
+ HSV->s = 0;
+
+ if (HSV->s > 0) {
+ float rd = (HSV->v - r) / temp,
+ gd = (HSV->v - g) / temp,
+ bd = (HSV->v - b) / temp;
+
+ if (HSV->v == r) {
+ if (min == g) HSV->h = 5 + bd;
+ else HSV->h = 1 - gd;
+ } else if (HSV->v == g) {
+ if (min == b) HSV->h = 1 + rd;
+ else HSV->h = 3 - bd;
+ } else {
+ if (min == r) HSV->h = 3 + gd;
+ else HSV->h = 5 - rd;
+ }
+
+ if (HSV->h < 6) HSV->h *= 60;
+ else HSV->h = 0;
+ }
+}
+
+void
+mac_convert_hsv_rgb(colorHSV *inHSV, colorRGB *RGB)
+{
+ if (inHSV->s == 0) {
+ RGB->red = RGB->green = RGB->blue = inHSV->v * 0xFFFF;
+ } else {
+ float h = inHSV->h / 60;
+ int i = trunc(h);
+ float fract = h - i;
+ unsigned short t1 = (inHSV->v * (1 - inHSV->s)) * 0xFFFF,
+ t2 = (inHSV->v * (1 - inHSV->s * fract)) * 0xFFFF,
+ t3 = (inHSV->v * (1 - inHSV->s * (1 - fract))) * 0xFFFF,
+ v = inHSV->v * 0xFFFF;
+
+ switch(i) {
+ case 0: RGB->red = v;
+ RGB->green = t3;
+ RGB->blue = t1;
+ break;
+
+ case 1: RGB->red = t2;
+ RGB->green = v;
+ RGB->blue = t1;
+ break;
+
+ case 2: RGB->red = t1;
+ RGB->green = v;
+ RGB->blue = t3;
+ break;
+
+ case 3: RGB->red = t1;
+ RGB->green = t2;
+ RGB->blue = v;
+ break;
+
+ case 4: RGB->red = t3;
+ RGB->green = t1;
+ RGB->blue = v;
+ break;
+
+ case 5: RGB->red = v;
+ RGB->green = t1;
+ RGB->blue = t2;
+ break;
+ }
+ }
+}
+
+// set color info and procedures according to pixeldepth
+static int
+mac_set_colordepth(gx_device *dev, int depth)
+{
+ gx_device_macos * mdev = (gx_device_macos *)dev;
+ gx_device_color_info * ci = &mdev->color_info;
+
+ if (depth != 1 && depth != 4 && depth != 7 && depth != 8 && depth != 24)
+ return gs_error_rangecheck;
+
+ mdev->color_info.depth = depth;
+ switch (depth)
+ {
+ case 1: // Black/White
+ ci->num_components = 1;
+ ci->max_gray = 1; ci->max_color = 0;
+ ci->dither_grays = 2; ci->dither_colors = 0;
+ set_dev_proc(dev, map_rgb_color, gx_default_b_w_map_rgb_color);
+ set_dev_proc(dev, map_color_rgb, gx_default_b_w_map_color_rgb);
+ break;
+
+ case 4: // 4Bit-Gray
+ ci->num_components = 1;
+ ci->max_gray = 15; ci->max_color = 0;
+ ci->dither_grays = 16; ci->dither_colors = 0;
+ set_dev_proc(dev, map_rgb_color, gx_default_gray_map_rgb_color);
+ set_dev_proc(dev, map_color_rgb, gx_default_gray_map_color_rgb);
+ break;
+
+ case 7: // 8Bit-Gray
+ ci->depth = 7;
+ ci->num_components = 1;
+ ci->max_gray = 255; ci->max_color = 0;
+ ci->dither_grays = 256; ci->dither_colors = 0;
+ set_dev_proc(dev, map_rgb_color, gx_default_gray_map_rgb_color);
+ set_dev_proc(dev, map_color_rgb, gx_default_gray_map_color_rgb);
+ break;
+
+ case 8: // 8Bit-Color
+ ci->num_components = 3;
+ ci->max_gray = 15; ci->max_color = 5;
+ ci->dither_grays = 16; ci->dither_colors = 6;
+ set_dev_proc(dev, map_rgb_color, gx_default_rgb_map_rgb_color);
+ set_dev_proc(dev, map_color_rgb, gx_default_rgb_map_color_rgb);
+ break;
+
+/* case 16: // 16Bit-Color
+ ci->num_components = 3;
+ ci->max_gray = 255; ci->max_color = 65535;
+ ci->dither_grays = 256; ci->dither_colors = 65536;
+ set_dev_proc(dev, map_rgb_color, gx_default_rgb_map_rgb_color);
+ set_dev_proc(dev, map_color_rgb, gx_default_rgb_map_color_rgb);
+ break;
+*/
+ case 24: // 24Bit-Color
+ ci->num_components = 3;
+ ci->max_gray = 255; ci->max_color = 16777215;
+ ci->dither_grays = 256; ci->dither_colors = 16777216;
+ set_dev_proc(dev, map_rgb_color, gx_default_rgb_map_rgb_color);
+ set_dev_proc(dev, map_color_rgb, gx_default_rgb_map_color_rgb);
+ break;
+ }
+
+ return 0;
+}
+
+static int
+mac_put_params(gx_device *dev, gs_param_list *plist)
+{
+ gx_device_macos *mdev = (gx_device_macos *)dev;
+
+ int isOpen = mdev->is_open;
+ int code;
+ int depth;
+ gs_param_string outputFile;
+
+ // Get the BitsPerPixel Parameter
+ code = param_read_int(plist, "BitsPerPixel", &depth);
+ if (!code) {
+ code = mac_set_colordepth(dev, depth);
+ if (code)
+ param_return_error(plist, "BitsPerPixel", gs_error_rangecheck);
+ }
+
+ // Get OutputFile
+ code = param_read_string(plist, "OutputFile", &outputFile);
+ if (code < 0) {
+ param_signal_error(plist, "OutputFile", code);
+ return code;
+ } else if (code == 0) {
+
+ if (dev->LockSafetyParams &&
+ bytes_compare(outputFile.data, outputFile.size,
+ (const byte *)mdev->outputFileName, strlen(mdev->outputFileName))) {
+ param_signal_error(plist, "OutputFile", gs_error_invalidaccess);
+ return gs_error_invalidaccess;
+ }
+ if (outputFile.size > (gp_file_name_sizeof - 1)) {
+ param_signal_error(plist, "OutputFile", gs_error_limitcheck);
+ return gs_error_limitcheck;
+ }
+
+ /* If filename changed, close file. */
+ if (outputFile.data != 0 &&
+ bytes_compare(outputFile.data, outputFile.size,
+ (const byte *)mdev->outputFileName, strlen(mdev->outputFileName))) {
+ /* Close the file if it's open. */
+ if (mdev->outputFile != NULL) {
+ gx_device_close_output_file(dev, mdev->outputFileName, mdev->outputFile);
+ memcpy(mdev->outputFileName, outputFile.data, outputFile.size);
+ mdev->outputFileName[outputFile.size] = 0;
+ gx_device_open_output_file(dev, mdev->outputFileName, true, true, &(mdev->outputFile));
+ } else {
+ memcpy(mdev->outputFileName, outputFile.data, outputFile.size);
+ mdev->outputFileName[outputFile.size] = 0;
+ }
+ }
+ }
+
+ // Get the Default Parameters
+ mdev->is_open = 0;
+ code = gx_default_put_params( dev, plist );
+ mdev->is_open = isOpen;
+
+ return code;
+}
+
+static int
+mac_get_params(gx_device *dev, gs_param_list *plist)
+{
+ gx_device_macos *mdev = (gx_device_macos *)dev;
+
+ int code;
+ gs_param_string outputFile;
+
+ code = gx_default_get_params(dev, plist);
+ if (code < 0)
+ return code;
+
+ // color depth
+ code = param_write_int(plist, "BitsPerPixel", &(mdev->color_info.depth));
+
+ // output file name
+ outputFile.data = (const byte *) mdev->outputFileName;
+ outputFile.size = strlen(mdev->outputFileName);
+ outputFile.persistent = false;
+ code = param_write_string(plist, "OutputFile", &outputFile);
+
+ return code;
+}
+
+/* let the caller get the device PictHandle, he has to draw it to screen */
+int GSDLLAPI
+gsdll_get_pict(unsigned char *dev, PicHandle *thePict)
+{
+ gx_device_macos * mdev = (gx_device_macos *)dev;
+
+ *thePict = mdev->pic;
+
+ return 0;
+}
+
+/*************************************************************************************/
+/*************************************************************************************/
+/** Experimental functions! **/
+/*************************************************************************************/
+/*************************************************************************************/
+
+#if 0
+/* NOT FUNCTIONAL !!! */
+/* Copy a color bitmap. */
+static int
+mac_copy_color (register gx_device *dev,
+ const unsigned char *base, int data_x, int raster, gx_bitmap_id id,
+ int x, int y, int w, int h)
+{
+ gx_device_macos * mdev = (gx_device_macos *)dev;
+
+ int byteCount = raster * h, color;
+ gx_color_value rgb[3];
+
+ fit_copy(dev, base, data_x, raster, id, x, y, w, h);
+
+ CheckMem(10*1024 + byteCount*4, 100*1024 + byteCount*4);
+ ResetPage();
+
+ GSSetStdCol(mdev->currPicPos); // Sets FgCol to Black and BkCol to White
+
+ if (mdev->color_info.depth == 24) {
+ PICTWriteOpcode(mdev->currPicPos, 0x009A);
+ PICTWriteLong(mdev->currPicPos, 0x000000FF);
+ PICTWritePixMap(mdev->currPicPos, 0, 0, raster/4, h, raster, 2, 0,
+ X2Fix(mdev->x_pixels_per_inch), X2Fix(mdev->y_pixels_per_inch), 32);
+ PICTWriteRect(mdev->currPicPos, data_x, 0, w, h);
+ PICTWriteRect(mdev->currPicPos, x, y, w, h);
+ PICTWriteInt(mdev->currPicPos, srcCopy);
+
+/* memcpy(mdev->currPicPos, base, byteCount);
+ (char*)(mdev->currPicPos) += byteCount;*/
+
+ {
+ short i;
+ byteCount = 0;
+
+ for (i=0; i<raster/4*h; i++) {
+ // PICTWriteByte(mdev->currPicPos, 0x00);
+ PICTWriteByte(mdev->currPicPos, 0x00);
+ PICTWriteByte(mdev->currPicPos, 0x00);
+ PICTWriteByte(mdev->currPicPos, 0x00);
+ byteCount += 3;
+ }
+ }
+
+ if (byteCount % 2)
+ PICTWriteFillByte(mdev->currPicPos);
+
+ } else if (mdev->color_info.depth <= 8) {
+ ColorSpec *colorTable;
+
+ colorTable = (ColorSpec*) malloc(sizeof(ColorSpec) * (1 << mdev->color_info.depth));
+ for (color=0; color < (1 << mdev->color_info.depth); color++) {
+ (*dev_proc(dev, map_color_rgb))(dev, color, rgb);
+ colorTable[color].value = color;
+ colorTable[color].rgb.red = rgb[0];
+ colorTable[color].rgb.green = rgb[1];
+ colorTable[color].rgb.blue = rgb[2];
+ }
+
+ PICTWriteOpcode(mdev->currPicPos, 0x0098);
+ PICTWritePixMap(mdev->currPicPos, 0, 0, raster*8/mdev->color_info.depth, h, raster, 1, 0,
+ X2Fix(mdev->x_pixels_per_inch), X2Fix(mdev->y_pixels_per_inch),
+ mdev->color_info.depth);
+ PICTWriteColorTable(mdev->currPicPos, 0, (1 << mdev->color_info.depth), colorTable);
+ PICTWriteRect(mdev->currPicPos, data_x, 0, w, h);
+ PICTWriteRect(mdev->currPicPos, x, y, w, h);
+ PICTWriteInt(mdev->currPicPos, srcCopy);
+
+ PICTWriteDataPackBits(mdev->currPicPos, base, raster, h);
+
+ free(colorTable);
+ } else {
+ gx_default_copy_color( dev, base, data_x, raster, id, x, y, w, h );
+ }
+
+ PICT_OpEndPicGoOn(mdev->currPicPos);
+
+ return 0;
+}
+#endif
+
+#if 0
+/* tile a rectangle with a bitmap or pixmap */
+static int
+mac_strip_tile_rectangle(register gx_device *dev, const gx_strip_bitmap *tile,
+ int x, int y, int w, int h,
+ gx_color_index color_0, gx_color_index color_1,
+ int phase_x, int phase_y)
+{
+ gx_device_macos * mdev = (gx_device_macos *)dev;
+
+ int byteCount = tile->raster * tile->size.y;
+/*
+ // tile is a pixmap
+ if (color_0 == gx_no_color_index && color_1 == gx_no_color_index)
+ return 0;// gx_default_strip_tile_rectangle(dev, tile, x, y, w, h, color_0, color_1, phase_x, phase_y);
+
+ if (color_0 != gx_no_color_index && color_1 != gx_no_color_index) {
+ // monochrome tiles
+ if (phase_x != 0 ||Êphase_y != 0 || tile->shift != 0 ||
+ tile->strip_height != 0 ||Êtile->strip_shift != 0) {
+ return gx_default_strip_tile_rectangle(dev, tile, x, y, w, h, color_0, color_1, phase_x, phase_y);
+ }
+
+ } else {
+ return 0;//gx_default_strip_tile_rectangle(dev, tile, x, y, w, h, color_0, color_1, phase_x, phase_y);
+ }
+*/
+}
+#endif
diff --git a/devices/gdevmac.h b/devices/gdevmac.h
new file mode 100644
index 000000000..c39a3c3bf
--- /dev/null
+++ b/devices/gdevmac.h
@@ -0,0 +1,164 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+#ifndef gdevmac_INCLUDED
+# define gdevmac_INCLUDED
+
+#include <stdlib.h>
+#include "math_.h"
+#include "string_.h"
+
+#include "gdevmacpictop.h"
+
+#include <Fonts.h>
+#include <FixMath.h>
+#include <Resources.h>
+
+#include "gx.h"
+#include "gxdevice.h"
+#include "gp.h"
+#include "gp_mac.h"
+#include "gsdll.h"
+
+#include "gsutil.h"
+#include "gxxfont.h"
+#include "gsstruct.h"
+#include "gserrors.h"
+
+/* Default device settings */
+
+#define DEFAULT_PAGE_SIZE_H 8.5
+#define DEFAULT_PAGE_SIZE_V 11.0
+#define DEFAULT_DEV_DPI 72.0
+#define DEFAULT_PAGE_DPI 72.0
+
+#define DEFAULT_DEV_WIDTH (DEFAULT_PAGE_SIZE_H * DEFAULT_DEV_DPI)
+#define DEFAULT_DEV_HEIGHT (DEFAULT_PAGE_SIZE_V * DEFAULT_DEV_DPI)
+#define DEFAULT_PAGE_WIDTH (DEFAULT_PAGE_SIZE_H * DEFAULT_PAGE_DPI)
+#define DEFAULT_PAGE_HEIGHT (DEFAULT_PAGE_SIZE_V * DEFAULT_PAGE_DPI)
+
+/* Define the Macintosh device */
+
+typedef struct gx_device_macos_s
+{
+ gx_device_common;
+ char outputFileName[gp_file_name_sizeof];
+ FILE *outputFile;
+ PicHandle pic;
+ short *currPicPos;
+ bool outputPage;
+ FMFontStyle lastFontFace;
+ FMFontSize lastFontSize;
+ FMFontFamily lastFontID;
+ int numUsedFonts;
+ FMFontFamily usedFontIDs[256];
+} gx_device_macos;
+
+#define DEV_MAC_NAME "macos"
+
+/* Device Procedures */
+
+dev_proc_open_device(mac_open);
+dev_proc_get_initial_matrix(mac_get_initial_matrix);
+dev_proc_sync_output(mac_sync_output);
+dev_proc_output_page(mac_output_page);
+dev_proc_get_params(mac_get_params);
+dev_proc_put_params(mac_put_params);
+dev_proc_close_device(mac_close);
+dev_proc_fill_rectangle(mac_fill_rectangle);
+dev_proc_strip_tile_rectangle(mac_strip_tile_rectangle);
+dev_proc_copy_mono(mac_copy_mono);
+dev_proc_copy_color(mac_copy_color);
+dev_proc_draw_line(mac_draw_line);
+dev_proc_copy_alpha(mac_copy_alpha);
+
+/* Define a MacOS xfont. */
+
+typedef struct mac_xfont_s mac_xfont;
+struct mac_xfont_s {
+ gx_xfont_common common;
+ gx_device *dev;
+ Str255 fontName;
+ FMFontFamily fontID;
+ FMFontStyle fontFace;
+ FMFontSize fontSize;
+ int fontEncoding;
+ FMetricRec fontMetrics;
+};
+
+/* Memory handling macros */
+
+#define CheckMem(a,b) \
+ { \
+ long offset = (long) mdev->currPicPos - (long) *mdev->pic; \
+ long len = GetHandleSize((Handle) mdev->pic); \
+ if (len - offset < a) { \
+ HUnlock((Handle) mdev->pic); \
+ SetHandleSize((Handle) mdev->pic, len + b); \
+ if (MemError() != noErr) return gs_error_VMerror; \
+ HLockHi((Handle) mdev->pic); \
+ mdev->currPicPos = (short*) ((long) *mdev->pic + offset); \
+ } \
+ }
+
+#define ResetPage() \
+ { \
+ if (mdev->outputPage) { \
+ mdev->outputPage = false; \
+ mdev->currPicPos = (short*) *mdev->pic; \
+ mdev->currPicPos += 42; /* header len */ \
+ mdev->lastFontID = mdev->lastFontSize = mdev->lastFontFace = -1; \
+ mdev->numUsedFonts = 0; \
+ } \
+ }
+
+/* Other datatypes */
+
+typedef struct {
+ unsigned short red;
+ unsigned short green;
+ unsigned short blue;
+} colorRGB;
+
+typedef struct {
+ float h;
+ float s;
+ float v;
+} colorHSV;
+
+/* Helper function definitions */
+
+static int mac_save_pict(gx_device * dev);
+static void mac_convert_rgb_hsv(colorRGB *inRGB, colorHSV *HSV);
+static void mac_convert_hsv_rgb(colorHSV *inHSV, colorRGB *RGB);
+
+static void mac_find_font_family(ConstStringPtr fname, int len,
+ FMFontFamily *fontFamilyID, FMFontStyle *fontFace);
+static int mac_get_font_encoding(mac_xfont *macxf);
+static void mac_get_font_resource(mac_xfont *macxf, ResType *resType, short *resID);
+
+static int mac_set_colordepth(gx_device *dev, int depth);
+
+/* additional DLL function definition */
+
+#pragma export on
+
+int GSDLLAPI gsdll_get_pict(unsigned char *, PicHandle *);
+typedef int (GSDLLAPI * PFN_gsdll_get_pict) (unsigned char *, PicHandle *);
+
+#pragma export off
+
+#endif /* gdevmac_INCLUDED */
diff --git a/devices/gdevmacpictop.h b/devices/gdevmacpictop.h
new file mode 100644
index 000000000..bfe61b7a2
--- /dev/null
+++ b/devices/gdevmacpictop.h
@@ -0,0 +1,674 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Helpers for working with Classic MacOS Quickdraw pictures */
+/* (obsoleted by the DISPLAY device) */
+
+#ifndef gdevmacpictop_INCLUDED
+# define gdevmacpictop_INCLUDED
+
+#include <QDOffscreen.h>
+
+/************************/
+/* PICT data structures */
+/************************/
+
+/* write raw data to PICT file */
+#define PICTWriteByte(ptr, data) *((unsigned char*) (ptr))++ = data;
+#define PICTWriteInt(ptr, data) *((short*) (ptr))++ = data;
+#define PICTWriteLong(ptr, data) *((long*) (ptr))++ = data;
+
+#define PICTWriteFillByte(ptr) PICTWriteByte(ptr, 0);
+
+/* write a PICT opcode */
+#define PICTWriteOpcode(ptr, op) PICTWriteInt(ptr, op);
+
+/*****************************/
+/* QuickDraw data structures */
+/*****************************/
+
+/* write a Point structure */
+#define PICTWritePoint(ptr, h, v) \
+ { \
+ PICTWriteInt(ptr, v); /* vertical coordinate */ \
+ PICTWriteInt(ptr, h); /* horizontal coordinate */ \
+ }
+
+/* write a Rect structure */
+#define PICTWriteRect(ptr, x, y, w, h) \
+ { \
+ PICTWritePoint(ptr, x, y); /* upper-left corner */ \
+ PICTWritePoint(ptr, x+w, y+h); /* lower-right corner */ \
+ }
+
+/* write a rectangular Region structure */
+#define PICTWriteRegionRectangular(ptr, x, y, w, h) \
+ { \
+ PICTWriteInt(ptr, 10); /* rgnSize */ \
+ PICTWriteRect(ptr, x, y, w, h); /* rgnBBox */ \
+ }
+
+/* write a non-rectangular Region structure */
+#define PICTWriteRegion(ptr, x, y, b, h, size, dataptr) \
+ { \
+ PICTWriteInt(ptr, 10+size); /* rgnSize */ \
+ PICTWriteRect(ptr, x, y, w, h); /* rgnBBox */ \
+ memcpy(ptr, dataptr, size); /* additional data */ \
+ ((char*)(ptr)) += size; \
+ }
+
+/* write a pattern */
+#define PICTWritePattern(ptr, byte1, byte2, byte3, byte4, byte5, byte6, byte7, byte8) \
+ { \
+ PICTWriteByte(ptr, byte1); /* pattern */ \
+ PICTWriteByte(ptr, byte2); /* pattern */ \
+ PICTWriteByte(ptr, byte3); /* pattern */ \
+ PICTWriteByte(ptr, byte4); /* pattern */ \
+ PICTWriteByte(ptr, byte5); /* pattern */ \
+ PICTWriteByte(ptr, byte6); /* pattern */ \
+ PICTWriteByte(ptr, byte7); /* pattern */ \
+ PICTWriteByte(ptr, byte8); /* pattern */ \
+ }
+
+/* write a RGBColor structure */
+#define PICTWriteRGBColor(ptr, r, g, b) \
+ { \
+ PICTWriteInt(ptr, r); /* red */ \
+ PICTWriteInt(ptr, g); /* green */ \
+ PICTWriteInt(ptr, b); /* blue */ \
+ }
+
+/* write a ColorSpec structure */
+#define PICTWriteColorSpec(ptr, value, r, g, b) \
+ { \
+ PICTWriteInt(ptr, value); /* value */ \
+ PICTWriteRGBColor(ptr, r, g, b); /* color */ \
+ }
+
+/* write a ColorTable structure */
+#define PICTWriteColorTable(ptr, seed, numEntries, cspecarr) \
+ { \
+ int i; \
+ PICTWriteLong(ptr, seed); /* ctSeed */ \
+ PICTWriteInt(ptr, 0); /* ctFlags */ \
+ PICTWriteInt(ptr, numEntries-1); /* ctSize */ \
+ for (i=0; i<numEntries; i++) /* ctTable */ \
+ PICTWriteColorSpec(ptr, cspecarr[i].value, \
+ cspecarr[i].rgb.red, \
+ cspecarr[i].rgb.green, \
+ cspecarr[i].rgb.blue); \
+ }
+
+/* write a PixMap structure */
+#define PICTWritePixMap(ptr, x, y, w, h, rowBytes, \
+ packType, packSize, \
+ hRes, vRes, pixelSize) \
+ { \
+ PICTWriteInt(ptr, 0x8000+rowBytes); /* rowBytes */ \
+ PICTWriteRect(ptr, x, y, w, h); /* bounds */ \
+ PICTWriteInt(ptr, 0); /* pmVersion */ \
+ PICTWriteInt(ptr, packType); /* packType */ \
+ PICTWriteLong(ptr, (packType ? packSize : 0)); /* packSize */ \
+ PICTWriteLong(ptr, hRes); /* hRes */ \
+ PICTWriteLong(ptr, vRes); /* vRes */ \
+ if (pixelSize < 16) { /* indexed */ \
+ PICTWriteInt(ptr, 0); /* pixelType */ \
+ PICTWriteInt(ptr, pixelSize); /* pixelSize */ \
+ PICTWriteInt(ptr, 1); /* cmpCount */ \
+ PICTWriteInt(ptr, pixelSize); /* cmpSize */ \
+ } else { /* direct */ \
+ PICTWriteInt(ptr, RGBDirect); /* pixelType */ \
+ PICTWriteInt(ptr, pixelSize); /* pixelSize */ \
+ PICTWriteInt(ptr, 3); /* cmpCount */ \
+ PICTWriteInt(ptr, (pixelSize==16 ? 5 : 8)); /* cmpSize */ \
+ } \
+ PICTWriteLong(ptr, 0); /* planeBytes */ \
+ PICTWriteLong(ptr, 0); /* pmTable */ \
+ PICTWriteLong(ptr, 0); /* pmReserved */ \
+ }
+
+/* write PackBits data */
+#define PICTWriteDataPackBits(ptr, base, rowBytes, lines) \
+ { \
+ short byteCount; \
+ if (raster < 8) { /* data uncompressed */ \
+ byteCount = rowBytes * lines; \
+ memcpy(ptr, base, byteCount); /* bitmap data */ \
+ (char*)(ptr) += byteCount; \
+ } else { /* raster >= 8 use PackBits compression */ \
+ Ptr destBufBegin = (Ptr) malloc(raster + (raster+126)/127), destBuf, \
+ srcBuf = (Ptr) base; \
+ short i, len; \
+ \
+ byteCount = 0; \
+ for (i=0; i<lines; i++) { \
+ destBuf = destBufBegin; \
+ PackBits(&srcBuf, &destBuf, rowBytes); \
+ len = destBuf - destBufBegin; \
+ if (rowBytes > 250) { \
+ PICTWriteInt(ptr, len); \
+ byteCount += 2; \
+ } else { \
+ PICTWriteByte(ptr, len); \
+ byteCount++; \
+ } \
+ \
+ memcpy(ptr, destBufBegin, len); \
+ (char*)(ptr) += len; \
+ byteCount += len; \
+ } \
+ free(destBufBegin); \
+ } \
+ \
+ if (byteCount % 2) \
+ PICTWriteFillByte(ptr); \
+ }
+
+/* write text */
+#define PICTWriteText(ptr, textptr /* pascal string*/) \
+ { \
+ memcpy(ptr, textptr, textptr[0]+1); /* copy string */ \
+ (char*)(ptr) += textptr[0]+1; \
+ }
+
+/****************/
+/* PICT Opcodes */
+/****************/
+
+#define PICT_NOP(ptr) \
+ { \
+ PICTWriteOpcode(ptr, 0x0000); /* NOP opcode */ \
+ }
+
+#define PICT_Clip_Rectangular(ptr, x, y, w, h) \
+ { \
+ PICTWriteOpcode(ptr, 0x0001); /* Clip opcode */ \
+ PICTWriteRegionRectangular(ptr, x, y, w, h); /* clipRgn */ \
+ }
+
+#define PICT_Clip(ptr, x, y, w, h, size, dataptr) \
+ { \
+ PICTWriteOpcode(ptr, 0x0001); /* Clip opcode */ \
+ PICTWriteRegion(ptr, x, y, w, h, size, dataptr); /* clipRgn */ \
+ }
+
+#define PICT_BkPat(ptr, byte1, byte2, byte3, byte4, byte5, byte6, byte7, byte8) \
+ { \
+ PICTWriteOpcode(ptr, 0x0002); /* BkPat opcode */ \
+ PICTWritePattern(ptr, byte1, byte2, byte3, byte4, /* Pattern data */ \
+ byte5, byte6, byte7, byte8); \
+ }
+
+#define PICT_TxFont(ptr, font) \
+ { \
+ PICTWriteOpcode(ptr, 0x0003); /* TxFont opcode */ \
+ PICTWriteInt(ptr, font); /* Font number */ \
+ }
+
+#define PICT_TxFace(ptr, style) \
+ { \
+ PICTWriteOpcode(ptr, 0x0004); /* TxFace opcode */ \
+ PICTWriteByte(ptr, style); /* Font style */ \
+ PICTWriteFillByte(ptr); /* Fill byte */ \
+ }
+
+#define PICT_TxMode(ptr, mode) \
+ { \
+ PICTWriteOpcode(ptr, 0x0005); /* TxMode opcode */ \
+ PICTWriteInt(ptr, mode); /* Source mode */ \
+ }
+
+#define PICT_PnSize(ptr, w, h) \
+ { \
+ PICTWriteOpcode(ptr, 0x0006); /* PnSize opcode */ \
+ PICTWritePoint(w, h); /* Pen size */ \
+ }
+
+#define PICT_PnMode(ptr, mode) \
+ { \
+ PICTWriteOpcode(ptr, 0x0007); /* PnMode opcode */ \
+ PICTWriteInt(ptr, mode); /* Pen mode */ \
+ }
+
+#define PICT_PnPat(ptr, byte1, byte2, byte3, byte4, byte5, byte6, byte7, byte8) \
+ { \
+ PICTWriteOpcode(ptr, 0x0009); /* PnPat opcode */ \
+ PICTWritePattern(ptr, byte1, byte2, byte3, byte4, /* Pattern data */ \
+ byte5, byte6, byte7, byte8); \
+ }
+
+#define PICT_FillPat(ptr, byte1, byte2, byte3, byte4, byte5, byte6, byte7, byte8) \
+ { \
+ PICTWriteOpcode(ptr, 0x000A); /* FillPat opcode */ \
+ PICTWritePattern(ptr, byte1, byte2, byte3, byte4, /* Pattern data */ \
+ byte5, byte6, byte7, byte8); \
+ }
+
+#define PICT_OvSize(ptr, w, h) \
+ { \
+ PICTWriteOpcode(ptr, 0x000B); /* OvSize opcode */ \
+ PICTWritePoint(w, h); /* Oval size */ \
+ }
+
+#define PICT_Origin(ptr, dh, dv) \
+ { \
+ PICTWriteOpcode(ptr, 0x000C); /* Origin opcode */ \
+ PICTWriteInt(ptr, dh); /* dh */ \
+ PICTWriteInt(ptr, dv); /* dv */ \
+ }
+
+#define PICT_TxSize(ptr, size) \
+ { \
+ PICTWriteOpcode(ptr, 0x000D); /* TxSize opcode */ \
+ PICTWriteInt(ptr, size); /* Text Size */ \
+ }
+
+#define PICT_FgColor(ptr, color) \
+ { \
+ PICTWriteOpcode(ptr, 0x000E); /* FgColor opcode */ \
+ PICTWriteLong(ptr, color); /* Foreground color */ \
+ }
+
+#define PICT_BkColor(ptr, color) \
+ { \
+ PICTWriteOpcode(ptr, 0x000F); /* BkColor opcode */ \
+ PICTWriteLong(ptr, color); /* Background color */ \
+ }
+
+#define PICT_TxRatio(ptr, num, denom) \
+ { \
+ PICTWriteOpcode(ptr, 0x0010); /* TxRatio opcode */ \
+ PICTWritePoint(ptr, num); /* Numerator (Point) */ \
+ PICTWritePoint(ptr, denom); /* Denominator (Point) */ \
+ }
+
+#define PICT_VersionOp(ptr, version) \
+ { \
+ PICTWriteOpcode(ptr, 0x0011); /* VersionOp opcode */ \
+ PICTWriteByte(ptr, version); /* Version */ \
+ PICTWriteFillByte(ptr); /* Fill byte */ \
+ }
+
+#define PICT_RGBFgCol(ptr, r, g, b) \
+ { \
+ PICTWriteOpcode(ptr, 0x001A); /* RGBFgCol opcode */ \
+ PICTWriteRGBColor(ptr, r, g, b); /* Foreground color */ \
+ }
+
+#define PICT_RGBBkCol(ptr, r, g, b) \
+ { \
+ PICTWriteOpcode(ptr, 0x001B); /* RGBBkCol opcode */ \
+ PICTWriteRGBColor(ptr, r, g, b); /* Background color */ \
+ }
+
+#define PICT_HiliteMode(ptr) \
+ { \
+ PICTWriteOpcode(ptr, 0x001C); /* HiliteMode opcode */ \
+ }
+
+#define PICT_HiliteColor(ptr, r, g, b) \
+ { \
+ PICTWriteOpcode(ptr, 0x001D); /* HiliteColor opcode */ \
+ PICTWriteRGBColor(ptr, r, g, b); /* Highlight color */ \
+ }
+
+#define PICT_DefHilite(ptr) \
+ { \
+ PICTWriteOpcode(ptr, 0x001E); /* DefHilite opcode */ \
+ }
+
+#define PICT_OpColor(ptr, r, g, b) \
+ { \
+ PICTWriteOpcode(ptr, 0x001F); /* OpColor opcode */ \
+ PICTWriteRGBColor(ptr, r, g, b); /* Opcolor */ \
+ }
+
+#define PICT_Line(ptr, x0, y0, x1, y1) \
+ { \
+ PICTWriteOpcode(ptr, 0x0020); /* Line opcode */ \
+ PICTWritePoint(ptr, x0, y0); /* pnLoc */ \
+ PICTWritePoint(ptr, x1, y1); /* newPt */ \
+ }
+
+#define PICT_LineFrom(ptr, x, y) \
+ { \
+ PICTWriteOpcode(ptr, 0x0021); /* LineFrom opcode */ \
+ PICTWritePoint(ptr, x, y); /* newPt */ \
+ }
+
+#define PICT_ShortLine(ptr, x, y, dh, dv) \
+ { \
+ PICTWriteOpcode(ptr, 0x0022); /* ShortLine opcode */ \
+ PICTWritePoint(ptr, x, y); /* pnLoc */ \
+ PICTWriteByte(ptr, dh); /* dh */ \
+ PICTWriteByte(ptr, dv); /* dv */ \
+ }
+
+#define PICT_ShortLineFrom(ptr, dh, dv) \
+ { \
+ PICTWriteOpcode(ptr, 0x0023); /* ShortLineFrom opcode */ \
+ PICTWriteByte(ptr, dh); /* dh */ \
+ PICTWriteByte(ptr, dv); /* dv */ \
+ }
+
+#define PICT_LongText(ptr, x, y, textptr /* pascal string */) \
+ { \
+ PICTWriteOpcode(ptr, 0x0028); /* LongText opcode */ \
+ PICTWritePoint(ptr, x, y); /* Point */ \
+ PICTWriteText(ptr, textptr); /* text */ \
+ if ((textptr[0]+1) % 2) PICTWriteFillByte(ptr); \
+ }
+
+#define PICT_DHText(ptr, dh, textptr /* pascal string */) \
+ { \
+ PICTWriteOpcode(ptr, 0x0029); /* DHText opcode */ \
+ PICTWriteByte(ptr, dh); /* dh */ \
+ PICTWriteText(ptr, textptr); /* text */ \
+ if (textptr[0] % 2) PICTWriteFillByte(ptr); \
+ }
+
+#define PICT_DVText(ptr, dv, textptr /* pascal string */) \
+ { \
+ PICTWriteOpcode(ptr, 0x002A); /* DVText opcode */ \
+ PICTWriteByte(ptr, dv); /* dv */ \
+ PICTWriteText(ptr, textptr); /* text */ \
+ if (textptr[0] % 2) PICTWriteFillByte(ptr); \
+ }
+
+#define PICT_DHDVText(ptr, dh, dv, textptr /* pascal string */) \
+ { \
+ PICTWriteOpcode(ptr, 0x002B); /* DHDVText opcode */ \
+ PICTWriteByte(ptr, dh); /* dh */ \
+ PICTWriteByte(ptr, dv); /* dv */ \
+ PICTWriteText(ptr, textptr); /* text */ \
+ if ((textptr[0]+1) % 2) PICTWriteFillByte(ptr); \
+ }
+
+#define PICT_fontName(ptr, id, nameptr /* pascal string */) \
+ { \
+ PICTWriteOpcode(ptr, 0x002C); /* fontName opcode */ \
+ PICTWriteInt(ptr, nameptr[0]+1+2); /* data length */ \
+ PICTWriteInt(ptr, id); /* font id */ \
+ PICTWriteText(ptr, nameptr); /* text */ \
+ if ((nameptr[0]+1) % 2) PICTWriteFillByte(ptr); \
+ }
+
+#define PICT_frameRect(ptr, x, y, w, h) \
+ { \
+ PICTWriteOpcode(ptr, 0x0030); /* frameRect opcode */ \
+ PICTWriteRect(ptr, x, y, w, h); /* Rectangle */ \
+ }
+
+#define PICT_paintRect(ptr, x, y, w, h) \
+ { \
+ PICTWriteOpcode(ptr, 0x0031); /* paintRect opcode */ \
+ PICTWriteRect(ptr, x, y, w, h); /* Rectangle */ \
+ }
+
+#define PICT_eraseRect(ptr, x, y, w, h) \
+ { \
+ PICTWriteOpcode(ptr, 0x0032); /* eraseRect opcode */ \
+ PICTWriteRect(ptr, x, y, w, h); /* Rectangle */ \
+ }
+
+#define PICT_invertRect(ptr, x, y, w, h) \
+ { \
+ PICTWriteOpcode(ptr, 0x0033); /* invertRect opcode */ \
+ PICTWriteRect(ptr, x, y, w, h); /* Rectangle */ \
+ }
+
+#define PICT_fillRect(ptr, x, y, w, h) \
+ { \
+ PICTWriteOpcode(ptr, 0x0034); /* fillRect opcode */ \
+ PICTWriteRect(ptr, x, y, w, h); /* Rectangle */ \
+ }
+
+#define PICT_frameSameRect(ptr) \
+ { \
+ PICTWriteOpcode(ptr, 0x0038); /* frameSameRect opcode */ \
+ }
+
+#define PICT_paintSameRect(ptr) \
+ { \
+ PICTWriteOpcode(ptr, 0x0039); /* paintSameRect opcode */ \
+ }
+
+#define PICT_eraseSameRect(ptr) \
+ { \
+ PICTWriteOpcode(ptr, 0x003A); /* eraseSameRect opcode */ \
+ }
+
+#define PICT_invertSameRect(ptr) \
+ { \
+ PICTWriteOpcode(ptr, 0x003B); /* invertSameRect opcode */ \
+ }
+
+#define PICT_fillSameRect(ptr) \
+ { \
+ PICTWriteOpcode(ptr, 0x003C); /* fillSameRect opcode */ \
+ }
+
+#define PICT_frameRRect(ptr, x, y, w, h) \
+ { \
+ PICTWriteOpcode(ptr, 0x0040); /* frameRRect opcode */ \
+ PICTWriteRect(ptr, x, y, w, h); /* Rectangle */ \
+ }
+
+#define PICT_paintRRect(ptr, x, y, w, h) \
+ { \
+ PICTWriteOpcode(ptr, 0x0041); /* paintRRect opcode */ \
+ PICTWriteRect(ptr, x, y, w, h); /* Rectangle */ \
+ }
+
+#define PICT_eraseRRect(ptr, x, y, w, h) \
+ { \
+ PICTWriteOpcode(ptr, 0x0042); /* eraseRRect opcode */ \
+ PICTWriteRect(ptr, x, y, w, h); /* Rectangle */ \
+ }
+
+#define PICT_invertRRect(ptr, x, y, w, h) \
+ { \
+ PICTWriteOpcode(ptr, 0x0043); /* invertRRect opcode */ \
+ PICTWriteRect(ptr, x, y, w, h); /* Rectangle */ \
+ }
+
+#define PICT_fillRRect(ptr, x, y, w, h) \
+ { \
+ PICTWriteOpcode(ptr, 0x0044); /* fillRRect opcode */ \
+ PICTWriteRect(ptr, x, y, w, h); /* Rectangle */ \
+ }
+
+#define PICT_frameSameRRect(ptr) \
+ { \
+ PICTWriteOpcode(ptr, 0x0048); /* frameSameRRect opcode */ \
+ }
+
+#define PICT_paintSameRRect(ptr) \
+ { \
+ PICTWriteOpcode(ptr, 0x0049); /* paintSameRRect opcode */ \
+ }
+
+#define PICT_eraseSameRRect(ptr) \
+ { \
+ PICTWriteOpcode(ptr, 0x004A); /* eraseSameRRect opcode */ \
+ }
+
+#define PICT_invertSameRRect(ptr) \
+ { \
+ PICTWriteOpcode(ptr, 0x004B); /* invertSameRRect opcode */\
+ }
+
+#define PICT_fillSameRRect(ptr) \
+ { \
+ PICTWriteOpcode(ptr, 0x004C); /* fillSameRRect opcode */ \
+ }
+
+#define PICT_frameOval(ptr, x, y, w, h) \
+ { \
+ PICTWriteOpcode(ptr, 0x0050); /* frameOval opcode */ \
+ PICTWriteRect(ptr, x, y, w, h); /* Rectangle */ \
+ }
+
+#define PICT_paintOval(ptr, x, y, w, h) \
+ { \
+ PICTWriteOpcode(ptr, 0x0051); /* paintOval opcode */ \
+ PICTWriteRect(ptr, x, y, w, h); /* Rectangle */ \
+ }
+
+#define PICT_eraseOval(ptr, x, y, w, h) \
+ { \
+ PICTWriteOpcode(ptr, 0x0052); /* eraseOval opcode */ \
+ PICTWriteRect(ptr, x, y, w, h); /* Rectangle */ \
+ }
+
+#define PICT_invertOval(ptr, x, y, w, h) \
+ { \
+ PICTWriteOpcode(ptr, 0x0053); /* invertOval opcode */ \
+ PICTWriteRect(ptr, x, y, w, h); /* Rectangle */ \
+ }
+
+#define PICT_fillOval(ptr, x, y, w, h) \
+ { \
+ PICTWriteOpcode(ptr, 0x0054); /* fillOval opcode */ \
+ PICTWriteRect(ptr, x, y, w, h); /* Rectangle */ \
+ }
+
+#define PICT_frameSameOval(ptr) \
+ { \
+ PICTWriteOpcode(ptr, 0x0058); /* frameSameOval opcode */ \
+ }
+
+#define PICT_paintSameOval(ptr) \
+ { \
+ PICTWriteOpcode(ptr, 0x0059); /* paintSameOval opcode */ \
+ }
+
+#define PICT_eraseSameOval(ptr) \
+ { \
+ PICTWriteOpcode(ptr, 0x005A); /* eraseSameOval opcode */ \
+ }
+
+#define PICT_invertSameOval(ptr) \
+ { \
+ PICTWriteOpcode(ptr, 0x005B); /* invertSameOval opcode */ \
+ }
+
+#define PICT_fillSameOval(ptr) \
+ { \
+ PICTWriteOpcode(ptr, 0x005C); /* fillSameOval opcode */ \
+ }
+
+#define PICT_frameArc(ptr, x, y, w, h, startAngle, arcAngle) \
+ { \
+ PICTWriteOpcode(ptr, 0x0060); /* frameArc opcode */ \
+ PICTWriteRect(ptr, x, y, w, h); /* Rectangle */ \
+ PICTWriteInt(ptr, startAngle); /* startAngle */ \
+ PICTWriteInt(ptr, arcAngle); /* arcAngle */ \
+ }
+
+#define PICT_paintArc(ptr, x, y, w, h, startAngle, arcAngle) \
+ { \
+ PICTWriteOpcode(ptr, 0x0061); /* paintArc opcode */ \
+ PICTWriteRect(ptr, x, y, w, h); /* Rectangle */ \
+ PICTWriteInt(ptr, startAngle); /* startAngle */ \
+ PICTWriteInt(ptr, arcAngle); /* arcAngle */ \
+ }
+
+#define PICT_eraseArc(ptr, x, y, w, h, startAngle, arcAngle) \
+ { \
+ PICTWriteOpcode(ptr, 0x0062); /* eraseArc opcode */ \
+ PICTWriteRect(ptr, x, y, w, h); /* Rectangle */ \
+ PICTWriteInt(ptr, startAngle); /* startAngle */ \
+ PICTWriteInt(ptr, arcAngle); /* arcAngle */ \
+ }
+
+#define PICT_invertArc(ptr, x, y, w, h, startAngle, arcAngle) \
+ { \
+ PICTWriteOpcode(ptr, 0x0063); /* invertArc opcode */ \
+ PICTWriteRect(ptr, x, y, w, h); /* Rectangle */ \
+ PICTWriteInt(ptr, startAngle); /* startAngle */ \
+ PICTWriteInt(ptr, arcAngle); /* arcAngle */ \
+ }
+
+#define PICT_fillArc(ptr, x, y, w, h, startAngle, arcAngle) \
+ { \
+ PICTWriteOpcode(ptr, 0x0064); /* fillArc opcode */ \
+ PICTWriteRect(ptr, x, y, w, h); /* Rectangle */ \
+ PICTWriteInt(ptr, startAngle); /* startAngle */ \
+ PICTWriteInt(ptr, arcAngle); /* arcAngle */ \
+ }
+
+/* use only with rowBytes < 8 !! */
+#define PICT_BitsRect_BitMap(ptr, x0, y0, w0, h0, x1, y1, w1, h1, rowBytes, mode, dataPtr) \
+ { \
+ PICTWriteOpcode(ptr, 0x0090); /* BitsRect opcode */ \
+ PICTWriteInt(ptr, rowBytes); /* rowBytes */ \
+ PICTWriteRect(ptr, x1, y1, w1, h1); /* bounds x1???? */ \
+ PICTWriteRect(ptr, x0, y0, w0, h0); /* srcRect */ \
+ PICTWriteRect(ptr, x1, y1, w1, h1); /* dstRect */ \
+ PICTWriteInt(ptr, mode); /* mode */ \
+ memcpy(ptr, dataPtr, h0*rowBytes); /* BitMap data */ \
+ }
+
+#define PICT_PackBitsRect_BitMap(ptr, x0, y0, w0, h0, x1, y1, w1, h1, rowBytes, mode, \
+ dataPtr, size) \
+ { \
+ PICTWriteOpcode(ptr, 0x0098); /* PackBitsRect opcode */ \
+ PICTWriteInt(ptr, rowBytes); /* rowBytes */ \
+ PICTWriteRect(ptr, x1, y1, w1, h1); /* bounds x1???? */ \
+ PICTWriteRect(ptr, x0, y0, w0, h0); /* srcRect */ \
+ PICTWriteRect(ptr, x1, y1, w1, h1); /* dstRect */ \
+ PICTWriteInt(ptr, mode); /* mode */ \
+ memcpy(ptr, dataPtr, size); /* BitMap data */ \
+ }
+
+#define PICT_OpEndPic(ptr) \
+ { \
+ PICTWriteOpcode(ptr, 0x00FF); /* OpEndPic opcode */ \
+ }
+
+/* same as PICT_OpEndPic, but doesn't move pointer */
+#define PICT_OpEndPicGoOn(ptr) \
+ { \
+ *(ptr) = 0x00FF; /* OpEndPic opcode */ \
+ }
+
+/******************************/
+/* ghostscript to PICT macros */
+/******************************/
+
+/* set forground color to black and background color to white */
+#define GSSetStdCol(ptr) \
+ { \
+ PICT_RGBFgCol(ptr, 0x0000, 0x0000, 0x0000); /* black */ \
+ PICT_RGBBkCol(ptr, 0xFFFF, 0xFFFF, 0xFFFF); /* white */ \
+ }
+
+#define GSSetFgCol(dev, ptr, col) \
+ { \
+ gx_color_value rgb[3]; \
+ (*dev_proc(dev, map_color_rgb))(dev, col, rgb); \
+ PICT_RGBFgCol(ptr, rgb[0], rgb[1], rgb[2]); \
+ }
+
+#define GSSetBkCol(dev, ptr, col) \
+ { \
+ gx_color_value rgb[3]; \
+ (*dev_proc(dev, map_color_rgb))(dev, col, rgb); \
+ PICT_RGBBkCol(ptr, rgb[0], rgb[1], rgb[2]); \
+ }
+
+#endif /* gdevmacpictop_INCLUDED */
diff --git a/devices/gdevmacttf.h b/devices/gdevmacttf.h
new file mode 100644
index 000000000..703a5a615
--- /dev/null
+++ b/devices/gdevmacttf.h
@@ -0,0 +1,58 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+/* Datatypes for simpler access to data structures in TrueType fonts */
+
+#ifndef gdevmacttf_INCLUDED
+# define gdevmacttf_INCLUDED
+
+/* Font Directory Component */
+
+typedef struct {
+ UInt32 tagName;
+ UInt32 checkSum;
+ UInt32 offset;
+ UInt32 length;
+} TTFontDirComponent;
+
+/* Font Directory */
+
+typedef struct {
+ UInt32 version;
+ UInt16 numTables;
+ UInt16 searchRange;
+ UInt16 entrySelector;
+ UInt16 rangeShift;
+ TTFontDirComponent components[1]; /* an array of numTables components */
+} TTFontDir;
+
+/* Tag definitions */
+#define TTF_FONT_NAMING_TABLE 'name'
+
+/* Font Naming Table */
+
+typedef struct {
+ UInt16 formatSelector;
+ UInt16 numNames;
+ UInt16 stringAreaOffset;
+ UInt16 platformID;
+ UInt16 platformSpecificID;
+ UInt16 languageID;
+ UInt16 nameID;
+ UInt16 length;
+ UInt16 offset;
+} TTFontNamingTable;
+
+#endif /* gdevmacttf_INCLUDED */
diff --git a/devices/gdevmeds.c b/devices/gdevmeds.c
new file mode 100644
index 000000000..c70a30df6
--- /dev/null
+++ b/devices/gdevmeds.c
@@ -0,0 +1,92 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+/*
+ * Media selection support for printer drivers
+ *
+ * Select from a NULL terminated list of media the smallest medium which is
+ * almost equal or larger then the actual imagesize.
+ *
+ * Written by Ulrich Schmid, uschmid@mail.hh.provi.de.
+ */
+
+#include "gdevmeds.h"
+
+#define CM * 0.01
+#define INCH * 0.0254
+#define TOLERANCE 0.1 CM
+
+static const struct {
+ const char* name;
+ float width;
+ float height;
+ float priority;
+} media[] = {
+#define X(name, width, height) {name, width, height, 1 / (width * height)}
+ X("a0", 83.9611 CM, 118.816 CM),
+ X("a1", 59.4078 CM, 83.9611 CM),
+ X("a2", 41.9806 CM, 59.4078 CM),
+ X("a3", 29.7039 CM, 41.9806 CM),
+ X("a4", 20.9903 CM, 29.7039 CM),
+ X("a5", 14.8519 CM, 20.9903 CM),
+ X("a6", 10.4775 CM, 14.8519 CM),
+ X("a7", 7.40833 CM, 10.4775 CM),
+ X("a8", 5.22111 CM, 7.40833 CM),
+ X("a9", 3.70417 CM, 5.22111 CM),
+ X("a10", 2.61056 CM, 3.70417 CM),
+ X("archA", 9 INCH, 12 INCH),
+ X("archB", 12 INCH, 18 INCH),
+ X("archC", 18 INCH, 24 INCH),
+ X("archD", 24 INCH, 36 INCH),
+ X("archE", 36 INCH, 48 INCH),
+ X("b0", 100.048 CM, 141.393 CM),
+ X("b1", 70.6967 CM, 100.048 CM),
+ X("b2", 50.0239 CM, 70.6967 CM),
+ X("b3", 35.3483 CM, 50.0239 CM),
+ X("b4", 25.0119 CM, 35.3483 CM),
+ X("b5", 17.6742 CM, 25.0119 CM),
+ X("flsa", 8.5 INCH, 13 INCH),
+ X("flse", 8.5 INCH, 13 INCH),
+ X("halfletter", 5.5 INCH, 8.5 INCH),
+ X("ledger", 17 INCH, 11 INCH),
+ X("legal", 8.5 INCH, 14 INCH),
+ X("letter", 8.5 INCH, 11 INCH),
+ X("note", 7.5 INCH, 10 INCH),
+ X("executive", 7.25 INCH, 10.5 INCH),
+ X("com10", 4.125 INCH, 9.5 INCH),
+ X("dl", 11 CM, 22 CM),
+ X("c5", 16.2 CM, 22.9 CM),
+ X("monarch", 3.875 INCH, 7.5 INCH)};
+
+int select_medium(gx_device_printer *pdev, const char **available, int default_index)
+{
+ int i, j, index = default_index;
+ float priority = 0;
+ float width = pdev->width / pdev->x_pixels_per_inch INCH;
+ float height = pdev->height / pdev->y_pixels_per_inch INCH;
+
+ for (i = 0; available[i]; i++) {
+ for (j = 0; j < sizeof(media) / sizeof(media[0]); j++) {
+ if (!strcmp(available[i], media[j].name) &&
+ media[j].width + TOLERANCE > width &&
+ media[j].height + TOLERANCE > height &&
+ media[j].priority > priority) {
+ index = i;
+ priority = media[j].priority;
+ }
+ }
+ }
+ return index;
+}
diff --git a/devices/gdevmeds.h b/devices/gdevmeds.h
new file mode 100644
index 000000000..bd2f8e000
--- /dev/null
+++ b/devices/gdevmeds.h
@@ -0,0 +1,26 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+/* Interface for gdevmeds.c */
+
+#ifndef gdevmeds_INCLUDED
+# define gdevmeds_INCLUDED
+
+#include "gdevprn.h"
+
+int select_medium(gx_device_printer *pdev, const char **available,
+ int default_index);
+
+#endif /* gdevmeds_INCLUDED */
diff --git a/devices/gdevmgr.c b/devices/gdevmgr.c
new file mode 100644
index 000000000..b1867e91a
--- /dev/null
+++ b/devices/gdevmgr.c
@@ -0,0 +1,431 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+/* MGR device driver */
+#include "gdevprn.h"
+#include "gdevpccm.h"
+#include "gdevmgr.h"
+
+/* Structure for MGR devices, which extend the generic printer device. */
+struct gx_device_mgr_s {
+ gx_device_common;
+ gx_prn_device_common;
+ /* Add MGR specific variables */
+ int mgr_depth;
+ /* globals for greymapped printing */
+ unsigned char bgreytable[16];
+ unsigned char bgreybacktable[16];
+ unsigned char bgrey256table[256];
+ unsigned char bgrey256backtable[256];
+ struct nclut clut[256];
+};
+typedef struct gx_device_mgr_s gx_device_mgr;
+
+static unsigned int clut2mgr(int, int);
+static void swap_bwords(unsigned char *, int);
+
+/* ------ The device descriptors ------ */
+
+/*
+ * Default X and Y resolution.
+ */
+#define X_DPI 72
+#define Y_DPI 72
+
+/* Macro for generating MGR device descriptors. */
+#define mgr_prn_device(procs, dev_name, num_comp, depth, mgr_depth,\
+ max_gray, max_rgb, dither_gray, dither_rgb, print_page)\
+{ prn_device_body(gx_device_mgr, procs, dev_name,\
+ DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS, X_DPI, Y_DPI,\
+ 0, 0, 0, 0,\
+ num_comp, depth, max_gray, max_rgb, dither_gray, dither_rgb,\
+ print_page),\
+ mgr_depth\
+}
+
+/* For all mgr variants we do some extra things at opening time. */
+/* static dev_proc_open_device(gdev_mgr_open); */
+#define gdev_mgr_open gdev_prn_open /* no we don't! */
+
+/* And of course we need our own print-page routines. */
+static dev_proc_print_page(mgr_print_page);
+static dev_proc_print_page(mgrN_print_page);
+static dev_proc_print_page(cmgrN_print_page);
+
+/* The device procedures */
+/* Since the print_page doesn't alter the device, this device can print in the background */
+static gx_device_procs mgr_procs =
+ prn_procs(gdev_mgr_open, gdev_prn_bg_output_page, gdev_prn_close);
+static gx_device_procs mgrN_procs =
+ prn_color_procs(gdev_mgr_open, gdev_prn_bg_output_page, gdev_prn_close,
+ gx_default_gray_map_rgb_color, gx_default_gray_map_color_rgb);
+static gx_device_procs cmgr4_procs =
+ prn_color_procs(gdev_mgr_open, gdev_prn_bg_output_page, gdev_prn_close,
+ pc_4bit_map_rgb_color, pc_4bit_map_color_rgb);
+static gx_device_procs cmgr8_procs =
+ prn_color_procs(gdev_mgr_open, gdev_prn_bg_output_page, gdev_prn_close,
+ mgr_8bit_map_rgb_color, mgr_8bit_map_color_rgb);
+
+/* The device descriptors themselves */
+gx_device_mgr far_data gs_mgrmono_device =
+ mgr_prn_device( mgr_procs, "mgrmono", 1, 1, 1, 1, 0, 2, 0, mgr_print_page);
+gx_device_mgr far_data gs_mgrgray2_device =
+ mgr_prn_device(mgrN_procs, "mgrgray2",1, 8, 2, 255, 0, 4, 0, mgrN_print_page);
+gx_device_mgr far_data gs_mgrgray4_device =
+ mgr_prn_device(mgrN_procs, "mgrgray4",1, 8, 4, 255, 0,16, 0, mgrN_print_page);
+gx_device_mgr far_data gs_mgrgray8_device =
+ mgr_prn_device(mgrN_procs, "mgrgray8",1, 8, 8, 255, 0, 0, 0, mgrN_print_page);
+gx_device_mgr far_data gs_mgr4_device =
+ mgr_prn_device(cmgr4_procs, "mgr4", 3, 8, 4, 1, 1, 2, 2, cmgrN_print_page);
+gx_device_mgr far_data gs_mgr8_device =
+ mgr_prn_device(cmgr8_procs, "mgr8", 3, 8, 8, 255, 255, 6, 5, cmgrN_print_page);
+
+/* ------ Internal routines ------ */
+
+/* Define a "cursor" that keeps track of where we are in the page. */
+typedef struct mgr_cursor_s {
+ gx_device_mgr *dev;
+ int bpp; /* bits per pixel */
+ uint line_size; /* bytes per scan line */
+ byte *data; /* output row buffer */
+ int lnum; /* row within page */
+} mgr_cursor;
+
+/* Begin an MGR output page. */
+/* Write the header information and initialize the cursor. */
+static int
+mgr_begin_page(gx_device_mgr *bdev, FILE *pstream, mgr_cursor *pcur)
+{ struct b_header head;
+ uint line_size =
+ gdev_prn_raster((gx_device_printer *)bdev) + 3;
+ /* FIXME: Note that there does not seem to free for 'data' LEAK ALERT */
+ byte *data = (byte *)gs_malloc(bdev->memory, line_size, 1, "mgr_begin_page");
+ if ( data == 0 )
+ return_error(gs_error_VMerror);
+
+ /* Write the header */
+ B_PUTHDR8(&head, bdev->width, bdev->height, bdev->mgr_depth);
+ fprintf(pstream, "");
+ if ( fwrite(&head, 1, sizeof(head), pstream) < sizeof(head) )
+ return_error(gs_error_ioerror);
+ fflush(pstream);
+
+ /* Initialize the cursor. */
+ pcur->dev = bdev;
+ pcur->bpp = bdev->color_info.depth;
+ pcur->line_size = line_size;
+ pcur->data = data;
+ pcur->lnum = 0;
+ return 0;
+}
+
+/* Advance to the next row. Return 0 if more, 1 if done. */
+static int
+mgr_next_row(mgr_cursor *pcur)
+{ if ( pcur->lnum >= pcur->dev->height )
+ { gs_free(((gx_device_printer *)pcur->dev)->memory,
+ (char *)pcur->data, pcur->line_size, 1,
+ "mgr_next_row(done)");
+ return 1;
+ }
+ gdev_prn_copy_scan_lines((gx_device_printer *)pcur->dev,
+ pcur->lnum++, pcur->data, pcur->line_size);
+ return 0;
+}
+
+/* ------ Individual page printing routines ------ */
+
+#define bdev ((gx_device_mgr *)pdev)
+
+/* Print a monochrome page. */
+static int
+mgr_print_page(gx_device_printer *pdev, FILE *pstream)
+{ mgr_cursor cur;
+ int mgr_wide;
+ int code = mgr_begin_page(bdev, pstream, &cur);
+ if ( code < 0 ) return code;
+
+ mgr_wide = bdev->width;
+ if (mgr_wide & 7)
+ mgr_wide += 8 - (mgr_wide & 7);
+
+ while ( !(code = mgr_next_row(&cur)) )
+ { if ( fwrite(cur.data, sizeof(char), mgr_wide / 8, pstream) <
+ mgr_wide / 8)
+ return_error(gs_error_ioerror);
+ }
+ return (code < 0 ? code : 0);
+}
+
+/* Print a gray-mapped page. */
+static int
+mgrN_print_page(gx_device_printer *pdev, FILE *pstream)
+{ mgr_cursor cur;
+ int i = 0, j, k, mgr_wide;
+ uint mgr_line_size;
+ byte *bp, *data = NULL, *dp;
+ gx_device_mgr *mgr = (gx_device_mgr *)pdev;
+
+ int code = mgr_begin_page(bdev, pstream, &cur);
+ if ( code < 0 ) return code;
+
+ mgr_wide = bdev->width;
+ if ( bdev->mgr_depth == 2 && mgr_wide & 3 )
+ mgr_wide += 4 - (mgr_wide & 3);
+ if ( bdev->mgr_depth == 4 && mgr_wide & 1 )
+ mgr_wide++;
+ mgr_line_size = mgr_wide / ( 8 / bdev->mgr_depth );
+
+ if ( bdev->mgr_depth == 4 )
+ for ( i = 0; i < 16; i++ ) {
+ mgr->bgreytable[i] = mgrlut[LUT_BGREY][RGB_RED][i];
+ mgr->bgreybacktable[mgr->bgreytable[i]] = i;
+ }
+
+ if ( bdev->mgr_depth == 8 ) {
+ for ( i = 0; i < 16; i++ ) {
+ mgr->bgrey256table[i] = mgrlut[LUT_BGREY][RGB_RED][i] << 4;
+ mgr->bgrey256backtable[mgr->bgrey256table[i]] = i;
+ }
+ for ( i = 16,j = 0; i < 256; i++ ) {
+ for ( k = 0; k < 16; k++ )
+ if ( j == mgrlut[LUT_BGREY][RGB_RED][k] << 4 ) {
+ j++;
+ break;
+ }
+ mgr->bgrey256table[i] = j;
+ mgr->bgrey256backtable[j++] = i;
+ }
+ }
+
+ if ( bdev->mgr_depth != 8 )
+ data = (byte *)gs_malloc(pdev->memory, mgr_line_size, 1, "mgrN_print_page");
+
+ while ( !(code = mgr_next_row(&cur)) )
+ {
+ switch (bdev->mgr_depth) {
+ case 2:
+ for (i = 0,dp = data,bp = cur.data; i < mgr_line_size; i++) {
+ *dp = *(bp++) & 0xc0;
+ *dp |= (*(bp++) & 0xc0) >> 2;
+ *dp |= (*(bp++) & 0xc0) >> 4;
+ *(dp++) |= (*(bp++) & 0xc0) >> 6;
+ }
+ if ( fwrite(data, sizeof(byte), mgr_line_size, pstream) < mgr_line_size )
+ return_error(gs_error_ioerror);
+ break;
+
+ case 4:
+ for (i = 0,dp = data, bp = cur.data; i < mgr_line_size; i++) {
+ *dp = mgr->bgreybacktable[*(bp++) >> 4] << 4;
+ *(dp++) |= mgr->bgreybacktable[*(bp++) >> 4];
+ }
+ if ( fwrite(data, sizeof(byte), mgr_line_size, pstream) < mgr_line_size )
+ return_error(gs_error_ioerror);
+ break;
+
+ case 8:
+ for (i = 0,bp = cur.data; i < mgr_line_size; i++, bp++)
+ *bp = mgr->bgrey256backtable[*bp];
+ if ( fwrite(cur.data, sizeof(cur.data[0]), mgr_line_size, pstream)
+ < mgr_line_size )
+ return_error(gs_error_ioerror);
+ break;
+ }
+ }
+ if (bdev->mgr_depth != 8)
+ gs_free(bdev->memory, (char *)data, mgr_line_size, 1, "mgrN_print_page(done)");
+
+ if (bdev->mgr_depth == 2) {
+ for (i = 0; i < 4; i++) {
+ mgr->clut[i].colnum = i;
+ mgr->clut[i].red = mgr->clut[i].green = mgr->clut[i].blue = clut2mgr(i, 2);
+ }
+ }
+ if (bdev->mgr_depth == 4) {
+ for (i = 0; i < 16; i++) {
+ mgr->clut[i].colnum = i;
+ mgr->clut[i].red = mgr->clut[i].green = mgr->clut[i].blue = clut2mgr(mgr->bgreytable[i], 4);
+ }
+ }
+ if (bdev->mgr_depth == 8) {
+ for (i = 0; i < 256; i++) {
+ mgr->clut[i].colnum = i;
+ mgr->clut[i].red = mgr->clut[i].green = mgr->clut[i].blue = clut2mgr(mgr->bgrey256table[i], 8);
+ }
+ }
+#if !arch_is_big_endian
+ swap_bwords( (unsigned char *) mgr->clut, sizeof( struct nclut ) * i );
+#endif
+ if ( fwrite(&mgr->clut, sizeof(struct nclut), i, pstream) < i )
+ return_error(gs_error_ioerror);
+ return (code < 0 ? code : 0);
+}
+
+/* Print a color page. */
+static int
+cmgrN_print_page(gx_device_printer *pdev, FILE *pstream)
+{ mgr_cursor cur;
+ int i = 0, j, mgr_wide, r, g, b, colors8 = 0;
+ uint mgr_line_size;
+ byte *bp, *data, *dp;
+ ushort prgb[3];
+ unsigned char table[256], backtable[256];
+ gx_device_mgr *mgr = (gx_device_mgr *)pdev;
+
+ int code = mgr_begin_page(bdev, pstream, &cur);
+ if ( code < 0 ) return code;
+
+ mgr_wide = bdev->width;
+ if (bdev->mgr_depth == 4 && mgr_wide & 1)
+ mgr_wide++;
+ mgr_line_size = mgr_wide / (8 / bdev->mgr_depth);
+ data = (byte *)gs_malloc(pdev->memory, mgr_line_size, 1, "cmgrN_print_page");
+
+ if ( bdev->mgr_depth == 8 ) {
+ memset( table, 0, sizeof(table) );
+ for ( r = 0; r <= 6; r++ )
+ for ( g = 0; g <= 6; g++ )
+ for ( b = 0; b <= 6; b++ )
+ if ( r == g && g == b )
+ table[ r + (256-7) ] = 1;
+ else
+ table[ (r << 5) + (g << 2) + (b >> 1) ] = 1;
+ for ( i = j = 0; i < sizeof(table); i++ )
+ if ( table[i] == 1 ) {
+ backtable[i] = j;
+ table[j++] = i;
+ }
+ colors8 = j;
+ }
+ while ( !(code = mgr_next_row(&cur)) )
+ {
+ switch (bdev->mgr_depth) {
+ case 4:
+ for (i = 0,dp = data, bp = cur.data; i < mgr_line_size; i++) {
+ *dp = *(bp++) << 4;
+ *(dp++) |= *(bp++) & 0x0f;
+ }
+ if ( fwrite(data, sizeof(byte), mgr_line_size, pstream) < mgr_line_size )
+ return_error(gs_error_ioerror);
+ break;
+
+ case 8:
+ for (i = 0,bp = cur.data; i < mgr_line_size; i++, bp++)
+ *bp = backtable[*bp] + MGR_RESERVEDCOLORS;
+ if ( fwrite(cur.data, sizeof(cur.data[0]), mgr_line_size, pstream) < mgr_line_size )
+ return_error(gs_error_ioerror);
+ break;
+ }
+ }
+ gs_free(bdev->memory, (char *)data, mgr_line_size, 1, "cmgrN_print_page(done)");
+
+ if (bdev->mgr_depth == 4) {
+ for (i = 0; i < 16; i++) {
+ pc_4bit_map_color_rgb((gx_device *)0, (gx_color_index) i, prgb);
+ mgr->clut[i].colnum = i;
+ mgr->clut[i].red = clut2mgr(prgb[0], 16);
+ mgr->clut[i].green = clut2mgr(prgb[1], 16);
+ mgr->clut[i].blue = clut2mgr(prgb[2], 16);
+ }
+ }
+ if (bdev->mgr_depth == 8) {
+ for (i = 0; i < colors8; i++) {
+ mgr_8bit_map_color_rgb((gx_device *)0, (gx_color_index)
+ table[i], prgb);
+ mgr->clut[i].colnum = MGR_RESERVEDCOLORS + i;
+ mgr->clut[i].red = clut2mgr(prgb[0], 16);
+ mgr->clut[i].green = clut2mgr(prgb[1], 16);
+ mgr->clut[i].blue = clut2mgr(prgb[2], 16);
+ }
+ }
+#if !arch_is_big_endian
+ swap_bwords( (unsigned char *) mgr->clut, sizeof( struct nclut ) * i );
+#endif
+ if ( fwrite(&mgr->clut, sizeof(struct nclut), i, pstream) < i )
+ return_error(gs_error_ioerror);
+ return (code < 0 ? code : 0);
+}
+
+/* Color mapping routines for 8-bit color with a fixed palette */
+/* (3 bits of R, 3 bits of G, 2 bits of B). */
+/* We have to trade off even spacing of colors along each axis */
+/* against the desire to have real gray shades; */
+/* MGR compromises by using a 7x7x4 "cube" with extra gray shades */
+/* (1/6, 1/2, and 5/6), instead of the obvious 8x8x4. */
+
+gx_color_index
+mgr_8bit_map_rgb_color(gx_device *dev, const gx_color_value cv[])
+{
+ uint rv = cv[0] / (gx_max_color_value / 7 + 1);
+ uint gv = cv[1] / (gx_max_color_value / 7 + 1);
+ uint bv = cv[2] / (gx_max_color_value / 7 + 1);
+ return (gx_color_index)
+ (rv == gv && gv == bv ? rv + (256-7) :
+ (rv << 5) + (gv << 2) + (bv >> 1));
+}
+int
+mgr_8bit_map_color_rgb(gx_device *dev, gx_color_index color,
+ gx_color_value prgb[3])
+{ static const gx_color_value ramp[8] =
+ { 0, gx_max_color_value / 6, gx_max_color_value / 3,
+ gx_max_color_value / 2, 2 * (gx_max_color_value / 3),
+ 5 * (gx_max_color_value / 6), gx_max_color_value,
+ /* The 8th entry is not actually ever used, */
+ /* except to fill out the palette. */
+ gx_max_color_value
+ };
+#define icolor (uint)color
+ if ( icolor >= 256-7 )
+ { prgb[0] = prgb[1] = prgb[2] = ramp[icolor - (256-7)];
+ }
+ else
+ { prgb[0] = ramp[(icolor >> 5) & 7];
+ prgb[1] = ramp[(icolor >> 2) & 7];
+ prgb[2] = ramp[(icolor & 3) << 1];
+ }
+#undef icolor
+ return 0;
+}
+
+/* convert the 8-bit look-up table into the standard MGR look-up table */
+static unsigned int
+clut2mgr(
+ register int v, /* value in clut */
+ register int bits /* number of bits in clut */
+)
+{
+ register unsigned int i;
+
+ i = (unsigned int) 0xffffffff / ((1<<bits)-1);
+ return((v*i)/0x10000);
+}
+
+/*
+ * s w a p _ b w o r d s
+ */
+static void
+swap_bwords(register unsigned char *p, int n)
+{
+ register unsigned char c;
+
+ n /= 2;
+
+ for (; n > 0; n--, p += 2) {
+ c = p[0];
+ p[0] = p[1];
+ p[1] = c;
+ }
+}
diff --git a/devices/gdevmgr.h b/devices/gdevmgr.h
new file mode 100644
index 000000000..d8c772f2b
--- /dev/null
+++ b/devices/gdevmgr.h
@@ -0,0 +1,115 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+/* Common header file for MGR devices */
+
+#ifndef gdevmgr_INCLUDED
+# define gdevmgr_INCLUDED
+
+#define MGR_RESERVEDCOLORS 16
+
+/* Color mapping routines for 8-bit color (with a fixed palette). */
+dev_proc_map_rgb_color(mgr_8bit_map_rgb_color);
+dev_proc_map_color_rgb(mgr_8bit_map_color_rgb);
+
+/* extract from dump.h */
+
+/*
+ * format for saved bitmaps
+ */
+
+#define B_PUTHDR8(hdr, w, h, d) ( \
+ (hdr)->magic[0] = 'y', (hdr)->magic[1] = 'z', \
+ (hdr)->h_wide = (((w) >> 6) & 0x3f) + ' ', \
+ (hdr)->l_wide = ((w) & 0x3f) + ' ', \
+ (hdr)->h_high = (((h) >> 6) & 0x3f) + ' ', \
+ (hdr)->l_high = ((h) & 0x3f) + ' ', \
+ (hdr)->depth = ((d) & 0x3f) + ' ', \
+ (hdr)->_reserved = ' ' )
+
+struct b_header {
+ char magic[2]; /* magics */
+ char h_wide; /* upper byte width (biased with 0x20) */
+ char l_wide; /* lower byte width (biased with 0x20) */
+ char h_high; /* upper byte height (biased with 0x20) */
+ char l_high; /* lower byte height (biased with 0x20) */
+ char depth; /* depth (biased with 0x20) */
+ char _reserved; /* for alignment */
+};
+
+/*
+ * Color lookup table information
+ */
+struct nclut {
+ unsigned short colnum;
+ unsigned short red, green, blue;
+} ;
+
+/* extract from color.h */
+
+/*
+ * MGR Color Definitions
+ */
+
+#define LUT_BW 0
+#define LUT_GREY 1
+#define LUT_BGREY 2
+#define LUT_VGA 3
+#define LUT_BCT 4
+#define LUT_USER 5
+#define LUT 6
+#define LUT_8 LUT
+
+#define RGB_RED 0
+#define RGB_GREEN 1
+#define RGB_BLUE 2
+#define RGB 3
+
+#define LUTENTRIES 16
+
+#define BW_RED 15, 0, 15, 0, 15, 0, 15, 0, 15, 0, 15, 0, 15, 0, 15, 0
+#define BW_GREEN BW_RED
+#define BW_BLUE BW_RED
+
+#define GREY_RED 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15
+#define GREY_GREEN GREY_RED
+#define GREY_BLUE GREY_RED
+
+#define BGREY_RED 1, 0, 2, 8, 4, 3, 13, 11, 7, 6, 10, 12, 14, 5, 9, 15
+#define BGREY_GREEN BGREY_RED
+#define BGREY_BLUE BGREY_RED
+
+#define VGA_RED 0, 0, 0, 0, 8, 8, 8, 12, 8, 0, 0, 0, 15, 15, 15, 15
+#define VGA_GREEN 0, 0, 8, 8, 0, 0, 8, 12, 8, 0, 15, 15, 0, 0, 15, 15
+#define VGA_BLUE 0, 8, 0, 8, 0, 8, 0, 12, 8, 15, 0, 15, 0, 15, 0, 15
+
+#define BCT_RED 1, 7, 6, 15, 14, 3, 13, 11, 7, 13, 13, 15, 15, 5, 9, 15
+#define BCT_GREEN 1, 7, 13, 12, 5, 3, 13, 11, 7, 14, 15, 15, 14, 5, 9, 15
+#define BCT_BLUE 1, 14, 6, 8, 5, 3, 13, 11, 7, 15, 14, 12, 13, 5, 9, 15
+
+#define USER_RED 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+#define USER_GREEN USER_RED
+#define USER_BLUE USER_RED
+
+static char mgrlut[LUT][RGB][LUTENTRIES] = {
+ { { BW_RED }, { BW_GREEN }, { BW_BLUE } },
+ { { GREY_RED }, { GREY_GREEN }, { GREY_BLUE } },
+ { { BGREY_RED }, { BGREY_GREEN }, { BGREY_BLUE } },
+ { { VGA_RED }, { VGA_GREEN }, { VGA_BLUE } },
+ { { BCT_RED }, { BCT_GREEN }, { BCT_BLUE } },
+ { { USER_RED }, { USER_GREEN }, { USER_BLUE } }
+};
+
+#endif /* gdevmgr_INCLUDED */
diff --git a/devices/gdevmiff.c b/devices/gdevmiff.c
new file mode 100644
index 000000000..4a5cde55c
--- /dev/null
+++ b/devices/gdevmiff.c
@@ -0,0 +1,83 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+/* MIFF file format driver */
+#include "gdevprn.h"
+
+/* ------ The device descriptor ------ */
+
+/*
+ * Default X and Y resolution.
+ */
+#define X_DPI 72
+#define Y_DPI 72
+
+static dev_proc_print_page(miff24_print_page);
+
+/* Since the print_page doesn't alter the device, this device can print in the background */
+static const gx_device_procs miff24_procs =
+prn_color_procs(gdev_prn_open, gdev_prn_bg_output_page, gdev_prn_close,
+ gx_default_rgb_map_rgb_color, gx_default_rgb_map_color_rgb);
+const gx_device_printer gs_miff24_device =
+prn_device(miff24_procs, "miff24",
+ DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
+ X_DPI, Y_DPI,
+ 0, 0, 0, 0, /* margins */
+ 24, miff24_print_page);
+
+/* Print one page in 24-bit RLE direct color format. */
+static int
+miff24_print_page(gx_device_printer * pdev, FILE * file)
+{
+ int raster = gx_device_raster((gx_device *) pdev, true);
+ byte *line = gs_alloc_bytes(pdev->memory, raster, "miff line buffer");
+ int y;
+ int code = 0; /* return code */
+
+ if (line == 0) /* can't allocate line buffer */
+ return_error(gs_error_VMerror);
+ fputs("id=ImageMagick\n", file);
+ fputs("class=DirectClass\n", file);
+ fprintf(file, "columns=%d\n", pdev->width);
+ fputs("compression=RunlengthEncoded\n", file);
+ fprintf(file, "rows=%d\n", pdev->height);
+ fputs(":\n", file);
+ for (y = 0; y < pdev->height; ++y) {
+ byte *row;
+ byte *end;
+
+ code = gdev_prn_get_bits(pdev, y, line, &row);
+ if (code < 0)
+ break;
+ end = row + pdev->width * 3;
+ while (row < end) {
+ int count = 0;
+
+ while (count < 255 && row < end - 3 &&
+ row[0] == row[3] && row[1] == row[4] &&
+ row[2] == row[5]
+ )
+ ++count, row += 3;
+ putc(row[0], file);
+ putc(row[1], file);
+ putc(row[2], file);
+ putc(count, file);
+ row += 3;
+ }
+ }
+ gs_free_object(pdev->memory, line, "miff line buffer");
+
+ return code;
+}
diff --git a/devices/gdevmswn.c b/devices/gdevmswn.c
new file mode 100644
index 000000000..902dad503
--- /dev/null
+++ b/devices/gdevmswn.c
@@ -0,0 +1,497 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+/*
+ * Microsoft Windows 3.n driver for Ghostscript.
+ *
+ * Original version by Russell Lang and Maurice Castro with help from
+ * Programming Windows, 2nd Ed., Charles Petzold, Microsoft Press;
+ * created from gdevbgi.c and gnuplot/term/win.trm 5th June 1992.
+ * Extensively modified by L. Peter Deutsch, Aladdin Enterprises.
+ */
+#include "gdevmswn.h"
+#include "gp.h"
+#include "gpcheck.h"
+#include "gsparam.h"
+#include "gdevpccm.h"
+#include "gsdll.h"
+
+/* Forward references */
+static int win_set_bits_per_pixel(gx_device_win *, int);
+
+#define TIMER_ID 1
+
+/* Open the win driver */
+int
+win_open(gx_device * dev)
+{
+ HDC hdc;
+ int code;
+
+ if (dev->width == INITIAL_WIDTH)
+ dev->width = (int)(8.5 * dev->x_pixels_per_inch);
+ if (dev->height == INITIAL_HEIGHT)
+ dev->height = (int)(11.0 * dev->y_pixels_per_inch);
+
+ if (wdev->BitsPerPixel == 0) {
+ int depth;
+
+ /* Set parameters that were unknown before opening device */
+ /* Find out if the device supports color */
+ /* We recognize 1, 4, 8, 16, 24 bit/pixel devices */
+ hdc = GetDC(NULL); /* get hdc for desktop */
+ depth = GetDeviceCaps(hdc, PLANES) * GetDeviceCaps(hdc, BITSPIXEL);
+ if (depth > 16) {
+ wdev->BitsPerPixel = 24;
+ } else if (depth > 8) {
+ wdev->BitsPerPixel = 16;
+ } else if (depth >= 8) {
+ wdev->BitsPerPixel = 8;
+ } else if (depth >= 4) {
+ wdev->BitsPerPixel = 4;
+ } else {
+ wdev->BitsPerPixel = 1;
+ }
+ ReleaseDC(NULL, hdc);
+ wdev->mapped_color_flags = 0;
+ }
+ if ((code = win_set_bits_per_pixel(wdev, wdev->BitsPerPixel)) < 0)
+ return code;
+
+ if (wdev->nColors > 0) {
+ /* create palette for display */
+ if ((wdev->limgpalette = win_makepalette(wdev))
+ == (LPLOGPALETTE) NULL)
+ return win_nomemory();
+ wdev->himgpalette = CreatePalette(wdev->limgpalette);
+ }
+ return 0;
+}
+
+/* Make the output appear on the screen. */
+int
+win_sync_output(gx_device * dev)
+{
+ if (pgsdll_callback)
+ (*pgsdll_callback) (GSDLL_SYNC, (unsigned char *)wdev, 0);
+ return (0);
+}
+
+/* Make the window visible, and display the output. */
+int
+win_output_page(gx_device * dev, int copies, int flush)
+{
+ if (pgsdll_callback)
+ (*pgsdll_callback) (GSDLL_PAGE, (unsigned char *)wdev, 0);
+ return gx_finish_output_page(dev, copies, flush);;
+}
+
+/* Close the win driver */
+int
+win_close(gx_device * dev)
+{
+ /* Free resources */
+ if (wdev->nColors > 0) {
+ gs_free(dev->memory,
+ wdev->mapped_color_flags, 4096, 1, "win_set_bits_per_pixel");
+ DeleteObject(wdev->himgpalette);
+ gs_free(dev->memory,
+ (char *)(wdev->limgpalette), 1, sizeof(LOGPALETTE) +
+ (1 << (wdev->color_info.depth)) * sizeof(PALETTEENTRY),
+ "win_close");
+ }
+ return (0);
+}
+
+/* Map a r-g-b color to the colors available under Windows */
+gx_color_index
+win_map_rgb_color(gx_device * dev, const gx_color_value cv[])
+{
+ gx_color_value r = cv[0];
+ gx_color_value g = cv[1];
+ gx_color_value b = cv[2];
+ switch (wdev->BitsPerPixel) {
+ case 24:
+ return (((unsigned long)b >> (gx_color_value_bits - 8)) << 16) +
+ (((unsigned long)g >> (gx_color_value_bits - 8)) << 8) +
+ (((unsigned long)r >> (gx_color_value_bits - 8)));
+ case 16:{
+ gx_color_index color = ((r >> (gx_color_value_bits - 5)) << 11) +
+ ((g >> (gx_color_value_bits - 6)) << 5) +
+ (b >> (gx_color_value_bits - 5));
+#if arch_is_big_endian
+ ushort color16 = (ushort)color;
+#else
+ ushort color16 = (ushort)((color << 8) | (color >> 8));
+#endif
+ return color16;
+ }
+ case 15:{
+ gx_color_index color = ((r >> (gx_color_value_bits - 5)) << 10) +
+ ((g >> (gx_color_value_bits - 5)) << 5) +
+ (b >> (gx_color_value_bits - 5));
+#if arch_is_big_endian
+ ushort color15 = (ushort)color;
+#else
+ ushort color15 = (ushort)((color << 8) | (color >> 8));
+#endif
+ return color15;
+ }
+ case 8:{
+ int i;
+ LPLOGPALETTE lpal = wdev->limgpalette;
+ PALETTEENTRY *pep;
+ byte cr, cg, cb;
+ int mc_index;
+ byte mc_mask;
+
+ /* Check for a color in the palette of 64. */
+ {
+ static const byte pal64[32] =
+ {
+ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 1
+ };
+
+ if (pal64[r >> (gx_color_value_bits - 5)] &&
+ pal64[g >> (gx_color_value_bits - 5)] &&
+ pal64[b >> (gx_color_value_bits - 5)]
+ )
+ return (gx_color_index) (
+ ((r >> (gx_color_value_bits - 2)) << 4) +
+ ((g >> (gx_color_value_bits - 2)) << 2) +
+ (b >> (gx_color_value_bits - 2))
+ );
+ }
+
+ /* map colors to 0->255 in 32 steps */
+ cr = win_color_value(r);
+ cg = win_color_value(g);
+ cb = win_color_value(b);
+
+ /* Search in palette, skipping the first 64. */
+ mc_index = ((cr >> 3) << 7) + ((cg >> 3) << 2) + (cb >> 6);
+ mc_mask = 0x80 >> ((cb >> 3) & 7);
+ if (wdev->mapped_color_flags[mc_index] & mc_mask)
+ for (i = wdev->nColors, pep = &lpal->palPalEntry[i];
+ --pep, --i >= 64;
+ ) {
+ if (cr == pep->peRed &&
+ cg == pep->peGreen &&
+ cb == pep->peBlue
+ )
+ return ((gx_color_index) i); /* found it */
+ }
+ /* next try adding it to palette */
+ i = wdev->nColors;
+ if (i < 220) { /* allow 36 for windows and other apps */
+ LPLOGPALETTE lipal = wdev->limgpalette;
+
+ wdev->nColors = i + 1;
+
+ DeleteObject(wdev->himgpalette);
+ lipal->palPalEntry[i].peFlags = 0;
+ lipal->palPalEntry[i].peRed = cr;
+ lipal->palPalEntry[i].peGreen = cg;
+ lipal->palPalEntry[i].peBlue = cb;
+ lipal->palNumEntries = wdev->nColors;
+ wdev->himgpalette = CreatePalette(lipal);
+
+ wdev->mapped_color_flags[mc_index] |= mc_mask;
+ return ((gx_color_index) i); /* return new palette index */
+ }
+ return (gx_no_color_index); /* not found - dither instead */
+ }
+ case 4:
+ return pc_4bit_map_rgb_color(dev, cv);
+ }
+ return (gx_default_map_rgb_color(dev, cv));
+}
+
+/* Map a color code to r-g-b. */
+int
+win_map_color_rgb(gx_device * dev, gx_color_index color,
+ gx_color_value prgb[3])
+{
+ gx_color_value one;
+ ushort value;
+
+ switch (wdev->BitsPerPixel) {
+ case 24:
+ one = (gx_color_value) (gx_max_color_value / 255);
+ prgb[0] = ((color) & 255) * one;
+ prgb[1] = ((color >> 8) & 255) * one;
+ prgb[2] = ((color >> 16) & 255) * one;
+ break;
+ case 16:
+ value = (color >> 11) & 0x1f;
+ prgb[0] = ((value << 11) + (value << 6) + (value << 1) + (value >> 4)) >> (16 - gx_color_value_bits);
+ value = (color >> 5) & 0x3f;
+ prgb[1] = ((value << 10) + (value << 4) + (value >> 2)) >> (16 - gx_color_value_bits);
+ value = (color) & 0x1f;
+ prgb[2] = ((value << 11) + (value << 6) + (value << 1) + (value >> 4)) >> (16 - gx_color_value_bits);
+ break;
+ case 15:
+ value = (color >> 10) & 0x1f;
+ prgb[0] = ((value << 11) + (value << 6) + (value << 1) + (value >> 4)) >> (16 - gx_color_value_bits);
+ value = (color >> 5) & 0x1f;
+ prgb[1] = ((value << 11) + (value << 6) + (value << 1) + (value >> 4)) >> (16 - gx_color_value_bits);
+ value = (color) & 0x1f;
+ prgb[2] = ((value << 11) + (value << 6) + (value << 1) + (value >> 4)) >> (16 - gx_color_value_bits);
+ break;
+ case 8:
+ if (!dev->is_open)
+ return -1;
+ one = (gx_color_value) (gx_max_color_value / 255);
+ prgb[0] = wdev->limgpalette->palPalEntry[(int)color].peRed * one;
+ prgb[1] = wdev->limgpalette->palPalEntry[(int)color].peGreen * one;
+ prgb[2] = wdev->limgpalette->palPalEntry[(int)color].peBlue * one;
+ break;
+ case 4:
+ pc_4bit_map_color_rgb(dev, color, prgb);
+ break;
+ default:
+ prgb[0] = prgb[1] = prgb[2] =
+ (int)color ? gx_max_color_value : 0;
+ }
+ return 0;
+}
+
+/* Get Win parameters */
+int
+win_get_params(gx_device * dev, gs_param_list * plist)
+{
+ int code = gx_default_get_params(dev, plist);
+
+ return code;
+}
+
+/* Put parameters. */
+/* Set window parameters -- size and resolution. */
+/* We implement this ourselves so that we can do it without */
+/* closing and opening the device. */
+int
+win_put_params(gx_device * dev, gs_param_list * plist)
+{
+ int ecode = 0, code;
+ bool is_open = dev->is_open;
+ int width = dev->width;
+ int height = dev->height;
+ int old_bpp = dev->color_info.depth;
+ int bpp = old_bpp;
+ byte *old_flags = wdev->mapped_color_flags;
+
+ /* Handle extra parameters */
+
+ switch (code = param_read_int(plist, "BitsPerPixel", &bpp)) {
+ case 0:
+ if (dev->is_open && bpp != old_bpp)
+ ecode = gs_error_rangecheck;
+ else { /* Don't release existing mapped_color_flags. */
+ if (bpp != 8)
+ wdev->mapped_color_flags = 0;
+ code = win_set_bits_per_pixel(wdev, bpp);
+ if (code < 0)
+ ecode = code;
+ else
+ break;
+ }
+ goto bppe;
+ default:
+ ecode = code;
+ bppe:param_signal_error(plist, "BitsPerPixel", ecode);
+ case 1:
+ break;
+ }
+
+ if (ecode >= 0) { /* Prevent gx_default_put_params from closing the device. */
+ dev->is_open = false;
+ ecode = gx_default_put_params(dev, plist);
+ dev->is_open = is_open;
+ }
+ if (ecode < 0) { /* If we allocated mapped_color_flags, release it. */
+ if (wdev->mapped_color_flags != 0 && old_flags == 0)
+ gs_free(wdev->memory,
+ wdev->mapped_color_flags, 4096, 1,
+ "win_put_params");
+ wdev->mapped_color_flags = old_flags;
+ if (bpp != old_bpp)
+ win_set_bits_per_pixel(wdev, old_bpp);
+ return ecode;
+ }
+ if (wdev->mapped_color_flags == 0 && old_flags != 0) { /* Release old mapped_color_flags. */
+ gs_free(dev->memory,
+ old_flags, 4096, 1, "win_put_params");
+ }
+ /* Hand off the change to the implementation. */
+ if (is_open && (bpp != old_bpp ||
+ dev->width != width || dev->height != height)
+ ) {
+ int ccode;
+
+ (*wdev->free_bitmap) (wdev);
+ ccode = (*wdev->alloc_bitmap) (wdev, (gx_device *) wdev);
+ if (ccode < 0) { /* Bad news! Some of the other device parameters */
+ /* may have changed. We don't handle this. */
+ /* This is ****** WRONG ******. */
+ dev->width = width;
+ dev->height = height;
+ win_set_bits_per_pixel(wdev, old_bpp);
+ (*wdev->alloc_bitmap) (wdev, dev);
+ return ccode;
+ }
+ }
+ return 0;
+}
+
+/* ------ Internal routines ------ */
+
+#undef wdev
+
+/* out of memory error message box */
+int
+win_nomemory(void)
+{
+ MessageBox((HWND) NULL, (LPSTR) "Not enough memory", (LPSTR) szAppName, MB_ICONSTOP);
+ return gs_error_limitcheck;
+}
+
+LPLOGPALETTE
+win_makepalette(gx_device_win * wdev)
+{
+ int i, val;
+ LPLOGPALETTE logpalette;
+
+ logpalette = (LPLOGPALETTE) gs_malloc(wdev->memory, 1, sizeof(LOGPALETTE) +
+ (1 << (wdev->color_info.depth)) * sizeof(PALETTEENTRY),
+ "win_makepalette");
+ if (logpalette == (LPLOGPALETTE) NULL)
+ return (0);
+ logpalette->palVersion = 0x300;
+ logpalette->palNumEntries = wdev->nColors;
+ for (i = 0; i < wdev->nColors; i++) {
+ logpalette->palPalEntry[i].peFlags = 0;
+ switch (wdev->nColors) {
+ case 64:
+ /* colors are rrggbb */
+ logpalette->palPalEntry[i].peRed = ((i & 0x30) >> 4) * 85;
+ logpalette->palPalEntry[i].peGreen = ((i & 0xC) >> 2) * 85;
+ logpalette->palPalEntry[i].peBlue = (i & 3) * 85;
+ break;
+ case 16:
+ /* colors are irgb */
+ val = (i & 8 ? 255 : 128);
+ logpalette->palPalEntry[i].peRed = i & 4 ? val : 0;
+ logpalette->palPalEntry[i].peGreen = i & 2 ? val : 0;
+ logpalette->palPalEntry[i].peBlue = i & 1 ? val : 0;
+ if (i == 8) { /* light gray */
+ logpalette->palPalEntry[i].peRed =
+ logpalette->palPalEntry[i].peGreen =
+ logpalette->palPalEntry[i].peBlue = 192;
+ }
+ break;
+ case 2:
+ logpalette->palPalEntry[i].peRed =
+ logpalette->palPalEntry[i].peGreen =
+ logpalette->palPalEntry[i].peBlue = (i ? 255 : 0);
+ break;
+ }
+ }
+ return (logpalette);
+}
+
+static int
+win_set_bits_per_pixel(gx_device_win * wdev, int bpp)
+{
+ static const gx_device_color_info win_24bit_color = dci_color(24, 255, 255);
+ static const gx_device_color_info win_16bit_color = dci_color(16, 255, 255);
+ static const gx_device_color_info win_8bit_color = dci_color(8, 31, 4);
+ static const gx_device_color_info win_ega_color = dci_pc_4bit;
+ static const gx_device_color_info win_vga_color = dci_pc_4bit;
+ static const gx_device_color_info win_mono_color = dci_black_and_white;
+ /* remember old anti_alias info */
+ gx_device_anti_alias_info anti_alias = wdev->color_info.anti_alias;
+ HDC hdc;
+
+ switch (bpp) {
+ case 24:
+ wdev->color_info = win_24bit_color;
+ wdev->nColors = -1;
+ break;
+ case 16:
+ case 15:
+ wdev->color_info = win_16bit_color;
+ wdev->nColors = -1;
+ break;
+ case 8:
+ /* use 64 static colors and 166 dynamic colors from 8 planes */
+ wdev->color_info = win_8bit_color;
+ wdev->nColors = 64;
+ break;
+ case 4:
+ hdc = GetDC(NULL);
+ if (GetDeviceCaps(hdc, VERTRES) <= 350)
+ wdev->color_info = win_ega_color;
+ else
+ wdev->color_info = win_vga_color;
+ ReleaseDC(NULL, hdc);
+ wdev->nColors = 16;
+ break;
+ case 1:
+ wdev->color_info = win_mono_color;
+ wdev->nColors = 2;
+ break;
+ default:
+ return (gs_error_rangecheck);
+ }
+ wdev->BitsPerPixel = bpp;
+
+ /* If necessary, allocate and clear the mapped color flags. */
+ if (bpp == 8) {
+ if (wdev->mapped_color_flags == 0) {
+ wdev->mapped_color_flags = gs_malloc(wdev->memory,
+ 4096, 1, "win_set_bits_per_pixel");
+ if (wdev->mapped_color_flags == 0)
+ return_error(gs_error_VMerror);
+ }
+ memset(wdev->mapped_color_flags, 0, 4096);
+ } else {
+ gs_free(wdev->memory,
+ wdev->mapped_color_flags, 4096, 1, "win_set_bits_per_pixel");
+ wdev->mapped_color_flags = 0;
+ }
+
+ /* copy encode/decode procedures */
+ wdev->procs.encode_color = wdev->procs.map_rgb_color;
+ wdev->procs.decode_color = wdev->procs.map_color_rgb;
+ if (bpp == 1) {
+ wdev->procs.get_color_mapping_procs =
+ gx_default_DevGray_get_color_mapping_procs;
+ wdev->procs.get_color_comp_index =
+ gx_default_DevGray_get_color_comp_index;
+ }
+ else {
+ wdev->procs.get_color_mapping_procs =
+ gx_default_DevRGB_get_color_mapping_procs;
+ wdev->procs.get_color_comp_index =
+ gx_default_DevRGB_get_color_comp_index;
+ }
+
+ /* restore old anti_alias info */
+ wdev->color_info.anti_alias = anti_alias;
+ return 0;
+}
diff --git a/devices/gdevmswn.h b/devices/gdevmswn.h
new file mode 100644
index 000000000..0411e3731
--- /dev/null
+++ b/devices/gdevmswn.h
@@ -0,0 +1,108 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+/* Shared definitions for Microsoft Windows 3.n drivers */
+
+#ifndef gdevmswn_INCLUDED
+# define gdevmswn_INCLUDED
+
+#include "string_.h"
+#include <stdlib.h>
+#include "gx.h"
+#include "gserrors.h"
+#include "gxdevice.h"
+#include "memory_.h"
+
+#include "windows_.h"
+#include <shellapi.h>
+#include "gp_mswin.h"
+
+typedef struct gx_device_win_s gx_device_win;
+
+/* Utility routines in gdevmswn.c */
+LPLOGPALETTE win_makepalette(gx_device_win *);
+int win_nomemory(void);
+void win_update(gx_device_win *);
+
+/* Device procedures shared by all implementations. */
+/* Implementations may wrap their own code around _open and _close. */
+dev_proc_open_device(win_open);
+dev_proc_sync_output(win_sync_output);
+dev_proc_output_page(win_output_page);
+dev_proc_close_device(win_close);
+dev_proc_map_rgb_color(win_map_rgb_color);
+dev_proc_map_color_rgb(win_map_color_rgb);
+dev_proc_get_params(win_get_params);
+dev_proc_put_params(win_put_params);
+dev_proc_get_xfont_procs(win_get_xfont_procs);
+dev_proc_get_alpha_bits(win_get_alpha_bits);
+
+/* Common part of the device descriptor. */
+
+#define win_proc_copy_to_clipboard(proc)\
+ void proc(gx_device_win *)
+
+#define win_proc_repaint(proc)\
+ void proc(gx_device_win *, HDC, int, int, int, int, int, int)
+
+#define win_proc_alloc_bitmap(proc)\
+ int proc(gx_device_win *, gx_device *)
+
+#define win_proc_free_bitmap(proc)\
+ void proc(gx_device_win *)
+
+#define win_gsview_sizeof 80
+
+#define gx_device_win_common\
+ int BitsPerPixel;\
+ int nColors;\
+ byte *mapped_color_flags;\
+ /* Implementation-specific procedures */\
+ win_proc_alloc_bitmap((*alloc_bitmap));\
+ win_proc_free_bitmap((*free_bitmap));\
+ /* Handles */\
+ HPALETTE himgpalette;\
+ LPLOGPALETTE limgpalette
+
+/* The basic window device */
+struct gx_device_win_s {
+ gx_device_common;
+ gx_device_win_common;
+};
+
+/* Initial values for width and height */
+#define INITIAL_RESOLUTION 96.0
+#define INITIAL_WIDTH (int)(INITIAL_RESOLUTION * 85 / 10 + 0.5)
+#define INITIAL_HEIGHT (int)(INITIAL_RESOLUTION * 11 + 0.5)
+
+/* A macro for casting the device argument */
+#define wdev ((gx_device_win *)dev)
+
+/* RasterOp codes */
+#define rop_write_at_1s 0xE20746L /* BitBlt: write brush at 1's */
+#define rop_write_at_0s 0xB8074AL /* BitBlt: write brush at 0's */
+#define rop_write_0_at_1s 0x220326L /* BitBlt: ~S & D */
+#define rop_write_0_at_0s 0x8800C6L /* BitBlt: S & D */
+#define rop_write_1s 0xFF0062L /* write 1's */
+#define rop_write_0s 0x000042L /* write 0's */
+#define rop_write_pattern 0xF00021L /* PatBlt: write brush */
+
+/* Compress a gx_color_value into an 8-bit Windows color value, */
+/* using only the high order 5 bits. */
+#define win_color_value(z)\
+ ((((z) >> (gx_color_value_bits - 5)) << 3) +\
+ ((z) >> (gx_color_value_bits - 3)))
+
+#endif /* gdevmswn_INCLUDED */
diff --git a/devices/gdevmsxf.c b/devices/gdevmsxf.c
new file mode 100644
index 000000000..baebc302a
--- /dev/null
+++ b/devices/gdevmsxf.c
@@ -0,0 +1,464 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+/* External font (xfont) implementation for Microsoft Windows. */
+#include "ctype_.h"
+#include "math_.h"
+#include "memory_.h"
+#include "string_.h"
+#include "gdevmswn.h"
+#include "gsutil.h"
+#include "gxxfont.h"
+#include "gsstruct.h"
+
+/* Imported from gdevemap.c */
+extern const byte gs_map_std_to_iso[256];
+
+/* Declare the xfont procedures */
+static xfont_proc_lookup_font(win_lookup_font);
+static xfont_proc_char_xglyph(win_char_xglyph);
+static xfont_proc_char_metrics(win_char_metrics);
+static xfont_proc_render_char(win_render_char);
+static xfont_proc_release(win_release);
+static const gx_xfont_procs win_xfont_procs =
+{
+ win_lookup_font,
+ win_char_xglyph,
+ win_char_metrics,
+ win_render_char,
+ win_release
+};
+
+/* Return the xfont procedure record. */
+const gx_xfont_procs *
+win_get_xfont_procs(gx_device * dev)
+{
+ return &win_xfont_procs;
+}
+
+/* Define a Windows xfont. */
+typedef struct win_xfont_s win_xfont;
+struct win_xfont_s {
+ gx_xfont_common common;
+ LOGFONT lf;
+ TEXTMETRIC tm;
+ HFONT hFont;
+ gx_device_win *dev; /* for GetDC */
+ int invert_y;
+ int y_offset;
+};
+
+gs_private_st_dev_ptrs1(st_win_xfont, win_xfont, "win_xfont",
+ win_xfont_enum_ptrs, win_xfont_reloc_ptrs, dev);
+#define wxf ((win_xfont *)xf)
+
+/* Forward references */
+static HDC near win_get_dc(gx_device_win *);
+static void near win_release_dc(gx_device_win *, HDC);
+static int win_select_font(HDC, win_xfont *);
+
+/* Map from PostScript to Windows character codes. */
+/* (These tables were generated by winmaps.ps.) */
+
+static const byte far_data gs_map_symbol_to_oem[256] =
+{
+ 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, 0, 0, 0, 0, 0, 0, 0,
+ 32, 33, 0, 35, 0, 37, 38, 0, 40, 41, 0, 43, 44, 0, 46, 47,
+ 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
+ 0, 0, 0, 0, 0, 0, 231, 225, 0, 0, 0, 0, 0, 0, 0, 0,
+ 226, 232, 0, 227, 0, 0, 0, 233, 0, 0, 0, 91, 0, 93, 0, 95,
+ 0, 223, 224, 0, 234, 0, 236, 0, 0, 0, 0, 0, 0, 229, 0, 0,
+ 0, 0, 0, 228, 230, 0, 0, 0, 0, 0, 0, 123, 124, 125, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 242, 0, 235, 0, 5, 4, 3, 6, 29, 27, 24, 26, 25,
+ 247, 240, 0, 241, 0, 0, 0, 7, 245, 0, 239, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 238, 0, 0, 0, 0, 0, 0, 237, 0,
+ 0, 0, 0, 0, 0, 0, 250, 248, 169, 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, 0, 243, 0, 244, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+static const byte far_data gs_map_iso_to_oem[256] =
+{
+ 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, 0, 0, 0, 0, 0, 0, 0,
+ 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 0, 46, 47,
+ 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
+ 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79,
+ 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95,
+ 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111,
+112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 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, 0, 0, 0, 0, 0, 0, 0, 0,
+ 32, 172, 154, 155, 0, 156, 0, 21, 0, 0, 165, 173, 169, 45, 0, 0,
+ 247, 240, 252, 0, 0, 229, 20, 249, 0, 0, 166, 174, 171, 170, 0, 167,
+ 0, 0, 0, 0, 141, 142, 145, 128, 0, 143, 0, 0, 0, 0, 0, 0,
+ 0, 164, 0, 0, 0, 0, 152, 0, 0, 0, 0, 0, 153, 0, 0, 0,
+ 133, 159, 131, 0, 132, 134, 144, 135, 138, 130, 136, 137, 140, 160, 0, 139,
+ 0, 163, 148, 161, 146, 0, 147, 245, 0, 150, 162, 149, 129, 0, 0, 151
+};
+
+/* Correlate PostScript font names with Windows font names. */
+/* This table should be an external resource, like Fontmap, */
+/* but that will have to wait till later. */
+
+typedef struct font_entry_s {
+ const char *key;
+ const char *value;
+ uint pitchAndFamily;
+} font_entry;
+
+static const font_entry font_names[] =
+{
+ {"Courier", "Courier New", FIXED_PITCH | FF_MODERN},
+ {"Helvetica", "Arial", VARIABLE_PITCH | FF_SWISS},
+ {"Helvetica", "Helv", VARIABLE_PITCH | FF_SWISS},
+ {"Times", "Times New Roman", VARIABLE_PITCH | FF_ROMAN},
+ {"Times", "Tms Rmn", VARIABLE_PITCH | FF_ROMAN}
+};
+
+/* Look up a font. */
+static int /*bool */ map_logical_font(HDC, win_xfont *);
+gx_xfont *
+win_lookup_font(gx_device * dev, const byte * fname, uint len,
+ int encoding_index, const gs_uid * puid, const gs_matrix * pmat,
+ gs_memory_t * mem)
+{
+ win_xfont f;
+ win_xfont *wf;
+ uint name_len = min(len, LF_FACESIZE - 1);
+ const font_entry *pfe;
+ HDC hdc;
+
+ /* Only handle simple cases for now. */
+ if (pmat->xy != 0 || pmat->yx != 0 || pmat->xx <= 0 ||
+ fabs(fabs(pmat->yy) - pmat->xx) > 0.00002
+ )
+ return NULL;
+ f.lf.lfHeight = (long)(pmat->xx * 1000);
+ /* Don't trust Windows with very small sizes. */
+ if (f.lf.lfHeight < 6 || f.lf.lfHeight >= 36)
+ return NULL;
+ f.lf.lfWidth = 0;
+ f.lf.lfEscapement = 0;
+ f.lf.lfOrientation = 0;
+ f.lf.lfWeight =
+ (string_match(fname, len, "*Bold*", 6, NULL) ?
+ FW_BOLD : FW_REGULAR);
+ f.lf.lfItalic =
+ string_match(fname, len, "*Italic*", 8, NULL) ||
+ string_match(fname, len, "*Oblique*", 9, NULL);
+ f.lf.lfUnderline = 0;
+ f.lf.lfStrikeOut = 0;
+ f.lf.lfCharSet =
+ (encoding_index == 2 ? SYMBOL_CHARSET : ANSI_CHARSET);
+ f.lf.lfOutPrecision = OUT_CHARACTER_PRECIS;
+ f.lf.lfClipPrecision = CLIP_STROKE_PRECIS;
+ f.lf.lfQuality = PROOF_QUALITY;
+ f.hFont = 0;
+ f.invert_y = pmat->yy >= 0;
+ hdc = win_get_dc(wdev);
+ if (hdc == NULL)
+ return NULL;
+ for (pfe = font_names; pfe != &font_names[countof(font_names)]; pfe++)
+ if (!strncmp(pfe->key, fname, strlen(pfe->key))) { /* Found a match. */
+ strcpy(f.lf.lfFaceName, pfe->value);
+ f.lf.lfPitchAndFamily = pfe->pitchAndFamily;
+ if (map_logical_font(hdc, &f))
+ break;
+ }
+ if (f.hFont == 0) { /* No matches in the table, try with the given name. */
+ uint len;
+
+ memcpy(f.lf.lfFaceName, fname, name_len); /* default */
+ for (len = 0; len < name_len; len++)
+ if (!isalnum(fname[len]))
+ break;
+ f.lf.lfFaceName[len] = 0;
+ f.lf.lfPitchAndFamily = 0; /* default */
+ if (!map_logical_font(hdc, &f)) {
+ win_release_dc(wdev, hdc);
+ return NULL;
+ }
+ }
+ GetTextMetrics(hdc, &f.tm);
+ win_release_dc(wdev, hdc);
+ f.y_offset = (!f.invert_y ? f.tm.tmAscent : f.tm.tmDescent);
+ wf = gs_alloc_struct(mem, win_xfont, &st_win_xfont, "win_lookup_font");
+ if (wf == 0) {
+ DeleteObject(f.hFont);
+ return NULL;
+ }
+ f.common.procs = &win_xfont_procs;
+ f.dev = wdev;
+ *wf = f;
+ return (gx_xfont *) wf;
+}
+/* Map the logical font, and see if the result is satisfactory. */
+static int /*bool */
+map_logical_font(HDC hdc, win_xfont * xf)
+{
+ char szFaceName[LF_FACESIZE];
+
+ xf->hFont = CreateFontIndirect(&xf->lf);
+ if (xf->hFont == 0)
+ return 0;
+ /* Check the face name */
+ SelectObject(hdc, xf->hFont);
+ GetTextFace(hdc, sizeof(szFaceName), szFaceName);
+ if (!strncmp(xf->lf.lfFaceName, szFaceName, strlen(xf->lf.lfFaceName)))
+ return 1;
+ DeleteObject(xf->hFont);
+ xf->hFont = 0;
+ return 0;
+}
+
+/* Convert a character name or index to an xglyph code. */
+gx_xglyph
+win_char_xglyph(gx_xfont * xf, gs_char chr, int encoding_index,
+ gs_glyph glyph, const gs_const_string *glyph_name)
+{
+ if (chr == gs_no_char)
+ return gx_no_xglyph; /* can't look up names yet */
+ if (encoding_index == 0) { /* Map StandardEncoding to ISOLatin1Encoding. */
+ /* We lose a couple of characters that exist in both */
+ /* StandardEncoding and the Windows OEM encoding but not in */
+ /* the ISOLatin1Encoding; we won't worry about this */
+ /* for now. */
+ chr = gs_map_std_to_iso[chr];
+ encoding_index = 1;
+ }
+ if (wxf->hFont == NULL) { /* TEXTMETRICS not filled in yet */
+ HDC hdc = win_get_dc(wxf->dev);
+ int code;
+
+ if (hdc == NULL)
+ return gx_no_xglyph;
+ code = win_select_font(hdc, wxf);
+ win_release_dc(wxf->dev, hdc);
+ if (code < 0)
+ return gx_no_xglyph;
+ }
+ switch (wxf->tm.tmCharSet) {
+ case ANSI_CHARSET:
+ if (encoding_index == 1 && (chr < 0x7f || chr > 0x9f ||
+ chr == 0x91 || chr == 0x92)
+ )
+ break;
+ return gx_no_xglyph;
+ case OEM_CHARSET:
+ switch (encoding_index) {
+ case 1: /* ISOLatin1 */
+ chr = gs_map_iso_to_oem[chr];
+ break;
+ case 2: /* Symbol */
+ chr = gs_map_symbol_to_oem[chr];
+ break;
+ default:
+ return gx_no_xglyph;
+ }
+ break;
+ default:
+ return gx_no_xglyph;
+ }
+ return (chr != 0 && chr >= wxf->tm.tmFirstChar &&
+ chr <= wxf->tm.tmLastChar ?
+ (gx_xglyph) chr : gx_no_xglyph);
+}
+
+/* Get the metrics for a character. */
+int
+win_char_metrics(gx_xfont * xf, gx_xglyph xg, int wmode,
+ gs_point * pwidth, gs_int_rect * pbbox)
+{
+ int code;
+ HDC hdc;
+ char chr = (char)xg;
+
+ if (wmode != 0)
+ return gs_error_undefined;
+ hdc = win_get_dc(wxf->dev);
+ if (hdc == NULL)
+ return gs_error_limitcheck;
+ if ((code = win_select_font(hdc, wxf)) < 0) {
+ win_release_dc(wxf->dev, hdc);
+ return code;
+ }
+#ifdef __WIN32__
+ {
+ SIZE sz;
+
+ GetTextExtentPoint(hdc, &chr, 1, &sz);
+ pwidth->x = sz.cx;
+ }
+#else
+ {
+ DWORD extent;
+
+ extent = GetTextExtent(hdc, &chr, 1);
+ pwidth->x = LOWORD(extent);
+ }
+#endif
+ win_release_dc(wxf->dev, hdc);
+ pwidth->y = 0;
+ pbbox->p.x = 0;
+ pbbox->q.x = (int)pwidth->x;
+ if (wxf->invert_y) {
+ pbbox->p.y = -wxf->tm.tmDescent;
+ pbbox->q.y = wxf->tm.tmAscent;
+ } else {
+ pbbox->p.y = -wxf->tm.tmAscent;
+ pbbox->q.y = wxf->tm.tmDescent;
+ }
+ return 0;
+}
+
+/* Render a character. */
+int
+win_render_char(gx_xfont * xf, gx_xglyph xg, gx_device * dev,
+ int xo, int yo, gx_color_index color, int required)
+{
+ char chr = (char)xg;
+ int code;
+
+#ifdef NOTUSED /* we don't own any windows so we can no longer do this */
+ if (dev->dname == gs_mswin_device.dname &&
+ wdev->hdctext != NULL && !wxf->invert_y
+ ) { /* Display the character directly */
+ HDC hdc = wdev->hdctext;
+ PALETTEENTRY *pal = &wdev->limgpalette->palPalEntry[color];
+
+ if ((code = win_select_font(hdc, wxf)) < 0)
+ return code;
+ SetTextColor(hdc, RGB(pal->peRed, pal->peGreen, pal->peBlue));
+ SetBkMode(hdc, TRANSPARENT);
+ TextOut(hdc, xo, yo - wxf->y_offset, &chr, 1);
+ } else
+#endif
+ if (!required)
+ code = -1; /* too hard */
+ else { /* Display on an intermediate bitmap, then copy the bits. */
+ gs_point wxy;
+ gs_int_rect bbox;
+ int w, h, wbm, raster;
+ gx_device_win *fdev = wxf->dev;
+ HBITMAP hbm;
+ byte *bits;
+
+ code = (*xf->common.procs->char_metrics) (xf, xg, 0,
+ &wxy, &bbox);
+ if (code < 0)
+ return code;
+ w = bbox.q.x - bbox.p.x;
+ h = bbox.q.y - bbox.p.y;
+ wbm = ROUND_UP(w, align_bitmap_mod * 8);
+ raster = wbm >> 3;
+ bits = gs_malloc(dev->memory, h, raster, "win_render_char");
+ if (bits == 0)
+ return gs_error_limitcheck;
+ hbm = CreateBitmap(wbm, h, 1, 1, NULL);
+ if (hbm == NULL) {
+ code = gs_error_limitcheck;
+ } else {
+ HDC hdcwin = win_get_dc(fdev);
+ HDC hdcbit = CreateCompatibleDC(hdcwin);
+
+ dev_proc_copy_mono((*copy_mono)) =
+ dev_proc(dev, copy_mono);
+ int y = yo - wxf->y_offset;
+
+ SetMapMode(hdcbit, GetMapMode(hdcwin));
+ win_select_font(hdcbit, wxf);
+ SelectObject(hdcbit, hbm);
+ PatBlt(hdcbit, 0, 0, wbm, h, rop_write_0s);
+ SetTextColor(hdcbit, 0xffffffL); /* 1 */
+ SetBkMode(hdcbit, TRANSPARENT);
+ TextOut(hdcbit, 0, 0, &chr, 1);
+ GetBitmapBits(hbm, (DWORD) raster * h, bits);
+ DeleteDC(hdcbit);
+ win_release_dc(fdev, hdcwin);
+ DeleteObject(hbm);
+ if (!wxf->invert_y)
+ code = (*copy_mono) (dev, bits, 0,
+ raster, gx_no_bitmap_id,
+ xo, y, w, h,
+ gx_no_color_index, color);
+ else { /* Copy scan lines in reverse order. */
+ int i;
+
+ y += h - 1;
+ for (i = 0; i < h; i++)
+ (*copy_mono) (dev, bits + i * raster,
+ 0, raster, gx_no_bitmap_id,
+ xo, y - i, w, 1,
+ gx_no_color_index, color);
+ }
+ }
+ gs_free(dev->memory, bits, h, raster, "win_render_char");
+ }
+ return (code < 0 ? code : 0);
+}
+
+/* Release an xfont. */
+static int
+win_release(gx_xfont * xf, gs_memory_t * mem)
+{
+ if (wxf->hFont) {
+ DeleteObject(wxf->hFont);
+ wxf->hFont = 0;
+ }
+ if (mem != NULL)
+ gs_free_object(mem, xf, "win_release");
+ return 0;
+}
+
+/* ------ Font utilities ------ */
+
+#undef wdev
+#undef wxf
+
+/* Get a DC for the font's device. */
+static HDC near
+win_get_dc(gx_device_win * wdev)
+{
+ /* Since we don't have a window, use the desktop */
+ /* Don't draw into it! */
+ return GetDC(HWND_DESKTOP);
+}
+
+/* Release a DC for the font's device. */
+static void near
+win_release_dc(gx_device_win * wdev, HDC hdc)
+{
+ ReleaseDC(HWND_DESKTOP, hdc);
+}
+
+/* Make an xfont current, possibly remapping it from a logical font. */
+static int
+win_select_font(HDC hdc, win_xfont * wxf)
+{
+ HFONT hFont = wxf->hFont;
+
+ if (hFont == NULL) { /* The font was released to free up resources. */
+ /* Re-acquire it now. */
+ wxf->hFont = CreateFontIndirect(&wxf->lf);
+ if (wxf->hFont == NULL)
+ return gs_error_limitcheck;
+ }
+ SelectObject(hdc, wxf->hFont);
+ return 0;
+}
diff --git a/devices/gdevn533.c b/devices/gdevn533.c
new file mode 100644
index 000000000..f8d145e7e
--- /dev/null
+++ b/devices/gdevn533.c
@@ -0,0 +1,207 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+/* Sony NWP-533 driver for GhostScript */
+#include "gdevprn.h"
+#define prn_dev ((gx_device_printer *)dev) /* needed in 5.31 et seq */
+#include <sys/ioctl.h>
+#include <newsiop/lbp.h>
+
+/***
+ *** Note: this driver was contributed by a user, Tero Kivinen:
+ *** please contact kivinen@joker.cs.hut.fi if you have questions.
+ ***/
+
+#define A4_PAPER 1
+
+#ifdef A4_PAPER
+#define PAPER_XDOTS A4_XDOTS
+#define PAPER_YDOTS A4_YDOTS
+#else
+#define PAPER_XDOTS B4_XDOTS
+#define PAPER_YDOTS B4_YDOTS
+#endif
+
+#ifndef TRUE
+#define TRUE 1
+#endif
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+/* The device descriptor */
+static dev_proc_open_device(nwp533_open);
+static dev_proc_print_page(nwp533_print_page);
+static dev_proc_close_device(nwp533_close);
+/* Since the print_page doesn't alter the device, this device can print in the background */
+static gx_device_procs nwp533_procs =
+ prn_procs(nwp533_open, gdev_prn_bg_output_page_seekable, nwp533_close);
+
+const gx_device_printer far_data gs_nwp533_device =
+ prn_device(nwp533_procs, "nwp533",
+ PAPER_XDOTS * 10.0 / DPI, /* width_10ths */
+ PAPER_YDOTS * 10.0 / DPI, /* height_10ths */
+ DPI, /* x_dpi */
+ DPI, /* y_dpi */
+ 0,0,0,0, /* margins */
+ 1, nwp533_print_page);
+
+/* return True if should retry - False if should quit */
+static int
+analyze_error(int printer_file)
+{
+ struct lbp_stat status;
+ char *detail = NULL, *old_detail = NULL;
+ int waiting = TRUE;
+ int retry_after_return = TRUE;
+
+ if(ioctl(printer_file, LBIOCRESET, 0) < 0)
+ {
+ perror("ioctl(LBIOCRESET)");
+ return FALSE;
+ }
+ if (ioctl(printer_file, LBIOCSTATUS, &status) < 0)
+ {
+ perror("ioctl(LBIOCSTATUS)");
+ return FALSE;
+ }
+
+ do
+ {
+ /* Is there an error */
+ if(status.stat[0] & (ST0_CALL | ST0_REPRINT_REQ | ST0_WAIT | ST0_PAUSE))
+ {
+ if(status.stat[1] & ST1_NO_CARTRIGE)/* mispelled? */
+ detail = "No cartridge - waiting";
+ else if(status.stat[1] & ST1_NO_PAPER)
+ detail = "Out of paper - waiting";
+ else if(status.stat[1] & ST1_JAM)
+ detail = "Paper jam - waiting";
+ else if(status.stat[1] & ST1_OPEN)
+ detail = "Door open - waiting";
+ else if(status.stat[1] & ST1_TEST)
+ detail = "Test printing - waiting";
+ else {
+ waiting = FALSE;
+ retry_after_return = FALSE;
+
+ if(status.stat[2] & ST2_FIXER)
+ detail = "Fixer trouble - quiting";
+ else if(status.stat[2] & ST2_SCANNER)
+ detail = "Scanner trouble - quiting";
+ else if(status.stat[2] & ST2_MOTOR)
+ detail = "Scanner motor trouble - quiting";
+ else if(status.stat[5] & ST5_NO_TONER)
+ detail = "No toner - quiting";
+ }
+ }
+ else
+ {
+ waiting = FALSE;
+ }
+ if(detail != NULL && detail != old_detail)
+ {
+ perror(detail);
+ old_detail = detail;
+ }
+ if(waiting)
+ {
+ ioctl(1, LBIOCRESET, 0);
+ sleep(5);
+ ioctl(1, LBIOCSTATUS, &status);
+ }
+ }
+ while(waiting);
+ return retry_after_return;
+}
+
+static int
+nwp533_open(gx_device *dev)
+{
+ gx_device_printer *pdev = (gx_device_printer *) dev;
+
+ if (pdev->fname[0] == '\0')
+ {
+ strcpy(pdev->fname, "/dev/lbp");
+ }
+ return gdev_prn_open(dev);
+}
+
+static int
+nwp533_close(gx_device *dev)
+{
+ if (((gx_device_printer *) dev)->file != NULL)
+ {
+ int printer_file;
+
+ printer_file = fileno(((gx_device_printer *) dev)->file);
+ restart2:
+ if(ioctl(printer_file, LBIOCSTOP, 0) < 0)
+ {
+ if(analyze_error(printer_file))
+ goto restart2;
+ perror("Waiting for device");
+ return_error(gs_error_ioerror);
+ }
+ }
+ return gdev_prn_close(dev);
+}
+
+/* Send the page to the printer. */
+static int
+nwp533_print_page(gx_device_printer *dev, FILE *prn_stream)
+{
+ int lnum;
+ int line_size = gdev_mem_bytes_per_scan_line(dev);
+ byte *in;
+ int printer_file;
+ printer_file = fileno(prn_stream);
+
+ if (line_size % 4 != 0)
+ {
+ line_size += 4 - (line_size % 4);
+ }
+ in = (byte *) gs_malloc(dev->memory, line_size, 1, "nwp533_output_page(in)");
+ restart:
+ if(ioctl(printer_file, LBIOCSTOP, 0) < 0)
+ {
+ if(analyze_error(printer_file))
+ goto restart;
+ perror("Waiting for device");
+ return_error(gs_error_ioerror);
+ }
+ lseek(printer_file, 0, 0);
+
+ for ( lnum = 0; lnum < dev->height; lnum++)
+ {
+ gdev_prn_copy_scan_lines(prn_dev, lnum, in, line_size);
+ if(write(printer_file, in, line_size) != line_size)
+ {
+ perror("Writting to output");
+ return_error(gs_error_ioerror);
+ }
+ }
+ retry:
+ if(ioctl(printer_file, LBIOCSTART, 0) < 0)
+ {
+ if(analyze_error(printer_file))
+ goto retry;
+ perror("Starting print");
+ return_error(gs_error_ioerror);
+ }
+ gs_free(dev->memory, in, line_size, 1, "nwp533_output_page(in)");
+
+ return 0;
+}
diff --git a/devices/gdevo182.c b/devices/gdevo182.c
new file mode 100644
index 000000000..f2475fb24
--- /dev/null
+++ b/devices/gdevo182.c
@@ -0,0 +1,307 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+/* Okidata Microline 182 printer driver */
+
+/* Contributed by Maarten Koning (smeg@bnr.ca) April 4, 1993 */
+
+/****************************************************************
+
+I use this driver from Unix with the following aliases:
+
+alias psp "gs -q -sDEVICE=oki182 -sOutputFile=\|lpr - <\!*"
+alias psphigh "gs -q -sDEVICE=oki182 -r144 -sOutputFile=\|lpr - <\!*"
+
+ps. I have my printer DIP switches set to the following (as viewed
+ while standing in front of your printer looking down into the
+ config access hatch located at the top of your printer
+ in the centre back).
+
+Upper Upper Bottom
+Left Right (at right)
+
+ x x x
+ x x x
+ x x x
+x x x
+ x x x
+ x x x
+x x x
+ x x x
+
+The upper DIP switches are on a SuperSpeed Serial
+card that will do 19200 baud. I have it set at 9600
+baud since that seems sufficient to keep the printer
+busy.
+
+The important thing is to be in 8-bit mode so that
+the graphics data can't match any Okidata commands
+(This driver sets the high bit of graphics data to 1).
+
+****************************************************************/
+
+#include "gdevprn.h"
+
+/*
+ * Available resolutions are 72x72 or 144x144;
+ * (144x72) would be possible to do also, but I didn't bother)
+ */
+
+/* The device descriptor */
+
+static dev_proc_print_page(oki_print_page);
+
+const gx_device_printer far_data gs_oki182_device =
+ prn_device(prn_bg_procs, "oki182", /* The print_page proc is compatible with allowing bg printing */
+ 80, /* width_10ths, 8.0" */
+ 110, /* height_10ths, 11" */
+ 72, /* x_dpi */
+ 72, /* y_dpi */
+ 0, 0, 0, 0, /* margins */
+ 1, oki_print_page);
+
+/* ------ internal routines ------ */
+
+/* out is a pointer to an array of 7 scan lines,
+ lineSize is the number of bytes between a pixel and
+ the pixel directly beneath it.
+ scanBits is the number of bits in each scan line
+ out is a pointer to an array of column data, which
+ is how the Okidata wants the graphics image.
+
+ each column of graphics data is 7 bits high and
+ is encoded in a byte - highest pixel in the column
+ is the lowest bit in the byte. The upper bit of the
+ byte is set so that the okidata doesn't mistake
+ graphic image data for graphic commands.
+*/
+
+static void
+oki_transpose(byte *in, byte *out, int scanBits, register int lineSize)
+{
+ register int bitMask = 0x80;
+ register byte *inPtr;
+ register byte outByte;
+
+ while (scanBits-- > 0) {
+
+ inPtr = in;
+
+ if (*inPtr & bitMask)
+ outByte = 0x81;
+ else
+ outByte = 0x80;
+ if (*(inPtr += lineSize) & bitMask)
+ outByte += 0x02;
+ if (*(inPtr += lineSize) & bitMask)
+ outByte += 0x04;
+ if (*(inPtr += lineSize) & bitMask)
+ outByte += 0x08;
+ if (*(inPtr += lineSize) & bitMask)
+ outByte += 0x10;
+ if (*(inPtr += lineSize) & bitMask)
+ outByte += 0x20;
+ if (*(inPtr += lineSize) & bitMask)
+ outByte += 0x40;
+
+ *out++ = outByte;
+
+ if ((bitMask >>= 1) == 0) {
+ bitMask = 0x80;
+ in ++;
+ }
+ }
+}
+
+/* This routine tries to compress a sequence of okidata
+ graphic bytes by trimming off leading and trailing
+ zeros. Trailing zeros can be thrown away and leading
+ zeros can be replaced with a much smaller number of spaces.
+
+ 'in' is a pointer to the graphic bytes to be compressed.
+ origWidth is the number of bytes pointed to by 'in'.
+ highRes is non-zero when 144x144 mode is being used.
+
+ numSpaces is set to the number of spaces that should
+ be printed before the compressed image. newWidth is
+ the new number of bytes that the return value of this
+ function points to.
+
+ xxx - A future enhancement would be to replace long sequences
+ of embedded zeros with exit.graphics-<n> spaces-enter.graphics
+*/
+static byte *
+oki_compress(byte *in, int origWidth, int highRes,
+ int *numSpaces, int *newWidth)
+{
+ int spaces = 0;
+ int columns_per_space = 6;
+
+ byte *in_end = in + origWidth;
+
+ /* remove trailing zeros (which are realy 0x80's) */
+ while (in_end > in && in_end[-1] == 0x80)
+ in_end --;
+
+ if (highRes)
+ columns_per_space = 12;
+
+ /* remove leading zeros that can be replaced by spaces */
+ while(in < in_end && in[0] == 0x80 && memcmp((char *)in,
+ (char *)in + 1, columns_per_space - 1) == 0) {
+ spaces++;
+ in += columns_per_space;
+ }
+
+ *numSpaces = spaces;
+
+ /* just in case we compressed this line out of existance */
+ if (in_end > in)
+ *newWidth = in_end - in;
+ else
+ *newWidth = 0;
+
+ return(in);
+}
+
+/* Send the page to the printer. */
+
+static int
+oki_print_page(gx_device_printer *pdev, FILE *prn_stream)
+{
+ int highRes = pdev->y_pixels_per_inch > 100;
+ int bits_per_column = 7;
+ int i, spaces, width;
+ int lcnt;
+
+ int line_size = gdev_prn_raster((gx_device_printer *)pdev);
+
+ byte *in = (byte *)gs_malloc(pdev->memory, 16, line_size, "oki_print_page(in)");
+
+ byte *out1 = (byte *)gs_malloc(pdev->memory, 8, line_size, "oki_print_page(out1)");
+ byte *out2 = (byte *)gs_malloc(pdev->memory, 8, line_size, "oki_print_page(out2)");
+
+ byte *out3;
+
+ int lnum = 0;
+ int skip = 0;
+ int code = 0;
+
+ if ( in == 0 || out1 == 0 || out2 == 0)
+ { code = gs_error_VMerror;
+ gs_note_error(code);
+ goto bail;
+ }
+
+ /* Initialize the printer. */
+ /* CAN; 72x72; left margin = 001; disable skip over perforation */
+ fwrite("\030\034\033%C001\033%S0", 1, 12, prn_stream);
+
+ if (highRes) {
+ fwrite("\033R", 1, 2, prn_stream);
+ bits_per_column = 14;
+ }
+
+ /* Transfer pixels to printer */
+ while ( lnum < pdev->height ) {
+
+ /* Copy 1 scan line and test for all zero. */
+ code = gdev_prn_copy_scan_lines(pdev, lnum, in, line_size);
+ if ( code < 0 )
+ goto xit;
+
+ /* if line is all zero, skip */
+ if ( in[0] == 0 && !memcmp((char *)in, (char *)in + 1,
+ line_size - 1)) {
+ lnum++;
+ if (highRes)
+ skip++;
+ else
+ skip += 2;
+ continue;
+ }
+
+ /* use fine line feed to get to the appropriate position. */
+ while ( skip > 127 ) {
+ fputs("\033%5\177", prn_stream);
+ skip -= 127;
+ }
+ if ( skip )
+ fprintf(prn_stream, "\033%%5%c",
+ (char) (skip & 0xff));
+ skip = 0;
+
+ /* get the rest of the scan lines */
+ code = gdev_prn_copy_scan_lines(pdev, lnum + 1,
+ in + line_size, (bits_per_column - 1) * line_size);
+
+ if ( code < 0 )
+ goto xit;
+
+ lcnt = code + 1; /* since we already grabbed one line */
+
+ if ( lcnt < bits_per_column )
+ memset(in + lcnt * line_size, 0,
+ (bits_per_column - lcnt) * line_size);
+
+ if (highRes) {
+ oki_transpose(in, out1, pdev->width, 2 * line_size);
+ oki_transpose(in + line_size, out2,
+ pdev->width, 2 * line_size);
+ } else
+ oki_transpose(in, out1, pdev->width, line_size);
+
+ out3 = oki_compress(out1, pdev->width, highRes,
+ &spaces, &width);
+
+ for (i=0; i < spaces; i++)
+ putc(' ', prn_stream);
+
+ fwrite("\003", 1, 1, prn_stream);
+ fwrite(out3, 1, width, prn_stream);
+
+ if (highRes) {
+ /* exit graphics; carriage return; 1 bit line feed */
+ fprintf(prn_stream, "\003\002\015\033%%5%c", (char) 1);
+ out3 = oki_compress(out2, pdev->width, highRes,
+ &spaces, &width);
+ for (i=0; i < spaces; i++)
+ putc(' ', prn_stream);
+ fwrite("\003", 1, 1, prn_stream);
+ fwrite(out3, 1, width, prn_stream);
+ fprintf(prn_stream, "\003\002\015\033%%5%c", (char) 13);
+ } else
+ fwrite("\003\016\003\002", 1, 4, prn_stream);
+
+ lnum += bits_per_column;
+ }
+
+ /* Eject the page */
+xit:
+ fputc(014, prn_stream); /* form feed */
+ fflush(prn_stream);
+
+bail:
+ if ( out1 != 0 )
+ gs_free(pdev->memory, (char *)out1, 8, line_size, "oki_print_page(out1)");
+
+ if ( out2 != 0 )
+ gs_free(pdev->memory, (char *)out2, 8, line_size, "oki_print_page(out2)");
+
+ if ( in != 0 )
+ gs_free(pdev->memory, (char *)in, 16, line_size, "oki_print_page(in)");
+
+ return code;
+}
diff --git a/devices/gdevokii.c b/devices/gdevokii.c
new file mode 100644
index 000000000..80e6b9f6a
--- /dev/null
+++ b/devices/gdevokii.c
@@ -0,0 +1,323 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+/*
+ * Okidata IBM compatible dot-matrix printer driver for Ghostscript.
+ *
+ * This device is for the Okidata Microline IBM compatible 9 pin dot
+ * matrix printers. It is derived from the Epson 9 pin printer driver
+ * using the standard 1/72" vertical pin spacing and the 60/120/240
+ * dpi horizontal resolutions. The vertical feed resolution however
+ * is 1/144" and the Okidata implements the standard 1/216" requests
+ * through "scaling":
+ *
+ * (power on)
+ * "\033J\001" (vertical feed 1/216") => Nothing happens
+ * "\033J\001" (vertical feed 1/216") => Advance 1/144"
+ * "\033J\001" (vertical feed 1/216") => Advance 1/144"
+ * "\033J\001" (vertical feed 1/216") => Nothing happens
+ * (and so on)
+ *
+ * The simple minded accounting used here keep track of when the
+ * page actually advances assumes the printer starts in a "power on"
+ * state.
+ *
+ * Supported resolutions are:
+ *
+ * 60x72 60x144
+ * 120x72 120x144
+ * 240x72 240x144
+ *
+ */
+#include "gdevprn.h"
+
+/*
+ * Valid values for X_DPI:
+ *
+ * 60, 120, 240
+ *
+ * The value specified at compile time is the default value used if the
+ * user does not specify a resolution at runtime.
+ */
+
+#ifndef X_DPI
+# define X_DPI 120
+#endif
+
+/*
+ * Valid values for Y_DPI:
+ *
+ * 72, 144
+ *
+ * The value specified at compile time is the default value used if the
+ * user does not specify a resolution at runtime.
+ */
+
+#ifndef Y_DPI
+# define Y_DPI 72
+#endif
+
+/* The device descriptor */
+static dev_proc_print_page(okiibm_print_page);
+
+/* Okidata IBM device */
+const gx_device_printer far_data gs_okiibm_device =
+ prn_device(prn_bg_procs, "okiibm", /* The print_page proc is compatible with allowing bg printing */
+ DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
+ X_DPI, Y_DPI,
+ 0.25, 0.0, 0.25, 0.0, /* margins */
+ 1, okiibm_print_page);
+
+/* ------ Internal routines ------ */
+
+/* Forward references */
+static void okiibm_output_run(byte *, int, int, char, FILE *, int);
+
+/* Send the page to the printer. */
+static int
+okiibm_print_page1(gx_device_printer *pdev, FILE *prn_stream, int y_9pin_high,
+ const char *init_string, int init_length,
+ const char *end_string, int end_length)
+{
+ static const char graphics_modes_9[5] =
+ {
+ -1, 0 /*60*/, 1 /*120*/, -1, 3 /*240*/
+ };
+
+ int in_y_mult = (y_9pin_high ? 2 : 1);
+ int line_size = gdev_mem_bytes_per_scan_line((gx_device *)pdev);
+ /* Note that in_size is a multiple of 8. */
+ int in_size = line_size * (8 * in_y_mult);
+ byte *buf1 = (byte *)gs_malloc(pdev->memory, in_size, 1, "okiibm_print_page(buf1)");
+ byte *buf2 = (byte *)gs_malloc(pdev->memory, in_size, 1, "okiibm_print_page(buf2)");
+ byte *in = buf1;
+ byte *out = buf2;
+ int out_y_mult = 1;
+ int x_dpi = pdev->x_pixels_per_inch;
+ char start_graphics = graphics_modes_9[x_dpi / 60];
+ int first_pass = (start_graphics == 3 ? 1 : 0);
+ int last_pass = first_pass * 2;
+ int y_passes = (y_9pin_high ? 2 : 1);
+ int skip = 0, lnum = 0, pass, ypass;
+ int y_step = 0;
+
+ /* Check allocations */
+ if ( buf1 == 0 || buf2 == 0 )
+ { if ( buf1 )
+ gs_free(pdev->memory, (char *)buf1, in_size, 1, "okiibm_print_page(buf1)");
+ if ( buf2 )
+ gs_free(pdev->memory, (char *)buf2, in_size, 1, "okiibm_print_page(buf2)");
+ return_error(gs_error_VMerror);
+ }
+
+ /* Initialize the printer. */
+ fwrite(init_string, 1, init_length, prn_stream);
+
+ /* Print lines of graphics */
+ while ( lnum < pdev->height )
+ {
+ byte *in_data;
+ byte *inp;
+ byte *in_end;
+ byte *out_end;
+ int lcnt;
+
+ /* Copy 1 scan line and test for all zero. */
+ gdev_prn_get_bits(pdev, lnum, in, &in_data);
+ if ( in_data[0] == 0 &&
+ !memcmp((char *)in_data, (char *)in_data + 1, line_size - 1)
+ )
+ {
+ lnum++;
+ skip += 2 / in_y_mult;
+ continue;
+ }
+
+ /*
+ * Vertical tab to the appropriate position.
+ * The skip count is in 1/144" steps. If total
+ * vertical request is not a multiple od 1/72"
+ * we need to make sure the page is actually
+ * going to advance.
+ */
+ if ( skip & 1 )
+ {
+ int n = 1 + (y_step == 0 ? 1 : 0);
+ fprintf(prn_stream, "\033J%c", n);
+ y_step = (y_step + n) % 3;
+ skip -= 1;
+ }
+ skip = skip / 2 * 3;
+ while ( skip > 255 )
+ {
+ fputs("\033J\377", prn_stream);
+ skip -= 255;
+ }
+ if ( skip )
+ {
+ fprintf(prn_stream, "\033J%c", skip);
+ }
+
+ /* Copy the the scan lines. */
+ lcnt = gdev_prn_copy_scan_lines(pdev, lnum, in, in_size);
+ if ( lcnt < 8 * in_y_mult )
+ { /* Pad with lines of zeros. */
+ memset(in + lcnt * line_size, 0,
+ in_size - lcnt * line_size);
+ }
+
+ if ( y_9pin_high )
+ { /* Shuffle the scan lines */
+ byte *p;
+ int i;
+ static const char index[] =
+ { 0, 2, 4, 6, 8, 10, 12, 14,
+ 1, 3, 5, 7, 9, 11, 13, 15
+ };
+ for ( i = 0; i < 16; i++ )
+ {
+ memcpy( out + (i * line_size),
+ in + (index[i] * line_size),
+ line_size);
+ }
+ p = in;
+ in = out;
+ out = p;
+ }
+
+ for ( ypass = 0; ypass < y_passes; ypass++ )
+ {
+ for ( pass = first_pass; pass <= last_pass; pass++ )
+ {
+ /* We have to 'transpose' blocks of 8 pixels x 8 lines, */
+ /* because that's how the printer wants the data. */
+
+ if ( pass == first_pass )
+ {
+ out_end = out;
+ inp = in;
+ in_end = inp + line_size;
+
+ for ( ; inp < in_end; inp++, out_end += 8 )
+ {
+ gdev_prn_transpose_8x8(inp + (ypass * 8 * line_size),
+ line_size, out_end, 1);
+ }
+ /* Remove trailing 0s. */
+ while ( out_end > out && out_end[-1] == 0 )
+ {
+ out_end--;
+ }
+ }
+
+ /* Transfer whatever is left and print. */
+ if ( out_end > out )
+ {
+ okiibm_output_run(out, (int)(out_end - out),
+ out_y_mult, start_graphics,
+ prn_stream, pass);
+ }
+ fputc('\r', prn_stream);
+ }
+ if ( ypass < y_passes - 1 )
+ {
+ int n = 1 + (y_step == 0 ? 1 : 0);
+ fprintf(prn_stream, "\033J%c", n);
+ y_step = (y_step + n) % 3;
+ }
+ }
+ skip = 16 - y_passes + 1; /* no skip on last Y pass */
+ lnum += 8 * in_y_mult;
+ }
+
+ /* Reinitialize the printer. */
+ fwrite(end_string, 1, end_length, prn_stream);
+ fflush(prn_stream);
+
+ gs_free(pdev->memory, (char *)buf2, in_size, 1, "okiibm_print_page(buf2)");
+ gs_free(pdev->memory, (char *)buf1, in_size, 1, "okiibm_print_page(buf1)");
+ return 0;
+}
+
+/* Output a single graphics command. */
+/* pass=0 for all columns, 1 for even columns, 2 for odd columns. */
+static void
+okiibm_output_run(byte *data, int count, int y_mult,
+ char start_graphics, FILE *prn_stream, int pass)
+{
+ int xcount = count / y_mult;
+
+ fputc(033, prn_stream);
+ fputc((int)("KLYZ"[(int)start_graphics]), prn_stream);
+ fputc(xcount & 0xff, prn_stream);
+ fputc(xcount >> 8, prn_stream);
+ if ( !pass )
+ {
+ fwrite(data, 1, count, prn_stream);
+ }
+ else
+ {
+ /* Only write every other column of y_mult bytes. */
+ int which = pass;
+ register byte *dp = data;
+ register int i, j;
+
+ for ( i = 0; i < xcount; i++, which++ )
+ {
+ for ( j = 0; j < y_mult; j++, dp++ )
+ {
+ putc(((which & 1) ? *dp : 0), prn_stream);
+ }
+ }
+ }
+}
+
+/* The print_page procedures are here, to avoid a forward reference. */
+
+static const char okiibm_init_string[] = { 0x18 };
+static const char okiibm_end_string[] = { 0x0c };
+static const char okiibm_one_direct[] = { 0x1b, 0x55, 0x01 };
+static const char okiibm_two_direct[] = { 0x1b, 0x55, 0x00 };
+
+static int
+okiibm_print_page(gx_device_printer *pdev, FILE *prn_stream)
+{
+ char init_string[16], end_string[16];
+ int init_length, end_length;
+
+ init_length = sizeof(okiibm_init_string);
+ memcpy(init_string, okiibm_init_string, init_length);
+
+ end_length = sizeof(okiibm_end_string);
+ memcpy(end_string, okiibm_end_string, end_length);
+
+ if ( pdev->y_pixels_per_inch > 72 &&
+ pdev->x_pixels_per_inch > 60 )
+ {
+ /* Unidirectional printing for the higher resolutions. */
+ memcpy( init_string + init_length, okiibm_one_direct,
+ sizeof(okiibm_one_direct) );
+ init_length += sizeof(okiibm_one_direct);
+
+ memcpy( end_string + end_length, okiibm_two_direct,
+ sizeof(okiibm_two_direct) );
+ end_length += sizeof(okiibm_two_direct);
+ }
+
+ return okiibm_print_page1( pdev, prn_stream,
+ pdev->y_pixels_per_inch > 72 ? 1 : 0,
+ init_string, init_length,
+ end_string, end_length );
+}
diff --git a/devices/gdevos2p.c b/devices/gdevos2p.c
new file mode 100644
index 000000000..106f8857e
--- /dev/null
+++ b/devices/gdevos2p.c
@@ -0,0 +1,671 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+/*
+ * OS/2 printer device
+ *
+ * By Russell Lang, derived from mswinpr2 device by Russell Lang and
+ * L. Peter Deutsch, Aladdin Enterprises.
+ *
+ * Bug fixed by Pierre Arnaud 2000-03-20 (os2prn_set_bpp did not set anti_alias)
+ */
+
+/* This device works when GS is a DLL loaded by a PM program */
+/* It does not work when GS is a text mode EXE */
+
+/* This driver uses the printer default size and resolution and
+ * ignores page size and resolution set using -gWIDTHxHEIGHT and
+ * -rXxY. You must still set the correct PageSize to get the
+ * correct clipping path. If you don't specify a value for
+ * -dBitsPerPixel, the depth will be obtained from the printer
+ * device context.
+ */
+
+#define INCL_DOS
+#define INCL_DOSERRORS
+#define INCL_DEV
+#define INCL_GPIBITMAPS
+#define INCL_SPL
+#define INCL_SPLDOSPRINT
+#define INCL_SPLERRORS
+
+#include <os2.h>
+
+#include "gdevprn.h"
+#include "gdevpccm.h"
+#include "gp.h"
+#include "gscdefs.h" /* for gs_product */
+
+extern HWND hwndtext; /* in gp_os2.h */
+
+typedef struct tagOS2QL {
+ PRQINFO3 *prq; /* queue list */
+ ULONG len; /* bytes in queue list (for gs_free) */
+ int defqueue; /* default queue */
+ int nqueues; /* number of queues */
+} OS2QL;
+
+#ifndef NERR_BufTooSmall
+#define NERR_BufTooSmall 2123 /* For SplEnumQueue */
+#endif
+
+/* Make sure we cast to the correct structure type. */
+typedef struct gx_device_os2prn_s gx_device_os2prn;
+
+#undef opdev
+#define opdev ((gx_device_os2prn *)dev)
+
+/* Device procedures */
+
+/* See gxdevice.h for the definitions of the procedures. */
+static dev_proc_open_device(os2prn_open);
+static dev_proc_close_device(os2prn_close);
+static dev_proc_print_page(os2prn_print_page);
+static dev_proc_map_rgb_color(os2prn_map_rgb_color);
+static dev_proc_map_color_rgb(os2prn_map_color_rgb);
+static dev_proc_put_params(os2prn_put_params);
+static dev_proc_get_params(os2prn_get_params);
+
+static void os2prn_set_bpp(gx_device * dev, int depth);
+static int os2prn_get_queue_list(gs_memory_t *mem, OS2QL * ql);
+static void os2prn_free_queue_list(gs_memory_t *mem, OS2QL * ql);
+int os2prn_get_printer(OS2QL * ql);
+
+static gx_device_procs os2prn_procs =
+prn_color_params_procs(os2prn_open, gdev_prn_output_page, os2prn_close,
+ os2prn_map_rgb_color, os2prn_map_color_rgb,
+ os2prn_get_params, os2prn_put_params);
+
+/* The device descriptor */
+struct gx_device_os2prn_s {
+ gx_device_common;
+ gx_prn_device_common;
+ HAB hab;
+ HDC hdc;
+ HPS hps;
+ char queue_name[256]; /* OS/2 printer queue name */
+ int newframe; /* false before first page */
+ OS2QL ql;
+ int clipbox[4]; /* llx, lly, urx, ury in pixels */
+ HDC hdcMem;
+ HPS hpsMem;
+};
+
+gx_device_os2prn far_data gs_os2prn_device =
+{
+ prn_device_std_body(gx_device_os2prn, os2prn_procs, "os2prn",
+ DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS, 72, 72,
+ 0, 0, 0, 0,
+ 0, os2prn_print_page), /* depth = 0 */
+ 0, /* hab */
+ 0, /* hdc */
+ 0, /* hps */
+ "" /* queue_name */
+};
+
+/* Open the os2prn driver */
+static int
+os2prn_open(gx_device * dev)
+{
+ int code;
+ PTIB pptib;
+ PPIB pppib;
+ DEVOPENSTRUC dop;
+ ULONG cbBuf;
+ ULONG cbNeeded;
+ APIRET rc;
+ PBYTE pbuf;
+ char *p;
+ SIZEL sizlPage;
+ LONG caps[2];
+ HCINFO hcinfo;
+ LONG nforms;
+ float m[4];
+ int depth;
+ FILE *pfile;
+ int i;
+ char *prefix = "\\\\spool\\"; /* 8 characters long */
+
+ PRQINFO3 *pprq;
+ gx_device_os2prn *oprn;
+
+ oprn = opdev;
+
+ if (DosGetInfoBlocks(&pptib, &pppib)) {
+ errprintf(dev->memory, "\nos2prn_open: Couldn't get pid\n");
+ return gs_error_limitcheck;
+ }
+ if (pppib->pib_ultype != 3) {
+ /* if caller is not PM app */
+ errprintf(dev->memory, "os2prn device can only be used from a PM application\n");
+ return gs_error_limitcheck;
+ }
+ opdev->hab = WinQueryAnchorBlock(hwndtext);
+ opdev->newframe = 0;
+
+ if (os2prn_get_queue_list(dev->memory, &opdev->ql))
+ return gs_error_limitcheck;
+
+ if (opdev->queue_name[0] == '\0') {
+ /* obtain printer name from filename */
+ p = opdev->fname;
+ for (i = 0; i < 8; i++) {
+ if (prefix[i] == '\\') {
+ if ((*p != '\\') && (*p != '/'))
+ break;
+ } else if (tolower(*p) != prefix[i])
+ break;
+ p++;
+ }
+ if (i == 8 && (strlen(p) != 0))
+ strcpy(opdev->queue_name, p);
+ }
+ pprq = NULL;
+ if (opdev->queue_name[0] != '\0') {
+ for (i = 0; i < opdev->ql.nqueues; i++) {
+ if (strcmp(opdev->ql.prq[i].pszName, opdev->queue_name) == 0) {
+ pprq = &(opdev->ql.prq[i]);
+ break;
+ }
+ }
+ } else {
+ /* use default queue */
+ pprq = &(opdev->ql.prq[opdev->ql.defqueue]);
+ }
+ if (pprq == (PRQINFO3 *) NULL) {
+ errprintf(opdev->memory, "Invalid os2prn queue name -sOS2QUEUE=\042%s\042\n", opdev->queue_name);
+ errprintf(opdev->memory, "Valid device names are:\n");
+ for (i = 0; i < opdev->ql.nqueues; i++) {
+ errprintf(opdev->memory, " -sOS2QUEUE=\042%s\042\n", opdev->ql.prq[i].pszName);
+ }
+ return gs_error_rangecheck;
+ }
+ /* open printer device */
+ memset(&dop, 0, sizeof(dop));
+ dop.pszLogAddress = pprq->pszName; /* queue name */
+ p = strchr(pprq->pszDriverName, '.');
+ if (p != (char *)NULL)
+ *p = '\0';
+ dop.pszDriverName = pprq->pszDriverName;
+ dop.pszDataType = "PM_Q_STD";
+ dop.pdriv = pprq->pDriverData;
+ opdev->hdc = DevOpenDC(opdev->hab, OD_QUEUED, "*", 9L, (PDEVOPENDATA) & dop, (HDC) NULL);
+ if (opdev->hdc == DEV_ERROR) {
+ ERRORID eid = WinGetLastError(opdev->hab);
+
+ errprintf(opdev->memory, "DevOpenDC for printer error 0x%x\n", eid);
+ return gs_error_limitcheck;
+ }
+ os2prn_free_queue_list(dev->memory, &opdev->ql);
+
+ /* find out resolution of printer */
+ /* this is returned in pixels/metre */
+ DevQueryCaps(opdev->hdc, CAPS_HORIZONTAL_RESOLUTION, 2, caps);
+ dev->x_pixels_per_inch = (int)(caps[0] * 0.0254 + 0.5);
+ dev->y_pixels_per_inch = (int)(caps[1] * 0.0254 + 0.5);
+
+ /* find out page size and margins */
+ /* these are returned in millimetres */
+ nforms = DevQueryHardcopyCaps(opdev->hdc, 0, 0, &hcinfo);
+ for (i = 0; i < nforms; i++) {
+ DevQueryHardcopyCaps(opdev->hdc, i, 1, &hcinfo);
+ if (hcinfo.flAttributes & HCAPS_CURRENT)
+ break; /* this is the default page size */
+ }
+ /* GS size is in pixels */
+ dev->width = hcinfo.cx * caps[0] / 1000;
+ dev->height = hcinfo.cy * caps[1] / 1000;
+ /* GS margins are in inches */
+ m[0] /*left */ = hcinfo.xLeftClip / 25.4;
+ m[1] /*bottom */ = hcinfo.yBottomClip / 25.4;
+ m[2] /*right */ = (hcinfo.cx - hcinfo.xRightClip) / 25.4;
+ m[3] /*top */ = (hcinfo.cy - hcinfo.yTopClip) / 25.4;
+ gx_device_set_margins(dev, m, true);
+ /* set bounding box in pixels for later drawing */
+ opdev->clipbox[0] = (int)(hcinfo.xLeftClip / 25.4 * dev->x_pixels_per_inch + 1); /* round inwards */
+ opdev->clipbox[1] = (int)(hcinfo.yBottomClip / 25.4 * dev->y_pixels_per_inch + 1);
+ opdev->clipbox[2] = (int)(hcinfo.xRightClip / 25.4 * dev->x_pixels_per_inch);
+ opdev->clipbox[3] = (int)(hcinfo.yTopClip / 25.4 * dev->y_pixels_per_inch);
+
+ /* get presentation space */
+ sizlPage.cx = dev->width;
+ sizlPage.cy = dev->height;
+ opdev->hps = GpiCreatePS(opdev->hab, opdev->hdc, &sizlPage,
+ PU_PELS | GPIF_DEFAULT | GPIT_NORMAL | GPIA_ASSOC);
+
+ depth = dev->color_info.depth;
+ if (depth == 0) {
+ /* Set parameters that were unknown before opening device */
+ /* Find out if the device supports color */
+ /* We recognize 1 bit monochrome and 24 bit color devices */
+ DevQueryCaps(opdev->hdc, CAPS_COLOR_PLANES, 2, caps);
+ /* caps[0] is #color planes, caps[1] is #bits per plane */
+ depth = caps[0] * caps[1];
+ if (depth > 1)
+ depth = 24;
+ }
+ os2prn_set_bpp(dev, depth);
+
+ /* create a memory DC compatible with printer */
+ opdev->hdcMem = DevOpenDC(opdev->hab, OD_MEMORY, "*", 0L, NULL, opdev->hdc);
+ if (opdev->hdcMem == DEV_ERROR) {
+ ERRORID eid = WinGetLastError(opdev->hab);
+
+ errprintf(opdev->memory, "DevOpenDC for memory error 0x%x\n", eid);
+ return gs_error_limitcheck;
+ }
+ sizlPage.cx = dev->width;
+ sizlPage.cy = dev->height;
+ opdev->hpsMem = GpiCreatePS(opdev->hab, opdev->hdcMem, &sizlPage,
+ PU_PELS | GPIF_DEFAULT | GPIT_NORMAL | GPIA_ASSOC);
+ if (opdev->hpsMem == GPI_ERROR) {
+ ERRORID eid = WinGetLastError(opdev->hab);
+
+ errprintf(opdev->memory, "GpiCreatePS for memory error 0x%x\n", eid);
+ return gs_error_limitcheck;
+ }
+ if (DevEscape(opdev->hdc, DEVESC_STARTDOC, (LONG) strlen(gs_product),
+ (char *)gs_product, NULL, NULL) == DEVESC_ERROR) {
+ ERRORID eid = WinGetLastError(opdev->hab);
+
+ errprintf(opdev->memory, "DEVESC_STARTDOC error 0x%x\n", eid);
+ return gs_error_limitcheck;
+ }
+ /* gdev_prn_open opens a temporary file which we don't want */
+ /* so we specify the name now so we can delete it later */
+ pfile = gp_open_scratch_file(opdev->memory, gp_scratch_file_name_prefix,
+ opdev->fname, "wb");
+ fclose(pfile);
+ code = gdev_prn_open(dev);
+
+ return code;
+}
+
+/* Close the os2prn driver */
+static int
+os2prn_close(gx_device * dev)
+{
+ int code;
+ LONG lOut;
+ USHORT usJobID;
+
+ /* tell printer that all is finished */
+ DevEscape(opdev->hdc, DEVESC_ENDDOC, 0L, NULL, &lOut, (PBYTE) & usJobID);
+ /* Free resources */
+ GpiAssociate(opdev->hps, (HDC) NULL);
+ GpiDestroyPS(opdev->hps);
+ DevCloseDC(opdev->hdc);
+
+ if (opdev->hpsMem != GPI_ERROR)
+ GpiDestroyPS(opdev->hpsMem);
+ if (opdev->hdcMem != DEV_ERROR)
+ DevCloseDC(opdev->hdcMem);
+
+ code = gdev_prn_close(dev);
+ /* delete unwanted temporary file */
+ unlink(opdev->fname);
+ return code;
+}
+
+/* Get os2pm parameters */
+int
+os2prn_get_params(gx_device * dev, gs_param_list * plist)
+{
+ int code = gdev_prn_get_params(dev, plist);
+ gs_param_string qs;
+
+ qs.data = opdev->queue_name, qs.size = strlen(qs.data),
+ qs.persistent = false;
+ code < 0 ||
+ (code = param_write_string(plist, "OS2QUEUE", &qs)) < 0;
+ return code;
+}
+
+/* We implement this ourselves so that we can change BitsPerPixel */
+/* before the device is opened */
+int
+os2prn_put_params(gx_device * dev, gs_param_list * plist)
+{
+ int ecode = 0, code;
+ int old_bpp = dev->color_info.depth;
+ int bpp = old_bpp;
+ gs_param_string qs;
+
+ /* Handle extra parameters */
+ switch (code = param_read_string(plist, "OS2QUEUE", &qs)) {
+ case 0:
+ if (qs.size == strlen(opdev->queue_name) &&
+ !memcmp(opdev->queue_name, qs.data, qs.size)
+ ) {
+ qs.data = 0;
+ break;
+ }
+ if (dev->is_open)
+ ecode = gs_error_rangecheck;
+ else if (qs.size >= sizeof(opdev->queue_name))
+ ecode = gs_error_limitcheck;
+ else
+ break;
+ goto qe;
+ default:
+ ecode = code;
+ qe:param_signal_error(plist, "OS2QUEUE", ecode);
+ case 1:
+ qs.data = 0;
+ break;
+ }
+
+ switch (code = param_read_int(plist, "BitsPerPixel", &bpp)) {
+ case 0:
+ if (dev->is_open)
+ ecode = gs_error_rangecheck;
+ else { /* change dev->color_info is valid before device is opened */
+ os2prn_set_bpp(dev, bpp);
+ break;
+ }
+ goto bppe;
+ default:
+ ecode = code;
+ bppe:param_signal_error(plist, "BitsPerPixel", ecode);
+ case 1:
+ break;
+ }
+
+ if (ecode >= 0)
+ ecode = gdev_prn_put_params(dev, plist);
+
+ if ((ecode >= 0) && (qs.data != 0)) {
+ memcpy(opdev->queue_name, qs.data, qs.size);
+ opdev->queue_name[qs.size] = 0;
+ }
+ return ecode;
+}
+
+/* ------ Internal routines ------ */
+
+#undef opdev
+#define opdev ((gx_device_os2prn *)pdev)
+
+/************************************************/
+
+/* ------ Private definitions ------ */
+
+/* new os2prn_print_page routine */
+
+/* Write BMP header to memory, then send bitmap to printer */
+/* one scan line at a time */
+static int
+os2prn_print_page(gx_device_printer * pdev, FILE * file)
+{
+ int raster = gdev_prn_raster(pdev);
+
+ /* BMP scan lines are padded to 32 bits. */
+ ulong bmp_raster = (raster + 3) & (~3);
+ ulong bmp_raster_multi;
+ int height = pdev->height;
+ int depth = pdev->color_info.depth;
+ byte *row;
+ int y;
+ int code = 0; /* return code */
+ POINTL apts[4];
+ APIRET rc;
+ POINTL aptsb[4];
+ HBITMAP hbmp, hbmr;
+ int i, lines;
+ int ystart, yend;
+ int yslice;
+
+ struct bmi_s {
+ BITMAPINFOHEADER2 h;
+ RGB2 pal[256];
+ } bmi;
+
+ yslice = 65535 / bmp_raster;
+ bmp_raster_multi = bmp_raster * yslice;
+ row = (byte *) gs_malloc(pdev->memory, bmp_raster_multi, 1, "bmp file buffer");
+ if (row == 0) /* can't allocate row buffer */
+ return_error(gs_error_VMerror);
+
+ if (opdev->newframe)
+ DevEscape(opdev->hdc, DEVESC_NEWFRAME, 0L, NULL, NULL, NULL);
+ opdev->newframe = 1;
+
+ /* Write the info header. */
+
+ memset(&bmi.h, 0, sizeof(bmi.h));
+ bmi.h.cbFix = sizeof(bmi.h);
+ bmi.h.cx = pdev->width; /* opdev->mdev.width; */
+ /* bmi.h.cy = height; */
+ bmi.h.cy = yslice; /* size for memory PS */
+ bmi.h.cPlanes = 1;
+ bmi.h.cBitCount = pdev->color_info.depth;
+
+ /* Write the palette. */
+
+ if (depth <= 8) {
+ int i;
+ gx_color_value rgb[3];
+ PRGB2 pq;
+
+ bmi.h.cclrUsed = 1 << depth;
+ bmi.h.cclrImportant = 1 << depth;
+ for (i = 0; i != 1 << depth; i++) {
+ (*dev_proc(pdev, map_color_rgb)) ((gx_device *) pdev,
+ (gx_color_index) i, rgb);
+ pq = &bmi.pal[i];
+ pq->bRed = gx_color_value_to_byte(rgb[0]);
+ pq->bGreen = gx_color_value_to_byte(rgb[1]);
+ pq->bBlue = gx_color_value_to_byte(rgb[2]);
+ pq->fcOptions = 0;
+ }
+ } else {
+ bmi.h.cclrUsed = 0;
+ bmi.h.cclrImportant = 0;
+ }
+
+ /* for GpiDrawBits */
+ /* target is inclusive */
+ apts[0].x = 0;
+ apts[0].y = 0; /* filled in later */
+ apts[1].x = pdev->width - 1;
+ apts[1].y = 0; /* filled in later */
+ /* source is not inclusive of top & right borders */
+ apts[2].x = 0;
+ apts[2].y = 0;
+ apts[3].x = pdev->width;
+ apts[3].y = 0; /* filled in later */
+
+ /* for GpiBitBlt */
+ /* target is not inclusive */
+ aptsb[0].x = opdev->clipbox[0];
+ aptsb[0].y = 0; /* filled in later */
+ aptsb[1].x = opdev->clipbox[2];
+ aptsb[1].y = 0; /* filled in later */
+ /* source is not inclusive */
+ aptsb[2].x = opdev->clipbox[0];
+ aptsb[2].y = 0;
+ aptsb[3].x = opdev->clipbox[2];
+ aptsb[3].y = 0; /* filled in later */
+
+ /* write the bits */
+ ystart = opdev->clipbox[3];
+ yend = opdev->clipbox[1];
+ y = ystart;
+ while (y > yend) {
+ /* create a bitmap for the memory DC */
+ hbmp = GpiCreateBitmap(opdev->hpsMem, &bmi.h, 0L, NULL, NULL);
+ if (hbmp == GPI_ERROR)
+ goto bmp_done;
+ hbmr = GpiSetBitmap(opdev->hpsMem, hbmp);
+
+ /* copy slice to memory bitmap */
+ if (y > yend + yslice)
+ lines = yslice;
+ else
+ lines = y - yend;
+ y -= lines;
+ for (i = lines - 1; i >= 0; i--)
+ gdev_prn_copy_scan_lines(pdev, ystart - 1 - (y + i), row + (bmp_raster * i), raster);
+ apts[0].y = 0; /* target */
+ apts[1].y = lines;
+ apts[3].y = lines - 1; /* source */
+ /* copy DIB bitmap to memory bitmap */
+ rc = GpiDrawBits(opdev->hpsMem, row, (BITMAPINFO2 *) & bmi, 4, apts,
+ (depth != 1) ? ROP_SRCCOPY : ROP_NOTSRCCOPY, 0);
+
+ /* copy slice to printer */
+ aptsb[0].y = y;
+ aptsb[1].y = y + lines;
+ aptsb[3].y = lines;
+ rc = GpiBitBlt(opdev->hps, opdev->hpsMem, 4, aptsb, ROP_SRCCOPY, BBO_IGNORE);
+
+ /* delete bitmap */
+ if (hbmr != HBM_ERROR)
+ GpiSetBitmap(opdev->hpsMem, (ULONG) 0);
+ hbmr = HBM_ERROR;
+ if (hbmp != GPI_ERROR)
+ GpiDeleteBitmap(hbmp);
+ hbmp = GPI_ERROR;
+ }
+
+ bmp_done:
+ if (row)
+ gs_free(pdev->memory, (char *)row, bmp_raster_multi, 1, "bmp file buffer");
+
+ return code;
+}
+
+/* combined color mappers */
+
+/* 24-bit color mappers (taken from gdevmem2.c). */
+/* Note that OS/2 expects RGB values in the order B,G,R. */
+
+/* Encode a r-g-b color to a color index. */
+static gx_color_index
+os2prn_map_rgb_color(gx_device * dev, const gx_color_value cv[])
+{
+ gx_color_value r = cv[0];
+ gx_color_value g = cv[1];
+ gx_color_value b = cv[2];
+ return gx_color_value_to_byte(r) +
+ ((uint) gx_color_value_to_byte(g) << 8) +
+ ((ulong) gx_color_value_to_byte(b) << 16);
+}
+
+/* Decode a color index to a r-g-b color. */
+static int
+os2prn_map_color_rgb(gx_device * dev, gx_color_index color,
+ gx_color_value prgb[3])
+{
+ prgb[2] = gx_color_value_from_byte(color >> 16);
+ prgb[1] = gx_color_value_from_byte((color >> 8) & 0xff);
+ prgb[0] = gx_color_value_from_byte(color & 0xff);
+ return 0;
+}
+
+void
+os2prn_set_bpp(gx_device * dev, int depth)
+{
+ gx_device_color_info dci = dev->color_info;
+ static const gx_device_color_info os2prn_dci_rgb = dci_std_color(24);
+ static const gx_device_color_info os2prn_dci_mono = dci_black_and_white;
+ if (depth == 24) {
+ dci = os2prn_dci_rgb;
+ dev->procs.get_color_mapping_procs = gx_default_DevRGB_get_color_mapping_procs;
+ dev->procs.get_color_comp_index = gx_default_DevRGB_get_color_comp_index;
+ dev->procs.map_rgb_color = dev->procs.encode_color =
+ os2prn_map_rgb_color;
+ dev->procs.map_color_rgb = dev->procs.decode_color =
+ os2prn_map_color_rgb;
+ } else { /* default is black and white */
+ dci = os2prn_dci_mono;
+ dev->procs.get_color_mapping_procs = gx_default_DevGray_get_color_mapping_procs;
+ dev->procs.get_color_comp_index = gx_default_DevGray_get_color_comp_index;
+ dev->procs.map_rgb_color = dev->procs.encode_color =
+ gx_default_b_w_map_rgb_color;
+ dev->procs.map_color_rgb = dev->procs.decode_color =
+ gx_default_b_w_map_color_rgb;
+ }
+ /* restore old anti_alias info */
+ dci.anti_alias = dev->color_info.anti_alias;
+ dev->color_info = dci;
+ /* Set the mask bits, etc. even though we are setting linear: unknown */
+ set_linear_color_bits_mask_shift(dev);
+}
+
+/* Get list of queues from SplEnumQueue */
+/* returns 0 if OK, non-zero for error */
+static int
+os2prn_get_queue_list(gs_memory_t *mem, OS2QL * ql)
+{
+ SPLERR splerr;
+ USHORT jobCount;
+ ULONG cbBuf;
+ ULONG cTotal;
+ ULONG cReturned;
+ ULONG cbNeeded;
+ ULONG ulLevel;
+ ULONG i;
+ PSZ pszComputerName;
+ PBYTE pBuf;
+ PPRQINFO3 prq;
+
+ ulLevel = 3L;
+ pszComputerName = (PSZ) NULL;
+ splerr = SplEnumQueue(pszComputerName, ulLevel, pBuf, 0L, /* cbBuf */
+ &cReturned, &cTotal,
+ &cbNeeded, NULL);
+ if (splerr == ERROR_MORE_DATA || splerr == NERR_BufTooSmall) {
+ pBuf = gs_malloc(mem, cbNeeded, 1, "OS/2 printer device info buffer");
+ ql->prq = (PRQINFO3 *) pBuf;
+ if (ql->prq != (PRQINFO3 *) NULL) {
+ ql->len = cbNeeded;
+ cbBuf = cbNeeded;
+ splerr = SplEnumQueue(pszComputerName, ulLevel, pBuf, cbBuf,
+ &cReturned, &cTotal,
+ &cbNeeded, NULL);
+ if (splerr == NO_ERROR) {
+ /* Set pointer to point to the beginning of the buffer. */
+ prq = (PPRQINFO3) pBuf;
+ /* cReturned has the count of the number of PRQINFO3 structures. */
+ ql->nqueues = cReturned;
+ ql->defqueue = 0;
+ for (i = 0; i < cReturned; i++) {
+ if (prq->fsType & PRQ3_TYPE_APPDEFAULT)
+ ql->defqueue = i;
+ prq++;
+ } /*endfor cReturned */
+ }
+ }
+ } else {
+ /* If we are here we had a bad error code. Print it and some other info. */
+ emprintf4(mem,
+ "SplEnumQueue Error=%ld, Total=%ld, Returned=%ld, Needed=%ld\n",
+ splerr, cTotal, cReturned, cbNeeded);
+ }
+ if (splerr)
+ return splerr;
+ return 0;
+}
+
+static void
+os2prn_free_queue_list(gs_memory_t *mem, OS2QL * ql)
+{
+ gs_free(mem, (char *)ql->prq, ql->len, 1, "os2prn queue list");
+ ql->prq = NULL;
+ ql->len = 0;
+ ql->defqueue = 0;
+ ql->nqueues = 0;
+}
diff --git a/devices/gdevp2up.c b/devices/gdevp2up.c
new file mode 100644
index 000000000..2da611537
--- /dev/null
+++ b/devices/gdevp2up.c
@@ -0,0 +1,144 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+/* A "2-up" PCX device for testing page objects. */
+#include "gdevprn.h"
+#include "gdevpccm.h"
+#include "gxclpage.h"
+
+extern gx_device_printer gs_pcx256_device;
+
+/* ------ The device descriptors ------ */
+
+/*
+ * Default X and Y resolution.
+ */
+#define X_DPI 72
+#define Y_DPI 72
+
+/*
+ * Define the size of the rendering buffer.
+ */
+#define RENDER_BUFFER_SPACE 500000
+
+/* This device only supports SVGA 8-bit color. */
+
+static dev_proc_open_device(pcx2up_open);
+static dev_proc_print_page(pcx2up_print_page);
+
+typedef struct gx_device_2up_s {
+ gx_device_common;
+ gx_prn_device_common;
+ bool have_odd_page;
+ gx_saved_page odd_page;
+} gx_device_2up;
+
+static const gx_device_procs pcx2up_procs =
+prn_color_procs(pcx2up_open, gdev_prn_output_page, gdev_prn_close,
+ pc_8bit_map_rgb_color, pc_8bit_map_color_rgb);
+gx_device_2up gs_pcx2up_device =
+{prn_device_body(gx_device_2up, pcx2up_procs, "pcx2up",
+ DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
+ X_DPI, Y_DPI,
+ 0, 0, 0, 0, /* margins */
+ 3, 8, 5, 5, 6, 6, pcx2up_print_page)
+};
+
+/* Open the device. We reimplement this to force banding with */
+/* delayed rasterizing. */
+static int
+pcx2up_open(gx_device * dev)
+{
+ gx_device_printer *pdev = (gx_device_printer *) dev;
+ int code;
+ gdev_prn_space_params save_params;
+
+ save_params = pdev->space_params;
+ pdev->space_params.MaxBitmap = 0; /* force banding */
+ pdev->space_params.band.BandWidth =
+ dev->width * 2 + (int)(dev->HWResolution[0] * 2.0);
+ pdev->space_params.band.BandBufferSpace = RENDER_BUFFER_SPACE;
+ code = gdev_prn_open(dev);
+ pdev->space_params = save_params;
+ ((gx_device_2up *) dev)->have_odd_page = false;
+ return code;
+}
+
+/* Write the page. */
+static int
+pcx2up_print_page(gx_device_printer * pdev, FILE * file)
+{
+ gx_device_2up *pdev2 = (gx_device_2up *) pdev;
+ const gx_device_printer *prdev_template =
+ (const gx_device_printer *)&gs_pcx2up_device;
+
+ if (!pdev2->have_odd_page) { /* This is the odd page, just save it. */
+ pdev2->have_odd_page = true;
+ return gdev_prn_save_page(pdev, &pdev2->odd_page);
+ } else { /* This is the even page, do 2-up output. */
+ gx_saved_page even_page;
+ gx_placed_page pages[2];
+ int x_offset = (int)(pdev->HWResolution[0] * 0.5);
+ int code = gdev_prn_save_page(pdev, &even_page);
+ int prdev_size = prdev_template->params_size;
+ gx_device_printer *prdev;
+
+#define rdev ((gx_device *)prdev)
+
+ if (code < 0)
+ return code;
+ /* Create the placed page list. */
+ pages[0].page = &pdev2->odd_page;
+ pages[0].offset.x = x_offset;
+ pages[0].offset.y = 0 /*y_offset */ ;
+ pages[1].page = &even_page;
+ pages[1].offset.x = pdev->width + x_offset * 3;
+ pages[1].offset.y = 0 /*y_offset */ ;
+ /* Create and open a device for rendering. */
+ prdev = (gx_device_printer *)
+ gs_alloc_bytes(pdev->memory, prdev_size,
+ "pcx2up_print_page(device)");
+ if (prdev == 0)
+ return_error(gs_error_VMerror);
+ memcpy(prdev, prdev_template, prdev_size);
+ check_device_separable((gx_device *)rdev);
+ gx_device_fill_in_procs(rdev);
+ set_dev_proc(prdev, open_device,
+ dev_proc(&gs_pcx256_device, open_device));
+ prdev->printer_procs.print_page =
+ gs_pcx256_device.printer_procs.print_page;
+ prdev->space_params.band =
+ pages[0].page->band_params; /* either one will do */
+ prdev->space_params.MaxBitmap = 0;
+ prdev->space_params.BufferSpace =
+ prdev->space_params.band.BandBufferSpace;
+ prdev->width = prdev->space_params.band.BandWidth;
+ prdev->OpenOutputFile = false;
+ code = (*dev_proc(rdev, open_device)) (rdev);
+ if (code < 0)
+ return code;
+ rdev->is_open = true;
+ prdev->file = pdev->file;
+ /* Render the pages. */
+ code = gdev_prn_render_pages(prdev, pages, 2);
+ /* Clean up. */
+ if (pdev->file != 0)
+ prdev->file = 0; /* don't close it */
+ gs_closedevice(rdev);
+ pdev2->have_odd_page = false;
+ return code;
+#undef rdev
+ }
+}
diff --git a/devices/gdevpbm.c b/devices/gdevpbm.c
new file mode 100644
index 000000000..08b9641fb
--- /dev/null
+++ b/devices/gdevpbm.c
@@ -0,0 +1,1401 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+/* Portable Bit/Gray/PixMap drivers */
+#include "gdevprn.h"
+#include "gscdefs.h"
+#include "gscspace.h" /* For pnm_begin_typed_image(..) */
+#include "gxgetbit.h"
+#include "gxlum.h"
+#include "gxiparam.h" /* For pnm_begin_typed_image(..) */
+#include "gdevmpla.h"
+#include "gdevplnx.h"
+#include "gdevppla.h"
+
+/*
+ * Thanks are due to Jos Vos (jos@bull.nl) for an earlier P*M driver,
+ * on which this one is based; to Nigel Roles (ngr@cotswold.demon.co.uk),
+ * for the plan9bm changes; and to Leon Bottou (leonb@research.att.com)
+ * for the color detection code in pnm_begin_typed_image.
+ */
+
+/*
+ * There are 8 (families of) drivers here, plus one less related one:
+ * pbm[raw] - outputs PBM (black and white).
+ * pgm[raw] - outputs PGM (gray-scale).
+ * pgnm[raw] - outputs PBM if the page contains only black and white,
+ * otherwise PGM.
+ * ppm[raw] - outputs PPM (RGB).
+ * pnm[raw] - outputs PBM if the page contains only black and white,
+ * otherwise PGM if the page contains only gray shades,
+ * otherwise PPM.
+ * If GrayDetection is true, then the pageneutral color is used to decide
+ between PGM and PPM.
+ * pkm[raw] - computes internally in CMYK, outputs PPM (RGB).
+ * pksm[raw] - computes internally in CMYK, outputs 4 PBM pages.
+ * pamcmyk4 - outputs CMYK as PAM 1-bit per color
+ * pamcmyk32 - outputs CMYK as PAM 8-bits per color
+ * pnmcmyk - With GrayDetection true, outputs either the 8-bit K plane as PGM or
+ * 32-bit CMYK (pam 8-bit per component) depending on pageneutralcolor.
+ * pam - previous name for the pamcmyk32 device retained for backwards compatibility
+ * plan9bm - outputs Plan 9 bitmap format.
+ */
+
+/*
+ * The code here is designed to work with variable depths for PGM and PPM.
+ * The code will work with any of the values in brackets, but the
+ * Ghostscript imager requires that depth be a power of 2 or be 24,
+ * so the actual allowed values are more limited.
+ * pgm, pgnm: 1, 2, 4, 8, 16. [1-16]
+ * pgmraw, pgnmraw: 1, 2, 4, 8. [1-8]
+ * ppm, pnm: 4(3x1), 8(3x2), 16(3x5), 24(3x8), 32(3x10). [3-32]
+ * ppmraw, pnmraw: 4(3x1), 8(3x2), 16(3x5), 24(3x8). [3-24]
+ * pkm, pkmraw: 4(4x1), 8(4x2), 16(4x4), 32(4x8). [4-32]
+ * pksm, pksmraw: ibid.
+ * pam: 32 (CMYK), 4 (CMYK)
+ */
+
+/* Structure for P*M devices, which extend the generic printer device. */
+
+#define MAX_COMMENT 70 /* max user-supplied comment */
+struct gx_device_pbm_s {
+ gx_device_common;
+ gx_prn_device_common;
+ /* Additional state for P*M devices */
+ char magic; /* n for "Pn" */
+ char comment[MAX_COMMENT + 1]; /* comment for head of file */
+ byte is_raw; /* 1 if raw format, 0 if plain */
+ byte optimize; /* 1 if optimization OK, 0 if not */
+ byte uses_color; /* 0 if image is black and white, */
+ /* 1 if gray (PGM or PPM only), */
+ /* 2 or 3 if colored (PPM only) */
+ bool UsePlanarBuffer; /* 0 if chunky buffer, 1 if planar */
+ dev_proc_copy_alpha((*save_copy_alpha));
+ dev_proc_begin_typed_image((*save_begin_typed_image));
+};
+typedef struct gx_device_pbm_s gx_device_pbm;
+
+/* ------ The device descriptors ------ */
+
+/*
+ * Default X and Y resolution.
+ */
+#define X_DPI 72
+#define Y_DPI 72
+
+/* Macro for generating P*M device descriptors. */
+#define pbm_prn_device(procs, dev_name, magic, is_raw, num_comp, depth, max_gray, max_rgb, optimize, x_dpi, y_dpi, print_page)\
+{ prn_device_body(gx_device_pbm, procs, dev_name,\
+ DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS, x_dpi, y_dpi,\
+ 0, 0, 0, 0,\
+ num_comp, depth, max_gray, max_rgb, max_gray + 1, max_rgb + 1,\
+ print_page),\
+ magic,\
+ { 0 },\
+ is_raw,\
+ optimize,\
+ 0, 0, 0\
+}
+
+/* For all but PBM, we need our own color mapping and alpha procedures. */
+static dev_proc_map_rgb_color(pgm_map_rgb_color);
+static dev_proc_map_rgb_color(ppm_map_rgb_color);
+static dev_proc_map_color_rgb(pgm_map_color_rgb);
+static dev_proc_map_color_rgb(ppm_map_color_rgb);
+static dev_proc_map_cmyk_color(pkm_map_cmyk_color);
+static dev_proc_map_color_rgb(pkm_map_color_rgb);
+static dev_proc_get_params(ppm_get_params);
+static dev_proc_put_params(ppm_put_params);
+static dev_proc_copy_alpha(pnm_copy_alpha);
+static dev_proc_begin_typed_image(pnm_begin_typed_image);
+
+/* We need to initialize uses_color when opening the device, */
+/* and after each showpage. */
+static dev_proc_open_device(ppm_open);
+static dev_proc_open_device(pnmcmyk_open);
+static dev_proc_output_page(ppm_output_page);
+
+/* And of course we need our own print-page routines. */
+static dev_proc_print_page(pbm_print_page);
+static dev_proc_print_page(pgm_print_page);
+static dev_proc_print_page(ppm_print_page);
+static dev_proc_print_page(pkm_print_page);
+static dev_proc_print_page(psm_print_page);
+static dev_proc_print_page(psm_print_page);
+static dev_proc_print_page(pam_print_page);
+static dev_proc_print_page(pam4_print_page);
+static dev_proc_print_page(pnmcmyk_print_page);
+
+/* The device procedures */
+
+/* See gdevprn.h for the template for the following. */
+#define pgpm_procs(p_open, p_get_params, p_map_rgb_color, p_map_color_rgb, p_map_cmyk_color) {\
+ p_open, NULL, NULL, ppm_output_page, gdev_prn_close,\
+ p_map_rgb_color, p_map_color_rgb, NULL, NULL, NULL, NULL, NULL, NULL,\
+ p_get_params, ppm_put_params,\
+ p_map_cmyk_color, NULL, NULL, NULL, gx_page_device_get_page_device\
+}
+
+static const gx_device_procs pbm_procs =
+ pgpm_procs(gdev_prn_open, gdev_prn_get_params,
+ gdev_prn_map_rgb_color, gdev_prn_map_color_rgb, NULL);
+static const gx_device_procs pgm_procs =
+ pgpm_procs(ppm_open, gdev_prn_get_params,
+ pgm_map_rgb_color, pgm_map_color_rgb, NULL);
+static const gx_device_procs ppm_procs =
+ pgpm_procs(ppm_open, ppm_get_params,
+ gx_default_rgb_map_rgb_color, ppm_map_color_rgb, NULL);
+static const gx_device_procs pnm_procs =
+ pgpm_procs(ppm_open, ppm_get_params,
+ ppm_map_rgb_color, ppm_map_color_rgb, NULL);
+static const gx_device_procs pkm_procs =
+ pgpm_procs(ppm_open, ppm_get_params,
+ NULL, cmyk_1bit_map_color_rgb, cmyk_1bit_map_cmyk_color);
+static const gx_device_procs pam_procs =
+ pgpm_procs(ppm_open, ppm_get_params,
+ NULL, cmyk_8bit_map_color_rgb, cmyk_8bit_map_cmyk_color);
+static const gx_device_procs pnmcmyk_procs =
+ pgpm_procs(pnmcmyk_open, ppm_get_params,
+ NULL, cmyk_8bit_map_color_rgb, cmyk_8bit_map_cmyk_color);
+
+/* The device descriptors themselves */
+const gx_device_pbm gs_pbm_device =
+pbm_prn_device(pbm_procs, "pbm", '1', 0, 1, 1, 1, 0, 0,
+ X_DPI, Y_DPI, pbm_print_page);
+const gx_device_pbm gs_pbmraw_device =
+pbm_prn_device(pbm_procs, "pbmraw", '4', 1, 1, 1, 1, 1, 0,
+ X_DPI, Y_DPI, pbm_print_page);
+const gx_device_pbm gs_pgm_device =
+pbm_prn_device(pgm_procs, "pgm", '2', 0, 1, 8, 255, 0, 0,
+ X_DPI, Y_DPI, pgm_print_page);
+const gx_device_pbm gs_pgmraw_device =
+pbm_prn_device(pgm_procs, "pgmraw", '5', 1, 1, 8, 255, 0, 0,
+ X_DPI, Y_DPI, pgm_print_page);
+const gx_device_pbm gs_pgnm_device =
+pbm_prn_device(pgm_procs, "pgnm", '2', 0, 1, 8, 255, 0, 1,
+ X_DPI, Y_DPI, pgm_print_page);
+const gx_device_pbm gs_pgnmraw_device =
+pbm_prn_device(pgm_procs, "pgnmraw", '5', 1, 1, 8, 255, 0, 1,
+ X_DPI, Y_DPI, pgm_print_page);
+const gx_device_pbm gs_ppm_device =
+pbm_prn_device(ppm_procs, "ppm", '3', 0, 3, 24, 255, 255, 0,
+ X_DPI, Y_DPI, ppm_print_page);
+const gx_device_pbm gs_ppmraw_device =
+pbm_prn_device(ppm_procs, "ppmraw", '6', 1, 3, 24, 255, 255, 0,
+ X_DPI, Y_DPI, ppm_print_page);
+const gx_device_pbm gs_pnm_device =
+pbm_prn_device(pnm_procs, "pnm", '3', 0, 3, 24, 255, 255, 1,
+ X_DPI, Y_DPI, ppm_print_page);
+const gx_device_pbm gs_pnmraw_device =
+pbm_prn_device(pnm_procs, "pnmraw", '6', 1, 3, 24, 255, 255, 1,
+ X_DPI, Y_DPI, ppm_print_page);
+const gx_device_pbm gs_pkm_device =
+pbm_prn_device(pkm_procs, "pkm", '3', 0, 4, 4, 1, 1, 0,
+ X_DPI, Y_DPI, pkm_print_page);
+const gx_device_pbm gs_pkmraw_device =
+pbm_prn_device(pkm_procs, "pkmraw", '6', 1, 4, 4, 1, 1, 0,
+ X_DPI, Y_DPI, pkm_print_page);
+const gx_device_pbm gs_pksm_device =
+pbm_prn_device(pkm_procs, "pksm", '1', 0, 4, 4, 1, 1, 0,
+ X_DPI, Y_DPI, psm_print_page);
+const gx_device_pbm gs_pksmraw_device =
+pbm_prn_device(pkm_procs, "pksmraw", '4', 1, 4, 4, 1, 1, 0,
+ X_DPI, Y_DPI, psm_print_page);
+const gx_device_pbm gs_pamcmyk32_device =
+pbm_prn_device(pam_procs, "pamcmyk32", '7', 1, 4, 32, 255, 255, 0,
+ X_DPI, Y_DPI, pam_print_page);
+const gx_device_pbm gs_pnmcmyk_device =
+pbm_prn_device(pnmcmyk_procs, "pnmcmyk", '7', 1, 4, 32, 255, 255, 0, /* optimize false since this relies on GrayDetection */
+ X_DPI, Y_DPI, pnmcmyk_print_page); /* May output PGM, magic = 5 */
+const gx_device_pbm gs_pamcmyk4_device =
+pbm_prn_device(pam_procs, "pamcmyk4", '7', 1, 4, 4, 1, 1, 0,
+ X_DPI, Y_DPI, pam4_print_page);
+/* Also keep the old device name so anyone using it won't be surprised */
+const gx_device_pbm gs_pam_device =
+pbm_prn_device(pam_procs, "pam", '7', 1, 4, 32, 255, 255, 0,
+ X_DPI, Y_DPI, pam_print_page);
+
+/* Plan 9 bitmaps default to 100 dpi. */
+const gx_device_pbm gs_plan9bm_device =
+pbm_prn_device(pbm_procs, "plan9bm", '9', 1, 1, 1, 1, 1, 1,
+ 100, 100, pbm_print_page);
+
+/* ------ Initialization ------ */
+
+/* Set the copy_alpha and color mapping procedures if necessary. */
+static void
+ppm_set_dev_procs(gx_device * pdev)
+{
+ gx_device_pbm * const bdev = (gx_device_pbm *)pdev;
+
+ if (dev_proc(pdev, copy_alpha) != pnm_copy_alpha) {
+ bdev->save_copy_alpha = dev_proc(pdev, copy_alpha);
+ if (pdev->color_info.depth > 4)
+ set_dev_proc(pdev, copy_alpha, pnm_copy_alpha);
+ }
+ if (dev_proc(pdev, begin_typed_image) != pnm_begin_typed_image) {
+ bdev->save_begin_typed_image = dev_proc(pdev, begin_typed_image);
+ set_dev_proc(pdev, begin_typed_image, pnm_begin_typed_image);
+ }
+ if (bdev->color_info.num_components == 4) {
+ if (bdev->color_info.depth == 4) {
+ set_dev_proc(pdev, map_color_rgb, cmyk_1bit_map_color_rgb);
+ set_dev_proc(pdev, map_cmyk_color, cmyk_1bit_map_cmyk_color);
+ } else if (bdev->magic == 7) {
+ set_dev_proc(pdev, map_color_rgb, cmyk_8bit_map_color_rgb);
+ set_dev_proc(pdev, map_cmyk_color, cmyk_8bit_map_cmyk_color);
+ } else {
+ set_dev_proc(pdev, map_color_rgb, pkm_map_color_rgb);
+ set_dev_proc(pdev, map_cmyk_color, pkm_map_cmyk_color);
+ }
+ }
+}
+
+/*
+ * Define a special open procedure that changes create_buf_device to use
+ * a planar device.
+ */
+
+static int
+ppm_open(gx_device * pdev)
+{
+ gx_device_pbm * bdev = (gx_device_pbm *)pdev;
+ int code;
+
+#ifdef TEST_PAD_AND_ALIGN
+ pdev->pad = 5;
+ pdev->log2_align_mod = 6;
+#endif
+
+ code = gdev_prn_open_planar(pdev, bdev->UsePlanarBuffer);
+ while (pdev->child)
+ pdev = pdev->child;
+
+ bdev = (gx_device_pbm *)pdev;;
+
+ if (code < 0)
+ return code;
+ pdev->color_info.separable_and_linear = GX_CINFO_SEP_LIN;
+ set_linear_color_bits_mask_shift(pdev);
+ bdev->uses_color = 0;
+ ppm_set_dev_procs(pdev);
+ return code;
+}
+
+/*
+ * For pnmcmyk, we set GrayDection true by default. This may be overridden by
+ * the default_put_params, but that's OK.
+ */
+static int
+pnmcmyk_open(gx_device *pdev)
+{
+ pdev->icc_struct->graydetection = true;
+ pdev->icc_struct->pageneutralcolor = true; /* enable detection */
+
+ return ppm_open(pdev);
+}
+
+/* Print a page, and reset uses_color if this is a showpage. */
+static int
+ppm_output_page(gx_device * pdev, int num_copies, int flush)
+{
+ /* Safe to start the page in the background */
+ int code = gdev_prn_bg_output_page(pdev, num_copies, flush);
+ gx_device_pbm * const bdev = (gx_device_pbm *)pdev;
+
+ if (code < 0)
+ return code;
+ if (flush)
+ bdev->uses_color = 0;
+ return code;
+}
+
+/* ------ Color mapping routines ------ */
+
+/* Map an RGB color to a PGM gray value. */
+/* Keep track of whether the image is black-and-white or gray. */
+static gx_color_index
+pgm_map_rgb_color(gx_device * pdev, const gx_color_value cv[])
+{ /* We round the value rather than truncating it. */
+ gx_color_value gray;
+ /* TO_DO_DEVICEN - Kludge to emulate pre DeviceN math errors */
+#if 1
+ gx_color_value r, g, b;
+
+ r = cv[0]; g = cv[0]; b = cv[0];
+ gray = ((r * (ulong) lum_red_weight) +
+ (g * (ulong) lum_green_weight) +
+ (b * (ulong) lum_blue_weight) +
+ (lum_all_weights / 2)) / lum_all_weights
+ * pdev->color_info.max_gray / gx_max_color_value;
+#else /* Should be ... */
+ gray = cv[0] * pdev->color_info.max_gray / gx_max_color_value;
+#endif
+
+ if (!(gray == 0 || gray == pdev->color_info.max_gray)) {
+ gx_device_pbm * const bdev = (gx_device_pbm *)pdev;
+
+ bdev->uses_color = 1;
+ }
+ return gray;
+}
+
+/* Map a PGM gray value back to an RGB color. */
+static int
+pgm_map_color_rgb(gx_device * dev, gx_color_index color,
+ gx_color_value prgb[3])
+{
+ gx_color_value gray =
+ color * gx_max_color_value / dev->color_info.max_gray;
+
+ prgb[0] = gray;
+ prgb[1] = gray;
+ prgb[2] = gray;
+ return 0;
+}
+
+/*
+ * Pre gs8.00 version of RGB mapping for 24-bit true (RGB) color devices
+ * It is kept here for backwards comparibility since the gs8.00 version
+ * has changed in functionality. The new one requires that the device be
+ * 'separable'. This routine is logically separable but does not require
+ * the various color_info fields associated with separability (comp_shift,
+ * comp_bits, and comp_mask) be setup.
+ */
+
+static gx_color_index
+gx_old_default_rgb_map_rgb_color(gx_device * dev,
+ gx_color_value r, gx_color_value g, gx_color_value b)
+{
+ if (dev->color_info.depth == 24)
+ return gx_color_value_to_byte(b) +
+ ((uint) gx_color_value_to_byte(g) << 8) +
+ ((ulong) gx_color_value_to_byte(r) << 16);
+ else {
+ int bpc = dev->color_info.depth / 3;
+ int drop = sizeof(gx_color_value) * 8 - bpc;
+
+ return ((((r >> drop) << bpc) + (g >> drop)) << bpc) + (b >> drop);
+ }
+}
+
+/* Map an RGB color to a PPM color tuple. */
+/* Keep track of whether the image is black-and-white, gray, or colored. */
+static gx_color_index
+ppm_map_rgb_color(gx_device * pdev, const gx_color_value cv[])
+{
+ gx_device_pbm * const bdev = (gx_device_pbm *)pdev;
+ gx_color_index color =
+ gx_old_default_rgb_map_rgb_color(pdev, cv[0], cv[1], cv[2]);
+ uint bpc = pdev->color_info.depth / 3;
+ gx_color_index mask =
+ ((gx_color_index)1 << (pdev->color_info.depth - bpc)) - 1;
+ if (!(((color >> bpc) ^ color) & mask)) { /* gray shade */
+ if (color != 0 && (~color & mask))
+ bdev->uses_color |= 1;
+ } else /* color */
+ bdev->uses_color = 2;
+ return color;
+}
+
+/* Map a PPM color tuple back to an RGB color. */
+static int
+ppm_map_color_rgb(gx_device * dev, gx_color_index color,
+ gx_color_value prgb[3])
+{
+ uint bitspercolor = dev->color_info.depth / 3;
+ uint colormask = (1 << bitspercolor) - 1;
+ uint max_rgb = dev->color_info.max_color;
+
+ prgb[0] = ((color >> (bitspercolor * 2)) & colormask) *
+ (ulong) gx_max_color_value / max_rgb;
+ prgb[1] = ((color >> bitspercolor) & colormask) *
+ (ulong) gx_max_color_value / max_rgb;
+ prgb[2] = (color & colormask) *
+ (ulong) gx_max_color_value / max_rgb;
+ return 0;
+}
+
+/* Map a CMYK color to a pixel value. */
+static gx_color_index
+pkm_map_cmyk_color(gx_device * pdev, const gx_color_value cv[])
+{
+ uint bpc = pdev->color_info.depth >> 2;
+ uint max_value = pdev->color_info.max_color;
+ uint cc = cv[0] * max_value / gx_max_color_value;
+ uint mc = cv[1] * max_value / gx_max_color_value;
+ uint yc = cv[2] * max_value / gx_max_color_value;
+ uint kc = cv[3] * max_value / gx_max_color_value;
+ gx_color_index color =
+ (((((cc << bpc) + mc) << bpc) + yc) << bpc) + kc;
+
+ return (color == gx_no_color_index ? color ^ 1 : color);
+}
+
+/* Map a CMYK pixel value to RGB. */
+static int
+pkm_map_color_rgb(gx_device * dev, gx_color_index color, gx_color_value rgb[3])
+{
+ int bpc = dev->color_info.depth >> 2;
+ gx_color_index cshift = color;
+ uint mask = (1 << bpc) - 1;
+ uint k = cshift & mask;
+ uint y = (cshift >>= bpc) & mask;
+ uint m = (cshift >>= bpc) & mask;
+ uint c = cshift >> bpc;
+ uint max_value = dev->color_info.max_color;
+ uint not_k = max_value - k;
+
+#define CVALUE(c)\
+ ((gx_color_value)((ulong)(c) * gx_max_color_value / max_value))
+ /* We use our improved conversion rule.... */
+ rgb[0] = CVALUE((max_value - c) * not_k / max_value);
+ rgb[1] = CVALUE((max_value - m) * not_k / max_value);
+ rgb[2] = CVALUE((max_value - y) * not_k / max_value);
+#undef CVALUE
+ return 0;
+}
+
+/* Augment get/put_params to add UsePlanarBuffer */
+
+static int
+ppm_get_params(gx_device * pdev, gs_param_list * plist)
+{
+ gx_device_pbm * const bdev = (gx_device_pbm *)pdev;
+ int code;
+
+ code = gdev_prn_get_params_planar(pdev, plist, &bdev->UsePlanarBuffer);
+ if (code < 0) return code;
+ code = param_write_null(plist, "OutputIntent");
+ return code;
+}
+
+static int
+ppm_put_params(gx_device * pdev, gs_param_list * plist)
+{
+ gx_device_pbm * const bdev = (gx_device_pbm *)pdev;
+ gx_device_color_info save_info;
+ int ncomps = pdev->color_info.num_components;
+ int bpc = pdev->color_info.depth / ncomps;
+ int ecode = 0;
+ int code;
+ long v;
+ gs_param_string_array intent;
+ const char *vname;
+
+ if ((code = param_read_string_array(plist, "OutputIntent", &intent)) == 0) {
+ /* This device does not use the OutputIntent parameter.
+ We include this code just as a sample how to handle it.
+ The PDF interpreter extracts OutputIntent from a PDF file and sends it to here,
+ if a device includes it in the .getdeviceparams response.
+ This device does include due to ppm_get_params implementation.
+ ppm_put_params must handle it (and ingore it) against 'rangecheck'.
+ */
+ static const bool debug_print_OutputIntent = false;
+
+ if (debug_print_OutputIntent) {
+ int i, j;
+
+ dmlprintf1(pdev->memory, "%d strings:\n", intent.size);
+ for (i = 0; i < intent.size; i++) {
+ const gs_param_string *s = &intent.data[i];
+ dmlprintf2(pdev->memory, " %d: size %d:", i, s->size);
+ if (i < 4) {
+ for (j = 0; j < s->size; j++)
+ dmlprintf1(pdev->memory, "%c", s->data[j]);
+ } else {
+ for (j = 0; j < s->size; j++)
+ dmlprintf1(pdev->memory, " %02x", s->data[j]);
+ }
+ dmlprintf(pdev->memory, "\n");
+ }
+ }
+ }
+ save_info = pdev->color_info;
+ if ((code = param_read_long(plist, (vname = "GrayValues"), &v)) != 1 ||
+ (code = param_read_long(plist, (vname = "RedValues"), &v)) != 1 ||
+ (code = param_read_long(plist, (vname = "GreenValues"), &v)) != 1 ||
+ (code = param_read_long(plist, (vname = "BlueValues"), &v)) != 1
+ ) {
+ if (code < 0)
+ ecode = code;
+ else if (v < 2 || v > (bdev->is_raw || ncomps > 1 ? 256 : 65536L))
+ param_signal_error(plist, vname,
+ ecode = gs_error_rangecheck);
+ else if (v == 2)
+ bpc = 1;
+ else if (v <= 4)
+ bpc = 2;
+ else if (v <= 16)
+ bpc = 4;
+ else if (v <= 32 && ncomps == 3)
+ bpc = 5;
+ else if (v <= 256)
+ bpc = 8;
+ else
+ bpc = 16;
+ if (ecode >= 0) {
+ static const byte depths[4][16] =
+ {
+ {1, 2, 0, 4, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 16},
+ {0},
+ {4, 8, 0, 16, 16, 0, 0, 24},
+ {4, 8, 0, 16, 0, 0, 0, 32},
+ };
+
+ pdev->color_info.depth = depths[ncomps - 1][bpc - 1];
+ pdev->color_info.max_gray = pdev->color_info.max_color =
+ (pdev->color_info.dither_grays =
+ pdev->color_info.dither_colors = (int)v) - 1;
+ }
+ }
+ if ((code = ecode) < 0 ||
+ (code = gdev_prn_put_params_planar(pdev, plist, &bdev->UsePlanarBuffer)) < 0
+ )
+ pdev->color_info = save_info;
+ ppm_set_dev_procs(pdev);
+ return code;
+}
+
+/* Copy an alpha map, noting whether we may generate some non-black/white */
+/* colors through blending. */
+static int
+pnm_copy_alpha(gx_device * pdev, const byte * data, int data_x,
+ int raster, gx_bitmap_id id, int x, int y, int width, int height,
+ gx_color_index color, int depth)
+{
+ gx_device_pbm * const bdev = (gx_device_pbm *)pdev;
+
+ if (pdev->color_info.depth < 24 ||
+ (color >> 8) == (color & 0xffff)
+ )
+ bdev->uses_color |= 1;
+ else
+ bdev->uses_color |= 2;
+ return (*bdev->save_copy_alpha) (pdev, data, data_x, raster, id,
+ x, y, width, height, color, depth);
+}
+
+/* Begin processing an image, noting whether we may generate some */
+/* non-black/white colors in the process. */
+static int
+pnm_begin_typed_image(gx_device *dev,
+ const gs_imager_state *pis, const gs_matrix *pmat,
+ const gs_image_common_t *pim, const gs_int_rect *prect,
+ const gx_drawing_color *pdcolor,
+ const gx_clip_path *pcpath,
+ gs_memory_t *memory, gx_image_enum_common_t **pinfo)
+{
+ gx_device_pbm * const bdev = (gx_device_pbm *)dev;
+ bool has_gray_icc;
+
+ /* Conservatively guesses whether this operation causes color usage
+ that might not be otherwise captured by ppm_map_color_rgb. */
+ if (pim && pim->type) {
+ switch (pim->type->index) {
+ case 1: case 3: case 4: {
+ /* Use colorspace to handle image types 1,3,4 */
+ const gs_pixel_image_t *pim1 = (const gs_pixel_image_t *)pim;
+
+ if (pim1->ColorSpace) {
+ has_gray_icc = false;
+ if (pim1->ColorSpace->cmm_icc_profile_data) {
+ if (pim1->ColorSpace->cmm_icc_profile_data->num_comps == 1) {
+ has_gray_icc = true;
+ }
+ }
+ if (gs_color_space_get_index(pim1->ColorSpace) ==
+ gs_color_space_index_DeviceGray || has_gray_icc) {
+ if (pim1->BitsPerComponent > 1)
+ bdev->uses_color |= 1;
+ } else
+ bdev->uses_color = 2;
+ }
+ break;
+ }
+ default:
+ /* Conservatively handles other image types */
+ bdev->uses_color = 2;
+ }
+ }
+ /* Forward to saved routine */
+ return (*bdev->save_begin_typed_image)(dev, pis, pmat, pim, prect,
+ pdcolor, pcpath, memory, pinfo);
+}
+
+/* ------ Internal routines ------ */
+
+/* NOP row processing function used when no output */
+static int nop_row_proc(gx_device_printer *pdev, byte *data, int len, FILE *f)
+{
+ return 0;
+}
+
+/* Print a page using a given row printing routine. */
+static int
+pbm_print_page_loop(gx_device_printer * pdev, char magic, FILE * pstream,
+ int (*row_proc) (gx_device_printer *, byte *, int, FILE *))
+{
+ gx_device_pbm * const bdev = (gx_device_pbm *)pdev;
+ uint raster = gdev_prn_raster_chunky(pdev);
+ byte *data = gs_alloc_bytes(pdev->memory, raster, "pbm_print_page_loop");
+ int lnum = 0;
+ int code = 0;
+ int output_is_nul = !strncmp(pdev->fname, "nul:", min(strlen(pdev->fname), 4)) ||
+ !strncmp(pdev->fname, "/dev/null", min(strlen(pdev->fname), 9));
+
+ if (data == 0)
+ return_error(gs_error_VMerror);
+ if (!output_is_nul) {
+ /* Hack. This should be done in the callers. */
+ if (magic == '9') {
+ if (fprintf(pstream, "%11d %11d %11d %11d %11d ",
+ 0, 0, 0, pdev->width, pdev->height) < 0) {
+ code = gs_note_error(gs_error_ioerror);
+ goto punt;
+ }
+ } else if (magic == '7') {
+ int ncomps = pdev->color_info.num_components;
+ if (fprintf(pstream, "P%c\n", magic) < 0) {
+ code = gs_note_error(gs_error_ioerror);
+ goto punt;
+ }
+ if (fprintf(pstream, "WIDTH %d\n", pdev->width) < 0) {
+ code = gs_note_error(gs_error_ioerror);
+ goto punt;
+ }
+ if (fprintf(pstream, "HEIGHT %d\n", pdev->height) < 0) {
+ code = gs_note_error(gs_error_ioerror);
+ goto punt;
+ }
+ if (fprintf(pstream, "DEPTH %d\n", ncomps) < 0) {
+ code = gs_note_error(gs_error_ioerror);
+ goto punt;
+ }
+ if (fprintf(pstream, "MAXVAL %d\n", 255) < 0) { /* force MAXVAL to 255 */
+ code = gs_note_error(gs_error_ioerror);
+ goto punt;
+ }
+ if (fprintf(pstream, "TUPLTYPE %s\n",
+ (ncomps == 4) ? "CMYK" :
+ ((ncomps == 3) ? "RGB" : "GRAYSCALE")) < 0) {
+ code = gs_note_error(gs_error_ioerror);
+ goto punt;
+ }
+ if (bdev->comment[0]) {
+ if (fprintf(pstream, "# %s\n", bdev->comment) < 0) {
+ code = gs_note_error(gs_error_ioerror);
+ goto punt;
+ }
+ } else {
+ if (fprintf(pstream, "# Image generated by %s\n", gs_product) < 0) {
+ code = gs_note_error(gs_error_ioerror);
+ goto punt;
+ }
+ }
+ if (fprintf(pstream, "ENDHDR\n") < 0) {
+ code = gs_note_error(gs_error_ioerror);
+ goto punt;
+ }
+ } else {
+ if (fprintf(pstream, "P%c\n", magic) < 0) {
+ code = gs_note_error(gs_error_ioerror);
+ goto punt;
+ }
+ if (bdev->comment[0]) {
+ if (fprintf(pstream, "# %s\n", bdev->comment) < 0) {
+ code = gs_note_error(gs_error_ioerror);
+ goto punt;
+ }
+ } else {
+ if (fprintf(pstream, "# Image generated by %s (device=%s)\n",
+ gs_product, pdev->dname) < 0) {
+ code = gs_note_error(gs_error_ioerror);
+ goto punt;
+ }
+ }
+ if (fprintf(pstream, "%d %d\n", pdev->width, pdev->height) < 0) {
+ code = gs_note_error(gs_error_ioerror);
+ goto punt;
+ }
+ }
+ switch (magic) {
+ case '1': /* pbm */
+ case '4': /* pbmraw */
+ case '7': /* pam */
+ case '9': /* plan9bm */
+ break;
+ case '3': /* pkm */
+ case '6': /* pkmraw */
+ if (fprintf(pstream, "%d\n", 255) < 0) {
+ code = gs_note_error(gs_error_ioerror);
+ goto punt;
+ }
+ break;
+ default:
+ if (fprintf(pstream, "%d\n", pdev->color_info.max_gray) < 0) {
+ code = gs_note_error(gs_error_ioerror);
+ goto punt;
+ }
+ }
+ }
+ if (output_is_nul)
+ row_proc = nop_row_proc;
+ for (; lnum < pdev->height; lnum++) {
+ byte *row;
+
+ code = gdev_prn_get_bits(pdev, lnum, data, &row);
+ if (code < 0)
+ break;
+ code = (*row_proc) (pdev, row, pdev->color_info.depth, pstream);
+ if (code < 0)
+ break;
+ }
+ punt:
+ gs_free_object(pdev->memory, data, "pbm_print_page_loop");
+ return (code < 0 ? code : 0);
+}
+
+/* ------ Individual page printing routines ------ */
+
+/* Print a monobit page. */
+static int
+pbm_print_row(gx_device_printer * pdev, byte * data, int depth,
+ FILE * pstream)
+{
+ gx_device_pbm * const bdev = (gx_device_pbm *)pdev;
+
+ if (bdev->is_raw) {
+ uint n = (pdev->width + 7) >> 3;
+
+ if (fwrite(data, 1, n, pstream) != n)
+ return_error(gs_error_ioerror);
+ } else {
+ byte *bp;
+ uint x, mask;
+
+ for (bp = data, x = 0, mask = 0x80; x < pdev->width;) {
+ if (putc((*bp & mask ? '1' : '0'), pstream) == EOF)
+ return_error(gs_error_ioerror);
+ if (++x == pdev->width || !(x & 63)) {
+ if (putc('\n', pstream) == EOF)
+ return_error(gs_error_ioerror);
+ }
+ if ((mask >>= 1) == 0)
+ bp++, mask = 0x80;
+ }
+ }
+ return 0;
+}
+static int
+pbm_print_page(gx_device_printer * pdev, FILE * pstream)
+{
+ gx_device_pbm * const bdev = (gx_device_pbm *)pdev;
+
+ return pbm_print_page_loop(pdev, bdev->magic, pstream, pbm_print_row);
+}
+
+/* Print a gray-mapped page. */
+static int
+pgm_print_row(gx_device_printer * pdev, byte * data, int depth,
+ FILE * pstream)
+{ /* Note that bpp <= 8 for raw format, bpp <= 16 for plain. */
+ gx_device_pbm * const bdev = (gx_device_pbm *)pdev;
+ uint mask = (1 << depth) - 1;
+ /*
+ * If we're writing planes for a CMYK device, we have 0 = white,
+ * mask = black, which is the opposite of the pgm convention.
+ */
+ uint invert = (pdev->color_info.polarity == GX_CINFO_POLARITY_SUBTRACTIVE);
+ byte *bp;
+ uint x;
+ int shift;
+
+ if (bdev->is_raw && depth == 8) {
+ if (invert) {
+ for (bp = data, x = 0; x < pdev->width; bp++, x++) {
+ if (putc((byte)~*bp, pstream) == EOF)
+ return_error(gs_error_ioerror);
+ }
+ } else {
+ if (fwrite(data, 1, pdev->width, pstream) != pdev->width)
+ return_error(gs_error_ioerror);
+ }
+ } else
+ for (bp = data, x = 0, shift = 8 - depth; x < pdev->width;) {
+ uint pixel;
+
+ if (shift < 0) { /* bpp = 16 */
+ pixel = ((uint) * bp << 8) + bp[1];
+ bp += 2;
+ } else {
+ pixel = (*bp >> shift) & mask;
+ if ((shift -= depth) < 0)
+ bp++, shift += 8;
+ }
+ ++x;
+ pixel ^= invert;
+ if (bdev->is_raw) {
+ if (putc(pixel, pstream) == EOF)
+ return_error(gs_error_ioerror);
+ } else {
+ if (fprintf(pstream, "%d%c", pixel,
+ (x == pdev->width || !(x & 15) ? '\n' : ' ')) < 0)
+ return_error(gs_error_ioerror);
+ }
+ }
+ return 0;
+}
+static int
+pxm_pbm_print_row(gx_device_printer * pdev, byte * data, int depth,
+ FILE * pstream)
+{ /* Compress a PGM or PPM row to a PBM row. */
+ /* This doesn't have to be very fast. */
+ /* Note that we have to invert the data as well. */
+ int delta = (depth + 7) >> 3;
+ byte *src = data + delta - 1; /* always big-endian */
+ byte *dest = data;
+ int x;
+ byte out_mask = 0x80;
+ byte out = 0;
+
+ if (depth >= 8) { /* One or more bytes per source pixel. */
+ for (x = 0; x < pdev->width; x++, src += delta) {
+ if (!(*src & 1))
+ out |= out_mask;
+ out_mask >>= 1;
+ if (!out_mask)
+ out_mask = 0x80,
+ *dest++ = out,
+ out = 0;
+ }
+ } else { /* Multiple source pixels per byte. */
+ byte in_mask = 0x100 >> depth;
+
+ for (x = 0; x < pdev->width; x++) {
+ if (!(*src & in_mask))
+ out |= out_mask;
+ in_mask >>= depth;
+ if (!in_mask)
+ in_mask = 0x100 >> depth,
+ src++;
+ out_mask >>= 1;
+ if (!out_mask)
+ out_mask = 0x80,
+ *dest++ = out,
+ out = 0;
+ }
+ }
+ if (out_mask != 0x80)
+ *dest = out;
+ return pbm_print_row(pdev, data, 1, pstream);
+}
+static int
+pgm_print_page(gx_device_printer * pdev, FILE * pstream)
+{
+ gx_device_pbm * const bdev = (gx_device_pbm *)pdev;
+
+ return (bdev->uses_color == 0 && bdev->optimize ?
+ pbm_print_page_loop(pdev, (char)((int)bdev->magic - 1), pstream,
+ pxm_pbm_print_row) :
+ pbm_print_page_loop(pdev, bdev->magic, pstream,
+ pgm_print_row));
+}
+
+/* Print a color-mapped page. */
+static int
+ppgm_print_row(gx_device_printer * pdev, byte * data, int depth,
+ FILE * pstream, bool color)
+{ /* If color=false, write only one value per pixel; */
+ /* if color=true, write 3 values per pixel. */
+ /* Note that depth <= 24 for raw format, depth <= 32 for plain. */
+ gx_device_pbm * const bdev = (gx_device_pbm *)pdev;
+ uint bpe = depth / 3; /* bits per r/g/b element */
+ uint mask = (1 << bpe) - 1;
+ byte *bp;
+ uint x;
+ uint eol_mask = (color ? 7 : 15);
+ int shift;
+
+ if (bdev->is_raw && depth == 24 && color) {
+ uint n = pdev->width * (depth / 8);
+
+ if (fwrite(data, 1, n, pstream) != n)
+ return_error(gs_error_ioerror);
+ } else {
+ for (bp = data, x = 0, shift = 8 - depth; x < pdev->width;) {
+ bits32 pixel = 0;
+ uint r, g, b;
+
+ switch (depth >> 3) {
+ case 4:
+ pixel = (bits32) * bp << 24;
+ bp++;
+ /* falls through */
+ case 3:
+ pixel += (bits32) * bp << 16;
+ bp++;
+ /* falls through */
+ case 2:
+ pixel += (uint) * bp << 8;
+ bp++;
+ /* falls through */
+ case 1:
+ pixel += *bp;
+ bp++;
+ break;
+ case 0: /* bpp == 4, bpe == 1 */
+ pixel = *bp >> shift;
+ if ((shift -= depth) < 0)
+ bp++, shift += 8;
+ break;
+ }
+ ++x;
+ b = pixel & mask;
+ pixel >>= bpe;
+ g = pixel & mask;
+ pixel >>= bpe;
+ r = pixel & mask;
+ if (bdev->is_raw) {
+ if (color) {
+ if (putc(r, pstream) == EOF)
+ return_error(gs_error_ioerror);
+ if (putc(g, pstream) == EOF)
+ return_error(gs_error_ioerror);
+ }
+ if (putc(b, pstream) == EOF)
+ return_error(gs_error_ioerror);
+ } else {
+ if (color) {
+ if (fprintf(pstream, "%d %d ", r, g) < 0)
+ return_error(gs_error_ioerror);
+ }
+ if (fprintf(pstream, "%d%c", b,
+ (x == pdev->width || !(x & eol_mask) ?
+ '\n' : ' ')) < 0)
+ return_error(gs_error_ioerror);
+ }
+ }
+ }
+ return 0;
+}
+static int
+ppm_print_row(gx_device_printer * pdev, byte * data, int depth,
+ FILE * pstream)
+{
+ return ppgm_print_row(pdev, data, depth, pstream, true);
+}
+static int
+ppm_pgm_print_row(gx_device_printer * pdev, byte * data, int depth,
+ FILE * pstream)
+{
+ return ppgm_print_row(pdev, data, depth, pstream, false);
+}
+static int
+ppm_print_page(gx_device_printer * pdev, FILE * pstream)
+{
+ gx_device_pbm * const bdev = (gx_device_pbm *)pdev;
+
+ return (bdev->uses_color >= 2 || !bdev->optimize ?
+ pbm_print_page_loop(pdev, bdev->magic, pstream,
+ ppm_print_row) :
+ bdev->uses_color == 1 ?
+ pbm_print_page_loop(pdev, (char)((int)bdev->magic - 1), pstream,
+ ppm_pgm_print_row) :
+ pbm_print_page_loop(pdev, (char)((int)bdev->magic - 2), pstream,
+ pxm_pbm_print_row));
+}
+
+static int
+pam_print_row(gx_device_printer * pdev, byte * data, int depth,
+ FILE * pstream)
+{
+ if (depth == 32) {
+ uint n = pdev->width * (depth / 8);
+
+ if (fwrite(data, 1, n, pstream) != n)
+ return_error(gs_error_ioerror);
+ }
+ return 0;
+}
+
+static int
+pam_print_page(gx_device_printer * pdev, FILE * pstream)
+{
+ gx_device_pbm * const bdev = (gx_device_pbm *)pdev;
+
+ return pbm_print_page_loop(pdev, bdev->magic, pstream,
+ pam_print_row);
+}
+
+static int
+pnmcmyk_print_page(gx_device_printer *pdev, FILE *pstream)
+{
+ if (pdev->icc_struct->graydetection == true && pdev->icc_struct->pageneutralcolor == true) {
+ /* Here we need to convert the data from CMYK to K (gray) then print */
+ gx_device_pbm * const bdev = (gx_device_pbm *)pdev;
+ uint raster = gdev_prn_raster_chunky(pdev); /* enough space for the CMYK data */
+ byte *data = gs_alloc_bytes(pdev->memory, raster, "pbm_print_page_loop");
+ int lnum = 0;
+ int code = 0;
+ int output_is_nul = !strncmp(pdev->fname, "nul:", min(strlen(pdev->fname), 4)) ||
+ !strncmp(pdev->fname, "/dev/null", min(strlen(pdev->fname), 9));
+ int (*row_proc) (gx_device_printer *, byte *, int, FILE *);
+
+ if (!output_is_nul) {
+ if (fprintf(pstream, "P5\n") < 0) { /* PGM raw */
+ code = gs_note_error(gs_error_ioerror);
+ goto punt;
+ }
+ if (bdev->comment[0]) {
+ if (fprintf(pstream, "# %s\n", bdev->comment) < 0) {
+ code = gs_note_error(gs_error_ioerror);
+ goto punt;
+ }
+ } else {
+ if (fprintf(pstream, "# Image generated by %s (device=%s)\n",
+ gs_product, pdev->dname) < 0) {
+ code = gs_note_error(gs_error_ioerror);
+ goto punt;
+ }
+ }
+ if (fprintf(pstream, "%d %d\n", pdev->width, pdev->height) < 0) {
+ code = gs_note_error(gs_error_ioerror);
+ goto punt;
+ }
+ if (fprintf(pstream, "255\n") < 0) {
+ code = gs_note_error(gs_error_ioerror);
+ goto punt;
+ }
+ row_proc = pgm_print_row;
+ } else
+ row_proc = nop_row_proc;
+
+
+ for (; lnum < pdev->height; lnum++) {
+ byte *row, *row_end;
+ byte *pcmyk, *pgray; /* scan pointers through the row */
+
+ code = gdev_prn_get_bits(pdev, lnum, data, &row);
+ if (code < 0)
+ break;
+ /* convert the CMYK to Gray */
+ pgray = row; /* destination for converted color */
+ row_end = row + (4 * pdev->width);
+ for (pcmyk = row; pcmyk < row_end;) {
+ int32_t cmy;
+ byte k;
+
+ /* For now we assume that the CMYK may have gone through an ICC profile */
+ /* so we do a more complex conversion from CMYK to K. If we are using */
+ /* FastColor, we may be able to do this more efficiently. */
+ cmy = ((255 - *pcmyk++) * lum_red_weight);
+ cmy += ((255 - *pcmyk++) * lum_green_weight);
+ cmy += ((255 - *pcmyk++) * lum_blue_weight);
+ cmy += (lum_all_weights / 2);
+ cmy /= lum_all_weights;
+
+ k = *pcmyk++;
+ if (k > cmy)
+ k = 0; /* additive black */
+ else
+ k = cmy - k; /* additive gray */
+ *pgray++ = k; /* store it */
+ }
+ /* we converted to normal "additive" gray (white == 1) so set */
+ /* the color_info.polarity so that pgm_print_row doesn't invert */
+ pdev->color_info.polarity = GX_CINFO_POLARITY_ADDITIVE;
+ code = (*row_proc) (pdev, row, 8, pstream);
+ pdev->color_info.polarity = GX_CINFO_POLARITY_SUBTRACTIVE; /* restore actual polarity */
+ if (code < 0)
+ break;
+ }
+ punt:
+ gs_free_object(pdev->memory, data, "pbm_print_page_loop");
+ return (code < 0 ? code : 0);
+ }
+ /* otherwise, just spit out the CMYK 32-bit PAM format */
+ return pam_print_page(pdev, pstream);
+}
+
+static int
+pam4_print_row(gx_device_printer * pdev, byte * data, int depth,
+ FILE * pstream)
+{
+ int w, s;
+ if (depth == 4) {
+ for (w = pdev->width; w > 0;) {
+ byte C = *data++;
+ for (s = 7; s >= 0; s -= 4)
+ {
+ fputc(((C>>s )&1)*0xff, pstream);
+ fputc(((C>>(s-1))&1)*0xff, pstream);
+ fputc(((C>>(s-2))&1)*0xff, pstream);
+ fputc(((C>>(s-3))&1)*0xff, pstream);
+ w--;
+ if (w == 0)
+ break;
+ }
+ }
+ }
+ return 0;
+}
+
+static int
+pam4_print_page(gx_device_printer * pdev, FILE * pstream)
+{
+ gx_device_pbm * const bdev = (gx_device_pbm *)pdev;
+
+ return pbm_print_page_loop(pdev, bdev->magic, pstream,
+ pam4_print_row);
+}
+
+/* Print a faux CMYK page. */
+/* Print a row where each pixel occupies 4 bits (depth == 4). */
+/* In this case, we also know pdev->color_info.max_color == 1. */
+static int
+pkm_print_row_4(gx_device_printer * pdev, byte * data, int depth,
+ FILE * pstream)
+{
+ gx_device_pbm * const bdev = (gx_device_pbm *)pdev;
+ byte *bp;
+ uint x;
+ byte rv[16], gv[16], bv[16], i;
+
+ /* Precompute all the possible pixel values. */
+ for (i = 0; i < 16; ++i) {
+ gx_color_value rgb[3];
+
+ cmyk_1bit_map_color_rgb((gx_device *)pdev, (gx_color_index)i, rgb);
+ rv[i] = rgb[0] / gx_max_color_value * 0xff;
+ gv[i] = rgb[1] / gx_max_color_value * 0xff;
+ bv[i] = rgb[2] / gx_max_color_value * 0xff;
+ }
+ /*
+ * Contrary to what the documentation implies, gcc compiles putc
+ * as a procedure call. This is ridiculous, but since we can't
+ * change it, we buffer groups of pixels ourselves and use fwrite.
+ */
+ if (bdev->is_raw) {
+ for (bp = data, x = 0; x < pdev->width;) {
+ byte raw[50 * 3]; /* 50 is arbitrary, but must be even */
+ int end = min(x + sizeof(raw) / 3, pdev->width);
+ byte *outp = raw;
+
+ for (; x < end; bp++, outp += 6, x += 2) {
+ uint b = *bp;
+ uint pixel = b >> 4;
+
+ outp[0] = rv[pixel], outp[1] = gv[pixel], outp[2] = bv[pixel];
+ pixel = b & 0xf;
+ outp[3] = rv[pixel], outp[4] = gv[pixel], outp[5] = bv[pixel];
+ }
+ /* x might overshoot the width by 1 pixel. */
+ if (x > end)
+ outp -= 3;
+ if (fwrite(raw, 1, outp - raw, pstream) != outp - raw)
+ return_error(gs_error_ioerror);
+ }
+ } else {
+ int shift;
+
+ for (bp = data, x = 0, shift = 4; x < pdev->width;) {
+ int pixel = (*bp >> shift) & 0xf;
+
+ shift ^= 4;
+ bp += shift >> 2;
+ ++x;
+ if (fprintf(pstream, "%d %d %d%c", rv[pixel], gv[pixel], bv[pixel],
+ (x == pdev->width || !(x & 7) ?
+ '\n' : ' ')) < 0)
+ return_error(gs_error_ioerror);
+ }
+ }
+ return 0;
+}
+/* Print a row where each pixel occupies 1 or more bytes (depth >= 8). */
+/* Note that the output is scaled up to 255 max value. */
+static int
+pkm_print_row(gx_device_printer * pdev, byte * data, int depth,
+ FILE * pstream)
+{
+ gx_device_pbm * const bdev = (gx_device_pbm *)pdev;
+ byte *bp;
+ uint x;
+
+ for (bp = data, x = 0; x < pdev->width;) {
+ bits32 pixel = 0;
+ gx_color_value rgb[3];
+ uint r, g, b;
+
+ switch (depth >> 3) {
+ case 4:
+ pixel = (bits32) * bp << 24;
+ bp++;
+ /* falls through */
+ case 3:
+ pixel += (bits32) * bp << 16;
+ bp++;
+ /* falls through */
+ case 2:
+ pixel += (uint) * bp << 8;
+ bp++;
+ /* falls through */
+ case 1:
+ pixel += *bp;
+ bp++;
+ }
+ ++x;
+ pkm_map_color_rgb((gx_device *) pdev, pixel, rgb);
+ r = rgb[0] * 0xff / gx_max_color_value;
+ g = rgb[1] * 0xff / gx_max_color_value;
+ b = rgb[2] * 0xff / gx_max_color_value;
+ if (bdev->is_raw) {
+ if (putc(r, pstream) == EOF)
+ return_error(gs_error_ioerror);
+ if (putc(g, pstream) == EOF)
+ return_error(gs_error_ioerror);
+ if (putc(b, pstream) == EOF)
+ return_error(gs_error_ioerror);
+ } else {
+ if (fprintf(pstream, "%d %d %d%c", r, g, b,
+ (x == pdev->width || !(x & 7) ?
+ '\n' : ' ')) < 0)
+ return_error(gs_error_ioerror);
+ }
+ }
+ return 0;
+}
+static int
+pkm_print_page(gx_device_printer * pdev, FILE * pstream)
+{
+ gx_device_pbm * const bdev = (gx_device_pbm *)pdev;
+
+ return pbm_print_page_loop(pdev, bdev->magic, pstream,
+ (pdev->color_info.depth < 8 ?
+ pkm_print_row_4 :
+ pkm_print_row));
+}
+
+/* Print individual separations on a single file. */
+static int
+psm_print_page(gx_device_printer * pdev, FILE * pstream)
+{
+ gx_device_pbm * const bdev = (gx_device_pbm *)pdev;
+ /*
+ * Allocate a large enough buffer for full pixels, on the theory that we
+ * don't know how many bits will be allocated to each component. (This
+ * is for didactic purposes only: we know perfectly well that each
+ * component will have 1/N of the bits.)
+ */
+ uint max_raster = bitmap_raster(pdev->width * pdev->color_info.depth);
+ byte *data = gs_alloc_bytes(pdev->memory, max_raster, "pksm_print_page");
+ int code = 0;
+ int plane;
+
+ if (data == 0)
+ return_error(gs_error_VMerror);
+ for (plane = 0; plane < pdev->color_info.num_components; ++plane) {
+ int lnum, band_end;
+ /*
+ * The following initialization is unnecessary: lnum == band_end on
+ * the first pass through the loop below, so marked will always be
+ * set before it is used. We initialize marked solely to suppress
+ * bogus warning messages from certain compilers.
+ */
+ gx_color_index marked = 0;
+ gx_render_plane_t render_plane;
+ int plane_depth;
+ int plane_shift;
+ gx_color_index plane_mask;
+ int raster;
+
+ gx_render_plane_init(&render_plane, (gx_device *)pdev, plane);
+ plane_depth = render_plane.depth;
+ plane_shift = render_plane.shift;
+ plane_mask = (1 << plane_depth) - 1;
+ raster = bitmap_raster(pdev->width * plane_depth);
+ if (fprintf(pstream, "P%c\n", bdev->magic + (plane_depth > 1)) < 0) {
+ code = gs_note_error(gs_error_ioerror);
+ goto punt;
+ }
+ if (bdev->comment[0]) {
+ if (fprintf(pstream, "# %s\n", bdev->comment) < 0) {
+ code = gs_note_error(gs_error_ioerror);
+ goto punt;
+ }
+ } else {
+ if (fprintf(pstream, "# Image generated by %s (device=%s)\n",
+ gs_product, pdev->dname) < 0) {
+ code = gs_note_error(gs_error_ioerror);
+ goto punt;
+ }
+ }
+ if (fprintf(pstream, "%d %d\n", pdev->width, pdev->height) < 0) {
+ code = gs_note_error(gs_error_ioerror);
+ goto punt;
+ }
+ if (plane_depth > 1) {
+ if (fprintf(pstream, "%d\n", pdev->color_info.max_gray) < 0) {
+ code = gs_note_error(gs_error_ioerror);
+ goto punt;
+ }
+ }
+ for (lnum = band_end = 0; lnum < pdev->height; lnum++) {
+ byte *row;
+
+ if (lnum == band_end) {
+ gx_color_usage_t color_usage;
+ int band_start;
+ int band_height =
+ gdev_prn_color_usage((gx_device *)pdev, lnum, 1,
+ &color_usage, &band_start);
+
+ band_end = band_start + band_height;
+ marked = color_usage.or & (plane_mask << plane_shift);
+ if (!marked)
+ memset(data, 0, raster);
+#ifdef DEBUG
+ if (plane == 0)
+ if_debug4m(':', pdev->memory,
+ "[:]%4d - %4d mask = 0x%lx, slow_rop = %d\n",
+ lnum, band_end - 1, (ulong)color_usage.or,
+ color_usage.slow_rop);
+#endif
+ }
+ if (marked) {
+ gx_render_plane_t render_plane;
+ uint actual_raster;
+
+ render_plane.index = plane;
+ code = gdev_prn_get_lines(pdev, lnum, 1, data, raster,
+ &row, &actual_raster,
+ &render_plane);
+ if (code < 0)
+ break;
+ } else
+ row = data;
+ code =
+ (plane_depth == 1 ?
+ pbm_print_row(pdev, row, plane_depth, pstream) :
+ pgm_print_row(pdev, row, plane_depth, pstream));
+ if (code < 0)
+ break;
+ }
+ }
+ punt:
+ gs_free_object(pdev->memory, data, "pksm_print_page");
+ return (code < 0 ? code : 0);
+}
diff --git a/devices/gdevpcfb.c b/devices/gdevpcfb.c
new file mode 100644
index 000000000..6870e78b1
--- /dev/null
+++ b/devices/gdevpcfb.c
@@ -0,0 +1,908 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* IBM PC frame buffer (EGA/VGA) drivers */
+#include "memory_.h"
+#include "gx.h"
+#include "gserrors.h"
+#include "gsparam.h"
+#include "gxdevice.h"
+#include "gdevpccm.h"
+#include "gdevpcfb.h"
+
+/* We may compile this in a non-segmented environment.... */
+#ifndef _ss
+#define _ss
+#endif
+
+/* Macro for casting gx_device argument */
+#define fb_dev ((gx_device_ega *)dev)
+
+/* Procedure record */
+static dev_proc_map_rgb_color(ega0_map_rgb_color);
+static dev_proc_map_rgb_color(ega1_map_rgb_color);
+#define ega2_map_rgb_color pc_4bit_map_rgb_color
+static dev_proc_map_color_rgb(ega01_map_color_rgb);
+#define ega2_map_color_rgb pc_4bit_map_color_rgb
+#if ega_bits_of_color == 0
+# define ega_map_rgb_color ega0_map_rgb_color
+# define ega_map_color_rgb ega01_map_color_rgb
+#else
+# if ega_bits_of_color == 1
+# define ega_map_rgb_color ega1_map_rgb_color
+# define ega_map_color_rgb ega01_map_color_rgb
+# else
+# define ega_map_rgb_color ega2_map_rgb_color
+# define ega_map_color_rgb ega2_map_color_rgb
+# endif
+#endif
+#define ega_std_procs(get_params, put_params)\
+ ega_open,\
+ NULL, /* get_initial_matrix */\
+ NULL, /* sync_output */\
+ NULL, /* output_page */\
+ ega_close,\
+ ega_map_rgb_color,\
+ ega_map_color_rgb,\
+ ega_fill_rectangle,\
+ ega_tile_rectangle,\
+ ega_copy_mono,\
+ ega_copy_color,\
+ NULL, /* draw_line */\
+ ega_get_bits,\
+ get_params,\
+ put_params,\
+ NULL, /* map_cmyk_color */\
+ NULL, /* get_xfont_procs */\
+ NULL, /* get_xfont_device */\
+ NULL, /* map_rgb_alpha_color */\
+ gx_page_device_get_page_device
+
+static const gx_device_procs ega_procs =
+{
+ ega_std_procs(NULL, NULL)
+};
+
+static dev_proc_get_params(svga16_get_params);
+static dev_proc_put_params(svga16_put_params);
+static const gx_device_procs svga16_procs =
+{
+ ega_std_procs(svga16_get_params, svga16_put_params)
+};
+
+/* All the known instances */
+ /* EGA */
+gx_device_ega far_data gs_ega_device =
+ega_device("ega", ega_procs, 80, 350, 48.0 / 35.0, 0x10);
+
+ /* VGA */
+gx_device_ega far_data gs_vga_device =
+ega_device("vga", ega_procs, 80, 480, 1.0, 0x12);
+
+ /* Generic SuperVGA, 800x600, 16-color mode */
+gx_device_ega far_data gs_svga16_device =
+ega_device("svga16", svga16_procs, 100, 600, 1.0, 0x29 /*Tseng */ );
+
+/* Save the BIOS state */
+static pcfb_bios_state pcfb_save_state =
+{-1};
+
+/* Initialize the EGA for graphics mode */
+int
+ega_open(gx_device * dev)
+{ /* Adjust the device resolution. */
+ /* This is a hack, pending refactoring of the put_params machinery. */
+ switch (fb_dev->video_mode) {
+ case 0x10: /* EGA */
+ gx_device_adjust_resolution(dev, 640, 350, 1);
+ break;
+ case 0x12: /* VGA */
+ gx_device_adjust_resolution(dev, 640, 480, 1);
+ break;
+ default: /* 800x600 SuperVGA */
+ gx_device_adjust_resolution(dev, 800, 600, 1);
+ break;
+ }
+ if (pcfb_save_state.display_mode < 0)
+ pcfb_get_state(&pcfb_save_state);
+ /* Do implementation-specific initialization */
+ pcfb_set_signals(dev);
+ pcfb_set_mode(fb_dev->video_mode);
+ set_s_map(-1); /* enable all maps */
+ return 0;
+}
+
+/* Reinitialize the EGA for text mode */
+int
+ega_close(gx_device * dev)
+{
+ if (pcfb_save_state.display_mode >= 0)
+ pcfb_set_state(&pcfb_save_state);
+ return 0;
+}
+
+/* Get/put the display mode parameter. */
+static int
+svga16_get_params(gx_device * dev, gs_param_list * plist)
+{
+ int code = gx_default_get_params(dev, plist);
+
+ if (code < 0)
+ return code;
+ return param_write_int(plist, "DisplayMode", &fb_dev->video_mode);
+}
+static int
+svga16_put_params(gx_device * dev, gs_param_list * plist)
+{
+ int ecode = 0;
+ int code;
+ int imode = fb_dev->video_mode;
+ const char *param_name;
+
+ switch (code = param_read_int(plist, (param_name = "DisplayMode"), &imode)) {
+ default:
+ ecode = code;
+ param_signal_error(plist, param_name, ecode);
+ case 0:
+ case 1:
+ break;
+ }
+
+ if (ecode < 0)
+ return ecode;
+ code = gx_default_put_params(dev, plist);
+ if (code < 0)
+ return code;
+
+ if (imode != fb_dev->video_mode) {
+ if (dev->is_open)
+ gs_closedevice(dev);
+ fb_dev->video_mode = imode;
+ }
+ return 0;
+}
+
+/* Map a r-g-b color to an EGA color code. */
+static gx_color_index
+ega0_map_rgb_color(gx_device * dev, const gx_color_value cv[])
+{
+ return pc_4bit_map_rgb_color(dev, cv);
+}
+static gx_color_index
+ega1_map_rgb_color(gx_device * dev, const gx_color_value cv[])
+{
+ const gx_color_value cvtop = (1 << (gx_color_value_bits - 1));
+ gx_color_value cvt[3];
+ cvt[0] = cv[0] & cvtop;
+ cvt[1] = cv[1] & cvtop;
+ cvt[2] = cv[2] & cvtop;
+ return pc_4bit_map_rgb_color(dev, cvt);
+}
+
+/* Map a color code to r-g-b. */
+#define icolor (int)color
+static int
+ega01_map_color_rgb(gx_device * dev, gx_color_index color,
+ gx_color_value prgb[3])
+{
+#define one (gx_max_color_value / 2 + 1)
+ prgb[0] = (icolor & 4 ? one : 0);
+ prgb[1] = (icolor & 2 ? one : 0);
+ prgb[2] = (icolor & 1 ? one : 0);
+ return 0;
+#undef one
+}
+#undef icolor
+
+/* ------ Internal routines ------ */
+
+/* Structure for operation parameters. */
+/* Note that this structure is known to assembly code. */
+/* Not all parameters are used for every operation. */
+typedef struct rop_params_s {
+ fb_ptr dest; /* pointer to frame buffer */
+ int draster; /* raster of frame buffer */
+ const byte *src; /* pointer to source data */
+ int sraster; /* source raster */
+ int width; /* width in bytes */
+ int height; /* height in scan lines */
+ int shift; /* amount to right shift source */
+ int invert; /* 0 or -1 to invert source */
+ int data; /* data for fill */
+} rop_params;
+typedef rop_params _ss *rop_ptr;
+
+/* Assembly language routines */
+
+static void
+memsetcol(rop_ptr rop) /* dest, draster, height, data */
+{
+ byte *addr = rop->dest;
+ int yc = rop->height;
+ byte data = rop->data;
+ int draster = rop->draster;
+
+ while (yc--) {
+ byte_discard(*addr);
+ *addr = data;
+ addr += draster;
+ }
+}
+
+static void
+memsetrect(rop_ptr rop) /* dest, draster, width, height, data */
+{
+ int yc = rop->height;
+ int width = rop->width;
+
+ if (yc <= 0 || width <= 0)
+ return;
+ {
+ byte *addr = rop->dest;
+ byte data = rop->data;
+
+ if (width > 5) { /* use memset */
+ int skip = rop->draster;
+
+ do {
+ memset(addr, data, width);
+ addr += skip;
+ }
+ while (--yc);
+ } else { /* avoid the fixed overhead */
+ int skip = rop->draster - width;
+
+ do {
+ int cnt = width;
+
+ do {
+ *addr++ = data;
+ } while (--cnt);
+ addr += skip;
+ }
+ while (--yc);
+ }
+ }
+}
+
+static void
+memrwcol(rop_ptr rop) /* dest, draster, src, sraster, height, shift, invert */
+
+{
+ byte *dp = rop->dest;
+ const byte *sp = rop->src;
+ int yc = rop->height;
+ int shift = rop->shift;
+ byte invert = rop->invert;
+ int sraster = rop->sraster, draster = rop->draster;
+
+ while (yc--) {
+ byte_discard(*dp);
+ *dp = ((*sp >> shift) + (*sp << (8 - shift))) ^ invert;
+ dp += draster, sp += sraster;
+ }
+}
+static void
+memrwcol0(rop_ptr rop) /* same except shift = 0 */
+{
+ byte *dp = rop->dest;
+ const byte *sp = rop->src;
+ int yc = rop->height;
+ byte invert = rop->invert;
+ int sraster = rop->sraster, draster = rop->draster;
+
+ if (yc > 0)
+ do {
+ byte_discard(*dp);
+ *dp = *sp ^ invert;
+ dp += draster, sp += sraster;
+ }
+ while (--yc);
+}
+
+static void
+memrwcol2(rop_ptr rop) /* dest, draster, src, sraster, height, shift, invert */
+{
+ byte *dp = rop->dest;
+ const byte *sp = rop->src;
+ int yc = rop->height;
+ int shift = rop->shift;
+ byte invert = rop->invert;
+ int sraster = rop->sraster, draster = rop->draster;
+
+ while (yc--) {
+ byte_discard(*dp);
+ *dp = ((sp[1] >> shift) + (*sp << (8 - shift))) ^ invert;
+ dp += draster, sp += sraster;
+ }
+}
+
+/* Forward definitions */
+int ega_write_dot(gx_device *, int, int, gx_color_index);
+static void fill_rectangle(rop_ptr, int, int, int);
+static void fill_row_only(byte *, int, int, int);
+
+/* Clean up after writing */
+#define dot_end()\
+ set_g_mask(0xff) /* all bits on */
+
+/* Write a dot using the EGA color codes. */
+/* This doesn't have to be efficient. */
+int
+ega_write_dot(gx_device * dev, int x, int y, gx_color_index color)
+{
+ byte data[4];
+
+ data[0] = (byte) color;
+ return ega_copy_color(dev, data, 1, 4, gx_no_bitmap_id, x, y, 1, 1);
+}
+
+/* Macro for testing bit-inclusion */
+#define bit_included_in(x,y) !((x)&~(y))
+
+/* Copy a monochrome bitmap. The colors are given explicitly. */
+/* Color = gx_no_color_index means transparent (no effect on the image). */
+int
+ega_copy_mono(gx_device * dev,
+ const byte * base, int sourcex, int raster, gx_bitmap_id id,
+ int x, int y, int w, int h, gx_color_index izero, gx_color_index ione)
+{
+ rop_params params;
+
+#define czero (int)izero
+#define cone (int)ione
+ int dleft, count;
+ byte mask, rmask;
+ fb_ptr save_dest;
+ int other_color = -1;
+
+ fit_copy(dev, base, sourcex, raster, id, x, y, w, h);
+ params.dest = mk_fb_ptr(x, y);
+ params.draster = fb_dev->raster;
+ params.src = base + (sourcex >> 3);
+ params.sraster = raster;
+ params.height = h;
+ params.shift = (x - sourcex) & 7;
+ /* Analyze the 16 possible cases: each of izero and ione may be */
+ /* 0, 0xf, transparent, or some other color. */
+ switch (czero) {
+ case no_color:
+ switch (cone) {
+ default: /* (T, other) */
+ /* Must do 2 passes */
+ other_color = cone;
+ save_dest = params.dest;
+ /* falls through */
+ case 0: /* (T, 0) */
+ set_g_function(gf_AND);
+ params.invert = -1;
+ break;
+ case 0xf: /* (T, 0xf) */
+ set_g_function(gf_OR);
+ params.invert = 0;
+ break;
+ case no_color: /* (T, T) */
+ return 0; /* nothing to do */
+ }
+ break;
+ case 0:
+ params.invert = 0;
+ switch (cone) {
+ default: /* (0, other) */
+ set_g_const(0);
+ set_g_const_map(cone ^ 0xf);
+ /* falls through */
+ case 0xf: /* (0, 0xf) */
+ break;
+ case no_color: /* (0, T) */
+ set_g_function(gf_AND);
+ break;
+ }
+ break;
+ case 0xf:
+ params.invert = -1;
+ switch (cone) {
+ case 0: /* (0xf, 0) */
+ break;
+ default: /* (0xf, other) */
+ set_g_const(0xf);
+ set_g_const_map(cone);
+ break;
+ case no_color: /* (0xf, T) */
+ set_g_function(gf_OR);
+ /* falls through */
+ }
+ break;
+ default:
+ switch (cone) {
+ default: /* (other, not T) */
+ if (bit_included_in(czero, cone)) {
+ set_g_const(czero);
+ set_g_const_map(czero ^ cone ^ 0xf);
+ params.invert = 0;
+ break;
+ } else if (bit_included_in(cone, czero)) {
+ set_g_const(cone);
+ set_g_const_map(cone ^ czero ^ 0xf);
+ params.invert = -1;
+ break;
+ }
+ /* No way around it, fill with one color first. */
+ save_dest = params.dest;
+ fill_rectangle((rop_ptr) & params, x & 7, w, cone);
+ params.dest = save_dest;
+ set_g_function(gf_XOR);
+ set_s_map(czero ^ cone);
+ other_color = -2; /* must reset s_map at end */
+ params.invert = -1;
+ break;
+ case no_color: /* (other, T) */
+ /* Must do 2 passes */
+ other_color = czero;
+ save_dest = params.dest;
+ set_g_function(gf_AND);
+ params.invert = 0;
+ break;
+ }
+ break;
+ }
+ /* Actually copy the bits. */
+ dleft = 8 - (x & 7);
+ mask = 0xff >> (8 - dleft);
+ count = w - dleft;
+ if (count < 0)
+ mask -= mask >> w,
+ rmask = 0;
+ else
+ rmask = 0xff00 >> (count & 7);
+ /* params: dest, src, sraster, height, shift, invert */
+ /* Smashes params.src, params.dest, count. */
+ copy:set_g_mask(mask);
+ if (params.shift == 0) { /* optimize the aligned case *//* Do left column */
+ memrwcol0((rop_ptr) & params);
+ /* Do center */
+ if ((count -= 8) >= 0) {
+ out_g_mask(0xff);
+ do {
+ params.src++, params.dest++;
+ memrwcol0((rop_ptr) & params);
+ }
+ while ((count -= 8) >= 0);
+ }
+ /* Do right column */
+ if (rmask) {
+ params.src++, params.dest++;
+ out_g_mask(rmask);
+ memrwcol0((rop_ptr) & params);
+ }
+ } else { /* Do left column */
+ int sleft = 8 - (sourcex & 7);
+
+ if (sleft >= dleft) { /* Source fits in one byte */
+ memrwcol((rop_ptr) & params);
+ } else if (w <= sleft) { /* Source fits in one byte, thin case */
+ memrwcol((rop_ptr) & params);
+ goto fin;
+ } else {
+ memrwcol2((rop_ptr) & params);
+ params.src++;
+ }
+ /* Do center */
+ if ((count -= 8) >= 0) {
+ out_g_mask(0xff);
+ do {
+ params.dest++;
+ memrwcol2((rop_ptr) & params);
+ params.src++;
+ }
+ while ((count -= 8) >= 0);
+ }
+ /* Do right column */
+ if (rmask) {
+ out_g_mask(rmask);
+ params.dest++;
+ if (count + 8 <= params.shift)
+ memrwcol((rop_ptr) & params);
+ else
+ memrwcol2((rop_ptr) & params);
+ }
+ }
+ fin:if (other_color != -1) {
+ if (other_color >= 0) { /* Do the second pass on (T, other) or (other, T). */
+ count = w - dleft;
+ params.src = base + (sourcex >> 3);
+ params.dest = save_dest;
+ params.invert ^= -1;
+ set_s_map(other_color);
+ set_g_function(gf_OR);
+ other_color = -2;
+ goto copy;
+ } else { /* Finished second pass, restore s_map */
+ set_s_map(-1);
+ }
+ }
+ set_g_function(gf_WRITE);
+ set_g_const_map(0);
+ dot_end();
+ return 0;
+#undef czero
+#undef cone
+}
+
+/* Copy a color pixelmap. This is just like a bitmap, */
+/* except that each pixel takes 4 bits instead of 1. */
+int
+ega_copy_color(gx_device * dev,
+ const byte * base, int sourcex, int raster, gx_bitmap_id id,
+ int x, int y, int w, int h)
+{
+ const byte *line = base + (sourcex >> 1);
+ unsigned mask = 0x80 >> (x & 7);
+ int px = sourcex & 1;
+ fb_ptr fb_line;
+ int fb_raster = fb_dev->raster;
+
+ fit_copy(dev, base, sourcex, raster, id, x, y, w, h);
+ fb_line = mk_fb_ptr(x, y);
+ set_g_mode(gm_FILL);
+ select_g_mask();
+ for (;; px++) {
+ const byte *bptr = line;
+ fb_ptr fbptr = fb_line;
+ int py = h;
+
+ out_g_mask(mask);
+ if (px & 1) {
+ do {
+ byte_discard(*fbptr); /* latch frame buffer data */
+ *fbptr = *bptr;
+ bptr += raster;
+ fbptr += fb_raster;
+ }
+ while (--py);
+ line++;
+ } else {
+ do {
+ byte_discard(*fbptr); /* latch frame buffer data */
+ *fbptr = *bptr >> 4;
+ bptr += raster;
+ fbptr += fb_raster;
+ }
+ while (--py);
+ }
+ if (!--w)
+ break;
+ if ((mask >>= 1) == 0)
+ mask = 0x80, fb_line++;
+ }
+ set_g_mode(gm_DATA);
+ dot_end();
+ return 0;
+}
+
+/* Fill a rectangle. */
+int
+ega_fill_rectangle(gx_device * dev, int x, int y, int w, int h,
+ gx_color_index color)
+{
+ rop_params params;
+
+ fit_fill(dev, x, y, w, h);
+ params.dest = mk_fb_ptr(x, y);
+ if (h == 1)
+ fill_row_only(params.dest, x & 7, w, (int)color);
+ else {
+ params.draster = fb_dev->raster;
+ params.height = h;
+ fill_rectangle((rop_ptr) & params, x & 7, w, (int)color);
+ dot_end();
+ }
+ return 0;
+}
+
+/* Tile a rectangle. Note that the two colors must both be supplied, */
+/* i.e. neither one can be gx_no_color_index (transparent): */
+/* a transparent color means that the tile is colored, not a mask. */
+int
+ega_tile_rectangle(gx_device * dev, const gx_tile_bitmap * tile,
+ int x, int y, int w, int h, gx_color_index czero, gx_color_index cone,
+ int px, int py)
+#define zero (int)czero
+#define one (int)cone
+{
+ rop_params params;
+ int xmod, width_bytes;
+ int tile_height = tile->size.y;
+ int xbit;
+ int lcount;
+ int mask, rmask;
+ byte narrow;
+ byte again;
+ int const_bits, maps;
+ int ymod, yleft;
+
+ fit_fill(dev, x, y, w, h);
+ /* We only handle the easiest cases directly. */
+ if ((tile->size.x & 7) || one == -1 || zero == -1 || px || py)
+ return gx_default_tile_rectangle(dev, tile, x, y, w, h,
+ czero, cone, px, py);
+ /* Following is similar to aligned case of copy_mono */
+ params.dest = mk_fb_ptr(x, y);
+ params.draster = fb_dev->raster;
+ params.sraster = tile->raster;
+ params.shift = 0;
+ xbit = x & 7;
+ /* Set up the graphics registers */
+ const_bits = (zero ^ one) ^ 0xf;
+ if (const_bits) {
+ set_g_const(zero); /* either color will do */
+ set_g_const_map(const_bits);
+ }
+ if ((maps = zero & ~one) != 0) {
+ set_s_map(maps += const_bits);
+ params.invert = -1;
+ again = one & ~zero;
+ } else {
+ maps = one & ~zero;
+ set_s_map(maps += const_bits);
+ params.invert = 0;
+ again = 0;
+ }
+ xmod = (x % tile->size.x) >> 3;
+ width_bytes = tile->size.x >> 3;
+ mask = 0xff >> xbit;
+ if (w + xbit <= 8)
+ mask -= mask >> w,
+ rmask = 0,
+ narrow = 1;
+ else {
+ rmask = (0xff00 >> ((w + x) & 7)) & 0xff;
+ if (xbit)
+ w += xbit - 8;
+ else
+ mask = 0, --xmod, --params.dest;
+ narrow = 0;
+ }
+ ymod = y % tile_height;
+ tile:yleft = tile_height - ymod;
+ params.src = tile->data + ymod * params.sraster + xmod;
+ lcount = h;
+ if (narrow) { /* Optimize narrow case */
+ set_g_mask(mask);
+ if (lcount > yleft) {
+ params.height = yleft;
+ memrwcol0((rop_ptr) & params);
+ params.dest += yleft * params.draster;
+ params.src = tile->data + xmod;
+ params.height = tile_height;
+ lcount -= yleft;
+ while (lcount >= tile_height) {
+ memrwcol0((rop_ptr) & params);
+ params.dest += tile_height * params.draster;
+ lcount -= tile_height;
+ }
+ }
+ if (lcount) {
+ params.height = lcount;
+ memrwcol0((rop_ptr) & params);
+ }
+ } else {
+ fb_ptr line = params.dest;
+ int xpos = width_bytes - xmod;
+
+ while (1) {
+ int xleft = xpos;
+ int count = w;
+
+ params.height = (lcount > yleft ? yleft : lcount);
+ /* Do first byte, if not a full byte. */
+ if (mask) {
+ set_g_mask(mask);
+ memrwcol0((rop_ptr) & params);
+ }
+ /* Do full bytes */
+ if ((count -= 8) >= 0) {
+ set_g_mask(0xff);
+ do {
+ if (!--xleft)
+ xleft = width_bytes,
+ params.src -= width_bytes;
+ ++params.src, ++params.dest;
+ memrwcol0((rop_ptr) & params);
+ }
+ while ((count -= 8) >= 0);
+ }
+ /* Do last byte */
+ if (rmask) {
+ if (!--xleft)
+ xleft = width_bytes,
+ params.src -= width_bytes;
+ set_g_mask(rmask);
+ ++params.src, ++params.dest;
+ memrwcol0((rop_ptr) & params);
+ }
+ if ((lcount -= params.height) == 0)
+ break;
+ params.dest = line += params.height * params.draster;
+ params.src = tile->data + xmod;
+ yleft = tile_height;
+ }
+ }
+ /* Now do the second color if needed */
+ if (again) {
+ maps = again + const_bits;
+ set_s_map(maps);
+ again = 0;
+ params.dest = mk_fb_ptr(x, y);
+ if (mask == 0)
+ params.dest--;
+ params.invert = 0;
+ goto tile;
+ }
+ if (maps != 0xf)
+ set_s_map(-1);
+ if (const_bits)
+ set_g_const_map(0);
+ dot_end();
+ return 0;
+}
+
+/* Read scan lines back from the frame buffer. */
+int
+ega_get_bits(gx_device * dev, int y, byte * data, byte ** actual_data)
+{ /* The maximum width for an EGA/VGA device is 800 pixels.... */
+ int width_bytes = (dev->width + 7) >> 3;
+ int i;
+ bits32 *dest;
+ const byte *src;
+ const byte *end;
+ byte planes[100 * 4];
+
+ /* Plane 0 is the least significant plane. */
+ /* We know we're on a little-endian machine.... */
+#define spread4(v)\
+ v+0x00000000, v+0x08000000, v+0x80000000, v+0x88000000,\
+ v+0x00080000, v+0x08080000, v+0x80080000, v+0x88080000,\
+ v+0x00800000, v+0x08800000, v+0x80800000, v+0x88800000,\
+ v+0x00880000, v+0x08880000, v+0x80880000, v+0x88880000
+ static const bits32 spread8[256] =
+ {spread4(0x0000), spread4(0x0800),
+ spread4(0x8000), spread4(0x8800),
+ spread4(0x0008), spread4(0x0808),
+ spread4(0x8008), spread4(0x8808),
+ spread4(0x0080), spread4(0x0880),
+ spread4(0x8080), spread4(0x8880),
+ spread4(0x0088), spread4(0x0888),
+ spread4(0x8088), spread4(0x8888)
+ };
+
+ if (y < 0 || y >= dev->height || dev->width > 800)
+ return_error(gs_error_rangecheck);
+ /* Read 4 planes into the holding buffer. */
+ for (i = 0; i < 4; ++i) {
+ set_g_read_plane(i);
+ memcpy(planes + 100 * i, mk_fb_ptr(0, y), width_bytes);
+ }
+ /* Now assemble the final data from the planes. */
+ for (dest = (bits32 *) data, src = planes, end = src + width_bytes;
+ src < end; ++dest, ++src
+ )
+ *dest = (((((spread8[src[0]] >> 1) | spread8[src[100]]) >> 1) |
+ spread8[src[200]]) >> 1) | spread8[src[300]];
+ if (actual_data != 0)
+ *actual_data = data;
+ return 0;
+}
+
+/* ------ Internal routines ------ */
+
+/* Mask table for rectangle fill. */
+static const byte rmask_tab[9] =
+{0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff
+};
+
+/* Fill a rectangle specified by pointer into frame buffer, */
+/* starting bit within byte, width, and height. */
+/* Smashes rop->dest. */
+static void
+fill_rectangle(register rop_ptr rop, int bit, int w, int color)
+ /* rop: dest, draster, height */
+{
+ set_g_const(color);
+ set_g_const_map(0xf);
+ select_g_mask();
+ if (bit + w <= 8) { /* Less than one byte */
+ out_g_mask(rmask_tab[w] >> bit);
+ memsetcol(rop);
+ } else {
+ byte right_mask;
+
+ if (bit) {
+ out_g_mask(0xff >> bit);
+ memsetcol(rop);
+ rop->dest++;
+ w += bit - 8;
+ }
+ if (w >= 8) {
+ out_g_mask(0xff); /* all bits */
+ rop->width = w >> 3;
+ memsetrect(rop);
+ rop->dest += rop->width;
+ w &= 7;
+ }
+ if ((right_mask = rmask_tab[w]) != 0) {
+ out_g_mask(right_mask);
+ memsetcol(rop);
+ }
+ }
+ set_g_const_map(0);
+}
+
+/* Fill a single row specified by pointer into frame buffer, */
+/* starting bit within byte, and width; clean up afterwards. */
+#define r_m_w(ptr) (*(ptr))++ /* read & write, data irrelevant */
+static void
+fill_row_only(byte * dest, int bit, int w, int color)
+ /* rop: dest */
+{
+ if (bit + w <= 8) { /* Less than one byte. */
+ /* Optimize filling with black or white. */
+ switch (color) {
+ case 0:
+ set_g_mask(rmask_tab[w] >> bit);
+ *dest &= color; /* read, then write 0s; */
+ /* some compilers optimize &= 0 to a store. */
+ out_g_mask(0xff); /* dot_end */
+ break;
+ case 0xf:
+ set_g_mask(rmask_tab[w] >> bit);
+ *dest |= 0xff; /* read, then write 1s; */
+ /* some compilers optimize &= 0 to a store. */
+ out_g_mask(0xff); /* dot_end */
+ break;
+ default:
+ set_g_const(color);
+ set_g_const_map(0xf);
+ set_g_mask(rmask_tab[w] >> bit);
+ r_m_w(dest);
+ out_g_mask(0xff); /* dot_end */
+ set_g_const_map(0);
+ }
+ } else {
+ byte right_mask;
+ int byte_count;
+
+ set_g_const(color);
+ set_g_const_map(0xf);
+ select_g_mask();
+ if (bit) {
+ out_g_mask(0xff >> bit);
+ r_m_w(dest);
+ dest++;
+ w += bit - 8;
+ }
+ byte_count = w >> 3;
+ if ((right_mask = rmask_tab[w & 7]) != 0) {
+ out_g_mask(right_mask);
+ r_m_w(dest + byte_count);
+ }
+ out_g_mask(0xff);
+ if (byte_count) {
+ memset(dest, 0, byte_count); /* data irrelevant */
+ }
+ set_g_const_map(0);
+ }
+}
diff --git a/devices/gdevpcfb.h b/devices/gdevpcfb.h
new file mode 100644
index 000000000..4636dba8b
--- /dev/null
+++ b/devices/gdevpcfb.h
@@ -0,0 +1,200 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* IBM PC frame buffer definitions */
+
+#ifndef gdevpcfb_INCLUDED
+# define gdevpcfb_INCLUDED
+
+#ifdef __MSDOS__
+# include "dos_.h"
+typedef union REGS registers;
+
+#endif
+
+/* For testing, the 16-color display may be defined as a monochrome, */
+/* 8-color, or 16-color device. */
+#define ega_bits_of_color 2 /* 0, 1, or 2 */
+#define rgb_max ega_bits_of_color
+
+/* Define the short (integer) version of "transparent" color. */
+/* ****** Depends on gx_no_color_index being all 1's. ***** */
+#define no_color ((int)gx_no_color_index)
+
+/* Procedures */
+
+ /* See gxdevice.h for the definitions of the procedures. */
+
+dev_proc_open_device(ega_open);
+dev_proc_close_device(ega_close);
+dev_proc_fill_rectangle(ega_fill_rectangle);
+dev_proc_tile_rectangle(ega_tile_rectangle);
+dev_proc_copy_mono(ega_copy_mono);
+dev_proc_copy_color(ega_copy_color);
+dev_proc_get_bits(ega_get_bits);
+
+/* Structure for saving state of BIOS variables. */
+typedef struct pcfb_bios_state_s {
+ int display_mode; /* must be first, see pcfb_save_state */
+ /* in gdevpcfb.c */
+ byte text_page;
+ uint text_cursor_mode;
+ uint text_font;
+ byte text_attribute;
+ byte border_color;
+} pcfb_bios_state;
+
+/* Procedures used by gdevpcfb.c */
+void pcfb_set_signals(gx_device *);
+void pcfb_get_state(pcfb_bios_state *);
+void pcfb_set_mode(int);
+void pcfb_set_state(const pcfb_bios_state *);
+
+/* Types for frame buffer pointers. */
+typedef byte *fb_ptr;
+typedef volatile byte *volatile_fb_ptr;
+
+/* Define the nominal page height in inches. */
+#ifdef A4
+# define PAGE_HEIGHT_INCHES 11.69
+#else
+# define PAGE_HEIGHT_INCHES 11.0
+#endif
+
+/* The device descriptor */
+typedef struct gx_device_ega_s gx_device_ega;
+struct gx_device_ega_s {
+ gx_device_common;
+ int raster; /* frame buffer bytes per line */
+ int fb_seg_mult; /* multiplier for segment part */
+ /* of frame buffer pointer */
+ int fb_byte_mult; /* multiplier for word part ditto */
+#define mk_fb_ptr(x, y)\
+ (fb_dev->fb_byte_mult == 0 ?\
+ (fb_ptr)MK_PTR(regen + (y) * (fb_dev->fb_seg_mult), (x) >> 3) :\
+ (fb_ptr)MK_PTR(regen + ((y) >> 4) * (fb_dev->fb_seg_mult),\
+ (((y) & 15) * fb_dev->fb_byte_mult) + ((x) >> 3)))
+ int video_mode;
+};
+
+/* Macro for creating instances */
+/* The initial parameters map an appropriate fraction of */
+/* the screen to a full-page coordinate space. */
+/* This may or may not be what is desired! */
+#define ega_device(dev_name, procs, fb_raster, screen_height, aspect_ratio, video_mode)\
+ { std_device_dci_body(gx_device_ega, &procs, dev_name,\
+ fb_raster * 8, screen_height,\
+ (screen_height * (aspect_ratio)) / PAGE_HEIGHT_INCHES, /* x dpi */\
+ screen_height / PAGE_HEIGHT_INCHES, /* y dpi */\
+ (rgb_max ? 3 : 1), /* num_components */\
+ 4, /* depth */\
+ (rgb_max ? rgb_max : 1), /* max_gray */\
+ rgb_max,\
+ (rgb_max ? rgb_max + 1 : 2), /* dither_grays */\
+ (rgb_max ? rgb_max + 1 : 0) /* dither_colors */\
+ ),\
+ { 0 }, /* std_procs */\
+ fb_raster,\
+ (fb_raster & 15 ? fb_raster : fb_raster >> 4),\
+ (fb_raster & 15 ? fb_raster : 0),\
+ video_mode\
+ }
+
+/* Define the device port and register numbers, and the regen map base */
+#define seq_addr 0x3c4
+#define s_map 2
+#define set_s_map(mask) outport2(seq_addr, s_map, mask)
+#define graph_addr 0x3ce
+#define g_const 0 /* set/reset */
+#define set_g_const(color) outport2(graph_addr, g_const, color)
+#define g_const_map 1 /* enable set/reset */
+#define set_g_const_map(map) outport2(graph_addr, g_const_map, map)
+#define g_function 3
+# define gf_WRITE 0
+# define gf_AND 8
+# define gf_OR 0x10
+# define gf_XOR 0x18
+#define set_g_function(func) outport2(graph_addr, g_function, func)
+#define g_read_plane 4
+#define set_g_read_plane(plane) outport2(graph_addr, g_read_plane, plane)
+#define g_mode 5
+# define gm_DATA 0
+# define gm_FILL 2
+#define set_g_mode(mode) outport2(graph_addr, g_mode, mode)
+#define g_mask 8
+#define set_g_mask(mask) outport2(graph_addr, g_mask, mask)
+#define select_g_mask() outportb(graph_addr, g_mask)
+#define out_g_mask(mask) outportb(graph_addr+1, mask)
+#define regen 0xa000
+
+/* Define access to the frame buffer and the video registers */
+/* according to whether we are on a DOS system or a Unix system. */
+
+#if defined(M_UNIX) || defined(M_XENIX) || defined(UNIX) || defined(SYSV) || defined(__linux__)
+
+ /* SCO Unix/Xenix, AT&T SVR4, or Linux. */
+
+#undef outportb
+
+#if defined(__GNUC__)
+ /* Inline assembly version for gcc */
+ /* Under SCO, requires installing the gnu assembler as "as" */
+static inline void
+outportb(int port, int data)
+{
+ __asm__ volatile ("outb %0,%1"::
+ "a" ((unsigned char)data),
+ "d" ((unsigned short)port));
+}
+static inline void
+outport2(int port, int index, int data)
+{
+ __asm__ volatile ("movb %0,%%ah; movb %1,%%al; outw %%ax,%2"::
+ "qmi" ((unsigned char)data),
+ "qmi" ((unsigned char)index),
+ "d" ((unsigned short)port):
+ "eax");
+}
+#else
+void outportb(uint, byte);
+void outport2(uint, byte, byte);
+
+#endif
+
+/* Redefine mk_fb_ptr -- no segmented addressing. */
+
+#undef mk_fb_ptr
+extern fb_ptr fb_addr;
+
+#define mk_fb_ptr(x, y) (fb_addr + (y) * (fb_dev->raster) + ((x) >> 3))
+
+#else
+
+ /* MS-DOS */
+
+/* outportb is defined in dos_.h */
+#define outport2(port, index, data)\
+ (outportb(port, index), outportb((port)+1, data))
+
+#endif
+
+/* Fetch and discard a byte. Prevent the compiler from */
+/* optimizing this away. */
+static unsigned char byte_discard_;
+
+#define byte_discard(expr) byte_discard_ = (expr)
+
+#endif /* gdevpcfb_INCLUDED */
diff --git a/devices/gdevpcl.c b/devices/gdevpcl.c
new file mode 100644
index 000000000..ee51adfbc
--- /dev/null
+++ b/devices/gdevpcl.c
@@ -0,0 +1,448 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Utilities for PCL printers */
+#include "gdevprn.h"
+#include "gdevpcl.h"
+#include "math_.h"
+
+/* ------ Get paper size ------ */
+
+/* Get the paper size code, based on width and height. */
+int
+gdev_pcl_paper_size(gx_device * dev)
+{
+ float width_inches = dev->width / dev->x_pixels_per_inch;
+ float height_inches = dev->height / dev->y_pixels_per_inch;
+ /* The initial value for height_difference, and for code below, is
+ unnecessary and is here just to stop the compiler from
+ complaining about a possible uninitialized variable usage. */
+ float width_difference = -1.0, height_difference = -1.0;
+ float new_width_difference, new_height_difference;
+ int code = PAPER_SIZE_LETTER;
+
+ if (dev->width > dev->height) {
+ /* Landscape orientation, switch width and height to find paper size */
+ width_inches = dev->height / dev->y_pixels_per_inch;
+ height_inches = dev->width / dev->x_pixels_per_inch;
+ }
+
+ /* Since we're telling the printer when to eject and start a new
+ page, the paper height doesn't matter a great deal, as long as
+ we ensure that it's at least as high as we want our pages to
+ be. However, the paper width is important, because on printers
+ which center the paper in the input tray, having the wrong
+ width will cause the image to appear in the wrong place on the
+ paper (perhaps even partially missing the paper completely).
+ Therefore, we choose our paper size by finding one whose width
+ and height are both equal to or greater than the desired width
+ and height and whose width is the closest to what we want. We
+ only pay close attention to the height when the widths of two
+ different paper sizes are equal.
+
+ We use "foo - bar > -0.01" instead of "foo >= bar" to avoid
+ minor floating point and rounding errors.
+ */
+#define CHECK_PAPER_SIZE(w,h,c) \
+ new_width_difference = w - width_inches; \
+ new_height_difference = h - height_inches; \
+ if ((new_width_difference > -0.01) && (new_height_difference > -0.01) && \
+ ((width_difference == -1.0) || \
+ (new_width_difference < width_difference) || \
+ ((new_width_difference == width_difference) && \
+ (new_height_difference < height_difference)))) { \
+ width_difference = new_width_difference; \
+ height_difference = new_height_difference; \
+ code = c; \
+ }
+
+ CHECK_PAPER_SIZE( 7.25, 10.5 , PAPER_SIZE_EXECUTIVE);
+ CHECK_PAPER_SIZE( 8.5 , 11.0 , PAPER_SIZE_LETTER);
+ CHECK_PAPER_SIZE( 8.5 , 14.0 , PAPER_SIZE_LEGAL);
+ CHECK_PAPER_SIZE(11.0 , 17.0 , PAPER_SIZE_LEDGER);
+ CHECK_PAPER_SIZE( 5.83, 8.27, PAPER_SIZE_A5);
+ CHECK_PAPER_SIZE( 8.27, 11.69, PAPER_SIZE_A4);
+ CHECK_PAPER_SIZE(11.69, 16.54, PAPER_SIZE_A3);
+ CHECK_PAPER_SIZE(16.54, 23.39, PAPER_SIZE_A2);
+ CHECK_PAPER_SIZE(23.39, 33.11, PAPER_SIZE_A1);
+ CHECK_PAPER_SIZE(33.11, 46.81, PAPER_SIZE_A0);
+ CHECK_PAPER_SIZE( 7.16, 10.12, PAPER_SIZE_JIS_B5);
+ CHECK_PAPER_SIZE(10.12, 14.33, PAPER_SIZE_JIS_B4);
+ CHECK_PAPER_SIZE( 3.94, 5.83, PAPER_SIZE_JPOST);
+ CHECK_PAPER_SIZE( 5.83, 7.87, PAPER_SIZE_JPOSTD);
+ CHECK_PAPER_SIZE( 3.87, 7.5 , PAPER_SIZE_MONARCH);
+ CHECK_PAPER_SIZE( 4.12, 9.5 , PAPER_SIZE_COM10);
+ CHECK_PAPER_SIZE( 4.33, 8.66, PAPER_SIZE_DL);
+ CHECK_PAPER_SIZE( 6.38, 9.01, PAPER_SIZE_C5);
+ CHECK_PAPER_SIZE( 6.93, 9.84, PAPER_SIZE_B5);
+
+#undef CHECK_PAPER_SIZE
+
+ return code;
+}
+
+/* ------ Get page orientation ------ */
+
+/* Get the page orientation, based on width and height. */
+int
+gdev_pcl_page_orientation(gx_device * dev)
+{
+ if (dev->height >= dev->width)
+ return PAGE_ORIENTATION_PORTRAIT;
+ else
+ return PAGE_ORIENTATION_LANDSCAPE;
+}
+
+/* ------ Color mapping ------ */
+
+/* The PaintJet and DeskJet 500C use additive colors in separate planes. */
+/* We only keep one bit of color, with 1 = R, 2 = G, 4 = B. */
+/* Because the buffering routines assume 0 = white, */
+/* we complement all the color components. */
+#define cv_shift (sizeof(gx_color_value) * 8 - 1)
+
+/* Map an RGB color to a printer color. */
+gx_color_index
+gdev_pcl_3bit_map_rgb_color(gx_device * dev, const gx_color_value cv[])
+{
+ gx_color_value r, g, b;
+ r = cv[0]; g = cv[1]; b = cv[2];
+ return (((b >> cv_shift) << 2) + ((g >> cv_shift) << 1) + (r >> cv_shift)) ^ 7;
+}
+
+/* Map the printer color back to RGB. */
+int
+gdev_pcl_3bit_map_color_rgb(gx_device * dev, gx_color_index color,
+ gx_color_value prgb[3])
+{
+ ushort cc = (ushort) color ^ 7;
+
+ prgb[0] = -(cc & 1);
+ prgb[1] = -((cc >> 1) & 1);
+ prgb[2] = -(cc >> 2);
+ return 0;
+}
+
+/* ------ Compression ------ */
+
+/*
+ * Mode 2 Row compression routine for the HP DeskJet & LaserJet IIp.
+ * Compresses data from row up to end_row, storing the result
+ * starting at compressed. Returns the number of bytes stored.
+ * Runs of K<=127 literal bytes are encoded as K-1 followed by
+ * the bytes; runs of 2<=K<=127 identical bytes are encoded as
+ * 257-K followed by the byte.
+ * In the worst case, the result is N+(N/127)+1 bytes long,
+ * where N is the original byte count (end_row - row).
+ * To speed up the search, we examine an entire word at a time.
+ * We will miss a few blocks of identical bytes; tant pis.
+ */
+int
+gdev_pcl_mode2compress_padded(const word * row, const word * end_row,
+ byte * compressed, bool pad)
+{
+ register const word *exam = row; /* word being examined in the row to compress */
+ register byte *cptr = compressed; /* output pointer into compressed bytes */
+
+ while (exam < end_row) { /* Search ahead in the input looking for a run */
+ /* of at least 4 identical bytes. */
+ const byte *compr = (const byte *)exam;
+ const byte *end_dis;
+ const word *next;
+ register word test = *exam;
+
+ while (((test << 8) ^ test) > 0xff) {
+ if (++exam >= end_row)
+ break;
+ test = *exam;
+ }
+
+ /* Find out how long the run is */
+ end_dis = (const byte *)exam;
+ if (exam == end_row) { /* no run */
+ /* See if any of the last 3 "dissimilar" bytes are 0. */
+ if (!pad && end_dis > compr && end_dis[-1] == 0) {
+ if (end_dis[-2] != 0)
+ end_dis--;
+ else if (end_dis[-3] != 0)
+ end_dis -= 2;
+ else
+ end_dis -= 3;
+ }
+ next = --end_row;
+ } else {
+ next = exam + 1;
+ while (next < end_row && *next == test)
+ next++;
+ /* See if any of the last 3 "dissimilar" bytes */
+ /* are the same as the repeated byte. */
+ if (end_dis > compr && end_dis[-1] == (byte) test) {
+ if (end_dis[-2] != (byte) test)
+ end_dis--;
+ else if (end_dis[-3] != (byte) test)
+ end_dis -= 2;
+ else
+ end_dis -= 3;
+ }
+ }
+
+ /* Now [compr..end_dis) should be encoded as dissimilar, */
+ /* and [end_dis..next) should be encoded as similar. */
+ /* Note that either of these ranges may be empty. */
+
+ for (;;) { /* Encode up to 127 dissimilar bytes */
+ uint count = end_dis - compr; /* uint for faster switch */
+
+ switch (count) { /* Use memcpy only if it's worthwhile. */
+ case 6:
+ cptr[6] = compr[5];
+ case 5:
+ cptr[5] = compr[4];
+ case 4:
+ cptr[4] = compr[3];
+ case 3:
+ cptr[3] = compr[2];
+ case 2:
+ cptr[2] = compr[1];
+ case 1:
+ cptr[1] = compr[0];
+ *cptr = count - 1;
+ cptr += count + 1;
+ case 0: /* all done */
+ break;
+ default:
+ if (count > 127)
+ count = 127;
+ *cptr++ = count - 1;
+ memcpy(cptr, compr, count);
+ cptr += count, compr += count;
+ continue;
+ }
+ break;
+ }
+
+ { /* Encode up to 127 similar bytes. */
+ /* Note that count may be <0 at end of row. */
+ int count = (const byte *)next - end_dis;
+
+ while (count > 0) {
+ int this = (count > 127 ? 127 : count);
+
+ *cptr++ = 257 - this;
+ *cptr++ = (byte) test;
+ count -= this;
+ }
+ exam = next;
+ }
+ }
+ return (cptr - compressed);
+}
+int
+gdev_pcl_mode2compress(const word * row, const word * end_row,
+ byte * compressed)
+{
+ return gdev_pcl_mode2compress_padded(row, end_row, compressed, false);
+}
+
+/*
+ * Mode 3 compression routine for the HP LaserJet III family.
+ * Compresses bytecount bytes starting at current, storing the result
+ * in compressed, comparing against and updating previous.
+ * Returns the number of bytes stored. In the worst case,
+ * the number of bytes is bytecount+(bytecount/8)+1.
+ */
+int
+gdev_pcl_mode3compress(int bytecount, const byte * current, byte * previous, byte * compressed)
+{
+ register const byte *cur = current;
+ register byte *prev = previous;
+ register byte *out = compressed;
+ const byte *end = current + bytecount;
+
+ while (cur < end) { /* Detect a maximum run of unchanged bytes. */
+ const byte *run = cur;
+ register const byte *diff;
+ const byte *stop;
+ int offset, cbyte;
+
+ while (cur < end && *cur == *prev) {
+ cur++, prev++;
+ }
+ if (cur == end)
+ break; /* rest of row is unchanged */
+ /* Detect a run of up to 8 changed bytes. */
+ /* We know that *cur != *prev. */
+ diff = cur;
+ stop = (end - cur > 8 ? cur + 8 : end);
+ do {
+ *prev++ = *cur++;
+ }
+ while (cur < stop && *cur != *prev);
+ /* Now [run..diff) are unchanged, and */
+ /* [diff..cur) are changed. */
+ /* Generate the command byte(s). */
+ offset = diff - run;
+ cbyte = (cur - diff - 1) << 5;
+ if (offset < 31)
+ *out++ = cbyte + offset;
+ else {
+ *out++ = cbyte + 31;
+ offset -= 31;
+ while (offset >= 255)
+ *out++ = 255, offset -= 255;
+ *out++ = offset;
+ }
+ /* Copy the changed data. */
+ while (diff < cur)
+ *out++ = *diff++;
+ }
+ return out - compressed;
+}
+
+/*
+ * Mode 9 2D compression for the HP DeskJets . This mode can give
+ * very good compression ratios, especially if there are areas of flat
+ * colour (or blank areas), and so is 'highly recommended' for colour
+ * printing in particular because of the very large amounts of data which
+ * can be generated
+ */
+int
+gdev_pcl_mode9compress(int bytecount, const byte *current,
+ const byte *previous, byte *compressed)
+{
+ register const byte *cur = current;
+ register const byte *prev = previous;
+ register byte *out = compressed;
+ const byte *end = current + bytecount;
+
+ while (cur < end) { /* Detect a run of unchanged bytes. */
+ const byte *run = cur;
+ register const byte *diff;
+ int offset;
+
+ while (cur < end && *cur == *prev) {
+ cur++, prev++;
+ }
+ if (cur == end)
+ break; /* rest of row is unchanged */
+ /* Detect a run of changed bytes. */
+ /* We know that *cur != *prev. */
+ diff = cur;
+ do {
+ prev++;
+ cur++;
+ }
+ while (cur < end && *cur != *prev);
+ /* Now [run..diff) are unchanged, and */
+ /* [diff..cur) are changed. */
+ offset = diff - run;
+ {
+ const byte *stop_test = cur - 4;
+ int dissimilar, similar;
+
+ while (diff < cur) {
+ const byte *compr = diff;
+ const byte *next; /* end of run */
+ byte value = 0;
+
+ while (diff <= stop_test &&
+ ((value = *diff) != diff[1] ||
+ value != diff[2] ||
+ value != diff[3]))
+ diff++;
+
+ /* Find out how long the run is */
+ if (diff > stop_test) /* no run */
+ next = diff = cur;
+ else {
+ next = diff + 4;
+ while (next < cur && *next == value)
+ next++;
+ }
+
+#define MAXOFFSETU 15
+#define MAXCOUNTU 7
+ /* output 'dissimilar' bytes, uncompressed */
+ if ((dissimilar = diff - compr)) {
+ int temp, i;
+
+ if ((temp = --dissimilar) > MAXCOUNTU)
+ temp = MAXCOUNTU;
+ if (offset < MAXOFFSETU)
+ *out++ = (offset << 3) | (byte) temp;
+ else {
+ *out++ = (MAXOFFSETU << 3) | (byte) temp;
+ offset -= MAXOFFSETU;
+ while (offset >= 255) {
+ *out++ = 255;
+ offset -= 255;
+ }
+ *out++ = offset;
+ }
+ if (temp == MAXCOUNTU) {
+ temp = dissimilar - MAXCOUNTU;
+ while (temp >= 255) {
+ *out++ = 255;
+ temp -= 255;
+ }
+ *out++ = (byte) temp;
+ }
+ for (i = 0; i <= dissimilar; i++)
+ *out++ = *compr++;
+ offset = 0;
+ } /* end uncompressed */
+#undef MAXOFFSETU
+#undef MAXCOUNTU
+
+#define MAXOFFSETC 3
+#define MAXCOUNTC 31
+ /* output 'similar' bytes, run-length encoded */
+ if ((similar = next - diff)) {
+ int temp;
+
+ if ((temp = (similar -= 2)) > MAXCOUNTC)
+ temp = MAXCOUNTC;
+ if (offset < MAXOFFSETC)
+ *out++ = 0x80 | (offset << 5) | (byte) temp;
+ else {
+ *out++ = 0x80 | (MAXOFFSETC << 5) | (byte) temp;
+ offset -= MAXOFFSETC;
+ while (offset >= 255) {
+ *out++ = 255;
+ offset -= 255;
+ }
+ *out++ = offset;
+ }
+ if (temp == MAXCOUNTC) {
+ temp = similar - MAXCOUNTC;
+ while (temp >= 255) {
+ *out++ = 255;
+ temp -= 255;
+ }
+ *out++ = (byte) temp;
+ }
+ *out++ = value;
+ offset = 0;
+ } /* end compressed */
+#undef MAXOFFSETC
+#undef MAXCOUNTC
+
+ diff = next;
+ }
+ }
+ }
+ return out - compressed;
+}
diff --git a/devices/gdevpcl.h b/devices/gdevpcl.h
new file mode 100644
index 000000000..58e109694
--- /dev/null
+++ b/devices/gdevpcl.h
@@ -0,0 +1,71 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Support for PCL-based printer drivers */
+/* Requires gdevprn.h */
+
+#ifndef gdevpcl_INCLUDED
+# define gdevpcl_INCLUDED
+
+/*
+ * Define the PCL paper size codes. H-P's documentation and coding for the
+ * 11x17 size are inconsistent: some printers seem to accept code 11 as well
+ * as code 6, and while the definitions below match the PCL5 reference
+ * manual, some documentation calls 11x17 "tabloid" and reserves the name
+ * "ledger" for 17x11.
+ */
+#define PAPER_SIZE_EXECUTIVE 1
+#define PAPER_SIZE_LETTER 2 /* 8.5" x 11" */
+#define PAPER_SIZE_LEGAL 3 /* 8.5" x 14" */
+#define PAPER_SIZE_LEDGER 6 /* 11" x 17" */
+#define PAPER_SIZE_A5 25 /* 14.8 cm x 21.0 cm */
+#define PAPER_SIZE_A4 26 /* 21.0 cm x 29.7 cm */
+#define PAPER_SIZE_A3 27 /* 29.7 cm x 42.0 cm */
+#define PAPER_SIZE_A2 28
+#define PAPER_SIZE_A1 29
+#define PAPER_SIZE_A0 30
+#define PAPER_SIZE_JIS_B5 45
+#define PAPER_SIZE_JIS_B4 46
+#define PAPER_SIZE_JPOST 71
+#define PAPER_SIZE_JPOSTD 72
+#define PAPER_SIZE_MONARCH 80
+#define PAPER_SIZE_COM10 81
+#define PAPER_SIZE_DL 90
+#define PAPER_SIZE_C5 91
+#define PAPER_SIZE_B5 100
+
+/* Get the paper size code, based on width and height. */
+int gdev_pcl_paper_size(gx_device *);
+
+#define PAGE_ORIENTATION_PORTRAIT 0
+#define PAGE_ORIENTATION_LANDSCAPE 1
+
+/* Get the page orientation, based on width and height. */
+int gdev_pcl_page_orientation(gx_device * dev);
+
+/* Color mapping procedures for 3-bit-per-pixel RGB printers */
+dev_proc_map_rgb_color(gdev_pcl_3bit_map_rgb_color);
+dev_proc_map_color_rgb(gdev_pcl_3bit_map_color_rgb);
+
+/* Row compression routines */
+typedef ulong word;
+int
+ gdev_pcl_mode2compress(const word * row, const word * end_row, byte * compressed),
+ gdev_pcl_mode2compress_padded(const word * row, const word * end_row, byte * compressed, bool pad),
+ gdev_pcl_mode3compress(int bytecount, const byte * current, byte * previous, byte * compressed),
+ gdev_pcl_mode9compress(int bytecount, const byte * current, const byte * previous, byte * compressed);
+
+#endif /* gdevpcl_INCLUDED */
diff --git a/devices/gdevpcx.c b/devices/gdevpcx.c
new file mode 100644
index 000000000..416859849
--- /dev/null
+++ b/devices/gdevpcx.c
@@ -0,0 +1,465 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* PCX file format drivers */
+#include "gdevprn.h"
+#include "gdevpccm.h"
+#include "gxlum.h"
+
+/* Thanks to Phil Conrad for donating the original version */
+/* of these drivers to Aladdin Enterprises. */
+
+/* ------ The device descriptors ------ */
+
+/*
+ * Default X and Y resolution.
+ */
+#define X_DPI 72
+#define Y_DPI 72
+
+/* Monochrome. */
+
+static dev_proc_print_page(pcxmono_print_page);
+
+/* Use the default RGB->color map, so we get black=0, white=1. */
+/* Since the print_page doesn't alter the device, this device can print in the background */
+static const gx_device_procs pcxmono_procs =
+prn_color_procs(gdev_prn_open, gdev_prn_bg_output_page, gdev_prn_close,
+ gx_default_map_rgb_color, gx_default_map_color_rgb);
+const gx_device_printer gs_pcxmono_device =
+prn_device(pcxmono_procs, "pcxmono",
+ DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
+ X_DPI, Y_DPI,
+ 0, 0, 0, 0, /* margins */
+ 1, pcxmono_print_page);
+
+/* Chunky 8-bit gray scale. */
+
+static dev_proc_print_page(pcx256_print_page);
+
+/* Since the print_page doesn't alter the device, this device can print in the background */
+static const gx_device_procs pcxgray_procs =
+prn_color_procs(gdev_prn_open, gdev_prn_bg_output_page, gdev_prn_close,
+ gx_default_gray_map_rgb_color, gx_default_gray_map_color_rgb);
+const gx_device_printer gs_pcxgray_device =
+{prn_device_body(gx_device_printer, pcxgray_procs, "pcxgray",
+ DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
+ X_DPI, Y_DPI,
+ 0, 0, 0, 0, /* margins */
+ 1, 8, 255, 255, 256, 256, pcx256_print_page)
+};
+
+/* 4-bit planar (EGA/VGA-style) color. */
+
+static dev_proc_print_page(pcx16_print_page);
+
+/* Since the print_page doesn't alter the device, this device can print in the background */
+static const gx_device_procs pcx16_procs =
+prn_color_procs(gdev_prn_open, gdev_prn_bg_output_page, gdev_prn_close,
+ pc_4bit_map_rgb_color, pc_4bit_map_color_rgb);
+const gx_device_printer gs_pcx16_device =
+{prn_device_body(gx_device_printer, pcx16_procs, "pcx16",
+ DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
+ X_DPI, Y_DPI,
+ 0, 0, 0, 0, /* margins */
+ 3, 4, 1, 1, 2, 2, pcx16_print_page)
+};
+
+/* Chunky 8-bit (SuperVGA-style) color. */
+/* (Uses a fixed palette of 3,3,2 bits.) */
+
+/* Since the print_page doesn't alter the device, this device can print in the background */
+static const gx_device_procs pcx256_procs =
+prn_color_procs(gdev_prn_open, gdev_prn_bg_output_page, gdev_prn_close,
+ pc_8bit_map_rgb_color, pc_8bit_map_color_rgb);
+const gx_device_printer gs_pcx256_device =
+{prn_device_body(gx_device_printer, pcx256_procs, "pcx256",
+ DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
+ X_DPI, Y_DPI,
+ 0, 0, 0, 0, /* margins */
+ 3, 8, 5, 5, 6, 6, pcx256_print_page)
+};
+
+/* 24-bit color, 3 8-bit planes. */
+
+static dev_proc_print_page(pcx24b_print_page);
+
+/* Since the print_page doesn't alter the device, this device can print in the background */
+static const gx_device_procs pcx24b_procs =
+prn_color_procs(gdev_prn_open, gdev_prn_bg_output_page, gdev_prn_close,
+ gx_default_rgb_map_rgb_color, gx_default_rgb_map_color_rgb);
+const gx_device_printer gs_pcx24b_device =
+prn_device(pcx24b_procs, "pcx24b",
+ DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
+ X_DPI, Y_DPI,
+ 0, 0, 0, 0, /* margins */
+ 24, pcx24b_print_page);
+
+/* 4-bit chunky CMYK color. */
+
+static dev_proc_print_page(pcxcmyk_print_page);
+
+static const gx_device_procs pcxcmyk_procs =
+{
+ gdev_prn_open,
+ NULL, /* get_initial_matrix */
+ NULL, /* sync_output */
+/* Since the print_page doesn't alter the device, this device can print in the background */
+ gdev_prn_bg_output_page,
+ gdev_prn_close,
+ NULL, /* map_rgb_color */
+ cmyk_1bit_map_color_rgb,
+ NULL, /* fill_rectangle */
+ NULL, /* tile_rectangle */
+ NULL, /* copy_mono */
+ NULL, /* copy_color */
+ NULL, /* draw_line */
+ NULL, /* get_bits */
+ gdev_prn_get_params,
+ gdev_prn_put_params,
+ cmyk_1bit_map_cmyk_color,
+ NULL, /* get_xfont_procs */
+ NULL, /* get_xfont_device */
+ NULL, /* map_rgb_alpha_color */
+ gx_page_device_get_page_device
+};
+const gx_device_printer gs_pcxcmyk_device =
+{prn_device_body(gx_device_printer, pcxcmyk_procs, "pcxcmyk",
+ DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
+ X_DPI, Y_DPI,
+ 0, 0, 0, 0, /* margins */
+ 4, 4, 1, 1, 2, 2, pcxcmyk_print_page)
+};
+
+/* ------ Private definitions ------ */
+
+/* All two-byte quantities are stored LSB-first! */
+#if arch_is_big_endian
+# define assign_ushort(a,v) a = ((v) >> 8) + ((v) << 8)
+#else
+# define assign_ushort(a,v) a = (v)
+#endif
+
+typedef struct pcx_header_s {
+ byte manuf; /* always 0x0a */
+ byte version;
+#define version_2_5 0
+#define version_2_8_with_palette 2
+#define version_2_8_without_palette 3
+#define version_3_0 /* with palette */ 5
+ byte encoding; /* 1=RLE */
+ byte bpp; /* bits per pixel per plane */
+ ushort x1; /* X of upper left corner */
+ ushort y1; /* Y of upper left corner */
+ ushort x2; /* x1 + width - 1 */
+ ushort y2; /* y1 + height - 1 */
+ ushort hres; /* horz. resolution (dots per inch) */
+ ushort vres; /* vert. resolution (dots per inch) */
+ byte palette[16 * 3]; /* color palette */
+ byte reserved;
+ byte nplanes; /* number of color planes */
+ ushort bpl; /* number of bytes per line (uncompressed) */
+ ushort palinfo;
+#define palinfo_color 1
+#define palinfo_gray 2
+ byte xtra[58]; /* fill out header to 128 bytes */
+} pcx_header;
+
+/* Define the prototype header. */
+static const pcx_header pcx_header_prototype =
+{
+ 10, /* manuf */
+ 0, /* version (variable) */
+ 1, /* encoding */
+ 0, /* bpp (variable) */
+ 00, 00, /* x1, y1 */
+ 00, 00, /* x2, y2 (variable) */
+ 00, 00, /* hres, vres (variable) */
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* palette (variable) */
+ 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ 0, /* reserved */
+ 0, /* nplanes (variable) */
+ 00, /* bpl (variable) */
+ 00, /* palinfo (variable) */
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* xtra */
+ 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, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
+};
+
+/*
+ * Define the DCX header. We don't actually use this yet.
+ * All quantities are stored little-endian!
+ bytes 0-3: ID = 987654321
+ bytes 4-7: file offset of page 1
+ [... up to 1023 entries ...]
+ bytes N-N+3: 0 to mark end of page list
+ * This is followed by the pages in order, each of which is a PCX file.
+ */
+#define dcx_magic 987654321
+#define dcx_max_pages 1023
+
+/* Forward declarations */
+static void pcx_write_rle(const byte *, const byte *, int, FILE *);
+static int pcx_write_page(gx_device_printer *, FILE *, pcx_header *, bool);
+
+/* Write a monochrome PCX page. */
+static int
+pcxmono_print_page(gx_device_printer * pdev, FILE * file)
+{
+ pcx_header header;
+
+ header = pcx_header_prototype;
+ header.version = version_2_8_with_palette;
+ header.bpp = 1;
+ header.nplanes = 1;
+ assign_ushort(header.palinfo, palinfo_gray);
+ /* Set the first two entries of the short palette. */
+ memcpy((byte *) header.palette, "\000\000\000\377\377\377", 6);
+ return pcx_write_page(pdev, file, &header, false);
+}
+
+/* Write an "old" PCX page. */
+static const byte pcx_ega_palette[16 * 3] =
+{
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xaa, 0x00, 0xaa, 0x00, 0x00, 0xaa, 0xaa,
+ 0xaa, 0x00, 0x00, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa,
+ 0x55, 0x55, 0x55, 0x55, 0x55, 0xff, 0x55, 0xff, 0x55, 0x55, 0xff, 0xff,
+ 0xff, 0x55, 0x55, 0xff, 0x55, 0xff, 0xff, 0xff, 0x55, 0xff, 0xff, 0xff
+};
+static int
+pcx16_print_page(gx_device_printer * pdev, FILE * file)
+{
+ pcx_header header;
+
+ header = pcx_header_prototype;
+ header.version = version_2_8_with_palette;
+ header.bpp = 1;
+ header.nplanes = 4;
+ /* Fill the EGA palette appropriately. */
+ memcpy((byte *) header.palette, pcx_ega_palette,
+ sizeof(pcx_ega_palette));
+ return pcx_write_page(pdev, file, &header, true);
+}
+
+/* Write a "new" PCX page. */
+static int
+pcx256_print_page(gx_device_printer * pdev, FILE * file)
+{
+ pcx_header header;
+ int code;
+
+ header = pcx_header_prototype;
+ header.version = version_3_0;
+ header.bpp = 8;
+ header.nplanes = 1;
+ assign_ushort(header.palinfo,
+ (pdev->color_info.num_components > 1 ?
+ palinfo_color : palinfo_gray));
+ code = pcx_write_page(pdev, file, &header, false);
+ if (code >= 0) { /* Write out the palette. */
+ fputc(0x0c, file);
+ code = pc_write_palette((gx_device *) pdev, 256, file);
+ }
+ return code;
+}
+
+/* Write a 24-bit color PCX page. */
+static int
+pcx24b_print_page(gx_device_printer * pdev, FILE * file)
+{
+ pcx_header header;
+
+ header = pcx_header_prototype;
+ header.version = version_3_0;
+ header.bpp = 8;
+ header.nplanes = 3;
+ assign_ushort(header.palinfo, palinfo_color);
+ return pcx_write_page(pdev, file, &header, true);
+}
+
+/* Write a 4-bit chunky CMYK color PCX page. */
+static const byte pcx_cmyk_palette[16 * 3] =
+{
+ 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x0f, 0x0f, 0x00,
+ 0xff, 0x00, 0xff, 0x0f, 0x00, 0x0f, 0xff, 0x00, 0x00, 0x0f, 0x00, 0x00,
+ 0x00, 0xff, 0xff, 0x00, 0x0f, 0x0f, 0x00, 0xff, 0x00, 0x00, 0x0f, 0x00,
+ 0x00, 0x00, 0xff, 0x00, 0x00, 0x0f, 0x1f, 0x1f, 0x1f, 0x0f, 0x0f, 0x0f,
+};
+static int
+pcxcmyk_print_page(gx_device_printer * pdev, FILE * file)
+{
+ pcx_header header;
+
+ header = pcx_header_prototype;
+ header.version = 2;
+ header.bpp = 4;
+ header.nplanes = 1;
+ /* Fill the palette appropriately. */
+ memcpy((byte *) header.palette, pcx_cmyk_palette,
+ sizeof(pcx_cmyk_palette));
+ return pcx_write_page(pdev, file, &header, false);
+}
+
+/* Write out a page in PCX format. */
+/* This routine is used for all formats. */
+/* The caller has set header->bpp, nplanes, and palette. */
+static int
+pcx_write_page(gx_device_printer * pdev, FILE * file, pcx_header * phdr,
+ bool planar)
+{
+ int raster = gdev_prn_raster(pdev);
+ uint rsize = ROUND_UP((pdev->width * phdr->bpp + 7) >> 3, 2); /* PCX format requires even */
+ int height = pdev->height;
+ int depth = pdev->color_info.depth;
+ uint lsize = raster + rsize;
+ byte *line = gs_alloc_bytes(pdev->memory, lsize, "pcx file buffer");
+ byte *plane = line + raster;
+ int y;
+ int code = 0; /* return code */
+
+ if (line == 0) /* can't allocate line buffer */
+ return_error(gs_error_VMerror);
+
+ /* Fill in the other variable entries in the header struct. */
+
+ assign_ushort(phdr->x2, pdev->width - 1);
+ assign_ushort(phdr->y2, height - 1);
+ assign_ushort(phdr->hres, (int)pdev->x_pixels_per_inch);
+ assign_ushort(phdr->vres, (int)pdev->y_pixels_per_inch);
+ assign_ushort(phdr->bpl, (planar || depth == 1 ? rsize :
+ raster + (raster & 1)));
+
+ /* Write the header. */
+
+ if (fwrite((const char *)phdr, 1, 128, file) < 128) {
+ code = gs_error_ioerror;
+ goto pcx_done;
+ }
+ /* Write the contents of the image. */
+ for (y = 0; y < height; y++) {
+ byte *row;
+ byte *end;
+
+ code = gdev_prn_get_bits(pdev, y, line, &row);
+ if (code < 0)
+ break;
+ end = row + raster;
+ if (!planar) { /* Just write the bits. */
+ if (raster & 1) { /* Round to even, with predictable padding. */
+ *end = end[-1];
+ ++end;
+ }
+ pcx_write_rle(row, end, 1, file);
+ } else
+ switch (depth) {
+
+ case 4:
+ {
+ byte *pend = plane + rsize;
+ int shift;
+
+ for (shift = 0; shift < 4; shift++) {
+ register byte *from, *to;
+ register int bright = 1 << shift;
+ register int bleft = bright << 4;
+
+ for (from = row, to = plane;
+ from < end; from += 4
+ ) {
+ *to++ =
+ (from[0] & bleft ? 0x80 : 0) |
+ (from[0] & bright ? 0x40 : 0) |
+ (from[1] & bleft ? 0x20 : 0) |
+ (from[1] & bright ? 0x10 : 0) |
+ (from[2] & bleft ? 0x08 : 0) |
+ (from[2] & bright ? 0x04 : 0) |
+ (from[3] & bleft ? 0x02 : 0) |
+ (from[3] & bright ? 0x01 : 0);
+ }
+ /* We might be one byte short of rsize. */
+ if (to < pend)
+ *to = to[-1];
+ pcx_write_rle(plane, pend, 1, file);
+ }
+ }
+ break;
+
+ case 24:
+ {
+ int pnum;
+
+ for (pnum = 0; pnum < 3; ++pnum) {
+ pcx_write_rle(row + pnum, row + raster, 3, file);
+ if (pdev->width & 1)
+ fputc(0, file); /* pad to even */
+ }
+ }
+ break;
+
+ default:
+ code = gs_note_error(gs_error_rangecheck);
+ goto pcx_done;
+
+ }
+ }
+
+ pcx_done:
+ gs_free_object(pdev->memory, line, "pcx file buffer");
+
+ return code;
+}
+
+/* ------ Internal routines ------ */
+
+/* Write one line in PCX run-length-encoded format. */
+static void
+pcx_write_rle(const byte * from, const byte * end, int step, FILE * file)
+{ /*
+ * The PCX format theoretically allows encoding runs of 63
+ * identical bytes, but some readers can't handle repetition
+ * counts greater than 15.
+ */
+#define MAX_RUN_COUNT 15
+ int max_run = step * MAX_RUN_COUNT;
+
+ while (from < end) {
+ byte data = *from;
+
+ from += step;
+ if (data != *from || from == end) {
+ if (data >= 0xc0)
+ putc(0xc1, file);
+ } else {
+ const byte *start = from;
+
+ while ((from < end) && (*from == data))
+ from += step;
+ /* Now (from - start) / step + 1 is the run length. */
+ while (from - start >= max_run) {
+ putc(0xc0 + MAX_RUN_COUNT, file);
+ putc(data, file);
+ start += max_run;
+ }
+ if (from > start || data >= 0xc0)
+ putc((from - start) / step + 0xc1, file);
+ }
+ putc(data, file);
+ }
+#undef MAX_RUN_COUNT
+}
diff --git a/devices/gdevpe.c b/devices/gdevpe.c
new file mode 100644
index 000000000..c7b4f1e1b
--- /dev/null
+++ b/devices/gdevpe.c
@@ -0,0 +1,365 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/*
+ * Private Eye display driver
+ *
+ * Hacked by Fran Taylor, Reflection Technology Inc.
+ */
+
+#include "memory_.h"
+#include "gx.h"
+#include "gxdevice.h"
+
+char *getenv(char *name);
+
+typedef struct gx_device_pe_s {
+ gx_device_common;
+ byte *fbaddr;
+ unsigned regs;
+} gx_device_pe;
+#define pedev ((gx_device_pe *)dev)
+
+typedef struct {
+ ushort reg, val;
+} regval;
+
+#define XSIZE 720
+#define YSIZE 280
+#define BPL 90
+#define XPPI 160.0
+#define YPPI 96.0
+#define DEFAULT_ADDRESS ((byte *) 0xb8000000)
+#define DEFAULT_REGISTERS 0x3d0
+
+dev_proc_open_device(pe_open);
+dev_proc_close_device(pe_close);
+dev_proc_fill_rectangle(pe_fill_rectangle);
+dev_proc_copy_mono(pe_copy_mono);
+
+static gx_device_procs pe_procs =
+{ pe_open,
+ NULL, /* get_initial_matrix */
+ NULL, /* sync_output */
+ NULL, /* output_page */
+ pe_close,
+ NULL, /* map_rgb_color */
+ NULL, /* map_color_rgb */
+ pe_fill_rectangle,
+ NULL, /* tile_rectangle */
+ pe_copy_mono,
+ NULL /* copy_color */
+};
+
+gx_device_pe far_data gs_pe_device =
+{ std_device_std_body(gx_device_pe, &pe_procs, "pe",
+ XSIZE, YSIZE, XPPI, YPPI),
+ { 0 }, /* std_procs */
+ DEFAULT_ADDRESS, DEFAULT_REGISTERS
+};
+
+static regval peinit[] = {{0x04, 0x1e}, {0x05, 0x00},
+ {0x04, 0x0c}, {0x05, 0x21},
+ {0x04, 0x0d}, {0x05, 0x98},
+ {0x08, 0x00}, {0x08, 0x1e},
+ {0x04, 0x1e}, {0x05, 0x01}};
+
+static regval pedone[] = {{0x04, 0x1e}, {0x05, 0x10},
+ {0x04, 0x0a}, {0x05, 0x00},
+ {0x04, 0x0b}, {0x05, 0x07},
+ {0x04, 0x0c}, {0x05, 0x00},
+ {0x04, 0x0d}, {0x05, 0x00},
+ {0x04, 0x0e}, {0x05, 0x00},
+ {0x04, 0x0f}, {0x05, 0x00},
+ {0x08, 0x00}, {0x08, 0x29}};
+
+int pe_open(gx_device *dev)
+{
+ char *str;
+ int i;
+
+ if ((str = getenv("PEFBADDR")) != 0)
+ {
+ if (!sscanf(str, "%lx", &(pedev->fbaddr)))
+ {
+ emprintf(dev->memory,
+ "Private Eye: PEFBADDR environment string format error\n");
+ exit(1);
+ }
+ }
+
+ if ((str = getenv("PEREGS")) != 0)
+ {
+ if (!sscanf(str, "%x", &(pedev->regs)))
+ {
+ emprintf(dev->memory,
+ "Private Eye: PEREGS environment string format error\n");
+ exit(1);
+ }
+ }
+
+ for (i = 0; i < 10; i++)
+ outportb(pedev->regs + peinit[i].reg, peinit[i].val);
+
+ return 0;
+}
+
+int pe_close(gx_device *dev)
+{
+ int i;
+
+ /* restore the screen */
+ for (i = 0; i < 16; i++)
+ outportb(pedev->regs + pedone[i].reg, pedone[i].val);
+
+ /* clear the frame buffer */
+ memset(pedev->fbaddr, 0, 4000);
+
+ return 0;
+}
+
+int pe_fill_rectangle(gx_device *dev, int x1, int y1, int w, int h,
+ gx_color_index color)
+{
+ int x2, y2, xlen;
+ byte led, red, d;
+ byte *ptr;
+
+ /* cull */
+
+ if ((w <= 0) || (h <= 0) || (x1 > XSIZE) || (y1 > YSIZE))
+ return 0;
+
+ x2 = x1 + w - 1;
+ y2 = y1 + h - 1;
+
+ /* cull some more */
+
+ if ((x2 < 0) || (y2 < 0))
+ return 0;
+
+ /* clip */
+
+ if (x1 < 0) x1 = 0;
+ if (x2 > XSIZE-1) x2 = XSIZE-1;
+ if (y1 < 0) y1 = 0;
+ if (y2 > YSIZE-1) y2 = YSIZE-1;
+
+ w = x2 - x1 + 1;
+ h = y2 - y1 + 1;
+ xlen = (x2 >> 3) - (x1 >> 3) - 1;
+ led = 0xff >> (x1 & 7);
+ red = 0xff << (7 - (x2 & 7));
+
+ ptr = pedev->fbaddr + (y1 * BPL) + (x1 >> 3);
+
+ if (color)
+ {
+ /* here to set pixels */
+
+ if (xlen == -1)
+ {
+ /* special for rectangles that fit in a byte */
+
+ d = led & red;
+ for(; h >= 0; h--, ptr += BPL)
+ *ptr |= d;
+ return 0;
+ }
+
+ /* normal fill */
+
+ for(; h >= 0; h--, ptr += BPL)
+ { register int x = xlen;
+ register byte *p = ptr;
+ *p++ |= led;
+ while ( x-- ) *p++ = 0xff;
+ *p |= red;
+ }
+ }
+
+ /* here to clear pixels */
+
+ led = ~led;
+ red = ~red;
+
+ if (xlen == -1)
+ {
+ /* special for rectangles that fit in a byte */
+
+ d = led | red;
+ for(; h >= 0; h--, ptr += BPL)
+ *ptr &= d;
+ return 0;
+ }
+
+ /* normal fill */
+
+ for(; h >= 0; h--, ptr += BPL)
+ { register int x = xlen;
+ register byte *p = ptr;
+ *p++ &= led;
+ while ( x-- ) *p++ = 0x00;
+ *p &= red;
+ }
+ return 0;
+}
+
+int pe_copy_mono(gx_device *dev,
+ const byte *base, int sourcex, int raster, gx_bitmap_id id,
+ int x, int y, int w, int h,
+ gx_color_index zero, gx_color_index one)
+{
+ const byte *line;
+ int sleft, dleft;
+ int mask, rmask;
+ int invert, zmask, omask;
+ byte *dest;
+ int offset;
+
+#define izero (int)zero
+#define ione (int)one
+
+if ( ione == izero ) /* vacuous case */
+ return pe_fill_rectangle(dev, x, y, w, h, zero);
+
+ /* clip */
+
+ if ((x > XSIZE) || (y > YSIZE) || ((x + w) < 0) || ((y + h) < 0))
+ return 0;
+
+ offset = x >> 3;
+ dest = pedev->fbaddr + (y * BPL) + offset;
+ line = base + (sourcex >> 3);
+ sleft = 8 - (sourcex & 7);
+ dleft = 8 - (x & 7);
+ mask = 0xff >> (8 - dleft);
+ if ( w < dleft )
+ mask -= mask >> w;
+ else
+ rmask = 0xff00 >> ((w - dleft) & 7);
+
+ /* Macros for writing partial bytes. */
+ /* bits has already been inverted by xor'ing with invert. */
+
+#define write_byte_masked(ptr, bits, mask)\
+ *ptr = ((bits | ~mask | zmask) & *ptr | (bits & mask & omask))
+
+#define write_byte(ptr, bits)\
+ *ptr = ((bits | zmask) & *ptr | (bits & omask))
+
+/* if ( dev->invert )
+ {
+ if ( izero != (int)gx_no_color_index ) zero ^= 1;
+ if ( ione != (int)gx_no_color_index ) one ^= 1;
+ } */
+ invert = (izero == 1 || ione == 0 ? -1 : 0);
+ zmask = (izero == 0 || ione == 0 ? 0 : -1);
+ omask = (izero == 1 || ione == 1 ? -1 : 0);
+
+#undef izero
+#undef ione
+
+ if (sleft == dleft) /* optimize the aligned case */
+ {
+ w -= dleft;
+ while ( --h >= 0 )
+ {
+ register const byte *bptr = line;
+ int count = w;
+ register byte *optr = dest;
+ register int bits = *bptr ^ invert; /* first partial byte */
+
+ write_byte_masked(optr, bits, mask);
+
+ /* Do full bytes. */
+
+ while ((count -= 8) >= 0)
+ {
+ bits = *++bptr ^ invert;
+ ++optr;
+ write_byte(optr, bits);
+ }
+
+ /* Do last byte */
+
+ if (count > -8)
+ {
+ bits = *++bptr ^ invert;
+ ++optr;
+ write_byte_masked(optr, bits, rmask);
+ }
+ dest += BPL;
+ line += raster;
+ }
+ }
+ else
+ {
+ int skew = (sleft - dleft) & 7;
+ int cskew = 8 - skew;
+
+ while (--h >= 0)
+ {
+ const byte *bptr = line;
+ int count = w;
+ byte *optr = dest;
+ register int bits;
+
+ /* Do the first partial byte */
+
+ if (sleft >= dleft)
+ {
+ bits = *bptr >> skew;
+ }
+ else /* ( sleft < dleft ) */
+ {
+ bits = *bptr++ << cskew;
+ if (count > sleft)
+ bits += *bptr >> skew;
+ }
+ bits ^= invert;
+ write_byte_masked(optr, bits, mask);
+ count -= dleft;
+ optr++;
+
+ /* Do full bytes. */
+
+ while ( count >= 8 )
+ {
+ bits = *bptr++ << cskew;
+ bits += *bptr >> skew;
+ bits ^= invert;
+ write_byte(optr, bits);
+ count -= 8;
+ optr++;
+ }
+
+ /* Do last byte */
+
+ if (count > 0)
+ {
+ bits = *bptr++ << cskew;
+ if (count > skew)
+ bits += *bptr >> skew;
+ bits ^= invert;
+ write_byte_masked(optr, bits, rmask);
+ }
+ dest += BPL;
+ line += raster;
+ }
+ }
+ return 0;
+}
diff --git a/devices/gdevperm.c b/devices/gdevperm.c
new file mode 100644
index 000000000..1fe2107e3
--- /dev/null
+++ b/devices/gdevperm.c
@@ -0,0 +1,477 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Device which permutes color components, for testing DeviceN. */
+#include "gdevprn.h"
+#include "gxdcconv.h"
+
+/**
+ * With no additional parameters, the device named "permute" looks to
+ * Ghostscript like a standard CMYK contone device, and outputs a PPM
+ * file, using a simple CMYK->RGB transform. This should be the
+ * baseline for regression testing.
+ *
+ * With the addition of -dPermute=1, the internal behavior changes
+ * somewhat, but in most cases the resulting rendered file should be
+ * the same. In this mode, the color model becomes "DeviceN" rather
+ * than "DeviceCMYK", the number of components goes to six, and the
+ * color model is considered to be the (yellow, cyan, cyan, magenta,
+ * 0, black) tuple. This is what's rendered into the memory
+ * buffer. Finally, on conversion to RGB for output, the colors are
+ * permuted back.
+ *
+ * As such, this code should check that all imaging code paths are
+ * 64-bit clean. Additionally, it should find incorrect code that
+ * assumes that the color model is one of DeviceGray, DeviceRGB, or
+ * DeviceCMYK.
+ **/
+
+static dev_proc_print_page(perm_print_page);
+static dev_proc_get_params(perm_get_params);
+static dev_proc_put_params(perm_put_params);
+static dev_proc_get_color_mapping_procs(perm_get_color_mapping_procs);
+static dev_proc_get_color_comp_index(perm_get_color_comp_index);
+static dev_proc_encode_color(perm_encode_color);
+static dev_proc_decode_color(perm_decode_color);
+
+struct gx_device_perm_s {
+ gx_device_common;
+ gx_prn_device_common;
+ const char **std_colorant_names;
+ int num_std_colorant_names; /* Number of names in list */
+ int mode;
+ int permute;
+};
+typedef struct gx_device_perm_s gx_device_perm_t;
+
+static const gx_device_procs perm_procs = {
+ gdev_prn_open,
+ NULL,
+/* Since the print_page doesn't alter the device, this device can print in the background */
+ NULL,
+ gdev_prn_bg_output_page,
+ gdev_prn_close,
+ NULL,
+ NULL,
+ NULL, /* fill_rectangle */
+ NULL, /* tile_rectangle */
+ NULL, /* copy_mono */
+ NULL, /* copy_color */
+ NULL, /* draw_line */
+ NULL, /* get_bits */
+ perm_get_params, /* get_params */
+ perm_put_params, /* put_params */
+ NULL, /* map_cmyk_color - not used */
+ NULL, /* get_xfont_procs */
+ NULL, /* get_xfont_device */
+ NULL, /* map_rgb_alpha_color */
+ gx_page_device_get_page_device, /* get_page_device */
+ NULL, /* get_alpha_bits */
+ NULL, /* copy_alpha */
+ NULL, /* get_band */
+ NULL, /* copy_rop */
+ NULL, /* fill_path */
+ NULL, /* stroke_path */
+ NULL, /* fill_mask */
+ NULL, /* fill_trapezoid */
+ NULL, /* fill_parallelogram */
+ NULL, /* fill_triangle */
+ NULL, /* draw_thin_line */
+ NULL, /* begin_image */
+ NULL, /* image_data */
+ NULL, /* end_image */
+ NULL, /* strip_tile_rectangle */
+ NULL, /* strip_copy_rop */
+ NULL, /* get_clipping_box */
+ NULL, /* begin_typed_image */
+ NULL, /* get_bits_rectangle */
+ NULL, /* map_color_rgb_alpha */
+ NULL, /* create_compositor */
+ NULL, /* get_hardware_params */
+ NULL, /* text_begin */
+ NULL, /* finish_copydevice */
+ NULL, /* begin_transparency_group */
+ NULL, /* end_transparency_group */
+ NULL, /* begin_transparency_mask */
+ NULL, /* end_transparency_mask */
+ NULL, /* discard_transparency_layer */
+ perm_get_color_mapping_procs, /* get_color_mapping_procs */
+ perm_get_color_comp_index,
+ perm_encode_color, /* encode_color */
+ perm_decode_color /* decode_color */
+
+};
+
+const gx_device_perm_t gs_perm_device = {
+ prn_device_body_extended(gx_device_perm_t, perm_procs, "permute",
+ DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS, 72, 72,
+ 0, 0, 0, 0,
+ GX_DEVICE_COLOR_MAX_COMPONENTS, 4,
+ GX_CINFO_POLARITY_SUBTRACTIVE,
+ 32, 0, 255, 255, 256, 256,
+ GX_CINFO_SEP_LIN,
+ "DeviceN",
+ perm_print_page),
+ NULL, 0, 0, 0
+};
+
+static int
+perm_print_page(gx_device_printer *pdev, FILE *pstream)
+{
+ int y;
+ gx_device_perm_t * const dev = (gx_device_perm_t *)pdev;
+ int ncomp = dev->num_std_colorant_names;
+ int raw_raster = pdev->width * ncomp;
+ byte *raw_line;
+ byte *cooked_line;
+ byte *row;
+ int code = 0;
+ int mode = dev->mode;
+ int permute = dev->permute;
+
+ fprintf(pstream, "P6\n%d %d\n255\n", dev->width, dev->height);
+ raw_line = gs_alloc_bytes(pdev->memory, raw_raster, "perm_print_page");
+ cooked_line = gs_alloc_bytes(pdev->memory, dev->width * 3, "perm_print_page");
+ for (y = 0; y < dev->height; y++) {
+ int x;
+ code = gdev_prn_get_bits(pdev, y, raw_line, &row);
+ for (x = 0; x < dev->width; x++) {
+ int c, m, y, k;
+ int r, g, b;
+
+ if (mode == 0) {
+ if (permute) {
+ c = row[x * ncomp + 1];
+ m = row[x * ncomp + 3];
+ y = row[x * ncomp + 0];
+ k = row[x * ncomp + 5];
+ } else {
+ c = row[x * ncomp];
+ m = row[x * ncomp + 1];
+ y = row[x * ncomp + 2];
+ k = row[x * ncomp + 3];
+ }
+ } else /* if (mode == 1) */ {
+ if (permute) {
+ c = row[x * ncomp + 1];
+ m = row[x * ncomp + 3];
+ y = row[x * ncomp + 0];
+ k = 0;
+ } else {
+ c = row[x * ncomp];
+ m = row[x * ncomp + 1];
+ y = row[x * ncomp + 2];
+ k = 0;
+ }
+ }
+ r = (255 - c) * (255 - k) / 255;
+ g = (255 - m) * (255 - k) / 255;
+ b = (255 - y) * (255 - k) / 255;
+ cooked_line[x * 3] = r;
+ cooked_line[x * 3 + 1] = g;
+ cooked_line[x * 3 + 2] = b;
+ }
+ fwrite(cooked_line, 1, dev->width * 3, pstream);
+ }
+ gs_free_object(pdev->memory, cooked_line, "perm_print_page");
+ gs_free_object(pdev->memory, raw_line, "perm_print_page");
+ return code;
+}
+
+static void
+perm_permute_cm(gx_device *pdev, frac out[])
+{
+ gx_device_perm_t * const dev = (gx_device_perm_t *)pdev;
+ if (dev->permute) {
+ frac y;
+ out[5] = dev->mode == 0 ? out[3] : 0;
+ out[4] = frac_0;
+ y = out[2];
+ out[3] = out[1];
+ out[2] = out[0];
+ out[1] = out[0];
+ out[0] = y;
+ }
+}
+
+static void
+gray_cs_to_perm_cm_0(gx_device *dev, frac gray, frac out[])
+{
+ out[0] = out[1] = out[2] = frac_0;
+ out[3] = frac_1 - gray;
+ perm_permute_cm(dev, out);
+}
+
+static void
+rgb_cs_to_perm_cm_0(gx_device *dev, const gs_imager_state *pis,
+ frac r, frac g, frac b, frac out[])
+{
+ color_rgb_to_cmyk(r, g, b, pis, out, dev->memory);
+ perm_permute_cm(dev, out);
+}
+
+static void
+cmyk_cs_to_perm_cm_0(gx_device *dev, frac c, frac m, frac y, frac k, frac out[])
+{
+ out[0] = c;
+ out[1] = m;
+ out[2] = y;
+ out[3] = k;
+ perm_permute_cm(dev, out);
+};
+
+static void
+gray_cs_to_perm_cm_1(gx_device *dev, frac gray, frac out[])
+{
+ out[0] = out[1] = out[2] = frac_1 - gray;
+ perm_permute_cm(dev, out);
+}
+
+static void
+rgb_cs_to_perm_cm_1(gx_device *dev, const gs_imager_state *pis,
+ frac r, frac g, frac b, frac out[])
+{
+ out[0] = frac_1 - r;
+ out[1] = frac_1 - g;
+ out[2] = frac_1 - b;
+ perm_permute_cm(dev, out);
+}
+
+static void
+cmyk_cs_to_perm_cm_1(gx_device *dev, frac c, frac m, frac y, frac k, frac out[])
+{
+ color_cmyk_to_rgb(c, m, y, k, NULL, out, dev->memory);
+ out[0] = frac_1 - out[0];
+ out[1] = frac_1 - out[1];
+ out[2] = frac_1 - out[2];
+ perm_permute_cm(dev, out);
+};
+
+static const gx_cm_color_map_procs perm_cmapping_procs_0 = {
+ gray_cs_to_perm_cm_0, rgb_cs_to_perm_cm_0, cmyk_cs_to_perm_cm_0
+};
+
+static const gx_cm_color_map_procs perm_cmapping_procs_1 = {
+ gray_cs_to_perm_cm_1, rgb_cs_to_perm_cm_1, cmyk_cs_to_perm_cm_1
+};
+
+static const gx_cm_color_map_procs *perm_cmapping_procs[] = {
+ &perm_cmapping_procs_0,
+ &perm_cmapping_procs_1
+};
+
+static const gx_cm_color_map_procs *
+perm_get_color_mapping_procs(const gx_device *dev)
+{
+ const gx_device_perm_t * const pdev = (const gx_device_perm_t *)dev;
+
+ if (pdev->mode < 0 || pdev->mode >= sizeof(perm_cmapping_procs) / sizeof(perm_cmapping_procs[0]))
+ return NULL;
+ return perm_cmapping_procs[pdev->mode];
+}
+
+#define compare_color_names(name, name_size, str, str_size) \
+ (name_size == str_size && \
+ (strncmp((const char *)name, (const char *)str, name_size) == 0))
+
+static int
+perm_get_color_comp_index(const gx_device *pdev, const char *pname,
+ int name_size, int component_type)
+{
+ const gx_device_perm_t * const dev = (const gx_device_perm_t *)pdev;
+ int n_separation_names = dev->num_std_colorant_names;
+ int i;
+
+ for (i = 0; i < n_separation_names; i++) {
+ const char *sep_name = dev->std_colorant_names[i];
+ if (compare_color_names(pname, name_size, sep_name, strlen(sep_name)))
+ return i;
+ }
+ return -1;
+}
+
+/* Note: the encode and decode procs are entirely standard. The
+ permutation is all done in the color space to color model mapping.
+ In fact, we could probably just use the default here.
+*/
+
+/*
+ * Encode a list of colorant values into a gx_color_index_value.
+ */
+static gx_color_index
+perm_encode_color(gx_device *dev, const gx_color_value colors[])
+{
+ int bpc = 8;
+ gx_color_index color = 0;
+ int i = 0;
+ int ncomp = dev->color_info.num_components;
+ COLROUND_VARS;
+
+ COLROUND_SETUP(bpc);
+ for (; i<ncomp; i++) {
+ color <<= bpc;
+ color |= COLROUND_ROUND(colors[i]);
+ }
+ return (color == gx_no_color_index ? color ^ 1 : color);
+}
+
+/*
+ * Decode a gx_color_index value back to a list of colorant values.
+ */
+static int
+perm_decode_color(gx_device *dev, gx_color_index color, gx_color_value *out)
+{
+ int bpc = 8;
+ int drop = sizeof(gx_color_value) * 8 - bpc;
+ int mask = (1 << bpc) - 1;
+ int i = 0;
+ int ncomp = dev->color_info.num_components;
+ COLDUP_VARS;
+
+ COLDUP_SETUP(bpc);
+ for (; i<ncomp; i++) {
+ out[ncomp - i - 1] = COLDUP_DUP(color & mask);
+ color >>= bpc;
+ }
+ return 0;
+}
+
+#define set_param_array(a, d, s)\
+ (a.data = d, a.size = s, a.persistent = false);
+
+static int
+perm_get_params(gx_device *pdev, gs_param_list *plist)
+{
+ gx_device_perm_t * const dev = (gx_device_perm_t *)pdev;
+ int code;
+
+ code = param_write_int(plist, "Permute", &dev->permute);
+ if (code >= 0)
+ code = param_write_int(plist, "Mode", &dev->mode);
+ /*
+ * We need to specify the SeparationColorNames if we are permuting the colors.
+ */
+ if (code >= 0 && dev->permute == 1) {
+ int i;
+ /* Temp variables. The data is copied into the plist below. */
+ gs_param_string_array scna;
+ gs_param_string scn[6];
+
+ set_param_array(scna, scn, dev->num_std_colorant_names);
+ /* Place colorant names into string array elements */
+ for (i = 0; i < dev->num_std_colorant_names; i++)
+ param_string_from_string(scn[i], dev->std_colorant_names[i]);
+ /*
+ * Place the name array in the plist. This includes allocating
+ * memory for the name array element and the actual string array.
+ */
+ code = param_write_name_array(plist, "SeparationColorNames", &scna);
+ }
+ if (code >= 0)
+ code = gdev_prn_get_params(pdev, plist);
+ return code;
+}
+
+#undef set_param_array
+
+static const char * DeviceCMYKComponents[] = {
+ "Cyan",
+ "Magenta",
+ "Yellow",
+ "Black",
+ 0 /* List terminator */
+};
+
+static const char * DeviceCMYComponents[] = {
+ "Cyan",
+ "Magenta",
+ "Yellow",
+ 0 /* List terminator */
+};
+
+static const char * DeviceNComponents[] = {
+ "Yellow",
+ "Cyan",
+ "Cyan2",
+ "Magenta",
+ "Zero",
+ "Black",
+ 0 /* List terminator */
+};
+
+static int
+perm_set_color_model(gx_device_perm_t *dev, int mode, int permute)
+{
+ dev->mode = mode;
+ dev->permute = permute;
+ if (mode == 0 && permute == 0) {
+ dev->std_colorant_names = DeviceCMYKComponents;
+ dev->num_std_colorant_names = 4;
+ dev->color_info.cm_name = "DeviceCMYK";
+ dev->color_info.polarity = GX_CINFO_POLARITY_SUBTRACTIVE;
+ } else if (mode == 0 && permute == 1) {
+ dev->std_colorant_names = DeviceNComponents;
+ dev->num_std_colorant_names = 6;
+ dev->color_info.cm_name = "DeviceN";
+ dev->color_info.polarity = GX_CINFO_POLARITY_SUBTRACTIVE;
+ } else if (mode == 1 && permute == 0) {
+ dev->std_colorant_names = DeviceCMYComponents;
+ dev->num_std_colorant_names = 3;
+ dev->color_info.cm_name = "DeviceCMY";
+ dev->color_info.polarity = GX_CINFO_POLARITY_SUBTRACTIVE;
+ } else if (mode == 1 && permute == 1) {
+ dev->std_colorant_names = DeviceNComponents;
+ dev->num_std_colorant_names = 6;
+ dev->color_info.cm_name = "DeviceN";
+ dev->color_info.polarity = GX_CINFO_POLARITY_SUBTRACTIVE;
+ } else {
+ return -1;
+ }
+ dev->color_info.num_components = dev->num_std_colorant_names;
+ dev->color_info.depth = 8 * dev->num_std_colorant_names;
+
+ return 0;
+}
+
+static int
+perm_put_params(gx_device *pdev, gs_param_list *plist)
+{
+ gx_device_perm_t * const dev = (gx_device_perm_t *)pdev;
+ gx_device_color_info save_info;
+ int code;
+ int new_permute = dev->permute;
+ int new_mode = dev->mode;
+
+ code = param_read_int(plist, "Permute", &new_permute);
+ if (code < 0)
+ return code;
+ code = param_read_int(plist, "Mode", &new_mode);
+ if (code < 0)
+ return code;
+ if (new_mode < 0 || new_mode >= sizeof(perm_cmapping_procs) / sizeof(perm_cmapping_procs[0])) {
+ dmlprintf(pdev->memory, "rangecheck!\n");
+ return_error(gs_error_rangecheck);
+ }
+ dev->permute = new_permute;
+ dev->mode = new_mode;
+ save_info = pdev->color_info;
+ code = perm_set_color_model(dev, dev->mode, dev->permute);
+ if (code >= 0)
+ code = gdev_prn_put_params(pdev, plist);
+ if (code < 0)
+ pdev->color_info = save_info;
+ return code;
+}
diff --git a/devices/gdevphex.c b/devices/gdevphex.c
new file mode 100644
index 000000000..43090f059
--- /dev/null
+++ b/devices/gdevphex.c
@@ -0,0 +1,3378 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+
+/****************************************************************************/
+/* Ghostscript printer driver for Epson Color Photo, Photo EX, Photo 700 */
+/****************************************************************************/
+
+#include "gdevprn.h"
+#include <math.h>
+
+/****************************************************************************/
+/* Legend */
+/****************************************************************************/
+
+/*
+
+HISTORY
+~~~~~~~
+
+8 June 1999 Zoltán Kócsi (aka Kocsonya) zoltan@bendor.com.au
+
+ Initial revision.
+ No shingling, depletion.
+ Colour only.
+ Dither matrix is blatantly copied from gslib.c.
+
+17 April 2000 Zoltán Kócsi
+
+ After much play worked out a reasonably simple colour mapping
+ that gives fairly good results. It has some very hairy things
+ in it but ot seems to work reasonably well on a variety of natural
+ as well as artificial images.
+
+LEGALISE
+~~~~~~~~
+
+The usual disclaimer applies, neither me (Zoltán Kócsi) nor
+Bendor Research Pty. Ltd. assume any liability whatsoever in
+relation to events arising out of or related to the use of
+the software or the included documentation in any form, way
+or purpose. This software is not guaranteed to work, you
+get it "as is" and use it for your own risk.
+
+This code has been donated to Aladdin Enterprises, see their
+license for details.
+
+CREDIT
+~~~~~~
+This driver was written from scratch, however, I have used the
+HP/BJ driver very heavily as a reference (GhostScript's documentation
+needs some working :-). In addition, I got some help in understanding
+the more arcane features of the printer by digging into the colour
+Epson driver and its documentation (documentation for the Photo EX
+did not exist). I thank to the authors of these drivers and the
+related docs.
+
+I do also hereby express my despising Epson, Inc. who try to enlarge
+Microsoft's monopoly by witholding programming information about such
+a commodity item as a printer.
+
+KNOWN BUGS/LIMITATIONS
+~~~~~~~~~~~~~~~~~~~~~~
+- Monochrome driver is not finished yet
+- The driver is not optimised for speed
+- The driver does not support TIFF compression
+- Shingling and depletion is not implemented
+- The colour correction and ink transfer curve are hardcoded
+- The dither matrix is straight stolen from Ghostscript
+- The alternative error diffusion included but does not work (yet)
+
+I plan to attend these issues later, however, I don't promise any timeframe
+for I have a lot else to do for bread & butter too.
+
+PREFACE
+~~~~~~~
+The Epson Stylus Photo EX is a colour ink-jet printer.
+It can handle papers up to A3. It uses 6 inks, black in one cartridge
+and cyan, magenta, yellow, light cyan and light magenta in an other
+cartridge. The head has 32 nozzles, with 1/90" spacing.
+The maximal resolution is 1440 dpi horizontal 720 dpi vertical.
+In 720x720 and 360x360 dpi it supports microweave. To achieve
+1440x720 you must use software weaving. It has only one built-in font,
+namely 12pt Courier; the printer in general havily relies on the
+driver software. It comes with (what else ?) Windows 9x and Mac drivers.
+
+The printer uses the ESC/P Raster protocol. This protocol is somewhat
+similar to the ESC/P2 one. Initially Epson refused to give any info
+about it. Later (unfortunately after I had already spent lot of time
+to reverse engineer it) they released its definition. It could be
+found on their website (http://www.ercipd.com/isv/level1/6clr_98b.pdf).
+Alas, they removed it, so at the moment I do not know about any existing
+docs of the printer.
+There are still a few commands which are not covered by the docs
+and for example the Windows driver uses them. There are others which
+are in the docs, saying that you can find them in other docs but you
+can't. Fortunately, these commands apparently have no effect on the
+printing process so this driver simply ignores them. Tricky business.
+
+By the way, my personal experience is that Epson tech support is
+a joke, or in Usenet lingvo it sucks big time - they know absolutely
+nothing about the product they supposed to support. Epson's webpage
+contains false info as well (they state that the Photo EX uses ESC/P2,
+which is simply not true).
+
+This driver should in theory support the Stylus 700 and the Stylus Photo
+as well but I have not tested it on them.
+
+If you think that you can get some useful info from me above of what you
+can find below, feel free to email me at zoltan@bendor.com.au.
+If you enhance the driver or find a bug *please* send me info about
+it.
+
+DRIVER
+~~~~~~
+The driver was written under Ghostscript 5.10.
+This file should contain two drivers, one for colour mode and one for B&W.
+The devices are "photoex" and "photoexm". The mono device driver is
+catered for (that is, the rendering part knows how to render for B&W)
+but it is not finished yet (no device structure and gray colour mapping
+procedures) mainly because all my B&W needs are fairly well satisfied
+by our laser printer.
+
+The driver features the following:
+
+Supported resolutions
+
+ 360x360 Y weaving (not that micro :-) by the printer
+ 720x720 Y microweave by the driver (quicker than the printer)
+ 1440x720 Y and X microweave by the driver
+
+ Resolutions other than these will result in a rangecheck error.
+
+Papersize:
+
+ Whatever Ghostscript supports. The printer docs say that if you load
+ multiple sheets of transparencies into the tray you should at least
+ have 30mm or 1.2" top margin. The driver always sets the smallest
+ possible top margin (3mm or 0.12"), it's up to you to comply.
+
+ In addition, the printer says that the bottom margin is at least
+ 14mm or 0.54". I violate it by setting it to 0.5" or 12.7mm.
+ 0.5" seems to be a common margin value for documents and you
+ would hate it when the last line of your page gets printed on the
+ top of the next sheet ...
+
+Options:
+
+ -dDotSize=n
+
+ n = 0 Let the driver choose a dotsize
+ n = 1 small dots
+ n = 2 more ink
+ n = 3 ink flood
+ n = 4 'super microdots' (whatever they are, they are *big*)
+
+ The default is 0 which is n=1 for 1440x720, 2 for 720x720 and
+ 3 for 360x360. Do not use large dots if you don't have to, you
+ will soak the paper. If you print 720x720 on normal paper, try
+ using n=1.
+
+ -dRender=n
+
+ n = 0 Floyd-Steinbeck error diffusion
+ n = 1 Clustered dither
+ n = 2 Bendor's error diffusion (experimental, do not use)
+
+ Default is Floyd-Steinbeck error diffusion
+
+ -dLeakage=nn
+
+ nn is between 0 and 25. It only effects Bendor's error diffusion.
+ It sets the percentage of the error which is left to 'leak', that
+ is it is the coefficient of an exponential decay of the error.
+ Experiments show that it can be beneficial on image quality.
+ Default is 0 (no leakage).
+
+ -dSplash=nn
+
+ nn is between 0 and 100. It only affects Bendor's error diffusion.
+ The ED routine tries to take the increase of dot diameter on certain
+ paper types into account.
+ It sets the percentage of the ink dot size increase as it splashes
+ onto the paper and spreads. 0 means no splashing, 100 means that
+ the dot is twice as large as it should be.
+ Default is 0.
+
+ -dBinhibit=n
+
+ If n is 1, then if black ink is deposited to a pixel, it will
+ inhibit the deposition of any other ink to the same pixel.
+ If 0, black ink may be deposited together with other inks.
+ Default is on (1).
+
+ESC/P RASTER DOCS
+~~~~~~~~~~~~~~~~~
+The parts of the ESC/P Raster protocol which I've managed to decipher,
+and which are actually used in this driver can be found below.
+nn, mm, xx, etc. represent a single byte with a binary value in it.
+nnnn, xxxx etc. represent a 16-bit binary number, sent in two bytes,
+in little endian order (low byte first). 2-digit numbers are a single
+byte in hex. Other chars are themselves.
+Quite a few commands are identical to the ESC/P2 commands, these are
+marked with (P2).
+
+ESC @ (P2)
+
+ Resets the printer.
+
+ESC ( U 01 00 nn (P2)
+
+ Sets the unit to 3600/nn dpi. Note that 1440 can not be set !
+
+ESC ( C 02 00 nnnn (P2)
+
+ Sets the page (paper) length to nnnn units
+
+ESC ( c 04 00 bbbb tttt (P2)
+
+ Sets the top margin to tttt units, the bottom margin to
+ bbbb units. The bottom margin is measured from the top
+ of the page not from the bottom of the page !
+
+ESC U nn (P2)
+
+ Unidirectional printing
+
+ nn
+ 00 off
+ 01 on
+ 30 off (this is ASCII 0)
+ 31 on (this is ASCII 1)
+
+ESC ( i 01 00 nn (P2)
+
+ Microweave
+
+ nn
+ 00 off
+ 01 on
+ 30 off (this is ASCII 0)
+ 31 on (this is ASCII 1)
+
+ Turns microweave on for 720x720 dpi printing.
+
+ESC r nn (P2)
+
+ Select colour
+
+ nn
+ 01 Cyan
+ 02 Magenta
+ 04 Yellow
+ 08 Black
+
+ESC ( G 01 00 nn (P2)
+
+ Selects graphics mode:
+
+ nn
+ 00 Off
+ 01 On
+ 30 Off
+ 31 On
+
+ESC ( v 02 00 dddd (P2)
+
+ Advance the paper by dddd units defined by ESC ( U
+
+ESC . cc vv hh nn mmmm <data> (P2)
+
+ Sends graphics data to the printer.
+
+ cc Encoding mode
+
+ 00 Raw data
+ 01 Run-length encoded data
+
+ vv Vertical resolution
+
+ 28 90 dpi *interleave*
+ 14 180 dpi *interleave*
+ 0a 360 dpi
+ 05 720 dpi
+
+ hh Horizontal resolution
+
+ 0a 360 dpi
+ 05 720 dpi
+
+ nn Number of nozzles
+
+ It should be set to 32 (normal printing) or 1 (microweave)
+
+ mmmm Number of collumns of data (not number of data bytes !)
+
+ <data>
+
+ The data should contain as many bytes as needed to fill the
+ mmmm * nn pixels. Data is presented horizontally, that is,
+ the bits of a byte will be represented by eight pixels in
+ a row. If the number of collumns is not an integer multiple
+ of eight, then some bits from the last byte belonging to the
+ row will be discarded and the next row starts on a byte boundary.
+ If a bit in a byte is '1' ink is deposited, if '0' not.
+ The leftmost pixel is represented by the MSB, rightmost by LSB.
+ In case of raw data that's about it.
+
+ In case of run-length encoded data, the following is done:
+ The first byte is a counter. If the counter is <= 127 then
+ the following counter+1 bytes are uncompressed data.
+ If the counter is >= 128 then the following single byte should
+ be repeated 257-counter times.
+
+ There are resolution restrictions:
+
+ 360x360 nozzle= 1 microweave on
+ 360x360 nozzle=32 microweave off
+ 720x 90 nozzle=32 microweave off
+ 720x720 nozzle= 1 microweave on
+
+ Other combinations are not supported.
+
+ESC ( e 02 00 00 nn
+
+ Sets the amount of ink spat onto the paper.
+
+ nn
+ 01 microdots (faint printing)
+ 02 normal dots (not so faint printing)
+ 03 double dots (full inking)
+ 04 super microdots (ink is continuously dripping :-)
+
+ Values other than that have apparently no effect.
+
+ESC ( K 02 00 xxxx
+
+ This command is sent by the Windows driver but it is not used
+ in the Epson test images. I have not found it having any effect
+ whatsoever. The driver does not use it. The Epson docs don't
+ mention it.
+
+ESC ( r 02 00 nn mm
+
+ Selects the ink according to this:
+
+ nn mm
+ 00 00 black
+ 00 01 magenta
+ 00 02 cyan
+ 00 04 yellow
+ 01 01 light magenta
+ 01 02 light yellow
+
+ESC ( \ 04 00 xxxx llll
+
+ Horizontal positioning of the head.
+
+ Moves the head to the position llll times 1/xxxx inches from
+ the left margin.
+ On the example images xxxx was always set to 1440.
+ I tried other values in which case the command was ignored,
+ so stick to 1440.
+
+ESC ( R ll 00 00 <text> <cc> xxxx nn .. nn
+ESC 00 00 00
+
+ This is supposedly sets the printer into 'remote' mode.
+ ll is the length of the <text> + 1 which consists of ASCII
+ characters (e.g. REMOTE1).
+ <cc> is a two-character code, for example "SN" or "LD".
+ xxxx is the number of bytes (nn -s) which will follow.
+ After that there's either a new <cc> xxxx nn .. nn sequence or
+ the ESC 00 00 00.
+ I have absolutely no idea about this command and the Epson document
+ says that it's in an other document. It's not in that other one.
+ The driver does not use it. The printer does not miss it.
+ The Epson test images use it and the Windows driver uses it too.
+ They send different <cc>-s and different values for identical <cc>-s.
+ Go figure.
+
+DRIVER INTERNALS
+~~~~~~~~~~~~~~~~
+First, some comments.
+Anything I know about the printer can be found above.
+Anything I know about Ghostscript internals (not much) can be
+found in the comments in the code. I do not believe in the 'it was hard
+to write, it should be hard to read' principle since I once had to
+understand my own code.
+Therefore, the code has lots of comments in it, sometimes apparently
+superfluous but I find it easier to understand the program 6 months
+later that way.
+I did not follow the Ghostscript or GNU style guide, I write code the way
+I like it - I'm a lazy dog :-) I use hard tabs at every 4th position,
+I use a *lot* of whitespace (as recommended by K&R in their original
+C book) and I have a formatting style similar to the K&R with the
+notable exception that I do not indent variable declarations that follow
+the curly. Anyway, you can run your favourite C formatter through the
+source.
+
+In addition to the above, the driver is not hand-optimised, it assumes
+that it is compiled with a good optimising compiler which will handle
+common subexpression ellimination, move loop independent code out of
+the loop, transform repeated array accesses to cached pointer arithmetics
+and so on. The code is much more readable this way and gcc is fairly
+good at doing optimisation. Feel free to hand-optimise it.
+
+So, the driver works the following way:
+
+When it has to render a page, first it sets up the basics such as margins
+and papersize and alike.
+
+Line scheduling
+---------------
+
+Then it calls the line scheduler. To see why do we have a scheduler, you
+have to understand weaving. The printer head has 32 nozzles which are
+spaced at 8 line intervals. Therefore, it prints 32 lines at a time but they
+are distributed over a 256 line high area. Obviously, if you want to print
+all the lines under the head, you should pass over the paper 8 times.
+You can do it the obvious way:
+Print, move down by one line, print ... repeat 8 times then move down
+by 256 - 8 lines and start again. Unfortunately, this would result in
+stripy images due to the differences between individual nozzles.
+Lines 0-7 would be printed by nozzle 0, 8-15 by nozzle 1 and so on. An
+8 line band has a visible height, so difference between nozzles will
+cause 8-line high bands to appear on the image.
+
+The solution is 'microweave', a funny way of doing interlaced printing.
+Instead of moving down 1, 1, 1, 1, .. 1, 248, 1, 1 .. you move down
+a constant, larger amount (called a band). This amount must be chosen
+in such a way that each line will be printed and preferably it will be
+printed only once.
+
+Let for example the move down amount (the band) be 31. Let's say,
+in band N nozzle 31 is over line 300, in which case nozzle 30 is over
+line 292. We move the head down by 31 lines, then line 299 will be
+under nozzle 27 and line 307 under nozzle 28.
+Next move, nozzle 23 will print line 298 and nozzle 24 line 306, then
+19/297 20/305, 15/296 16/304, 11/295 12/303, 7/294 8/302, 3/293 4/302,
+0/292 3/301 which covers the entire area between 292 and 307.
+The same will apply to any other area on the page. Also note that
+adjacent lines are always printed by different nozzles.
+You probably have realised that line 292 was printed in the first pass
+and in the last one. In this case, of course, the line must not be printed
+twice, one or the other pass should not deliver data to the nozzle which
+passes over this line.
+
+Now there's a twist. When the horizontal resolution is 1440 dpi you have
+to print each line twice, first depositing all even pixels then offset
+the head by 1/1440" and deposit all odd pixels (the printer can only
+print with 720 dpi but you can initially position the head with 1440 dpi
+resolution). You could do it the easy way, passing over the same area
+twice but you can do better. You can find a band size which will result
+each line being printed twice. Instead of suppressing the double print,
+you use this mechanism to print the odd and the even pixels.
+Now if you print one line's odd pixels, obviously, all lines belonging
+to the 31 other nozzles of the head will have their odd pixels printed too.
+Therefore, you have to keep track which lines have been printed in which
+phase and try to find an odd-even phase assignment to bands so that each line
+has both groups printed (and each group only once).
+The added bonus is that even the same line will be printed by two different
+nozzles thus effects of nozzle differences can be decreased further.
+
+The whole issue is further complicated with the beginning of the page and
+the end of the page. When you print the first 8 lines you *must* use the
+print, down by 1, print ... method but then you have to switch over to the
+banding method. To do it well, you should minimise the number of lines which
+are printed out of band. This optimisation is not complex but not trivial
+either. Our solution is to employ precalculated tables for the first 8 lines.
+(Epson's solution is not to print the 'problematic' lines at all - they
+warn you in the manual that at the top and bottom you may have "slight
+distortions". Analyzing their output reveals the reason ... ).
+The bottom is different. It is easier, because you are already banding, so
+you can't screw up the rest of the image. On the other hand, you can't use
+tables because these tables would depend on the page height which you don't
+know a priori. Our solution is to switch to single line mode when we can
+not do the banding any more and try to finish the page with the minimal
+amount of passes.
+
+So, first the driver calls the scheduler which returns a list of lines which
+it dispatched to print in the current band. Then the driver checks if it has
+all these lines halftoned. Since the head covers an area of 256 lines, we
+have to buffer that many lines (actually, 256-7). As the head moves down,
+we can flush lines which it has left and halftone the new ones.
+
+Colour transformations
+----------------------
+
+The next important issue is the colour transformation. The reason for doing
+this is that the ink is not perfect. Ideally, you have 3 inks, namely cyan
+magenta and yellow. Mixing these you can have all colours. Now the inks
+are not pure, that is the cyan ink contains some particles that have a
+colour other than the ideal cyan and so on. In addition, the inks are
+not exactly cyan, magenta and yellow. Therefore, you have to do some
+transformations that will map the ideal C, M, Y values to amounts of
+ink of the real kind. You also have a black ink. Although in theory
+mixing C, M, Y in equal amount will give you black, it doesn't exactly
+work that way. In addition, black ink is cheap compared to the colour
+so if you can use black, you rather use that. On top of all that,
+because of other effects (ink splashing on the paper and things like that)
+you have to apply some non-linear functions to get reasonable colours.
+
+Halftoning
+----------
+
+The driver has different halftoning methods.
+There is the classic Floyd-Stenberg error diffusion. There is an other
+ED, of which I'm hammering the matrix. The matrix is larger than the
+FS one and IMHO results in somewhat lower halftoning noise. However,
+it completely screws up some flat colours so don't use it.
+There is also dithering, which is quick but noisy.
+
+For any halftoning method, it is assumed that the haltoning can be
+done on the 4 colours (CMYK) separately and all interdependencies are
+already handled. It is an optimistic assumption, however, close enough.
+
+You can add any halftoning method you like by writing a halftoner
+module. A halftoner module consists of 4 functions:
+
+- Init, which is called before halftoning starts.
+- Threshold, which should return a number which tells the driver how many
+ empty lines needed before halftoning can be stopped (i.e. for how many
+ lines will a line affect halftoning of subsequent lines).
+- Halftone, which halftones one colour of one line
+- EndOfLine which is called when all colours of a scanline are halftoned,
+ you can do your housekeeping functions here.
+
+For example, in the case of ED init() clears the error buffers, threshold()
+returns ~5 (5 empty lines are enough for the accumulated error to go to
+almost zero), endofline() shuffles the error buffers and halftone() itself
+does the error diffusion. In case of dithering, threshold is 0 (dithering
+has no memory), init and endofline do nothing and halftone simply
+dithers a line.
+
+A few options are available for all halftoners:
+
+- the black is rendered first. Now this black line is presented to all
+ further passes. If a pixel is painted black, there's no point to
+ deposit any other colour on it, even if the halftoning itself would do.
+ Therefore, an already set black pixel can block the halftoning of colours
+ for that pixel. Whether this thing is activated or not is a command line
+ switch (default is on). Your halftoner may choose to ignore this flag.
+
+- the intensity value of the light-cyan and light-magenta ink can be
+ set from the command line. My experience is that the default 127 is
+ good enough, but you can override it if you want to.
+
+Apart from these features, each halftoner can have all sorts of other
+switches. Currently there are switches for the Bendor ED, see the
+comments in front of the BendorLine() function to see what they are.
+
+Postprocessing
+--------------
+
+After lines are halftoned, they are packed into bitstreams. If you use
+1440x720 then the 2 passes for the horizontal interleave are separated.
+Postprocessing should also do the shingling/depletion, but it is not
+yet done.
+
+Compression
+-----------
+
+The driver, before it sends the data to the printer, compresses it using
+RLE (run-length encoding) compression. It is not very effective but still
+more than nothing. I have not yet ventured into using TIFF as output format,
+it may come later.
+
+*/
+
+/****************************************************************************/
+/* Device specific definitions */
+/****************************************************************************/
+
+/*
+* Device limits
+*/
+
+#define MAX_WIDTH 11.46 /* Maximum printable width, 8250 dots */
+#define MAX_PIXELS 8250
+#define MAX_BYTES (MAX_PIXELS+7)/8
+
+/*
+* Margins (in inch)
+*/
+
+#define MARGIN_L 0.12 /* Left margin */
+#define MARGIN_R 0.12 /* Right margin */
+#define MARGIN_T 0.12 /* Top margin */
+#define MARGIN_B 0.50 /* Bottom margin (should be 0.54 !) */
+
+/*
+* We default to 720x720 dpi
+*/
+
+#define Y_DPI 720 /* Default vertical resolution [dpi] */
+#define X_DPI 720 /* Default horizontal resolution [dpi] */
+
+/*
+* Encoding of resolutions. Does *not* work with 1440 dpi !
+*/
+
+#define RESCODE( x ) (3600/(x))
+
+/*
+* The device has 6 different inks
+*/
+
+#define DCOLN 6
+
+/*
+* Device colour codes
+* CAVEAT: if you change them change the SendColour() procedure too !
+*/
+
+#define DEV_BLACK 0
+#define DEV_CYAN 1
+#define DEV_MAGENTA 2
+#define DEV_YELLOW 3
+#define DEV_LCYAN 4
+#define DEV_LMAGENTA 5
+
+/*
+* The head has 32 nozzles, with 8 x 1/720" spacing
+*/
+
+#define NOZZLES 32
+#define HEAD_SPACING 8
+
+/*
+* Some ASCII control characters
+*/
+
+#define CR 13 /* Carriage return */
+#define FF 12 /* Form feed */
+#define ESC "\033" /* Escape */
+
+/****************************************************************************/
+/* Internally used definitions */
+/****************************************************************************/
+
+#ifndef TRUE
+#define TRUE 1
+#endif
+
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+/*
+* Since the printer is CMYK, we use 4 colours internally
+*/
+
+#define ICOLN 4
+
+/*
+* This is the maximum number of error lines needed by any
+* currently implemented rendering function.
+* If you need more, increase it.
+*/
+
+#define MAX_ED_LINES 3
+
+/*
+* If this is defined to !0 then we use Adobe's CMYK -> RGB mapping,
+* Ghostscript's otherwise. Ghostscript claims that their mapping
+* is better. The mapping of CMYK to RGB according to Adobe is:
+*
+* R = 1.0 - min( 1.0, C + K )
+* G = 1.0 - min( 1.0, M + K )
+* B = 1.0 - min( 1.0, Y + K )
+*
+* while Ghostscript uses this:
+*
+* R = ( 1.0 - C ) * ( 1.0 - K )
+* G = ( 1.0 - M ) * ( 1.0 - K )
+* B = ( 1.0 - Y ) * ( 1.0 - K )
+*/
+
+#define MAP_RGB_ADOBE 0
+
+/*
+* We store a CMYK value in a 32 bit entity, each component being 8 bit.
+* These macros pack and unpack these blocks.
+* Ghostscript guarantees that when we get them back the unsigned long
+* will be placed in memory in a big-endian format (regardless of the
+* actual architecture it's running on), so we declare the colour offsets
+* accordingly.
+*/
+
+#define OFFS_C 0
+#define OFFS_M 1
+#define OFFS_Y 2
+#define OFFS_K 3
+
+#define DECOMPOSE_CMYK( index, c, m, y, k ) \
+ { \
+ (k) = (index) & 255; \
+ (y) = ( (index) >> 8 ) & 255; \
+ (m) = ( (index) >> 16 ) & 255; \
+ (c) = ( (index) >> 24 ) & 255; \
+ }
+
+#define BUILD_CMYK( c, m, y, k ) \
+ ((((long)(c)&255)<<24)|(((long)(m)&255)<<16)|\
+ (((long)(y)&255)<<8)|((long)(k)&255))
+
+/*
+* This structure is for colour compensation
+*/
+
+typedef struct {
+
+ int ra; /* Real colour angle (hue) */
+ int ia; /* Theoretical ink colour angle */
+ int c; /* Cyan component */
+ int m; /* Magenta component */
+ int y; /* Yellow component */
+
+} CCOMP;
+
+/*
+* Our device structure has some extensions
+*/
+
+typedef struct gx_photoex_device_s {
+
+ gx_device_common; /* This macro defines a graphics dev. */
+ gx_prn_device_common; /* This macro extends for printer dev. */
+ int shingling; /* Shingling (multipass, overlap) mode */
+ int depletion; /* Excess dot removal */
+ int halftoner; /* Rendering type */
+ int splash; /* Splashing compensation factor */
+ int leakage; /* Error leakage (percentage) */
+ int mono; /* Monochrome mode (black only) */
+ int pureblack; /* Black ink blocks others */
+ int midcyan; /* Light cyan ink value */
+ int midmagenta; /* Light magenta ink value */
+ int dotsize; /* Size of the ink dot */
+
+} gx_photoex_device;
+
+/*
+* These can save some typing
+*/
+
+typedef gx_device DEV;
+typedef gx_device_printer PDEV;
+typedef gx_photoex_device EDEV;
+typedef gx_color_index CINX;
+typedef gx_color_value CVAL;
+typedef gs_param_list PLIST;
+typedef gs_param_name PNAME;
+
+/*
+* How many lines do we have to think ahead
+*/
+
+#define MAX_MARK ((NOZZLES)*(HEAD_SPACING))
+
+/*
+* This structure stores a device scanline for one colour
+*/
+
+typedef struct {
+
+ int first; /* Index of the first useful byte */
+ int last; /* Index of the last useful byte */
+ byte data[ MAX_BYTES ]; /* Actual raw data */
+
+} RAWLINE;
+
+/*
+* These definitions are used by the microweave scheduler.
+* These are the band height definitions. Do not fiddle with them,
+* they are the largest number with which no lines are skipped
+* and the unused nozzles in the head for each band is minimal.
+* They, of course, depend on the number of nozzles in the head
+* and their spacing, these numbers are for 32 and 8, respectively.
+*/
+
+#define BAND_1440 13 /* Band height for 1440dpi, double scan */
+#define BAND_720 31 /* Band height for 720dpi, single scan */
+#define BAND_360 1 /* Band height for 360dpi, single scan */
+
+#define NOZZLE_1440 (NOZZLES) /* Number of nozzles used for 1440dpi */
+#define NOZZLE_720 (NOZZLES) /* Number of nozzles used for 720dpi */
+#define NOZZLE_360 1 /* Number of nozzles used for 360dpi */
+
+/*
+* This structure is used to generate the line scheduling data.
+* Input/output refers to the scheduler I/F: input means data
+* given to the scheduler, output is what it gives back. Unspecified
+* data is scheduler private.
+*/
+
+typedef struct {
+
+ int last; /* Input Last line to print */
+ int resol; /* Input X Resolution */
+ int nozzle; /* Output Number of nozzles */
+ int down; /* Output Lines to move down */
+ int head[ NOZZLES ]; /* Output Which lines to be sent */
+ int offset; /* Output Offset line by 1/1440" */
+ int top; /* Head position now */
+ int markbeg; /* First marked line */
+ byte mark[ MAX_MARK ]; /* Marks already printed lines */
+
+} SCHEDUL;
+
+/*
+* These macros are used to access the printer device
+*/
+
+#define SendByte( s, x ) fputc( (x), (s) )
+
+#define SendWord( s, x ) SendByte((s), (x) & 255); \
+ SendByte((s), ((x) >> 8 ) & 255);
+
+/*
+* This structure stores all the data during rendering
+*/
+
+typedef struct {
+
+ EDEV *dev; /* The actual device struct */
+ FILE *stream; /* Output stream */
+ int yres; /* Y resolution */
+ int xres; /* X resolution */
+ int start; /* Left margin in 1/1440 inches */
+ int width; /* Input data width in pixels */
+ int lines; /* Number of lines */
+ int mono; /* Black only */
+ byte *dbuff; /* Data buffer */
+ int htone_thold; /* Halftoner restart threshold */
+ int htone_last; /* Last line halftoned */
+ SCHEDUL schedule; /* Line scheduling info */
+
+ /* These are the error buffers for error diffusion. MAX_PIXELS*2
+ is needed for 1440 dpi printing. */
+
+ short err[ MAX_ED_LINES ][ ICOLN ][ MAX_PIXELS*2 ];
+
+ /* Error buffer pointers. I love C :-) */
+
+ short ( *error[ MAX_ED_LINES ] )[ MAX_PIXELS*2 ];
+
+ /* This stores the halftoning result for a line,
+ not yet in device format. (It's CMYK 1 byte/pixel/colour) */
+
+ byte res[ ICOLN ][ MAX_PIXELS*2 ];
+
+ /* This is the buffer for rendered lines, converted
+ to raw device data (not yet run-length encoded).
+ That is, it's 6 colours, 1 bit/pixel/colour.
+ The first index is the 1440 dpi X-weave phase. */
+
+ RAWLINE raw[ 2 ][ DCOLN ][ MAX_MARK ];
+
+ /* This buffer stores a single line of one colour,
+ run-length encoded, ready to send to the printer */
+
+ byte rle[ MAX_PIXELS * 2 ];
+
+} RENDER;
+
+/*
+* This is the sctructure used by the actual halftoner algorithms
+*/
+
+typedef struct {
+
+ RENDER *render; /* Render info, if needed */
+ byte *data; /* Input data */
+ int step; /* Steps on input data */
+ byte *res; /* Result */
+ byte *block; /* Blocking data */
+ short **err; /* Pointers to error buffers */
+ int lim1; /* Halftoning lower limit */
+ int lim2; /* Halftoning upper limit */
+ int mval; /* Level represented by 'light' colour */
+
+} HTONE;
+
+/*
+* Halftoner function table
+*/
+
+typedef struct {
+
+ int (*hthld)( RENDER *rend );
+ void (*hstrt)( RENDER *rend, int line );
+ void (*hteol)( RENDER *rend, int line );
+ void (*htone)( HTONE *htone, int line );
+
+} HFUNCS;
+
+/*
+* Number of known halftoning methods
+*/
+
+#define MAXHTONE 3
+
+/*
+* Dither matrix size
+*/
+
+#define DMATRIX_X 16
+#define DMATRIX_Y 16
+
+/****************************************************************************/
+/* Prototypes */
+/****************************************************************************/
+
+static int photoex_open( gx_device *pdev );
+static int photoex_print_page( PDEV *dev, FILE *prn_stream );
+static CINX photoex_map_rgb_color( DEV *dev, const CVAL prgb[] );
+static int photoex_map_color_rgb( DEV *dev, CINX index, CVAL prgb[] );
+static int photoex_get_params( DEV *dev, PLIST *plist );
+static int photoex_put_params( DEV *dev, PLIST *plist );
+
+static int PutInt( PLIST *plist, PNAME name, int *val,
+ int minval, int maxval, int code );
+static int GetInt( PLIST *list, PNAME name, int *value, int code );
+
+static int Cmy2A( int c, int m, int y );
+
+static void SchedulerInit( SCHEDUL *p );
+static int ScheduleLines( SCHEDUL *p );
+static void ScheduleLeading( SCHEDUL *p );
+static void ScheduleMiddle( SCHEDUL *p );
+static void ScheduleTrailing( SCHEDUL *p );
+static void ScheduleBand( SCHEDUL *p, int mask );
+
+static void RenderPage( RENDER *p );
+static void RenderLine( RENDER *p, int line );
+static int IsScanlineEmpty( RENDER *p, byte *line );
+
+static int RleCompress( RAWLINE *raw, int min, int max, byte *rle_data );
+static int RleFlush( byte *first, byte *reps, byte *now, byte *out );
+
+static void SendReset( FILE *stream );
+static void SendMargin( FILE *stream, int top, int bot );
+static void SendPaper( FILE *stream, int length );
+static void SendGmode( FILE *stream, int on );
+static void SendUnit( FILE *stream, int res );
+static void SendUnidir( FILE *stream, int on );
+static void SendMicro( FILE *stream, int on );
+static void SendInk( FILE *stream, int x );
+static void SendDown( FILE *stream, int x );
+static void SendRight( FILE *stream, int amount );
+static void SendColour( FILE *stream, int col );
+static void SendData( FILE *stream, int hres, int vres, int noz, int col );
+static void SendString( FILE *stream, const char *s );
+
+static void HalftonerStart( RENDER *render, int line );
+static int HalftoneThold( RENDER *render );
+static void HalftoneLine( RENDER *render, int line, byte *data );
+
+static int BendorThold( RENDER *p );
+static void BendorStart( RENDER *p, int line );
+static void BendorEol( RENDER *p, int line );
+static void BendorLine( HTONE *htone, int y );
+
+static int FloydSThold( RENDER *p );
+static void FloydSStart( RENDER *p, int line );
+static void FloydSEol( RENDER *p, int line );
+static void FloydSLine( HTONE *htone, int y );
+
+static int DitherThold( RENDER *p );
+static void DitherStart( RENDER *p, int line );
+static void DitherEol( RENDER *p, int line );
+static void DitherLine( HTONE *htone, int y );
+
+/****************************************************************************/
+/* Static data */
+/****************************************************************************/
+
+/*
+* Halftoner function table
+*/
+
+static const HFUNCS htable[ MAXHTONE ] = {
+
+ { FloydSThold, FloydSStart, FloydSEol, FloydSLine },
+ { DitherThold, DitherStart, DitherEol, DitherLine },
+ { BendorThold, BendorStart, BendorEol, BendorLine }
+};
+
+/*
+* Define the printer procedures.
+* The definition is based on GS macros, the only real stuff that we
+* define here are the photoex_ functions.
+*/
+
+static const gx_device_procs photoex_device_procs = prn_color_params_procs(
+
+ photoex_open, /* Opens the device */
+/* Since the print_page doesn't alter the device, this device can print in the background */
+ gdev_prn_bg_output_page,
+ gdev_prn_close,
+ photoex_map_rgb_color, /* Maps an RGB pixel to device colour */
+ photoex_map_color_rgb, /* Maps device colour back to RGB */
+ photoex_get_params, /* Gets device parameters */
+ photoex_put_params /* Puts device parameters */
+);
+
+/*
+* Device descriptor structure - this is what GhostScript looks
+* for and uses to identify our device.
+* Do not make it static !
+*/
+
+gx_photoex_device far_data gs_photoex_device = {
+
+ /* This is a macro that fills GS specific fields in the struct */
+
+ prn_device_body(
+
+ gx_photoex_device, /* Device struct type */
+ photoex_device_procs, /* Procedure table */
+ "photoex", /* Name of the device */
+ DEFAULT_WIDTH_10THS, /* Default width */
+ DEFAULT_HEIGHT_10THS, /* Default height */
+ X_DPI, /* Vertical resolution */
+ Y_DPI, /* Horizontal resolution */
+ MARGIN_L, /* Left margin */
+ MARGIN_B, /* Bottom margin */
+ MARGIN_R, /* Right margin */
+ MARGIN_T, /* Top margin */
+ ICOLN, /* Number of colours (4:CMYK) */
+ 32, /* Bit per pixel for the device(!) */
+ 255, /* Max. gray level */
+ 255, /* Max. colour level */
+ 256, /* Number of gray gradations */
+ 256, /* Number of colour gradations */
+ photoex_print_page /* Print page procedure */
+ ),
+
+ /* Here come our extensions */
+
+ 0, /* Shingling off, not implemented */
+ 0, /* Depletion off, not implemented */
+ 0, /* Dither type: FS ED */
+ 0, /* No splash correction */
+ 0, /* No leakage */
+ 0, /* Not monochrome */
+ 1, /* Colour inhibition on black */
+ 127, /* Mid level cyan */
+ 127, /* Mid level magenta */
+ 0 /* Automatic dot size setting */
+};
+
+/*
+* This table contains the line scheduling table for the first
+* few runs if we are in 720 dpi mode.
+*/
+
+static const int start_720[ HEAD_SPACING ][ NOZZLES ] = {
+
+ { 0, 8, 16, 24, 32, 40, 48, 56,
+ 64, 72, 80, 88, 96, 104, 112, 120,
+ 128, 136, 144, 152, 160, 168, 176, 184,
+ 192, 200, 208, 216, 224, 232, 240, 248 },
+
+ { 1, 9, 17, 25, 33, 41, 49, 57,
+ 65, 73, 81, 89, 97, 105, 113, 121,
+ 129, 137, 145, 153, 161, 169, 177, 185,
+ 193, 201, 209, -1, -1, -1, -1, -1 },
+
+ { 2, 10, 18, 26, 34, 42, 50, 58,
+ 66, 74, 82, 90, 98, 106, 114, 122,
+ 130, 138, 146, 154, 162, 170, 178, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1 },
+
+ { 3, 11, 19, 27, 35, 43, 51, 59,
+ 67, 75, 83, 91, 99, 107, 115, 123,
+ 131, 139, 147, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1 },
+
+ { 4, 12, 20, 28, 36, 44, 52, 60,
+ 68, 76, 84, 92, 100, 108, 116, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1 },
+
+ { 5, 13, 21, 29, 37, 45, 53, 61,
+ 69, 77, 85, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1 },
+
+ { 6, 14, 22, 30, 38, 46, 54, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1 },
+
+ { 7, 15, 23, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1 }
+};
+
+/*
+* This table contains the scheduling table for the first
+* few lines if we are in 1440 dpi mode
+*/
+
+static const int start_1440[ 2 ][ HEAD_SPACING ][ NOZZLES ] = {
+ {
+ { 0, 8, 16, 24, 32, 40, 48, 56,
+ 64, 72, 80, 88, 96, 104, 112, 120,
+ 128, 136, 144, 152, 160, 168, 176, 184,
+ 192, 200, 208, 216, 224, 232, 240, 248 },
+
+ { 1, 9, 17, 25, 33, 41, 49, 57,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1 },
+
+ { 2, 10, 18, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1 },
+
+ { 3, 11, 19, 27, 35, 43, 51, 59,
+ 67, 75, 83, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1 },
+
+ { 4, 12, 20, 28, 36, 44, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1 },
+
+ { 5, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1 },
+
+ { 6, 14, 22, 30, 38, 46, 54, 62,
+ 70, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1 },
+
+ { 7, 15, 23, 31, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1 },
+
+ },
+ {
+ { 0, 8, 16, 24, 32, 40, 48, 56,
+ 64, 72, 80, 88, 96, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1 },
+
+ { 1, 9, 17, 25, 33, 41, 49, 57,
+ 65, 73, 81, 89, 97, 105, 113, 121,
+ 129, 137, 145, 153, 161, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1 },
+
+ { 2, 10, 18, 26, 34, 42, 50, 58,
+ 66, 74, 82, 90, 98, 106, 114, 122,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1 },
+
+ { 3, 11, 19, 27, 35, 43, 51, 59,
+ 67, 75, 83, 91, 99, 107, 115, 123,
+ 131, 139, 147, 155, 163, 171, 179, 187,
+ -1, -1, -1, -1, -1, -1, -1, -1 },
+
+ { 4, 12, 20, 28, 36, 44, 52, 60,
+ 68, 76, 84, 92, 100, 108, 116, 124,
+ 132, 140, 148, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1 },
+
+ { 5, 13, 21, 29, 37, 45, 53, 61,
+ 69, 77, 85, 93, 101, 109, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1 },
+
+ { 6, 14, 22, 30, 38, 46, 54, 62,
+ 70, 78, 86, 94, 102, 110, 118, 126,
+ 134, 142, 150, 158, 166, 174, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1 },
+
+ { 7, 15, 23, 31, 39, 47, 55, 63,
+ 71, 79, 87, 95, 103, 111, 119, 127,
+ 135, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1 },
+
+ }
+};
+
+/*
+* This is the dither matrix we use for ordered dither
+* It is a shameless copy of Ghostscript's own ...
+*/
+
+static byte dmatrix[ DMATRIX_Y ][ DMATRIX_X ] = {
+ {
+ 0x0e, 0x8e, 0x2e, 0xae, 0x06, 0x86, 0x26, 0xa6,
+ 0x0c, 0x8c, 0x2c, 0xac, 0x04, 0x84, 0x24, 0xa4
+ },
+ {
+ 0xce, 0x4e, 0xee, 0x6e, 0xc6, 0x46, 0xe6, 0x66,
+ 0xcc, 0x4c, 0xec, 0x6c, 0xc4, 0x44, 0xe4, 0x64
+ },
+ {
+ 0x3e, 0xbe, 0x1e, 0x9e, 0x36, 0xb6, 0x16, 0x96,
+ 0x3c, 0xbc, 0x1c, 0x9c, 0x34, 0xb4, 0x14, 0x94
+ },
+ {
+ 0xfe, 0x7e, 0xde, 0x5e, 0xf6, 0x76, 0xd6, 0x56,
+ 0xfc, 0x7c, 0xdc, 0x5c, 0xf4, 0x74, 0xd4, 0x54
+ },
+ {
+ 0x01, 0x81, 0x21, 0xa1, 0x09, 0x89, 0x29, 0xa9,
+ 0x03, 0x83, 0x23, 0xa3, 0x0b, 0x8b, 0x2b, 0xab
+ },
+ {
+ 0xc1, 0x41, 0xe1, 0x61, 0xc9, 0x49, 0xe9, 0x69,
+ 0xc3, 0x43, 0xe3, 0x63, 0xcb, 0x4b, 0xeb, 0x6b
+ },
+ {
+ 0x31, 0xb1, 0x11, 0x91, 0x39, 0xb9, 0x19, 0x99,
+ 0x33, 0xb3, 0x13, 0x93, 0x3b, 0xbb, 0x1b, 0x9b
+ },
+ {
+ 0xf1, 0x71, 0xd1, 0x51, 0xf9, 0x79, 0xd9, 0x59,
+ 0xf3, 0x73, 0xd3, 0x53, 0xfb, 0x7b, 0xdb, 0x5b
+ },
+ {
+ 0x0d, 0x8d, 0x2d, 0xad, 0x05, 0x85, 0x25, 0xa5,
+ 0x0f, 0x8f, 0x2f, 0xaf, 0x07, 0x87, 0x27, 0xa7
+ },
+ {
+ 0xcd, 0x4d, 0xed, 0x6d, 0xc5, 0x45, 0xe5, 0x65,
+ 0xcf, 0x4f, 0xef, 0x6f, 0xc7, 0x47, 0xe7, 0x67
+ },
+ {
+ 0x3d, 0xbd, 0x1d, 0x9d, 0x35, 0xb5, 0x15, 0x95,
+ 0x3f, 0xbf, 0x1f, 0x9f, 0x37, 0xb7, 0x17, 0x97
+ },
+ {
+ 0xfd, 0x7d, 0xdd, 0x5d, 0xf5, 0x75, 0xd5, 0x55,
+ 0xff, 0x7f, 0xdf, 0x5f, 0xf7, 0x77, 0xd7, 0x57
+ },
+ {
+ 0x02, 0x82, 0x22, 0xa2, 0x0a, 0x8a, 0x2a, 0xaa,
+ 0x01, 0x80, 0x20, 0xa0, 0x08, 0x88, 0x28, 0xa8
+ },
+ {
+ 0xc2, 0x42, 0xe2, 0x62, 0xca, 0x4a, 0xea, 0x6a,
+ 0xc0, 0x40, 0xe0, 0x60, 0xc8, 0x48, 0xe8, 0x68
+ },
+ {
+ 0x32, 0xb2, 0x12, 0x92, 0x3a, 0xba, 0x1a, 0x9a,
+ 0x30, 0xb0, 0x10, 0x90, 0x38, 0xb8, 0x18, 0x98
+ },
+ {
+ 0xf2, 0x72, 0xd2, 0x52, 0xfa, 0x7a, 0xda, 0x5a,
+ 0xf0, 0x70, 0xd0, 0x50, 0xf8, 0x78, 0xd8, 0x58
+ }
+};
+
+/*
+* This is the (minimalistic) colour compensation table
+*/
+
+static CCOMP ctable[] = {
+
+ { -255, -255, 0, 0, 255 }, /* same as green */
+ { 102, 0, 255, 0, 0 }, /* cyan */
+ { 255, 255, 255, 255, 0 }, /* blue */
+ { 560, 512, 0, 255, 0 }, /* magenta */
+ { 765, 765, 0, 255, 255 }, /* red */
+ { 1045, 1020, 0, 0, 255 }, /* yellow */
+ { 1275, 1275, 255, 0, 255 }, /* green */
+ { 1632, 1530, 255, 0, 0 } /* same as cyan */
+};
+
+/*
+* This is the ink transfer function.
+* We use only one for all inks, this may be wrong.
+*/
+
+static const unsigned char xtrans[ 256 ] = {
+
+ 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, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 3, 3,
+ 3, 3, 3, 3, 3, 4, 4, 4,
+ 4, 4, 4, 5, 5, 5, 5, 5,
+ 6, 6, 6, 6, 6, 7, 7, 7,
+ 7, 8, 8, 8, 8, 9, 9, 9,
+ 10, 10, 10, 11, 11, 11, 12, 12,
+ 12, 13, 13, 13, 14, 14, 14, 15,
+ 15, 16, 16, 17, 17, 17, 18, 18,
+ 19, 19, 20, 20, 21, 21, 22, 22,
+ 23, 23, 24, 24, 25, 26, 26, 27,
+ 27, 28, 29, 29, 30, 30, 31, 32,
+ 32, 33, 34, 34, 35, 36, 37, 37,
+ 38, 39, 40, 40, 41, 42, 43, 44,
+ 44, 45, 46, 47, 48, 49, 50, 51,
+ 51, 52, 53, 54, 55, 56, 57, 58,
+ 59, 60, 61, 62, 63, 64, 65, 67,
+ 68, 69, 70, 71, 72, 73, 74, 76,
+ 77, 78, 79, 80, 82, 83, 84, 86,
+ 87, 88, 89, 91, 92, 94, 95, 96,
+ 98, 99, 101, 102, 103, 105, 106, 108,
+ 109, 111, 112, 114, 116, 117, 119, 120,
+ 122, 124, 125, 127, 129, 130, 132, 134,
+ 136, 137, 139, 141, 143, 145, 146, 148,
+ 150, 152, 154, 156, 158, 160, 162, 164,
+ 166, 168, 170, 172, 174, 176, 178, 180
+};
+
+/****************************************************************************/
+/* Device opening */
+/****************************************************************************/
+
+static int photoex_open( DEV *pdev )
+{
+double height;
+double width;
+float margins[ 4 ]; /* L, B, R, T */
+
+ height = pdev->height / pdev->y_pixels_per_inch;
+ width = pdev->width / pdev->x_pixels_per_inch;
+
+ margins[ 0 ] = 0.12;
+ margins[ 1 ] = 0.5;
+ margins[ 2 ] = 0.12;
+ margins[ 3 ] = ( width > 11.46+0.12 ) ? width - (11.46+0.12) : 0.12;
+
+ gx_device_set_margins( pdev, margins, true );
+ return( gdev_prn_open( pdev ) );
+}
+
+/****************************************************************************/
+/* Colour procedures */
+/****************************************************************************/
+
+/*
+* Map an RGB colour to device colour.
+* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+*
+* Since we present ourselves to Ghostscript as if we were a
+* full colour resolution RGB device, we calculate the CMYK
+* values and pack them into the result. This depends on
+* color_index being at least 32 bit !!!
+*/
+
+static CINX photoex_map_rgb_color( DEV *dev, const CVAL prgb[] )
+{
+CVAL r = prgb[0], g = prgb[1], b = prgb[2];
+int c, y, m, k;
+int a, s, f;
+EDEV *edev;
+int i;
+
+ edev = (EDEV *) dev;
+
+ /* White and black are treated on their own */
+
+ if ( ( r & g & b ) == ( 1 << gx_color_value_bits ) - 1 ) {
+
+ /* White */
+
+ return( BUILD_CMYK( 0, 0, 0, 0 ) );
+ }
+
+ if ( ( r | g | b ) == 0 ) {
+
+ /* Black */
+
+ return( BUILD_CMYK( 0, 0, 0, xtrans[ 0xff ] ) );
+ }
+
+ /* Map RGB to 8 bit/colour CMY */
+
+ c = 255 - ( r >> ( gx_color_value_bits - 8 ) );
+ m = 255 - ( g >> ( gx_color_value_bits - 8 ) );
+ y = 255 - ( b >> ( gx_color_value_bits - 8 ) );
+
+ k = xtrans[ min( c, min( m, y ) ) ] * 0.8; /* FIXME:empirical constant */
+ c -= k;
+ m -= k;
+ y -= k;
+
+ s = max ( c, max( y, m ) );
+
+ /* Map the colour to an angle and find the relevant table range */
+
+ a = Cmy2A( c, m, y );
+ for ( i = 1 ; a > ctable[ i ].ra ; i++ );
+
+ /* Now map c, m, y. */
+
+ f = ((a - ctable[ i-1 ].ra) << 16 ) / (ctable[ i ].ra - ctable[ i-1 ].ra);
+ c = (( ctable[i-1].c << 16 ) + ( ctable[i].c - ctable[i-1].c ) * f ) >> 16;
+ m = (( ctable[i-1].m << 16 ) + ( ctable[i].m - ctable[i-1].m ) * f ) >> 16;
+ y = (( ctable[i-1].y << 16 ) + ( ctable[i].y - ctable[i-1].y ) * f ) >> 16;
+
+ s = xtrans[ s ];
+ c = ( c * s ) >> 8;
+ m = ( m * s ) >> 8;
+ y = ( y * s ) >> 8;
+
+ return( BUILD_CMYK( c, m, y, k ) );
+}
+
+/*
+* Map a device colour value back to RGB.
+* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+*
+* CAVEAT:
+* This mapping is *not* the inverse of the RGB->CMYK.
+* It does not do any ink transfer compensation, colour compensation etc.
+*/
+
+static int photoex_map_color_rgb( DEV *dev, CINX index, CVAL prgb[] )
+{
+uint c, m, y, k;
+CVAL r, g, b;
+
+ /* Let's separate the colours */
+
+ DECOMPOSE_CMYK( index, c, m, y, k );
+
+ k = index & 255;
+ y = ( index >> 8 ) & 255;
+ m = ( index >> 16 ) & 255;
+ c = ( index >> 24 ) & 255;
+
+ /* Depending on whether we use Adobe or Ghostscript mapping,
+ calculate the colours */
+
+ if ( MAP_RGB_ADOBE ) {
+
+ r = gx_max_color_value * ( 1.0 - min( 1.0, (c / 255.0 + k / 255.0) ) );
+ g = gx_max_color_value * ( 1.0 - min( 1.0, (m / 255.0 + k / 255.0) ) );
+ b = gx_max_color_value * ( 1.0 - min( 1.0, (y / 255.0 + k / 255.0) ) );
+ }
+ else {
+
+ r = gx_max_color_value * ( 1.0 - c / 255.0 ) * ( 1.0 - k / 255.0);
+ g = gx_max_color_value * ( 1.0 - m / 255.0 ) * ( 1.0 - k / 255.0);
+ b = gx_max_color_value * ( 1.0 - y / 255.0 ) * ( 1.0 - k / 255.0);
+ }
+
+ prgb[ 0 ] = r;
+ prgb[ 1 ] = g;
+ prgb[ 2 ] = b;
+
+ return( 0 );
+}
+
+/*
+* This function maps a (c,m,y) triplet into an angle.
+* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+*
+* Angle: 0 cyan C=255 M= 0 Y= 0
+* 255 blue C=255 M=255 Y= 0
+* 510 magenta C= 0 M=255 Y= 0
+* 765 red C= 0 M=255 Y=255
+* 1020 yellow C= 0 M= 0 Y=255
+* 1275 green C=255 M= 0 Y=255
+* 1530 cyan
+*/
+
+static int Cmy2A( int c, int m, int y )
+{
+int black;
+int maxim;
+int a;
+
+ /* Calculate the black level */
+
+ black = min( c, min( m, y ) );
+
+ /* Remove the black from the colours themselves */
+
+ c -= black;
+ m -= black;
+ y -= black;
+
+ /* If all 3 remaining colours are 0, then it is a gray: special case */
+
+ if ( ! c && ! m && ! y ) return( 0 );
+
+ /* Normalise the colours. At least one at most two of them is 0
+ and at least one at most two of them is 255 */
+
+ maxim = max( c, max( m, y ) );
+
+ c = ( 255 * c ) / maxim;
+ m = ( 255 * m ) / maxim;
+ y = ( 255 * y ) / maxim;
+
+ if ( c == 255 ) {
+
+ if ( ! y )
+
+ a = m; /* cyan - blue */
+ else
+ a = 1530 - y; /* green - cyan */
+ }
+ else if ( m == 255 ) {
+
+ if ( ! c )
+
+ a = 510 + y; /* magenta - red */
+ else
+ a = 510 - c; /* blue - magenta */
+ }
+ else {
+
+ if ( ! m )
+
+ a = 1020 + c; /* yellow - green */
+ else
+ a = 1020 - m; /* red - yellow */
+ }
+
+ return( a );
+}
+
+/****************************************************************************/
+/* Device parameter handling */
+/****************************************************************************/
+
+/*
+* Tell Ghostscript all about our extra device parameters
+* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+*/
+
+static int photoex_get_params( DEV *device, PLIST *plist )
+{
+int code;
+EDEV *dev;
+
+ dev = (EDEV *) device;
+
+ code = gdev_prn_get_params( device, plist );
+
+ code = GetInt( plist, "Depletion", &dev->depletion, code );
+ code = GetInt( plist, "Shingling", &dev->shingling, code );
+ code = GetInt( plist, "Render", &dev->halftoner, code );
+ code = GetInt( plist, "Splash", &dev->splash, code );
+ code = GetInt( plist, "Leakage", &dev->leakage, code );
+ code = GetInt( plist, "Binhibit", &dev->pureblack, code );
+ code = GetInt( plist, "DotSize", &dev->dotsize, code );
+ return( code );
+}
+
+/*
+* Get all extra device-dependent parameters
+* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+*/
+
+static int photoex_put_params( DEV *device, PLIST *plist )
+{
+int code;
+EDEV *dev;
+
+ dev = (EDEV *) device;
+ code = 0;
+
+ code = PutInt( plist, "Depletion", &dev->depletion, 0, 2, code );
+ code = PutInt( plist, "Shingling", &dev->shingling, 0, 2, code );
+ code = PutInt( plist, "Render", &dev->halftoner, 0,MAXHTONE-1, code );
+ code = PutInt( plist, "Splash", &dev->splash, 0, 50, code );
+ code = PutInt( plist, "Leakage", &dev->leakage, 0, 25, code );
+ code = PutInt( plist, "Binhibit", &dev->pureblack, 0, 1, code );
+ code = PutInt( plist, "DotSize", &dev->dotsize, 0, 4, code );
+
+ if ( code < 0 )
+
+ return( code );
+ else
+ return( gdev_prn_put_params( device, plist ) );
+}
+
+/*
+* Reads a named integer from Ghostscript
+* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+*/
+
+static int PutInt( PLIST *plist, PNAME name, int *val,
+ int minval, int maxval, int code )
+{
+int new;
+
+ /* If code is already an error, we return it and do nothing. */
+
+ if ( code ) return( code );
+
+ /* Otherwise we try to read the value */
+
+ new = *val;
+
+ switch ( code = param_read_int( plist, name, &new ) ) {
+
+ case 1: /* No such parameter defined, it's OK */
+
+ code = 0;
+ break;
+
+ case 0: /* We have received a value, rangecheck */
+
+ if ( minval > new || new > maxval )
+
+ param_signal_error( plist, name, gs_error_rangecheck );
+ else
+ *val = new;
+
+ break;
+
+ default: /* Error */
+ break;
+ }
+
+ return( code );
+}
+
+/*
+* Writes a named integer to Ghostscript
+* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+*/
+
+static int GetInt( PLIST *list, PNAME name, int *value, int code )
+{
+ if ( code < 0 ) return( code );
+ return( param_write_int( list, name, value ) );
+}
+
+/****************************************************************************/
+/* Page rendering */
+/****************************************************************************/
+
+/*
+* This is the function that Ghostscript calls to render a page
+* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+*/
+
+static int photoex_print_page( PDEV *device, FILE *stream )
+{
+int pixels; /* Length of the line */
+int x; /* Work vars */
+EDEV *dev; /* Our device */
+RENDER *render; /* Rendering info */
+
+int xres, yres;
+int start, width;
+int unit;
+double psize;
+
+ dev = (EDEV *) device;
+
+ /* Check if the resolution is one of the supported ones */
+
+ yres = (int) dev->y_pixels_per_inch;
+ xres = (int) dev->x_pixels_per_inch;
+
+ if ( ! ( ( xres == 360 && yres == 360 ) ||
+ ( xres == 720 && yres == 720 ) ||
+ ( xres == 1440 && yres == 720 ) ) )
+
+ return( gs_error_rangecheck );
+
+ pixels = gdev_prn_raster( device ) / sizeof( long );
+ psize = device->height / device->y_pixels_per_inch;
+
+ /* Check if the requested width is within device limits.
+ The calculations are in 1440 dpi units. */
+
+ start = 1440.0 * dev_l_margin( device );
+
+ x = xres == 360 ? 4 : xres == 720 ? 2 : 1;
+
+ if ( start + x * pixels > 2 * MAX_PIXELS ) {
+
+ /* We're over the limit, clip width to the required level */
+
+ width = ( 2 * MAX_PIXELS - start ) / x;
+
+ /* It is rather inprobable that someone would set up a
+ left margin wider than the printer, still ... */
+
+ if ( width <= 0 ) return( gs_error_rangecheck );
+ }
+ else {
+
+ /* We accept the width as it is */
+
+ width = pixels;
+ }
+
+ /* Now try to get the memory we need. It's actually quite a lot,
+ since we have to cache 256 processed lines at 6kbyte each plus
+ we need error buffers and stuff. All in all, we'll request
+ about 1.5 ~ 2M. */
+
+ if ( ! ( render = (RENDER *) gs_malloc( dev->memory, 1, sizeof( RENDER ), "PhotoEX" )))
+
+ return_error( gs_error_VMerror );
+
+ if ( ! ( render->dbuff = (byte *) gs_malloc( dev->memory, pixels, sizeof( long ),
+ "PhotoEX" ) ) ) {
+
+ gs_free( dev->memory, render, 1, sizeof( RENDER ), "PhotoEX" );
+ return_error( gs_error_VMerror );
+ }
+
+ /* We've done every possible check and preparation, now
+ do the work. Fill the rest of the structure so we can pass
+ it to the actual render routine. */
+
+ render->dev = dev;
+ render->yres = yres;
+ render->xres = xres;
+ render->width = width;
+ render->lines = dev->height;
+ render->stream = stream;
+ render->mono = dev->mono;
+
+ /* Initialise the printer */
+
+ SendReset( stream );
+ SendReset( stream );
+ SendGmode( stream, 1 );
+
+ /* Set up units */
+
+ unit = ( yres == 360 ) ? 360 : 720;
+ SendUnit( stream, RESCODE( unit ) );
+
+ /* Set up papersize and margins */
+
+ SendPaper( stream, device->height / device->y_pixels_per_inch * unit );
+ SendMargin( stream, ( psize - dev_b_margin( device ) ) * unit,
+ dev_t_margin( device ) * unit );
+
+ /* Dot size as per user setting */
+
+ if ( dev->dotsize )
+
+ SendInk( stream, dev->dotsize );
+ else
+ SendInk( stream, yres == 360 ? 3 : ( xres == 720 ? 2 : 1 ) );
+
+ /* Microveawe is off, unidirectional printing on */
+
+ SendMicro( stream, 0 );
+ SendUnidir( stream, 1 );
+
+ /* Render the page and send image data to printer */
+
+ RenderPage( render );
+
+ /* Eject the paper, reset printer */
+
+ SendByte( stream, FF );
+ SendReset( stream );
+
+ /* Release the memory and return */
+
+ gs_free( dev->memory, render->dbuff, pixels, sizeof( long ), "PhotoEX" );
+ gs_free( dev->memory, render, 1, sizeof( RENDER ), "PhotoEX" );
+ return( 0 );
+}
+
+/*
+* Renders a page
+* ~~~~~~~~~~~~~~
+*/
+
+static void RenderPage( RENDER *p )
+{
+int last_done; /* The last line rendered */
+int last_need; /* The largest line number we need */
+int move_down; /* Amount of delayed head positioning */
+int last_band; /* Indicates the last band */
+int min, max; /* Min/max active bytes in a raw line */
+int phase; /* 1440dpi X weave offset */
+int i, j, l, col;
+
+ p->htone_thold = HalftoneThold( p );
+ p->htone_last = -1 - p->htone_thold;
+
+ p->schedule.top = -1;
+ p->schedule.resol = p->xres;
+ p->schedule.last = p->lines;
+
+ last_done = -1;
+ move_down = 0;
+
+ do {
+
+ /* Schedule the next batch of lines */
+
+ last_band = ScheduleLines( &p->schedule );
+
+ /* Find the largest line number we have to process and
+ halftone all lines which have not yet been done */
+
+ last_need = last_done;
+ for ( i = NOZZLES-1 ; i >= 0 && p->schedule.head[ i ] == -1 ; i-- );
+ if ( i >= 0 ) last_need = p->schedule.head[ i ];
+ while ( last_need > last_done ) RenderLine( p, ++last_done );
+
+ /* Now loop through the colours and build the data stream */
+
+ phase = p->schedule.offset;
+
+ for ( col = 0 ; col < DCOLN ; col++ ) {
+
+ /* First see if we have to send any data at all */
+
+ min = MAX_BYTES;
+ max = 0;
+
+ for ( i = 0 ; i < NOZZLES && i < p->schedule.nozzle ; i++ ) {
+
+ if ( ( j = p->schedule.head[ i ] ) != -1 ) {
+
+ j %= MAX_MARK;
+
+ if ( p->raw[ phase ][ col ][ j ].first < min )
+
+ min = p->raw[ phase ][ col ][ j ].first;
+
+ if ( p->raw[ phase ][ col ][ j ].last > max )
+
+ max = p->raw[ phase ][ col ][ j ].last;
+ }
+ }
+
+ if ( min <= max ) {
+
+ max++;
+
+ /* We have to send data to the printer. If we have
+ to position the head, do so now */
+
+ if ( move_down ) {
+
+ SendDown( p->stream, move_down );
+ move_down = 0;
+ }
+
+ /* Set the desired colour */
+
+ SendColour( p->stream, col );
+
+ /* Move the head to the desired position */
+
+ if ( p->xres == 360 )
+
+ SendRight( p->stream, 4 * 8 * min );
+
+ else if ( p->xres == 720 )
+
+ SendRight( p->stream, 2 * 8 * min );
+ else
+ SendRight( p->stream, 8 * min + phase );
+
+ /* Send the data */
+
+ SendData( p->stream, p->xres, p->yres, p->schedule.nozzle,
+ ( max-min ) * 8 );
+
+ for ( i = 0 ; i < p->schedule.nozzle ; i++ ) {
+
+ if ( ( j = p->schedule.head[ i ] ) == -1 ||
+ ( p->raw[ phase ][ col ][ j % MAX_MARK ].last <
+ p->raw[ phase ][ col ][ j % MAX_MARK ].first ) ) {
+
+ l = RleCompress( NULL, min, max, p->rle );
+ }
+ else {
+
+ l = RleCompress( p->raw[ phase ][ col ] + j % MAX_MARK,
+ min, max, p->rle );
+ }
+
+ fwrite( p->rle, l, 1, p->stream );
+ }
+
+ SendByte( p->stream, CR );
+ }
+ }
+
+ /* Note the amount the head should go down before it prints the
+ next band */
+
+ move_down += p->schedule.down;
+
+ } while ( ! last_band );
+}
+
+/*
+* Render the the next scanline
+* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+*
+* If it finds a continuous sequence of empty lines, it renders
+* the first htone_thold number of them then stops calling the
+* actual rendering function (which is computationally expensive).
+* When it sees a nonempty line again, it restarts the renderer.
+*/
+
+static void RenderLine( RENDER *p, int line )
+{
+byte *data;
+int i;
+
+ /* Get the line from Ghostscript and see if its empty */
+
+ gdev_prn_get_bits( (PDEV *) p->dev, line, p->dbuff, &data );
+
+ if ( IsScanlineEmpty( p, data ) ) {
+
+ if ( line - p->htone_last > p->htone_thold ) {
+
+ /* The line is empty and is farer from the last nonempty
+ line than the threshold, no need to render it. */
+
+ for ( i = 0 ; i < DCOLN ; i++ ) {
+
+ p->raw[ 0 ][ i ][ line % MAX_MARK ].first = MAX_BYTES;
+ p->raw[ 0 ][ i ][ line % MAX_MARK ].last = 0;
+ p->raw[ 1 ][ i ][ line % MAX_MARK ].first = MAX_BYTES;
+ p->raw[ 1 ][ i ][ line % MAX_MARK ].last = 0;
+
+ }
+ }
+ else {
+
+ /* The line is empty but it is within the threshold, so we
+ have to render it. We do not move the index, though */
+
+ HalftoneLine( p, line, data );
+ }
+ }
+ else {
+
+ /* This line is not empty */
+
+ if ( line - p->htone_last >= p->htone_thold ) {
+
+ /* Previous lines were empty and we have already stopped
+ rendering them. We have to restart the renderer */
+
+ HalftonerStart( p, line );
+ }
+
+ /* Render the line and move the last active index to this line */
+
+ HalftoneLine( p, line, data );
+ p->htone_last = line;
+ }
+}
+
+/*
+* This function tests if a scanline is empty
+* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+*/
+
+static int IsScanlineEmpty( RENDER *r, byte *line )
+{
+int i;
+long *p;
+
+ p = (long *) line;
+
+ for ( i = 0 ; i < r->width ; i++ ) {
+
+ if ( *p++ ) return( FALSE );
+ }
+
+ return( TRUE );
+}
+
+/****************************************************************************/
+/* Microweaved line scheduling */
+/****************************************************************************/
+
+/*
+* Schedule head data for the next band
+* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+*
+* This function fills the SCHEDUL structure with information
+* about what to print. The head field will contain the line numbers
+* which are assigned to the nozzles in the head. -1 means that
+* no active data is assigned to the nozzle in this band.
+* The offset field is only used for horizontal microweaving, if it
+* is set then the line should be offseted by 1/1440".
+* The down field contains the number of units which the head should
+* move down when printing of the band is finished. Other fields are
+* mainly for the routine's internal use. At the first call, however,
+* the top field should be set to -1, the resol field should be set
+* to 360, 720 or 1440 and the last field should contain the number
+* of lines to print (that is, last + 1 :-).
+*
+* The routine returns a flag indicating if this was the last print
+* for the page.
+*/
+
+static int ScheduleLines( SCHEDUL *p )
+{
+int i;
+
+ if ( p->top == -1 ) {
+
+ /* First call, init everything, then fall through to the rest */
+
+ SchedulerInit( p );
+ }
+
+ /* If nozzle is one, just schedule the next line and that's it.
+ You can use this feature for hardware microweave at 720 dpi,
+ the driver uses it for 360 dpi. */
+
+ if ( p->nozzle == 1 ) {
+
+ p->head[ 0 ] = p->top;
+ p->down = 1;
+ p->top++;
+ return( p->top == p->last );
+ }
+
+ /* Release all expired entries in the mark array */
+
+ for ( i = p->markbeg ; i < p->top ; i++ ) p->mark[ i % MAX_MARK ] = 0;
+ p->markbeg = p->top;
+
+ /* If top is less than the the head spacing, then create the image
+ by single steps. This will cause banding on the very top, but
+ there's nothing we can do about it. We're still better than
+ Epson's driver which simply ignores the first few lines,
+ it does not even try to schedule them ... */
+
+ if ( p->top < HEAD_SPACING ) {
+
+ ScheduleLeading( p );
+ return( FALSE );
+ }
+
+ /* See if we are almost at the end. If yes, we will advance line by
+ line. */
+
+ if ( p->top + p->resol + (NOZZLES) * HEAD_SPACING > p->last ) {
+
+ ScheduleTrailing( p );
+
+ if ( p->down )
+
+ return( p->top + (NOZZLES-1) * HEAD_SPACING >= p->last );
+ else
+ return( FALSE );
+ }
+
+ /* Otherwise we're in the middle of the page, just do the
+ simple banding and selecting as many lines as we can. */
+
+ ScheduleMiddle( p );
+ return( FALSE );
+}
+
+/*
+* Initialise the scheduler
+* ~~~~~~~~~~~~~~~~~~~~~~~~
+*/
+
+static void SchedulerInit( SCHEDUL *p )
+{
+int i;
+
+ p->top = 0;
+
+ switch ( p->resol ) {
+
+ case 360:
+ p->offset = 0;
+ p->resol = BAND_360;
+ p->nozzle = NOZZLE_360;
+ break;
+
+ case 720:
+ p->offset = 0;
+ p->resol = BAND_720;
+ p->nozzle = NOZZLE_720;
+ break;
+
+ case 1440:
+ p->offset = 1; /* Need to be set for the algorithm! */
+ p->resol = BAND_1440;
+ p->nozzle = NOZZLE_1440;
+ break;
+ }
+
+ for ( i = 0 ; i < NOZZLES ; i++ ) p->head[ i ] = -1;
+ for ( i = 0 ; i < MAX_MARK ; i++ ) p->mark[ i ] = 0;
+ p->markbeg = 0;
+}
+
+/*
+* Scheduling the first BAND lines for the image
+* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+*/
+
+static void ScheduleLeading( SCHEDUL *p )
+{
+int i;
+
+ if ( p->resol == BAND_720 ) {
+
+ /* Copy the line scheduling data to the struct */
+
+ memcpy( p->head, start_720[ p->top ], sizeof( int ) * NOZZLES );
+
+ /* Mark all lines to be set */
+
+ for ( i = 0 ; i < NOZZLES ; i++ )
+
+ if ( p->head[ i ] != -1 )
+
+ p->mark[ p->head[ i ] % MAX_MARK ] = 1;
+
+ /* We move down by one line except at the end */
+
+ if ( p->top == HEAD_SPACING - 1 ) {
+
+ p->down = BAND_720 - p->top;
+ p->top = BAND_720;
+ }
+ else {
+
+ p->down = 1;
+ p->top++;
+ }
+ }
+ else {
+
+ /* 1440 dpi version, two passes needed for each scanline */
+
+ if ( p->offset ) {
+
+ /* Copy the non-offseted scheduling data to the struct */
+
+ memcpy( p->head, start_1440[0][p->top], sizeof( int ) * NOZZLES );
+
+ /* Mark all lines to be set */
+
+ for ( i = 0 ; i < NOZZLES ; i++ )
+
+ if ( p->head[ i ] != -1 )
+
+ p->mark[ p->head[ i ] % MAX_MARK ] = 1;
+
+ /* This is the non-offseted line, do not move ! */
+
+ p->offset = 0;
+ p->down = 0;
+ }
+ else {
+
+ /* Copy the non-offseted schduling data to the struct */
+
+ memcpy( p->head, start_1440[1][p->top], sizeof( int ) * NOZZLES );
+
+ /* Mark all lines to be set */
+
+ for ( i = 0 ; i < NOZZLES ; i++ )
+
+ if ( p->head[ i ] != -1 )
+
+ p->mark[ p->head[ i ] % MAX_MARK ] |= 2;
+
+ /* We move down by one line except at the end and set offset */
+
+ if ( p->top == HEAD_SPACING - 1 ) {
+
+ p->down = BAND_1440 - p->top;
+ p->top = BAND_1440;
+ }
+ else {
+
+ p->down = 1;
+ p->top++;
+ }
+
+ p->offset = 1;
+ }
+ }
+}
+
+/*
+* Scheduling the bulk of the image
+* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+*/
+
+static void ScheduleMiddle( SCHEDUL *p )
+{
+int ph0, ph1;
+int line, mask;
+int i;
+
+ if ( p->resol == BAND_720 ) {
+
+ /* 720 DPI printing. See which lines should we print and
+ fill the head array accordingly, then move down a band. */
+
+ ScheduleBand( p, 1 );
+ p->down = BAND_720;
+ p->top += BAND_720;
+ }
+ else {
+
+ /* 1440 dpi printing. This is a bit more complex than the
+ 720 dpi one. First, see how many lines in each phase
+ has already been printed. */
+
+ ph0 = ph1 = 0;
+
+ for ( line = p->top, i=0 ; i < NOZZLES ; i++, line += HEAD_SPACING ) {
+
+ line = p->top + i * HEAD_SPACING;
+ ph0 += p->mark[ line % MAX_MARK ] & 1;
+ ph1 += p->mark[ line % MAX_MARK ] & 2;
+ }
+
+ ph1 >>= 1;
+
+ /* Choose the phase which has less lines in it. */
+
+ if ( ph0 <= ph1 ) {
+
+ p->offset = 0;
+ mask = 1;
+ }
+ else {
+
+ p->offset = 1;
+ mask = 2;
+ }
+
+ /* Fill the line array and mark the phase.
+ We should check here if moving down the head will leave
+ any line empty, but we do not because we *know* that it
+ won't - the BAND_1440 is selected by finding a value
+ which guarantees that it will cover every line. */
+
+ ScheduleBand( p, mask );
+ p->down = BAND_1440;
+ p->top += BAND_1440;
+ }
+}
+
+/*
+* Scheduling the last lines of the image
+* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+*/
+
+static void ScheduleTrailing( SCHEDUL *p )
+{
+int mask;
+
+ if ( p->down > 1 ) {
+
+ /* This is the first time we came here. */
+
+ p->offset = 1;
+ }
+
+ if ( p->resol == BAND_720 ) {
+
+ p->offset = 0;
+ p->down = 1;
+ mask = 1;
+ }
+ else {
+
+ if ( p->offset ) {
+
+ p->offset = 0;
+ p->down = 0;
+ mask = 1;
+ }
+ else {
+
+ p->offset = 1;
+ p->down = 1;
+ mask = 2;
+ }
+ }
+
+ ScheduleBand( p, mask );
+ p->top += p->down;
+}
+
+/*
+* Select lines from a given set
+* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+*/
+
+static void ScheduleBand( SCHEDUL *p, int mask )
+{
+int i;
+int line;
+
+ for ( line = p->top, i = 0 ; i < NOZZLES ; i++, line += HEAD_SPACING ) {
+
+ if ( p->mark[ line % MAX_MARK ] & mask ) {
+
+ p->head[ i ] = -1;
+ }
+ else {
+
+ p->head[ i ] = line;
+ p->mark[ line % MAX_MARK ] |= mask;
+ }
+ }
+}
+
+/****************************************************************************/
+/* Formatting printer data */
+/****************************************************************************/
+
+/*
+* Packs a line to raw device format
+* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+*
+* Reads pixnum pixels and if the pixel is lev_on, then sets the
+* appropriate bit in the resulting datastream. The length of the
+* result is pixnum/8 (rounded up).
+*/
+
+static void PackLine( byte *input, int pixnum, int lev_on, int step,
+ RAWLINE *line )
+{
+byte bits;
+byte *result;
+int i, j, k;
+
+ result = line->data;
+ line->first = MAX_PIXELS;
+ line->last = 0;
+
+ for ( j = 0x80, bits = k = i = 0 ; i < pixnum ; i += step, input += step ){
+
+ if ( *input == lev_on ) bits |= j;
+
+ if ( ! ( j >>= 1 ) ) {
+
+ if ( bits ) {
+
+ if ( line->first > k ) line->first = k;
+ if ( line->last < k ) line->last = k;
+ }
+
+ *result++ = bits;
+ j = 0x80;
+ bits = 0;
+ k++;
+ }
+ }
+
+ if ( j != 0x80 ) {
+
+ *result = bits;
+
+ if ( bits ) {
+
+ if ( line->first > k ) line->first = k;
+ if ( line->last < k ) line->last = k;
+ }
+ }
+}
+
+/*
+* Compresses (run-length encodes) a line
+* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+*
+* Returns the length of the RLE data.
+*/
+
+static int RleCompress( RAWLINE *raw, int min, int max, byte *rle_data )
+{
+int i, n;
+byte pbyte;
+byte *start, *rstrt;
+int length;
+byte *input;
+int len;
+
+ if ( ! raw ) {
+
+ /* This is an empty line */
+
+ for ( n = 0, i = max - min ; i >= 129 ; i -= 129 ) {
+
+ *rle_data++ = 128;
+ *rle_data++ = 0;
+ n += 2;
+ }
+
+ if ( i >= 2 ) {
+
+ *rle_data++ = 257 - i;
+ *rle_data++ = 0;
+ n += 2;
+ }
+ else if ( i ) {
+
+ *rle_data++ = 0;
+ *rle_data++ = 0;
+ n+= 2;
+ }
+
+ return( n );
+ }
+
+ /* There's data, set up encoding parameters */
+
+ input = raw->data + min;
+ len = max - min;
+
+ /* Create a run-length encoded version. We do it even if no pixel
+ was set because it may be that this line is just part of a
+ multi-line band. */
+
+ length = 0;
+ start = input;
+ rstrt = NULL;
+ pbyte = *input++;
+
+ for ( i = 1 ; i < len ; i++, input++ ) {
+
+ if ( *input == pbyte ) {
+
+ /* This byte is identical to the previous one(s). */
+
+ if ( ! rstrt ) {
+
+ /* This is the start of a new repeating sequence */
+
+ rstrt = input - 1;
+ }
+ }
+ else {
+
+ /* Different byte than the previous one(s) */
+
+ if ( rstrt ) {
+
+ /* There was a repetitive sequence. */
+
+ if ( rstrt - input < 4 ) {
+
+ /* For less than four bytes it isn't worth
+ to do RLE, we discard them */
+
+ rstrt = NULL;
+ }
+ else {
+
+ /* We must flush */
+
+ n = RleFlush( start, rstrt, input, rle_data );
+ rle_data += n;
+ length += n;
+
+ /* Initialise again */
+
+ start = rle_data;
+ rstrt = NULL;
+ }
+ }
+
+ pbyte = *rle_data;
+ }
+ }
+
+ /* We flush whatever is left over */
+
+ length += RleFlush( start, rstrt, input, rle_data );
+
+ return( length );
+}
+
+/*
+* This function flushes the RLE encoding buffer
+* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+*
+* Assumes that it gets a nonrepetitive pattern followed by a repetitive
+* one. 'first' points to the start of the non-repetitive part.
+* 'reps' points to the first byte in the repetitive sequence or it
+* may be NULL, if there were no repetitve bytes. 'now' points to
+* one after the last byte in the sequence.
+* It puts the result into 'out' and returns the number of bytes
+* written out.
+*
+* There is one possible performance penalty in using this method:
+* If the repetitive sequence is n*128+1 byte long, then the last
+* byte will be written out as single byte. If the following sequence
+* has a nonrepetitive start, this byte could be combined into that
+* but it isn't. This can cause some penalty, however, we will live
+* with that for now.
+*/
+
+static int RleFlush( byte *first, byte *reps, byte *now, byte *out )
+{
+int count;
+int l;
+
+ if ( ! first ) return( 0 );
+
+ if ( ! reps ) reps = now;
+
+ count = 0;
+
+ /* Write the nonrepetitve pattern first */
+
+ while ( ( l = reps - first ) ) {
+
+ if ( l > 128 ) {
+
+ /* More than 128 consecutive bytes, write out a 128 byte chunk */
+
+ *out++ = 127;
+ memcpy( out, first, 128 );
+ out += 128;
+ first += 128;
+ count += 129;
+ }
+ else {
+
+ /* There are not more than 128 bytes, write them into a
+ single chunk */
+
+ *out++ = l - 1;
+ memcpy( out, first, l );
+ count += l + 1;
+ first += l;
+ out += l;
+ }
+ }
+
+ /* Now write the repeated pattern */
+
+ while ( ( l = now - reps ) ) {
+
+ if ( l > 128 ) {
+
+ /* More than 128 bytes are identical, write out a
+ 129 byte chunk */
+
+ *out++ = 128;
+ *out++ = *reps;
+ count += 2;
+ reps += 129;
+ }
+ else {
+
+ if ( l == 1 ) {
+
+ /* There is only one byte left, write it out as a
+ nonrepetitive chunk */
+
+ *out++ = 0;
+ *out++ = *reps;
+ count += 2;
+ reps++;
+ }
+ else {
+
+ /* What remains is at least 2 bytes but not larger than what
+ can be written in a single chunk */
+
+ *out++ = 257 - l;
+ *out++ = *reps;
+ count += 2;
+ reps = now;
+ }
+ }
+ }
+
+ return( count );
+}
+
+/****************************************************************************/
+/* Low level procedures to send various commands to the printer */
+/****************************************************************************/
+
+static void SendReset( FILE *stream )
+{
+ SendString( stream, ESC "@" );
+}
+
+static void SendMargin( FILE *stream, int top, int bot )
+{
+ SendString( stream, ESC "(c" );
+ SendWord( stream, 4 );
+ SendWord( stream, bot );
+ SendWord( stream, top );
+}
+
+static void SendPaper( FILE *stream, int length )
+{
+ SendString( stream, ESC "(C" );
+ SendWord( stream, 2 );
+ SendWord( stream, length );
+}
+
+static void SendGmode( FILE *stream, int on )
+{
+ SendString( stream, ESC "(G" );
+ SendWord( stream, 1 );
+ SendByte( stream, on );
+}
+
+static void SendUnit( FILE *stream, int res )
+{
+ SendString( stream, ESC "(U" );
+ SendWord( stream, 1 );
+ SendByte( stream, res );
+}
+
+static void SendUnidir( FILE *stream, int on )
+{
+ SendString( stream, ESC "U" );
+ SendByte( stream, on );
+}
+
+static void SendMicro( FILE *stream, int on )
+{
+ SendString( stream, ESC "(i" );
+ SendWord( stream, 1 );
+ SendByte( stream, on );
+}
+
+static void SendInk( FILE *stream, int x )
+{
+ SendString( stream, ESC "(e" );
+ SendWord( stream, 2 );
+ SendByte( stream, 0 );
+ SendByte( stream, x );
+}
+
+static void SendDown( FILE *stream, int x )
+{
+ SendString( stream, ESC "(v" );
+ SendWord( stream, 2 );
+ SendWord( stream, x );
+}
+
+static void SendRight( FILE *stream, int amount )
+{
+ SendString( stream, ESC "(\\" );
+ SendWord( stream, 4 );
+ SendWord( stream, 1440 );
+ SendWord( stream, amount );
+}
+
+static void SendColour( FILE *stream, int col )
+{
+static int ccode[] = { 0x000, 0x200, 0x100, 0x400, 0x201, 0x101 };
+
+ SendString( stream, ESC "(r" );
+ SendWord( stream, 2 );
+ SendWord( stream, ccode[ col ] );
+}
+
+static void SendData( FILE *stream, int hres, int vres, int noz, int col )
+{
+ SendString( stream, ESC "." );
+ SendByte( stream, 1 ); /* Run-length encoded data */
+
+ /* If we use 1 nozzle, then vertical resolution is what it is.
+ Otherwise it must be set to 90 dpi */
+
+ if ( noz == 1 )
+
+ SendByte( stream, RESCODE( vres ) );
+ else
+ SendByte( stream, RESCODE( 90 ) );
+
+ /* The horizontal resolution is max. 720 dpi */
+
+ if ( hres > 720 )
+
+ SendByte( stream, RESCODE( 720 ) );
+ else
+ SendByte( stream, RESCODE( hres ) );
+
+ SendByte( stream, noz );
+ SendWord( stream, col );
+}
+
+static void SendString( FILE *stream, const char *s )
+{
+ while ( *s ) SendByte( stream, *s++ );
+}
+
+/****************************************************************************/
+/* Halftoning wrapper functions */
+/****************************************************************************/
+
+/*
+* Calls the start function of the choosen halftoner
+* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+*/
+
+static void HalftonerStart( RENDER *render, int line )
+{
+ (*(htable[ render->dev->halftoner ].hstrt))( render, line );
+}
+
+/*
+* Returns the restart threshold for the given halftoner
+* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+*/
+
+static int HalftoneThold( RENDER *render )
+{
+ return( (*(htable[ render->dev->halftoner ].hthld))( render ) );
+}
+
+/*
+* This function renders a line
+* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+*
+* This function has one fundamental assumption: halftoning of separate
+* colours is independent of each other.
+*
+* It calls the mono halftoner with the K, C, M, Y components.
+*/
+
+static void HalftoneLine( RENDER *render, int line, byte *data )
+{
+void (*htone)( HTONE *, int );
+EDEV *dev;
+int offs;
+HTONE hdata;
+short *errs[ MAX_ED_LINES ];
+int i;
+
+ /* Get the rendering function */
+
+ dev = render->dev;
+ htone = htable[ render->dev->halftoner ].htone;
+ offs = render->mono ? 0 : OFFS_K;
+
+ if ( dev->mono ) {
+
+ /* Monochrome, do only the black */
+
+ for ( i = 0 ; i < MAX_ED_LINES ; i++ )
+
+ errs[ i ] = render->error[ i ][ OFFS_K ];
+
+ hdata.render = render;
+ hdata.data = data + OFFS_K;
+ hdata.step = sizeof( byte );
+ hdata.res = render->res[ OFFS_K ];
+ hdata.block = NULL;
+ hdata.err = errs;
+ hdata.mval = 255;
+
+ (*htone)( &hdata, line );
+ }
+ else {
+
+ /* Colour. D black first */
+
+ for ( i = 0 ; i < MAX_ED_LINES ; i++ )
+
+ errs[ i ] = render->error[ i ][ OFFS_K ];
+
+ hdata.render = render;
+ hdata.step = sizeof( long );
+ hdata.data = data + OFFS_K;
+ hdata.res = render->res[ OFFS_K ];
+ hdata.block = NULL;
+ hdata.err = errs;
+ hdata.mval = 255;
+
+ (*htone)( &hdata, line );
+
+ /* Yellow has no intermediate ink. The already done black
+ may inhibit it. */
+
+ for ( i = 0 ; i < MAX_ED_LINES ; i++ )
+
+ errs[ i ] = render->error[ i ][ OFFS_Y ];
+
+ hdata.render = render;
+ hdata.step = sizeof( long );
+ hdata.data = data + OFFS_Y;
+ hdata.res = render->res[ OFFS_Y ];
+ hdata.block = dev->pureblack ? render->res[ OFFS_K ] : NULL;
+ hdata.err = errs;
+ hdata.mval = 255;
+
+ (*htone)( &hdata, line );
+
+ /* Cyan and magenta has intermediate colour ink, black may inhibit */
+
+ for ( i = 0 ; i < MAX_ED_LINES ; i++ )
+
+ errs[ i ] = render->error[ i ][ OFFS_C ];
+
+ hdata.data = data + OFFS_C;
+ hdata.res = render->res[ OFFS_C ];
+ hdata.block = dev->pureblack ? render->res[ OFFS_K ] : NULL;
+ hdata.mval = dev->midcyan;
+
+ (*htone)( &hdata, line );
+
+ for ( i = 0 ; i < MAX_ED_LINES ; i++ )
+
+ errs[ i ] = render->error[ i ][ OFFS_M ];
+
+ hdata.data = data + OFFS_M;
+ hdata.res = render->res[ OFFS_M ];
+ hdata.block = dev->pureblack ? render->res[ OFFS_K ] : NULL;
+ hdata.mval = dev->midmagenta;
+
+ (*htone)( &hdata, line );
+ }
+
+ /* Here we have create the raw device format scanlines */
+
+ if ( dev->mono ) {
+
+ if ( render->xres == 1440 ) {
+
+ PackLine( render->res[ OFFS_K ], render->width, 255, 2,
+ render->raw[ 0 ][ DEV_BLACK ]+ line % MAX_MARK );
+
+ PackLine( render->res[ OFFS_K ]+1, render->width-1, 255, 2,
+ render->raw[ 1 ][ DEV_BLACK ]+ line % MAX_MARK );
+ }
+ else {
+
+ PackLine( render->res[ OFFS_K ], render->width, 255, 1,
+ render->raw[ 0 ][ DEV_BLACK ]+ line % MAX_MARK );
+ }
+ }
+ else {
+
+ if ( render->xres == 1440 ) {
+
+ PackLine( render->res[ OFFS_K ], render->width, 255, 2,
+ render->raw[ 0 ][ DEV_BLACK ]+ line % MAX_MARK );
+
+ PackLine( render->res[ OFFS_K ]+1, render->width-1, 255, 2,
+ render->raw[ 1 ][ DEV_BLACK ]+ line % MAX_MARK );
+
+ PackLine( render->res[ OFFS_C ], render->width, 255, 2,
+ render->raw[ 0 ][ DEV_CYAN ]+ line % MAX_MARK );
+
+ PackLine( render->res[ OFFS_C ]+1, render->width-1, 255, 2,
+ render->raw[ 1 ][ DEV_CYAN ]+ line % MAX_MARK );
+
+ PackLine( render->res[ OFFS_M ], render->width, 255, 2,
+ render->raw[ 0 ][ DEV_MAGENTA ]+ line % MAX_MARK);
+
+ PackLine( render->res[ OFFS_M ]+1, render->width-1, 255, 2,
+ render->raw[ 1 ][ DEV_MAGENTA ]+ line % MAX_MARK);
+
+ PackLine( render->res[ OFFS_Y ], render->width, 255, 2,
+ render->raw[ 0 ][ DEV_YELLOW ]+ line % MAX_MARK );
+
+ PackLine( render->res[ OFFS_Y ]+1, render->width-1, 255, 2,
+ render->raw[ 1 ][ DEV_YELLOW ]+ line % MAX_MARK );
+
+ PackLine( render->res[ OFFS_C ], render->width, dev->midcyan,
+ 2, render->raw[ 0 ][ DEV_LCYAN ]+ line % MAX_MARK );
+
+ PackLine( render->res[ OFFS_C ]+1, render->width-1, dev->midcyan,
+ 2, render->raw[ 1 ][ DEV_LCYAN ]+ line % MAX_MARK );
+
+ PackLine( render->res[ OFFS_M ], render->width, dev->midmagenta,
+ 2, render->raw[0][ DEV_LMAGENTA ]+ line % MAX_MARK );
+
+ PackLine( render->res[ OFFS_M ]+1, render->width-1,dev->midmagenta,
+ 2, render->raw[1][ DEV_LMAGENTA ]+ line % MAX_MARK );
+ }
+ else {
+
+ PackLine( render->res[ OFFS_K ], render->width, 255, 1,
+ render->raw[ 0 ][ DEV_BLACK ]+ line % MAX_MARK );
+
+ PackLine( render->res[ OFFS_C ], render->width, 255, 1,
+ render->raw[ 0 ][ DEV_CYAN ]+ line % MAX_MARK );
+
+ PackLine( render->res[ OFFS_M ], render->width, 255, 1,
+ render->raw[ 0 ][ DEV_MAGENTA ]+ line % MAX_MARK);
+
+ PackLine( render->res[ OFFS_Y ], render->width, 255, 1,
+ render->raw[ 0 ][ DEV_YELLOW ]+ line % MAX_MARK );
+
+ PackLine( render->res[ OFFS_C ], render->width, dev->midcyan,
+ 1, render->raw[ 0 ][ DEV_LCYAN ]+ line % MAX_MARK );
+
+ PackLine( render->res[ OFFS_M ], render->width, dev->midmagenta,
+ 1, render->raw[0][ DEV_LMAGENTA ]+ line % MAX_MARK );
+ }
+ }
+
+ /* Call the halftoner specific end-of-line function */
+
+ (*htable[ render->dev->halftoner ].hteol)( render, line );
+}
+
+/****************************************************************************/
+/* Floyd - Steinberg error diffusion */
+/****************************************************************************/
+
+/*
+* This function returns the empty range threshold
+* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+*/
+
+static int FloydSThold( RENDER *p )
+{
+ return( 5 );
+}
+
+/*
+* This function initialises the halftoner
+* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+*/
+
+static void FloydSStart( RENDER *p, int line )
+{
+ memset( p->err, 0, ICOLN * MAX_PIXELS*2 );
+ p->error[ 0 ] = p->err[ 0 ];
+}
+
+/*
+* This function does the end-of-line processing
+* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+*/
+
+static void FloydSEol( RENDER *p, int line )
+{
+ /* Since we use single error buffering, nothing to do */
+}
+
+/*
+* This is the classical Floyd-Steinberg error diffusion.
+* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+*
+* The matrix is the following:
+*
+* * 7/16 r
+* 3/16 5/16 1/16
+*
+* r is the residual (0, in theory).
+* Absolutely nothing fancy is done here.
+*
+*/
+
+static void FloydSLine( HTONE *htone, int y )
+{
+int x; /* Counts the pixels */
+int pixel; /* Current pixel value */
+int pixerr; /* Error value */
+int length; /* Number of pixels to process */
+byte *res; /* Result */
+byte *data; /* Input data */
+byte *block; /* Block pixel */
+int lim1, lim2; /* Limits */
+short e0, e1; /* Propagating errors in current line */
+short *l0; /* Error buffer pointer */
+
+ length = htone->render->width;
+
+ res = htone->res;
+ data = htone->data;
+ block = htone->block;
+
+ lim1 = htone->mval / 2;
+ lim2 = ( htone->mval + 256 ) / 2;
+
+ l0 = htone->err[ 0 ];
+
+ e0 = l0[ 1 ];
+ e1 = l0[ 2 ];
+
+ l0[ 1 ] = 0;
+ l0[ 2 ] = 0;
+
+ for ( x = 0 ; x < length ; x++ ) {
+
+ /* First, clear the res byte. It is needed for the black */
+
+ *res = 0;
+
+ /* Add the actual error to the pixel, normalise, init, whatever. */
+
+ pixel = ( ( *data << 4 ) + e0 );
+ e0 = e1;
+ e1 = l0[ 3 ] + ( pixel & 15 ); /* This is the residual */
+
+ l0[ 3 ] = 0;
+ pixel >>= 4;
+
+ if ( ( block && *block ) || ( pixel < lim1 ) )
+
+ *res = 0;
+
+ else if ( pixel >= lim2 )
+
+ *res = 255;
+ else
+ *res = htone->mval;
+
+ /* Calculate the err */
+
+ pixerr = pixel - *res;
+
+ /* Diffuse the err */
+
+ e0 += ( pixerr << 3 ) - pixerr; /* 7/16 */
+ l0[ 0 ] += ( pixerr << 2 ) - pixerr; /* 3/16 */
+ l0[ 1 ] += ( pixerr << 2 ) + pixerr; /* 5/16 */
+ l0[ 2 ] += pixerr; /* 1/16 */
+
+ /* We have done everything, move the pointers */
+
+ res++;
+ if ( block ) block++;
+ data += htone->step;
+ l0++;
+ }
+}
+
+/****************************************************************************/
+/* Ordered dither */
+/****************************************************************************/
+
+/*
+* This function returns the empty range threshold
+* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+*/
+
+static int DitherThold( RENDER *p )
+{
+ return( 0 );
+}
+
+/*
+* This function initialises the halftoner
+* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+*/
+
+static void DitherStart( RENDER *p, int line )
+{
+ /* Nothing to initialise */
+}
+
+/*
+* This function does the end-of-line processing
+* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+*/
+
+static void DitherEol( RENDER *p, int line )
+{
+ /* Nothing to do - dithering has no memory */
+}
+
+/*
+* Clustered dither of a particular colour of a line
+* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+*/
+
+static void DitherLine( HTONE *htone, int y )
+{
+int x; /* Counts the pixels */
+int pixel; /* Current pixel value */
+int length; /* Number of pixels to process */
+byte *res; /* Result */
+byte *data; /* Input data */
+byte *block; /* Block pixel */
+byte *matrix; /* Dither matrix's current line */
+int mx; /* Matrix index */
+int lval, hval; /* Halftoned high/low values */
+
+ length = htone->render->width;
+
+ res = htone->res;
+ data = htone->data;
+ block = htone->block;
+
+ matrix = dmatrix[ y % DMATRIX_Y ];
+
+ for ( mx = x = 0 ; x < length ; x++ ) {
+
+ /* First, clear the res byte. It is needed for the black */
+
+ *res = 0;
+
+ /* Next, see if the pixel is above the mval */
+
+ if ( ( pixel = *data ) > htone->mval ) {
+
+ lval = htone->mval;
+ hval = 255;
+
+ if ( htone->mval == 127 )
+
+ pixel = ( ( pixel - htone->mval ) * 2 - 1 ) / 2;
+ else
+ pixel = ( pixel - htone->mval ) * 255 / ( 255 - htone->mval );
+ }
+ else {
+
+ lval = 0;
+ hval = htone->mval;
+
+ if ( htone->mval != 255 ) {
+
+ if ( htone->mval == 127 )
+
+ pixel = ( pixel * 4 + 1 ) / 2;
+ else
+ pixel = pixel * 255 / htone->mval;
+ }
+ }
+
+ if ( block && *block ) {
+
+ *res = 0;
+ }
+ else {
+
+ if ( pixel >= matrix[ mx ] )
+
+ *res = hval;
+ else
+ *res = lval;
+ }
+
+ res++;
+ if ( ++mx == DMATRIX_X ) mx = 0;
+ if ( block ) block++;
+ data += htone->step;
+ }
+}
+
+/****************************************************************************/
+/* Bendor's error diffusion */
+/****************************************************************************/
+
+/*
+* This function returns the empty range threshold
+* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+*/
+
+static int BendorThold( RENDER *p )
+{
+ return( 5 );
+}
+
+/*
+* This function initialises the halftoner
+* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+*/
+
+static void BendorStart( RENDER *p, int line )
+{
+ memset( p->err, 0, 2 * ICOLN * MAX_PIXELS*2 );
+ p->error[ 0 ] = p->err[ 0 ];
+ p->error[ 1 ] = p->err[ 1 ];
+}
+
+/*
+* This function does the end-of-line processing
+* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+*/
+
+static void BendorEol( RENDER *p, int line )
+{
+void *x;
+
+ x = p->error[ 0 ];
+ p->error[ 0 ] = p->error[ 1 ];
+ p->error[ 1 ] = x;
+}
+
+/*
+* Error diffusion of a particular colour of a line
+* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+*
+* This is not yet finished (the matrix is bad, actually).
+*
+* The matrix is the following (the normalisation factor is 1/128,
+* '*' represents the current pixel, r is the truncation residual):
+*
+* * 20 10 r
+* 8 14 20 14 8
+* 4 8 10 8 4
+*
+* We also try to take the splashing effect into account (the ink disperses
+* when it hits the paper so it partially covers surrounding pixels).
+* We use an other matrix for that, which is very simple:
+*
+* * 3
+* 2 3 2
+*
+* and the normalisation factor can be set by the user.
+* The splash matrix is only applied if we have actually deposited
+* ink and the amount added to the errors is independent that of the
+* actual image value, it only depends on the ink applied.
+* Of course, the ink spreads up and left as well and we could compensate
+* for this for a certain extent by keeping track of the errors caused in
+* previous pixels and lines and modifying them accordingly but it
+* would lead to a horrible code mess and it wouldn't be worth the effort.
+*
+* A further enhancement that we allow the error to 'leak'. Experimental
+* results show that with a 5-15% loss of error the image quality
+* increases and the colour distortion remains very low. If you think
+* about it, this, in effect stops the error to spread its effect over
+* large areas but it will have almost undisturbed effect on neighbouring
+* areas (you allow for an exponential error decay).
+* This parameter is user definable, too.
+*/
+
+static void BendorLine( HTONE *htone, int y )
+{
+int x; /* Counts the pixels */
+int pixel; /* Current pixel value */
+int pixerr; /* Error value */
+int pixe14; /* 14 * err value */
+int sval; /* Splash correction value */
+int splash; /* Splash factor */
+int leakage; /* Leakage factor */
+int length; /* Number of pixels to process */
+byte *res; /* Result */
+byte *data; /* Input data */
+byte *block; /* Block pixel */
+int lim1, lim2; /* Limits */
+short e0, e1; /* Propagating errors in current line */
+short *l0, *l1; /* Error buffer pointers */
+
+ splash = htone->render->dev->splash;
+ leakage = htone->render->dev->splash;
+ length = htone->render->width;
+
+ res = htone->res;
+ data = htone->data;
+ block = htone->block;
+
+ lim1 = htone->mval / 2;
+ lim2 = ( htone->mval + 256 ) / 2;
+
+ l0 = htone->err[ 0 ];
+ l1 = htone->err[ 1 ];
+
+ e0 = l0[ 2 ];
+ e1 = l0[ 3 ];
+
+ l0[ 2 ] = 0;
+ l0[ 3 ] = 0;
+
+ for ( x = 0 ; x < length ; x++ ) {
+
+ /* First, clear the res byte. It is needed for the black */
+
+ *res = 0;
+
+ /* Add the actual error to the pixel, normalise, init, whatever. */
+
+ pixel = ( ( *data << 7 ) + e0 );
+ e0 = e1;
+ e1 = l0[ 4 ] + ( pixel & 127 ); /* This is the residual */
+
+ l0[ 4 ] = 0;
+ pixel >>= 7;
+
+ if ( ( block && *block ) || ( pixel < lim1 ) )
+
+ *res = 0;
+
+ else if ( pixel >= lim2 )
+
+ *res = 255;
+ else
+ *res = htone->mval;
+
+ /* Calculate the err */
+
+ pixerr = pixel - *res;
+
+ /* If leakage is defined, apply it */
+
+ if ( leakage ) pixerr -= ( pixerr * leakage ) / 100;
+
+ /* Diffuse the err */
+
+ pixerr <<= 1; /* Multiplier is 2 */
+ pixe14 = pixerr; /* pixe14 now 2 */
+ pixerr <<= 1; /* Multiplier is 4 */
+ pixe14 += pixerr; /* pixe14 now 6 */
+
+ l0[ 0 ] += pixerr;
+ l0[ 4 ] += pixerr;
+
+ pixerr <<= 1; /* Multiplier is 8 */
+ pixe14 += pixerr; /* pixe14 now 14 */
+
+ l0[ 1 ] += pixerr;
+ l0[ 3 ] += pixerr;
+ l1[ 0 ] += pixerr;
+ l1[ 4 ] += pixerr;
+
+ pixerr += pixerr >> 2; /* Multiplier is 10 */
+
+ l0[ 2 ] += pixerr;
+ e1 += pixerr;
+
+ pixerr <<= 1; /* Multiplier is 20 */
+
+ l1[ 2 ] += pixerr;
+ e0 += pixerr;
+
+ /* pixe14 already contains 14 * err */
+
+ l1[ 1 ] += pixe14;
+ l1[ 3 ] += pixe14;
+
+ /* If splashing is defined, apply the splash matrix.
+ The splash value is normalised to the same level as the err */
+
+ if ( splash && *res ) {
+
+ sval = splash * *res; /* This is the 2x value */
+
+ l1[ 1 ] -= sval;
+ l1[ 3 ] -= sval;
+
+ sval += sval >> 1; /* This represents 3x */
+
+ e0 -= sval;
+ l1[ 2 ] -= sval;
+ }
+
+ /* We have done everything, move the pointers */
+
+ res++;
+ if ( block ) block++;
+ data += htone->step;
+ l0++, l1++;
+ }
+}
diff --git a/devices/gdevpjet.c b/devices/gdevpjet.c
new file mode 100644
index 000000000..7a876f848
--- /dev/null
+++ b/devices/gdevpjet.c
@@ -0,0 +1,250 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* H-P PaintJet, PaintJet XL, and DEC LJ250 drivers. */
+/* Thanks to Rob Reiss (rob@moray.berkeley.edu) for the PaintJet XL */
+/* modifications. */
+#include "gdevprn.h"
+#include "gdevpcl.h"
+
+/* X_DPI and Y_DPI must be the same, and may be either 90 or 180. */
+#define X_DPI 180
+#define Y_DPI 180
+
+/* We round up LINE_SIZE to a multiple of 8 bytes */
+/* because that's the unit of transposition from pixels to planes. */
+#define LINE_SIZE ((X_DPI * 85 / 10 + 63) / 64 * 8)
+
+/* The device descriptors */
+static dev_proc_print_page(lj250_print_page);
+static dev_proc_print_page(paintjet_print_page);
+static dev_proc_print_page(pjetxl_print_page);
+static int pj_common_print_page(gx_device_printer *, FILE *, int, const char *);
+/* Since the print_page doesn't alter the device, this device can print in the background */
+static gx_device_procs paintjet_procs =
+ prn_color_procs(gdev_prn_open, gdev_prn_bg_output_page, gdev_prn_close,
+ gdev_pcl_3bit_map_rgb_color, gdev_pcl_3bit_map_color_rgb);
+const gx_device_printer far_data gs_lj250_device =
+ prn_device(paintjet_procs, "lj250",
+ 85, /* width_10ths, 8.5" */
+ 110, /* height_10ths, 11" */
+ X_DPI, Y_DPI,
+ 0.25, 0, 0.25, 0, /* margins */
+ 3, lj250_print_page);
+const gx_device_printer far_data gs_paintjet_device =
+ prn_device(paintjet_procs, "paintjet",
+ 85, /* width_10ths, 8.5" */
+ 110, /* height_10ths, 11" */
+ X_DPI, Y_DPI,
+ 0.25, 0, 0.25, 0, /* margins */
+ 3, paintjet_print_page);
+/* Since the print_page doesn't alter the device, this device can print in the background */
+static gx_device_procs pjetxl_procs =
+ prn_color_procs(gdev_prn_open, gdev_prn_bg_output_page, gdev_prn_close,
+ gdev_pcl_3bit_map_rgb_color, gdev_pcl_3bit_map_color_rgb);
+const gx_device_printer far_data gs_pjetxl_device =
+ prn_device(pjetxl_procs, "pjetxl",
+ 85, /* width_10ths, 8.5" */
+ 110, /* height_10ths, 11" */
+ X_DPI, Y_DPI,
+ 0.25, 0, 0, 0, /* margins */
+ 3, pjetxl_print_page);
+
+/* Forward references */
+static int compress1_row(const byte *, const byte *, byte *);
+
+/* ------ Internal routines ------ */
+
+/* Send a page to the LJ250. We need to enter and exit */
+/* the PaintJet emulation mode. */
+static int
+lj250_print_page(gx_device_printer *pdev, FILE *prn_stream)
+{ fputs("\033%8", prn_stream); /* Enter PCL emulation mode */
+ /* ends raster graphics to set raster graphics resolution */
+ fputs("\033*rB", prn_stream);
+ /* Exit PCL emulation mode after printing */
+ return pj_common_print_page(pdev, prn_stream, 0, "\033*r0B\014\033%@");
+}
+
+/* Send a page to the PaintJet. */
+static int
+paintjet_print_page(gx_device_printer *pdev, FILE *prn_stream)
+{ /* ends raster graphics to set raster graphics resolution */
+ fputs("\033*rB", prn_stream);
+ return pj_common_print_page(pdev, prn_stream, 0, "\033*r0B\014");
+}
+
+/* Send a page to the PaintJet XL. */
+static int
+pjetxl_print_page(gx_device_printer *pdev, FILE *prn_stream)
+{ /* Initialize PaintJet XL for printing */
+ fputs("\033E", prn_stream);
+ /* The XL has a different vertical origin, who knows why?? */
+ return pj_common_print_page(pdev, prn_stream, -360, "\033*rC");
+}
+
+/* Send the page to the printer. Compress each scan line. */
+static int
+pj_common_print_page(gx_device_printer *pdev, FILE *prn_stream, int y_origin,
+ const char *end_page)
+{
+#define DATA_SIZE (LINE_SIZE * 8)
+ byte *data =
+ (byte *)gs_malloc(pdev->memory, DATA_SIZE, 1,
+ "paintjet_print_page(data)");
+ byte *plane_data =
+ (byte *)gs_malloc(pdev->memory, LINE_SIZE * 3, 1,
+ "paintjet_print_page(plane_data)");
+ if ( data == 0 || plane_data == 0 )
+ { if ( data )
+ gs_free(pdev->memory, (char *)data, DATA_SIZE, 1,
+ "paintjet_print_page(data)");
+ if ( plane_data )
+ gs_free(pdev->memory, (char *)plane_data, LINE_SIZE * 3, 1,
+ "paintjet_print_page(plane_data)");
+ return_error(gs_error_VMerror);
+ }
+
+ /* set raster graphics resolution -- 90 or 180 dpi */
+ fprintf(prn_stream, "\033*t%dR", X_DPI);
+
+ /* set the line width */
+ fprintf(prn_stream, "\033*r%dS", DATA_SIZE);
+
+ /* set the number of color planes */
+ fprintf(prn_stream, "\033*r%dU", 3); /* always 3 */
+
+ /* move to top left of page */
+ fprintf(prn_stream, "\033&a0H\033&a%dV", y_origin);
+
+ /* select data compression */
+ fputs("\033*b1M", prn_stream);
+
+ /* start raster graphics */
+ fputs("\033*r1A", prn_stream);
+
+ /* Send each scan line in turn */
+ { int lnum;
+ int line_size = gdev_mem_bytes_per_scan_line((gx_device *)pdev);
+ int num_blank_lines = 0;
+ for ( lnum = 0; lnum < pdev->height; lnum++ )
+ { byte *end_data = data + line_size;
+ gdev_prn_copy_scan_lines(pdev, lnum,
+ (byte *)data, line_size);
+ /* Remove trailing 0s. */
+ while ( end_data > data && end_data[-1] == 0 )
+ end_data--;
+ if ( end_data == data )
+ { /* Blank line */
+ num_blank_lines++;
+ }
+ else
+ { int i;
+ byte *odp;
+ byte *row;
+
+ /* Pad with 0s to fill out the last */
+ /* block of 8 bytes. */
+ memset(end_data, 0, 7);
+
+ /* Transpose the data to get pixel planes. */
+ for ( i = 0, odp = plane_data; i < DATA_SIZE;
+ i += 8, odp++
+ )
+ { /* The following is for 16-bit machines */
+#define spread3(c)\
+ { 0, c, c*0x100, c*0x101, c*0x10000L, c*0x10001L, c*0x10100L, c*0x10101L }
+ static ulong spr40[8] = spread3(0x40);
+ static ulong spr8[8] = spread3(8);
+ static ulong spr2[8] = spread3(2);
+ register byte *dp = data + i;
+ register ulong pword =
+ (spr40[dp[0]] << 1) +
+ (spr40[dp[1]]) +
+ (spr40[dp[2]] >> 1) +
+ (spr8[dp[3]] << 1) +
+ (spr8[dp[4]]) +
+ (spr8[dp[5]] >> 1) +
+ (spr2[dp[6]]) +
+ (spr2[dp[7]] >> 1);
+ odp[0] = (byte)(pword >> 16);
+ odp[LINE_SIZE] = (byte)(pword >> 8);
+ odp[LINE_SIZE*2] = (byte)(pword);
+ }
+ /* Skip blank lines if any */
+ if ( num_blank_lines > 0 )
+ { /* move down from current position */
+ fprintf(prn_stream, "\033&a+%dV",
+ num_blank_lines * (720 / Y_DPI));
+ num_blank_lines = 0;
+ }
+
+ /* Transfer raster graphics */
+ /* in the order R, G, B. */
+ for ( row = plane_data + LINE_SIZE * 2, i = 0;
+ i < 3; row -= LINE_SIZE, i++
+ )
+ { byte temp[LINE_SIZE * 2];
+ int count = compress1_row(row, row + LINE_SIZE, temp);
+ fprintf(prn_stream, "\033*b%d%c",
+ count, "VVW"[i]);
+ fwrite(temp, sizeof(byte),
+ count, prn_stream);
+ }
+ }
+ }
+ }
+
+ /* end the page */
+ fputs(end_page, prn_stream);
+
+ gs_free(pdev->memory, (char *)data, DATA_SIZE, 1, "paintjet_print_page(data)");
+ gs_free(pdev->memory, (char *)plane_data, LINE_SIZE * 3, 1, "paintjet_print_page(plane_data)");
+
+ return 0;
+}
+
+/*
+ * Row compression for the H-P PaintJet.
+ * Compresses data from row up to end_row, storing the result
+ * starting at compressed. Returns the number of bytes stored.
+ * The compressed format consists of a byte N followed by a
+ * data byte that is to be repeated N+1 times.
+ * In the worst case, the `compressed' representation is
+ * twice as large as the input.
+ * We complement the bytes at the same time, because
+ * we accumulated the image in complemented form.
+ */
+static int
+compress1_row(const byte *row, const byte *end_row,
+ byte *compressed)
+{ register const byte *in = row;
+ register byte *out = compressed;
+ while ( in < end_row )
+ { byte test = *in++;
+ const byte *run = in;
+ while ( in < end_row && *in == test ) in++;
+ /* Note that in - run + 1 is the repetition count. */
+ while ( in - run > 255 )
+ { *out++ = 255;
+ *out++ = ~test;
+ run += 256;
+ }
+ *out++ = in - run;
+ *out++ = ~test;
+ }
+ return out - compressed;
+}
diff --git a/devices/gdevplan.c b/devices/gdevplan.c
new file mode 100644
index 000000000..ca207cfd7
--- /dev/null
+++ b/devices/gdevplan.c
@@ -0,0 +1,553 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+/* PLANar devices */
+#include "gdevprn.h"
+#include "gscdefs.h"
+#include "gscspace.h" /* For pnm_begin_typed_image(..) */
+#include "gxgetbit.h"
+#include "gxlum.h"
+#include "gxiparam.h" /* For pnm_begin_typed_image(..) */
+#include "gdevmpla.h"
+#include "gdevplnx.h"
+#include "gdevppla.h"
+#include "gdevmem.h"
+
+/* This file defines 5 different devices:
+ *
+ * plan 24 bit RGB (8 bits per channel)
+ * plang 8 bit Grayscale
+ * planm 1 bit Monochrome
+ * planc 32 bit CMYK (8 bits per channel)
+ * plank 4 bit CMYK (1 bit per channel)
+ */
+
+/* Define DEBUG_PRINT to enable some debugging printfs. */
+#undef DEBUG_PRINT
+
+/* Define DEBUG_DUMP to dump the data to the output stream. */
+#define DEBUG_DUMP
+
+/* Define HT_RAW_DUMP to store the output as a raw CMYK buffer with the
+ data size packed into the file name. Photoshop does not handle pam
+ cmyk properly so we resort to this for debugging */
+#define HT_RAW_DUMP
+
+/* ------ The device descriptors ------ */
+
+/*
+ * Default X and Y resolution.
+ */
+#define X_DPI 600
+#define Y_DPI 600
+
+/* For all but mono, we need our own color mapping and alpha procedures. */
+static dev_proc_decode_color(plan_decode_color);
+static dev_proc_encode_color(plang_encode_color);
+static dev_proc_decode_color(plang_decode_color);
+static dev_proc_encode_color(planc_encode_color);
+static dev_proc_decode_color(planc_decode_color);
+static dev_proc_map_color_rgb(planc_map_color_rgb);
+
+static dev_proc_open_device(plan_open);
+static dev_proc_close_device(plan_close);
+
+/* And of course we need our own print-page routines. */
+static dev_proc_print_page(plan_print_page);
+
+static int plan_print_page(gx_device_printer * pdev, FILE * pstream);
+static int planm_print_page(gx_device_printer * pdev, FILE * pstream);
+static int plang_print_page(gx_device_printer * pdev, FILE * pstream);
+static int planc_print_page(gx_device_printer * pdev, FILE * pstream);
+static int plank_print_page(gx_device_printer * pdev, FILE * pstream);
+
+/* The device procedures */
+
+/* See gdevprn.h for the template for the following. */
+#define pgpm_procs(p_color_rgb, encode_color, decode_color) {\
+ plan_open,\
+ NULL, /* get_initial_matrix */ \
+ NULL, /* sync output */ \
+ /* Since the print_page doesn't alter the device, this device can print in the background */\
+ gdev_prn_bg_output_page, \
+ plan_close,\
+ NULL, /* map_rgb_color */ \
+ p_color_rgb, /* map_color_rgb */ \
+ NULL, /* fill_rectangle */ \
+ NULL, /* tile_rectangle */ \
+ NULL, /* copy_mono */ \
+ NULL, /* copy_color */ \
+ NULL, /* draw_line */ \
+ NULL, /* get_bits */ \
+ gdev_prn_get_params, \
+ gdev_prn_put_params,\
+ NULL, /* map_cmyk_color */ \
+ NULL, /* get_xfont_procs */ \
+ NULL, /* get_xfont_device */ \
+ NULL, /* map_rgb_alpha_color */ \
+ gx_page_device_get_page_device, \
+ NULL, /* get_alpha_bits */\
+ NULL, /* copy_alpha */\
+ NULL, /* get_band */\
+ NULL, /* copy_rop */\
+ NULL, /* fill_path */\
+ NULL, /* stroke_path */\
+ NULL, /* fill_mask */\
+ NULL, /* fill_trapezoid */\
+ NULL, /* fill_parallelogram */\
+ NULL, /* fill_triangle */\
+ NULL, /* draw_thin_line */\
+ NULL, /* begin_image */\
+ NULL, /* image_data */\
+ NULL, /* end_image */\
+ NULL, /* strip_tile_rectangle */\
+ NULL, /* strip_copy_rop */\
+ NULL, /* get_clipping_box */\
+ NULL, /* begin_typed_image */\
+ NULL, /* get_bits_rectangle */\
+ NULL, /* map_color_rgb_alpha */\
+ NULL, /* create_compositor */\
+ NULL, /* get_hardware_params */\
+ NULL, /* text_begin */\
+ NULL, /* finish_copydevice */\
+ NULL, /* begin_transparency_group */\
+ NULL, /* end_transparency_group */\
+ NULL, /* begin_transparency_mask */\
+ NULL, /* end_transparency_mask */\
+ NULL, /* discard_transparency_layer */\
+ NULL, /* get_color_mapping_procs */\
+ NULL, /* get_color_comp_index */\
+ encode_color, /* encode_color */\
+ decode_color, /* decode_color */\
+ NULL, /* pattern_manage */\
+ NULL, /* fill_rectangle_hl_color */\
+ NULL, /* include_color_space */\
+ NULL, /* fill_linear_color_scanline */\
+ NULL, /* fill_linear_color_trapezoid */\
+ NULL, /* fill_linear_color_triangle */\
+ NULL, /* update spot */\
+ NULL, /* DevN params */\
+ NULL, /* fill page */\
+ NULL, /* push_transparency_state */\
+ NULL, /* pop_transparency_state */\
+ NULL, /* put_image */\
+ NULL /* dev_spec_op */\
+}
+
+static const gx_device_procs planm_procs =
+ pgpm_procs(gdev_prn_map_color_rgb, gdev_prn_map_rgb_color, gdev_prn_map_color_rgb);
+static const gx_device_procs plang_procs =
+ pgpm_procs(plang_decode_color, plang_encode_color, plang_decode_color);
+static const gx_device_procs plan_procs =
+ pgpm_procs(plan_decode_color, gx_default_rgb_map_rgb_color, plan_decode_color);
+static const gx_device_procs planc_procs =
+ pgpm_procs(planc_map_color_rgb, planc_encode_color, planc_decode_color);
+static const gx_device_procs plank_procs =
+ pgpm_procs(planc_map_color_rgb, planc_encode_color, planc_decode_color);
+
+/* Macro for generating device descriptors. */
+#define plan_prn_device(procs, dev_name, num_comp, depth, max_gray, max_rgb, print_page) \
+{ prn_device_body(gx_device_printer, procs, dev_name,\
+ DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS, X_DPI, Y_DPI,\
+ 0, 0, 0, 0,\
+ num_comp, depth, max_gray, max_rgb, max_gray + 1, max_rgb + 1,\
+ print_page)\
+}
+
+/* The device descriptors themselves */
+const gx_device_printer gs_plan_device =
+ plan_prn_device(plan_procs, "plan", 3, 24, 255, 255, plan_print_page);
+const gx_device_printer gs_plang_device =
+ plan_prn_device(plang_procs, "plang", 1, 8, 255, 0, plang_print_page);
+const gx_device_printer gs_planm_device =
+ plan_prn_device(planm_procs, "planm", 1, 1, 1, 0, planm_print_page);
+const gx_device_printer gs_plank_device =
+ plan_prn_device(plank_procs, "plank", 4, 4, 1, 1, plank_print_page);
+const gx_device_printer gs_planc_device =
+ plan_prn_device(planc_procs, "planc", 4, 32, 255, 255, planc_print_page);
+
+/* ------ Initialization ------ */
+
+#ifdef DEBUG_DUMP
+static void dump_row_ppm(int w, byte **data, FILE *dump_file)
+{
+ byte *r = data[0];
+ byte *g = data[1];
+ byte *b = data[2];
+
+ if (dump_file == NULL)
+ return;
+ while (w--) {
+ fputc(*r++, dump_file);
+ fputc(*g++, dump_file);
+ fputc(*b++, dump_file);
+ }
+}
+
+static void dump_row_pnmk(int w, byte **data, FILE *dump_file)
+{
+ byte *r = data[0];
+ byte *g = data[1];
+ byte *b = data[2];
+ byte *k = data[3];
+
+ if (dump_file == NULL)
+ return;
+ while (w) {
+ byte C = *r++;
+ byte M = *g++;
+ byte Y = *b++;
+ byte K = *k++;
+ int s;
+ for (s=7; s>=0; s--) {
+ fputc(255*((C>>s)&1), dump_file);
+ fputc(255*((M>>s)&1), dump_file);
+ fputc(255*((Y>>s)&1), dump_file);
+ fputc(255*((K>>s)&1), dump_file);
+ w--;
+ if (w == 0) break;
+ }
+ }
+}
+
+static void dump_row_pnmc(int w, byte **data, FILE *dump_file)
+{
+ byte *r = data[0];
+ byte *g = data[1];
+ byte *b = data[2];
+ byte *k = data[3];
+
+ if (dump_file == NULL)
+ return;
+ while (w--) {
+ fputc(*r++, dump_file);
+ fputc(*g++, dump_file);
+ fputc(*b++, dump_file);
+ fputc(*k++, dump_file);
+ }
+}
+
+static void dump_row_pbm(int w, byte **data, FILE *dump_file)
+{
+ byte *r = data[0];
+
+ if (dump_file == NULL)
+ return;
+ w = (w+7)>>3;
+ while (w--) {
+ fputc(*r++, dump_file);
+ }
+}
+
+static void dump_row_pgm(int w, byte **data, FILE *dump_file)
+{
+ byte *r = data[0];
+
+ if (dump_file == NULL)
+ return;
+ while (w--) {
+ fputc(*r++, dump_file);
+ }
+}
+
+typedef void (*dump_row)(int w, byte **planes, FILE *file);
+
+static dump_row dump_start(int w, int h, int num_comps, int log2bits,
+ FILE *dump_file)
+{
+ dump_row row_proc = NULL;
+ if ((num_comps == 3) && (log2bits == 3)) {
+ row_proc = dump_row_ppm;
+ } else if ((num_comps == 1) && (log2bits == 0)) {
+ row_proc = dump_row_pbm;
+ } else if ((num_comps == 1) && (log2bits == 3)) {
+ row_proc = dump_row_pgm;
+ } else if ((num_comps == 4) && (log2bits == 0)) {
+ row_proc = dump_row_pnmk;
+ } else if ((num_comps == 4) && (log2bits == 3)) {
+ row_proc = dump_row_pnmc;
+ } else
+ return NULL;
+ if (dump_file == NULL)
+ return row_proc;
+ if (num_comps == 3)
+ fprintf(dump_file, "P6 %d %d 255\n", w, h);
+ else if (num_comps == 4) {
+ if (log2bits == 0)
+ fprintf(dump_file, "P7\nWIDTH %d\nHEIGHT %d\nDEPTH 4\n"
+ "MAXVAL 255\nTUPLTYPE CMYK\n# Image generated by %s\nENDHDR\n", w, h, gs_product);
+
+ else
+ fprintf(dump_file, "P7\nWIDTH %d\nHEIGHT %d\nDEPTH 4\n"
+ "MAXVAL 255\nTUPLTYPE CMYK\n# Image generated by %s\nENDHDR\n", w, h, gs_product);
+ } else if (log2bits == 0)
+ fprintf(dump_file, "P4 %d %d\n", w, h);
+ else
+ fprintf(dump_file, "P5 %d %d 255\n", w, h);
+ return row_proc;
+}
+#endif
+
+/*
+ * Define a special open procedure to ensure we use a planar device.
+ */
+static int
+plan_open(gx_device * pdev)
+{
+ gx_device_printer * const bdev = (gx_device_printer *)pdev;
+ int code;
+
+#ifdef DEBUG_PRINT
+ emprintf(pdev->memory, "plan_open\n");
+#endif
+ code = gdev_prn_open_planar(pdev, 1);
+ if (code < 0)
+ return code;
+ pdev->color_info.separable_and_linear = GX_CINFO_SEP_LIN;
+ set_linear_color_bits_mask_shift(pdev);
+
+ return code;
+}
+
+static int
+plan_close(gx_device *pdev)
+{
+ gx_device_printer *pldev = (gx_device_printer *)pdev;
+
+#ifdef DEBUG_PRINT
+ emprintf(pdev->memory, "plan_close\n");
+#endif
+
+ return gdev_prn_close(pdev);
+}
+
+/* ------ Color mapping routines ------ */
+
+/* Map an RGB color to a gray value. */
+static gx_color_index
+plang_encode_color(gx_device * pdev, const gx_color_value cv[])
+{ /* We round the value rather than truncating it. */
+ gx_color_value gray;
+ gx_color_value r, g, b;
+
+ r = cv[0]; g = cv[0]; b = cv[0];
+ gray = ((r * (ulong) lum_red_weight) +
+ (g * (ulong) lum_green_weight) +
+ (b * (ulong) lum_blue_weight) +
+ (lum_all_weights / 2)) / lum_all_weights
+ * pdev->color_info.max_gray / gx_max_color_value;
+
+ return gray;
+}
+
+/* Map a gray value back to an RGB color. */
+static int
+plang_decode_color(gx_device * dev, gx_color_index color,
+ gx_color_value prgb[3])
+{
+ gx_color_value gray =
+ color * gx_max_color_value / dev->color_info.max_gray;
+
+ prgb[0] = gray;
+ prgb[1] = gray;
+ prgb[2] = gray;
+ return 0;
+}
+
+/* Map an rgb color tuple back to an RGB color. */
+static int
+plan_decode_color(gx_device * dev, gx_color_index color,
+ gx_color_value prgb[3])
+{
+ uint bitspercolor = dev->color_info.depth / 3;
+ uint colormask = (1 << bitspercolor) - 1;
+ uint max_rgb = dev->color_info.max_color;
+
+ prgb[0] = ((color >> (bitspercolor * 2)) & colormask) *
+ (ulong) gx_max_color_value / max_rgb;
+ prgb[1] = ((color >> bitspercolor) & colormask) *
+ (ulong) gx_max_color_value / max_rgb;
+ prgb[2] = (color & colormask) *
+ (ulong) gx_max_color_value / max_rgb;
+ return 0;
+}
+
+/* Map a cmyk color tuple back to an gs color. */
+static int
+planc_decode_color(gx_device * dev, gx_color_index color,
+ gx_color_value prgb[4])
+{
+ uint bitspercolor = dev->color_info.depth / 4;
+ uint colormask = (1 << bitspercolor) - 1;
+ uint c, m, y, k;
+
+#define cvalue(c) ((gx_color_value)((ulong)(c) * gx_max_color_value / colormask))
+
+ k = color & colormask;
+ color >>= bitspercolor;
+ y = color & colormask;
+ color >>= bitspercolor;
+ m = color & colormask;
+ c = color >> bitspercolor;
+ prgb[0] = cvalue(c);
+ prgb[1] = cvalue(m);
+ prgb[2] = cvalue(y);
+ prgb[3] = cvalue(k);
+ return 0;
+}
+
+/* Map a cmyk color back to an rgb tuple. */
+static int
+planc_map_color_rgb(gx_device * dev, gx_color_index color,
+ gx_color_value prgb[3])
+{
+ uint bitspercolor = dev->color_info.depth / 4;
+ uint colormask = (1 << bitspercolor) - 1;
+ uint c, m, y, k;
+
+#define cvalue(c) ((gx_color_value)((ulong)(c) * gx_max_color_value / colormask))
+
+ k = color & colormask;
+ color >>= bitspercolor;
+ y = color & colormask;
+ color >>= bitspercolor;
+ m = color & colormask;
+ c = color >> bitspercolor;
+ k = colormask - k;
+ prgb[0] = cvalue((colormask - c) * k / colormask);
+ prgb[1] = cvalue((colormask - m) * k / colormask);
+ prgb[2] = cvalue((colormask - y) * k / colormask);
+ return 0;
+}
+
+/* Map CMYK to color. */
+static gx_color_index
+planc_encode_color(gx_device * dev, const gx_color_value cv[])
+{
+ int bpc = dev->color_info.depth / 4;
+ gx_color_index color;
+ COLROUND_VARS;
+
+ COLROUND_SETUP(bpc);
+ color = ((((((COLROUND_ROUND(cv[0]) << bpc) +
+ COLROUND_ROUND(cv[1])) << bpc) +
+ COLROUND_ROUND(cv[2])) << bpc) +
+ COLROUND_ROUND(cv[3]));
+
+ return (color == gx_no_color_index ? color ^ 1 : color);
+}
+
+/* ------ Internal routines ------ */
+
+/* Print a page using a given row printing routine. */
+static int
+plan_print_page_loop(gx_device_printer * pdev, int log2bits, int numComps,
+ FILE *pstream)
+{
+ gx_device_printer *pldev = (gx_device_printer *)pdev;
+ int lnum;
+ int code = 0;
+ gs_get_bits_options_t options;
+#ifdef DEBUG_DUMP
+ dump_row row_proc = NULL;
+ int output_is_nul = !strncmp(pdev->fname, "nul:", min(strlen(pdev->fname), 4)) ||
+ !strncmp(pdev->fname, "/dev/null", min(strlen(pdev->fname), 9));
+
+ if (!output_is_nul)
+ row_proc = dump_start(pdev->width, pdev->height, numComps, log2bits, pstream);
+#endif
+ options = GB_ALIGN_ANY |
+ GB_RETURN_POINTER |
+ GB_OFFSET_0 |
+ GB_RASTER_STANDARD |
+ GB_COLORS_NATIVE |
+ GB_ALPHA_NONE;
+ if (numComps == 1)
+ options |= GB_PACKING_CHUNKY;
+ else
+ options |= GB_PACKING_PLANAR;
+ for (lnum = 0; lnum < pdev->height; lnum++) {
+ gs_int_rect *unread, rect;
+ gs_get_bits_params_t params;
+
+ rect.p.x = 0;
+ rect.p.y = lnum;
+ rect.q.x = pdev->width;
+ rect.q.y = lnum+1;
+ memset(&params, 0, sizeof(params));
+ params.options = options;
+ params.x_offset = 0;
+ code = (*dev_proc(pdev, get_bits_rectangle))((gx_device *)pdev, &rect, &params,&unread);
+ if (code < 0)
+ break;
+#ifdef DEBUG_DUMP
+ if (row_proc)
+ (*row_proc)(pdev->width, params.data, pstream);
+#endif
+ }
+ return (code < 0 ? code : 0);
+}
+
+/* ------ Individual page printing routines ------ */
+
+/* Print a monobit page. */
+static int
+planm_print_page(gx_device_printer * pdev, FILE * pstream)
+{
+#ifdef DEBUG_PRINT
+ emprintf(pdev->memory, "planm_print_page\n");
+#endif
+ return plan_print_page_loop(pdev, 0, 1, pstream);
+}
+
+/* Print a gray-mapped page. */
+static int
+plang_print_page(gx_device_printer * pdev, FILE * pstream)
+{
+#ifdef DEBUG_PRINT
+ emprintf(pdev->memory, "plang_print_page\n");
+#endif
+ return plan_print_page_loop(pdev, 3, 1, pstream);
+}
+
+/* Print a color-mapped page. */
+static int
+plan_print_page(gx_device_printer * pdev, FILE * pstream)
+{
+#ifdef DEBUG_PRINT
+ emprintf(pdev->memory, "planc_print_page\n");
+#endif
+ return plan_print_page_loop(pdev, 3, 3, pstream);
+}
+
+/* Print a 1 bit CMYK page. */
+static int
+plank_print_page(gx_device_printer * pdev, FILE * pstream)
+{
+#ifdef DEBUG_PRINT
+ emprintf(pdev->memory, "plank_print_page\n");
+#endif
+ return plan_print_page_loop(pdev, 0, 4, pstream);
+}
+
+/* Print an 8bpc CMYK page. */
+static int
+planc_print_page(gx_device_printer * pdev, FILE * pstream)
+{
+#ifdef DEBUG_PRINT
+ emprintf(pdev->memory, "planc_print_page\n");
+#endif
+ return plan_print_page_loop(pdev, 3, 4, pstream);
+}
diff --git a/devices/gdevplib.c b/devices/gdevplib.c
new file mode 100644
index 000000000..4042696e5
--- /dev/null
+++ b/devices/gdevplib.c
@@ -0,0 +1,1021 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+/* PLanar Interlaced Banded device */
+#include "gdevprn.h"
+#include "gscdefs.h"
+#include "gscspace.h" /* For pnm_begin_typed_image(..) */
+#include "gxgetbit.h"
+#include "gxlum.h"
+#include "gxiparam.h" /* For pnm_begin_typed_image(..) */
+#include "gdevmpla.h"
+#include "gdevplnx.h"
+#include "gdevppla.h"
+#include "gdevplib.h" /* Band donor functions */
+#include "gdevmem.h"
+
+/* This file defines 5 different devices:
+ *
+ * plib 24 bit RGB (8 bits per channel)
+ * plibg 8 bit Grayscale
+ * plibm 1 bit Monochrome
+ * plibc 32 bit CMYK (8 bits per channel)
+ * plibk 4 bit CMYK (1 bit per channel)
+ *
+ * It is intended that this device will be built on top of a 'Band Donor'
+ * that will be responsible for allocating and pass us band buffers for us
+ * to fill, and to process them as it wishes on completion.
+ *
+ * If the band_donor functions are not thread safe, or modify the device, then
+ * the gdev_prn_bg_output_page should be changed to use gdev_prn_output_page.
+ *
+ * For debugging/QA purposes this file can be built with the following
+ * define enabled, and stub versions of these band donor functions will
+ * be included here.
+ */
+#define TESTING_WITH_NO_BAND_DONOR
+
+/* Define DEBUG_PRINT to enable some debugging printfs. */
+#undef DEBUG_PRINT
+
+/* Define DEBUG_DUMP to dump the data to the output stream. */
+#define DEBUG_DUMP
+
+/* Define HT_RAW_DUMP to store the output as a raw CMYK buffer with the
+ data size packed into the file name. Photoshop does not handle pam
+ cmyk properly so we resort to this for debugging */
+#define HT_RAW_DUMP
+
+/* Define SHORTSTOP_MEMCPY_ETC to enable braindead implementations of memcpy
+ * and memset etc in this file. This serves to help profiling on some
+ * systems, though it should be noted that our implementations here are NOT
+ * anywhere near as efficient as typical C libraries ones. */
+#undef SHORTSTOP_MEMCPY_ETC
+
+#ifdef SHORTSTOP_MEMCPY_ETC
+
+void *memset(void *s_, int c, size_t n)
+{
+ byte *s = (byte *)s_;
+ while (n--)
+ *s++ = (unsigned char)c;
+ return s;
+}
+
+void __aebi_memset8(void *dest, size_t n, int c)
+{
+ memset(dest, c,n);
+}
+void __aebi_memset4(void *dest, size_t n, int c)
+{
+ memset(dest, c,n);
+}
+void __aebi_memset(void *dest, size_t n, int c)
+{
+ memset(dest, c,n);
+}
+
+void __aebi_memclr8(void *dest, size_t n)
+{
+ memset(dest, 0,n);
+}
+void __aebi_memclr4(void *dest, size_t n)
+{
+ memset(dest, 0,n);
+}
+void __aebi_memclr(void *dest, size_t n)
+{
+ memset(dest, 0,n);
+}
+
+void *memcpy(void *s_, const void *t_, size_t n)
+{
+ byte *s = (byte *)s_;
+ const byte *t = (const byte *)t_;
+ while (n--)
+ *s++ = *t++;
+ return s;
+}
+
+void __aebi_memcpy8(void *dest, const void *src, size_t n)
+{
+ memcpy(dest, src,n);
+}
+void __aebi_memcpy4(void *dest, const void *src, size_t n)
+{
+ memcpy(dest, src,n);
+}
+void __aebi_memcpy(void *dest, const void *src, size_t n)
+{
+ memcpy(dest, src,n);
+}
+
+void *memmove(void *s_, const void *t_, size_t n)
+{
+ byte *s = (byte *)s_;
+ const byte *t = (const byte *)t_;
+
+ if (s < t) {
+ while (n--)
+ *s++ = *t++;
+ } else {
+ s += n;
+ t += n;
+ while (n--)
+ *--s = *--t;
+ }
+ return s;
+}
+
+void __aebi_memmove8(void *dest, const void *src, size_t n)
+{
+ memmove(dest, src,n);
+}
+void __aebi_memcmove4(void *dest, const void *src, size_t n)
+{
+ memmove(dest, src,n);
+}
+void __aebi_memmove(void *dest, const void *src, size_t n)
+{
+ memmove(dest, src,n);
+}
+
+#endif
+
+#ifdef TESTING_WITH_NO_BAND_DONOR
+
+#include <malloc_.h>
+
+static void *my_buffer;
+
+int gs_band_donor_init(void **opaque,
+ gs_memory_t *mem)
+{
+#ifdef DEBUG_PRINT
+ emprintf(mem, "gs_band_donor_init\n");
+#endif
+ *opaque = NULL;
+ return 0;
+}
+
+void *gs_band_donor_band_get(void *opaque,
+ uint uWidth,
+ uint uHeight,
+ uint uBitDepth,
+ uint uComponents,
+ uint uStride,
+ uint uBandHeight)
+{
+#ifdef DEBUG_PRINT
+ eprintf6("gs_band_donor_band_get[%dx%dx%dx%d (stride=%d bandHeight=%d)]\n",
+ uWidth, uHeight, uBitDepth, uComponents, uStride, uBandHeight);
+#endif
+ my_buffer = (void *)malloc(uStride * uComponents * uBandHeight);
+
+#ifdef DEBUG_PRINT
+ q = my_buffer;
+ for (y = uBandHeight; y > 0; y--) {
+ for (p = 0; p < uComponents; p++) {
+ memset(q, 0x10+p, uStride);
+ q += uStride;
+ }
+ }
+#endif
+ return my_buffer;
+}
+
+int gs_band_donor_band_full(void *opaque, uint nLines)
+{
+#ifdef DEBUG_PRINT
+ eprintf1("gs_band_donor_band_full[%d]\n", nLines);
+#endif
+ return 0;
+}
+
+int gs_band_donor_band_release(void *opaque)
+{
+#ifdef DEBUG_PRINT
+ eprintf("gs_band_donor_band_release\n");
+#endif
+ free(my_buffer);
+ my_buffer = NULL;
+ return 0;
+}
+
+void gs_band_donor_fin(void *opaque)
+{
+#ifdef DEBUG_PRINT
+ eprintf("gs_band_donor_fin\n");
+#endif
+}
+#endif
+
+/* Sanit requires us to work in bands of at least 200 lines */
+#define MINBANDHEIGHT 200
+
+/* Structure for plib devices, which extend the generic printer device. */
+
+struct gx_device_plib_s {
+ gx_device_common;
+ gx_prn_device_common;
+ /* Additional state for plib device */
+ void *opaque;
+};
+typedef struct gx_device_plib_s gx_device_plib;
+
+/* ------ The device descriptors ------ */
+
+/*
+ * Default X and Y resolution.
+ */
+#define X_DPI 600
+#define Y_DPI 600
+
+/* For all but mono, we need our own color mapping and alpha procedures. */
+static dev_proc_decode_color(plib_decode_color);
+static dev_proc_encode_color(plibg_encode_color);
+static dev_proc_decode_color(plibg_decode_color);
+static dev_proc_decode_color(plibc_decode_color);
+static dev_proc_encode_color(plibc_encode_color);
+static dev_proc_map_color_rgb(plibc_map_color_rgb);
+
+static dev_proc_open_device(plib_open);
+static dev_proc_close_device(plib_close);
+
+static dev_proc_get_params(plib_get_params);
+static dev_proc_put_params(plib_put_params);
+
+/* And of course we need our own print-page routines. */
+static dev_proc_print_page(plib_print_page);
+
+static int plib_print_page(gx_device_printer * pdev, FILE * pstream);
+static int plibm_print_page(gx_device_printer * pdev, FILE * pstream);
+static int plibg_print_page(gx_device_printer * pdev, FILE * pstream);
+static int plibc_print_page(gx_device_printer * pdev, FILE * pstream);
+static int plibk_print_page(gx_device_printer * pdev, FILE * pstream);
+
+/* The device procedures */
+
+/* See gdevprn.h for the template for the following. */
+#define pgpm_procs(p_color_rgb, p_encode_color, p_decode_color) {\
+ plib_open,\
+ NULL, /* get_initial_matrix */ \
+ NULL, /* sync output */ \
+ /* Since the print_page doesn't alter the device, this device can print in the background */\
+ gdev_prn_bg_output_page, \
+ plib_close,\
+ NULL, /* map_rgb_color */ \
+ p_color_rgb, /* map_color_rgb */ \
+ NULL, /* fill_rectangle */ \
+ NULL, /* tile_rectangle */ \
+ NULL, /* copy_mono */ \
+ NULL, /* copy_color */ \
+ NULL, /* draw_line */ \
+ NULL, /* get_bits */ \
+ gdev_prn_get_params, \
+ plib_put_params,\
+ NULL, /* map_cmyk_color */ \
+ NULL, /* get_xfont_procs */ \
+ NULL, /* get_xfont_device */ \
+ NULL, /* map_rgb_alpha_color */ \
+ gx_page_device_get_page_device, \
+ NULL, /* get_alpha_bits */\
+ NULL, /* copy_alpha */\
+ NULL, /* get_band */\
+ NULL, /* copy_rop */\
+ NULL, /* fill_path */\
+ NULL, /* stroke_path */\
+ NULL, /* fill_mask */\
+ NULL, /* fill_trapezoid */\
+ NULL, /* fill_parallelogram */\
+ NULL, /* fill_triangle */\
+ NULL, /* draw_thin_line */\
+ NULL, /* begin_image */\
+ NULL, /* image_data */\
+ NULL, /* end_image */\
+ NULL, /* strip_tile_rectangle */\
+ NULL, /* strip_copy_rop */\
+ NULL, /* get_clipping_box */\
+ NULL, /* begin_typed_image */\
+ NULL, /* get_bits_rectangle */\
+ NULL, /* map_color_rgb_alpha */\
+ NULL, /* create_compositor */\
+ NULL, /* get_hardware_params */\
+ NULL, /* text_begin */\
+ NULL, /* finish_copydevice */\
+ NULL, /* begin_transparency_group */\
+ NULL, /* end_transparency_group */\
+ NULL, /* begin_transparency_mask */\
+ NULL, /* end_transparency_mask */\
+ NULL, /* discard_transparency_layer */\
+ NULL, /* get_color_mapping_procs */\
+ NULL, /* get_color_comp_index */\
+ p_encode_color, /* encode_color */\
+ p_decode_color, /* decode_color */\
+ NULL, /* pattern_manage */\
+ NULL, /* fill_rectangle_hl_color */\
+ NULL, /* include_color_space */\
+ NULL, /* fill_linear_color_scanline */\
+ NULL, /* fill_linear_color_trapezoid */\
+ NULL, /* fill_linear_color_triangle */\
+ NULL, /* update spot */\
+ NULL, /* DevN params */\
+ NULL, /* fill page */\
+ NULL, /* push_transparency_state */\
+ NULL, /* pop_transparency_state */\
+ NULL, /* put_image */\
+ NULL /* dev_spec_op */\
+}
+
+static const gx_device_procs plibm_procs =
+ pgpm_procs(NULL, gdev_prn_map_rgb_color, gdev_prn_map_color_rgb);
+static const gx_device_procs plibg_procs =
+ pgpm_procs(NULL, plibg_encode_color, plibg_decode_color);
+static const gx_device_procs plib_procs =
+ pgpm_procs(NULL, gx_default_rgb_map_rgb_color, plib_decode_color);
+static const gx_device_procs plibc_procs =
+ pgpm_procs(plibc_map_color_rgb, plibc_encode_color, plibc_decode_color);
+static const gx_device_procs plibk_procs =
+ pgpm_procs(plibc_map_color_rgb, plibc_encode_color, plibc_decode_color);
+
+/* Macro for generating device descriptors. */
+/* Ideally we'd use something like:
+ * #define plib_prn_device(procs, dev_name, num_comp, depth, max_gray, max_rgb, print_page) \
+ * { prn_device_body(gx_device_plib, procs, dev_name,\
+ * DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS, X_DPI, Y_DPI,\
+ * 0, 0, 0, 0,\
+ * num_comp, depth, max_gray, max_rgb, max_gray + 1, max_rgb + 1,\
+ * print_page)\
+ * }
+ * But that doesn't let us override the band space params. So we have to do
+ * it the large way.
+ */
+#define plib_prn_device(procs, dev_name, num_comp, depth, max_gray, max_rgb, print_page) \
+{ std_device_full_body_type(gx_device_plib, &procs, dev_name, &st_device_printer,\
+ (int)((float)(DEFAULT_WIDTH_10THS) * (X_DPI) / 10 + 0.5),\
+ (int)((float)(DEFAULT_HEIGHT_10THS) * (Y_DPI) / 10 + 0.5),\
+ X_DPI, Y_DPI,\
+ num_comp, depth, max_gray, max_rgb, max_gray + 1, max_rgb + 1,\
+ (float)(0), (float)(0),\
+ (float)(0), (float)(0),\
+ (float)(0), (float)(0)\
+ ),\
+ { 0 }, /* std_procs */\
+ { 0 }, /* skip */\
+ { print_page,\
+ gx_default_print_page_copies,\
+ { gx_default_create_buf_device,\
+ gx_default_size_buf_device,\
+ gx_default_setup_buf_device,\
+ gx_default_destroy_buf_device\
+ },\
+ gdev_prn_default_get_space_params,\
+ gx_default_start_render_thread,\
+ gx_default_open_render_device,\
+ gx_default_close_render_device,\
+ gx_default_buffer_page\
+ },\
+ { 0 }, /* fname */\
+ 0/*false*/, /* OpenOutputFile */\
+ 0/*false*/, /* ReopenPerPage */\
+ 0/*false*/, /* page_uses_transparency */\
+ 0/*false*/, /* background_render */\
+ 0/*false*/, -1, /* Duplex[_set] */\
+ 0/*false*/, 0, 0, 0, /* file_is_new ... buf */\
+ 0, 0, 0, 0, 0/*false*/, 0, 0, /* buffer_memory ... clist_dis'_mask */\
+ 0, /* num_render_threads_requested */\
+ { 0 }, /* save_procs_while_delaying_erasepage */\
+ { 0 } /* ... orig_procs */}
+
+/* The device descriptors themselves */
+const gx_device_plib gs_plib_device =
+ plib_prn_device(plib_procs, "plib", 3, 24, 255, 255, plib_print_page);
+const gx_device_plib gs_plibg_device =
+ plib_prn_device(plibg_procs, "plibg", 1, 8, 255, 0, plibg_print_page);
+const gx_device_plib gs_plibm_device =
+ plib_prn_device(plibm_procs, "plibm", 1, 1, 1, 0, plibm_print_page);
+const gx_device_plib gs_plibk_device =
+ plib_prn_device(plibk_procs, "plibk", 4, 4, 1, 1, plibk_print_page);
+const gx_device_plib gs_plibc_device =
+ plib_prn_device(plibc_procs, "plibc", 4, 32, 255, 255, plibc_print_page);
+
+/* ------ Initialization ------ */
+
+/*
+ * We need to create custom memory buffer devices that just point into the
+ * bandBuffer we've got from the digicolor system.
+ */
+static byte *bandBufferBase = NULL;
+static int bandBufferStride = 0;
+
+#ifdef DEBUG_DUMP
+static int dump_w;
+static int dump_nc;
+static int dump_l2bits;
+
+static void dump_start(int w, int h, int num_comps, int log2bits,
+ FILE *dump_file)
+{
+ if ((num_comps == 3) && (log2bits == 3)) {
+ /* OK */
+ } else if ((num_comps == 1) && (log2bits == 0)) {
+ /* OK */
+ } else if ((num_comps == 1) && (log2bits == 3)) {
+ /* OK */
+ } else if ((num_comps == 4) && (log2bits == 0)) {
+ /* OK */
+ } else if ((num_comps == 4) && (log2bits == 3)) {
+ /* OK */
+ } else
+ return;
+ dump_nc = num_comps;
+ dump_l2bits = log2bits;
+ if (dump_file == NULL)
+ return;
+ if (dump_nc == 3)
+ fprintf(dump_file, "P6 %d %d 255\n", w, h);
+ else if (dump_nc == 4) {
+ if (log2bits == 0)
+ fprintf(dump_file, "P7\nWIDTH %d\nHEIGHT %d\nDEPTH 4\n"
+ "MAXVAL 255\nTUPLTYPE CMYK\nENDHDR\n", w, h);
+ else
+ fprintf(dump_file, "P7\nWIDTH %d\nHEIGHT %d\nDEPTH 4\n"
+ "MAXVAL 255\nTUPLTYPE CMYK\nENDHDR\n", w, h);
+ } else if (log2bits == 0)
+ fprintf(dump_file, "P4 %d %d\n", w, h);
+ else
+ fprintf(dump_file, "P5 %d %d 255\n", w, h);
+ dump_w = w;
+}
+
+static void dump_band(int y, FILE *dump_file)
+{
+ byte *r = bandBufferBase;
+ byte *g = r + bandBufferStride;
+ byte *b = g + bandBufferStride;
+ byte *k = b + bandBufferStride;
+
+ if (dump_file == NULL)
+ return;
+ if (dump_nc == 3) {
+ while (y--) {
+ int w = dump_w;
+ while (w--) {
+ fputc(*r++, dump_file);
+ fputc(*g++, dump_file);
+ fputc(*b++, dump_file);
+ }
+ r += bandBufferStride*3-dump_w;
+ g += bandBufferStride*3-dump_w;
+ b += bandBufferStride*3-dump_w;
+ }
+ } else if (dump_nc == 4) {
+ if (dump_l2bits == 0) {
+ while (y--) {
+ int w = dump_w;
+ while (w) {
+ byte C = *r++;
+ byte M = *g++;
+ byte Y = *b++;
+ byte K = *k++;
+ int s;
+ for (s=7; s>=0; s--) {
+ fputc(255*((C>>s)&1), dump_file);
+ fputc(255*((M>>s)&1), dump_file);
+ fputc(255*((Y>>s)&1), dump_file);
+ fputc(255*((K>>s)&1), dump_file);
+ w--;
+ if (w == 0) break;
+ }
+ }
+ r += bandBufferStride*4-((dump_w+7)>>3);
+ g += bandBufferStride*4-((dump_w+7)>>3);
+ b += bandBufferStride*4-((dump_w+7)>>3);
+ k += bandBufferStride*4-((dump_w+7)>>3);
+ }
+ } else {
+ while (y--) {
+ int w = dump_w;
+ while (w--) {
+ fputc(*r++, dump_file);
+ fputc(*g++, dump_file);
+ fputc(*b++, dump_file);
+ fputc(*k++, dump_file);
+ }
+ r += bandBufferStride*4-dump_w;
+ g += bandBufferStride*4-dump_w;
+ b += bandBufferStride*4-dump_w;
+ k += bandBufferStride*4-dump_w;
+ }
+ }
+ } else {
+ if (dump_l2bits == 0) {
+ while (y--) {
+ int w = (dump_w+7)>>3;
+ while (w--) {
+ fputc(*r++, dump_file);
+ }
+ r += bandBufferStride - ((dump_w+7)>>3);
+ }
+ } else {
+ while (y--) {
+ int w = dump_w;
+ while (w--) {
+ fputc(*r++, dump_file);
+ }
+ r += bandBufferStride - dump_w;
+ }
+ }
+ }
+}
+#endif
+
+int
+plib_put_params(gx_device * pdev, gs_param_list * plist)
+{
+ int ecode = 0;
+ int code;
+ gx_device_printer * const ppdev = (gx_device_printer *)pdev;
+
+ /* Assumed to be valid on entry - remember it */
+ int bandHeight = ppdev->space_params.band.BandHeight;
+
+ code = gdev_prn_put_params(pdev, plist);
+ /* Note that 0 means "default". This will encounter a future check in "open" */
+ if (ppdev->space_params.band.BandHeight != 0 &&
+ ppdev->space_params.band.BandHeight < MINBANDHEIGHT) {
+ emprintf2(pdev->memory, "BandHeight of %d not valid, BandHeight minimum is %d\n",
+ ppdev->space_params.band.BandHeight, MINBANDHEIGHT);
+ ecode = gs_error_rangecheck;
+ /* Restore to the previous (possibly default == 0) value */
+ ppdev->space_params.band.BandHeight = bandHeight;
+ }
+ if (ecode >= 0)
+ ecode = code;
+ return ecode;
+}
+
+/*
+ * Set up the scan line pointers of a memory device.
+ * See gxdevmem.h for the detailed specification.
+ * Sets or uses line_ptrs, base, raster; uses width, color_info.depth,
+ * num_planes, plane_depths, plane_depth.
+ */
+static int
+set_line_ptrs(gx_device_memory * mdev, byte * base, int raster,
+ byte **line_ptrs, int setup_height)
+{
+ int num_planes = mdev->color_info.num_components;
+ gx_render_plane_t plane1;
+ const gx_render_plane_t *planes;
+ int pi;
+
+ if (num_planes) {
+ if (base && !mdev->plane_depth)
+ return_error(gs_error_rangecheck);
+ planes = mdev->planes;
+ } else {
+ planes = &plane1;
+ plane1.depth = mdev->color_info.depth;
+ num_planes = 1;
+ }
+ if (line_ptrs)
+ mdev->line_ptrs = line_ptrs;
+ for (pi = 0; pi < num_planes; ++pi) {
+ byte **pend = line_ptrs + setup_height;
+ byte *scan_line = base;
+
+ while (line_ptrs < pend) {
+ *line_ptrs++ = scan_line;
+ scan_line += raster * num_planes;
+ }
+ base += raster;
+ }
+
+ return 0;
+}
+
+static int
+plib_setup_buf_device(gx_device *bdev, byte *buffer, int bytes_per_line,
+ byte **line_ptrs, int y, int setup_height,
+ int full_height)
+{
+ gx_device_memory *mdev = (gx_device_memory *)bdev;
+ int code;
+
+ /* buffer is the buffer used by clist writing. We could use that as the
+ * page buffer, but we'd rather use the buffer given to us by the
+ * digicolor code. b */
+
+ if (line_ptrs == NULL) {
+ /* Free any existing line pointers array */
+ if (mdev->line_ptrs != NULL)
+ gs_free_object(mdev->line_pointer_memory, mdev->line_ptrs,
+ "mem_close");
+ /*
+ * Allocate line pointers now; free them when we close the device.
+ * Note that for multi-planar devices, we have to allocate using
+ * full_height rather than setup_height.
+ */
+ line_ptrs = (byte **)
+ gs_alloc_byte_array(mdev->memory,
+ (mdev->is_planar ?
+ full_height * mdev->color_info.num_components :
+ setup_height),
+ sizeof(byte *), "setup_buf_device");
+ if (line_ptrs == 0)
+ return_error(gs_error_VMerror);
+ mdev->line_pointer_memory = mdev->memory;
+ mdev->foreign_line_pointers = false;
+ mdev->line_ptrs = line_ptrs;
+ mdev->raster = bandBufferStride * (mdev->is_planar ? mdev->color_info.num_components : 1);
+ }
+ mdev->height = full_height;
+ code = set_line_ptrs(mdev,
+ bandBufferBase + bandBufferStride*(mdev->is_planar ? mdev->color_info.num_components : 1)*y,
+ bandBufferStride,
+ line_ptrs,
+ setup_height);
+ mdev->height = setup_height;
+ bdev->height = setup_height; /* do here in case mdev == bdev */
+ return code;
+}
+
+static int
+plib_get_bits_rectangle_mem(gx_device *pdev, const gs_int_rect *prect,
+ gs_get_bits_params_t *params, gs_int_rect **pprect)
+{
+ gx_device_memory *mdev = (gx_device_memory *)pdev;
+ int x = prect->p.x, w = prect->q.x - x, y = prect->p.y, h = prect->q.y - y;
+ /* First off, see if we can satisfy get_bits_rectangle with just returning
+ * pointers to the existing data. */
+ {
+ gs_get_bits_params_t copy_params;
+ byte **base = &scan_line_base(mdev, y);
+ int code;
+
+ copy_params.options =
+ GB_COLORS_NATIVE | GB_PACKING_PLANAR | GB_ALPHA_NONE |
+ (mdev->raster ==
+ bitmap_raster(mdev->width * mdev->color_info.depth) ?
+ GB_RASTER_STANDARD : GB_RASTER_SPECIFIED);
+ copy_params.raster = mdev->raster;
+ code = gx_get_bits_return_pointer(pdev, x, h, params,
+ &copy_params, base);
+ if (code >= 0)
+ return code;
+ }
+ return mem_get_bits_rectangle(pdev, prect, params, pprect);
+}
+
+static int
+plib_create_buf_device(gx_device **pbdev, gx_device *target, int y,
+ const gx_render_plane_t *render_plane, gs_memory_t *mem,
+ gx_color_usage_t *color_usage)
+{
+ int code = gdev_prn_create_buf_planar(pbdev, target, y, render_plane,
+ mem, color_usage);
+ if (code < 0)
+ return code;
+ if ((*pbdev)->procs.get_bits_rectangle == mem_get_bits_rectangle)
+ (*pbdev)->procs.get_bits_rectangle = plib_get_bits_rectangle_mem;
+ return 0;
+}
+
+static int
+plib_size_buf_device(gx_device_buf_space_t *space, gx_device *target,
+ const gx_render_plane_t *render_plane,
+ int height, bool for_band)
+{
+ return gdev_prn_size_buf_planar(space, target, render_plane,
+ height, for_band);
+}
+
+/*
+ * Define a special open procedure that changes create_buf_device to use
+ * a planar device.
+ */
+static int
+plib_open(gx_device * pdev)
+{
+ gx_device_plib * const bdev = (gx_device_plib *)pdev;
+ gx_device_printer * const ppdev = (gx_device_printer *)pdev;
+ int code;
+
+#ifdef DEBUG_PRINT
+ emprintf(pdev->memory, "plib_open\n");
+#endif
+ bdev->printer_procs.buf_procs.create_buf_device = plib_create_buf_device;
+ bdev->printer_procs.buf_procs.setup_buf_device = plib_setup_buf_device;
+ bdev->printer_procs.buf_procs.size_buf_device = plib_size_buf_device;
+ pdev->is_planar = 1;
+
+ /* You might expect us to call gdev_prn_open_planar rather than
+ * gdev_prn_open, but if we do that, it overwrites the 2 function
+ * pointers we've just overwritten! */
+ code = gdev_prn_open(pdev);
+ if (code < 0)
+ return code;
+ if (ppdev->space_params.band.BandHeight < MINBANDHEIGHT) {
+ emprintf2(pdev->memory, "BandHeight of %d not valid, BandHeight minimum is %d\n",
+ ((gx_device_printer *)pdev)->space_params.band.BandHeight,
+ MINBANDHEIGHT);
+
+ return_error(gs_error_rangecheck);
+ }
+ pdev->color_info.separable_and_linear = GX_CINFO_SEP_LIN;
+ set_linear_color_bits_mask_shift(pdev);
+
+ /* Start the actual job. */
+#ifdef DEBUG_PRINT
+ emprintf(pdev->memory, "calling job_begin\n");
+#endif
+ code = gs_band_donor_init(&bdev->opaque, pdev->memory);
+#ifdef DEBUG_PRINT
+ emprintf(pdev->memory, "called\n");
+#endif
+
+ return code;
+}
+
+static int
+plib_close(gx_device *pdev)
+{
+ gx_device_plib *pldev = (gx_device_plib *)pdev;
+
+#ifdef DEBUG_PRINT
+ emprintf(pdev->memory, "plib_close\n");
+#endif
+ gs_band_donor_fin(pldev->opaque);
+ pldev->opaque = NULL;
+
+ return gdev_prn_close(pdev);
+}
+
+/* ------ Color mapping routines ------ */
+
+/* Map an RGB color to a gray value. */
+static gx_color_index
+plibg_encode_color(gx_device * pdev, const gx_color_value cv[])
+{ /* We round the value rather than truncating it. */
+ gx_color_value gray;
+ gx_color_value r, g, b;
+
+ r = cv[0]; g = cv[0]; b = cv[0];
+ gray = ((r * (ulong) lum_red_weight) +
+ (g * (ulong) lum_green_weight) +
+ (b * (ulong) lum_blue_weight) +
+ (lum_all_weights / 2)) / lum_all_weights
+ * pdev->color_info.max_gray / gx_max_color_value;
+
+ return gray;
+}
+
+/* Map a gray value back to an RGB color. */
+static int
+plibg_decode_color(gx_device * dev, gx_color_index color,
+ gx_color_value prgb[3])
+{
+ gx_color_value gray =
+ color * gx_max_color_value / dev->color_info.max_gray;
+
+ prgb[0] = gray;
+ prgb[1] = gray;
+ prgb[2] = gray;
+ return 0;
+}
+
+/* Map an rgb color tuple back to an RGB color. */
+static int
+plib_decode_color(gx_device * dev, gx_color_index color,
+ gx_color_value prgb[3])
+{
+ uint bitspercolor = dev->color_info.depth / 3;
+ uint colormask = (1 << bitspercolor) - 1;
+ uint max_rgb = dev->color_info.max_color;
+
+ prgb[0] = ((color >> (bitspercolor * 2)) & colormask) *
+ (ulong) gx_max_color_value / max_rgb;
+ prgb[1] = ((color >> bitspercolor) & colormask) *
+ (ulong) gx_max_color_value / max_rgb;
+ prgb[2] = (color & colormask) *
+ (ulong) gx_max_color_value / max_rgb;
+ return 0;
+}
+
+/* Map a cmyk color tuple back to CMYK colorants. */
+static int
+plibc_decode_color(gx_device * dev, gx_color_index color,
+ gx_color_value prgb[4])
+{
+ uint bitspercolor = dev->color_info.depth / 4;
+ uint colormask = (1 << bitspercolor) - 1;
+ uint max_cmyk = dev->color_info.max_color;
+ uint c, m, y, k;
+
+#define cvalue(c) ((gx_color_value)((ulong)(c) * gx_max_color_value / colormask))
+
+ k = color & colormask;
+ color >>= bitspercolor;
+ y = color & colormask;
+ color >>= bitspercolor;
+ m = color & colormask;
+ c = color >> bitspercolor;
+ prgb[0] = cvalue(c);
+ prgb[1] = cvalue(m);
+ prgb[2] = cvalue(y);
+ prgb[3] = cvalue(k);
+ return 0;
+}
+
+/* Map CMYK to color. */
+static gx_color_index
+plibc_encode_color(gx_device * dev, const gx_color_value cv[])
+{
+ int bpc = dev->color_info.depth / 4;
+ gx_color_index color;
+ COLROUND_VARS;
+
+ COLROUND_SETUP(bpc);
+ color = ((((((COLROUND_ROUND(cv[0]) << bpc) +
+ COLROUND_ROUND(cv[1])) << bpc) +
+ COLROUND_ROUND(cv[2])) << bpc) +
+ COLROUND_ROUND(cv[3]));
+
+ /* The bitcmyk device does this:
+ * return (color == gx_no_color_index ? color ^ 1 : color);
+ * But I don't understand why.
+ */
+ return color;
+}
+
+/* Map a cmyk color back to an rgb tuple. */
+static int
+plibc_map_color_rgb(gx_device * dev, gx_color_index color,
+ gx_color_value prgb[3])
+{
+ uint bitspercolor = dev->color_info.depth / 4;
+ uint colormask = (1 << bitspercolor) - 1;
+ uint max_cmyk = dev->color_info.max_color;
+ uint c, m, y, k;
+
+#define cvalue(c) ((gx_color_value)((ulong)(c) * gx_max_color_value / colormask))
+
+ k = color & colormask;
+ color >>= bitspercolor;
+ y = color & colormask;
+ color >>= bitspercolor;
+ m = color & colormask;
+ c = color >> bitspercolor;
+ k = colormask - k;
+ prgb[0] = cvalue((colormask - c) * k / colormask);
+ prgb[1] = cvalue((colormask - m) * k / colormask);
+ prgb[2] = cvalue((colormask - y) * k / colormask);
+ return 0;
+}
+
+/* ------ Internal routines ------ */
+
+/* Print a page using a given row printing routine. */
+static int
+plib_print_page_loop(gx_device_printer * pdev, int log2bits, int numComps,
+ FILE *pstream)
+{
+ gx_device_plib *pldev = (gx_device_plib *)pdev;
+ int lnum;
+ int code = 0;
+ byte *buffer;
+ int stride = bitmap_raster(pdev->width * (1<<log2bits));
+ int bandHeight = pdev->space_params.band.BandHeight;
+
+#ifdef DEBUG_PRINT
+ emprintf(pdev->memory, "Calling page_begin\n");
+#endif
+ buffer = gs_band_donor_band_get(pldev->opaque,
+ pdev->width,
+ pdev->height,
+ 1<<log2bits,
+ numComps,
+ stride,
+ bandHeight);
+#ifdef DEBUG_PRINT
+ emprintf1(pdev->memory, "Called page_begin %x\n", buffer);
+#endif
+ if (buffer == NULL)
+ return_error(gs_error_VMerror);
+
+ /* Write these into the globals here so the setup_buf_device code can
+ * find it later. Nasty. */
+ bandBufferBase = buffer;
+ bandBufferStride = stride;
+
+#ifdef DEBUG_DUMP
+ dump_start(pdev->width, pdev->height, numComps, log2bits, pstream);
+#endif
+ for (lnum = 0; lnum < pdev->height; lnum += bandHeight) {
+ gs_int_rect *unread, rect;
+ gs_get_bits_params_t params;
+
+ rect.p.x = 0;
+ rect.p.y = lnum;
+ rect.q.x = pdev->width;
+ rect.q.y = lnum+bandHeight;
+ if (rect.q.y > pdev->height)
+ rect.q.y = pdev->height;
+ memset(&params, 0, sizeof(params));
+ params.options = GB_ALIGN_ANY |
+ GB_RETURN_POINTER |
+ GB_OFFSET_0 |
+ GB_RASTER_STANDARD |
+ GB_PACKING_PLANAR |
+ GB_COLORS_NATIVE |
+ GB_ALPHA_NONE;
+ params.x_offset = 0;
+ code = (*dev_proc(pdev, get_bits_rectangle))((gx_device *)pdev, &rect, &params,&unread);
+ if (code < 0)
+ break;
+#ifdef DEBUG_DUMP
+ dump_band(rect.q.y-rect.p.y, pstream);
+#endif
+#ifdef DEBUG_PRINT
+ emprintf3(pdev->memory, "Calling band_full (%d->%d) of %d\n",
+ rect.p.y, rect.q.y, pdev->height);
+#endif
+ gs_band_donor_band_full(pldev->opaque, rect.q.y-rect.p.y);
+#ifdef DEBUG_PRINT
+ emprintf(pdev->memory, "Called band_full\n");
+#endif
+ }
+#ifdef DEBUG_PRINT
+ emprintf(pdev->memory, "Calling band_release\n");
+#endif
+ gs_band_donor_band_release(pldev->opaque);
+#ifdef DEBUG_PRINT
+ emprintf(pdev->memory, "Called band_release\n");
+#endif
+ return (code < 0 ? code : 0);
+}
+
+/* ------ Individual page printing routines ------ */
+
+/* Print a monobit page. */
+static int
+plibm_print_page(gx_device_printer * pdev, FILE * pstream)
+{
+#ifdef DEBUG_PRINT
+ emprintf(pdev->memory, "plibm_print_page\n");
+#endif
+ return plib_print_page_loop(pdev, 0, 1, pstream);
+}
+
+/* Print a gray-mapped page. */
+static int
+plibg_print_page(gx_device_printer * pdev, FILE * pstream)
+{
+#ifdef DEBUG_PRINT
+ emprintf(pdev->memory, "plibg_print_page\n");
+#endif
+ return plib_print_page_loop(pdev, 3, 1, pstream);
+}
+
+/* Print a color-mapped page. */
+static int
+plib_print_page(gx_device_printer * pdev, FILE * pstream)
+{
+#ifdef DEBUG_PRINT
+ emprintf(pdev->memory, "plibc_print_page\n");
+#endif
+ return plib_print_page_loop(pdev, 3, 3, pstream);
+}
+
+/* Print a 1 bit CMYK page. */
+static int
+plibk_print_page(gx_device_printer * pdev, FILE * pstream)
+{
+#ifdef DEBUG_PRINT
+ emprintf(pdev->memory, "plibk_print_page\n");
+#endif
+ return plib_print_page_loop(pdev, 0, 4, pstream);
+}
+
+/* Print an 8bpc CMYK page. */
+static int
+plibc_print_page(gx_device_printer * pdev, FILE * pstream)
+{
+#ifdef DEBUG_PRINT
+ emprintf(pdev->memory, "plibc_print_page\n");
+#endif
+ return plib_print_page_loop(pdev, 3, 4, pstream);
+}
diff --git a/devices/gdevplib.h b/devices/gdevplib.h
new file mode 100644
index 000000000..936b3ca37
--- /dev/null
+++ b/devices/gdevplib.h
@@ -0,0 +1,61 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Interface functions that need to be provided for the plib device. */
+
+#ifndef gdevplib_INCLUDED
+# define gdevplib_INCLUDED
+
+/* Function called at the start of a job.
+ *
+ * Pass in a memory pointer, get back an opaque value to parrot in future
+ * calls. Standard gs error return code.
+ */
+int gs_band_donor_init(void **opaque, /* Opaque value */
+ gs_memory_t *mem); /* Memory pointer */
+
+/* Function called at the start of each page to get a band buffer.
+ *
+ * Returns a band buffer to be filled. NULL indicates error.
+ */
+void *gs_band_donor_band_get(void *opaque, /* Value returned at init */
+ uint uWidth, /* Page Width (pixels) */
+ uint uHeight, /* Page Height (pixels) */
+ uint uBitDepth, /* Num bits per component */
+ uint uComponents, /* Number of components */
+ uint uStride, /* Line stride (bytes) */
+ uint uBandHeight); /* Band height (pixels) */
+
+/* Called repeatedly when the band buffer is filled.
+ *
+ * Buffer is filled with nLines*uComponents*uStride*uComponents of data.
+ * (First component first scanline padded to uStride bytes, then next
+ * component first scanline (similarly padded), until all the components have
+ * been sent, then first component, second scanline etc...)
+ * Standard gs error return code.
+ */
+int gs_band_donor_band_full(void *opaque, /* Value returned at init */
+ uint nLines); /* How many lines are filled */
+
+/* Called at the end of each page to release the band buffer.
+ */
+int gs_band_donor_band_release(void *opaque); /* Value returned at init */
+
+/* Called at the end of the job to allow the band donor to release
+ * any resources. */
+void gs_band_donor_fin(void *opaque);
+
+#endif /* gdevplib_INCLUDED */
diff --git a/devices/gdevpm.h b/devices/gdevpm.h
new file mode 100644
index 000000000..f67dd7e58
--- /dev/null
+++ b/devices/gdevpm.h
@@ -0,0 +1,37 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Defines common to gdevpm.c, gspmdrv.c and PM GSview */
+
+#ifndef gdevpm_INCLUDED
+# define gdevpm_INCLUDED
+
+#define SHARED_NAME "\\SHAREMEM\\%s"
+#define SYNC_NAME "\\SEM32\\SYNC_%s"
+#define NEXT_NAME "\\SEM32\\NEXT_%s"
+#define MUTEX_NAME "\\SEM32\\MUTEX_%s"
+#define QUEUE_NAME "\\QUEUES\\%s"
+
+#define GS_UPDATING 1
+#define GS_SYNC 2
+#define GS_PAGE 3
+#define GS_CLOSE 4
+#define GS_ERROR 5
+#define GS_PALCHANGE 6
+#define GS_BEGIN 7
+#define GS_END 8
+
+#endif /* gdevpm_INCLUDED */
diff --git a/devices/gdevpng.c b/devices/gdevpng.c
new file mode 100644
index 000000000..4267d4522
--- /dev/null
+++ b/devices/gdevpng.c
@@ -0,0 +1,1016 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* PNG (Portable Network Graphics) Format. Pronounced "ping". */
+/* lpd 1999-09-24: changes PNG_NO_STDIO to PNG_NO_CONSOLE_IO for libpng
+ versions 1.0.3 and later. */
+/* lpd 1999-07-01: replaced remaining uses of gs_malloc and gs_free with
+ gs_alloc_bytes and gs_free_object. */
+/* lpd 1999-03-08: changed png.h to png_.h to allow compiling with only
+ headers in /usr/include, no source code. */
+/* lpd 1997-07-20: changed from using gs_malloc/png_xxx_int to png_create_xxx
+ * for allocating structures, and from gs_free to png_write_destroy for
+ * freeing them. */
+/* lpd 1997-5-7: added PNG_LIBPNG_VER conditional for operand types of
+ * dummy png_push_fill_buffer. */
+/* lpd 1997-4-13: Added PNG_NO_STDIO to remove library access to stderr. */
+/* lpd 1997-3-14: Added resolution (pHYs) to output. */
+/* lpd 1996-6-24: Added #ifdef for compatibility with old libpng versions. */
+/* lpd 1996-6-11: Edited to remove unnecessary color mapping code. */
+/* lpd (L. Peter Deutsch) 1996-4-7: Modified for libpng 0.88. */
+/* Original version by Russell Lang 1995-07-04 */
+
+#include "gdevprn.h"
+#include "gdevmem.h"
+#include "gdevpccm.h"
+#include "gscdefs.h"
+#include "gxdownscale.h"
+
+/*
+ * libpng versions 1.0.3 and later allow disabling access to the stdxxx
+ * files while retaining support for FILE * I/O.
+ */
+#define PNG_NO_CONSOLE_IO
+/*
+ * Earlier libpng versions require disabling FILE * I/O altogether.
+ * This produces a compiler warning about no prototype for png_init_io.
+ * The right thing will happen at link time, since the library itself
+ * is compiled with stdio support. Unfortunately, we can't do this
+ * conditionally depending on PNG_LIBPNG_VER, because this is defined
+ * in png.h.
+ */
+/*#define PNG_NO_STDIO*/
+#include "png_.h"
+
+/* ------ The device descriptors ------ */
+
+/*
+ * Default X and Y resolution.
+ */
+#define X_DPI 72
+#define Y_DPI 72
+
+static dev_proc_print_page(png_print_page);
+static dev_proc_print_page(png_print_page_monod);
+static dev_proc_open_device(pngalpha_open);
+static dev_proc_encode_color(pngalpha_encode_color);
+static dev_proc_decode_color(pngalpha_decode_color);
+static dev_proc_copy_alpha(pngalpha_copy_alpha);
+static dev_proc_fillpage(pngalpha_fillpage);
+static dev_proc_put_image(pngalpha_put_image);
+static dev_proc_get_params(pngalpha_get_params);
+static dev_proc_put_params(pngalpha_put_params);
+static dev_proc_create_buf_device(pngalpha_create_buf_device);
+static dev_proc_get_params(png_get_params_downscale);
+static dev_proc_put_params(png_put_params_downscale);
+static dev_proc_get_params(png_get_params_downscale_mfs);
+static dev_proc_put_params(png_put_params_downscale_mfs);
+
+typedef struct gx_device_png_s gx_device_png;
+struct gx_device_png_s {
+ gx_device_common;
+ gx_prn_device_common;
+ int downscale_factor;
+ int min_feature_size;
+};
+
+/* Monochrome. */
+
+const gx_device_png gs_pngmono_device =
+{ /* The print_page proc is compatible with allowing bg printing */
+ prn_device_body(gx_device_png, prn_bg_procs, "pngmono",
+ DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
+ X_DPI, Y_DPI,
+ 0, 0, 0, 0, /* margins */
+ 1, 1, 1, 1, 2, 2, png_print_page),
+ 1, /* downscale_factor */
+ 0 /* min_feature_size */
+};
+
+
+/* 4-bit planar (EGA/VGA-style) color. */
+
+/* Since the print_page doesn't alter the device, this device can print in the background */
+static const gx_device_procs png16_procs =
+prn_color_procs(gdev_prn_open, gdev_prn_bg_output_page, gdev_prn_close,
+ pc_4bit_map_rgb_color, pc_4bit_map_color_rgb);
+const gx_device_png gs_png16_device = {
+ prn_device_body(gx_device_png, png16_procs, "png16",
+ DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
+ X_DPI, Y_DPI,
+ 0, 0, 0, 0, /* margins */
+ 3, 4, 1, 1, 2, 2, png_print_page),
+ 1, /* downscale_factor */
+ 0 /* min_feature_size */
+};
+
+/* 8-bit (SuperVGA-style) color. */
+/* (Uses a fixed palette of 3,3,2 bits.) */
+
+/* Since the print_page doesn't alter the device, this device can print in the background */
+static const gx_device_procs png256_procs =
+prn_color_procs(gdev_prn_open, gdev_prn_bg_output_page, gdev_prn_close,
+ pc_8bit_map_rgb_color, pc_8bit_map_color_rgb);
+const gx_device_png gs_png256_device = {
+ prn_device_body(gx_device_png, png256_procs, "png256",
+ DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
+ X_DPI, Y_DPI,
+ 0, 0, 0, 0, /* margins */
+ 3, 8, 5, 5, 6, 6, png_print_page),
+ 1, /* downscale_factor */
+ 0 /* min_feature_size */
+};
+
+/* 8-bit gray */
+
+/* Since the print_page doesn't alter the device, this device can print in the background */
+static const gx_device_procs pnggray_procs =
+prn_color_params_procs(gdev_prn_open, gdev_prn_bg_output_page, gdev_prn_close,
+ gx_default_gray_map_rgb_color,
+ gx_default_gray_map_color_rgb,
+ png_get_params_downscale, png_put_params_downscale);
+const gx_device_png gs_pnggray_device =
+{prn_device_body(gx_device_png, pnggray_procs, "pnggray",
+ DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
+ X_DPI, Y_DPI,
+ 0, 0, 0, 0, /* margins */
+ 1, 8, 255, 0, 256, 0, png_print_page),
+ 1, /* downscale_factor */
+ 0 /* min_feature_size */
+};
+
+/* Monochrome (with error diffusion) */
+
+/* Since the print_page doesn't alter the device, this device can print in the background */
+static const gx_device_procs pngmonod_procs =
+prn_color_params_procs(gdev_prn_open, gdev_prn_bg_output_page, gdev_prn_close,
+ gx_default_gray_map_rgb_color,
+ gx_default_gray_map_color_rgb,
+ png_get_params_downscale_mfs,
+ png_put_params_downscale_mfs);
+const gx_device_png gs_pngmonod_device =
+{prn_device_body(gx_device_png, pngmonod_procs, "pngmonod",
+ DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
+ X_DPI, Y_DPI,
+ 0, 0, 0, 0, /* margins */
+ 1, 8, 255, 0, 256, 0, png_print_page_monod),
+ 1, /* downscale_factor */
+ 0 /* min_feature_size */
+};
+
+/* 24-bit color. */
+
+/* Since the print_page doesn't alter the device, this device can print in the background */
+static const gx_device_procs png16m_procs =
+prn_color_params_procs(gdev_prn_open, gdev_prn_bg_output_page, gdev_prn_close,
+ gx_default_rgb_map_rgb_color,
+ gx_default_rgb_map_color_rgb,
+ png_get_params_downscale, png_put_params_downscale);
+const gx_device_png gs_png16m_device =
+{prn_device_body(gx_device_png, png16m_procs, "png16m",
+ DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
+ X_DPI, Y_DPI,
+ 0, 0, 0, 0, /* margins */
+ 3, 24, 255, 255, 256, 256, png_print_page),
+ 1, /* downscale_factor */
+ 0 /* min_feature_size */
+};
+
+/* 48 bit color. */
+
+/* Since the print_page doesn't alter the device, this device can print in the background */
+static const gx_device_procs png48_procs =
+prn_color_procs(gdev_prn_open, gdev_prn_bg_output_page, gdev_prn_close,
+ gx_default_rgb_map_rgb_color, gx_default_rgb_map_color_rgb);
+const gx_device_png gs_png48_device =
+{prn_device_body(gx_device_png, png48_procs, "png48",
+ DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
+ X_DPI, Y_DPI,
+ 0, 0, 0, 0, /* margins */
+ 3, 48, 0, 65535, 1, 65536, png_print_page),
+ 1, /* downscale_factor */
+ 0 /* min_feature_size */
+};
+
+/* 32-bit RGBA */
+/* pngalpha device is 32-bit RGBA, with the alpha channel
+ * indicating pixel coverage, not true transparency.
+ * Anti-aliasing is enabled by default.
+ * An erasepage will erase to transparent, not white.
+ * It is intended to be used for creating web graphics with
+ * a transparent background.
+ */
+typedef struct gx_device_pngalpha_s gx_device_pngalpha;
+struct gx_device_pngalpha_s {
+ gx_device_common;
+ gx_prn_device_common;
+ int downscale_factor;
+ int min_feature_size;
+ int background;
+};
+static const gx_device_procs pngalpha_procs =
+{
+ pngalpha_open,
+ NULL, /* get_initial_matrix */
+ NULL, /* sync_output */
+ /* Since the print_page doesn't alter the device, this device can print in the background */
+ gdev_prn_bg_output_page,
+ gdev_prn_close,
+ pngalpha_encode_color, /* map_rgb_color */
+ pngalpha_decode_color, /* map_color_rgb */
+ NULL, /* fill_rectangle */
+ NULL, /* tile_rectangle */
+ NULL, /* copy_mono */
+ NULL, /* copy_color */
+ NULL, /* draw_line */
+ NULL, /* get_bits */
+ pngalpha_get_params,
+ pngalpha_put_params,
+ NULL, /* map_cmyk_color */
+ NULL, /* get_xfont_procs */
+ NULL, /* get_xfont_device */
+ NULL, /* map_rgb_alpha_color */
+ gx_page_device_get_page_device,
+ NULL, /* get_alpha_bits */
+ pngalpha_copy_alpha,
+ NULL, /* get_band */
+ NULL, /* copy_rop */
+ NULL, /* fill_path */
+ NULL, /* stroke_path */
+ NULL, /* fill_mask */
+ NULL, /* fill_trapezoid */
+ NULL, /* fill_parallelogram */
+ NULL, /* fill_triangle */
+ NULL, /* draw_thin_line */
+ NULL, /* begin_image */
+ NULL, /* image_data */
+ NULL, /* end_image */
+ NULL, /* strip_tile_rectangle */
+ NULL, /* strip_copy_rop, */
+ NULL, /* get_clipping_box */
+ NULL, /* begin_typed_image */
+ NULL, /* get_bits_rectangle */
+ NULL, /* map_color_rgb_alpha */
+ NULL, /* create_compositor */
+ NULL, /* get_hardware_params */
+ NULL, /* text_begin */
+ NULL, /* finish_copydevice */
+ NULL, /* begin_transparency_group */
+ NULL, /* end_transparency_group */
+ NULL, /* begin_transparency_mask */
+ NULL, /* end_transparency_mask */
+ NULL, /* discard_transparency_layer */
+ gx_default_DevRGB_get_color_mapping_procs,
+ gx_default_DevRGB_get_color_comp_index,
+ pngalpha_encode_color,
+ pngalpha_decode_color,
+ NULL, /* pattern_manage */
+ NULL, /* fill_rectangle_hl_color */
+ NULL, /* include_color_space */
+ NULL, /* fill_linear_color_scanline */
+ NULL, /* fill_linear_color_trapezoid */
+ NULL, /* fill_linear_color_triangle */
+ NULL, /* update_spot_equivalent_colors */
+ NULL, /* ret_devn_params */
+ pngalpha_fillpage,
+ NULL, /* push_transparency_state */
+ NULL, /* pop_transparency_state */
+ pngalpha_put_image
+};
+
+const gx_device_pngalpha gs_pngalpha_device = {
+ std_device_part1_(gx_device_pngalpha, &pngalpha_procs, "pngalpha",
+ &st_device_printer, open_init_closed),
+ /* color_info */
+ {3 /* max components */,
+ 3 /* number components */,
+ GX_CINFO_POLARITY_ADDITIVE /* polarity */,
+ 32 /* depth */,
+ -1 /* gray index */,
+ 255 /* max gray */,
+ 255 /* max color */,
+ 256 /* dither grays */,
+ 256 /* dither colors */,
+ { 4, 4 } /* antialias info text, graphics */,
+ GX_CINFO_SEP_LIN_NONE /* separable_and_linear */,
+ { 0 } /* component shift */,
+ { 0 } /* component bits */,
+ { 0 } /* component mask */,
+ "DeviceRGB" /* process color name */,
+ GX_CINFO_OPMODE_UNKNOWN /* opmode */,
+ 0 /* process_cmps */,
+ 0 /* icc_locations */
+ },
+ std_device_part2_(
+ (int)((float)(DEFAULT_WIDTH_10THS) * (X_DPI) / 10 + 0.5),
+ (int)((float)(DEFAULT_HEIGHT_10THS) * (Y_DPI) / 10 + 0.5),
+ X_DPI, Y_DPI),
+ offset_margin_values(0, 0, 0, 0, 0, 0),
+ std_device_part3_(),
+ prn_device_body_rest_(png_print_page),
+ 1, /* downscale_factor */
+ 0, /* min_feature_size */
+ 0xffffff /* white background */
+};
+
+/* ------ Private definitions ------ */
+
+static int
+png_get_params_downscale(gx_device * dev, gs_param_list * plist)
+{
+ gx_device_png *pdev = (gx_device_png *)dev;
+ int code, ecode;
+
+ ecode = 0;
+ if (pdev->downscale_factor < 1)
+ pdev->downscale_factor = 1;
+ if ((code = param_write_int(plist, "DownScaleFactor", &pdev->downscale_factor)) < 0)
+ ecode = code;
+
+ code = gdev_prn_get_params(dev, plist);
+ if (code < 0)
+ ecode = code;
+
+ return ecode;
+}
+
+static int
+png_put_params_downscale(gx_device *dev, gs_param_list *plist)
+{
+ gx_device_png *pdev = (gx_device_png *)dev;
+ int code, ecode;
+ int dsf = pdev->downscale_factor;
+ const char *param_name;
+
+ ecode = 0;
+ switch (code = param_read_int(plist, (param_name = "DownScaleFactor"), &dsf)) {
+ case 0:
+ if (dsf >= 1)
+ break;
+ code = gs_error_rangecheck;
+ default:
+ ecode = code;
+ param_signal_error(plist, param_name, ecode);
+ case 1:
+ break;
+ }
+
+ code = gdev_prn_put_params(dev, plist);
+ if (code < 0)
+ ecode = code;
+
+ pdev->downscale_factor = dsf;
+
+ return ecode;
+}
+
+static int
+png_get_params_downscale_mfs(gx_device *dev, gs_param_list *plist)
+{
+ gx_device_png *pdev = (gx_device_png *)dev;
+ int code, ecode;
+
+ ecode = 0;
+ if ((code = param_write_int(plist, "MinFeatureSize", &pdev->min_feature_size)) < 0)
+ ecode = code;
+ code = png_get_params_downscale(dev, plist);
+ if (code < 0)
+ ecode = code;
+
+ return ecode;
+}
+
+static int
+png_put_params_downscale_mfs(gx_device *dev, gs_param_list *plist)
+{
+ gx_device_png *pdev = (gx_device_png *)dev;
+ int code, ecode;
+ int mfs = pdev->min_feature_size;
+ const char *param_name;
+
+ ecode = 0;
+ switch (code = param_read_int(plist, (param_name = "MinFeatureSize"), &mfs)) {
+ case 0:
+ if (mfs >= 0 && mfs <= 2)
+ break;
+ code = gs_error_rangecheck;
+ default:
+ ecode = code;
+ param_signal_error(plist, param_name, ecode);
+ case 1:
+ break;
+ }
+
+ code = png_put_params_downscale(dev, plist);
+ if (code < 0)
+ ecode = code;
+
+ pdev->min_feature_size = mfs;
+
+ return ecode;
+}
+
+
+/* Write out a page in PNG format. */
+/* This routine is used for all formats. */
+static int
+do_png_print_page(gx_device_png * pdev, FILE * file, bool monod)
+{
+ gs_memory_t *mem = pdev->memory;
+ int raster = gdev_prn_raster(pdev);
+ gx_downscaler_t ds;
+
+ /* PNG structures */
+ byte *row = gs_alloc_bytes(mem, raster, "png raster buffer");
+ png_struct *png_ptr =
+ png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
+ png_info *info_ptr =
+ png_create_info_struct(png_ptr);
+ int depth = pdev->color_info.depth;
+ int y;
+ int code; /* return code */
+ char software_key[80];
+ char software_text[256];
+ png_text text_png;
+ int dst_bpc, src_bpc;
+ bool errdiff = 0;
+ int factor = pdev->downscale_factor;
+ int mfs = pdev->min_feature_size;
+
+ bool invert = false, endian_swap = false, bg_needed = false;
+ png_byte bit_depth = 0;
+ png_byte color_type = 0;
+ png_uint_32 x_pixels_per_unit;
+ png_uint_32 y_pixels_per_unit;
+ png_byte phys_unit_type;
+ png_color_16 background;
+ png_uint_32 width, height;
+#if PNG_LIBPNG_VER_MINOR >= 5
+ png_color palette[256];
+#endif
+ png_color *palettep;
+ png_uint_16 num_palette;
+ png_uint_32 valid = 0;
+
+ /* Sanity check params */
+ if (factor < 1)
+ factor = 1;
+ if (mfs < 1)
+ mfs = 1;
+ else if (mfs > 2)
+ mfs = 2;
+
+ /* Slightly nasty, but it saves us duplicating this entire routine. */
+ if (monod) {
+ errdiff = 1;
+ depth = 1;
+ }
+
+ if (row == 0 || png_ptr == 0 || info_ptr == 0) {
+ code = gs_note_error(gs_error_VMerror);
+ goto done;
+ }
+ /* set error handling */
+#if PNG_LIBPNG_VER_MINOR >= 5
+ code = setjmp(png_jmpbuf(png_ptr));
+#else
+ code = setjmp(png_ptr->jmpbuf);
+#endif
+ if (code) {
+ /* If we get here, we had a problem reading the file */
+ code = gs_note_error(gs_error_VMerror);
+ goto done;
+ }
+ code = 0; /* for normal path */
+ /* set up the output control */
+ png_init_io(png_ptr, file);
+
+ /* set the file information here */
+ /* resolution is in pixels per meter vs. dpi */
+ x_pixels_per_unit =
+ (png_uint_32) (pdev->HWResolution[0] * (100.0 / 2.54) / factor + 0.5);
+ y_pixels_per_unit =
+ (png_uint_32) (pdev->HWResolution[1] * (100.0 / 2.54) / factor + 0.5);
+
+ phys_unit_type = PNG_RESOLUTION_METER;
+ valid |= PNG_INFO_pHYs;
+
+ switch (depth) {
+ case 32:
+ bit_depth = 8;
+ color_type = PNG_COLOR_TYPE_RGB_ALPHA;
+ invert = true;
+
+ { gx_device_pngalpha *ppdev = (gx_device_pngalpha *)pdev;
+ background.index = 0;
+ background.red = (ppdev->background >> 16) & 0xff;
+ background.green = (ppdev->background >> 8) & 0xff;
+ background.blue = (ppdev->background) & 0xff;
+ background.gray = 0;
+ bg_needed = true;
+ }
+ errdiff = 1;
+ break;
+ case 48:
+ bit_depth = 16;
+ color_type = PNG_COLOR_TYPE_RGB;
+#if defined(ARCH_IS_BIG_ENDIAN) && (!ARCH_IS_BIG_ENDIAN)
+ endian_swap = true;
+#endif
+ break;
+ case 24:
+ bit_depth = 8;
+ color_type = PNG_COLOR_TYPE_RGB;
+ errdiff = 1;
+ break;
+ case 8:
+ bit_depth = 8;
+ if (gx_device_has_color(pdev)) {
+ color_type = PNG_COLOR_TYPE_PALETTE;
+ errdiff = 0;
+ } else {
+ color_type = PNG_COLOR_TYPE_GRAY;
+ errdiff = 1;
+ }
+ break;
+ case 4:
+ bit_depth = 4;
+ color_type = PNG_COLOR_TYPE_PALETTE;
+ break;
+ case 1:
+ bit_depth = 1;
+ color_type = PNG_COLOR_TYPE_GRAY;
+ /* invert monocrome pixels */
+ if (!monod) {
+ invert = true;
+ }
+ break;
+ }
+
+ /* set the palette if there is one */
+ if (color_type == PNG_COLOR_TYPE_PALETTE) {
+ int i;
+ int num_colors = 1 << depth;
+ gx_color_value rgb[3];
+
+#if PNG_LIBPNG_VER_MINOR >= 5
+ palettep = palette;
+#else
+ palettep =
+ (void *)gs_alloc_bytes(mem, 256 * sizeof(png_color),
+ "png palette");
+ if (palettep == 0) {
+ code = gs_note_error(gs_error_VMerror);
+ goto done;
+ }
+#endif
+ num_palette = num_colors;
+ valid |= PNG_INFO_PLTE;
+ for (i = 0; i < num_colors; i++) {
+ (*dev_proc(pdev, map_color_rgb)) ((gx_device *) pdev,
+ (gx_color_index) i, rgb);
+ palettep[i].red = gx_color_value_to_byte(rgb[0]);
+ palettep[i].green = gx_color_value_to_byte(rgb[1]);
+ palettep[i].blue = gx_color_value_to_byte(rgb[2]);
+ }
+ }
+ else {
+ palettep = NULL;
+ num_palette = 0;
+ }
+ /* add comment */
+ strncpy(software_key, "Software", sizeof(software_key));
+ gs_sprintf(software_text, "%s %d.%02d", gs_product,
+ (int)(gs_revision / 100), (int)(gs_revision % 100));
+ text_png.compression = -1; /* uncompressed */
+ text_png.key = software_key;
+ text_png.text = software_text;
+ text_png.text_length = strlen(software_text);
+
+ dst_bpc = bit_depth;
+ src_bpc = dst_bpc;
+ if (errdiff)
+ src_bpc = 8;
+ else
+ factor = 1;
+ width = pdev->width/factor;
+ height = pdev->height/factor;
+
+#if PNG_LIBPNG_VER_MINOR >= 5
+ png_set_pHYs(png_ptr, info_ptr,
+ x_pixels_per_unit, y_pixels_per_unit, phys_unit_type);
+
+ png_set_IHDR(png_ptr, info_ptr,
+ width, height, bit_depth,
+ color_type, PNG_INTERLACE_NONE,
+ PNG_COMPRESSION_TYPE_DEFAULT,
+ PNG_FILTER_TYPE_DEFAULT);
+ if (palettep)
+ png_set_PLTE(png_ptr, info_ptr, palettep, num_palette);
+
+ png_set_text(png_ptr, info_ptr, &text_png, 1);
+#else
+ info_ptr->bit_depth = bit_depth;
+ info_ptr->color_type = color_type;
+ info_ptr->width = width;
+ info_ptr->height = height;
+ info_ptr->x_pixels_per_unit = x_pixels_per_unit;
+ info_ptr->y_pixels_per_unit = y_pixels_per_unit;
+ info_ptr->phys_unit_type = phys_unit_type;
+ info_ptr->palette = palettep;
+ info_ptr->num_palette = num_palette;
+ info_ptr->valid |= valid;
+ info_ptr->text = &text_png;
+ info_ptr->num_text = 1;
+ /* Set up the ICC information */
+ if (pdev->icc_struct != NULL && pdev->icc_struct->device_profile[0] != NULL) {
+ cmm_profile_t *icc_profile = pdev->icc_struct->device_profile[0];
+ /* PNG can only be RGB or gray. No CIELAB :( */
+ if (icc_profile->data_cs == gsRGB || icc_profile->data_cs == gsGRAY) {
+ if (icc_profile->num_comps == pdev->color_info.num_components &&
+ !(pdev->icc_struct->usefastcolor)) {
+ info_ptr->iccp_name = icc_profile->name;
+ info_ptr->iccp_profile = icc_profile->buffer;
+ info_ptr->iccp_proflen = icc_profile->buffer_size;
+ info_ptr->valid |= PNG_INFO_iCCP;
+ }
+ }
+ }
+#endif
+ if (invert) {
+ if (depth == 32)
+ png_set_invert_alpha(png_ptr);
+ else
+ png_set_invert_mono(png_ptr);
+ }
+ if (bg_needed) {
+ png_set_bKGD(png_ptr, info_ptr, &background);
+ }
+#if defined(ARCH_IS_BIG_ENDIAN) && (!ARCH_IS_BIG_ENDIAN)
+ if (endian_swap) {
+ png_set_swap(png_ptr);
+ }
+#endif
+
+ /* write the file information */
+ png_write_info(png_ptr, info_ptr);
+
+#if PNG_LIBPNG_VER_MINOR >= 5
+#else
+ /* don't write the comments twice */
+ info_ptr->num_text = 0;
+ info_ptr->text = NULL;
+#endif
+
+ /* For simplicity of code, we always go through the downscaler. For
+ * non-supported depths, it will pass through with minimal performance
+ * hit. So ensure that we only trigger downscales when we need them.
+ */
+ code = gx_downscaler_init(&ds, (gx_device *)pdev, src_bpc, dst_bpc,
+ depth/dst_bpc, factor, mfs, NULL, 0);
+ if (code >= 0)
+ {
+ /* Write the contents of the image. */
+ for (y = 0; y < height; y++) {
+ gx_downscaler_copy_scan_lines(&ds, y, row, raster);
+ png_write_rows(png_ptr, &row, 1);
+ }
+ gx_downscaler_fin(&ds);
+ }
+
+ /* write the rest of the file */
+ png_write_end(png_ptr, info_ptr);
+
+#if PNG_LIBPNG_VER_MINOR >= 5
+#else
+ /* if you alloced the palette, free it here */
+ gs_free_object(mem, palettep, "png palette");
+#endif
+
+ done:
+ /* free the structures */
+ png_destroy_write_struct(&png_ptr, &info_ptr);
+ gs_free_object(mem, row, "png raster buffer");
+
+ return code;
+}
+
+static int
+png_print_page(gx_device_printer * pdev, FILE * file)
+{
+ return do_png_print_page((gx_device_png *)pdev, file, 0);
+}
+
+static int
+png_print_page_monod(gx_device_printer * pdev, FILE * file)
+{
+ return do_png_print_page((gx_device_png *)pdev, file, 1);
+}
+
+#if PNG_LIBPNG_VER_MINOR < 5
+
+/*
+ * Patch around a static reference to a never-used procedure.
+ * This could be avoided if we were willing to edit pngconf.h to
+ * #undef PNG_PROGRESSIVE_READ_SUPPORTED
+ */
+#ifdef PNG_PROGRESSIVE_READ_SUPPORTED
+# if PNG_LIBPNG_VER >= 95
+# define PPFB_LENGTH_T png_size_t
+# else
+# define PPFB_LENGTH_T png_uint_32
+# endif
+void
+png_push_fill_buffer(png_structp, png_bytep, PPFB_LENGTH_T);
+void
+png_push_fill_buffer(png_structp png_ptr, png_bytep buffer,
+ PPFB_LENGTH_T length)
+{
+}
+#endif
+#endif
+
+static int
+pngalpha_open(gx_device * pdev)
+{
+ gx_device_pngalpha *ppdev = (gx_device_pngalpha *)pdev;
+ int code;
+ /* We replace create_buf_device so we can replace copy_alpha
+ * for memory device, but not clist. We also replace the fillpage
+ * proc with our own to fill with transparent.
+ */
+ ppdev->printer_procs.buf_procs.create_buf_device =
+ pngalpha_create_buf_device;
+ code = gdev_prn_open(pdev);
+ return code;
+}
+
+static int
+pngalpha_create_buf_device(gx_device **pbdev, gx_device *target, int y,
+ const gx_render_plane_t *render_plane, gs_memory_t *mem,
+ gx_color_usage_t *color_usage)
+{
+ gx_device_printer *ptarget = (gx_device_printer *)target;
+ int code = gx_default_create_buf_device(pbdev, target, y,
+ render_plane, mem, color_usage);
+ /* Now set copy_alpha to one that handles RGBA */
+ set_dev_proc(*pbdev, copy_alpha, ptarget->orig_procs.copy_alpha);
+ set_dev_proc(*pbdev, fillpage, pngalpha_fillpage);
+ return code;
+}
+
+static int
+pngalpha_put_params(gx_device * pdev, gs_param_list * plist)
+{
+ gx_device_pngalpha *ppdev = (gx_device_pngalpha *)pdev;
+ int background;
+ int code, ecode;
+ int dsf = ppdev->downscale_factor;
+ const char *param_name;
+
+ /* BackgroundColor in format 16#RRGGBB is used for bKGD chunk */
+ switch(code = param_read_int(plist, "BackgroundColor", &background)) {
+ case 0:
+ ppdev->background = background & 0xffffff;
+ break;
+ case 1: /* not found */
+ code = 0;
+ break;
+ default:
+ param_signal_error(plist, "BackgroundColor", code);
+ break;
+ }
+
+ switch (ecode = param_read_int(plist, (param_name = "DownScaleFactor"), &dsf)) {
+ case 0:
+ if (dsf >= 1)
+ break;
+ ecode = gs_error_rangecheck;
+ default:
+ code = ecode;
+ param_signal_error(plist, param_name, ecode);
+ case 1:
+ break;
+ }
+
+ if (code == 0) {
+ code = gdev_prn_put_params(pdev, plist);
+ }
+ ppdev->downscale_factor = dsf;
+ return code;
+}
+
+/* Get device parameters */
+static int
+pngalpha_get_params(gx_device * pdev, gs_param_list * plist)
+{
+ gx_device_pngalpha *ppdev = (gx_device_pngalpha *)pdev;
+ int code = gdev_prn_get_params(pdev, plist);
+ int ecode;
+ if (code >= 0)
+ code = param_write_int(plist, "BackgroundColor",
+ &(ppdev->background));
+ ecode = 0;
+ if (ppdev->downscale_factor < 1)
+ ppdev->downscale_factor = 1;
+ if ((ecode = param_write_int(plist, "DownScaleFactor", &ppdev->downscale_factor)) < 0)
+ code = ecode;
+
+ return code;
+}
+
+/* RGB mapping for 32-bit RGBA color devices */
+
+static gx_color_index
+pngalpha_encode_color(gx_device * dev, const gx_color_value cv[])
+{
+ /* low 7 are alpha, stored inverted to avoid white/opaque
+ * being 0xffffffff which is also gx_no_color_index.
+ * So 0xff is transparent and 0x00 is opaque.
+ * We always return opaque colors (bits 0-7 = 0).
+ * Return value is 0xRRGGBB00.
+ */
+ return
+ ((uint) gx_color_value_to_byte(cv[2]) << 8) +
+ ((ulong) gx_color_value_to_byte(cv[1]) << 16) +
+ ((ulong) gx_color_value_to_byte(cv[0]) << 24);
+}
+
+/* Map a color index to a r-g-b color. */
+static int
+pngalpha_decode_color(gx_device * dev, gx_color_index color,
+ gx_color_value prgb[3])
+{
+ prgb[0] = gx_color_value_from_byte((color >> 24) & 0xff);
+ prgb[1] = gx_color_value_from_byte((color >> 16) & 0xff);
+ prgb[2] = gx_color_value_from_byte((color >> 8) & 0xff);
+ return 0;
+}
+
+/* fill the page fills with transparent */
+static int
+pngalpha_fillpage(gx_device *dev, gs_imager_state * pis, gx_device_color *pdevc)
+{
+ return (*dev_proc(dev, fill_rectangle))(dev, 0, 0, dev->width, dev->height, 0xffffffff);
+}
+
+/* Handle the RGBA planes from the PDF 1.4 compositor */
+static int
+pngalpha_put_image (gx_device *pdev, const byte *buffer, int num_chan, int xstart,
+ int ystart, int width, int height, int row_stride,
+ int plane_stride, int alpha_plane_index, int tag_plane_index)
+{
+ gx_device_memory *pmemdev = (gx_device_memory *)pdev;
+ byte *buffer_prn;
+ int yend = ystart + height;
+ int xend = xstart + width;
+ int x, y;
+ int src_position, des_position;
+
+ /* Eventually, the pdf14 device might be chunky pixels, punt for now */
+ if (plane_stride == 0)
+ return 0;
+ if (num_chan != 3 || alpha_plane_index <= 0)
+ return_error(gs_error_unknownerror); /* can't handle these cases */
+
+ /* Now we need to convert the 4 channels (RGBA) planar into what */
+ /* the do_png_print_page expects -- chunky inverted data. For that */
+ /* we need to find the underlying gx_device_memory buffer for the */
+ /* data (similar to bit_put_image, and borrwed from there). */
+ /* Drill down to get the appropriate memory buffer pointer */
+ buffer_prn = pmemdev->base;
+ /* Now go ahead and process the planes into chunky as the memory device needs */
+ for ( y = ystart; y < yend; y++ ) {
+ src_position = (y - ystart) * row_stride;
+ des_position = y * pmemdev->raster + xstart * 4;
+ for ( x = xstart; x < xend; x++ ) {
+ buffer_prn[des_position++] = buffer[src_position];
+ buffer_prn[des_position++] = buffer[src_position + plane_stride];
+ buffer_prn[des_position++] = buffer[src_position + 2 * plane_stride];
+ /* Alpha data in low bits. Note that Alpha is inverted. */
+ buffer_prn[des_position++] = (255 - buffer[src_position + alpha_plane_index * plane_stride]);
+ src_position += 1;
+ }
+ }
+ return height; /* we used all of the data */
+}
+
+/* Implementation for 32-bit RGBA in a memory buffer */
+/* Derived from gx_default_copy_alpha, but now maintains alpha channel. */
+static int
+pngalpha_copy_alpha(gx_device * dev, const byte * data, int data_x,
+ int raster, gx_bitmap_id id, int x, int y, int width, int height,
+ gx_color_index color, int depth)
+{ /* This might be called with depth = 1.... */
+ if (depth == 1)
+ return (*dev_proc(dev, copy_mono)) (dev, data, data_x, raster, id,
+ x, y, width, height,
+ gx_no_color_index, color);
+ /*
+ * Simulate alpha by weighted averaging of RGB values.
+ * This is very slow, but functionally correct.
+ */
+ {
+ const byte *row;
+ gs_memory_t *mem = dev->memory;
+ int bpp = dev->color_info.depth;
+ int ncomps = dev->color_info.num_components;
+ uint in_size = gx_device_raster(dev, false);
+ byte *lin;
+ uint out_size;
+ byte *lout;
+ int code = 0;
+ gx_color_value color_cv[GX_DEVICE_COLOR_MAX_COMPONENTS];
+ int ry;
+
+ fit_copy(dev, data, data_x, raster, id, x, y, width, height);
+ row = data;
+ out_size = bitmap_raster(width * bpp);
+ lin = gs_alloc_bytes(mem, in_size, "copy_alpha(lin)");
+ lout = gs_alloc_bytes(mem, out_size, "copy_alpha(lout)");
+ if (lin == 0 || lout == 0) {
+ code = gs_note_error(gs_error_VMerror);
+ goto out;
+ }
+ (*dev_proc(dev, decode_color)) (dev, color, color_cv);
+ for (ry = y; ry < y + height; row += raster, ++ry) {
+ byte *line;
+ int sx, rx;
+
+ DECLARE_LINE_ACCUM_COPY(lout, bpp, x);
+
+ code = (*dev_proc(dev, get_bits)) (dev, ry, lin, &line);
+ if (code < 0)
+ break;
+ for (sx = data_x, rx = x; sx < data_x + width; ++sx, ++rx) {
+ gx_color_index previous = gx_no_color_index;
+ gx_color_index composite;
+ int alpha2, alpha;
+
+ if (depth == 2) /* map 0 - 3 to 0 - 15 */
+ alpha = ((row[sx >> 2] >> ((3 - (sx & 3)) << 1)) & 3) * 5;
+ else
+ alpha2 = row[sx >> 1],
+ alpha = (sx & 1 ? alpha2 & 0xf : alpha2 >> 4);
+ if (alpha == 15) { /* Just write the new color. */
+ composite = color;
+ } else {
+ if (previous == gx_no_color_index) { /* Extract the old color. */
+ const byte *src = line + (rx * (bpp >> 3));
+ previous = 0;
+ previous += (gx_color_index) * src++ << 24;
+ previous += (gx_color_index) * src++ << 16;
+ previous += (gx_color_index) * src++ << 8;
+ previous += *src++;
+ }
+ if (alpha == 0) { /* Just write the old color. */
+ composite = previous;
+ } else { /* Blend values. */
+ gx_color_value cv[GX_DEVICE_COLOR_MAX_COMPONENTS];
+ int i;
+ int old_coverage;
+ int new_coverage;
+
+ (*dev_proc(dev, decode_color)) (dev, previous, cv);
+ /* decode color doesn't give us coverage */
+ cv[3] = previous & 0xff;
+ old_coverage = 255 - cv[3];
+ new_coverage =
+ (255 * alpha + old_coverage * (15 - alpha)) / 15;
+ for (i=0; i<ncomps; i++)
+ cv[i] = min(((255 * alpha * color_cv[i]) +
+ (old_coverage * (15 - alpha ) * cv[i]))
+ / (new_coverage * 15), gx_max_color_value);
+ composite =
+ (*dev_proc(dev, encode_color)) (dev, cv);
+ /* encode color doesn't include coverage */
+ composite |= (255 - new_coverage) & 0xff;
+
+ /* composite can never be gx_no_color_index
+ * because pixel is never completely transparent
+ * (low byte != 0xff).
+ */
+ }
+ }
+ LINE_ACCUM(composite, bpp);
+ }
+ LINE_ACCUM_COPY(dev, lout, bpp, x, rx, raster, ry);
+ }
+ out:gs_free_object(mem, lout, "copy_alpha(lout)");
+ gs_free_object(mem, lin, "copy_alpha(lin)");
+ return code;
+ }
+}
diff --git a/devices/gdevpsd.c b/devices/gdevpsd.c
new file mode 100644
index 000000000..4db034e83
--- /dev/null
+++ b/devices/gdevpsd.c
@@ -0,0 +1,1390 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* PhotoShop (PSD) export device, supporting DeviceN color models. */
+
+#include "math_.h"
+#include "gdevprn.h"
+#include "gsparam.h"
+#include "gscrd.h"
+#include "gscrdp.h"
+#include "gxlum.h"
+#include "gdevdcrd.h"
+#include "gstypes.h"
+#include "gxdcconv.h"
+#include "gdevdevn.h"
+#include "gsequivc.h"
+#include "gscms.h"
+#include "gsicc_cache.h"
+#include "gsicc_manage.h"
+#include "gxgetbit.h"
+#include "gdevppla.h"
+#include "gxdownscale.h"
+#include "gdevdevnprn.h"
+
+#ifndef cmm_gcmmhlink_DEFINED
+ #define cmm_gcmmhlink_DEFINED
+ typedef void* gcmmhlink_t;
+#endif
+
+#ifndef cmm_gcmmhprofile_DEFINED
+ #define cmm_gcmmhprofile_DEFINED
+ typedef void* gcmmhprofile_t;
+#endif
+
+#ifndef MAX_CHAN
+# define MAX_CHAN 15
+#endif
+
+/* Enable logic for a local ICC output profile. */
+#define ENABLE_ICC_PROFILE 0
+
+/* Define the device parameters. */
+#ifndef X_DPI
+# define X_DPI 72
+#endif
+#ifndef Y_DPI
+# define Y_DPI 72
+#endif
+
+/* The device descriptor */
+static dev_proc_open_device(psd_prn_open);
+static dev_proc_close_device(psd_prn_close);
+static dev_proc_get_params(psd_get_params);
+static dev_proc_put_params(psd_put_params);
+static dev_proc_print_page(psd_print_page);
+static dev_proc_map_color_rgb(psd_map_color_rgb);
+static dev_proc_get_color_mapping_procs(get_psdrgb_color_mapping_procs);
+static dev_proc_get_color_mapping_procs(get_psd_color_mapping_procs);
+static dev_proc_get_color_comp_index(psd_get_color_comp_index);
+
+/* This is redundant with color_info.cm_name. We may eliminate this
+ typedef and use the latter string for everything. */
+typedef enum {
+ psd_DEVICE_GRAY,
+ psd_DEVICE_RGB,
+ psd_DEVICE_CMYK,
+ psd_DEVICE_N
+} psd_color_model;
+
+/*
+ * A structure definition for a DeviceN type device
+ */
+typedef struct psd_device_s {
+ gx_devn_prn_device_common;
+
+ psd_color_model color_model;
+
+ long downscale_factor;
+ int max_spots;
+ bool lock_colorants;
+
+ /* ICC color profile objects, for color conversion.
+ These are all device link profiles. At least that
+ is how it appears looking at how this code
+ was written to work with the old icclib. Just
+ doing minimal updates here so that it works
+ with the new CMM API. I would be interested
+ to hear how people are using this. */
+
+ char profile_rgb_fn[256];
+ cmm_profile_t *rgb_profile;
+ gcmmhlink_t rgb_icc_link;
+
+ char profile_cmyk_fn[256];
+ cmm_profile_t *cmyk_profile;
+ gcmmhlink_t cmyk_icc_link;
+
+ char profile_out_fn[256];
+ cmm_profile_t *output_profile;
+ gcmmhlink_t output_icc_link;
+
+ bool warning_given; /* Used to notify the user that max colorants reached */
+
+} psd_device;
+
+/* GC procedures */
+static
+ENUM_PTRS_WITH(psd_device_enum_ptrs, psd_device *pdev)
+{
+ ENUM_PREFIX(st_gx_devn_prn_device, 0);
+ return 0;
+}
+ENUM_PTRS_END
+
+static RELOC_PTRS_WITH(psd_device_reloc_ptrs, psd_device *pdev)
+{
+ RELOC_PREFIX(st_gx_devn_prn_device);
+}
+RELOC_PTRS_END
+
+/* Even though psd_device_finalize is the same as gx_devn_prn_device_finalize,
+ * we need to implement it separately because st_composite_final
+ * declares all 3 procedures as private. */
+static void
+psd_device_finalize(const gs_memory_t *cmem, void *vpdev)
+{
+ gx_devn_prn_device_finalize(cmem, vpdev);
+}
+
+gs_private_st_composite_final(st_psd_device, psd_device,
+ "psd_device", psd_device_enum_ptrs, psd_device_reloc_ptrs,
+ psd_device_finalize);
+
+/*
+ * Macro definition for psd device procedures
+ */
+#define device_procs(get_color_mapping_procs)\
+{ psd_prn_open,\
+ gx_default_get_initial_matrix,\
+ NULL, /* sync_output */\
+ /* Since the print_page doesn't alter the device, this device can print in the background */\
+ gdev_prn_bg_output_page, /* output_page */\
+ psd_prn_close, /* close */\
+ NULL, /* map_rgb_color - not used */\
+ psd_map_color_rgb, /* map_color_rgb */\
+ NULL, /* fill_rectangle */\
+ NULL, /* tile_rectangle */\
+ NULL, /* copy_mono */\
+ NULL, /* copy_color */\
+ NULL, /* draw_line */\
+ NULL, /* get_bits */\
+ psd_get_params, /* get_params */\
+ psd_put_params, /* put_params */\
+ NULL, /* map_cmyk_color - not used */\
+ NULL, /* get_xfont_procs */\
+ NULL, /* get_xfont_device */\
+ NULL, /* map_rgb_alpha_color */\
+ gx_page_device_get_page_device, /* get_page_device */\
+ NULL, /* get_alpha_bits */\
+ NULL, /* copy_alpha */\
+ NULL, /* get_band */\
+ NULL, /* copy_rop */\
+ NULL, /* fill_path */\
+ NULL, /* stroke_path */\
+ NULL, /* fill_mask */\
+ NULL, /* fill_trapezoid */\
+ NULL, /* fill_parallelogram */\
+ NULL, /* fill_triangle */\
+ NULL, /* draw_thin_line */\
+ NULL, /* begin_image */\
+ NULL, /* image_data */\
+ NULL, /* end_image */\
+ NULL, /* strip_tile_rectangle */\
+ NULL, /* strip_copy_rop */\
+ NULL, /* get_clipping_box */\
+ NULL, /* begin_typed_image */\
+ NULL, /* get_bits_rectangle */\
+ NULL, /* map_color_rgb_alpha */\
+ NULL, /* create_compositor */\
+ NULL, /* get_hardware_params */\
+ NULL, /* text_begin */\
+ NULL, /* finish_copydevice */\
+ NULL, /* begin_transparency_group */\
+ NULL, /* end_transparency_group */\
+ NULL, /* begin_transparency_mask */\
+ NULL, /* end_transparency_mask */\
+ NULL, /* discard_transparency_layer */\
+ get_color_mapping_procs, /* get_color_mapping_procs */\
+ psd_get_color_comp_index, /* get_color_comp_index */\
+ gx_devn_prn_encode_color, /* encode_color */\
+ gx_devn_prn_decode_color, /* decode_color */\
+ NULL, /* pattern_manage */\
+ NULL, /* fill_rectangle_hl_color */\
+ NULL, /* include_color_space */\
+ NULL, /* fill_linear_color_scanline */\
+ NULL, /* fill_linear_color_trapezoid */\
+ NULL, /* fill_linear_color_triangle */\
+ gx_devn_prn_update_spot_equivalent_colors, /* update_spot_equivalent_colors */\
+ gx_devn_prn_ret_devn_params /* ret_devn_params */\
+}
+
+static fixed_colorant_name DeviceGrayComponents[] = {
+ "Gray",
+ 0 /* List terminator */
+};
+
+static fixed_colorant_name DeviceRGBComponents[] = {
+ "Red",
+ "Green",
+ "Blue",
+ 0 /* List terminator */
+};
+
+#define psd_device_body(procs, dname, ncomp, pol, depth, mg, mc, sl, cn)\
+ std_device_full_body_type_extended(psd_device, &procs, dname,\
+ &st_psd_device,\
+ (int)((long)(DEFAULT_WIDTH_10THS) * (X_DPI) / 10),\
+ (int)((long)(DEFAULT_HEIGHT_10THS) * (Y_DPI) / 10),\
+ X_DPI, Y_DPI,\
+ GX_DEVICE_COLOR_MAX_COMPONENTS, /* MaxComponents */\
+ ncomp, /* NumComp */\
+ pol, /* Polarity */\
+ depth, 0, /* Depth, GrayIndex */\
+ mg, mc, /* MaxGray, MaxColor */\
+ mg + 1, mc + 1, /* DitherGray, DitherColor */\
+ sl, /* Linear & Separable? */\
+ cn, /* Process color model name */\
+ 0, 0, /* offsets */\
+ 0, 0, 0, 0 /* margins */\
+ ),\
+ prn_device_body_rest_(psd_print_page)
+
+/*
+ * PSD device with RGB process color model.
+ */
+static const gx_device_procs spot_rgb_procs =
+ device_procs(get_psdrgb_color_mapping_procs);
+
+const psd_device gs_psdrgb_device =
+{
+ psd_device_body(spot_rgb_procs, "psdrgb", 3, GX_CINFO_POLARITY_ADDITIVE, 24, 255, 255, GX_CINFO_SEP_LIN, "DeviceRGB"),
+ /* devn_params specific parameters */
+ { 8, /* Bits per color - must match ncomp, depth, etc. above */
+ DeviceRGBComponents, /* Names of color model colorants */
+ 3, /* Number colorants for RGB */
+ 0, /* MaxSeparations has not been specified */
+ -1, /* PageSpotColors has not been specified */
+ {0}, /* SeparationNames */
+ 0, /* SeparationOrder names */
+ {0, 1, 2, 3, 4, 5, 6, 7 } /* Initial component SeparationOrder */
+ },
+ { true }, /* equivalent CMYK colors for spot colors */
+ /* PSD device specific parameters */
+ psd_DEVICE_RGB, /* Color model */
+ 1, /* downscale_factor */
+ GS_SOFT_MAX_SPOTS, /* max_spots */
+ false, /* colorants not locked */
+};
+
+/*
+ * PSD device with CMYK process color model and spot color support.
+ */
+static const gx_device_procs spot_cmyk_procs
+ = device_procs(get_psd_color_mapping_procs);
+
+const psd_device gs_psdcmyk_device =
+{
+ psd_device_body(spot_cmyk_procs, "psdcmyk",
+ ARCH_SIZEOF_GX_COLOR_INDEX, /* Number of components - need a nominal 1 bit for each */
+ GX_CINFO_POLARITY_SUBTRACTIVE,
+ ARCH_SIZEOF_GX_COLOR_INDEX * 8, /* 8 bits per component (albeit in planes) */
+ 255, 255, GX_CINFO_SEP_LIN, "DeviceCMYK"),
+ /* devn_params specific parameters */
+ { 8, /* Bits per color - must match ncomp, depth, etc. above */
+ DeviceCMYKComponents, /* Names of color model colorants */
+ 4, /* Number colorants for CMYK */
+ 0, /* MaxSeparations has not been specified */
+ -1, /* PageSpotColors has not been specified */
+ {0}, /* SeparationNames */
+ 0, /* SeparationOrder names */
+ {0, 1, 2, 3, 4, 5, 6, 7 } /* Initial component SeparationOrder */
+ },
+ { true }, /* equivalent CMYK colors for spot colors */
+ /* PSD device specific parameters */
+ psd_DEVICE_CMYK, /* Color model */
+ 1, /* downscale_factor */
+ GS_SOFT_MAX_SPOTS, /* max_spots */
+ false, /* colorants not locked */
+};
+
+/* Open the psd devices */
+int
+psd_prn_open(gx_device * pdev)
+{
+ psd_device *pdev_psd = (psd_device *) pdev;
+ int code;
+ int k;
+ bool force_pdf, limit_icc, force_ps;
+ cmm_dev_profile_t *profile_struct;
+
+#ifdef TEST_PAD_AND_ALIGN
+ pdev->pad = 5;
+ pdev->log2_align_mod = 6;
+#endif
+
+ /* There are 2 approaches to the use of a DeviceN ICC output profile.
+ One is to simply limit our device to only output the colorants
+ defined in the output ICC profile. The other is to use the
+ DeviceN ICC profile to color manage those N colorants and
+ to let any other separations pass through unmolested. The define
+ LIMIT_TO_ICC sets the option to limit our device to only the ICC
+ colorants defined by -sICCOutputColors (or to the ones that are used
+ as default names if ICCOutputColors is not used). The pass through option
+ (LIMIT_TO_ICC set to 0) makes life a bit more difficult since we don't
+ know if the page_spot_colors overlap with any spot colorants that exist
+ in the DeviceN ICC output profile. Hence we don't know how many planes
+ to use for our device. This is similar to the issue when processing
+ a PostScript file. So that I remember, the cases are
+ DeviceN Profile? limit_icc Result
+ 0 0 force_pdf 0 force_ps 0 (no effect)
+ 0 0 force_pdf 0 force_ps 0 (no effect)
+ 1 0 force_pdf 0 force_ps 1 (colorants not known)
+ 1 1 force_pdf 1 force_ps 0 (colorants known)
+ */
+#if LIMIT_TO_ICC
+ limit_icc = true;
+#else
+ limit_icc = false;
+#endif
+ code = dev_proc(pdev, get_profile)((gx_device *)pdev, &profile_struct);
+ if (profile_struct->spotnames == NULL) {
+ force_pdf = false;
+ force_ps = false;
+ } else {
+ if (limit_icc) {
+ force_pdf = true;
+ force_ps = false;
+ } else {
+ force_pdf = false;
+ force_ps = true;
+ }
+ }
+ pdev_psd->warning_given = false;
+ /* With planar the depth can be more than 64. Update the color
+ info to reflect the proper depth and number of planes. Also note
+ that the number of spot colors can change from page to page.
+ Update things so that we only output separations for the
+ inks on that page. */
+
+ if (pdev->color_info.polarity == GX_CINFO_POLARITY_SUBTRACTIVE) {
+ if ((pdev_psd->devn_params.page_spot_colors >= 0 || force_pdf) && !force_ps) {
+ if (force_pdf) {
+ /* Use the information that is in the ICC profle. We will be here
+ anytime that we have limited ourselves to a fixed number
+ of colorants specified by the DeviceN ICC profile */
+ pdev->color_info.num_components =
+ (pdev_psd->devn_params.separations.num_separations
+ + pdev_psd->devn_params.num_std_colorant_names);
+ if (pdev->color_info.num_components > pdev->color_info.max_components)
+ pdev->color_info.num_components = pdev->color_info.max_components;
+ /* Limit us only to the ICC colorants */
+ pdev->color_info.max_components = pdev->color_info.num_components;
+ } else {
+ /* Use the information that is in the page spot color. We should
+ be here if we are processing a PDF and we do not have a DeviceN
+ ICC profile specified for output */
+ if (!(pdev_psd->lock_colorants)) {
+ pdev->color_info.num_components =
+ (pdev_psd->devn_params.page_spot_colors
+ + pdev_psd->devn_params.num_std_colorant_names);
+ if (pdev->color_info.num_components > pdev->color_info.max_components)
+ pdev->color_info.num_components = pdev->color_info.max_components;
+ }
+ }
+ } else {
+ /* We do not know how many spots may occur on the page.
+ For this reason we go ahead and allocate the maximum that we
+ have available. Note, lack of knowledge only occurs in the case
+ of PS files. With PDF we know a priori the number of spot
+ colorants. */
+ if (!(pdev_psd->lock_colorants)) {
+ int num_comp = pdev_psd->max_spots + 4; /* Spots + CMYK */
+ if (num_comp > GS_CLIENT_COLOR_MAX_COMPONENTS)
+ num_comp = GS_CLIENT_COLOR_MAX_COMPONENTS;
+ pdev->color_info.num_components = num_comp;
+ pdev->color_info.max_components = num_comp;
+ }
+ }
+ }
+ /* Push this to the max amount as a default if someone has not set it */
+ if (pdev_psd->devn_params.num_separation_order_names == 0)
+ for (k = 0; k < GS_CLIENT_COLOR_MAX_COMPONENTS; k++) {
+ pdev_psd->devn_params.separation_order_map[k] = k;
+ }
+ pdev->color_info.depth = pdev->color_info.num_components *
+ pdev_psd->devn_params.bitspercomponent;
+ pdev->color_info.separable_and_linear = GX_CINFO_SEP_LIN;
+ pdev->icc_struct->supports_devn = true;
+ code = gdev_prn_open_planar(pdev, true);
+ return code;
+}
+
+/* 2007/05/04
+psdgray device
+*/
+static void
+gray_cs_to_psdgray_cm(gx_device * dev, frac gray, frac out[])
+{
+ out[0] = gray;
+}
+
+static void
+rgb_cs_to_psdgray_cm(gx_device * dev, const gs_imager_state *pis,
+ frac r, frac g, frac b, frac out[])
+{
+ out[0] = color_rgb_to_gray(r, g, b, NULL);
+}
+
+static void
+cmyk_cs_to_psdgray_cm(gx_device * dev, frac c, frac m, frac y, frac k, frac out[])
+{
+ out[0] = color_cmyk_to_gray(c, m, y, k, NULL);
+}
+
+/*
+ * The following procedures are used to map the standard color spaces into
+ * the color components for the psdrgb device.
+ */
+static void
+gray_cs_to_psdrgb_cm(gx_device * dev, frac gray, frac out[])
+{
+ int i = ((psd_device *)dev)->devn_params.separations.num_separations;
+
+ out[0] = out[1] = out[2] = gray;
+ for(; i>0; i--) /* Clear spot colors */
+ out[2 + i] = 0;
+}
+
+static void
+rgb_cs_to_psdrgb_cm(gx_device * dev, const gs_imager_state *pis,
+ frac r, frac g, frac b, frac out[])
+{
+ int i = ((psd_device *)dev)->devn_params.separations.num_separations;
+
+ out[0] = r;
+ out[1] = g;
+ out[2] = b;
+ for(; i>0; i--) /* Clear spot colors */
+ out[2 + i] = 0;
+}
+
+static void
+cmyk_cs_to_psdrgb_cm(gx_device * dev,
+ frac c, frac m, frac y, frac k, frac out[])
+{
+ int i = ((psd_device *)dev)->devn_params.separations.num_separations;
+
+ color_cmyk_to_rgb(c, m, y, k, NULL, out, dev->memory);
+ for(; i>0; i--) /* Clear spot colors */
+ out[2 + i] = 0;
+}
+
+/* Color mapping routines for the psdcmyk device */
+
+static void
+gray_cs_to_psdcmyk_cm(gx_device * dev, frac gray, frac out[])
+{
+ int * map = ((psd_device *) dev)->devn_params.separation_order_map;
+
+ gray_cs_to_devn_cm(dev, map, gray, out);
+}
+
+static void
+rgb_cs_to_psdcmyk_cm(gx_device * dev, const gs_imager_state *pis,
+ frac r, frac g, frac b, frac out[])
+{
+ int * map = ((psd_device *) dev)->devn_params.separation_order_map;
+
+ rgb_cs_to_devn_cm(dev, map, pis, r, g, b, out);
+}
+
+static void
+cmyk_cs_to_psdcmyk_cm(gx_device * dev,
+ frac c, frac m, frac y, frac k, frac out[])
+{
+ const gs_devn_params *devn = gx_devn_prn_ret_devn_params(dev);
+ const int *map = devn->separation_order_map;
+ int j;
+
+ if (devn->num_separation_order_names > 0) {
+ /* This is to set only those that we are using */
+ for (j = 0; j < devn->num_separation_order_names; j++) {
+ switch (map[j]) {
+ case 0 :
+ out[0] = c;
+ break;
+ case 1:
+ out[1] = m;
+ break;
+ case 2:
+ out[2] = y;
+ break;
+ case 3:
+ out[3] = k;
+ break;
+ default:
+ break;
+ }
+ }
+ } else {
+ cmyk_cs_to_devn_cm(dev, map, c, m, y, k, out);
+ }
+}
+
+static void
+cmyk_cs_to_spotn_cm(gx_device * dev, frac c, frac m, frac y, frac k, frac out[])
+{
+ psd_device *xdev = (psd_device *)dev;
+ int n = xdev->devn_params.separations.num_separations;
+
+ gcmmhlink_t link = xdev->cmyk_icc_link;
+ int i;
+
+ if (link != NULL) {
+
+ unsigned short in[4];
+ unsigned short tmp[MAX_CHAN];
+ int outn = xdev->cmyk_profile->num_comps_out;
+
+ in[0] = frac2ushort(c);
+ in[1] = frac2ushort(m);
+ in[2] = frac2ushort(y);
+ in[3] = frac2ushort(k);
+
+ gscms_transform_color(dev, link, &(in[0]),
+ &(tmp[0]), 2);
+
+ for (i = 0; i < outn; i++)
+ out[i] = ushort2frac(tmp[i]);
+ for (; i < n + 4; i++)
+ out[i] = 0;
+
+ } else {
+ /* If no profile given, assume CMYK */
+ out[0] = c;
+ out[1] = m;
+ out[2] = y;
+ out[3] = k;
+ for(i = 0; i < n; i++) /* Clear spot colors */
+ out[4 + i] = 0;
+ }
+}
+
+static void
+gray_cs_to_spotn_cm(gx_device * dev, frac gray, frac out[])
+{
+ cmyk_cs_to_spotn_cm(dev, 0, 0, 0, (frac)(frac_1 - gray), out);
+}
+
+static void
+rgb_cs_to_spotn_cm(gx_device * dev, const gs_imager_state *pis,
+ frac r, frac g, frac b, frac out[])
+{
+ psd_device *xdev = (psd_device *)dev;
+ int n = xdev->devn_params.separations.num_separations;
+ gcmmhlink_t link = xdev->rgb_icc_link;
+ int i;
+
+ if (link != NULL) {
+
+ unsigned short in[3];
+ unsigned short tmp[MAX_CHAN];
+ int outn = xdev->rgb_profile->num_comps_out;
+
+ in[0] = frac2ushort(r);
+ in[1] = frac2ushort(g);
+ in[2] = frac2ushort(b);
+
+ gscms_transform_color(dev, link, &(in[0]),
+ &(tmp[0]), 2);
+
+ for (i = 0; i < outn; i++)
+ out[i] = ushort2frac(tmp[i]);
+ for (; i < n + 4; i++)
+ out[i] = 0;
+
+ } else {
+ frac cmyk[4];
+
+ color_rgb_to_cmyk(r, g, b, pis, cmyk, dev->memory);
+ cmyk_cs_to_spotn_cm(dev, cmyk[0], cmyk[1], cmyk[2], cmyk[3],
+ out);
+ }
+}
+
+static const gx_cm_color_map_procs psdGray_procs = {/* 2007/05/04 Test */
+ gray_cs_to_psdgray_cm, rgb_cs_to_psdgray_cm, cmyk_cs_to_psdgray_cm
+};
+
+static const gx_cm_color_map_procs psdRGB_procs = {
+ gray_cs_to_psdrgb_cm, rgb_cs_to_psdrgb_cm, cmyk_cs_to_psdrgb_cm
+};
+
+static const gx_cm_color_map_procs psdCMYK_procs = {
+ gray_cs_to_psdcmyk_cm, rgb_cs_to_psdcmyk_cm, cmyk_cs_to_psdcmyk_cm
+};
+
+static const gx_cm_color_map_procs psdN_procs = {
+ gray_cs_to_spotn_cm, rgb_cs_to_spotn_cm, cmyk_cs_to_spotn_cm
+};
+
+/*
+ * These are the handlers for returning the list of color space
+ * to color model conversion routines.
+ */
+static const gx_cm_color_map_procs *
+get_psdrgb_color_mapping_procs(const gx_device * dev)
+{
+ return &psdRGB_procs;
+}
+
+static const gx_cm_color_map_procs *
+get_psd_color_mapping_procs(const gx_device * dev)
+{
+ const psd_device *xdev = (const psd_device *)dev;
+
+ if (xdev->color_model == psd_DEVICE_RGB)
+ return &psdRGB_procs;
+ else if (xdev->color_model == psd_DEVICE_CMYK)
+ return &psdCMYK_procs;
+ else if (xdev->color_model == psd_DEVICE_N)
+ return &psdN_procs;
+ else if (xdev->color_model == psd_DEVICE_GRAY)
+ return &psdGray_procs;
+ else
+ return NULL;
+}
+
+/*
+ * Convert a gx_color_index to RGB.
+ */
+static int
+psd_map_color_rgb(gx_device *dev, gx_color_index color, gx_color_value rgb[3])
+{
+ psd_device *xdev = (psd_device *)dev;
+
+ if (xdev->color_model == psd_DEVICE_RGB)
+ return gx_devn_prn_decode_color(dev, color, rgb);
+ /* TODO: return reasonable values. */
+ rgb[0] = 0;
+ rgb[1] = 0;
+ rgb[2] = 0;
+ return 0;
+}
+
+#if ENABLE_ICC_PROFILE
+static int
+psd_open_profile(const char *profile_out_fn, cmm_profile_t *icc_profile, gcmmhlink_t icc_link, gs_memory_t *memory)
+{
+
+ gsicc_rendering_param_t rendering_params;
+
+ icc_profile = gsicc_get_profile_handle_file(profile_out_fn,
+ strlen(profile_out_fn), memory);
+
+ if (icc_profile == NULL)
+ return gs_throw(-1, "Could not create profile for psd device");
+
+ /* Set up the rendering parameters */
+
+ rendering_params.black_point_comp = gsBPNOTSPECIFIED;
+ rendering_params.graphics_type_tag = GS_UNKNOWN_TAG; /* Already rendered */
+ rendering_params.rendering_intent = gsPERCEPTUAL;
+
+ /* Call with a NULL destination profile since we are using a device link profile here */
+ icc_link = gscms_get_link(icc_profile,
+ NULL, &rendering_params);
+
+ if (icc_link == NULL)
+ return gs_throw(-1, "Could not create link handle for psd device");
+
+ return(0);
+
+}
+
+static int
+psd_open_profiles(psd_device *xdev)
+{
+ int code = 0;
+
+ if (xdev->output_icc_link == NULL && xdev->profile_out_fn[0]) {
+
+ code = psd_open_profile(xdev->profile_out_fn, xdev->output_profile,
+ xdev->output_icc_link, xdev->memory);
+
+ }
+
+ if (code >= 0 && xdev->rgb_icc_link == NULL && xdev->profile_rgb_fn[0]) {
+
+ code = psd_open_profile(xdev->profile_rgb_fn, xdev->rgb_profile,
+ xdev->rgb_icc_link, xdev->memory);
+
+ }
+
+ if (code >= 0 && xdev->cmyk_icc_link == NULL && xdev->profile_cmyk_fn[0]) {
+
+ code = psd_open_profile(xdev->profile_cmyk_fn, xdev->cmyk_profile,
+ xdev->cmyk_icc_link, xdev->memory);
+
+ }
+
+ return code;
+
+}
+#endif
+
+/* Get parameters. We provide a default CRD. */
+static int
+psd_get_params(gx_device * pdev, gs_param_list * plist)
+{
+ psd_device *xdev = (psd_device *)pdev;
+ int code;
+#if ENABLE_ICC_PROFILE
+ gs_param_string pos;
+ gs_param_string prgbs;
+ gs_param_string pcmyks;
+#endif
+
+ code = gx_devn_prn_get_params(pdev, plist);
+ if (code < 0)
+ return code;
+
+#if ENABLE_ICC_PROFILE
+ pos.data = (const byte *)xdev->profile_out_fn,
+ pos.size = strlen(xdev->profile_out_fn),
+ pos.persistent = false;
+ code = param_write_string(plist, "ProfileOut", &pos);
+ if (code < 0)
+ return code;
+
+ prgbs.data = (const byte *)xdev->profile_rgb_fn,
+ prgbs.size = strlen(xdev->profile_rgb_fn),
+ prgbs.persistent = false;
+ code = param_write_string(plist, "ProfileRgb", &prgbs);
+ if (code < 0)
+ return code;
+
+ pcmyks.data = (const byte *)xdev->profile_cmyk_fn,
+ pcmyks.size = strlen(xdev->profile_cmyk_fn),
+ pcmyks.persistent = false;
+ code = param_write_string(plist, "ProfileCmyk", &prgbs);
+ if (code < 0)
+ return code;
+#endif
+ code = param_write_long(plist, "DownScaleFactor", &xdev->downscale_factor);
+ if (code < 0)
+ return code;
+ code = param_write_int(plist, "MaxSpots", &xdev->max_spots);
+ if (code < 0)
+ return code;
+ code = param_write_bool(plist, "LockColorants", &xdev->lock_colorants);
+ return code;
+}
+
+#if ENABLE_ICC_PROFILE
+static int
+psd_param_read_fn(gs_param_list *plist, const char *name,
+ gs_param_string *pstr, uint max_len)
+{
+ int code = param_read_string(plist, name, pstr);
+
+ if (code == 0) {
+ if (pstr->size >= max_len)
+ param_signal_error(plist, name, code = gs_error_rangecheck);
+ } else {
+ pstr->data = 0;
+ }
+ return code;
+}
+#endif
+
+/* Compare a C string and a gs_param_string. */
+static bool
+param_string_eq(const gs_param_string *pcs, const char *str)
+{
+ return (strlen(str) == pcs->size &&
+ !strncmp(str, (const char *)pcs->data, pcs->size));
+}
+
+static int
+psd_set_color_model(psd_device *xdev, psd_color_model color_model)
+{
+ xdev->color_model = color_model;
+ if (color_model == psd_DEVICE_GRAY) {
+ xdev->devn_params.std_colorant_names = DeviceGrayComponents;
+ xdev->devn_params.num_std_colorant_names = 1;
+ xdev->color_info.cm_name = "DeviceGray";
+ xdev->color_info.polarity = GX_CINFO_POLARITY_ADDITIVE;
+ } else if (color_model == psd_DEVICE_RGB) {
+ xdev->devn_params.std_colorant_names = DeviceRGBComponents;
+ xdev->devn_params.num_std_colorant_names = 3;
+ xdev->color_info.cm_name = "DeviceRGB";
+ xdev->color_info.polarity = GX_CINFO_POLARITY_ADDITIVE;
+ } else if (color_model == psd_DEVICE_CMYK) {
+ xdev->devn_params.std_colorant_names = DeviceCMYKComponents;
+ xdev->devn_params.num_std_colorant_names = 4;
+ xdev->color_info.cm_name = "DeviceCMYK";
+ xdev->color_info.polarity = GX_CINFO_POLARITY_SUBTRACTIVE;
+ } else if (color_model == psd_DEVICE_N) {
+ xdev->devn_params.std_colorant_names = DeviceCMYKComponents;
+ xdev->devn_params.num_std_colorant_names = 4;
+ xdev->color_info.cm_name = "DeviceN";
+ xdev->color_info.polarity = GX_CINFO_POLARITY_SUBTRACTIVE;
+ } else {
+ return -1;
+ }
+
+ return 0;
+}
+
+/* Set parameters. We allow setting the number of bits per component. */
+static int
+psd_put_params(gx_device * pdev, gs_param_list * plist)
+{
+ psd_device * const pdevn = (psd_device *) pdev;
+ int code = 0;
+#if ENABLE_ICC_PROFILE
+ gs_param_string po;
+ gs_param_string prgb;
+ gs_param_string pcmyk;
+#endif
+ gs_param_string pcm;
+ psd_color_model color_model = pdevn->color_model;
+ gx_device_color_info save_info = pdevn->color_info;
+
+ switch (code = param_read_long(plist,
+ "DownScaleFactor",
+ &pdevn->downscale_factor)) {
+ case 0:
+ if (pdevn->downscale_factor <= 0)
+ pdevn->downscale_factor = 1;
+ break;
+ case 1:
+ break;
+ default:
+ param_signal_error(plist, "DownScaleFactor", code);
+ return code;
+ }
+
+ switch (code = param_read_bool(plist, "LockColorants", &(pdevn->lock_colorants))) {
+ case 0:
+ break;
+ case 1:
+ break;
+ default:
+ param_signal_error(plist, "LockColorants", code);
+ return code;
+ }
+
+ switch (code = param_read_int(plist,
+ "MaxSpots",
+ &pdevn->max_spots)) {
+ case 0:
+ if (pdevn->max_spots >= 0 && pdevn->max_spots <= GS_CLIENT_COLOR_MAX_COMPONENTS-4)
+ break;
+ emprintf1(pdevn->memory, "MaxSpots must be between 0 and %d\n",
+ GS_CLIENT_COLOR_MAX_COMPONENTS-4);
+ code = gs_error_rangecheck;
+ /* fall through */
+ default:
+ param_signal_error(plist, "MaxSpots", code);
+ return code;
+ case 1:
+ break;
+ }
+
+#if ENABLE_ICC_PROFILE
+ code = psd_param_read_fn(plist, "ProfileOut", &po,
+ sizeof(pdevn->profile_out_fn));
+ if (code >= 0)
+ code = psd_param_read_fn(plist, "ProfileRgb", &prgb,
+ sizeof(pdevn->profile_rgb_fn));
+ if (code >= 0)
+ code = psd_param_read_fn(plist, "ProfileCmyk", &pcmyk,
+ sizeof(pdevn->profile_cmyk_fn));
+#endif
+
+ if (code >= 0)
+ code = param_read_name(plist, "ProcessColorModel", &pcm);
+ if (code == 0) {
+ if (param_string_eq (&pcm, "DeviceGray"))
+ color_model = psd_DEVICE_GRAY;
+ else if (param_string_eq (&pcm, "DeviceRGB"))
+ color_model = psd_DEVICE_RGB;
+ else if (param_string_eq (&pcm, "DeviceCMYK"))
+ color_model = psd_DEVICE_CMYK;
+ else if (param_string_eq (&pcm, "DeviceN"))
+ color_model = psd_DEVICE_N;
+ else {
+ param_signal_error(plist, "ProcessColorModel",
+ code = gs_error_rangecheck);
+ }
+ }
+
+ if (code >= 0)
+ code = psd_set_color_model(pdevn, color_model);
+
+ /* handle the standard DeviceN related parameters */
+ if (code == 0)
+ code = gx_devn_prn_put_params(pdev, plist);
+
+ if (code < 0) {
+ pdev->color_info = save_info;
+ return code;
+ }
+
+#if ENABLE_ICC_PROFILE
+ /* Open any ICC profiles that have been specified. */
+ if (po.data != 0) {
+ memcpy(pdevn->profile_out_fn, po.data, po.size);
+ pdevn->profile_out_fn[po.size] = 0;
+ }
+ if (prgb.data != 0) {
+ memcpy(pdevn->profile_rgb_fn, prgb.data, prgb.size);
+ pdevn->profile_rgb_fn[prgb.size] = 0;
+ }
+ if (pcmyk.data != 0) {
+ memcpy(pdevn->profile_cmyk_fn, pcmyk.data, pcmyk.size);
+ pdevn->profile_cmyk_fn[pcmyk.size] = 0;
+ }
+ if (memcmp(&pdevn->color_info, &save_info,
+ size_of(gx_device_color_info)) != 0)
+ code = psd_open_profiles(pdevn);
+#endif
+ return code;
+}
+
+/*
+ * This routine will check to see if the color component name match those
+ * that are available amoung the current device's color components.
+ *
+ * Parameters:
+ * dev - pointer to device data structure.
+ * pname - pointer to name (zero termination not required)
+ * nlength - length of the name
+ *
+ * This routine returns a positive value (0 to n) which is the device colorant
+ * number if the name is found. It returns a negative value if not found.
+ */
+static int
+psd_get_color_comp_index(gx_device * dev, const char * pname,
+ int name_size, int component_type)
+{
+ int index;
+ psd_device *pdev = (psd_device *)dev;
+
+ if (strncmp(pname, "None", name_size) == 0) return -1;
+ index = gx_devn_prn_get_color_comp_index(dev, pname, name_size,
+ component_type);
+ /* This is a one shot deal. That is it will simply post a notice once that
+ some colorants will be converted due to a limit being reached. It will
+ not list names of colorants since then I would need to keep track of
+ which ones I have already mentioned. Also, if someone is fooling with
+ num_order, then this warning is not given since they should know what
+ is going on already */
+ if (index < 0 && component_type == SEPARATION_NAME &&
+ pdev->warning_given == false &&
+ pdev->devn_params.num_separation_order_names == 0) {
+ dmlprintf(dev->memory, "**** Max spot colorants reached.\n");
+ dmlprintf(dev->memory, "**** Some colorants will be converted to equivalent CMYK values.\n");
+ dmlprintf(dev->memory, "**** If this is a Postscript file, try using the -dMaxSpots= option.\n");
+ pdev->warning_given = true;
+ }
+ return index;
+}
+
+/* ------ Private definitions ------ */
+
+/* All two-byte quantities are stored MSB-first! */
+#if arch_is_big_endian
+# define assign_u16(a,v) a = (v)
+# define assign_u32(a,v) a = (v)
+#else
+# define assign_u16(a,v) a = ((v) >> 8) + ((v) << 8)
+# define assign_u32(a,v) a = (((v) >> 24) & 0xff) + (((v) >> 8) & 0xff00) + (((v) & 0xff00) << 8) + (((v) & 0xff) << 24)
+#endif
+
+int
+psd_setup(psd_write_ctx *xc, gx_devn_prn_device *dev, FILE *file, int w, int h)
+{
+ int i;
+ int spot_count;
+
+ xc->f = file;
+
+#define NUM_CMYK_COMPONENTS 4
+ for (i = 0; i < GX_DEVICE_COLOR_MAX_COMPONENTS; i++) {
+ if (dev->devn_params.std_colorant_names[i] == NULL)
+ break;
+ }
+ xc->base_bytes_pp = dev->devn_params.num_std_colorant_names;
+ xc->num_channels = i;
+ if (dev->color_info.polarity == GX_CINFO_POLARITY_SUBTRACTIVE) {
+ if (dev->devn_params.num_separation_order_names == 0) {
+ xc->n_extra_channels = dev->devn_params.separations.num_separations;
+ } else {
+ /* Have to figure out how many in the order list were not std
+ colorants */
+ spot_count = 0;
+ for (i = 0; i < dev->devn_params.num_separation_order_names; i++) {
+ if (dev->devn_params.separation_order_map[i] >= NUM_CMYK_COMPONENTS) {
+ spot_count++;
+ }
+ }
+ xc->n_extra_channels = spot_count;
+ }
+ } else {
+ xc->n_extra_channels = 0;
+ }
+ xc->width = w;
+ xc->height = h;
+ /*
+ * Determine the order of the output components. This is based upon
+ * the SeparationOrder parameter. This parameter can be used to select
+ * which planes are actually imaged. For the process color model channels
+ * we image the channels which are requested. Non requested process color
+ * model channels are simply filled with white. For spot colors we only
+ * image the requested channels.
+ */
+ for (i = 0; i < xc->num_channels + xc->n_extra_channels; i++) {
+ xc->chnl_to_position[i] = i;
+ xc->chnl_to_orig_sep[i] = i;
+ }
+ /* If we had a specify order name, then we may need to adjust things */
+ if (dev->color_info.polarity == GX_CINFO_POLARITY_SUBTRACTIVE) {
+ if (dev->devn_params.num_separation_order_names > 0) {
+ for (i = 0; i < dev->devn_params.num_separation_order_names; i++) {
+ int sep_order_num = dev->devn_params.separation_order_map[i];
+ if (sep_order_num >= NUM_CMYK_COMPONENTS) {
+ xc->chnl_to_position[xc->num_channels] = sep_order_num;
+ xc->chnl_to_orig_sep[xc->num_channels++] = sep_order_num;
+ }
+ }
+ } else {
+ xc->num_channels += dev->devn_params.separations.num_separations;
+ }
+ }
+ return 0;
+}
+
+int
+psd_write(psd_write_ctx *xc, const byte *buf, int size) {
+ int code;
+
+ code = fwrite(buf, 1, size, xc->f);
+ if (code < 0)
+ return code;
+ return 0;
+}
+
+int
+psd_write_8(psd_write_ctx *xc, byte v)
+{
+ return psd_write(xc, (byte *)&v, 1);
+}
+
+int
+psd_write_16(psd_write_ctx *xc, bits16 v)
+{
+ bits16 buf;
+
+ assign_u16(buf, v);
+ return psd_write(xc, (byte *)&buf, 2);
+}
+
+int
+psd_write_32(psd_write_ctx *xc, bits32 v)
+{
+ bits32 buf;
+
+ assign_u32(buf, v);
+ return psd_write(xc, (byte *)&buf, 4);
+}
+
+static fixed_colorant_name
+get_sep_name(gx_devn_prn_device *pdev, int n)
+{
+ fixed_colorant_name p = NULL;
+ int i;
+
+ for (i = 0; i <= n; i++) {
+ p = pdev->devn_params.std_colorant_names[i];
+ if (p == NULL)
+ break;
+ }
+ return p;
+}
+
+int
+psd_write_header(psd_write_ctx *xc, gx_devn_prn_device *pdev)
+{
+ int code = 0;
+ int bytes_pp = xc->num_channels;
+ int chan_idx;
+ int chan_names_len = 0;
+ int sep_num;
+ const devn_separation_name *separation_name;
+
+ psd_write(xc, (const byte *)"8BPS", 4); /* Signature */
+ psd_write_16(xc, 1); /* Version - Always equal to 1*/
+ /* Reserved 6 Bytes - Must be zero */
+ psd_write_32(xc, 0);
+ psd_write_16(xc, 0);
+ psd_write_16(xc, (bits16) bytes_pp); /* Channels (2 Bytes) - Supported range is 1 to 24 */
+ psd_write_32(xc, xc->height); /* Rows */
+ psd_write_32(xc, xc->width); /* Columns */
+ psd_write_16(xc, 8); /* Depth - 1, 8 and 16 */
+ psd_write_16(xc, (bits16) xc->base_bytes_pp); /* Mode - RGB=3, CMYK=4 */
+
+ /* Color Mode Data */
+ psd_write_32(xc, 0); /* No color mode data */
+
+ /* Image Resources */
+
+ /* Channel Names */
+ for (chan_idx = NUM_CMYK_COMPONENTS; chan_idx < xc->num_channels; chan_idx++) {
+ fixed_colorant_name n = pdev->devn_params.std_colorant_names[chan_idx];
+ if (n == NULL)
+ break;
+ chan_names_len += strlen(n) + 1;
+ }
+ for (; chan_idx < xc->num_channels; chan_idx++) {
+ sep_num = xc->chnl_to_orig_sep[chan_idx] - NUM_CMYK_COMPONENTS;
+ separation_name = &(pdev->devn_params.separations.names[sep_num]);
+ chan_names_len += (separation_name->size + 1);
+ }
+ psd_write_32(xc, 12 + (chan_names_len + (chan_names_len % 2))
+ + (12 + (14 * (xc->num_channels - xc->base_bytes_pp)))
+ + 28);
+ psd_write(xc, (const byte *)"8BIM", 4);
+ psd_write_16(xc, 1006); /* 0x03EE */
+ psd_write_16(xc, 0); /* PString */
+ psd_write_32(xc, chan_names_len + (chan_names_len % 2));
+ for (chan_idx = NUM_CMYK_COMPONENTS; chan_idx < xc->num_channels; chan_idx++) {
+ int len;
+ fixed_colorant_name n = pdev->devn_params.std_colorant_names[chan_idx];
+ if (n == NULL)
+ break;
+ len = strlen(n);
+ psd_write_8(xc, (byte)len);
+ psd_write(xc, (const byte *)n, len);
+ }
+ for (; chan_idx < xc->num_channels; chan_idx++) {
+ sep_num = xc->chnl_to_orig_sep[chan_idx] - NUM_CMYK_COMPONENTS;
+ separation_name = &(pdev->devn_params.separations.names[sep_num]);
+ psd_write_8(xc, (byte) separation_name->size);
+ psd_write(xc, separation_name->data, separation_name->size);
+ }
+ if (chan_names_len % 2)
+ psd_write_8(xc, 0); /* pad */
+
+ /* DisplayInfo - Colors for each spot channels */
+ psd_write(xc, (const byte *)"8BIM", 4);
+ psd_write_16(xc, 1007); /* 0x03EF */
+ psd_write_16(xc, 0); /* PString */
+ psd_write_32(xc, 14 * (xc->num_channels - xc->base_bytes_pp)); /* Length */
+ for (chan_idx = NUM_CMYK_COMPONENTS; chan_idx < xc->num_channels; chan_idx++) {
+ sep_num = xc->chnl_to_orig_sep[chan_idx] - NUM_CMYK_COMPONENTS;
+ psd_write_16(xc, 02); /* CMYK */
+ /* PhotoShop stores all component values as if they were additive. */
+ if (pdev->equiv_cmyk_colors.color[sep_num].color_info_valid) {
+#define convert_color(component) ((bits16)((65535 * ((double)\
+ (frac_1 - pdev->equiv_cmyk_colors.color[sep_num].component)) / frac_1)))
+ psd_write_16(xc, convert_color(c)); /* Cyan */
+ psd_write_16(xc, convert_color(m)); /* Magenta */
+ psd_write_16(xc, convert_color(y)); /* Yellow */
+ psd_write_16(xc, convert_color(k)); /* Black */
+#undef convert_color
+ } else {
+ /* This is a bit of a hack, introduced for the psdcmykog device
+ * so that we get a reasonable approximation for the colors out
+ * even when used without the appropriate profile. */
+ fixed_colorant_name sepname = get_sep_name(pdev, chan_idx);
+ if (sepname && !strcmp(sepname, "Artifex Orange")) {
+ psd_write_16(xc, 0xfbde); /* Cyan */
+ psd_write_16(xc, 0x7376); /* Magenta */
+ psd_write_16(xc, 0x0000); /* Yellow */
+ psd_write_16(xc, 0xffff); /* Black */
+ } else if (sepname && !strcmp(sepname, "Artifex Green")) {
+ psd_write_16(xc, 0x0000); /* Cyan */
+ psd_write_16(xc, 0xe33d); /* Magenta */
+ psd_write_16(xc, 0x0000); /* Yellow */
+ psd_write_16(xc, 0xf8c8); /* Black */
+ } else {
+ /* Else set C = M = Y = 0, K = 1 */
+ psd_write_16(xc, 65535); /* Cyan */
+ psd_write_16(xc, 65535); /* Magenta */
+ psd_write_16(xc, 65535); /* Yellow */
+ psd_write_16(xc, 0); /* Black */
+ }
+ }
+ psd_write_16(xc, 0); /* Opacity 0 to 100 */
+ psd_write_8(xc, 2); /* Don't know */
+ psd_write_8(xc, 0); /* Padding - Always Zero */
+ }
+
+ /* Image resolution */
+ psd_write(xc, (const byte *)"8BIM", 4);
+ psd_write_16(xc, 1005); /* 0x03ED */
+ psd_write_16(xc, 0); /* PString */
+ psd_write_32(xc, 16); /* Length */
+ /* Resolution is specified as a fixed 16.16 bits */
+ psd_write_32(xc, (int) (pdev->HWResolution[0] * 0x10000 * xc->width / pdev->width + 0.5));
+ psd_write_16(xc, 1); /* width: 1 --> resolution is pixels per inch */
+ psd_write_16(xc, 1); /* width: 1 --> resolution is pixels per inch */
+ psd_write_32(xc, (int) (pdev->HWResolution[1] * 0x10000 * xc->height / pdev->height + 0.5));
+ psd_write_16(xc, 1); /* height: 1 --> resolution is pixels per inch */
+ psd_write_16(xc, 1); /* height: 1 --> resolution is pixels per inch */
+
+ /* Layer and Mask information */
+ psd_write_32(xc, 0); /* No layer or mask information */
+
+ psd_write_16(xc, 0); /* Compression */
+
+ return code;
+}
+
+/*
+ * Close device and clean up ICC structures.
+ */
+static int
+psd_prn_close(gx_device *dev)
+{
+ psd_device * const xdev = (psd_device *) dev;
+
+ if (xdev->cmyk_icc_link != NULL) {
+ gscms_release_link(xdev->cmyk_icc_link);
+ rc_decrement(xdev->cmyk_profile, "psd_prn_close");
+ }
+
+ if (xdev->rgb_icc_link != NULL) {
+ gscms_release_link(xdev->rgb_icc_link);
+ rc_decrement(xdev->rgb_profile, "psd_prn_close");
+ }
+
+ if (xdev->output_icc_link != NULL) {
+ gscms_release_link(xdev->output_icc_link);
+ rc_decrement(xdev->output_profile, "psd_prn_close");
+ }
+
+ return gdev_prn_close(dev);
+}
+
+/*
+ * Output the image data for the PSD device. The data for the PSD is
+ * written in separate planes. If the device is psdrgb then we simply
+ * write three planes of RGB data. The DeviceN parameters (SeparationOrder,
+ * SeparationCOlorNames, and MaxSeparations) are not applied to the psdrgb
+ * device.
+ *
+ * The DeviceN parameters are applied to the psdcmyk device. If the
+ * SeparationOrder parameter is not specified then first we write out the data
+ * for the CMYK planes and then any separation planes. If the SeparationOrder
+ * parameter is specified, then things are more complicated. Logically we
+ * would simply write the planes specified by the SeparationOrder data.
+ * However Photoshop expects there to be CMYK data. First we will write out
+ * four planes of data for CMYK. If any of these colors are present in the
+ * SeparationOrder data then the plane data will contain the color information.
+ * If a color is not present then the plane data will be zero. After the CMYK
+ * data, we will write out any separation data which is specified in the
+ * SeparationOrder data.
+ */
+
+static int
+psd_write_image_data(psd_write_ctx *xc, gx_device_printer *pdev)
+{
+ int raster_plane = bitmap_raster(pdev->width * 8);
+ byte *planes[GS_CLIENT_COLOR_MAX_COMPONENTS];
+ int code = 0;
+ int i, j;
+ byte *sep_line;
+ int base_bytes_pp = xc->base_bytes_pp;
+ int chan_idx;
+/* psd_device *xdev = (psd_device *)pdev;
+ gcmmhlink_t link = xdev->output_icc_link; */
+ byte * unpacked;
+ int num_comp = xc->num_channels;
+ gs_get_bits_params_t params;
+ gx_downscaler_t ds = { NULL };
+ psd_device *psd_dev = (psd_device *)pdev;
+
+ /* Return planar data */
+ params.options = (GB_RETURN_POINTER | GB_RETURN_COPY |
+ GB_ALIGN_STANDARD | GB_OFFSET_0 | GB_RASTER_STANDARD |
+ GB_PACKING_PLANAR | GB_COLORS_NATIVE | GB_ALPHA_NONE);
+ params.x_offset = 0;
+ params.raster = bitmap_raster(pdev->width * pdev->color_info.depth);
+
+ sep_line = gs_alloc_bytes(pdev->memory, xc->width, "psd_write_sep_line");
+
+ for (chan_idx = 0; chan_idx < num_comp; chan_idx++) {
+ planes[chan_idx] = gs_alloc_bytes(pdev->memory, raster_plane,
+ "psd_write_sep_line");
+ params.data[chan_idx] = planes[chan_idx];
+ if (params.data[chan_idx] == NULL)
+ return_error(gs_error_VMerror);
+ }
+
+ if (sep_line == NULL)
+ return_error(gs_error_VMerror);
+
+ code = gx_downscaler_init_planar(&ds, (gx_device *)pdev, &params, num_comp,
+ psd_dev->downscale_factor, 0, 8, 8);
+ if (code < 0)
+ goto cleanup;
+
+ /* Print the output planes */
+ for (chan_idx = 0; chan_idx < num_comp; chan_idx++) {
+ int data_pos = xc->chnl_to_position[chan_idx];
+ if (data_pos >= 0) {
+ for (j = 0; j < xc->height; ++j) {
+ code = gx_downscaler_get_bits_rectangle(&ds, &params, j);
+ if (code < 0)
+ goto cleanup;
+
+ unpacked = params.data[data_pos];
+ /* To do, get ICC stuff in place for planar device */
+ // if (link == NULL) {
+ if (base_bytes_pp == 3) {
+ /* RGB */
+ memcpy(sep_line, unpacked, xc->width);
+ } else {
+ for (i = 0; i < xc->width; ++i) {
+ /* CMYK + spots*/
+ sep_line[i] = 255 - unpacked[i];
+ }
+ }
+ /* } else {
+ psd_calib_row((gx_device*) xdev, xc, &sep_line, unpacked, data_pos,
+ link, xdev->output_profile->num_comps,
+ xdev->output_profile->num_comps_out);
+ } */
+ psd_write(xc, sep_line, xc->width);
+ }
+ } else {
+ if (chan_idx < NUM_CMYK_COMPONENTS) {
+ /* Write empty process color in the area */
+ memset(sep_line,255,xc->width);
+ psd_write(xc, sep_line, xc->width);
+ }
+ }
+ }
+
+cleanup:
+ gx_downscaler_fin(&ds);
+ gs_free_object(pdev->memory, sep_line, "psd_write_sep_line");
+ for (chan_idx = 0; chan_idx < num_comp; chan_idx++) {
+ gs_free_object(pdev->memory, planes[chan_idx],
+ "psd_write_image_data");
+ }
+ return code;
+}
+
+static int
+psd_print_page(gx_device_printer *pdev, FILE *file)
+{
+ psd_write_ctx xc;
+ gx_devn_prn_device *devn_dev = (gx_devn_prn_device *)pdev;
+ psd_device *psd_dev = (psd_device *)pdev;
+
+ psd_setup(&xc, devn_dev, file,
+ gx_downscaler_scale(pdev->width, psd_dev->downscale_factor),
+ gx_downscaler_scale(pdev->height, psd_dev->downscale_factor));
+ psd_write_header(&xc, devn_dev);
+ psd_write_image_data(&xc, pdev);
+ return 0;
+}
diff --git a/devices/gdevpsim.c b/devices/gdevpsim.c
new file mode 100644
index 000000000..be6dc750c
--- /dev/null
+++ b/devices/gdevpsim.c
@@ -0,0 +1,388 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* PostScript image output device */
+#include "gdevprn.h"
+#include "gdevpsu.h"
+#include "stream.h"
+#include "strimpl.h"
+#include "sa85x.h"
+#include "srlx.h"
+
+/*
+ * There are two drivers in this file, both of which produce PostScript
+ * output consisting of a single bitmap per page. The psmono/psgray
+ * driver produces monochrome Level 1 images using home-grown run length
+ * compression; the psrgb driver produces planar RGB Level 2 images
+ * using the RunLengthEncode filter.
+ */
+
+/* ---------------- Shared code ---------------- */
+
+/* Define the device parameters. */
+#ifndef X_DPI
+# define X_DPI 300
+#endif
+#ifndef Y_DPI
+# define Y_DPI 300
+#endif
+
+/* Write the file (if necessary) and page headers. */
+static void
+ps_image_write_headers(FILE *f, gx_device_printer *pdev,
+ const char *const setup[],
+ gx_device_pswrite_common_t *pdpc)
+{
+ if (gdev_prn_file_is_new(pdev)) {
+ gs_rect bbox;
+
+ bbox.p.x = 0;
+ bbox.p.y = 0;
+ bbox.q.x = pdev->width / pdev->HWResolution[0] * 72.0;
+ bbox.q.y = pdev->height / pdev->HWResolution[1] * 72.0;
+ psw_begin_file_header(f, (gx_device *)pdev, &bbox, pdpc, false);
+ psw_print_lines(f, setup);
+ psw_end_file_header(f);
+ }
+ {
+ byte buf[100]; /* arbitrary */
+ stream s;
+
+ s_init(&s, pdev->memory);
+ swrite_file(&s, f, buf, sizeof(buf));
+ psw_write_page_header(&s, (gx_device *)pdev, pdpc, true, pdev->PageCount + 1, 10);
+ sflush(&s);
+ }
+}
+
+/* ---------------- Level 1 monochrome driver ---------------- */
+
+/*
+ * This driver produces a bitmap in the form of a PostScript file that can
+ * be fed to any PostScript printer. It uses a run-length compression
+ * method that executes quickly (unlike some produced by PostScript
+ * drivers!).
+ *
+ * There are two devices here, one for 1-bit black-and-white and one
+ * for 8-bit gray. In fact, the same code could also handle 2- and
+ * 4-bit gray output.
+ */
+
+/* The device descriptor */
+static const char *const psmono_setup[] = {
+ /* Initialize the strings for filling runs. */
+ "/.ImageFills [ 0 1 255 {",
+ " 256 string dup 0 1 7 { 3 index put dup } for { 8 16 32 64 128 } {",
+ " 2 copy 0 exch getinterval putinterval dup",
+ " } forall pop exch pop",
+ "} bind for ] def",
+ /* Initialize the procedure table for input dispatching. */
+ "/.ImageProcs [",
+ /* Stack: <buffer> <file> <xdigits> <previous> <byte> */
+ " 32 { { pop .ImageItem } } repeat",
+ " 16 { {", /* 0x20-0x2f: (N-0x20) data bytes follow */
+ " 32 sub 3 -1 roll add 3 index exch 0 exch getinterval 2 index exch",
+ " readhexstring pop exch pop 0 exch dup",
+ " } bind } repeat",
+ " 16 { {", /* 0x30-0x3f: prefix hex digit (N-0x30) to next count */
+ " 48 sub 3 -1 roll add 4 bitshift exch .ImageItem",
+ " } bind } repeat",
+ " 32 { {", /* 0x40-0x5f: repeat last data byte (N-0x40) times */
+ " 64 sub 3 -1 roll add .ImageFills 2 index dup length 1 sub get get",
+ " exch 0 exch getinterval 0 3 1 roll",
+ " } bind } repeat",
+ " 160 { { pop .ImageItem } } repeat",
+ "] readonly def",
+ /* Read one item from a compressed image. */
+ /* Stack contents: <buffer> <file> <xdigits> <previous> */
+ "/.ImageItem {",
+ " 2 index read pop dup .ImageProcs exch get exec",
+ "} bind def",
+ /* Read and print an entire compressed image. */
+ "/.ImageRead {" /* <width> <height> <bpc> .ImageRead - */
+ " gsave [",
+ /* Stack: width height bpc -mark- */
+ " 1 0 0 -1 0 7 index",
+ /* Stack: width height bpc -mark- 1 0 0 -1 0 height */
+ " ] { .ImageItem }",
+ /* Stack: width height bpc <matrix> <proc> */
+ " 4 index 3 index mul 7 add 8 idiv string currentfile 0 ()",
+ /* Stack: width height bpc <matrix> <proc> <buffer> <file> 0 () */
+ " 9 4 roll",
+ /* Stack: <buffer> <file> 0 () width height bpc <matrix> <proc> */
+ " image pop pop pop pop grestore",
+ "} def",
+ 0
+};
+static const gx_device_pswrite_common_t psmono_values =
+ PSWRITE_COMMON_VALUES(1, 0 /*false*/, 1);
+
+#define data_run_code 0x20
+#define xdigit_code 0x30
+#define max_data_per_line 35
+#define repeat_run_code 0x40
+#define max_repeat_run_code 31
+#define max_repeat_run 255
+
+/* Send the page to the printer. */
+static void write_data_run(const byte *, int, FILE *, byte);
+static int
+psmono_print_page(gx_device_printer * pdev, FILE * prn_stream)
+{
+ int line_size = gdev_mem_bytes_per_scan_line((gx_device *) pdev);
+ int lnum;
+ byte *line = gs_alloc_bytes(pdev->memory, line_size, "psmono_print_page");
+ byte invert = (pdev->color_info.depth == 1 ? 0xff : 0);
+ gx_device_pswrite_common_t pswrite_common;
+
+ if (line == 0)
+ return_error(gs_error_VMerror);
+ pswrite_common = psmono_values;
+
+ /* If this is the first page of the file, */
+ /* write the setup code. */
+ ps_image_write_headers(prn_stream, pdev, psmono_setup, &pswrite_common);
+
+ /* Write the .ImageRead command. */
+ fprintf(prn_stream,
+ "%d %d %d .ImageRead\n",
+ pdev->width, pdev->height, pdev->color_info.depth);
+
+ /* Compress each scan line in turn. */
+ for (lnum = 0; lnum < pdev->height; lnum++) {
+ const byte *p;
+ int left = line_size;
+ byte *data;
+
+ gdev_prn_get_bits(pdev, lnum, line, &data);
+ p = data;
+ /* Loop invariant: p + left = data + line_size. */
+#define min_repeat_run 10
+ while (left >= min_repeat_run) { /* Detect a maximal run of non-repeated data. */
+ const byte *p1 = p;
+ int left1 = left;
+ byte b;
+ int count, count_left;
+
+ while (left1 >= min_repeat_run &&
+ ((b = *p1) != p1[1] ||
+ b != p1[2] || b != p1[3] || b != p1[4] ||
+ b != p1[5] || b != p1[6] || b != p1[7] ||
+ b != p1[8] || b != p1[9])
+ )
+ ++p1, --left1;
+ if (left1 < min_repeat_run)
+ break; /* no repeated data left */
+ write_data_run(p, (int)(p1 - p + 1), prn_stream,
+ invert);
+ /* Detect a maximal run of repeated data. */
+ p = ++p1 + (min_repeat_run - 1);
+ left = --left1 - (min_repeat_run - 1);
+ while (left > 0 && *p == b)
+ ++p, --left;
+ for (count = p - p1; count > 0;
+ count -= count_left
+ ) {
+ count_left = min(count, max_repeat_run);
+ if (count_left > max_repeat_run_code)
+ fputc(xdigit_code + (count_left >> 4),
+ prn_stream),
+ fputc(repeat_run_code + (count_left & 0xf),
+ prn_stream);
+ else
+ putc(repeat_run_code + count_left,
+ prn_stream);
+ }
+ if (ferror(prn_stream))
+ return_error(gs_error_ioerror);
+ }
+ /* Write the remaining data, if any. */
+ write_data_run(p, left, prn_stream, invert);
+ }
+
+ /* Clean up and return. */
+ fputs("\n", prn_stream);
+ psw_write_page_trailer(prn_stream, 1, true);
+ gs_free_object(pdev->memory, line, "psmono_print_page");
+ if (ferror(prn_stream))
+ return_error(gs_error_ioerror);
+ return 0;
+}
+
+/* Close the file. */
+static int
+psmono_close(gx_device *dev)
+{
+ int code = psw_end_file(((gx_device_printer *)dev)->file, dev,
+ &psmono_values, NULL, dev->PageCount);
+
+ if (code < 0)
+ return code;
+ return gdev_prn_close(dev);
+}
+
+/* Write a run of data on the file. */
+static void
+write_data_run(const byte * data, int count, FILE * f, byte invert)
+{
+ const byte *p = data;
+ const char *const hex_digits = "0123456789abcdef";
+ int left = count;
+ char line[sizeof(count) * 2 + max_data_per_line * 2 + 3];
+ char *q = line;
+
+ /* Write the count. */
+
+ if (!count)
+ return;
+ {
+ int shift = sizeof(count) * 8;
+
+ while ((shift -= 4) > 0 && (count >> shift) == 0);
+ for (; shift > 0; shift -= 4)
+ *q++ = xdigit_code + ((count >> shift) & 0xf);
+ *q++ = data_run_code + (count & 0xf);
+ }
+
+ /* Write the data. */
+
+ while (left > 0) {
+ register int wcount = min(left, max_data_per_line);
+
+ left -= wcount;
+ for (; wcount > 0; ++p, --wcount) {
+ byte b = *p ^ invert;
+
+ *q++ = hex_digits[b >> 4];
+ *q++ = hex_digits[b & 0xf];
+ }
+ *q++ = '\n';
+ fwrite(line, 1, q - line, f);
+ q = line;
+ }
+
+}
+
+/* ---------------- Level 2 RGB driver ---------------- */
+
+/*
+ * This driver produces plane-separated, run-length-encoded, 24-bit RGB
+ * images suitable for a PostScript Level 2 printer. LZW compression would
+ * be better, but Unisys' claim to own the compression algorithm and their
+ * demand for licensing and payment even for freely distributed software
+ * rule this out.
+ */
+static const char *const psrgb_setup[] = {
+ "/rgbimage {", /* <width> <height> rgbimage - */
+ " gsave 2 copy scale /h exch def /w exch def",
+ " /s1 w string def /s2 w string def /s3 w string def",
+ " /f currentfile /ASCII85Decode filter /RunLengthDecode filter def",
+ " w h 8 [w 0 0 h neg 0 h]",
+ " {f s1 readstring pop} {f s2 readstring pop} {f s3 readstring pop}",
+ " true 3 colorimage grestore",
+ "} bind def",
+ 0
+};
+static const gx_device_pswrite_common_t psrgb_values =
+ PSWRITE_COMMON_VALUES(2, 0 /*false*/, 1);
+
+/* Send the page to the printer. */
+static int
+psrgb_print_page(gx_device_printer * pdev, FILE * prn_stream)
+{
+ gs_memory_t *mem = pdev->memory;
+ int width = pdev->width;
+ byte *lbuf = gs_alloc_bytes(mem, width * 3,
+ "psrgb_print_page(lbuf)");
+ int lnum;
+ stream fs, a85s, rls;
+ stream_A85E_state a85state;
+ stream_RLE_state rlstate;
+ byte fsbuf[200]; /* arbitrary, must be >2 */
+ byte a85sbuf[100]; /* arbitrary, must be >=6 */
+ byte rlsbuf[200]; /* arbitrary, must be >128 */
+ gx_device_pswrite_common_t pswrite_common;
+ pswrite_common = psrgb_values;
+
+ if (lbuf == 0)
+ return_error(gs_error_VMerror);
+ ps_image_write_headers(prn_stream, pdev, psrgb_setup, &pswrite_common);
+ fprintf(prn_stream, "%d %d rgbimage\n", width, pdev->height);
+ s_init(&fs, mem);
+ swrite_file(&fs, prn_stream, fsbuf, sizeof(fsbuf));
+ fs.memory = 0;
+
+ if (s_A85E_template.set_defaults)
+ (*s_A85E_template.set_defaults) ((stream_state *) & a85state);
+ s_init(&a85s, mem);
+ s_std_init(&a85s, a85sbuf, sizeof(a85sbuf), &s_filter_write_procs,
+ s_mode_write);
+ a85s.memory = 0;
+ a85state.memory = 0;
+ a85state.templat = &s_A85E_template;
+ (*s_A85E_template.init) ((stream_state *) & a85state);
+ a85s.state = (stream_state *) & a85state;
+ a85s.procs.process = s_A85E_template.process;
+ a85s.strm = &fs;
+
+ (*s_RLE_template.set_defaults) ((stream_state *) & rlstate);
+ s_init(&rls, mem);
+ s_std_init(&rls, rlsbuf, sizeof(rlsbuf), &s_filter_write_procs,
+ s_mode_write);
+ rls.memory = 0;
+ rlstate.memory = 0;
+ rlstate.templat = &s_RLE_template;
+ (*s_RLE_template.init) ((stream_state *) & rlstate);
+ rls.state = (stream_state *) & rlstate;
+ rls.procs.process = s_RLE_template.process;
+ rls.strm = &a85s;
+
+ for (lnum = 0; lnum < pdev->height; ++lnum) {
+ byte *data;
+ int i, c;
+
+ gdev_prn_get_bits(pdev, lnum, lbuf, &data);
+ for (c = 0; c < 3; ++c) {
+ const byte *p;
+
+ for (i = 0, p = data + c; i < width; ++i, p += 3)
+ sputc(&rls, *p);
+ if (rls.end_status == ERRC)
+ return_error(gs_error_ioerror);
+ }
+ }
+ sclose(&rls);
+ sclose(&a85s);
+ sflush(&fs);
+ fputs("\n", prn_stream);
+ psw_write_page_trailer(prn_stream, 1, true);
+ gs_free_object(mem, lbuf, "psrgb_print_page(lbuf)");
+ if (ferror(prn_stream))
+ return_error(gs_error_ioerror);
+ return 0;
+}
+
+/* Close the file. */
+static int
+psrgb_close(gx_device *dev)
+{
+ int code = psw_end_file(((gx_device_printer *)dev)->file, dev,
+ &psrgb_values, NULL, dev->PageCount);
+
+ if (code < 0)
+ return code;
+ return gdev_prn_close(dev);
+}
diff --git a/devices/gdevpxut.c b/devices/gdevpxut.c
new file mode 100644
index 000000000..693afdd4e
--- /dev/null
+++ b/devices/gdevpxut.c
@@ -0,0 +1,434 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Utilities for PCL XL generation */
+#include "math_.h"
+#include "string_.h"
+#include "gx.h"
+#include "stream.h"
+#include "gxdevcli.h"
+#include "gdevpxat.h"
+#include "gdevpxen.h"
+#include "gdevpxop.h"
+#include "gdevpxut.h"
+
+/* ---------------- High-level constructs ---------------- */
+
+/* Write the file header, including the resolution. */
+int
+px_write_file_header(stream *s, const gx_device *dev)
+{
+ static const char *const enter_pjl_header =
+ "\033%-12345X@PJL SET RENDERMODE=";
+ static const char *const rendermode_gray = "GRAYSCALE";
+ static const char *const rendermode_color = "COLOR";
+ static const char *const pjl_resolution =
+ "\n@PJL SET RESOLUTION=";
+ static const char *const resolution_150 = "150";
+ static const char *const resolution_300 = "300";
+ static const char *const resolution_600 = "600";
+ static const char *const resolution_1200 = "1200";
+ static const char *const resolution_2400 = "2400";
+ static const char *const file_header =
+ "\n@PJL ENTER LANGUAGE = PCLXL\n\
+) HP-PCL XL;1;1;Comment Copyright Artifex Sofware, Inc. 2005\000\n";
+ static const byte stream_header[] = {
+ DA(pxaUnitsPerMeasure),
+ DUB(0), DA(pxaMeasure),
+ DUB(eBackChAndErrPage), DA(pxaErrorReport),
+ pxtBeginSession,
+ DUB(0), DA(pxaSourceType),
+ DUB(eBinaryLowByteFirst), DA(pxaDataOrg),
+ pxtOpenDataSource
+ };
+
+ px_put_bytes(s, (const byte *)enter_pjl_header,
+ strlen(enter_pjl_header));
+
+ if (dev->color_info.num_components == 1)
+ px_put_bytes(s, (const byte *)rendermode_gray,
+ strlen(rendermode_gray));
+ else
+ px_put_bytes(s, (const byte *)rendermode_color,
+ strlen(rendermode_color));
+
+ px_put_bytes(s, (const byte *)pjl_resolution,
+ strlen(pjl_resolution));
+
+ if ((uint) (dev->HWResolution[0] + 0.5) == 150)
+ px_put_bytes(s, (const byte *)resolution_150,
+ strlen(resolution_150));
+ else if ((uint) (dev->HWResolution[0] + 0.5) == 300)
+ px_put_bytes(s, (const byte *)resolution_300,
+ strlen(resolution_300));
+ else if ((uint) (dev->HWResolution[0] + 0.5) == 1200)
+ px_put_bytes(s, (const byte *)resolution_1200,
+ strlen(resolution_1200));
+ else if ((uint) (dev->HWResolution[0] + 0.5) == 2400)
+ px_put_bytes(s, (const byte *)resolution_2400,
+ strlen(resolution_2400));
+ else
+ px_put_bytes(s, (const byte *)resolution_600,
+ strlen(resolution_600));
+ if ((uint) (dev->HWResolution[1] + 0.5) !=
+ (uint) (dev->HWResolution[0] + 0.5)) {
+ px_put_bytes(s, (const byte *)"x", strlen("x"));
+ if ((uint) (dev->HWResolution[1] + 0.5) == 150)
+ px_put_bytes(s, (const byte *)resolution_150,
+ strlen(resolution_150));
+ else if ((uint) (dev->HWResolution[1] + 0.5) == 300)
+ px_put_bytes(s, (const byte *)resolution_300,
+ strlen(resolution_300));
+ else if ((uint) (dev->HWResolution[1] + 0.5) == 1200)
+ px_put_bytes(s, (const byte *)resolution_1200,
+ strlen(resolution_1200));
+ else if ((uint) (dev->HWResolution[1] + 0.5) == 2400)
+ px_put_bytes(s, (const byte *)resolution_2400,
+ strlen(resolution_2400));
+ else
+ px_put_bytes(s, (const byte *)resolution_600,
+ strlen(resolution_600));
+ }
+
+ /* We have to add 2 to the strlen because the next-to-last */
+ /* character is a null. */
+ px_put_bytes(s, (const byte *)file_header,
+ strlen(file_header) + 2);
+ px_put_usp(s, (uint) (dev->HWResolution[0] + 0.5),
+ (uint) (dev->HWResolution[1] + 0.5));
+ PX_PUT_LIT(s, stream_header);
+ return 0;
+}
+
+/* Write the page header, including orientation. */
+int
+px_write_page_header(stream *s, const gx_device *dev)
+{
+ /* Orientation is deferred until px_write_select_media... */
+ return 0;
+}
+
+/* Write the media selection command if needed, updating the media size. */
+int
+px_write_select_media(stream *s, const gx_device *dev,
+ pxeMediaSize_t *pms, byte *media_source,
+ int page, bool Duplex, bool Tumble, int media_type_set, char *media_type)
+{
+#define MSD(ms, mstr, res, w, h) \
+ { ms, mstr, (float)((w) * 1.0 / (res)), (float)((h) * 1.0 / res) },
+ static const struct {
+ pxeMediaSize_t ms;
+ const char *media_name;
+ float width, height;
+ } media_sizes[] = {
+ px_enumerate_media(MSD)
+ { pxeMediaSize_next }
+ };
+#undef MSD
+ float w = dev->width / dev->HWResolution[0],
+ h = dev->height / dev->HWResolution[1];
+ int i;
+ pxeMediaSize_t size = eDefaultPaperSize;
+ byte tray = eAutoSelect;
+ byte orientation = ePortraitOrientation;
+ bool match_found = false;
+
+ /* The default is eDefaultPaperSize (=96), but we'll emit CustomMediaSize */
+ /* 0.05 = 30@r600 - one of the test files is 36 off and within 5.0/72@600 */
+ for (i = countof(media_sizes) - 2; i > 0; --i)
+ if (fabs(media_sizes[i].width - w) < 0.05 &&
+ fabs(media_sizes[i].height - h) < 0.05 &&
+ media_sizes[i].ms < 22 /* HP uses up to 21; Ricoh uses 201-224 */
+ ) {
+ match_found = true;
+ size = media_sizes[i].ms;
+ break;
+ } else if (fabs(media_sizes[i].height - w) < 0.05 &&
+ fabs(media_sizes[i].width - h) < 0.05 &&
+ media_sizes[i].ms < 22
+ ) {
+ match_found = true;
+ size = media_sizes[i].ms;
+ orientation = eLandscapeOrientation;
+ break;
+ }
+ /*
+ * According to the PCL XL documentation, MediaSize/CustomMediaSize must always
+ * be specified, but MediaSource is optional.
+ */
+ px_put_uba(s, orientation, pxaOrientation);
+ if (match_found) {
+ /* standard media */
+ px_put_uba(s, (byte)size, pxaMediaSize);
+ } else {
+ /* CustomMediaSize in Inches */
+ px_put_rpa(s, w, h, pxaCustomMediaSize);
+ px_put_uba(s, (byte)eInch, pxaCustomMediaSizeUnits);
+ }
+
+ if (media_source != NULL)
+ tray = *media_source;
+ /* suppress eAutoSelect if type is set */
+ if (!media_type_set || (tray != eAutoSelect))
+ px_put_uba(s, tray, pxaMediaSource);
+ /* suppress empty(="plain") type if tray is non-auto */
+ if (media_type_set)
+ if ((tray == eAutoSelect) || strlen(media_type))
+ px_put_ubaa(s, (const byte *)media_type, strlen(media_type), pxaMediaType);
+
+ if_debug2('|', "duplex %d tumble %d\n", Duplex, Tumble);
+ if (Duplex)
+ {
+ if (Tumble)
+ px_put_uba(s, (byte)eDuplexHorizontalBinding, pxaDuplexPageMode);
+ else
+ px_put_uba(s, (byte)eDuplexVerticalBinding, pxaDuplexPageMode);
+
+ if (page & 1)
+ px_put_uba(s, (byte)eFrontMediaSide, pxaDuplexPageSide);
+ else
+ px_put_uba(s, (byte)eBackMediaSide, pxaDuplexPageSide);
+ }
+ else
+ px_put_uba(s, (byte)eSimplexFrontSide, pxaSimplexPageMode);
+
+ if (pms)
+ *pms = size;
+
+ return 0;
+}
+
+/*
+ * Write the file trailer. Note that this takes a FILE *, not a stream *,
+ * since it may be called after the stream is closed.
+ */
+int
+px_write_file_trailer(FILE *file)
+{
+ static const byte file_trailer[] = {
+ pxtCloseDataSource,
+ pxtEndSession,
+ 033, '%', '-', '1', '2', '3', '4', '5', 'X'
+ };
+
+ fwrite(file_trailer, 1, sizeof(file_trailer), file);
+ return 0;
+}
+
+/* ---------------- Low-level data output ---------------- */
+
+/* Write a sequence of bytes. */
+void
+px_put_bytes(stream * s, const byte * data, uint count)
+{
+ uint used;
+
+ sputs(s, data, count, &used);
+}
+
+/* Utilities for writing data values. */
+/* H-P printers only support little-endian data, so that's what we emit. */
+void
+px_put_a(stream * s, px_attribute_t a)
+{
+ sputc(s, pxt_attr_ubyte);
+ sputc(s, (byte)a);
+}
+void
+px_put_ac(stream *s, px_attribute_t a, px_tag_t op)
+{
+ px_put_a(s, a);
+ sputc(s, (byte)op);
+}
+
+void
+px_put_ub(stream * s, byte b)
+{
+ sputc(s, pxt_ubyte);
+ sputc(s, b);
+}
+void
+px_put_uba(stream *s, byte b, px_attribute_t a)
+{
+ px_put_ub(s, b);
+ px_put_a(s, a);
+}
+
+void
+px_put_s(stream * s, int i)
+{
+ sputc(s, (byte) i);
+ if (i < 0)
+ i |= 0x8000;
+ sputc(s, (byte) (i >> 8));
+}
+void
+px_put_us(stream * s, uint i)
+{
+ sputc(s, pxt_uint16);
+ px_put_s(s, i);
+}
+void
+px_put_usa(stream *s, uint i, px_attribute_t a)
+{
+ px_put_us(s, i);
+ px_put_a(s, a);
+}
+void
+px_put_u(stream * s, uint i)
+{
+ if (i <= 255)
+ px_put_ub(s, (byte)i);
+ else
+ px_put_us(s, i);
+}
+
+void
+px_put_usp(stream * s, uint ix, uint iy)
+{
+ spputc(s, pxt_uint16_xy);
+ px_put_s(s, ix);
+ px_put_s(s, iy);
+}
+void
+px_put_usq_fixed(stream * s, fixed x0, fixed y0, fixed x1, fixed y1)
+{
+ spputc(s, pxt_uint16_box);
+ px_put_s(s, fixed2int(x0));
+ px_put_s(s, fixed2int(y0));
+ px_put_s(s, fixed2int(x1));
+ px_put_s(s, fixed2int(y1));
+}
+
+void
+px_put_ss(stream * s, int i)
+{
+ sputc(s, pxt_sint16);
+ px_put_s(s, i);
+}
+void
+px_put_ssp(stream * s, int ix, int iy)
+{
+ sputc(s, pxt_sint16_xy);
+ px_put_s(s, ix);
+ px_put_s(s, iy);
+}
+
+void
+px_put_l(stream * s, ulong l)
+{
+ sputc(s, (byte) l);
+ sputc(s, (byte) (l >> 8));
+ sputc(s, (byte) (l >> 16));
+ sputc(s, (byte) (l >> 24));
+}
+
+/*
+ The single-precison IEEE float is represented with 32-bit as follows:
+
+ 1 8 23
+ sign | exponent | matissa_bits
+
+ switch(exponent):
+ case 0:
+ (-1)^sign * 2^(-126) * 0.<matissa_bits>_base2
+ case 0xFF:
+ +- infinity
+ default: (0x01 - 0xFE)
+ (-1)^sign * 2^(exponent - 127) * 1.<matissa_bits>_base2
+
+ The "1." part is not coded since it is always "1.".
+
+ To uses frexp, which returns
+ 0.<matissa_bits>_base2 * 2^exp
+ We need to think of it as:
+ 1.<matissa_bits,drop_MSB>_base2 * 2^(exp-1)
+
+ 2009: the older version of this code has always been wrong (since 2000),
+ missing the -1 (the number was wrong by a factor of 2). Checked against
+ inserting hexdump code and compared with python snipplets (and pxldis):
+
+ import struct
+ for x in struct.pack("f", number):
+ hex(ord(x))
+*/
+
+void
+px_put_r(stream * s, double r)
+{ /* Convert to single-precision IEEE float. */
+ int exp;
+ long mantissa = (long)(frexp(r, &exp) * 0x1000000);
+
+ /* we can go a bit lower than -126 and represent:
+ 2^(-126) * 0.[22 '0' then '1']_base2 = 2 ^(146) * 0.1_base2
+ but it is simplier for such small number to be zero. */
+ if (exp < -126)
+ mantissa = 0, exp = 0; /* unnormalized */
+ /* put the sign bit in the right place */
+ if (mantissa < 0)
+ exp += 128, mantissa = -mantissa;
+ /* All quantities are little-endian. */
+ spputc(s, (byte) mantissa);
+ spputc(s, (byte) (mantissa >> 8));
+ spputc(s, (byte) (((exp + 126) << 7) + ((mantissa >> 16) & 0x7f)));
+ spputc(s, (byte) ((exp + 126) >> 1));
+}
+void
+px_put_rl(stream * s, double r)
+{
+ spputc(s, pxt_real32);
+ px_put_r(s, r);
+}
+
+void
+px_put_rp(stream * s, double rx, double ry)
+{
+ spputc(s, pxt_real32_xy);
+ px_put_r(s, rx);
+ px_put_r(s, ry);
+}
+
+void
+px_put_rpa(stream * s, double rx, double ry, px_attribute_t a)
+{
+ px_put_rp(s, rx, ry);
+ px_put_a(s, a);
+}
+
+/* ubyte_array with attribute */
+void
+px_put_ubaa(stream * s, const byte * data, uint count, px_attribute_t a)
+{
+ if (count < 0)
+ return;
+ spputc(s, pxt_ubyte_array);
+ /* uint16 LE length field */
+ px_put_us(s, count);
+ px_put_bytes(s, data, count);
+ px_put_a(s, a);
+}
+
+void
+px_put_data_length(stream * s, uint num_bytes)
+{
+ if (num_bytes > 255) {
+ spputc(s, pxt_dataLength);
+ px_put_l(s, (ulong) num_bytes);
+ } else {
+ spputc(s, pxt_dataLengthByte);
+ spputc(s, (byte) num_bytes);
+ }
+}
diff --git a/devices/gdevpxut.h b/devices/gdevpxut.h
new file mode 100644
index 000000000..77007f981
--- /dev/null
+++ b/devices/gdevpxut.h
@@ -0,0 +1,89 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Utilities for PCL XL generation */
+/* Requires gdevpxat.h, gdevpxen.h, gdevpxop.h */
+
+#ifndef gdevpxut_INCLUDED
+# define gdevpxut_INCLUDED
+
+/* ---------------- High-level constructs ---------------- */
+
+/* Write the file header, including the resolution. */
+int px_write_file_header(stream *s, const gx_device *dev);
+
+/* Write the page header, including orientation. */
+int px_write_page_header(stream *s, const gx_device *dev);
+
+/* Write the media selection command if needed, updating the media size. */
+int px_write_select_media(stream *s, const gx_device *dev,
+ pxeMediaSize_t *pms,
+ byte *media_source,
+ int page, bool Duplex, bool Tumble,
+ int media_type_set, char *media_type);
+
+/*
+ * Write the file trailer. Note that this takes a FILE *, not a stream *,
+ * since it may be called after the stream is closed.
+ */
+int px_write_file_trailer(FILE *file);
+
+/* ---------------- Low-level data output ---------------- */
+
+/* Write a sequence of bytes. */
+#define PX_PUT_LIT(s, bytes) px_put_bytes(s, bytes, sizeof(bytes))
+void px_put_bytes(stream * s, const byte * data, uint count);
+
+/* Utilities for writing data values. */
+/* H-P printers only support little-endian data, so that's what we emit. */
+
+#define DA(a) pxt_attr_ubyte, (a)
+void px_put_a(stream * s, px_attribute_t a);
+void px_put_ac(stream *s, px_attribute_t a, px_tag_t op);
+
+#define DUB(b) pxt_ubyte, (byte)(b)
+void px_put_ub(stream * s, byte b);
+void px_put_uba(stream *s, byte b, px_attribute_t a);
+
+/* signed and unsigned shorts */
+#define DS(i) (byte)(i), (byte)(((i) >= 0 ? (i) : ((i)|0x8000)) >> 8)
+#define US(i) (byte)(i), (byte)((i) >> 8)
+void px_put_s(stream * s, int i);
+
+#define DUS(i) pxt_uint16, US(i)
+void px_put_us(stream * s, uint i);
+void px_put_usa(stream *s, uint i, px_attribute_t a);
+void px_put_u(stream * s, uint i);
+
+#define DUSP(ix,iy) pxt_uint16_xy, US(ix), US(iy)
+void px_put_usp(stream * s, uint ix, uint iy);
+void px_put_usq_fixed(stream * s, fixed x0, fixed y0, fixed x1, fixed y1);
+
+void px_put_ss(stream * s, int i);
+void px_put_ssp(stream * s, int ix, int iy);
+
+void px_put_l(stream * s, ulong l);
+
+void px_put_r(stream * s, double r); /* no tag */
+void px_put_rl(stream * s, double r); /* pxt_real32 tag */
+void px_put_rp(stream * s, double rx, double ry);
+void px_put_rpa(stream * s, double rx, double ry, px_attribute_t a);
+
+void px_put_ubaa(stream * s, const byte * data, uint count, px_attribute_t a);
+
+void px_put_data_length(stream * s, uint num_bytes);
+
+#endif /* gdevpxut_INCLUDED */
diff --git a/devices/gdevrinkj.c b/devices/gdevrinkj.c
new file mode 100644
index 000000000..f55bc603e
--- /dev/null
+++ b/devices/gdevrinkj.c
@@ -0,0 +1,1202 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Support for rinkj (resplendent inkjet) drivers. */
+
+#include "math_.h"
+#include "gdevprn.h"
+#include "gsparam.h"
+#include "gscrd.h"
+#include "gscrdp.h"
+#include "gxlum.h"
+#include "gdevdcrd.h"
+#include "gstypes.h"
+#include "gxdcconv.h"
+#include "gscms.h"
+#include "gsicc_cache.h"
+#include "gsicc_manage.h"
+
+#ifndef cmm_gcmmhlink_DEFINED
+ #define cmm_gcmmhlink_DEFINED
+ typedef void* gcmmhlink_t;
+#endif
+
+#ifndef cmm_gcmmhprofile_DEFINED
+ #define cmm_gcmmhprofile_DEFINED
+ typedef void* gcmmhprofile_t;
+#endif
+
+#include "rinkj/rinkj-device.h"
+#include "rinkj/rinkj-byte-stream.h"
+#include "rinkj/rinkj-screen-eb.h"
+#include "rinkj/rinkj-epson870.h"
+
+#ifndef MAX_CHAN
+# define MAX_CHAN 15
+#endif
+
+/* Define the device parameters. */
+#ifndef X_DPI
+# define X_DPI 720
+#endif
+#ifndef Y_DPI
+# define Y_DPI 720
+#endif
+
+/* The device descriptor */
+static dev_proc_get_params(rinkj_get_params);
+static dev_proc_close_device(rinkj_close_device);
+static dev_proc_put_params(rinkj_put_params);
+static dev_proc_print_page(rinkj_print_page);
+static dev_proc_map_color_rgb(rinkj_map_color_rgb);
+static dev_proc_get_color_mapping_procs(get_rinkj_color_mapping_procs);
+static dev_proc_get_color_comp_index(rinkj_get_color_comp_index);
+static dev_proc_encode_color(rinkj_encode_color);
+static dev_proc_decode_color(rinkj_decode_color);
+
+/*
+ * Type definitions associated with the fixed color model names.
+ */
+typedef const char * fixed_colorant_name;
+typedef fixed_colorant_name fixed_colorant_names_list[];
+
+/*
+ * Structure for holding SeparationNames and SeparationOrder elements.
+ */
+typedef struct gs_separation_names_s {
+ int num_names;
+ const gs_param_string * names[GX_DEVICE_COLOR_MAX_COMPONENTS];
+} gs_separation_names;
+
+/* This is redundant with color_info.cm_name. We may eliminate this
+ typedef and use the latter string for everything. */
+typedef enum {
+ RINKJ_DEVICE_GRAY,
+ RINKJ_DEVICE_RGB,
+ RINKJ_DEVICE_CMYK,
+ RINKJ_DEVICE_N
+} rinkj_color_model;
+
+/*
+ * A structure definition for a DeviceN type device
+ */
+typedef struct rinkj_device_s {
+ gx_device_common;
+ gx_prn_device_common;
+
+ /* ... device-specific parameters ... */
+
+ rinkj_color_model color_model;
+
+ /*
+ * Bits per component (device colorant). Currently only 1 and 8 are
+ * supported.
+ */
+ int bitspercomponent;
+ int n_planes_out; /* actual number of channels in device */
+
+ /*
+ * Pointer to the colorant names for the color model. This will be
+ * null if we have DeviceN type device. The actual possible colorant
+ * names are those in this list plus those in the separation_names
+ * list (below).
+ */
+ const fixed_colorant_names_list * std_colorant_names;
+ int num_std_colorant_names; /* Number of names in list */
+
+ /*
+ * Separation names (if any).
+ */
+ gs_separation_names separation_names;
+
+ /*
+ * Separation Order (if specified).
+ */
+ gs_separation_names separation_order;
+
+ /* ICC color profile objects, for color conversion. */
+ char profile_out_fn[256];
+
+ /* This device can use a device link ICC profile to map
+ the colors to the appropriate color space. Not
+ as flexible as having source and destination profiles
+ and creating the link on the fly, but I am doing
+ the minimal changes on this device to make it work
+ with the new ICC architecture. No optimizations yet. */
+
+ gcmmhlink_t icc_link;
+ cmm_profile_t *link_profile;
+
+ char setup_fn[256];
+} rinkj_device;
+
+/*
+ * Macro definition for DeviceN procedures
+ */
+#define device_procs(get_color_mapping_procs)\
+{ gdev_prn_open,\
+ gx_default_get_initial_matrix,\
+ NULL, /* sync_output */\
+ /* Since the print_page doesn't alter the device, this device can print in the background */\
+ gdev_prn_bg_output_page, /* output_page */\
+ rinkj_close_device, /* close */\
+ NULL, /* map_rgb_color - not used */\
+ rinkj_map_color_rgb, /* map_color_rgb */\
+ NULL, /* fill_rectangle */\
+ NULL, /* tile_rectangle */\
+ NULL, /* copy_mono */\
+ NULL, /* copy_color */\
+ NULL, /* draw_line */\
+ NULL, /* get_bits */\
+ rinkj_get_params, /* get_params */\
+ rinkj_put_params, /* put_params */\
+ NULL, /* map_cmyk_color - not used */\
+ NULL, /* get_xfont_procs */\
+ NULL, /* get_xfont_device */\
+ NULL, /* map_rgb_alpha_color */\
+ gx_page_device_get_page_device, /* get_page_device */\
+ NULL, /* get_alpha_bits */\
+ NULL, /* copy_alpha */\
+ NULL, /* get_band */\
+ NULL, /* copy_rop */\
+ NULL, /* fill_path */\
+ NULL, /* stroke_path */\
+ NULL, /* fill_mask */\
+ NULL, /* fill_trapezoid */\
+ NULL, /* fill_parallelogram */\
+ NULL, /* fill_triangle */\
+ NULL, /* draw_thin_line */\
+ NULL, /* begin_image */\
+ NULL, /* image_data */\
+ NULL, /* end_image */\
+ NULL, /* strip_tile_rectangle */\
+ NULL, /* strip_copy_rop */\
+ NULL, /* get_clipping_box */\
+ NULL, /* begin_typed_image */\
+ NULL, /* get_bits_rectangle */\
+ NULL, /* map_color_rgb_alpha */\
+ NULL, /* create_compositor */\
+ NULL, /* get_hardware_params */\
+ NULL, /* text_begin */\
+ NULL, /* finish_copydevice */\
+ NULL, /* begin_transparency_group */\
+ NULL, /* end_transparency_group */\
+ NULL, /* begin_transparency_mask */\
+ NULL, /* end_transparency_mask */\
+ NULL, /* discard_transparency_layer */\
+ get_color_mapping_procs, /* get_color_mapping_procs */\
+ rinkj_get_color_comp_index, /* get_color_comp_index */\
+ rinkj_encode_color, /* encode_color */\
+ rinkj_decode_color /* decode_color */\
+}
+
+static const fixed_colorant_names_list DeviceGrayComponents = {
+ "Gray",
+ 0 /* List terminator */
+};
+
+static const fixed_colorant_names_list DeviceRGBComponents = {
+ "Red",
+ "Green",
+ "Blue",
+ 0 /* List terminator */
+};
+
+static const fixed_colorant_names_list DeviceCMYKComponents = {
+ "Cyan",
+ "Magenta",
+ "Yellow",
+ "Black",
+ 0 /* List terminator */
+};
+
+static const gx_device_procs spot_cmyk_procs = device_procs(get_rinkj_color_mapping_procs);
+
+const rinkj_device gs_rinkj_device =
+{
+ prn_device_body_extended(rinkj_device, spot_cmyk_procs, "rinkj",
+ DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
+ X_DPI, Y_DPI, /* X and Y hardware resolution */
+ 0, 0, 0, 0, /* margins */
+ GX_DEVICE_COLOR_MAX_COMPONENTS, 4, /* MaxComponents, NumComp */
+ GX_CINFO_POLARITY_SUBTRACTIVE, /* Polarity */
+ 32, 0, /* Depth, Gray_index, */
+ 255, 255, 1, 1, /* MaxGray, MaxColor, DitherGray, DitherColor */
+ GX_CINFO_SEP_LIN, /* Linear & Separable */
+ "DeviceN", /* Process color model name */
+ rinkj_print_page), /* Printer page print routine */
+ /* DeviceN device specific parameters */
+ RINKJ_DEVICE_CMYK, /* Color model */
+ 8, /* Bits per color - must match ncomp, depth, etc. above */
+ 4, /* Number of output color planes, overwritten below. */
+ (&DeviceCMYKComponents), /* Names of color model colorants */
+ 4, /* Number colorants for CMYK */
+ {0}, /* SeparationNames */
+ {0} /* SeparationOrder names */
+};
+
+/*
+ * The following procedures are used to map the standard color spaces into
+ * the color components for the spotrgb device.
+ */
+static void
+gray_cs_to_spotrgb_cm(gx_device * dev, frac gray, frac out[])
+{
+/* TO_DO_DEVICEN This routine needs to include the effects of the SeparationOrder array */
+ int i = ((rinkj_device *)dev)->separation_names.num_names;
+
+ out[0] = out[1] = out[2] = gray;
+ for(; i>0; i--) /* Clear spot colors */
+ out[2 + i] = 0;
+}
+
+static void
+rgb_cs_to_spotrgb_cm(gx_device * dev, const gs_imager_state *pis,
+ frac r, frac g, frac b, frac out[])
+{
+/* TO_DO_DEVICEN This routine needs to include the effects of the SeparationOrder array */
+ int i = ((rinkj_device *)dev)->separation_names.num_names;
+
+ out[0] = r;
+ out[1] = g;
+ out[2] = b;
+ for(; i>0; i--) /* Clear spot colors */
+ out[2 + i] = 0;
+}
+
+static void
+cmyk_cs_to_spotrgb_cm(gx_device * dev, frac c, frac m, frac y, frac k, frac out[])
+{
+/* TO_DO_DEVICEN This routine needs to include the effects of the SeparationOrder array */
+ int i = ((rinkj_device *)dev)->separation_names.num_names;
+
+ color_cmyk_to_rgb(c, m, y, k, NULL, out, dev->memory);
+ for(; i>0; i--) /* Clear spot colors */
+ out[2 + i] = 0;
+}
+
+static void
+gray_cs_to_spotcmyk_cm(gx_device * dev, frac gray, frac out[])
+{
+/* TO_DO_DEVICEN This routine needs to include the effects of the SeparationOrder array */
+ int i = ((rinkj_device *)dev)->separation_names.num_names;
+
+ out[0] = out[1] = out[2] = 0;
+ out[3] = frac_1 - gray;
+ for(; i>0; i--) /* Clear spot colors */
+ out[3 + i] = 0;
+}
+
+static void
+rgb_cs_to_spotcmyk_cm(gx_device * dev, const gs_imager_state *pis,
+ frac r, frac g, frac b, frac out[])
+{
+/* TO_DO_DEVICEN This routine needs to include the effects of the SeparationOrder array */
+ rinkj_device *rdev = (rinkj_device *)dev;
+ int n = rdev->separation_names.num_names;
+ int i;
+
+ color_rgb_to_cmyk(r, g, b, pis, out, dev->memory);
+ for(i = 0; i < n; i++) /* Clear spot colors */
+ out[4 + i] = 0;
+}
+
+static void
+cmyk_cs_to_spotcmyk_cm(gx_device * dev, frac c, frac m, frac y, frac k, frac out[])
+{
+/* TO_DO_DEVICEN This routine needs to include the effects of the SeparationOrder array */
+ rinkj_device *rdev = (rinkj_device *)dev;
+ int n = rdev->separation_names.num_names;
+ int i;
+
+ out[0] = c;
+ out[1] = m;
+ out[2] = y;
+ out[3] = k;
+ for(i = 0; i < n; i++) /* Clear spot colors */
+ out[4 + i] = 0;
+};
+
+static void
+cmyk_cs_to_spotn_cm(gx_device * dev, frac c, frac m, frac y, frac k, frac out[])
+{
+/* TO_DO_DEVICEN This routine needs to include the effects of the SeparationOrder array */
+ rinkj_device *rdev = (rinkj_device *)dev;
+ int n = rdev->separation_names.num_names;
+ int i;
+
+ /* If no profile given, assume CMYK */
+ out[0] = c;
+ out[1] = m;
+ out[2] = y;
+ out[3] = k;
+ for(i = 0; i < n; i++) /* Clear spot colors */
+ out[4 + i] = 0;
+};
+
+static void
+gray_cs_to_spotn_cm(gx_device * dev, frac gray, frac out[])
+{
+/* TO_DO_DEVICEN This routine needs to include the effects of the SeparationOrder array */
+
+ cmyk_cs_to_spotn_cm(dev, 0, 0, 0, frac_1 - gray, out);
+}
+
+static void
+rgb_cs_to_spotn_cm(gx_device * dev, const gs_imager_state *pis,
+ frac r, frac g, frac b, frac out[])
+{
+/* TO_DO_DEVICEN This routine needs to include the effects of the SeparationOrder array */
+ frac cmyk[4];
+
+ color_rgb_to_cmyk(r, g, b, pis, cmyk, dev->memory);
+ cmyk_cs_to_spotn_cm(dev, cmyk[0], cmyk[1], cmyk[2], cmyk[3],
+ out);
+}
+
+static const gx_cm_color_map_procs spotRGB_procs = {
+ gray_cs_to_spotrgb_cm, rgb_cs_to_spotrgb_cm, cmyk_cs_to_spotrgb_cm
+};
+
+static const gx_cm_color_map_procs spotCMYK_procs = {
+ gray_cs_to_spotcmyk_cm, rgb_cs_to_spotcmyk_cm, cmyk_cs_to_spotcmyk_cm
+};
+
+static const gx_cm_color_map_procs spotN_procs = {
+ gray_cs_to_spotn_cm, rgb_cs_to_spotn_cm, cmyk_cs_to_spotn_cm
+};
+
+/*
+ * These are the handlers for returning the list of color space
+ * to color model conversion routines.
+ */
+
+static const gx_cm_color_map_procs *
+get_rinkj_color_mapping_procs(const gx_device * dev)
+{
+ const rinkj_device *rdev = (const rinkj_device *)dev;
+
+ if (rdev->color_model == RINKJ_DEVICE_RGB)
+ return &spotRGB_procs;
+ else if (rdev->color_model == RINKJ_DEVICE_CMYK)
+ return &spotCMYK_procs;
+ else if (rdev->color_model == RINKJ_DEVICE_N)
+ return &spotN_procs;
+ else
+ return NULL;
+}
+
+/*
+ * Encode a list of colorant values into a gx_color_index_value.
+ */
+static gx_color_index
+rinkj_encode_color(gx_device *dev, const gx_color_value colors[])
+{
+ int bpc = ((rinkj_device *)dev)->bitspercomponent;
+ gx_color_index color = 0;
+ int i = 0;
+ int ncomp = dev->color_info.num_components;
+ COLROUND_VARS;
+
+ COLROUND_SETUP(bpc);
+ for (; i<ncomp; i++) {
+ color <<= bpc;
+ color |= COLROUND_ROUND(colors[i]);
+ }
+ return (color == gx_no_color_index ? color ^ 1 : color);
+}
+
+/*
+ * Decode a gx_color_index value back to a list of colorant values.
+ */
+static int
+rinkj_decode_color(gx_device * dev, gx_color_index color, gx_color_value * out)
+{
+ int bpc = ((rinkj_device *)dev)->bitspercomponent;
+ int drop = sizeof(gx_color_value) * 8 - bpc;
+ int mask = (1 << bpc) - 1;
+ int i = 0;
+ int ncomp = dev->color_info.num_components;
+
+ for (; i<ncomp; i++) {
+ out[ncomp - i - 1] = (color & mask) << drop;
+ color >>= bpc;
+ }
+ return 0;
+}
+
+/*
+ * Convert a gx_color_index to RGB.
+ */
+static int
+rinkj_map_color_rgb(gx_device *dev, gx_color_index color, gx_color_value rgb[3])
+{
+ rinkj_device *rdev = (rinkj_device *)dev;
+
+ if (rdev->color_model == RINKJ_DEVICE_RGB)
+ return rinkj_decode_color(dev, color, rgb);
+ /* TODO: return reasonable values. */
+ rgb[0] = 0;
+ rgb[1] = 0;
+ rgb[2] = 0;
+ return 0;
+}
+
+static int
+rinkj_open_profile(rinkj_device *rdev)
+{
+ gsicc_rendering_param_t rendering_params;
+
+ if (rdev->link_profile == NULL && rdev->profile_out_fn[0]) {
+
+ rdev->link_profile = gsicc_get_profile_handle_file(rdev->profile_out_fn,
+ strlen(rdev->profile_out_fn), rdev->memory);
+
+ if (rdev->link_profile == NULL)
+ return gs_throw(-1, "Could not create output profile for rinkj device");
+
+ /* Set up the rendering parameters */
+
+ rendering_params.black_point_comp = gsBPNOTSPECIFIED;
+ rendering_params.graphics_type_tag = GS_UNKNOWN_TAG; /* Already rendered */
+ rendering_params.rendering_intent = gsPERCEPTUAL;
+
+ /* Call with a NULL destination profile since we are using a device link profile here */
+ rdev->icc_link = gscms_get_link(rdev->link_profile,
+ NULL, &rendering_params, 0, rdev->memory);
+
+ if (rdev->icc_link == NULL)
+ return gs_throw(-1, "Could not create link handle for rinkj device");
+
+ }
+ return(0);
+}
+
+#define set_param_array(a, d, s)\
+ (a.data = d, a.size = s, a.persistent = false);
+
+/* Get parameters. We provide a default CRD. */
+static int
+rinkj_get_params(gx_device * pdev, gs_param_list * plist)
+{
+ rinkj_device *rdev = (rinkj_device *)pdev;
+ int code;
+ bool seprs = false;
+ gs_param_string_array scna;
+ gs_param_string pos;
+ gs_param_string sfs;
+
+ set_param_array(scna, NULL, 0);
+
+ if ( (code = gdev_prn_get_params(pdev, plist)) < 0 ||
+ (code = sample_device_crd_get_params(pdev, plist, "CRDDefault")) < 0 ||
+ (code = param_write_name_array(plist, "SeparationColorNames", &scna)) < 0 ||
+ (code = param_write_bool(plist, "Separations", &seprs)) < 0)
+ return code;
+
+ pos.data = (const byte *)rdev->profile_out_fn,
+ pos.size = strlen(rdev->profile_out_fn),
+ pos.persistent = false;
+ code = param_write_string(plist, "ProfileOut", &pos);
+ if (code < 0)
+ return code;
+
+ sfs.data = (const byte *)rdev->setup_fn,
+ sfs.size = strlen(rdev->setup_fn),
+ sfs.persistent = false;
+ code = param_write_string(plist, "SetupFile", &sfs);
+
+ return code;
+}
+#undef set_param_array
+
+#define compare_color_names(name, name_size, str, str_size) \
+ (name_size == str_size && \
+ (strncmp((const char *)name, (const char *)str, name_size) == 0))
+
+/*
+ * This routine will check if a name matches any item in a list of process model
+ * color component names.
+ */
+static bool
+check_process_color_names(const fixed_colorant_names_list * pcomp_list,
+ const gs_param_string * pstring)
+{
+ if (pcomp_list) {
+ const fixed_colorant_name * plist = *pcomp_list;
+ uint size = pstring->size;
+
+ while( *plist) {
+ if (compare_color_names(*plist, strlen(*plist), pstring->data, size)) {
+ return true;
+ }
+ plist++;
+ }
+ }
+ return false;
+}
+
+/*
+ * This utility routine calculates the number of bits required to store
+ * color information. In general the values are rounded up to an even
+ * byte boundary except those cases in which mulitple pixels can evenly
+ * into a single byte.
+ *
+ * The parameter are:
+ * ncomp - The number of components (colorants) for the device. Valid
+ * values are 1 to GX_DEVICE_COLOR_MAX_COMPONENTS
+ * bpc - The number of bits per component. Valid values are 1, 2, 4, 5,
+ * and 8.
+ * Input values are not tested for validity.
+ */
+static int
+bpc_to_depth(int ncomp, int bpc)
+{
+ static const byte depths[4][8] = {
+ {1, 2, 0, 4, 8, 0, 0, 8},
+ {2, 4, 0, 8, 16, 0, 0, 16},
+ {4, 8, 0, 16, 16, 0, 0, 24},
+ {4, 8, 0, 16, 32, 0, 0, 32}
+ };
+
+ if (ncomp <=4 && bpc <= 8)
+ return depths[ncomp -1][bpc-1];
+ else
+ return (ncomp * bpc + 7) & ~7;
+}
+
+#define BEGIN_ARRAY_PARAM(pread, pname, pa, psize, e)\
+ BEGIN\
+ switch (code = pread(plist, (param_name = pname), &(pa))) {\
+ case 0:\
+ if ((pa).size != psize) {\
+ ecode = gs_note_error(gs_error_rangecheck);\
+ (pa).data = 0; /* mark as not filled */\
+ } else
+#define END_ARRAY_PARAM(pa, e)\
+ goto e;\
+ default:\
+ ecode = code;\
+e: param_signal_error(plist, param_name, ecode);\
+ case 1:\
+ (pa).data = 0; /* mark as not filled */\
+ }\
+ END
+
+static int
+rinkj_param_read_fn(gs_param_list *plist, const char *name,
+ gs_param_string *pstr, int max_len)
+{
+ int code = param_read_string(plist, name, pstr);
+
+ if (code == 0) {
+ if (pstr->size >= max_len)
+ param_signal_error(plist, name, code = gs_error_rangecheck);
+ } else {
+ pstr->data = 0;
+ }
+ return code;
+}
+
+/* Compare a C string and a gs_param_string. */
+static bool
+param_string_eq(const gs_param_string *pcs, const char *str)
+{
+ return (strlen(str) == pcs->size &&
+ !strncmp(str, (const char *)pcs->data, pcs->size));
+}
+
+static int
+rinkj_set_color_model(rinkj_device *rdev, rinkj_color_model color_model)
+{
+ int bpc = 8;
+
+ rdev->color_model = color_model;
+ if (color_model == RINKJ_DEVICE_GRAY) {
+ rdev->std_colorant_names = &DeviceGrayComponents;
+ rdev->num_std_colorant_names = 1;
+ rdev->color_info.cm_name = "DeviceGray";
+ rdev->color_info.polarity = GX_CINFO_POLARITY_ADDITIVE;
+ } else if (color_model == RINKJ_DEVICE_RGB) {
+ rdev->std_colorant_names = &DeviceRGBComponents;
+ rdev->num_std_colorant_names = 3;
+ rdev->color_info.cm_name = "DeviceRGB";
+ rdev->color_info.polarity = GX_CINFO_POLARITY_ADDITIVE;
+ } else if (color_model == RINKJ_DEVICE_CMYK) {
+ rdev->std_colorant_names = &DeviceCMYKComponents;
+ rdev->num_std_colorant_names = 4;
+ rdev->color_info.cm_name = "DeviceCMYK";
+ rdev->color_info.polarity = GX_CINFO_POLARITY_SUBTRACTIVE;
+ } else if (color_model == RINKJ_DEVICE_N) {
+ rdev->std_colorant_names = &DeviceCMYKComponents;
+ rdev->num_std_colorant_names = 4;
+ rdev->color_info.cm_name = "DeviceN";
+ rdev->color_info.polarity = GX_CINFO_POLARITY_SUBTRACTIVE;
+ } else {
+ return -1;
+ }
+
+ rdev->color_info.max_components = rdev->num_std_colorant_names;
+ rdev->color_info.num_components = rdev->num_std_colorant_names;
+ rdev->color_info.depth = bpc * rdev->num_std_colorant_names;
+ return 0;
+}
+
+/* Set parameters. We allow setting the number of bits per component. */
+static int
+rinkj_put_params(gx_device * pdev, gs_param_list * plist)
+{
+ rinkj_device * const pdevn = (rinkj_device *) pdev;
+ gx_device_color_info save_info;
+ gs_param_name param_name;
+ int npcmcolors;
+ int num_spot = pdevn->separation_names.num_names;
+ int ecode = 0;
+ int code;
+ gs_param_string_array scna;
+ gs_param_string po;
+ gs_param_string sf;
+ gs_param_string pcm;
+ rinkj_color_model color_model = pdevn->color_model;
+
+ BEGIN_ARRAY_PARAM(param_read_name_array, "SeparationColorNames", scna, scna.size, scne) {
+ break;
+ } END_ARRAY_PARAM(scna, scne);
+
+ if (code >= 0)
+ code = rinkj_param_read_fn(plist, "ProfileOut", &po,
+ sizeof(pdevn->profile_out_fn));
+
+ if (code >= 0)
+ code = rinkj_param_read_fn(plist, "SetupFile", &sf,
+ sizeof(pdevn->setup_fn));
+
+ if (code >= 0)
+ code = param_read_name(plist, "ProcessColorModel", &pcm);
+ if (code == 0) {
+ if (param_string_eq (&pcm, "DeviceGray"))
+ color_model = RINKJ_DEVICE_GRAY;
+ else if (param_string_eq (&pcm, "DeviceRGB"))
+ color_model = RINKJ_DEVICE_RGB;
+ else if (param_string_eq (&pcm, "DeviceCMYK"))
+ color_model = RINKJ_DEVICE_CMYK;
+ else if (param_string_eq (&pcm, "DeviceN"))
+ color_model = RINKJ_DEVICE_N;
+ else {
+ param_signal_error(plist, "ProcessColorModel",
+ code = gs_error_rangecheck);
+ }
+ }
+ if (code < 0)
+ ecode = code;
+
+ /*
+ * Save the color_info in case gdev_prn_put_params fails, and for
+ * comparison.
+ */
+ save_info = pdevn->color_info;
+ ecode = rinkj_set_color_model(pdevn, color_model);
+ if (ecode == 0)
+ ecode = gdev_prn_put_params(pdev, plist);
+ if (ecode < 0) {
+ pdevn->color_info = save_info;
+ return ecode;
+ }
+
+ /* Separations are only valid with a subtractive color model */
+ if (pdev->color_info.polarity == GX_CINFO_POLARITY_SUBTRACTIVE) {
+ /*
+ * Process the separation color names. Remove any names that already
+ * match the process color model colorant names for the device.
+ */
+ if (scna.data != 0) {
+ int i;
+ int num_names = scna.size;
+ const fixed_colorant_names_list * pcomp_names =
+ ((rinkj_device *)pdev)->std_colorant_names;
+
+ for (i = num_spot = 0; i < num_names; i++) {
+ if (!check_process_color_names(pcomp_names, &scna.data[i]))
+ pdevn->separation_names.names[num_spot++] = &scna.data[i];
+ }
+ pdevn->separation_names.num_names = num_spot;
+ if (pdevn->is_open)
+ gs_closedevice(pdev);
+ }
+ }
+ npcmcolors = pdevn->num_std_colorant_names;
+ pdevn->color_info.num_components = npcmcolors + num_spot;
+ /*
+ * The DeviceN device can have zero components if nothing has been
+ * specified. This causes some problems so force at least one
+ * component until something is specified.
+ */
+ if (!pdevn->color_info.num_components)
+ pdevn->color_info.num_components = 1;
+ pdevn->color_info.depth = bpc_to_depth(pdevn->color_info.num_components,
+ pdevn->bitspercomponent);
+ if (pdevn->color_info.depth != save_info.depth) {
+ gs_closedevice(pdev);
+ }
+
+ if (po.data != 0) {
+ memcpy(pdevn->profile_out_fn, po.data, po.size);
+ pdevn->profile_out_fn[po.size] = 0;
+ }
+ if (sf.data != 0) {
+ memcpy(pdevn->setup_fn, sf.data, sf.size);
+ pdevn->setup_fn[sf.size] = 0;
+ }
+ code = rinkj_open_profile(pdevn);
+
+ return code;
+}
+
+/*
+ * Close device and clean up ICC structures.
+ */
+
+static int
+rinkj_close_device(gx_device *dev)
+{
+ rinkj_device * const rdev = (rinkj_device *) dev;
+
+ gscms_release_link(rdev->icc_link);
+ rc_decrement(rdev->link_profile, "rinkj_close_device");
+
+ return gdev_prn_close(dev);
+}
+
+/*
+ * This routine will check to see if the color component name match those
+ * that are available amoung the current device's color components.
+ *
+ * Parameters:
+ * dev - pointer to device data structure.
+ * pname - pointer to name (zero termination not required)
+ * nlength - length of the name
+ *
+ * This routine returns a positive value (0 to n) which is the device colorant
+ * number if the name is found. It returns a negative value if not found.
+ */
+static int
+rinkj_get_color_comp_index(gx_device * dev, const char * pname, int name_size,
+ int src_index)
+{
+/* TO_DO_DEVICEN This routine needs to include the effects of the SeparationOrder array */
+ const fixed_colorant_names_list * list = ((const rinkj_device *)dev)->std_colorant_names;
+ const fixed_colorant_name * pcolor = *list;
+ int color_component_number = 0;
+ int i;
+
+ /* Check if the component is in the implied list. */
+ if (pcolor) {
+ while( *pcolor) {
+ if (compare_color_names(pname, name_size, *pcolor, strlen(*pcolor)))
+ return color_component_number;
+ pcolor++;
+ color_component_number++;
+ }
+ }
+
+ /* Check if the component is in the separation names list. */
+ {
+ const gs_separation_names * separations = &((const rinkj_device *)dev)->separation_names;
+ int num_spot = separations->num_names;
+
+ for (i=0; i<num_spot; i++) {
+ if (compare_color_names((const char *)separations->names[i]->data,
+ separations->names[i]->size, pname, name_size)) {
+ return color_component_number;
+ }
+ color_component_number++;
+ }
+ }
+
+ return -1;
+}
+
+/* simple linear interpolation */
+static double
+rinkj_graph_lookup (const double *graph_x, const double *graph_y, int n_graph, double x)
+{
+ int i;
+
+ for (i = 0; i < n_graph - 1; i++)
+ {
+ if (graph_x[i + 1] > x)
+ break;
+ }
+ return graph_y[i] + (x - graph_x[i]) * (graph_y[i + 1] - graph_y[i]) /
+ (graph_x[i + 1] - graph_x[i]);
+}
+
+typedef struct rinkj_lutset_s rinkj_lutset;
+typedef struct rinkj_lutchain_s rinkj_lutchain;
+
+struct rinkj_lutset_s {
+ const char *plane_names;
+ rinkj_lutchain *lut[MAX_CHAN];
+};
+
+struct rinkj_lutchain_s {
+ rinkj_lutchain *next;
+ int n_graph;
+ double *graph_x;
+ double *graph_y;
+};
+
+static int
+rinkj_add_lut(rinkj_device *rdev, rinkj_lutset *lutset, char plane, FILE *f)
+{
+ char linebuf[256];
+ rinkj_lutchain *chain;
+ int n_graph;
+ int plane_ix;
+ int i;
+ rinkj_lutchain **pp;
+
+ for (plane_ix = 0; lutset->plane_names[plane_ix]; plane_ix++)
+ if (lutset->plane_names[plane_ix] == plane)
+ break;
+ if (lutset->plane_names[plane_ix] != plane)
+ return -1;
+ pp = &lutset->lut[plane_ix];
+
+ if (fgets(linebuf, sizeof(linebuf), f) == NULL)
+ return -1;
+ if (sscanf(linebuf, "%d", &n_graph) != 1)
+ return -1;
+ chain = (rinkj_lutchain *)gs_alloc_bytes(rdev->memory, sizeof(rinkj_lutchain), "rinkj_add_lut");
+ chain->next = NULL;
+ chain->n_graph = n_graph;
+ chain->graph_x = (double *)gs_alloc_bytes(rdev->memory, sizeof(double) * n_graph, "rinkj_add_lut");
+ chain->graph_y = (double *)gs_alloc_bytes(rdev->memory, sizeof(double) * n_graph, "rinkj_add_lut");
+ for (i = 0; i < n_graph; i++) {
+ double x, y;
+
+ if (fgets(linebuf, sizeof(linebuf), f) == NULL)
+ return -1;
+ if (sscanf(linebuf, "%lf %lf", &y, &x) != 2)
+ return -1;
+ chain->graph_x[i] = x / 1.0;
+ chain->graph_y[i] = y / 1.0;
+ }
+ /* add at end of chain */
+ while (*pp) {
+ pp = &((*pp)->next);
+ }
+ *pp = chain;
+ return 0;
+}
+
+static int
+rinkj_apply_luts(rinkj_device *rdev, RinkjDevice *cmyk_dev, const rinkj_lutset *lutset)
+{
+ int plane_ix;
+ double lut[256];
+
+ for (plane_ix = 0; plane_ix < 7; plane_ix++) {
+ int i;
+ for (i = 0; i < 256; i++) {
+ double g = i / 255.0;
+ rinkj_lutchain *chain;
+
+ for (chain = lutset->lut[plane_ix]; chain; chain = chain->next) {
+ g = rinkj_graph_lookup(chain->graph_x, chain->graph_y,
+ chain->n_graph, g);
+ }
+ lut[i] = g;
+ }
+ rinkj_screen_eb_set_lut(cmyk_dev, plane_ix, lut);
+ }
+ return 0;
+}
+
+static int
+rinkj_set_luts(rinkj_device *rdev,
+ RinkjDevice *printer_dev, RinkjDevice *cmyk_dev,
+ const char *config_fn, const RinkjDeviceParams *params)
+{
+ FILE *f = gp_fopen(config_fn, "r");
+ char linebuf[256];
+ char key[256];
+ char *val;
+ rinkj_lutset lutset;
+ int i;
+
+ lutset.plane_names = "KkCMcmY";
+ for (i = 0; i < MAX_CHAN; i++) {
+ lutset.lut[i] = NULL;
+ }
+ for (;;) {
+ if (fgets(linebuf, sizeof(linebuf), f) == NULL)
+ break;
+ for (i = 0; linebuf[i]; i++)
+ if (linebuf[i] == ':') break;
+ if (linebuf[i] != ':') {
+ continue;
+ }
+ memcpy(key, linebuf, i);
+ key[i] = 0;
+ for (i++; linebuf[i] == ' '; i++);
+ val = linebuf + i;
+
+ if (!strcmp(key, "AddLut")) {
+ if_debug1m('r', rdev->memory, "[r]%s", linebuf);
+ rinkj_add_lut(rdev, &lutset, val[0], f);
+ } else if (!strcmp(key, "Dither") || !strcmp(key, "Aspect")) {
+ rinkj_device_set_param_string(cmyk_dev, key, val);
+ } else {
+ rinkj_device_set_param_string(printer_dev, key, val);
+ }
+ }
+
+ fclose(f);
+
+ rinkj_apply_luts(rdev, cmyk_dev, &lutset);
+ /* todo: free lutset contents */
+
+ return 0;
+}
+
+static RinkjDevice *
+rinkj_init(rinkj_device *rdev, FILE *file)
+{
+ RinkjByteStream *bs;
+ RinkjDevice *epson_dev;
+ RinkjDevice *cmyk_dev;
+ RinkjDeviceParams params;
+
+ bs = rinkj_byte_stream_file_new(file);
+ epson_dev = rinkj_epson870_new(bs);
+ cmyk_dev = rinkj_screen_eb_new(epson_dev);
+
+ params.width = rdev->width;
+ params.height = rdev->height;
+ params.n_planes = 7;
+ params.plane_names = "CMYKcmk";
+ rdev->n_planes_out = params.n_planes;
+
+ rinkj_set_luts(rdev, epson_dev, cmyk_dev, rdev->setup_fn, &params);
+
+ rinkj_device_init (cmyk_dev, &params);
+
+ return cmyk_dev;
+}
+
+typedef struct rinkj_color_cache_entry_s rinkj_color_cache_entry;
+
+struct rinkj_color_cache_entry_s {
+ bits32 key;
+ bits32 value;
+};
+
+#define RINKJ_CCACHE_LOGSIZE 16
+#define RINKJ_CCACHE_SIZE (1 << RINKJ_CCACHE_LOGSIZE)
+
+static inline bits32
+rinkj_color_hash(bits32 color)
+{
+ /* This is somewhat arbitrary */
+ return (color ^ (color >> 10) ^ (color >> 20)) & (RINKJ_CCACHE_SIZE - 1);
+}
+
+static int
+rinkj_write_image_data(gx_device_printer *pdev, RinkjDevice *cmyk_dev)
+{
+ rinkj_device *rdev = (rinkj_device *)pdev;
+ int raster = gdev_prn_raster(rdev);
+ byte *line;
+ byte *plane_data[MAX_CHAN];
+ const byte *split_plane_data[MAX_CHAN];
+ int xsb;
+ int n_planes;
+ int n_planes_in = pdev->color_info.num_components;
+ int n_planes_out = 4;
+ int i;
+ int y;
+ int code = 0;
+ rinkj_color_cache_entry *cache = NULL;
+
+ n_planes = n_planes_in + rdev->separation_names.num_names;
+ if_debug1m('r', rdev->memory, "[r]n_planes = %d\n", n_planes);
+ xsb = pdev->width;
+ for (i = 0; i < n_planes_out; i++)
+ plane_data[i] = gs_alloc_bytes(pdev->memory, xsb, "rinkj_write_image_data");
+
+ if (rdev->icc_link != NULL) {
+
+ cache = (rinkj_color_cache_entry *)gs_alloc_bytes(pdev->memory, RINKJ_CCACHE_SIZE * sizeof(rinkj_color_cache_entry), "rinkj_write_image_data");
+ if (cache == NULL)
+ return gs_note_error(gs_error_VMerror);
+
+ /* Set up cache so that none of the keys will hit. */
+
+ cache[0].key = 1;
+ for (i = 1; i < RINKJ_CCACHE_SIZE; i++)
+ cache[i].key = 0;
+
+ }
+
+ /* do CMYK -> CMYKcmk ink split by plane replication */
+ split_plane_data[0] = plane_data[0];
+ split_plane_data[1] = plane_data[1];
+ split_plane_data[2] = plane_data[2];
+ split_plane_data[3] = plane_data[3];
+ split_plane_data[4] = plane_data[0];
+ split_plane_data[5] = plane_data[1];
+ split_plane_data[6] = plane_data[3];
+
+ line = gs_alloc_bytes(pdev->memory, raster, "rinkj_write_image_data");
+ for (y = 0; y < pdev->height; y++) {
+ byte *row;
+ int x;
+
+ code = gdev_prn_get_bits(pdev, y, line, &row);
+
+ if (rdev->icc_link == NULL) {
+ int rowix = 0;
+ for (x = 0; x < pdev->width; x++) {
+ for (i = 0; i < n_planes_in; i++)
+ plane_data[i][x] = row[rowix + i];
+ rowix += n_planes;
+ }
+ } else if (n_planes == 3) {
+ int rowix = 0;
+ for (x = 0; x < pdev->width; x++) {
+ byte cbuf[4] = {0, 0, 0, 0};
+ bits32 color;
+ bits32 hash;
+ byte vbuf[4];
+
+ memcpy(cbuf, row + rowix, 3);
+ color = ((bits32 *)cbuf)[0];
+ hash = rinkj_color_hash(color);
+
+ if (cache[hash].key != color) {
+
+ /* 3 channel to CMYK */
+ gscms_transform_color((gx_device *)rdev, rdev->icc_link,
+ &cbuf, &(vbuf), 1);
+ cache[hash].key = color;
+ cache[hash].value = ((bits32 *)vbuf)[0];
+
+ } else {
+ ((bits32 *)vbuf)[0] = cache[hash].value;
+ }
+ plane_data[0][x] = vbuf[0];
+ plane_data[1][x] = vbuf[1];
+ plane_data[2][x] = vbuf[2];
+ plane_data[3][x] = vbuf[3];
+ rowix += n_planes;
+ }
+ } else if (n_planes == 4) {
+ for (x = 0; x < pdev->width; x++) {
+ bits32 color = ((bits32 *)row)[x];
+ bits32 hash = rinkj_color_hash(color);
+ byte vbuf[4];
+
+ if (cache[hash].key != color) {
+ byte cbuf[4];
+
+ ((bits32 *)cbuf)[0] = color;
+
+ /* 4 channel to CMYK */
+ gscms_transform_color((gx_device *)rdev, rdev->icc_link,
+ &cbuf, &(vbuf), 1);
+ cache[hash].key = color;
+ cache[hash].value = ((bits32 *)vbuf)[0];
+ } else {
+ ((bits32 *)vbuf)[0] = cache[hash].value;
+ }
+ plane_data[0][x] = vbuf[0];
+ plane_data[1][x] = vbuf[1];
+ plane_data[2][x] = vbuf[2];
+ plane_data[3][x] = vbuf[3];
+ }
+ } else if (n_planes == 5) {
+ int rowix = 0;
+ for (x = 0; x < pdev->width; x++) {
+ byte cbuf[4];
+ bits32 color;
+ bits32 hash;
+ byte vbuf[4];
+ byte spot;
+ int scolor[4] = { 0x08, 0xc0, 0x80, 0 };
+
+ memcpy(cbuf, row + rowix, 4);
+ color = ((bits32 *)cbuf)[0];
+ hash = rinkj_color_hash(color);
+
+ if (cache[hash].key != color) {
+
+ /* Not sure what is going on here. Old
+ code was still working with 4 to 4
+ conversion. Replacing with new ICC AMP call */
+
+ gscms_transform_color((gx_device *) rdev, rdev->icc_link,
+ &cbuf, &(vbuf), 1);
+ cache[hash].key = color;
+ cache[hash].value = ((bits32 *)vbuf)[0];
+ } else {
+ ((bits32 *)vbuf)[0] = cache[hash].value;
+ }
+ spot = row[rowix + 4];
+ if (spot != 0) {
+ for (i = 0; i < 4; i++) {
+ int cmyk = vbuf[i], sp_i = spot;
+ int tmp = (cmyk << 8) - cmyk;
+ tmp += (sp_i * scolor[i] * (255 - cmyk)) >> 8;
+ tmp += 0x80;
+ plane_data[i][x] = (tmp + (tmp >> 8)) >> 8;
+ }
+ } else {
+ plane_data[0][x] = vbuf[0];
+ plane_data[1][x] = vbuf[1];
+ plane_data[2][x] = vbuf[2];
+ plane_data[3][x] = vbuf[3];
+ }
+ rowix += n_planes;
+ }
+ }
+
+ code = rinkj_device_write(cmyk_dev, (const char **)split_plane_data);
+ }
+
+ rinkj_device_write(cmyk_dev, NULL);
+ for (i = 0; i < n_planes_in; i++)
+ gs_free_object(pdev->memory, plane_data[i], "rinkj_write_image_data");
+ gs_free_object(pdev->memory, line, "rinkj_write_image_data");
+ gs_free_object(pdev->memory, cache, "rinkj_write_image_data");
+
+ return code;
+}
+
+static int
+rinkj_print_page(gx_device_printer *pdev, FILE *file)
+{
+ rinkj_device *rdev = (rinkj_device *)pdev;
+ int code = 0;
+ RinkjDevice *cmyk_dev;
+
+ cmyk_dev = rinkj_init(rdev, file);
+ if (cmyk_dev == 0)
+ return gs_note_error(gs_error_ioerror);
+
+ code = rinkj_write_image_data(pdev, cmyk_dev);
+ return code;
+}
diff --git a/devices/gdevs3ga.c b/devices/gdevs3ga.c
new file mode 100644
index 000000000..5d79e6a67
--- /dev/null
+++ b/devices/gdevs3ga.c
@@ -0,0 +1,244 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* S3 86C911 driver */
+#include "gx.h"
+#include "gserrors.h"
+#include "gxdevice.h"
+#include "gdevpcfb.h"
+#include "gdevsvga.h"
+
+/* Shared routines from gdevsvga.c */
+extern int vesa_get_mode(void);
+extern void vesa_set_mode(int);
+
+/* Macro for casting gx_device argument */
+#define fb_dev ((gx_device_svga *)dev)
+
+/* ------ The S3 86C911 device ------ */
+
+static dev_proc_open_device(s3_open);
+static dev_proc_fill_rectangle(s3_fill_rectangle);
+static dev_proc_copy_mono(s3_copy_mono);
+static const gx_device_procs s3_procs =
+{
+ s3_open,
+ NULL, /* get_initial_matrix */
+ NULL, /* sync_output */
+ NULL, /* output_page */
+ svga_close,
+ svga_map_rgb_color,
+ svga_map_color_rgb,
+ s3_fill_rectangle,
+ NULL, /* tile_rectangle */
+ s3_copy_mono,
+ svga_copy_color,
+/****** DOESN'T WORK ******/
+ NULL, /* draw_line */
+ svga_get_bits
+/****** DOESN'T WORK ******/
+};
+gx_device_svga far_data gs_s3vga_device =
+svga_device(s3_procs, "s3vga", vesa_get_mode, vesa_set_mode, NULL);
+
+/* Keep track of the character bitmap cache in off-screen memory. */
+#define log2_cell_width 5
+#define cell_width (1 << log2_cell_width)
+#define cache_x_bits (log2_cache_width_bits - log2_cell_width)
+#define log2_cell_height 5
+#define cell_height (1 << log2_cell_height)
+#define cache_y_bits (log2_cache_height - log2_cell_height)
+#define log2_cache_width_bits 10
+#define log2_cache_width_bytes (log2_cache_width_bits - 3)
+#define log2_cache_height 8
+#define log2_cache_capacity (cache_x_bits + cache_y_bits)
+#define cache_capacity (1 << log2_cache_capacity)
+static gx_bitmap_id cache_ids[cache_capacity];
+
+/* Define additional registers and I/O addresses. */
+#define crtc_addr 0x3d4 /* (color) */
+#define crt_lock 0x35
+#define crt_s3_lock1 0x38
+#define crt_s3_lock2 0x39
+#define s3_y_pos 0x82e8
+#define s3_x_pos 0x86e8
+#define s3_y_dest 0x8ae8
+#define s3_x_dest 0x8ee8
+#define s3_width 0x96e8
+#define s3_status 0x9ae8 /* read only */
+#define s3_command 0x9ae8 /* write only */
+#define s3_back_color 0xa2e8
+#define s3_fore_color 0xa6e8
+#define s3_write_mask 0xaae8
+#define s3_read_mask 0xaee8
+#define s3_back_mix 0xb6e8
+#define s3_fore_mix 0xbae8
+#define s3_height 0xbee8
+#define s3_mf_control 0xbee8
+# define mf_data_ones 0xa000
+# define mf_data_cpu 0xa080
+# define mf_data_display 0xa0c0
+#define s3_pixel_data 0xe2e8
+/* Wait for the command FIFO to empty. */
+#define s3_wait_fifo()\
+ while ( inport(s3_status) & 0xff )
+/* Load the parameters for a rectangle operation. */
+#define out_s3_rect(x, y, w, h)\
+ (outport(s3_x_pos, x), outport(s3_y_pos, y),\
+ outport(s3_width, (w) - 1), outport(s3_height, (h) - 1))
+
+static int
+s3_open(gx_device * dev)
+{
+ static const mode_info mode_table[] =
+ {
+ {640, 480, 0x201},
+ {800, 600, 0x203},
+ {1024, 768, 0x205},
+ {-1, -1, -1}
+ };
+ int code = svga_find_mode(dev, mode_table);
+
+ if (code < 0)
+ return_error(gs_error_rangecheck);
+ /* The enhanced modes all use a 1024-pixel raster. */
+ fb_dev->raster = 1024;
+ code = svga_open(dev);
+ if (code < 0)
+ return code;
+ /* Clear the cache */
+ {
+ int i;
+
+ for (i = 0; i < cache_capacity; i++)
+ cache_ids[i] = gx_no_bitmap_id;
+ }
+ return 0;
+}
+
+/* Fill a rectangle. */
+int
+s3_fill_rectangle(gx_device * dev, int x, int y, int w, int h,
+ gx_color_index color)
+{
+ fit_fill(dev, x, y, w, h);
+ s3_wait_fifo();
+ outport(s3_fore_mix, 0x27);
+ outport(s3_fore_color, (int)color);
+ outport(s3_mf_control, mf_data_ones);
+ out_s3_rect(x, y, w, h);
+ outport(s3_command, 0x40b3);
+ return 0;
+}
+
+/* Copy a monochrome bitmap. The colors are given explicitly. */
+/* Color = gx_no_color_index means transparent (no effect on the image). */
+static int
+s3_copy_mono(gx_device * dev,
+ const byte * base, int sourcex, int raster, gx_bitmap_id id,
+ int x, int y, int w, int h, gx_color_index czero, gx_color_index cone)
+{
+ int sbit;
+ const byte *sptr;
+ int run;
+ byte lmask;
+ byte lmerge = 0;
+ int cache_index, cache_x, cache_y;
+ int i, j;
+
+ fit_copy(dev, base, sourcex, raster, id, x, y, w, h);
+ sbit = sourcex & 7;
+ sptr = base + (sourcex >> 3);
+ run = (sbit + w + 7) >> 3;
+ lmask = 0xff >> sbit;
+ /* See whether the cache is applicable. */
+ if (id != gx_no_bitmap_id && w <= cell_width - 7 &&
+ h <= cell_height
+ ) {
+ cache_index = (int)(id & (cache_capacity - 1));
+ cache_x = ((cache_index & ((1 << cache_x_bits) - 1)) <<
+ log2_cell_width) + 7;
+ cache_y = ((cache_index >> cache_x_bits) <<
+ log2_cell_height) + 768;
+ if (cache_ids[cache_index] != id) {
+ cache_ids[cache_index] = id;
+ /* Copy the bitmap to the cache. */
+ s3_wait_fifo();
+ out_s3_rect(cache_x - sbit, cache_y, w + sbit, h);
+ outport(s3_fore_mix, 0x22); /* 1s */
+ outport(s3_back_mix, 0x01); /* 0s */
+ outport(s3_mf_control, mf_data_cpu);
+ outport(s3_command, 0x41b3);
+ {
+ const int skip = raster - run;
+
+ for (i = h; i > 0; i--, sptr += skip)
+ for (j = run; j > 0; j--, sptr++)
+ outportb(s3_pixel_data, *sptr);
+ }
+ }
+ s3_wait_fifo();
+ } else {
+ cache_index = -1;
+ if (lmask != 0xff) { /* The hardware won't do the masking for us. */
+ if (czero != gx_no_color_index) {
+ if (cone != gx_no_color_index) {
+ s3_fill_rectangle(dev, x, y, w, h, czero);
+ czero = gx_no_color_index;
+ } else {
+ lmerge = ~lmask;
+ }
+ }
+ }
+ s3_wait_fifo();
+ out_s3_rect(x - sbit, y, w + sbit, h);
+ }
+ /* Load the colors for the real transfer. */
+ if (cone != gx_no_color_index) {
+ outport(s3_fore_mix, 0x27);
+ outport(s3_fore_color, (int)cone);
+ } else
+ outport(s3_fore_mix, 0x63);
+ if (czero != gx_no_color_index) {
+ outport(s3_back_mix, 0x07);
+ outport(s3_back_color, (int)czero);
+ } else
+ outport(s3_back_mix, 0x63);
+ s3_wait_fifo();
+ if (cache_index < 0) { /* direct transfer */
+ outport(s3_mf_control, mf_data_cpu);
+ outport(s3_command, 0x41b3);
+ if (run == 1 && !lmerge) { /* special case for chars */
+ for (i = h; i > 0; i--, sptr += raster)
+ outportb(s3_pixel_data, *sptr & lmask);
+ } else {
+ const int skip = raster - run;
+
+ for (i = h; i > 0; i--, sptr += skip) {
+ outportb(s3_pixel_data, (*sptr++ & lmask) | lmerge);
+ for (j = run; j > 1; j--, sptr++)
+ outportb(s3_pixel_data, *sptr);
+ }
+ }
+ } else { /* Copy the character from the cache to the screen. */
+ out_s3_rect(cache_x, cache_y, w, h);
+ outport(s3_x_dest, x);
+ outport(s3_y_dest, y);
+ outport(s3_mf_control, mf_data_display);
+ outport(s3_command, 0xc0b3);
+ }
+ return 0;
+}
diff --git a/devices/gdevsco.c b/devices/gdevsco.c
new file mode 100644
index 000000000..e1d427f15
--- /dev/null
+++ b/devices/gdevsco.c
@@ -0,0 +1,286 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* 17Jul91 - wb - based on gdevpcfb.c */
+/* 31Jul91 - Rick Calder rick@rick.att.com - ifdefs for AT&T UNIX 4.0 2.1 */
+/* 13Sep91 - wb - modify for gs24b2 */
+/* 9Mar92 - wb - modify for gs24b4 */
+/* generate SCO Xenix/Unix style memory mapped ioctl output */
+#include "memory_.h"
+#include "gx.h"
+#include "gserrors.h"
+#include "gxdevice.h"
+#include "gdevpcfb.h"
+#include <signal.h>
+
+#ifdef M_XENIX
+#include <sys/console.h> /* SCO Xenix and SCO UNIX */
+#ifndef CONSIO
+#include <sys/machdep.h> /* Xenix needs this also */
+#endif
+#else
+#include <sys/kd.h> /* AT&T SVR4 */
+#endif
+
+#if defined(__STDC__)
+#include <stdlib.h>
+#else
+extern char *getenv(const char *);
+#endif
+
+#if defined(M_XENIX)
+#include <prototypes.h>
+#include <fcntl.h>
+#else
+extern int ioctl(int, int,...);
+extern int open(const char *, int,...);
+#endif
+
+static int console_fd = -1; /* file descriptor of console */
+fb_ptr fb_addr; /* address of frame buffer for unix */
+static int cur_mode = -1; /* current video mode */
+
+/* open the console */
+/* possible files to open:
+ * /dev/console = current system console
+ * /dev/vga = vga monitor
+ * /dev/tty = current terminal
+ */
+
+static void open_console(void);
+
+static void
+open_console()
+{
+ const char *dev;
+
+ if (console_fd != -1)
+ return;
+ dev = getenv("GSDEVICE");
+ if (dev == NULL || *dev == '\0')
+ dev = "/dev/tty";
+ console_fd = open(dev, 0);
+ if (console_fd == -1) {
+ ega_close((gx_device *) NULL);
+ eprintf1("unable to map display '%s'\n", dev);
+ perror("open_console");
+ exit(1);
+ }
+}
+
+#if defined(__GNUC__)
+ /* Done with inline assembly in gdevpcfb.h */
+#else
+/* Output to a port */
+void
+outportb(uint port, byte data)
+{
+ int i;
+ struct port_io_arg pio;
+
+ if (console_fd == -1)
+ open_console();
+ pio.args[0].dir = OUT_ON_PORT;
+ pio.args[0].port = port;
+ pio.args[0].data = data;
+ pio.args[1].port = 0;
+ pio.args[2].port = 0;
+ pio.args[3].port = 0;
+ i = ioctl(console_fd, CONSIO, (long)(&pio));
+ if (i == -1) {
+ ega_close((gx_device *) NULL);
+ eprintf("error setting device register\n");
+ perror("outportb");
+ exit(1);
+ }
+}
+
+/* Output to 2 consecutive ports */
+void
+outport2(uint port, byte index, byte data)
+{
+ int i;
+ struct port_io_arg pio;
+
+ if (console_fd == -1)
+ open_console();
+ pio.args[0].dir = OUT_ON_PORT;
+ pio.args[0].port = port;
+ pio.args[0].data = index;
+ pio.args[1].dir = OUT_ON_PORT;
+ pio.args[1].port = port + 1;
+ pio.args[1].data = data;
+ pio.args[2].port = 0;
+ pio.args[3].port = 0;
+ i = ioctl(console_fd, CONSIO, (long)(&pio));
+ if (i == -1) {
+ ega_close((gx_device *) NULL);
+ eprintf("error setting device register\n");
+ perror("outport2");
+ exit(1);
+ }
+}
+#endif
+
+/* interrupt signal handler */
+/* restore the video mode and exit */
+static void
+ega_int_handler(int sig)
+{
+ ega_close((gx_device *) NULL);
+ eprintf("GS exiting...\n");
+ exit(1);
+}
+
+/*
+ * FIXME to make this work, the SIGCONT handler must restore the
+ * the video state, including all the registers.
+ * For now, I made the SIGSTOP handler exit just call the SIGINT handler
+ */
+
+#ifdef SIGTSTP
+/* user tried to stop us. restore video and stop */
+static void
+ega_tstp_handler(int sig)
+{
+#if 1
+ ega_int_handler(sig);
+#else
+ /* Preferable, but sco does not restore the monitor corretly */
+ signal(SIGTSTP, ega_tstp_handler);
+ ega_close((gx_device *) NULL);
+ eprintf("GS stopping...\n");
+ signal(SIGSTOP, SIG_DFL);
+ kill(getpid(), SIGSTOP);
+#endif
+}
+#endif /* SIGTSTP */
+
+#ifdef SIGCONT
+/* we were unstopped. reopen video */
+static void
+ega_cont_handler(int sig)
+{
+#if 1
+ ega_int_handler(sig);
+#else
+ signal(SIGCONT, ega_cont_handler);
+ ega_set_mode(cur_mode);
+#endif
+}
+#endif /* SIGCONT */
+
+/* ------ Internal routines ------ */
+
+/* Catch signals so we can restore the video mode on exit. */
+void
+pcfb_set_signals(gx_device * dev)
+{
+ signal(SIGINT, ega_int_handler);
+ signal(SIGTERM, ega_int_handler);
+#ifdef SIGTSTP
+ signal(SIGTSTP, ega_tstp_handler);
+#endif
+#ifdef SIGCONT
+ signal(SIGCONT, ega_cont_handler);
+#endif
+}
+
+/* Read the device mode */
+void
+pcfb_get_state(pcfb_bios_state * pbs)
+{
+ int mode;
+
+ open_console();
+ mode = ioctl(console_fd, CONS_CURRENT, 0L);
+ if (mode == -1) {
+#ifdef __linux__
+ mode = M_ENH_C80x25;
+#else
+ ega_close((gx_device *) NULL);
+ eprintf("unable to get current console mode\n");
+ perror("pcfb_get_state");
+ exit(1);
+#endif
+ }
+ pbs->display_mode =
+ (mode == M_ENH_CG640 || mode == M_CG640x350 ? 0x10 :
+#ifdef M_VGA12
+ mode == M_VGA12 ? 0x12 :
+#endif
+ 0x03);
+}
+
+/* Set the device mode */
+void
+pcfb_set_mode(int mode)
+{
+ int i, mode1;
+
+ open_console();
+ cur_mode = mode;
+ mode1 = -1;
+ if (mode == 0x10)
+ mode = SW_ENH_CG640;
+#ifdef SW_VGA12
+ else if (mode == 0x12)
+ mode = SW_VGA12;
+#endif
+ else if (mode == 0x03) {
+#ifdef SW_VGA80x25
+ mode = SW_VGA80x25;
+ mode1 = SW_ENHC80x25;
+#else
+ mode = SW_ENHC80x25;
+#endif
+ } else {
+ eprintf1("can not set to video mode %d\n", mode);
+ exit(1);
+ }
+ i = ioctl(console_fd, mode, 0L);
+ if (i == -1 && mode1 != -1)
+ i = ioctl(console_fd, mode1, 0L);
+ if (i == -1) {
+ ega_close((gx_device *) NULL);
+ eprintf("unable to set console mode\n");
+ perror("pcfb_set_mode");
+ exit(1);
+ }
+#ifdef VGA_IOPRIVL
+ if (ioctl(console_fd, VGA_IOPRIVL, 1) == -1) {
+ ega_close((gx_device *) NULL);
+ eprintf("unable to get I/O privilege\n");
+ perror("pcfb_set_mode");
+ exit(1);
+ }
+#endif
+ i = ioctl(console_fd, MAPCONS, 0L);
+ if (i == -1) {
+ ega_close((gx_device *) NULL);
+ eprintf("unable to map console adaptor's display memory\n");
+ perror("pcfb_set_mode");
+ exit(1);
+ }
+ fb_addr = (fb_ptr) (i);
+}
+
+/* Restore the device state */
+void
+pcfb_set_state(const pcfb_bios_state * pbs)
+{
+ pcfb_set_mode(pbs->display_mode);
+}
diff --git a/devices/gdevsgi.c b/devices/gdevsgi.c
new file mode 100644
index 000000000..d9424b39a
--- /dev/null
+++ b/devices/gdevsgi.c
@@ -0,0 +1,288 @@
+/*
+ * This file is distributed with Ghostscript, but its author,
+ * Tanmoy Bhattacharya (tanmoy@qcd.lanl.gov) hereby places it in the
+ * public domain.
+ */
+
+/* SGI raster file driver */
+#include "gdevprn.h"
+#include "gdevsgi.h"
+
+#define X_DPI 72
+#define Y_DPI 72
+
+#define sgi_prn_device(procs, dev_name, num_comp, depth, max_gray, max_color, print_page)\
+{prn_device_body(gx_device_printer, procs, dev_name, \
+ DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS, X_DPI, Y_DPI, \
+ 0, 0, 0, 0, \
+ num_comp, depth, max_gray, max_color, max_gray+1, max_color+1, \
+ print_page)}
+
+static dev_proc_map_rgb_color(sgi_map_rgb_color);
+static dev_proc_map_color_rgb(sgi_map_color_rgb);
+
+static dev_proc_print_page(sgi_print_page);
+
+/* Since the print_page doesn't alter the device, this device can print in the background */
+static gx_device_procs sgi_procs =
+ prn_color_procs(gdev_prn_open, gdev_prn_bg_output_page_seekable, gdev_prn_close,
+ sgi_map_rgb_color, sgi_map_color_rgb);
+
+const gx_device_printer far_data gs_sgirgb_device =
+ sgi_prn_device(sgi_procs, "sgirgb", 3, 24, 255, 255, sgi_print_page);
+
+static gx_color_index
+sgi_map_rgb_color(gx_device * dev, const ushort cv[])
+{ ushort bitspercolor = dev->color_info.depth / 3;
+ ulong max_value = (1 << bitspercolor) - 1;
+ ushort red, green, blue;
+ red = cv[0]; green = cv[1]; blue = cv[2];
+
+ return ((red*max_value / gx_max_color_value) << (bitspercolor * 2)) +
+ ((green*max_value / gx_max_color_value) << bitspercolor) +
+ (blue*max_value / gx_max_color_value);
+}
+
+static int
+sgi_map_color_rgb(gx_device *dev, gx_color_index color, ushort prgb[3])
+{ ushort bitspercolor = dev->color_info.depth / 3;
+ ushort colormask = (1 << bitspercolor) - 1;
+
+ prgb[0] = (ushort)(((color >> (bitspercolor * 2)) & colormask) *
+ (ulong)gx_max_color_value / colormask);
+ prgb[1] = (ushort)(((color >> bitspercolor) & colormask) *
+ (ulong)gx_max_color_value / colormask);
+ prgb[2] = (ushort)((color & colormask) *
+ (ulong)gx_max_color_value / colormask);
+ return 0;
+}
+
+typedef struct sgi_cursor_s {
+ gx_device_printer *dev;
+ int bpp;
+ uint line_size;
+ byte *data;
+ int lnum;
+} sgi_cursor;
+
+/* write a short int to disk as big-endean */
+static int putshort(unsigned int val, FILE *outf)
+{
+ unsigned char buf[2];
+
+ buf[0] = (val>>8);
+ buf[1] = val;
+ return fwrite(buf,2,1,outf);
+}
+
+/* write an int to disk as big-endean */
+static int putint(unsigned int val, FILE *outf)
+{
+ unsigned char buf[4];
+
+ buf[0] = (val>>24);
+ buf[1] = (val>>16);
+ buf[2] = (val>>8);
+ buf[3] = val;
+ return fwrite(buf,4,1,outf);
+}
+
+/* write the header field by field in big-endian */
+static void putheader(IMAGE *header, FILE *outf)
+{
+ int i;
+ char filler= '\0';
+
+ putshort(header->imagic, outf);
+ fputc(1, outf); /* RLE */
+ fputc(1, outf); /* bpp */
+ putshort(header->dim, outf);
+ putshort(header->xsize, outf);
+ putshort(header->ysize, outf);
+ putshort(header->zsize, outf);
+
+ putint(header->min_color, outf);
+ putint(header->max_color, outf);
+ putint(header->wastebytes, outf);
+
+ fwrite(header->name,80,1,outf);
+
+ putint(header->colormap, outf);
+
+ /* put the filler for the rest */
+ for (i=0; i<404; i++)
+ fputc(filler,outf);
+ }
+
+static int
+sgi_begin_page(gx_device_printer *bdev, FILE *pstream, sgi_cursor *pcur)
+{
+ uint line_size;
+ byte *data;
+ IMAGE *header;
+
+ if (bdev->PageCount >= 1 && !bdev->file_is_new) { /* support single page only */
+ emprintf(bdev->memory,
+ "sgi rgb format only supports one page per file.\n"
+ "Please use the '%%d' OutputFile option to create one file for each page.\n");
+ return_error(gs_error_rangecheck);
+ }
+ line_size = gdev_mem_bytes_per_scan_line((gx_device_printer*)bdev);
+ data = (byte*)gs_malloc(bdev->memory, line_size, 1, "sgi_begin_page");
+ header= (IMAGE*)gs_malloc(bdev->memory, sizeof(IMAGE),1,"sgi_begin_page");
+
+ if ((data == (byte*)0)||(header == (IMAGE*)0)) {
+ gs_free(bdev->memory, data, line_size, 1, "sgi_begin_page");
+ gs_free(bdev->memory, header, sizeof(IMAGE),1,"sgi_begin_page");
+ return_error(gs_error_VMerror);
+ }
+ memset(header,0, sizeof(IMAGE));
+ header->imagic = IMAGIC;
+ header->type = RLE(1);
+ header->dim = 3;
+ header->xsize=bdev->width;
+ header->ysize=bdev->height;
+ header->zsize=3;
+ header->min_color = 0;
+ header->max_color = bdev->color_info.max_color;
+ header->wastebytes = 0;
+ strncpy(header->name,"gs picture",80);
+ header->colormap = CM_NORMAL;
+ header->dorev=0;
+ putheader(header,pstream);
+ pcur->dev = bdev;
+ pcur->bpp = bdev->color_info.depth;
+ pcur->line_size = line_size;
+ pcur->data = data;
+ return 0;
+}
+
+static int
+sgi_next_row(sgi_cursor *pcur)
+{ if (pcur->lnum < 0)
+ return 1;
+ gdev_prn_copy_scan_lines((gx_device_printer*)pcur->dev,
+ pcur->lnum--, pcur->data, pcur->line_size);
+ return 0;
+}
+
+#define bdev ((gx_device_printer *)pdev)
+
+static int
+sgi_print_page(gx_device_printer *pdev, FILE *pstream)
+{ sgi_cursor cur;
+ int code = sgi_begin_page(bdev, pstream, &cur);
+ uint bpe, mask;
+ int separation;
+ int *rowsizes;
+ byte *edata ;
+ long lastval;
+ int rownumber;
+
+ if (pdev->PageCount >= 1 && !pdev->file_is_new)
+ return_error(gs_error_rangecheck); /* support single page only, can't happen */
+
+#define aref2(a,b) (a)*bdev->height+(b)
+ rowsizes=(int*)gs_malloc(pdev->memory, sizeof(int),3*bdev->height,"sgi_print_page");
+ edata = (byte*)gs_malloc(pdev->memory, cur.line_size, 1, "sgi_begin_page");
+
+ if((code<0)||(rowsizes==(int*)NULL)||(edata==(byte*)NULL)) {
+ code = gs_note_error(gs_error_VMerror);
+ goto free_mem;
+ }
+
+ lastval = 512+4*6*bdev->height; /* skip offset table */
+ fseek(pstream,lastval,0);
+ for (separation=0; separation < 3; separation++)
+ {
+ cur.lnum = cur.dev->height-1;
+ rownumber = 0;
+ bpe = cur.bpp/3;
+ mask = (1<<bpe) - 1;
+ while ( !(code=sgi_next_row(&cur)))
+ { byte *bp;
+ uint x;
+ int shift;
+ byte *curcol=cur.data;
+ byte *startcol=edata;
+ int count;
+ byte todo, cc;
+ byte *iptr, *sptr, *optr, *ibufend;
+ for (bp = cur.data, x=0, shift = 8 - cur.bpp;
+ x < bdev->width;
+ )
+ { uint pixel = 0;
+ uint r, g, b;
+ switch (cur.bpp >> 3)
+ {
+ case 3: pixel = (uint)*bp << 16; bp++;
+ case 2: pixel += (uint)*bp << 8; bp++;
+ case 1: pixel += *bp; bp++; break;
+ case 0: pixel = *bp >> shift;
+ if ((shift-=cur.bpp) < 0)
+ bp++, shift += 8; break;
+ }
+ ++x;
+ b = pixel & mask; pixel >>= bpe;
+ g = pixel & mask; pixel >>= bpe;
+ r = pixel & mask;
+ switch(separation)
+ {
+ case 0: *curcol++=r; break;
+ case 1: *curcol++=g; break;
+ case 2: *curcol++=b; break;
+ }
+ }
+ iptr=cur.data;
+ optr=startcol;
+ ibufend=curcol-1;
+ while(iptr<ibufend) {
+ sptr = iptr;
+ iptr += 2;
+ while((iptr<ibufend)&&((iptr[-2]!=iptr[-1])||(iptr[-1]!=iptr[0])))
+ iptr++;
+ iptr -= 2;
+ count = iptr-sptr;
+ while(count) {
+ todo = count>126 ? 126:count;
+ count -= todo;
+ *optr++ = 0x80|todo;
+ while(todo--)
+ *optr++ = *sptr++;
+ }
+ sptr = iptr;
+ cc = *iptr++;
+ while( (iptr<ibufend) && (*iptr == cc) )
+ iptr++;
+ count = iptr-sptr;
+ while(count) {
+ todo = count>126 ? 126:count;
+ count -= todo;
+ *optr++ = todo;
+ *optr++ = cc;
+ }
+ }
+ *optr++ = 0;
+ rowsizes[aref2(separation,rownumber++)] = optr-startcol;
+ if (fwrite(startcol,1,optr-startcol,pstream) != optr-startcol) {
+ code = gs_note_error(gs_error_ioerror);
+ goto free_mem;
+ }
+ }
+ }
+ fseek(pstream,512L,0);
+ for(separation=0; separation<3; separation++)
+ for(rownumber=0; rownumber<bdev->height; rownumber++)
+ {putint(lastval,pstream);
+ lastval+=rowsizes[aref2(separation,rownumber)];}
+ for(separation=0; separation<3; separation++)
+ for(rownumber=0; rownumber<bdev->height; rownumber++)
+ {lastval=rowsizes[aref2(separation,rownumber)];
+ putint(lastval,pstream);}
+ free_mem:
+ gs_free(pdev->memory, (char*)cur.data, cur.line_size, 1,
+ "sgi_print_page(done)");
+ gs_free(pdev->memory, (char*)edata, cur.line_size, 1, "sgi_print_page(done)");
+ gs_free(pdev->memory, (char*)rowsizes,4,3*bdev->height,"sgi_print_page(done)");
+ return (code < 0 ? code : 0);
+}
diff --git a/devices/gdevsgi.h b/devices/gdevsgi.h
new file mode 100644
index 000000000..95bc58368
--- /dev/null
+++ b/devices/gdevsgi.h
@@ -0,0 +1,70 @@
+/*
+ * This file is distributed with Ghostscript, but its author,
+ * Tanmoy Bhattacharya (tanmoy@qcd.lanl.gov) hereby places it in the
+ * public domain.
+ *
+ * The contents of this file were derived (indeed, largely copied) from
+ * the file image.h on SGI's file server; there is no copyright on that file.
+ */
+
+/* SGI raster file definitions */
+
+#ifndef gdevsgi_INCLUDED
+# define gdevsgi_INCLUDED
+
+#define IMAGIC 0732
+
+/* colormap of images */
+#define CM_NORMAL 0
+#define CM_DITHERED 1
+#define CM_SCREEN 2
+#define CM_COLORMAP 3
+#define TYPEMASK 0xff00
+#define BPPMASK 0x00ff
+#define ITYPE_VERBATIM 0x0000
+#define ITYPE_RLE 0x0100
+#define ISRLE(type) (((type) & 0xff00) == ITYPE_RLE)
+#define ISVERBATIM(type) (((type) & 0xff00) == ITYPE_VERBATIM)
+#define BPP(type) ((type) & BPPMASK)
+#define RLE(bpp) (ITYPE_RLE | (bpp))
+#define VERBATIM(bpp) (ITYPE_VERBATIM | (bpp))
+#define IBUFSIZE(pixels) ((pixels+(pixels>>6))<<2)
+#define RLE_NOP 0x00
+
+#define ierror(p) (((p)->flags&_IOERR)!=0)
+#define ifileno(p) ((p)->file)
+#define getpix(p) (--(p)->cnt>=0 ? *(p)->ptr++ : ifilbuf(p))
+#define putpix(p,x) (--(p)->cnt>=0 \
+ ? ((int)(*(p)->ptr++=(unsigned)(x))) \
+ : iflsbuf(p,(unsigned)(x)))
+
+typedef struct {
+ unsigned short imagic; /* stuff saved on disk . . */
+ unsigned short type;
+ unsigned short dim;
+ unsigned short xsize;
+ unsigned short ysize;
+ unsigned short zsize;
+ unsigned int min_color;
+ unsigned int max_color;
+ unsigned int wastebytes;
+ char name[80];
+ unsigned int colormap;
+
+ long file; /* stuff used in core only */
+ unsigned short flags;
+ short dorev;
+ short x;
+ short y;
+ short z;
+ short cnt;
+ unsigned short *ptr;
+ unsigned short *base;
+ unsigned short *tmpbuf;
+ unsigned long offset;
+ unsigned long rleend; /* for rle images */
+ unsigned long *rowstart; /* for rle images */
+ long *rowsize; /* for rle images */
+} IMAGE;
+
+#endif /* gdevsgi_INCLUDED */
diff --git a/devices/gdevsj48.c b/devices/gdevsj48.c
new file mode 100644
index 000000000..4464729d7
--- /dev/null
+++ b/devices/gdevsj48.c
@@ -0,0 +1,286 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/*
+ * StarJet SJ48 printer driver.
+ *
+ * --- derived from gdevbj10.c 1993-10-07
+ * by Mats kerblom (f86ma@dd.chalmers.se).
+ */
+
+#include "gdevprn.h"
+
+/*
+ * The only available resolutions (in the program) are (180,360)x(180,360).
+ *
+ * Used control codes:
+ * <Esc>@ Printer reset
+ * <Esc>J<n> Make a n/180 inch linefeed
+ * <Esc>\<a><b> Move the print position (a+256b)/180 inch to the right
+ * <Esc>*<m><a><b>... Print graphics; m=39: 180*180 dpi
+ * m=40: 360*180 dpi
+ * m=71: 180*360 dpi
+ * m=72: 360*360 dpi
+ * a+256b columns is printed.
+ */
+
+/* The device descriptor */
+static dev_proc_print_page(sj48_print_page);
+gx_device_printer far_data gs_sj48_device =
+ prn_device(prn_bg_procs, "sj48", /* The print_page proc is compatible with allowing bg printing */
+ 80, /* width_10ths, 8" */
+ 105, /* height_10ths, 10.5" */
+ 360, /* x_dpi */
+ 360, /* y_dpi */
+ 0,0,0,0, /* margins */
+ 1, sj48_print_page);
+
+/* This comes from the bj10/bj200 source. I don't know how it applies
+ * for a StarJet. --- Mats kerblom.
+ *
+ *
+ * The following is taken from the BJ200 Programmer's manual. The top
+ * margin is 3mm (0.12"), and the bottom margin is 6.4mm (0.25"). The
+ * left and right margin depend on the type of paper -- US letter or
+ * A4 -- but ultimately rest on a print width of 203.2mm (8"). For letter
+ * paper, the left margin (and hence the right) is 6.4mm (0.25"), while
+ * for A4 paper, both are 3.4mm (0.13").
+ *
+ * The bottom margin requires a bit of care. The image is printed
+ * as strips, each about 3.4mm wide. We can only attain the bottom
+ * margin if the final strip coincides with it. Note that each strip
+ * is generated using only 48 of the available 64 jets, and the absence
+ * of those bottom 16 jets makes our bottom margin, in effect, about
+ * 1.1mm (0.04") larger.
+ *
+ * The bj200 behaves, in effect, as though the origin were at the first
+ * printable position, rather than the top left corner of the page, so
+ * we add a translation to the initial matrix to compensate for this.
+ *
+ * Except for the details of getting the margins correct, the bj200 is
+ * no different from the bj10e, and uses the same routine to print each
+ * page.
+ *
+ */
+
+/* Send the page to the printer. */
+static int
+sj48_print_page(gx_device_printer *pdev, FILE *prn_stream)
+{ int line_size = gx_device_raster((gx_device *)pdev, 0);
+ int xres = pdev->x_pixels_per_inch;
+ int yres = pdev->y_pixels_per_inch;
+ int mode = (yres == 180 ?
+ (xres == 180 ? 39 : 40) :
+ (xres == 180 ? 71 : 72));
+ int bytes_per_column = (yres == 180) ? 3 : 6;
+ int bits_per_column = bytes_per_column * 8;
+ int skip_unit = bytes_per_column * (xres == 180 ? 1 : 2); /* Skips in step of 1/180" */
+ byte *in = (byte *)gs_malloc(pdev->memory, 8, line_size, "sj48_print_page(in)");
+ byte *out = (byte *)gs_malloc(pdev->memory, bits_per_column, line_size, "sj48_print_page(out)");
+ int lnum = 0;
+ int skip = 0;
+ int skips;
+ int code = 0;
+ int last_row = dev_print_scan_lines(pdev);
+ int limit = last_row - bits_per_column;
+
+ if ( in == 0 || out == 0 )
+ { code = gs_error_VMerror;
+ gs_note_error(code);
+ goto fin;
+ }
+
+ /* Abort if the requested resolution is unsupported. */
+ if ((xres !=180 && xres != 360) || (yres !=180 && yres != 360))
+ { code = gs_error_rangecheck;
+ gs_note_error(code);
+ goto fin;
+ }
+
+ /* Initialize the printer. */
+ fwrite("\033@\000\000", 1, 4, prn_stream); /* <Printer reset>, <0>, <0>. */
+
+ /* Transfer pixels to printer. The last row we can print is defined
+ by "last_row". Only the bottom of the print head can print at the
+ bottom margin, and so we align the final printing pass. The print
+ head is kept from moving below "limit", which is exactly one pass
+ above the bottom margin. Once it reaches this limit, we make our
+ final printing pass of a full "bits_per_column" rows. */
+ while ( lnum < last_row )
+ {
+ byte *in_data;
+ byte *in_end = in + line_size;
+ byte *out_beg = out;
+ byte *out_end = out + bytes_per_column * pdev->width;
+ byte *outl = out;
+ int bnum;
+
+ /* Copy 1 scan line and test for all zero. */
+ code = gdev_prn_get_bits(pdev, lnum, in, &in_data);
+ if ( code < 0 ) goto xit;
+ /* The mem... or str... functions should be faster than */
+ /* the following code, but all systems seem to implement */
+ /* them so badly that this code is faster. */
+ { register const long *zip = (const long *)in_data;
+ register int zcnt = line_size;
+ register const byte *zipb;
+ for ( ; zcnt >= 4 * sizeof(long); zip += 4, zcnt -= 4 * sizeof(long) )
+ { if ( zip[0] | zip[1] | zip[2] | zip[3] )
+ goto notz;
+ }
+ zipb = (const byte *)zip;
+ while ( --zcnt >= 0 )
+ {
+ if ( *zipb++ )
+ goto notz;
+ }
+ /* Line is all zero, skip */
+ lnum++;
+ skip++;
+ continue;
+notz: ;
+ }
+
+ /* Vertical tab to the appropriate position. Note here that
+ we make sure we don't move below limit. */
+ if ( lnum > limit )
+ { skip -= (limit - lnum);
+ lnum = limit;
+ }
+
+ /* The SJ48 can only skip in steps of 1/180" */
+ if (yres == 180) {
+ skips = skip;
+ } else {
+ if (skip & 1) {
+ skip--; /* Makes skip even. */
+ lnum--;
+ }
+ skips = skip/2;
+ }
+
+ while ( skips > 255 )
+ { fputs("\033J\377", prn_stream);
+ skips -= 255;
+ }
+ if ( skips )
+ fprintf(prn_stream, "\033J%c", skips);
+
+ /* If we've printed as far as "limit", then reset "limit"
+ to "last_row" for the final printing pass. */
+ if ( lnum == limit )
+ limit = last_row;
+ skip = 0;
+
+ /* Transpose in blocks of 8 scan lines. */
+ for ( bnum = 0; bnum < bits_per_column; bnum += 8 )
+ { int lcnt = min(8, limit - lnum);
+ byte *inp = in;
+ byte *outp = outl;
+ lcnt = gdev_prn_copy_scan_lines(pdev,
+ lnum, in, lcnt * line_size);
+ if ( lcnt < 0 )
+ { code = lcnt;
+ goto xit;
+ }
+ if ( lcnt < 8 )
+ memset(in + lcnt * line_size, 0,
+ (8 - lcnt) * line_size);
+ for ( ; inp < in_end; inp++, outp += bits_per_column )
+ { gdev_prn_transpose_8x8(inp, line_size,
+ outp, bytes_per_column);
+ }
+ outl++;
+ lnum += lcnt;
+ skip += lcnt;
+ }
+
+ /* Send the bits to the printer. We alternate horizontal
+ skips with the data. The horizontal skips are in units
+ of 1/180 inches, so we look at the data in groups of
+ 1 or 2 columns depending on resolution (controlled
+ by skip_unit). */
+ outl = out;
+ do
+ { int count;
+ int n;
+ byte *out_ptr;
+
+ /* First look for blank groups of columns. */
+ while(outl < out_end)
+ { n = count = min(out_end - outl, skip_unit);
+ out_ptr = outl;
+ while ( --count >= 0 )
+ { if ( *out_ptr++ )
+ break;
+ }
+ if ( count >= 0 )
+ break;
+ else
+ outl = out_ptr;
+ }
+ if (outl >= out_end)
+ break;
+ if (outl > out_beg)
+ { count = (outl - out_beg) / skip_unit;
+ fprintf(prn_stream, "\033\\%c%c",
+ count & 0xff, count >> 8);
+ }
+
+ /* Next look for non-blank groups of columns. */
+ out_beg = outl;
+ outl += n;
+ while(outl < out_end)
+ { n = count = min(out_end - outl, skip_unit);
+ out_ptr = outl;
+ while ( --count >= 0 )
+ { if ( *out_ptr++ )
+ break;
+ }
+ if ( count < 0 )
+ break;
+ else
+ outl += n;
+ }
+ count = outl - out_beg;
+ {
+ /* What to transmit is the number of columns in the row.
+ Compare this with the <Esc>|*-command wich expects the
+ total number of bytes in the graphic row! */
+ int count1 = count/bytes_per_column;
+ fprintf(prn_stream, "\033*%c%c%c",
+ mode, count1 & 0xff, count1 >> 8);
+ }
+ fwrite(out_beg, 1, count, prn_stream);
+ out_beg = outl;
+ outl += n;
+ }
+ while ( out_beg < out_end );
+
+ fputc('\r', prn_stream);
+ skip = bits_per_column; /* <CR> only moves to the beginning of the row. */
+ }
+
+ /* Eject the page */
+xit: fputc(014, prn_stream); /* form feed */
+ fflush(prn_stream);
+fin: if ( out != 0 )
+ gs_free(pdev->memory, (char *)out, bits_per_column, line_size,
+ "sj48_print_page(out)");
+ if ( in != 0 )
+ gs_free(pdev->memory, (char *)in, 8, line_size, "sj48_print_page(in)");
+ return code;
+}
diff --git a/devices/gdevsnfb.c b/devices/gdevsnfb.c
new file mode 100644
index 000000000..24bdcb575
--- /dev/null
+++ b/devices/gdevsnfb.c
@@ -0,0 +1,114 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Sony News frame buffer driver for GhostScript */
+#include "gdevprn.h"
+#define prn_dev ((gx_device_printer *)dev) /* needed in 5.31 et seq */
+/*#include <sys/types.h> problems with ushort! */
+typedef char * caddr_t;
+typedef long off_t;
+#include <sys/uio.h>
+#include <newsiop/framebuf.h>
+
+/* The device descriptor */
+static dev_proc_open_device(sonyfb_open);
+static dev_proc_output_page(sonyfb_output_page);
+static dev_proc_close_device(sonyfb_close);
+static gx_device_procs sonyfb_procs =
+ prn_procs(sonyfb_open, sonyfb_output_page, sonyfb_close);
+const gx_device_printer far_data gs_sonyfb_device =
+ prn_device(sonyfb_procs, "sonyfb",
+ 102.4, /* width_10ths */
+ 103.2, /* height_10ths */
+ 100, /* x_dpi */
+ 100, /* y_dpi */
+ 0,0,0,0, /* margins */
+ 1, 0);
+
+static int fb_file = -1;
+sPrimRect prect;
+
+static int
+sonyfb_open(gx_device *dev)
+{
+ sScrType stype;
+
+ if(fb_file < 0)
+ if((fb_file = open("/dev/fb", 2)) < 0)
+ perror("open failed");
+ else
+ if(ioctl(fb_file, FBIOCGETSCRTYPE, &stype) < 0)
+ perror("ioctl failed");
+ else
+ prect.rect = stype.visiblerect;
+
+ return gdev_prn_open(dev);
+}
+
+static int
+sonyfb_close(gx_device *dev)
+{
+ if(fb_file >= 0)
+ {
+ close(fb_file);
+ fb_file = -1;
+ }
+ return gdev_prn_close(dev);
+}
+
+#define FRAME_WIDTH 1024
+
+/* Send the page to the printer. */
+static int
+sonyfb_output_page(gx_device *dev, int num_copies, int flush)
+{
+ int l, i, byte_width, height;
+ unsigned char *bm, *fbs, *fb;
+
+ byte_width = (dev->width + 7) / 8;
+ height = dev->height;
+ bm = (typeof(bm))prn_dev->mem.base;
+
+ prect.refPoint.x = 0;
+ prect.refPoint.y = 0;
+ prect.ptnRect = prect.rect;
+
+ prect.ptnBM.type = BM_MEM;
+ prect.ptnBM.depth = 1;
+ prect.ptnBM.width = (byte_width + 1) / 2;
+ prect.ptnBM.rect.origin.x = 0;
+ prect.ptnBM.rect.origin.y = 0;
+ prect.ptnBM.rect.extent.x = byte_width * 8; /* width in 16bit words */
+ prect.ptnBM.rect.extent.y = height;
+ prect.ptnBM.base = (typeof(prect.ptnBM.base))bm;
+
+ prect.fore_color = 1;
+ prect.aux_color = 0;
+ prect.planemask = FB_PLANEALL;
+ prect.transp = 0;
+ prect.func = BF_S;
+ prect.clip = prect.rect;
+ prect.drawBM.type = BM_FB;
+ prect.drawBM.depth = 1;
+ prect.drawBM.width = (prect.rect.extent.x + 15) / 16;
+ prect.drawBM.rect = prect.rect;
+ prect.drawBM.base = 0;
+
+ if(ioctl(fb_file, FBIOCRECTANGLE, &prect) < 0)
+ perror("rect ioctl failed");
+
+ return gx_finish_output_page(dev, num_copies, flush);
+}
diff --git a/devices/gdevsppr.c b/devices/gdevsppr.c
new file mode 100644
index 000000000..9fdbbaf72
--- /dev/null
+++ b/devices/gdevsppr.c
@@ -0,0 +1,187 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* SPARCprinter driver for Ghostscript */
+#include "gdevprn.h"
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/ioccom.h>
+#include <unbdev/lpviio.h>
+
+/*
+ Thanks to Martin Schulte (schulte@thp.Uni-Koeln.DE) for contributing
+ this driver to Ghostscript. He supplied the following notes.
+
+The device-driver (normally) returns two differnt types of Error-Conditions,
+FATALS and WARNINGS. In case of a fatal, the print routine returns -1, in
+case of a warning (such as paper out), a string describing the error is
+printed to stderr and the output-operation is repeated after five seconds.
+
+A problem is that not all possible errors seem to return the correct error,
+under some circumstance I get the same response as if an error repeated,
+that's why there is this the strange code about guessing the error.
+
+I didn't implement asynchronous IO (yet), because "`normal"' multipage-
+printings like TEX-Output seem to be printed with the maximum speed whereas
+drawings normally occur as one-page outputs, where asynchronous IO doesn't
+help anyway.
+*/
+
+static dev_proc_open_device(sparc_open);
+static dev_proc_print_page(sparc_print_page);
+
+#define SPARC_MARGINS_A4 0.15, 0.12, 0.12, 0.15
+#define SPARC_MARGINS_LETTER 0.15, 0.12, 0.12, 0.15
+
+/* Since the print_page doesn't alter the device, this device can print in the background */
+gx_device_procs prn_sparc_procs =
+ prn_procs(sparc_open, gdev_prn_bg_output_page, gdev_prn_close);
+
+const gx_device_printer far_data gs_sparc_device =
+prn_device(prn_sparc_procs,
+ "sparc",
+ DEFAULT_WIDTH_10THS,DEFAULT_HEIGHT_10THS,
+ 400,400,
+ 0,0,0,0,
+ 1,
+ sparc_print_page);
+
+/* Open the printer, and set the margins. */
+static int
+sparc_open(gx_device *pdev)
+{ /* Change the margins according to the paper size. */
+ const float *m;
+ static const float m_a4[4] = { SPARC_MARGINS_A4 };
+ static const float m_letter[4] = { SPARC_MARGINS_LETTER };
+
+ m = (pdev->height / pdev->y_pixels_per_inch >= 11.1 ? m_a4 : m_letter);
+ gx_device_set_margins(pdev, m, true);
+ return gdev_prn_open(pdev);
+}
+
+char *errmsg[]={
+ "EMOTOR",
+ "EROS",
+ "EFUSER",
+ "XEROFAIL",
+ "ILCKOPEN",
+ "NOTRAY",
+ "NOPAPR",
+ "XITJAM",
+ "MISFEED",
+ "WDRUMX",
+ "WDEVEX",
+ "NODRUM",
+ "NODEVE",
+ "EDRUMX",
+ "EDEVEX",
+ "ENGCOLD",
+ "TIMEOUT",
+ "EDMA",
+ "ESERIAL"
+ };
+
+/* The static buffer is unfortunate.... */
+static char err_buffer[80];
+static char *
+err_code_string(int err_code)
+ {
+ if ((err_code<EMOTOR)||(err_code>ESERIAL))
+ {
+ gs_sprintf(err_buffer,"err_code out of range: %d",err_code);
+ return err_buffer;
+ }
+ return errmsg[err_code];
+ }
+
+int warning=0;
+
+static int
+sparc_print_page(gx_device_printer *pdev, FILE *prn)
+ {
+ struct lpvi_page lpvipage;
+ struct lpvi_err lpvierr;
+ char *out_buf;
+ int out_size;
+ if (ioctl(fileno(prn),LPVIIOC_GETPAGE,&lpvipage)!=0)
+ {
+ errprintf(pdev->memory, "sparc_print_page: LPVIIOC_GETPAGE failed\n");
+ return -1;
+ }
+ lpvipage.bitmap_width=gdev_mem_bytes_per_scan_line((gx_device *)pdev);
+ lpvipage.page_width=lpvipage.bitmap_width*8;
+ lpvipage.page_length=pdev->height;
+ lpvipage.resolution = (pdev->x_pixels_per_inch == 300 ? DPI300 : DPI400);
+ if (ioctl(fileno(prn),LPVIIOC_SETPAGE,&lpvipage)!=0)
+ {
+ errprintf(pdev->memory, "sparc_print_page: LPVIIOC_SETPAGE failed\n");
+ return -1;
+ }
+ out_size=lpvipage.bitmap_width*lpvipage.page_length;
+ out_buf=gs_malloc(pdev->memory, out_size,1,"sparc_print_page: out_buf");
+ gdev_prn_copy_scan_lines(pdev,0,out_buf,out_size);
+ while (write(fileno(prn),out_buf,out_size)!=out_size)
+ {
+ if (ioctl(fileno(prn),LPVIIOC_GETERR,&lpvierr)!=0)
+ {
+ errprintf(pdev->memory, "sparc_print_page: LPVIIOC_GETERR failed\n");
+ return -1;
+ }
+ switch (lpvierr.err_type)
+ {
+ case 0:
+ if (warning==0)
+ {
+ errprintf(pdev->memory,
+ "sparc_print_page: Printer Problem with unknown reason...");
+ dmflush(pdev->memory);
+ warning=1;
+ }
+ sleep(5);
+ break;
+ case ENGWARN:
+ errprintf(pdev->memory,
+ "sparc_print_page: Printer-Warning: %s...",
+ err_code_string(lpvierr.err_code));
+ dmflush(pdev->memory);
+ warning=1;
+ sleep(5);
+ break;
+ case ENGFATL:
+ errprintf(pdev->memory,
+ "sparc_print_page: Printer-Fatal: %s\n",
+ err_code_string(lpvierr.err_code));
+ return -1;
+ case EDRVR:
+ errprintf(pdev->memory,
+ "sparc_print_page: Interface/driver error: %s\n",
+ err_code_string(lpvierr.err_code));
+ return -1;
+ default:
+ errprintf(pdev->memory,
+ "sparc_print_page: Unknown err_type=%d(err_code=%d)\n",
+ lpvierr.err_type,lpvierr.err_code);
+ return -1;
+ }
+ }
+ if (warning==1)
+ {
+ errprintf(pdev->memory, "OK.\n");
+ warning=0;
+ }
+ gs_free(pdev->memory, out_buf,out_size,1,"sparc_print_page: out_buf");
+ return 0;
+ }
diff --git a/devices/gdevstc.c b/devices/gdevstc.c
new file mode 100644
index 000000000..f35da113f
--- /dev/null
+++ b/devices/gdevstc.c
@@ -0,0 +1,3573 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Epson Stylus-Color Printer-Driver */
+
+/***
+ *** This file was "copied" from gdevcdj.c (ghostscript-3.12), which was
+ *** contributed by:
+ *** George Cameron - g.cameron@biomed.abdn.ac.ukis
+ *** Koert Zeilstra - koert@zen.cais.com
+ *** Eckhard Rueggeberg - eckhard@ts.go.dlr.de
+ ***
+ *** Some of the ESC/P2-code was drawn from gdevescp.c, contributed by
+ *** Richard Brown - rab@eos.ncsu.edu
+ ***
+ *** The POSIX-Interrupt-Code is from (Compile-Time-Option -DSTC_SIGNAL)
+ *** Frederic Loyer - loyer@ensta.fr
+ ***
+ *** And several improvements are based on discussions with
+ *** Brian Converse - BCONVERSE@ids.net
+ *** Bill Davidson - bdavidson@ra.isisnet.com
+ *** Gero Guenther - gero@cs.tu-berlin.de
+ *** Jason Patterson - jason@reflections.com.au
+ *** ? Rueschstroer - rue@ibe.med.uni-muenchen.de
+ *** Steven Singer - S.Singer@ph.surrey.ac.uk
+ ***
+ *** And the remaining little rest, mainly the bugs, were written by me:
+ *** Gunther Hess - gunther@elmos.de
+ ***
+ *** P.S.: there is some documentation, see devices.doc
+ ***
+ *** Revision-History:
+ *** 16-DEC-1994 1.1 - initial Version (GS-Dithering & Plain-Write)
+ ...
+ *** 30-JAN-1995 1.11 - FS-Improvements, u/sWeave, 1/4/24-Bits
+ *** 5-MAR-1995 1.12 - L. Peter Deutsch - updated put_params routine
+ (first distributed version with gs3.33)
+ *** 26-APR-1995 1.13 - merged Peters fixes with algorithmic changes:
+ Changed 24Bit-Mode, added 32Bit-Mode (moves colors)
+ [Arrgh: much better than 1.12, but patch was lost]
+ *** 5-JUN-1995 1.14 - Added Color-Correction & Transfer-Curves
+ (Several Beta-Testers, but not distributed)
+ ...
+ *** 24-JUL-1995 1.16 - Made dithering-Algorithms external-functions.
+ (Mailed for Beta-Distribution)
+ *** 10-AUG-1995 1.17 - Several Bug-Fixes and some new features:
+ CMYK10-Coding added
+ Readonly Parameters added
+ "Algorithms", "BitsPerComponent", "Version"
+ Parameters Flag0-4, Model, OutputCode
+ (mailed for distribution)
+ *** 14-SEP-1995 1.18 Fixes Bugs with Borland C (gs3.47)
+ *** 23-SEP-1995 1.19 - reorganized printcode + bug-fixing
+ *** 24-SEP-1995 1.20 - Little Cleanup for the release
+ *** 25-SEP-1995 1.21 - Readonly-Parameters added to put_params.
+ *** 31-Dec-1995 1.22 - Sanitary Engineering on the code
+ *** 16-Jan-1996 1.23 - Added String escp_Release
+ *** 8-May-1996 1.90 - Reintroduced Deltarow & Fixed MEMORY-BUG!
+ ***/
+
+#include "gdevstc.h"
+#ifdef STC_SIGNAL
+# include <signal.h>
+#endif /* STC_SIGNAL */
+/***
+ *** Mode-Table - the various algorithms
+ *** (The intention is, that this source can live alone)
+ ***/
+
+static stc_proc_dither(stc_gscmyk); /* resides in this file */
+static stc_proc_dither(stc_hscmyk); /* resides in this file */
+
+#include <stdlib.h> /* for rand, used in stc_hscmyk */
+
+static const stc_dither_t stc_dither[] = {
+ {"gscmyk", stc_gscmyk, DeviceCMYK|STC_BYTE|STC_DIRECT,0,{0.0,1.0}},
+ {"hscmyk", stc_hscmyk,
+ DeviceCMYK|STC_LONG|STC_CMYK10|STC_DIRECT|1*STC_SCAN,1+2*4,
+ {0.0, 1023.0}},
+ STC_MODI
+ { NULL , NULL , 0, 0,{0.0,0.0}}
+};
+
+/***
+ *** forward-declarations of routines
+ ***/
+
+/* Primary Device functions
+ * (I've the idea to rename the driver to stc)
+ */
+static dev_proc_print_page(stc_print_page);
+static dev_proc_open_device(stc_open);
+static dev_proc_close_device(stc_close);
+static dev_proc_get_params(stc_get_params);
+static dev_proc_put_params(stc_put_params);
+
+/*
+ * Color-Mapping-functions.
+ */
+
+/* routines for monochrome monochrome modi */
+static dev_proc_map_rgb_color(stc_map_gray_color);
+static dev_proc_map_color_rgb(stc_map_color_gray);
+
+/* routines for RGB-Modi */
+static dev_proc_map_rgb_color(stc_map_rgb_color);
+static dev_proc_map_color_rgb(stc_map_color_rgb);
+
+/* routines for general CMYK-Modi */
+static dev_proc_map_cmyk_color(stc_map_cmyk_color);
+static dev_proc_map_color_rgb(stc_map_color_cmyk);
+
+/* routines for 10Bit/Component CMYK */
+static dev_proc_map_cmyk_color(stc_map_cmyk10_color);
+static dev_proc_map_color_rgb(stc_map_color_cmyk10);
+
+/***
+ *** Table of Device-Procedures
+ ***/
+static gx_device_procs stcolor_procs = {
+ stc_open,
+ gx_default_get_initial_matrix,
+ gx_default_sync_output,
+ /* Since the print_page doesn't alter the device, this device can print in the background */
+ gdev_prn_bg_output_page,
+ stc_close,
+ NULL,
+ stc_map_color_cmyk,
+ NULL, /* fill_rectangle */
+ NULL, /* tile_rectangle */
+ NULL, /* copy_mono */
+ NULL, /* copy_color */
+ NULL, /* draw_line */
+ gx_default_get_bits,
+ stc_get_params,
+ stc_put_params,
+ stc_map_cmyk_color
+};
+
+/***
+ *** A local dummy-array for extvals
+ ***/
+
+static float defext[] = { 0.0, 1.0 };
+
+/***
+ *** Main device-control structure
+ ***/
+stcolor_device far_data gs_stcolor_device = {
+ prn_device_body(stcolor_device, stcolor_procs, "stcolor",
+ DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
+ X_DPI, Y_DPI,
+ STC_L_MARGIN,STC_B_MARGIN,STC_R_MARGIN,STC_T_MARGIN,
+ 4, 4, 1, 1, 2, 2, /* default: cmyk-direct */
+ stc_print_page),
+ {STCNWEAVE, /* stcflags: noWeave/bidirectional */
+ 1, /* stcbits: matches the default */
+ stc_dither, /* stcdither: first algorithm */
+ NULL, /* stcam: NULL -> not used */
+ { NULL, NULL, NULL, NULL}, /* extcode: none defined yet */
+ { 0, 0, 0, 0}, /* sizcode: 0, since no extcode yet */
+ { NULL, NULL, NULL, NULL}, /* stccode: computed by put_params */
+ {defext,defext,defext,defext},/* extvals: default */
+ { 2, 2, 2, 2}, /* sizvals: default countof(defext) */
+ { NULL, NULL, NULL, NULL}, /* stcvals: computed by put_params */
+ { 0, 0, 0}, /* white-run */
+ { 0, 0, 0}, /* white-end */
+ {NULL,0,false}, /* algorithm-table */
+ {NULL,0,false}, /* initialization-String (BOP) */
+ {NULL,0,false}, /* release-String (EOP) */
+ 0,0,0,0, /* New escp-stuff */
+ 1} /* itemsize used by algorithm */
+};
+/***
+ *** Test for white scan-lines
+ ***/
+static bool stc_iswhite(stcolor_device *, int, byte *);
+
+/***
+ *** Functions used for conversion inside the print-loop
+ ***/
+#define stc_proc_iconvert(Name) \
+byte * Name(stcolor_device *sd,byte *ext_data,int prt_pixels,byte *alg_line)
+
+static stc_proc_iconvert(stc_any_depth); /* general input-conversion */
+static stc_proc_iconvert(stc_rgb24_long); /* 24Bit RGB -> long's */
+
+static stc_proc_iconvert(stc_cmyk32_long); /* 32Bit CMYK -> long's */
+static stc_proc_iconvert(stc_any_direct); /* use ext_data as input */
+
+static stc_proc_iconvert(stc_cmyk10_byte); /* CMYK10->vals-> any type */
+static stc_proc_iconvert(stc_cmyk10_long); /* CMYK10->vals-> any type */
+static stc_proc_iconvert(stc_cmyk10_float); /* CMYK10->vals-> any type */
+static stc_proc_iconvert(stc_cmyk10_dbyte); /* CMYK10 direct bytes */
+static stc_proc_iconvert(stc_cmyk10_dlong); /* CMYK10 direct longs */
+
+/***
+ *** Print-functions
+ ***/
+static void stc_print_weave(stcolor_device *sd,FILE *prn_stream);
+static void stc_print_bands(stcolor_device *sd,FILE *prn_stream);
+static void stc_print_delta(stcolor_device *sd,FILE *prn_stream);
+static int stc_print_setup(stcolor_device *sd);
+
+/***
+ *** compute the ESC/P2 specific values
+ ***/
+
+static int
+stc_print_setup(stcolor_device *sd)
+{
+
+/*
+ * Compute the resolution-parameters
+ */
+ sd->stc.escp_u = (int)(3600.0 / sd->y_pixels_per_inch); /* y-units */
+ sd->stc.escp_h = (int)(3600.0 / sd->x_pixels_per_inch); /* x-units */
+ sd->stc.escp_v = sd->stc.flags & (STCUWEAVE | STCNWEAVE) ?
+ sd->stc.escp_u : 40;
+/*
+ * Initialize color
+ */
+ sd->stc.escp_c = 0; /* preselect-black */
+
+/*
+ * Band-Width
+ */
+ if((sd->stc.flags & STCBAND) == 0) {
+ if(sd->stc.escp_v != sd->stc.escp_u) sd->stc.escp_m = 15;
+ else if(STCSTCII == (sd->stc.flags & STCMODEL)) sd->stc.escp_m = 1;
+ else if( sd->stc.flags & STCUWEAVE) sd->stc.escp_m = 1;
+ else if((sd->stc.escp_v == sd->stc.escp_u) &&
+ (sd->stc.escp_u == 5)) sd->stc.escp_m = 1;
+ else sd->stc.escp_m = 1;
+ }
+
+/*
+ * Page-Dimensions
+ */
+ if((sd->stc.flags & STCWIDTH ) == 0)
+ sd->stc.escp_width = (int)(sd->width -
+ (dev_l_margin(sd)+dev_r_margin(sd))*sd->x_pixels_per_inch);
+
+ if((sd->stc.flags & STCHEIGHT) == 0)
+ sd->stc.escp_height = sd->height;
+
+ if((sd->stc.flags & STCTOP) == 0)
+ sd->stc.escp_top = (int)(dev_t_margin(sd)*sd->y_pixels_per_inch);
+
+ if((sd->stc.flags & STCBOTTOM) == 0)
+ sd->stc.escp_bottom = (int)(sd->height -
+ dev_b_margin(sd)*sd->y_pixels_per_inch);
+
+ if((sd->stc.flags & STCINIT) == 0) { /* No Initialization-String defined */
+ int need = 8 /* Reset, Graphics-Mode 1 */
+ + 6 /* MicroWeave */
+ + 6 /* Select Units */
+ + 7 /* Set Page-Length */
+ + 9 /* Set Margins */
+ + 3; /* Select Unidirectionality */
+ byte *bp = (byte *) (sd->stc.escp_init.data);
+
+ if(need != sd->stc.escp_init.size) { /* Reallocate */
+
+ if(NULL != (bp = gs_malloc(sd->memory, need,1,"stcolor/init"))) { /* Replace */
+ if(0 != sd->stc.escp_init.size)
+ gs_free(sd->memory, (byte *)sd->stc.escp_init.data,sd->stc.escp_init.size,1,
+ "stcolor/init");
+ sd->stc.escp_init.data = bp;
+ sd->stc.escp_init.size = need;
+ sd->stc.escp_init.persistent = false;
+ } else { /* Replace */
+ return_error(gs_error_VMerror);
+ }
+ }
+
+ if(need != 39) return_error(gs_error_unregistered);
+
+ memcpy(bp,
+/* 1 1 11 1 11 1 1 1 2 22 2 2 22 2 22 3 3 3333 3 33*/
+/* 0 1 2 34 5 6 7 8 90 1 23 4 56 7 8 9 0 12 3 4 56 7 89 0 1 2345 6 78*/
+"\033@\033(G\001\0\1\033(i\1\0w\033(U\001\000u\033(C\2\000hh\033(c\4\000ttbb\033U",
+ need);
+
+ if((sd->stc.flags & STCUWEAVE) != 0) bp[13] = '\1';
+ else bp[13] = '\0';
+
+ bp[19] = sd->stc.escp_u;
+
+ bp[25] = sd->stc.escp_height & 0xff;
+ bp[26] = (sd->stc.escp_height>>8) & 0xff;
+
+ bp[32] = sd->stc.escp_top & 0xff;
+ bp[33] = (sd->stc.escp_top>>8) & 0xff;
+ bp[34] = sd->stc.escp_bottom & 0xff;
+ bp[35] = (sd->stc.escp_bottom>>8) & 0xff;
+
+ if(sd->stc.flags & STCUNIDIR) bp[38] = 1;
+ else bp[38] = 0;
+
+ } /* No Initialization-String defined */
+
+ if((sd->stc.flags & STCRELEASE) == 0) { /* No Release-String defined */
+ int need = 3; /* ESC @ \f */
+ byte *bp = (byte *) (sd->stc.escp_release.data);
+
+ if(need != sd->stc.escp_release.size) { /* Reallocate */
+
+ if(NULL != (bp = gs_malloc(sd->memory, need,1,"stcolor/release"))) { /* Replace */
+ if(0 != sd->stc.escp_release.size)
+ gs_free(sd->memory, (byte *)sd->stc.escp_release.data,sd->stc.escp_release.size,1,
+ "stcolor/release");
+ sd->stc.escp_release.data = bp;
+ sd->stc.escp_release.size = need;
+ sd->stc.escp_release.persistent = false;
+ } else { /* Replace */
+ return_error(gs_error_VMerror);
+ }
+ }
+
+ if(need != 3) return_error(gs_error_unregistered);
+
+ memcpy(bp,"\033@\f",need);
+
+ } /* No Release-String defined */
+
+ return 0;
+}
+
+/***
+ *** stc_print_page: here we go to do the nasty work
+ ***/
+
+static int
+stc_print_page(gx_device_printer * pdev, FILE * prn_stream)
+{
+ stcolor_device *sd = (stcolor_device *) pdev;
+ long flags = sd == NULL ? 0 : sd->stc.flags;
+
+ int npass; /* # of print-passes (softweave) */
+
+ int ext_size; /* size of a ghostscript-scanline */
+ byte *ext_line; /* dyn: for this scanline */
+
+ int alg_size; /* size of a scanline for the dithering-algorithm */
+ byte *alg_line; /* dyn: 1 scanline for the dithering-algorithm */
+ int buf_size; /* size of the private-buffer for dither-function */
+ byte *buf; /* dyn: the private buffer */
+
+ int prt_pixels; /* Number of pixels printed */
+ byte *col_line; /* A Line with a byte per pixel */
+
+#define OK4GO ((flags & STCOK4GO) != 0)
+#define SORRY ( flags &= ~STCOK4GO)
+
+ if(0 > (npass = stc_print_setup(sd))) return_error(npass);
+
+ npass = sd->stc.escp_v / sd->stc.escp_u;
+
+/***
+ *** Allocate dynamic memory
+ ***/
+
+ ext_size = gdev_prn_raster(sd);
+ ext_line = gs_malloc(sd->memory, ext_size,1,"stc_print_page/ext_line");
+ if(ext_line == NULL) SORRY;
+
+ prt_pixels = sd->stc.escp_width;
+ sd->stc.prt_size = (prt_pixels+7)/8;
+ prt_pixels = sd->stc.prt_size * 8;
+
+ sd->stc.prt_scans = (int)(sd->height -
+ (dev_t_margin(sd)+dev_b_margin(sd))*sd->y_pixels_per_inch);
+
+ col_line = gs_malloc(sd->memory, prt_pixels,1,"stc_print_page/col_line");
+ if(col_line == NULL) SORRY;
+
+ alg_size = prt_pixels;
+ alg_size *= sd->color_info.num_components;
+
+ if((sd->stc.dither->flags & STC_DIRECT) ||
+ ((sd->stc.bits == 8) &&
+ (sd->stc.alg_item == 1))) {
+ alg_line = NULL;
+ } else {
+ alg_line = gs_malloc(sd->memory, alg_size,sd->stc.alg_item,"stc_print_page/alg_line");
+ if(alg_line == NULL) SORRY;
+ }
+
+ buf_size = sd->stc.dither->bufadd
+ + alg_size*(sd->stc.dither->flags/STC_SCAN);
+ if(buf_size > 0) {
+ buf = gs_malloc(sd->memory, buf_size,sd->stc.alg_item,"stc_print_page/buf");
+ if(buf == NULL) SORRY;
+ } else {
+ buf = NULL;
+ }
+
+/*
+ * compute the number of printer-buffers
+ */
+
+ for(sd->stc.prt_buf = 16; sd->stc.prt_buf < (sd->stc.escp_m * npass);
+ sd->stc.prt_buf <<= 1);
+ if(sd->color_info.num_components > 1) sd->stc.prt_buf *= 4;
+
+ sd->stc.prt_width = gs_malloc(sd->memory, sd->stc.prt_buf,sizeof(int),
+ "stc_print_page/prt_width");
+ if(sd->stc.prt_width == NULL) SORRY;
+
+ sd->stc.prt_data = gs_malloc(sd->memory, sd->stc.prt_buf,sizeof(byte *),
+ "stc_print_page/prt_data");
+
+ if(sd->stc.prt_data == NULL) {
+ SORRY;
+ } else {
+ int i;
+
+ for(i = 0; i < sd->stc.prt_buf; ++i) {
+ sd->stc.prt_data[i] = gs_malloc(sd->memory, sd->stc.prt_size,1,
+ "stc_print_page/prt");
+ if(sd->stc.prt_data[i] == NULL) SORRY;
+ }
+ }
+
+ sd->stc.seed_size = (sd->stc.prt_size + 2*sizeof(int) - 1)/sizeof(int);
+ {
+ int i;
+ for(i = 0; i < sd->color_info.num_components; ++i) {
+ if((flags & STCCOMP) == STCDELTA) {
+ sd->stc.seed_row[i] = gs_malloc(sd->memory, sd->stc.seed_size,sizeof(int),
+ "stc_print_page/seed_row");
+ if(sd->stc.seed_row[i] == NULL) SORRY;
+ else memset(sd->stc.seed_row[i],0,sd->stc.seed_size*sizeof(int));
+ } else {
+ sd->stc.seed_row[i] = NULL;
+ }
+ }
+ while(i < countof(sd->stc.seed_row)) sd->stc.seed_row[i++] = NULL;
+ }
+
+ switch(flags & STCCOMP) {
+ case STCPLAIN:
+ sd->stc.escp_size = 64 + sd->stc.prt_size;
+ break;
+ case STCDELTA:
+ sd->stc.escp_size = 64 + 2 * sd->stc.prt_size;
+ break;
+ default:
+ sd->stc.escp_size = 64 +
+ sd->stc.prt_size + (sd->stc.prt_size + 127)/128;
+ break;
+ }
+
+ sd->stc.escp_data = gs_malloc(sd->memory, sd->stc.escp_size,1,
+ "stc_print_page/escp_data");
+ if(sd->stc.escp_data == NULL) SORRY;
+
+/*
+ * If we're still ok, we can print something
+ */
+
+ if(OK4GO) {
+
+ int ncolor;
+ int buf_i;
+ stc_proc_iconvert((*iconvert)) = stc_any_depth;
+
+/*
+ * initialize col_line
+ */
+ if(sd->color_info.num_components == 3) {
+ memset(col_line,RED|GREEN|BLUE,prt_pixels);
+ } else {
+ memset(col_line,0, prt_pixels);
+ }
+
+/*
+ * select proper conversion for input to algorithm
+ */
+ if( (sd->stc.dither->flags & STC_DIRECT ) ||
+ ((sd->stc.bits == 8) &&
+ (sd->stc.alg_item == 1)))
+ iconvert = stc_any_direct;
+ else if((sd->color_info.num_components == 3) &&
+ (sd->color_info.depth == 24) &&
+ (sizeof(long) == sd->stc.alg_item))
+ iconvert = stc_rgb24_long;
+ else if(sd->stc.flags & STCCMYK10) {
+ if( ((sd->stc.dither->flags & STC_TYPE) == STC_BYTE) &&
+ ( sd->stc.dither->minmax[0] == 0.0 ))
+ iconvert = stc_cmyk10_dbyte;
+ else if ((sd->stc.dither->flags & STC_TYPE) == STC_BYTE)
+ iconvert = stc_cmyk10_byte;
+ else if(((sd->stc.dither->flags & STC_TYPE) == STC_LONG) &&
+ ( sd->stc.dither->minmax[0] == 0.0 ) &&
+ ( sd->stc.dither->minmax[1] <= 1023.0 ))
+ iconvert = stc_cmyk10_dlong;
+ else if( (sd->stc.dither->flags & STC_TYPE) == STC_LONG)
+ iconvert = stc_cmyk10_long;
+ else
+ iconvert = stc_cmyk10_float;
+ }
+ else if((sd->color_info.num_components == 4) &&
+ (sd->color_info.depth == 32) &&
+ (sizeof(long) == sd->stc.alg_item))
+ iconvert = stc_cmyk32_long;
+
+/*
+ * initialize the algorithm
+ */
+
+ if((*sd->stc.dither->fun)(sd,-prt_pixels,alg_line,buf,col_line) < 0)
+ SORRY;
+
+/*
+ * Main-Print-Loop
+ */
+
+ if(OK4GO) {
+#ifdef STC_SIGNAL
+ sigset_t stc_int_mask, stc_int_save, stc_int_pending;
+
+ sigemptyset(&stc_int_mask);
+ sigaddset(&stc_int_mask,SIGINT);
+ sigprocmask(SIG_BLOCK,&stc_int_mask, &stc_int_save);
+#endif /* STC_SIGNAL */
+
+ if(sd->color_info.num_components > 1) ncolor = 4;
+ else ncolor = 1;
+
+/*
+ * Decide, wether we Adjust Linefeeds or not. (I hate it here)
+ */
+ if((0 == ((sd->stc.escp_m*sd->stc.escp_u) % 10)) &&
+ (256 > ((sd->stc.escp_m*sd->stc.escp_u) / 10))) sd->stc.escp_lf = sd->stc.escp_m;
+ else sd->stc.escp_lf = 0;
+
+/*
+ * prepare run-values, then loop over scans
+ */
+ sd->stc.stc_y = 0; /* current printer y-Position */
+ sd->stc.buf_y = 0; /* Top-Position within the buffer */
+ sd->stc.prt_y = 0; /* physical position of the printer */
+ buf_i = 0; /* next free line in buffer */
+ sd->stc.flags &= ~STCPRINT; /* no data yet */
+
+ while(sd->stc.stc_y < sd->stc.prt_scans) { /* Until all scans are processed */
+ int need;
+
+ need = sd->stc.stc_y + npass * sd->stc.escp_m;
+
+ if(sd->stc.buf_y < need) { /* Nr. 5 (give me input) */
+
+/* read as much as the buffer can hold */
+ if(ncolor == 1) need = sd->stc.stc_y + sd->stc.prt_buf;
+ else need = sd->stc.stc_y + (sd->stc.prt_buf>>2);
+
+ for(;sd->stc.buf_y < need;
+ buf_i = (sd->stc.prt_buf-1) & (buf_i+ncolor),
+ ++sd->stc.buf_y) {
+
+ int color;
+ byte *ext_data;
+ byte *alg_data;
+
+/* initialize output data 1st -> may take shortcut */
+
+ for(color = 0; color < ncolor; ++color) {
+ memset(sd->stc.prt_data[buf_i+color],0,sd->stc.prt_size);
+ sd->stc.prt_width[buf_i+color] = 0;
+ }
+
+/* "read data", immediately continue if all is white */
+
+ if(sd->stc.buf_y < sd->stc.prt_scans) { /* Test for White */
+
+ gdev_prn_get_bits(pdev,sd->stc.buf_y,ext_line,&ext_data);
+
+ color = stc_iswhite(sd,prt_pixels,ext_data) ? ext_size : 0;
+
+ } else {
+
+ color = ext_size;
+
+ } /* Test for White */
+
+ if(color >= ext_size) { /* bypass processing */
+
+ if(sd->stc.dither->flags & STC_WHITE)
+ (*sd->stc.dither->fun)(sd,prt_pixels,NULL,buf,col_line);
+ continue;
+
+ } /* bypass processing */
+
+/* convert data for the various cases */
+
+ alg_data = (*iconvert)(sd,ext_data,prt_pixels,alg_line);
+
+/*
+ * invoke the dithering-algorithm
+ */
+
+ (*sd->stc.dither->fun)(sd,prt_pixels,alg_data,buf,col_line);
+/*
+ * convert col_line to printer-format (separate colors)
+ */
+ switch(sd->color_info.num_components) {
+ case 1: /* Black & White: just merge into 8 Bytes */
+ {
+ byte *bytein,*byteout;
+ int width;
+
+ bytein = col_line;
+ byteout = sd->stc.prt_data[buf_i];
+
+ for(width = 1; width <= sd->stc.prt_size; ++width) {
+ byte tmp = 0;
+ byte i;
+
+ for(i = 128; i; i >>= 1) if(*bytein++) tmp |= i;
+
+ if(tmp != 0) sd->stc.prt_width[buf_i] = width;
+
+ *byteout++ = tmp;
+ }
+ }
+ break;
+ case 3: /* convert rgb into cmyk */
+ {
+ byte *bytein;
+ int width;
+
+ bytein = col_line;
+
+ for(width = 0; width < sd->stc.prt_size; ++width) {
+ byte i,tmp,cmyk[4];
+
+ memset(cmyk,0,4);
+
+ for(i = 128; i; i >>= 1) {
+ static const byte rgb2cmyk[] = {
+ BLACK, /* 0->Black */
+ CYAN | MAGENTA, /* 1->BLUE */
+ CYAN | YELLOW, /* 2->GREEN */
+ CYAN, /* 3->CYAN */
+ MAGENTA | YELLOW, /* 4->RED */
+ MAGENTA, /* 5->MAGENTA */
+ YELLOW, /* 6->YELLOW */
+ 0}; /* 7->WHITE */
+
+ tmp = rgb2cmyk[(*bytein++) & 7];
+
+ if(tmp & BLACK) cmyk[3] |= i;
+ if(tmp & YELLOW) cmyk[2] |= i;
+ if(tmp & MAGENTA) cmyk[1] |= i;
+ if(tmp & CYAN) cmyk[0] |= i;
+ }
+
+ for(i = 0; i < 4; ++i) {
+ if(cmyk[i] != 0) sd->stc.prt_width[buf_i+i] = width+1;
+ sd->stc.prt_data[buf_i+i][width] = cmyk[i];
+ }
+ }
+ }
+ break;
+ case 4: /* split cmyk */
+ {
+ byte *bytein;
+ int width;
+
+ bytein = col_line;
+
+ for(width = 0; width < sd->stc.prt_size; ++width) {
+ byte i,tmp,cmyk[4];
+
+ memset(cmyk,0,4);
+
+ for(i = 128; i; i >>= 1) {
+ tmp = (*bytein++) & 15;
+ if(tmp & BLACK) cmyk[3] |= i;
+ if(tmp & YELLOW) cmyk[2] |= i;
+ if(tmp & MAGENTA) cmyk[1] |= i;
+ if(tmp & CYAN) cmyk[0] |= i;
+ }
+
+ for(i = 0; i < 4; ++i) {
+ if(cmyk[i] != 0) sd->stc.prt_width[buf_i+i] = width+1;
+ sd->stc.prt_data[buf_i+i][width] = cmyk[i];
+ }
+ }
+ }
+ break;
+ default: break;
+ }
+ }
+ } /* Nr. 5 (give me input) */
+
+/*
+ * Nr. 5 has got enough input, now we should print it
+ */
+ if((flags & STCCOMP) == STCDELTA) stc_print_delta(sd,prn_stream);
+ else if(npass > 1) stc_print_weave(sd,prn_stream);
+ else stc_print_bands(sd,prn_stream);
+
+#ifdef STC_SIGNAL
+ sigpending(&stc_int_pending);
+ if(sigismember(&stc_int_pending,SIGINT)) {
+ fputs("\033@[Aborted]\f", prn_stream);
+ fflush(prn_stream);
+ sigprocmask(SIG_SETMASK,&stc_int_save,NULL);
+ break;
+ }
+#endif /* STC_SIGNAL */
+
+ } /* Until all scans are processed */
+
+ if(sd->stc.flags & STCPRINT) {
+ if((flags & STCCOMP) == STCDELTA) fputc(0xe3,prn_stream);
+ fwrite(sd->stc.escp_release.data,1,sd->stc.escp_release.size,prn_stream);
+ fflush(prn_stream);
+ }
+#ifdef STC_SIGNAL
+ sigprocmask(SIG_SETMASK,&stc_int_save,NULL);
+#endif /* STC_DIGNAL */
+
+ }
+ }
+
+/***
+ *** Release the dynamic memory
+ ***/
+
+ if(ext_line != NULL)
+ gs_free(sd->memory, ext_line,ext_size,1,"stc_print_page/ext_line");
+
+ if(col_line != NULL)
+ gs_free(sd->memory, col_line,prt_pixels,1,"stc_print_page/col_line");
+
+ if(alg_line != NULL)
+ gs_free(sd->memory, alg_line,alg_size,sd->stc.alg_item,
+ "stc_print_page/alg_line");
+
+ if(buf != NULL)
+ gs_free(sd->memory, buf,buf_size,sd->stc.alg_item,"stc_print_page/buf");
+
+ if(sd->stc.prt_width != NULL)
+ gs_free(sd->memory, sd->stc.prt_width,sd->stc.prt_buf,sizeof(int),
+ "stc_print_page/prt_width");
+
+ if(sd->stc.prt_data != NULL) {
+ int i;
+
+ for(i = 0; i < sd->stc.prt_buf; ++i) {
+ if(sd->stc.prt_data[i] != NULL)
+ gs_free(sd->memory, sd->stc.prt_data[i],sd->stc.prt_size,1,
+ "stc_print_page/prt");
+ }
+
+ gs_free(sd->memory, sd->stc.prt_data,sd->stc.prt_buf,sizeof(byte *),
+ "stc_print_page/prt_data");
+ }
+
+ {
+ int i;
+ for(i = 0; i < sd->color_info.num_components; ++i) {
+ if(sd->stc.seed_row[i] != NULL)
+ gs_free(sd->memory, sd->stc.seed_row[i],sd->stc.seed_size,sizeof(int),
+ "stc_print_page/seed_row");
+ }
+ }
+
+ if(sd->stc.escp_data != NULL)
+ gs_free(sd->memory, sd->stc.escp_data,sd->stc.escp_size,1,
+ "stc_print_page/escp_data");
+
+ return OK4GO ? 0 : gs_error_undefined;
+}
+
+/*
+ * white-check
+ */
+static bool
+stc_iswhite(stcolor_device *sd, int prt_pixels,byte *ext_data)
+{
+ long b2do = (prt_pixels*sd->color_info.depth+7)>>3;
+ int bcmp = 4 * countof(sd->stc.white_run);
+ byte *wht = (byte *) sd->stc.white_run;
+
+ while(b2do >= bcmp) {
+ if(memcmp(ext_data,wht,bcmp)) break;
+ ext_data += bcmp;
+ b2do -= bcmp;
+ }
+
+ if((b2do > 0) && (b2do < bcmp))
+ b2do = memcmp(ext_data,sd->stc.white_end,b2do);
+
+ return b2do ? false : true;
+}
+
+/***
+ *** A bunch of routines that convert gslines into algorithms format.
+ ***/
+static byte *
+stc_any_depth(stcolor_device *sd,byte *ext_data,int prt_pixels,byte *alg_line)
+{ /* general conversion */
+
+ int p,c, niext, nbits;
+ gx_color_index ciext,ci,cimsk,cvmsk;
+ byte *ap = alg_line;
+
+ nbits = sd->stc.bits;
+ cvmsk = ((gx_color_index) 1<<nbits) - 1;
+
+/* it is nonsense to use this algorithm for this cases, but if it claims
+ * generality, it should deliver correct results in this cases too */
+ if(sd->color_info.depth == (sd->color_info.num_components<<3)) nbits = 8;
+
+ cimsk = cvmsk;
+ for(c = 1; c < sd->color_info.num_components; ++c)
+ cimsk = (cimsk<<nbits) | cvmsk;
+
+ ciext = 0;
+ niext = 0;
+
+ for(p = 0; p < prt_pixels; ++p) { /* over pixels */
+
+ ci = ciext;
+ for(c = sd->color_info.depth-niext; c >= 8; c -= 8)
+ ci = (ci<<8) | *ext_data++;
+
+ if(c > 0) { /* partial byte required */
+
+ niext = 8 - c;
+ ciext = *ext_data++;
+ ci = (ci<<c) | (ciext>>niext);
+ ciext &= (1L<<niext)-1;
+
+ } else if(c < 0) { /* some bits left in ciext */
+
+ niext = -c;
+ ciext &= (1L<<niext)-1;
+ ci = ci>>niext;
+
+ } else { /* entire ciext used */
+
+ niext = 0;
+ ciext = 0;
+
+ } /* ciext-adjust */
+
+ ci &= cimsk;
+
+# define stc_storeapc(T) \
+ ((T *)ap)[c] = ((T *)(sd->stc.vals[c]))[ci & cvmsk];
+
+ for(c = sd->color_info.num_components; c--;) { /* comp */
+ STC_TYPESWITCH(sd->stc.dither,stc_storeapc)
+ ci >>= nbits;
+ } /* comp */
+
+# undef stc_storeapc
+
+ ap += sd->color_info.num_components * sd->stc.alg_item;
+
+ } /* over pixels */
+
+ return alg_line;
+} /* general conversion */
+
+/*
+ * rgb-data with depth=24, can use a faster algorithm
+ */
+static byte *
+stc_rgb24_long(stcolor_device *sd,byte *ext_data,int prt_pixels,byte *alg_line)
+{ /* convert 3 bytes into appropriate long-Values */
+ register int p;
+ register long *out = (long *) alg_line;
+ register long *rvals = (long *) (sd->stc.vals[0]);
+ register long *gvals = (long *) (sd->stc.vals[1]);
+ register long *bvals = (long *) (sd->stc.vals[2]);
+
+ for(p = prt_pixels; p; --p) {
+ *out++ = rvals[*ext_data++];
+ *out++ = gvals[*ext_data++];
+ *out++ = bvals[*ext_data++];
+ }
+
+ return alg_line;
+} /* convert 3 bytes into appropriate long-Values */
+
+/*
+ * cmyk-data with depth=32, can use a faster algorithm
+ */
+static byte *
+stc_cmyk32_long(stcolor_device *sd,byte *ext_data,int prt_pixels,byte *alg_line)
+{ /* convert 4 bytes into appropriate long-Values */
+ register int p;
+ register long *out = (long *) alg_line;
+ register long *cvals = (long *) (sd->stc.vals[0]);
+ register long *mvals = (long *) (sd->stc.vals[1]);
+ register long *yvals = (long *) (sd->stc.vals[2]);
+ register long *kvals = (long *) (sd->stc.vals[3]);
+
+ for(p = prt_pixels; p; --p) {
+ *out++ = cvals[*ext_data++];
+ *out++ = mvals[*ext_data++];
+ *out++ = yvals[*ext_data++];
+ *out++ = kvals[*ext_data++];
+ }
+
+ return alg_line;
+} /* convert 4 bytes into appropriate long-Values */
+
+/*
+ * handle indirect encoded cmyk-data
+ */
+#define STC_CMYK10_ANY(T)\
+ \
+ register int p = prt_pixels; \
+ register stc_pixel ci,k,n,mode; \
+ register stc_pixel *in = (stc_pixel *) ext_data; \
+ register T *out = (T *) alg_line; \
+ register T *cv = (T *) sd->stc.vals[0]; \
+ register T *mv = (T *) sd->stc.vals[1]; \
+ register T *yv = (T *) sd->stc.vals[2]; \
+ register T *kv = (T *) sd->stc.vals[3]; \
+ \
+ while(p--) { \
+ ci = *in++; \
+ mode = ci & 3; \
+ k = (ci>>2) & 0x3ff; \
+ if(mode == 3) { \
+ *out++ = cv[0]; \
+ *out++ = mv[0]; \
+ *out++ = yv[0]; \
+ *out++ = kv[k]; \
+ } else { \
+ out[3] = kv[k]; \
+ n = (ci>>12) & 0x3ff; \
+ if(mode == 2) { out[2] = yv[k]; } \
+ else { out[2] = yv[n]; n = (ci>>22) & 0x3ff; } \
+ if(mode == 1) { out[1] = mv[k]; } \
+ else { out[1] = mv[n]; n = (ci>>22) & 0x3ff; } \
+ if(mode == 0) out[0] = cv[k]; \
+ else out[0] = cv[n]; \
+ out += 4; \
+ } \
+ } \
+ \
+ return alg_line;
+
+static byte *
+stc_cmyk10_byte(stcolor_device *sd,
+ byte *ext_data,int prt_pixels,byte *alg_line)
+{
+ STC_CMYK10_ANY(byte)
+}
+static byte *
+stc_cmyk10_long(stcolor_device *sd,
+ byte *ext_data,int prt_pixels,byte *alg_line)
+{
+ STC_CMYK10_ANY(long)
+}
+static byte *
+stc_cmyk10_float(stcolor_device *sd,
+ byte *ext_data,int prt_pixels,byte *alg_line)
+{
+ STC_CMYK10_ANY(float)
+}
+
+#undef STC_CMYK10_ANY
+
+#define STC_CMYK10_DANY(T)\
+ \
+ register int p = prt_pixels; \
+ register stc_pixel ci,k,n,mode; \
+ register stc_pixel *in = (stc_pixel *) ext_data; \
+ register T *out = (T *) alg_line; \
+ \
+ while(p--) { \
+ ci = *in++; \
+ mode = ci & 3; \
+ k = (ci>>2) & 0x3ff; \
+ if(mode == 3) { \
+ *out++ = 0; \
+ *out++ = 0; \
+ *out++ = 0; \
+ *out++ = k; \
+ } else { \
+ out[3] = k; \
+ n = (ci>>12) & 0x3ff; \
+ if(mode == 2) { out[2] = k; } \
+ else { out[2] = n; n = (ci>>22) & 0x3ff; } \
+ if(mode == 1) { out[1] = k; } \
+ else { out[1] = n; n = (ci>>22) & 0x3ff; } \
+ if(mode == 0) out[0] = k; \
+ else out[0] = n; \
+ out += 4; \
+ } \
+ } \
+ \
+ return alg_line;
+
+static byte *
+stc_cmyk10_dbyte(stcolor_device *sd,
+ byte *ext_data,int prt_pixels,byte *alg_line)
+{
+ STC_CMYK10_DANY(byte)
+}
+static byte *
+stc_cmyk10_dlong(stcolor_device *sd,
+ byte *ext_data,int prt_pixels,byte *alg_line)
+{
+ STC_CMYK10_DANY(long)
+}
+
+#undef STC_CMYK10_DANY
+
+/*
+ * if the algorithm uses bytes & bytes are in ext_data, use them
+ */
+/*ARGSUSED*/
+static byte *
+stc_any_direct(stcolor_device *sd,byte *ext_data,int prt_pixels,byte *alg_line)
+{ /* return ext_data */
+ return ext_data;
+} /* return ext_data */
+
+/* ----------------------------------------------------------------------- */
+/* stc_rle: epson ESC/P2 RLE-Encoding
+ */
+static int
+stc_rle(byte *out,const byte *in,int width)
+{
+
+ int used = 0;
+ int crun,cdata;
+ byte run;
+
+ if(in != NULL) { /* Data present */
+
+ crun = 1;
+
+ while(width > 0) { /* something to compress */
+
+ run = in[0];
+
+ while((width > crun) && (run == in[crun])) if(++crun == 129) break;
+
+ if((crun > 2) || (crun == width)) { /* use this run */
+
+ *out++ = (257 - crun) & 0xff; *out++ = run; used += 2;
+
+ width -= crun; in += crun;
+ crun = 1;
+
+ } else { /* ignore this run */
+
+ for(cdata = crun; (width > cdata) && (crun < 4);) {
+ if(run == in[cdata]) crun += 1;
+ else run = in[cdata], crun = 1;
+ if(++cdata == 128) break;
+ }
+
+ if(crun < 3) crun = 0; /* ignore trailing run */
+ else cdata -= crun;
+
+ *out++ = cdata-1; used++;
+ memcpy(out,in,cdata); used += cdata; out += cdata;
+
+ width -= cdata; in += cdata;
+
+ } /* use/ignore run */
+
+ } /* something to compress */
+
+ } else { /* Empty scans to fill bands */
+
+ while(width > 0) {
+ crun = width > 129 ? 129 : width;
+ width -= crun;
+ *out++ = (257 - crun) & 0xff;
+ *out++ = 0;
+ used += 2;
+ }
+ } /* Data present or empty */
+ return used;
+}
+
+/*
+ * Horizontal & vertical positioning, color-selection, "ESC ."
+ */
+static int
+stc_print_escpcmd(stcolor_device *sd, FILE *prn_stream,
+ int escp_used, int color,int m,int wbytes)
+{
+
+ int dy = sd->stc.stc_y - sd->stc.prt_y; /* number of units to skip */
+ int nlf;
+
+/* ESC-R color codes, used only here */
+ static const byte stc_colors[] = { 0x02, 0x01, 0x04, 0x00 }; /* CMYK */
+
+/*
+ * initialize the printer, if necessary
+ */
+ if(0 == (sd->stc.flags & STCPRINT)) {
+
+ fwrite(sd->stc.escp_init.data,1,sd->stc.escp_init.size,prn_stream);
+
+ if(0 < sd->stc.escp_lf) { /* Adjust Linefeed */
+ fputc('\033', prn_stream);
+ fputc('+', prn_stream);
+ fputc(((sd->stc.escp_m*sd->stc.escp_u) / 10),prn_stream);
+ } /* Adjust Linefeed */
+ sd->stc.flags |= STCPRINT;
+ }
+
+ sd->stc.escp_data[escp_used++] = '\r'; /* leftmost position */
+
+ if(dy) { /* position the printer */
+ if(( sd->stc.escp_lf > 0) && /* Linefeed allowed */
+ ((dy % sd->stc.escp_lf) == 0)) /* and possible */
+ nlf = dy / sd->stc.escp_lf;
+ else nlf = 7;
+
+ if(nlf > 6) {
+ sd->stc.escp_data[escp_used++] = '\033';
+ sd->stc.escp_data[escp_used++] = '(';
+ sd->stc.escp_data[escp_used++] = 'V';
+ sd->stc.escp_data[escp_used++] = '\002';
+ sd->stc.escp_data[escp_used++] = '\000';
+ sd->stc.escp_data[escp_used++] = sd->stc.stc_y & 0xff;
+ sd->stc.escp_data[escp_used++] = (sd->stc.stc_y >> 8) & 0xff;
+ } else {
+ while(nlf--) sd->stc.escp_data[escp_used++] = '\n';
+ }
+ sd->stc.prt_y = sd->stc.stc_y;
+ } /* position the printer */
+
+ if((sd->color_info.num_components > 1) &&
+ (sd->stc.escp_c != stc_colors[color])) { /* select color */
+ sd->stc.escp_data[escp_used++] = '\033';
+ sd->stc.escp_data[escp_used++] = 'r';
+ sd->stc.escp_c = stc_colors[color];
+ sd->stc.escp_data[escp_used++] = sd->stc.escp_c;
+ } /* select color */
+
+/*
+ * Build the command used
+ */
+ sd->stc.escp_data[escp_used++] = '\033';
+ sd->stc.escp_data[escp_used++] = '.';
+ sd->stc.escp_data[escp_used++] =
+ (sd->stc.flags & STCCOMP) == STCPLAIN ? 0 : 1;
+ sd->stc.escp_data[escp_used++] = sd->stc.escp_v;
+ sd->stc.escp_data[escp_used++] = sd->stc.escp_h;
+ sd->stc.escp_data[escp_used++] = m;
+ sd->stc.escp_data[escp_used++] = (wbytes<<3) & 0xff; /* width in Pixels */
+ sd->stc.escp_data[escp_used++] = (wbytes>>5) & 0xff;
+
+ return escp_used;
+}
+
+/*
+ * compute width of a group of scanlines
+ */
+static int
+stc_bandwidth(stcolor_device *sd,int color,int m,int npass)
+{
+ int ncolor = sd->color_info.num_components == 1 ? 1 : 4;
+ int buf_a = (sd->stc.prt_buf-1) & (sd->stc.stc_y * ncolor + color);
+ int w = 0;
+
+ while(m-- > 0) { /* check width */
+ if(sd->stc.prt_width[buf_a] > w) w = sd->stc.prt_width[buf_a];
+ buf_a = (sd->stc.prt_buf-1) & (buf_a + ncolor * npass);
+ } /* check width */
+
+ return w;
+}
+
+/*
+ * Multi-Pass Printing-Routine
+ */
+static void
+stc_print_weave(stcolor_device *sd, FILE *prn_stream)
+{
+
+ int escp_used,nprint,nspace,color,buf_a,iprint,w;
+
+ int npass = sd->stc.escp_v / sd->stc.escp_u;
+ int ncolor = sd->color_info.num_components == 1 ? 1 : 4;
+
+ while(sd->stc.stc_y < sd->stc.prt_scans) {
+
+/*
+ * compute spacing & used heads (seems to work with odd escp_m)
+ */
+ if(sd->stc.stc_y >= sd->stc.escp_m) { /* in normal mode */
+ nprint = sd->stc.escp_m;
+ nspace = sd->stc.escp_m;
+ } else if((sd->stc.stc_y) < npass) { /* initialisation */
+ nprint = sd->stc.escp_m - sd->stc.stc_y * ((sd->stc.escp_m+1)/npass);
+ nspace = 1;
+ } else { /* switch to normal */
+ nprint = sd->stc.escp_m - sd->stc.stc_y * ((sd->stc.escp_m+1)/npass);
+ nspace = sd->stc.escp_m - sd->stc.stc_y;
+ }
+ iprint = sd->stc.stc_y + npass * nprint;
+ if(sd->stc.buf_y < iprint) break;
+
+ escp_used = 0;
+ for(color = 0; color < ncolor; ++color) { /* print the colors */
+
+ if(0 == (w = stc_bandwidth(sd,color,nprint,npass))) continue;
+
+ escp_used = stc_print_escpcmd(sd,prn_stream,
+ escp_used,color,sd->stc.escp_m,w);
+
+ buf_a = (sd->stc.prt_buf-1) & (sd->stc.stc_y * ncolor + color);
+ for(iprint = 0; iprint < nprint; ++iprint) { /* send data */
+
+ if((sd->stc.flags & STCCOMP) == STCPLAIN) {
+ memcpy(sd->stc.escp_data+escp_used,sd->stc.prt_data[buf_a],w);
+ escp_used += w;
+ } else {
+ escp_used += stc_rle(sd->stc.escp_data+escp_used,
+ sd->stc.prt_data[buf_a],w);
+ }
+
+ fwrite(sd->stc.escp_data,1,escp_used,prn_stream);
+ escp_used = 0;
+
+ buf_a = (sd->stc.prt_buf-1) & (buf_a + ncolor * npass);
+
+ } /* send data */
+
+ while(iprint++ < sd->stc.escp_m) { /* add empty rows */
+
+ if((sd->stc.flags & STCCOMP) == STCPLAIN) {
+ memset(sd->stc.escp_data+escp_used,0,w);
+ escp_used += w;
+ } else {
+ escp_used += stc_rle(sd->stc.escp_data+escp_used,NULL,w);
+ }
+
+ fwrite(sd->stc.escp_data,1,escp_used,prn_stream);
+ escp_used = 0;
+ } /* add empty rows */
+ } /* print the colors */
+
+ sd->stc.stc_y += nspace;
+ }
+}
+
+/*
+ * Single-Pass printing-Routine
+ */
+static void
+stc_print_bands(stcolor_device *sd, FILE *prn_stream)
+{
+
+ int escp_used,color,buf_a,iprint,w,m;
+
+ int ncolor = sd->color_info.num_components == 1 ? 1 : 4;
+
+ while(sd->stc.stc_y < sd->stc.prt_scans) {
+
+/*
+ * find the begin of the band
+ */
+ for(w = 0; sd->stc.stc_y < sd->stc.buf_y; ++sd->stc.stc_y) {
+ buf_a = (sd->stc.prt_buf-1) & (sd->stc.stc_y * ncolor);
+ for(color = 0; color < ncolor; ++color)
+ if(sd->stc.prt_width[buf_a+color] > w)
+ w = sd->stc.prt_width[buf_a+color];
+ if(w != 0) break;
+ }
+ if(w == 0) break;
+/*
+ * adjust the band-height
+ */
+ w = sd->stc.prt_scans - sd->stc.stc_y;
+ if((w < sd->stc.escp_m) && (sd->stc.escp_v != 40)) {
+ if(w < 8) m = 1;
+ else if(w < 24) m = 8;
+ else m = 24;
+ } else {
+ m = sd->stc.escp_m;
+ }
+
+ if(sd->stc.buf_y < (sd->stc.stc_y+m)) break;
+
+ escp_used = 0;
+ for(color = 0; color < ncolor; ++color) { /* print the colors */
+
+ if(0 == (w = stc_bandwidth(sd,color,m,1))) continue; /* shortcut */
+
+ escp_used = stc_print_escpcmd(sd,prn_stream,escp_used,color,m,w);
+
+ buf_a = (sd->stc.prt_buf-1) & (sd->stc.stc_y * ncolor + color);
+ for(iprint = 0; iprint < m; ++iprint) { /* send data */
+
+ if((sd->stc.flags & STCCOMP) == STCPLAIN) {
+ memcpy(sd->stc.escp_data+escp_used,sd->stc.prt_data[buf_a],w);
+ escp_used += w;
+ } else {
+ escp_used += stc_rle(sd->stc.escp_data+escp_used,
+ sd->stc.prt_data[buf_a],w);
+ }
+
+ fwrite(sd->stc.escp_data,1,escp_used,prn_stream);
+ escp_used = 0;
+
+ buf_a = (sd->stc.prt_buf-1) & (buf_a + ncolor);
+
+ } /* send data */
+
+ } /* print the colors */
+
+ sd->stc.stc_y += m;
+ }
+}
+/* ----------------------------------------------------------------------- */
+
+static int
+stc_deltarow(byte *out,const byte *in,int width,byte *seed)
+{
+
+ int istop,nmove,ndata,i,j;
+ int *wseed = (int *) seed;
+ int used = 0;
+
+ seed += sizeof(int);
+
+ if((in != NULL) && (width > 0)) { /* Data present */
+
+ istop = width < wseed[0] ? wseed[0] : width;
+
+ i = 0;
+ while(i < istop) {
+
+ for(j = i; j < istop; ++j) if(in[j] != seed[j]) break;
+
+ nmove = j - i;
+
+ if(nmove > 0) { /* issue a move */
+ i = j;
+ if(i == istop) break;
+
+ if( nmove < 8) {
+ out[used++] = 0x40 | nmove;
+ } else if(nmove < 128) {
+ out[used++] = 0x51;
+ out[used++] = nmove;
+ } else {
+ out[used++] = 0x52;
+ out[used++] = 0xff & nmove;
+ out[used++] = 0xff & (nmove>>8);
+ }
+ } /* issue a move */
+
+/*
+ * find the end of this run
+ */
+ nmove = 0;
+ for(j = i+1; (j < istop) && ((nmove < 4)); ++j) {
+ if(in[j] == seed[j]) nmove += 1;
+ else nmove = 0;
+ }
+
+ ndata = j-i-nmove;
+
+ nmove = stc_rle(out+used+3,in+i,ndata);
+ if(nmove < 16) {
+ out[used++] = 0x20 | nmove;
+ for(j = 0; j < nmove; ++j) out[used+j] = out[used+j+2];
+ } else if(nmove < 256) {
+ out[used++] = 0x31;
+ out[used++] = nmove;
+ for(j = 0; j < nmove; ++j) out[used+j] = out[used+j+1];
+ } else {
+ out[used++] = 0x32;
+ out[used++] = 0xff & nmove;
+ out[used++] = 0xff & (nmove>>8);
+ }
+ used += nmove;
+ i += ndata;
+ }
+
+ memcpy(seed,in,istop);
+ wseed[0] = width;
+
+ } else if(wseed[0] > 0) { /* blank line, but seed has data */
+
+ out[used++] = 0xe1; /* clear row */
+ memset(seed,0,wseed[0]);
+ wseed[0] = 0;
+
+ }
+
+ return used;
+}
+
+/*
+ * Slightly different single-pass printing
+ */
+static void
+stc_print_delta(stcolor_device *sd, FILE *prn_stream)
+{
+
+ int color,buf_a,w;
+ int escp_used = 0;
+ int ncolor = sd->color_info.num_components == 1 ? 1 : 4;
+
+ while(sd->stc.stc_y < sd->stc.prt_scans) {
+
+/*
+ * find the begin of the band
+ */
+ for(w = 0; sd->stc.stc_y < sd->stc.buf_y; ++sd->stc.stc_y) {
+ buf_a = (sd->stc.prt_buf-1) & (sd->stc.stc_y * ncolor);
+ for(color = 0; color < ncolor; ++color)
+ if(sd->stc.prt_width[buf_a+color] > w)
+ w = sd->stc.prt_width[buf_a+color];
+ if(w != 0) break;
+ }
+
+ if(sd->stc.buf_y == sd->stc.stc_y) break;
+
+ escp_used = 0;
+
+/*
+ * Send Initialization & ESC . 3 once
+ */
+ if(0 == (sd->stc.flags & STCPRINT)) {
+
+ sd->stc.flags |= STCPRINT;
+
+ fwrite(sd->stc.escp_init.data,1,sd->stc.escp_init.size,prn_stream);
+
+ sd->stc.escp_data[escp_used++] = '\033';
+ sd->stc.escp_data[escp_used++] = '.';
+ sd->stc.escp_data[escp_used++] = 3;
+ sd->stc.escp_data[escp_used++] = sd->stc.escp_v;
+ sd->stc.escp_data[escp_used++] = sd->stc.escp_h;
+ sd->stc.escp_data[escp_used++] = sd->stc.escp_m;
+ sd->stc.escp_data[escp_used++] = 0;
+ sd->stc.escp_data[escp_used++] = 0;
+ sd->stc.escp_data[escp_used++] = 0xe4; /* MOVXBYTE */
+ }
+
+ if(sd->stc.stc_y != sd->stc.prt_y) { /* really position the printer */
+ w = sd->stc.stc_y - sd->stc.prt_y;
+ if( w < 16) {
+ sd->stc.escp_data[escp_used++] = 0x60 | w;
+ } else if(w < 256) {
+ sd->stc.escp_data[escp_used++] = 0x71;
+ sd->stc.escp_data[escp_used++] = w;
+ } else {
+ sd->stc.escp_data[escp_used++] = 0x72;
+ sd->stc.escp_data[escp_used++] = 0xff & w;
+ sd->stc.escp_data[escp_used++] = 0xff & (w>>8);
+ }
+ sd->stc.prt_y = sd->stc.stc_y;
+ } /* really position the printer */
+
+ for(color = 0; color < ncolor; ++color) { /* print the colors */
+
+/* Color-Selection */
+ if(color == (ncolor-1)) {
+ sd->stc.escp_data[escp_used++] = 0x80; /* Black */
+ } else {
+ switch(color) {
+ case 1: sd->stc.escp_data[escp_used++] = 0x81; break; /* M */
+ case 2: sd->stc.escp_data[escp_used++] = 0x84; break; /* Y */
+ default: sd->stc.escp_data[escp_used++] = 0x82; break; /* C */
+ }
+ }
+
+/* Data-Transfer */
+ buf_a = (sd->stc.prt_buf-1) & (sd->stc.stc_y * ncolor + color);
+
+ w = stc_deltarow(sd->stc.escp_data+escp_used,
+ sd->stc.prt_data[buf_a],sd->stc.prt_width[buf_a],
+ sd->stc.seed_row[color]);
+
+ if(w == 0) escp_used -= 1;
+ else escp_used += w;
+
+ if(escp_used > 0) fwrite(sd->stc.escp_data,1,escp_used,prn_stream);
+ escp_used = 0;
+
+ } /* print the colors */
+
+ sd->stc.stc_y += 1;
+
+ }
+
+}
+
+/* ----------------------------------------------------------------------- */
+
+/***
+ *** Free-Data: release the specific-Arrays
+ ***/
+static void
+stc_freedata(gs_memory_t *mem, stc_t *stc)
+{
+ int i,j;
+
+ for(i = 0; i < 4; ++i) {
+ if(stc->code[i] != NULL) {
+
+ for(j = 0; j < i; ++j) if(stc->code[i] == stc->code[j]) break;
+
+ if(i == j) gs_free(mem, stc->code[i],1<<stc->bits,sizeof(gx_color_value),
+ "stcolor/code");
+ }
+
+ if(stc->vals[i] != NULL) {
+
+ for(j = 0; j < i; ++j)
+ if(stc->vals[i] == stc->vals[j]) break;
+
+ if(i == j) gs_free(mem, stc->vals[i],1<<stc->bits,sd->stc.alg_item,
+ "stcolor/transfer");
+ }
+ }
+
+ for(i = 0; i < 4; ++i) {
+ stc->code[i] = NULL;
+ stc->vals[i] = NULL;
+ }
+}
+
+/***
+ *** open the device and initialize margins & arrays
+ ***/
+
+static int
+stc_open(gx_device *pdev) /* setup margins & arrays */
+{
+ stcolor_device *sd = (stcolor_device *) pdev;
+ int i,j,code;
+ gx_color_index white;
+ byte *bpw,*bpm;
+
+ code = 0;
+/*
+ * Establish Algorithm-Table, if not present
+ */
+ if(sd->stc.algorithms.size == 0) {
+ gs_param_string *dp;
+ for(i = 0; stc_dither[i].name != NULL; ++i); /* count 'em */
+ sd->stc.algorithms.size = i;
+ dp = gs_malloc(sd->memory, i,sizeof(gs_param_string),
+ "stcolor/algorithms");
+ if(dp == NULL) {
+ code = gs_error_VMerror;
+ sd->stc.algorithms.size = 0;
+ } else {
+ sd->stc.algorithms.data = dp;
+ sd->stc.algorithms.persistent = true;
+ for(i = 0; stc_dither[i].name != NULL; ++i) {
+ param_string_from_string(dp[i],stc_dither[i].name);
+ }
+ }
+ }
+
+# define stc_sizeofitem(T) sd->stc.alg_item = sizeof(T)
+ STC_TYPESWITCH(sd->stc.dither,stc_sizeofitem)
+
+ stc_print_setup(sd);
+
+/*
+ * Establish internal Value & Code-Arrays
+ */
+
+ for(i = 0; i < sd->color_info.num_components; ++i) { /* comp */
+
+ if((sd->stc.sizc[i] > 1) && (sd->stc.extc[i] != NULL)) { /* code req. */
+
+ for(j = 0; j < i; ++j) if(sd->stc.extc[i] == sd->stc.extc[j]) break;
+
+ if(i == j) { /* new one */
+ sd->stc.code[i] = gs_malloc(sd->memory, 1<<sd->stc.bits,sizeof(gx_color_value),
+ "stcolor/code");
+
+ if(sd->stc.code[i] == NULL) { /* error */
+ code = gs_error_VMerror;
+ } else { /* success */
+/*
+ * Try making things easier:
+ * normalize values to 0.0/1.0-Range
+ * X-Axis: Color-Values (implied)
+ * Y-Values: Indices (given)
+ */
+ unsigned long ly,iy;
+ double ystep,xstep,fx,fy;
+
+/* normalize */
+
+ fx = 1e18;
+ fy = -1e18;
+ for(ly = 0; ly < sd->stc.sizc[i]; ++ly) {
+ if(sd->stc.extc[i][ly] < fx) fx = sd->stc.extc[i][ly];
+ if(sd->stc.extc[i][ly] > fy) fy = sd->stc.extc[i][ly];
+ }
+ if((fx != 0.0) || (fy != 1.0)) {
+ fy = 1.0 / (fy - fx);
+ for(ly = 0; ly < sd->stc.sizc[i]; ++ly)
+ sd->stc.extc[i][ly] = fy * (sd->stc.extc[i][ly]-fx);
+ }
+
+/* interpolate */
+ ystep = 1.0 / (double)((1<<sd->stc.bits)-1);
+ xstep = 1.0 / (double)( sd->stc.sizc[i] -1);
+
+ iy = 0;
+ for(ly = 0; ly < (1<<sd->stc.bits); ++ly) {
+ fy = ystep * ly;
+ while(((iy+1) < sd->stc.sizc[i]) &&
+ ( fy > sd->stc.extc[i][iy+1])) ++iy;
+ fx = iy + (fy - sd->stc.extc[i][iy])
+ / (sd->stc.extc[i][iy+1] - sd->stc.extc[i][iy]);
+ fx *= xstep * gx_max_color_value;
+
+ fx = fx < 0.0 ? 0.0 :
+ (fx > gx_max_color_value ? gx_max_color_value : fx);
+
+ sd->stc.code[i][ly] = (gx_color_value)fx;
+ if((fx-sd->stc.code[i][ly]) >= 0.5) sd->stc.code[i][ly] += 1;
+ }
+ } /* error || success */
+
+ } else { /* shared one */
+
+ sd->stc.code[i] = sd->stc.code[j];
+
+ } /* new || shared one */
+ } /* code req. */
+
+ if((sd->stc.sizv[i] > 1) && (sd->stc.extv[i] != NULL)) { /* vals req. */
+
+ for(j = 0; j < i; ++j)
+ if((sd->stc.extc[i] == sd->stc.extc[j]) &&
+ (sd->stc.extv[i] == sd->stc.extv[j])) break;
+
+ if(i == j) { /* new one */
+
+ sd->stc.vals[i] =
+ gs_malloc(sd->memory, 1<<sd->stc.bits,sd->stc.alg_item,"stcolor/transfer");
+
+ if(sd->stc.vals[i] == NULL) {
+
+ code = gs_error_VMerror;
+
+ } else { /* success */
+
+ if(sd->stc.code[i] == NULL) { /* linear */
+
+ byte *Out = sd->stc.vals[i];
+ int Nout = 1<<sd->stc.bits;
+ double Omin = sd->stc.dither->minmax[0];
+ double Omax = sd->stc.dither->minmax[1];
+ float *In = sd->stc.extv[i];
+ int Nin = sd->stc.sizv[i];
+ unsigned long I,io;
+ double Istep,Ostep,Y;
+ byte Ovb; long Ovl;
+
+ Istep = 1.0 / (double) ((Nin)-1);
+ Ostep = 1.0 / (double) ((Nout)-1);
+
+ for(io = 0; io < (Nout); ++io) {
+ I = (long)(io * ((Nin)-1))/((Nout)-1);
+
+ if((I+1) < (Nin))
+ Y = In[I] + (In[I+1]-In[I])
+ * ((double) io * Ostep - (double)I * Istep)
+ / (double) Istep;
+ else
+ Y = In[I] + (In[I]-In[I-1])
+ * ((double) io * Ostep - (double)I * Istep)
+ / (double) Istep;
+
+ Y = Omin + (Omax-Omin) * Y;
+ Y = Y < Omin ? Omin : (Y > Omax ? Omax : Y);
+
+ switch(sd->stc.dither->flags & STC_TYPE) {
+ case STC_BYTE:
+ Ovb = (byte)Y;
+ if(((Y-Ovb) >= 0.5) && ((Ovb+1) <= Omax)) Ovb += 1;
+ Out[io] = Ovb;
+ break;
+ case STC_LONG:
+ Ovl = (long)Y;
+ if(((Y-Ovl) >= 0.5) && ((Ovl+1) <= Omax)) Ovl += 1;
+ if(((Ovl-Y) >= 0.5) && ((Ovl-1) >= Omax)) Ovl -= 1;
+ ((long *)Out)[io] = Ovl;
+ break;
+ default:
+ ((float *)Out)[io] = Y;
+ break;
+ }
+ }
+
+ } else { /* encoded */
+ unsigned long j,o;
+ double xstep,x,y;
+
+ xstep = 1.0 / (double) (sd->stc.sizv[i]-1);
+
+/*
+ * The following differs in so far from the previous, that the desired
+ * X-Values are stored in another array.
+ */
+ for(o = 0; o < (1<<sd->stc.bits); ++o) { /* code-loop */
+
+ x = sd->stc.code[i][o]; x /= gx_max_color_value;
+
+ j = (unsigned long)(x / xstep);
+
+ if((j+1) < sd->stc.sizv[i]) {
+ y = sd->stc.extv[i][j];
+ y += (sd->stc.extv[i][j+1]-y)*(x-(double)j*xstep)/xstep;
+ } else {
+ y = sd->stc.extv[i][j];
+ y += (y-sd->stc.extv[i][j-1])*(x-(double)j*xstep)/xstep;
+ }
+
+ y = sd->stc.dither->minmax[0]
+ +(sd->stc.dither->minmax[1]-sd->stc.dither->minmax[0])*y;
+
+# define stc_adjvals(T) \
+ ((T *)(sd->stc.vals[i]))[o] = (T)y; \
+ \
+ if(((y-((T *)(sd->stc.vals[i]))[o]) >= 0.5) && \
+ ((1+((T *)(sd->stc.vals[i]))[o]) <= sd->stc.dither->minmax[1]))\
+ ((T *)(sd->stc.vals[i]))[o] += 1; \
+ \
+ if(((((T *)(sd->stc.vals[i]))[o]-y) >= 0.5) && \
+ ((((T *)(sd->stc.vals[i]))[o]-1) >= sd->stc.dither->minmax[0]))\
+ ((T *)(sd->stc.vals[i]))[o] -= 1;
+
+ STC_TYPESWITCH(sd->stc.dither,stc_adjvals)
+
+# undef stc_adjvals
+ } /* code-loop */
+ } /* lineaer / encoded */
+ } /* error || success */
+
+ } else { /* shared one */
+
+ sd->stc.vals[i] = sd->stc.vals[j];
+
+ } /* new || shared one */
+ } /* vals req. */
+ } /* comp */
+ sd->stc.dir = 1;
+
+ if(code == 0) {
+ gx_color_value cv[4];
+ sd->stc.flags |= STCOK4GO;
+
+/*
+ * Arrgh: open-procedure seems to be the right-place, but it is
+ * necessary to establish the defaults for omitted procedures too.
+ */
+
+ switch(sd->color_info.num_components) { /* Establish color-procs */
+ case 1:
+ sd->color_info.polarity = GX_CINFO_POLARITY_ADDITIVE;
+ set_dev_proc(sd,map_rgb_color, stc_map_gray_color);
+ set_dev_proc(sd,map_cmyk_color,gx_default_map_cmyk_color);
+ set_dev_proc(sd,map_color_rgb, stc_map_color_gray);
+ set_dev_proc(sd,encode_color, stc_map_gray_color);
+ set_dev_proc(sd,decode_color, stc_map_color_gray);
+ set_dev_proc(sd, get_color_mapping_procs,
+ gx_default_DevGray_get_color_mapping_procs);
+ set_dev_proc(sd, get_color_comp_index,
+ gx_default_DevGray_get_color_comp_index );
+ cv[0] = cv[1] = cv[2] = gx_max_color_value;
+ white = stc_map_gray_color((gx_device *) sd, cv);
+ break;
+ case 3:
+ sd->color_info.polarity = GX_CINFO_POLARITY_ADDITIVE;
+ set_dev_proc(sd,map_rgb_color, stc_map_rgb_color);
+ set_dev_proc(sd,map_cmyk_color,gx_default_map_cmyk_color);
+ set_dev_proc(sd,map_color_rgb, stc_map_color_rgb);
+ set_dev_proc(sd,encode_color, stc_map_rgb_color);
+ set_dev_proc(sd,decode_color, stc_map_color_rgb);
+ set_dev_proc(sd, get_color_mapping_procs,
+ gx_default_DevRGB_get_color_mapping_procs);
+ set_dev_proc(sd, get_color_comp_index,
+ gx_default_DevRGB_get_color_comp_index );
+ cv[0] = cv[1] = cv[2] = gx_max_color_value;
+ white = stc_map_rgb_color((gx_device *) sd, cv);
+ break;
+ default:
+ sd->color_info.polarity = GX_CINFO_POLARITY_SUBTRACTIVE;
+ set_dev_proc(sd,map_rgb_color, gx_default_map_rgb_color);
+ set_dev_proc(sd, get_color_mapping_procs,
+ gx_default_DevCMYK_get_color_mapping_procs);
+ set_dev_proc(sd, get_color_comp_index,
+ gx_default_DevCMYK_get_color_comp_index );
+ if(sd->stc.flags & STCCMYK10) {
+ set_dev_proc(sd,map_cmyk_color,stc_map_cmyk10_color);
+ set_dev_proc(sd,map_color_rgb, stc_map_color_cmyk10);
+ set_dev_proc(sd,encode_color,stc_map_cmyk10_color);
+ set_dev_proc(sd,decode_color, stc_map_color_cmyk10);
+ cv[0] = cv[1] = cv[2] = cv[3] = 0;
+ white = stc_map_cmyk10_color((gx_device *) sd, cv);
+ } else {
+ set_dev_proc(sd,map_cmyk_color,stc_map_cmyk_color);
+ set_dev_proc(sd,map_color_rgb, stc_map_color_cmyk);
+ set_dev_proc(sd,encode_color,stc_map_cmyk_color);
+ set_dev_proc(sd,decode_color, stc_map_color_cmyk);
+ cv[0] = cv[1] = cv[2] = cv[3] = 0;
+ white = stc_map_cmyk_color((gx_device *) sd,cv);
+ }
+ break; /* Establish color-procs */
+ }
+
+/*
+ * create at least a Byte
+ */
+ if(sd->color_info.depth < 2) white |= (white<<1);
+ if(sd->color_info.depth < 4) white |= (white<<2);
+ if(sd->color_info.depth < 8) white |= (white<<4);
+
+/*
+ * copy the Bytes
+ */
+ bpw = (byte *) sd->stc.white_run;
+
+ if(sd->color_info.depth < 16) {
+ for(i = 0; i < sizeof(sd->stc.white_run); i += 1) {
+ bpw[i] = 0xff & white;
+ }
+ } else if(sd->color_info.depth < 24) {
+ for(i = 0; i < sizeof(sd->stc.white_run); i += 2) {
+ bpw[i] = 0xff & (white>>8);
+ bpw[i+1] = 0xff & white;
+ }
+ } else if(sd->color_info.depth < 32) {
+ for(i = 0; i < sizeof(sd->stc.white_run); i += 3) {
+ bpw[i] = 0xff & (white>>16);
+ bpw[i+1] = 0xff & (white>> 8);
+ bpw[i+2] = 0xff & white;
+ }
+ } else {
+ for(i = 0; i < sizeof(sd->stc.white_run); i += 4) {
+ bpw[i] = 0xff & (white>>24);
+ bpw[i+1] = 0xff & (white>>16);
+ bpw[i+2] = 0xff & (white>> 8);
+ bpw[i+3] = 0xff & white;
+ }
+ }
+/*
+ * compute the trailer
+ */
+ j = (unsigned long)(sd->width -
+ (dev_l_margin(sd)+dev_r_margin(sd))*sd->x_pixels_per_inch);
+ j = j * sd->color_info.depth; /* the Bit-count */
+ j = j % (32*countof(sd->stc.white_run)); /* remaining Bits */
+
+ bpm = (byte *) sd->stc.white_end;
+ for(i = 0; i < (4*countof(sd->stc.white_end)); ++i) {
+ if( j <= 0) {
+ bpm[i] = 0;
+ } else if(j >= 8) {
+ bpm[i] = 0xff;
+ j -= 8;
+ } else {
+ bpm[i] = 0xff ^ ((1<<(8-j))-1);
+ j = 0;
+ }
+ bpm[i] &= bpw[i];
+ }
+
+/*
+ * Call super-class open
+ */
+
+ return gdev_prn_open(pdev);
+
+ } else {
+
+ stc_freedata(sd->memory, &sd->stc);
+
+ return_error(code);
+ }
+
+}
+
+/***
+ *** stc_close: release the internal data
+ ***/
+static int
+stc_close(gx_device *pdev)
+{
+ stc_freedata(pdev->memory, &((stcolor_device *) pdev)->stc);
+ ((stcolor_device *) pdev)->stc.flags &= ~STCOK4GO;
+ return gdev_prn_close(pdev);
+}
+
+/***
+ *** Function for Bit-Truncation, including direct-byte-transfer
+ ***/
+static gx_color_value
+stc_truncate(stcolor_device *sd,int i,gx_color_value v)
+{
+
+ if(sd->stc.bits < gx_color_value_bits) {
+ if(sd->stc.code[i] != NULL) {
+/*
+ * Perform binary search in the code-array
+ */
+ long s;
+ gx_color_value *p;
+
+ s = sd->stc.bits > 1 ? 1L<<(sd->stc.bits-2) : 0L;
+ p = sd->stc.code[i]+(1L<<(sd->stc.bits-1));
+
+ while(s > 0) {
+ if(v > *p) {
+ p += s;
+ } else if(v < p[-1]) {
+ p -= s;
+ } else {
+ if((v-p[-1]) < (p[0]-v)) p -= 1;
+ break;
+ }
+ s >>= 1;
+ }
+ if((v-p[-1]) < (p[0]-v)) p -= 1;
+ v = p - sd->stc.code[i];
+
+ } else {
+
+ v >>= gx_color_value_bits-sd->stc.bits;
+
+ }
+
+/*
+ V = (((1L<<D->stc.bits)-1)*V+(gx_max_color_value>>1))\
+ /gx_max_color_value; \
+*/
+ }
+ return v;
+}
+
+static gx_color_value
+stc_truncate1(stcolor_device *sd,int i,gx_color_value v)
+{
+
+ return sd->stc.vals[i][stc_truncate(sd,i,v)];
+}
+
+/***
+ *** Expansion of indices for reverse-mapping
+ ***/
+static gx_color_value
+stc_expand(stcolor_device *sd,int i,gx_color_index col)
+{
+
+ gx_color_index cv;
+ gx_color_index l = (1<<sd->stc.bits)-1;
+
+ if(sd->stc.code[i] != NULL) {
+
+ cv = sd->stc.code[i][col & l];
+
+ } else if(sd->stc.bits < gx_color_value_bits) {
+
+ cv = (col & l)<<(gx_color_value_bits-sd->stc.bits);
+ cv += (col & l)/l * ((1<<(gx_color_value_bits-sd->stc.bits))-1);
+
+ } else if(sd->stc.bits > gx_color_value_bits) {
+
+ cv = (col & l)>>(sd->stc.bits-gx_color_value_bits);
+
+ } else {
+
+ cv = col & l;
+
+ }
+
+ return cv;
+}
+
+/***
+ *** color-mapping of gray-scales
+ ***/
+static gx_color_index
+stc_map_gray_color(gx_device *pdev, const gx_color_value cv[])
+{
+
+ stcolor_device *sd = (stcolor_device *) pdev;
+ gx_color_index rv;
+ gx_color_value r = cv[0];
+ gx_color_value g = cv[1];
+ gx_color_value b = cv[2];
+
+ if((r == g) && (g == b)) {
+
+ rv = gx_max_color_value - r;
+
+ } else if(sd->stc.am != NULL) {
+ float *m,fv;
+
+ m = sd->stc.am;
+
+ fv = gx_max_color_value;
+ fv -= *m++ * (float) r; fv -= *m++ * (float) g; fv -= *m * (float) b;
+
+ if( fv < 0.0) rv = 0;
+ else if((fv+0.5) > gx_max_color_value) rv = gx_max_color_value;
+ else rv = (gx_color_index)(fv+0.5);
+
+ } else {
+
+ rv = ((gx_color_index)gx_max_color_value)<<3;
+ rv -= (gx_color_index) 3 * r;
+ rv -= (gx_color_index) 3 * g;
+ rv -= ((gx_color_index)b)<<1;
+ rv = (rv+4)>>3;
+ if(rv > gx_max_color_value) rv = gx_max_color_value;
+
+ }
+
+ if(( sd->stc.bits == 8) &&
+ ((sd->stc.dither->flags & STC_TYPE) == STC_BYTE))
+ rv = stc_truncate1(sd,0,(gx_color_value)rv);
+ else
+ rv = stc_truncate(sd,0,(gx_color_value)rv);
+
+ return rv;
+}
+
+static int
+stc_map_color_gray(gx_device *pdev, gx_color_index color,gx_color_value prgb[3])
+{
+ stcolor_device *sd = (stcolor_device *) pdev;
+ gx_color_index l = ((gx_color_index)1<<sd->stc.bits)-1;
+
+ prgb[0] = gx_max_color_value - stc_expand(sd,0,color & l);
+ prgb[1] = prgb[0]; prgb[2] = prgb[0];
+
+ return 0;
+}
+
+/***
+ *** color-mapping of rgb-values
+ ***/
+static gx_color_index
+stc_map_rgb_color(gx_device *pdev, const gx_color_value cv[])
+{
+
+ stcolor_device *sd = (stcolor_device *) pdev;
+ int shift = sd->color_info.depth == 24 ? 8 : sd->stc.bits;
+ gx_color_index rv = 0;
+ gx_color_value r = cv[0];
+ gx_color_value g = cv[1];
+ gx_color_value b = cv[2];
+ if((sd->stc.am != NULL) && ((r != g) || (g != b))) {
+ float *m,fr,fg,fb,fv;
+
+ m = sd->stc.am;
+ fr = r; fg = g; fb = b;
+
+ fv = *m++ * fr; fv += *m++ * fg; fv += *m++ * fb;
+
+ if( fv < 0.0) r = 0;
+ else if((fv+0.5) > gx_max_color_value) r = gx_max_color_value;
+ else r = (gx_color_value)(fv+0.5);
+
+ fv = *m++ * fr; fv += *m++ * fg; fv += *m++ * fb;
+
+ if( fv < 0.0) g = 0;
+ else if((fv+0.5) > gx_max_color_value) g = gx_max_color_value;
+ else g = (gx_color_value)(fv+0.5);
+
+ fv = *m++ * fr; fv += *m++ * fg; fv += *m++ * fb;
+
+ if( fv < 0.0) b = 0;
+ else if((fv+0.5) > gx_max_color_value) b = gx_max_color_value;
+ else b = (gx_color_value)(fv+0.5);
+
+ }
+
+ if(( sd->stc.bits == 8) &&
+ ((sd->stc.dither->flags & STC_TYPE) == STC_BYTE)) {
+ rv = stc_truncate1(sd,0,r);
+ rv = (rv<<shift) | stc_truncate1(sd,1,g);
+ rv = (rv<<shift) | stc_truncate1(sd,2,b);
+ } else {
+ rv = stc_truncate(sd,0,r);
+ rv = (rv<<shift) | stc_truncate(sd,1,g);
+ rv = (rv<<shift) | stc_truncate(sd,2,b);
+ }
+
+ return rv;
+}
+
+static int
+stc_map_color_rgb(gx_device *pdev, gx_color_index color,gx_color_value prgb[3])
+{
+
+ stcolor_device *sd = (stcolor_device *) pdev;
+ int shift = sd->color_info.depth == 24 ? 8 : sd->stc.bits;
+ gx_color_index l = ((gx_color_index)1<<sd->stc.bits)-1;
+
+ prgb[0] = stc_expand(sd,0,((color>>(shift<<1)) & l));
+ prgb[1] = stc_expand(sd,1,((color>> shift ) & l));
+ prgb[2] = stc_expand(sd,2,( color & l));
+
+ return 0;
+}
+
+/***
+ *** color-mapping of cmyk-values
+ ***/
+static gx_color_index
+stc_map_cmyk_color(gx_device *pdev, const gx_color_value cv[])
+{
+
+ stcolor_device *sd = (stcolor_device *) pdev;
+ int shift = sd->color_info.depth == 32 ? 8 : sd->stc.bits;
+ gx_color_index rv = 0;
+ gx_color_value c = cv[0];
+ gx_color_value m = cv[1];
+ gx_color_value y = cv[2];
+ gx_color_value k = cv[3];
+
+ if((c == m) && (m == y)) {
+
+ k = c > k ? c : k;
+ c = m = y = 0;
+
+ if(( sd->stc.bits == 8) &&
+ ((sd->stc.dither->flags & STC_TYPE) == STC_BYTE)) {
+ k = stc_truncate1(sd,3,k);
+ } else {
+ k = stc_truncate(sd,3,k);
+ }
+
+ } else {
+
+ if(sd->stc.am != NULL) {
+
+ float *a,fc,fm,fy,fk,fv;
+
+ if(k == 0) { /* no separated black yet */
+ k = c < m ? c : m;
+ k = k < y ? k : y;
+ if(k) { /* no black at all */
+ c -= k;
+ m -= k;
+ y -= k;
+ } /* no black at all */
+ } /* no separated black yet */
+
+ a = sd->stc.am;
+ fc = c; fm = m; fy = y; fk = k;
+
+ fv = *a++ * fc; fv += *a++ * fm; fv += *a++ * fy; fv += *a++ * fk;
+ if( fv < 0.0) c = 0;
+ else if((fv+0.5) > gx_max_color_value) c = gx_max_color_value;
+ else c = (gx_color_value)(fv+0.5);
+
+ fv = *a++ * fc; fv += *a++ * fm; fv += *a++ * fy; fv += *a++ * fk;
+ if( fv < 0.0) m = 0;
+ else if((fv+0.5) > gx_max_color_value) m = gx_max_color_value;
+ else m = (gx_color_value)(fv+0.5);
+
+ fv = *a++ * fc; fv += *a++ * fm; fv += *a++ * fy; fv += *a++ * fk;
+ if( fv < 0.0) y = 0;
+ else if((fv+0.5) > gx_max_color_value) y = gx_max_color_value;
+ else y = (gx_color_value)(fv+0.5);
+
+ fv = *a++ * fc; fv += *a++ * fm; fv += *a++ * fy; fv += *a++ * fk;
+ if( fv < 0.0) k = 0;
+ else if((fv+0.5) > gx_max_color_value) k = gx_max_color_value;
+ else k = (gx_color_value)(fv+0.5);
+
+ } else if(k == 0) {
+
+ k = c < m ? c : m;
+ k = k < y ? k : y;
+ }
+
+ if(( sd->stc.bits == 8) &&
+ ((sd->stc.dither->flags & STC_TYPE) == STC_BYTE)) {
+ c = stc_truncate1(sd,0,c);
+ m = stc_truncate1(sd,1,m);
+ y = stc_truncate1(sd,2,y);
+ k = stc_truncate1(sd,3,k);
+ } else {
+ c = stc_truncate(sd,0,c);
+ m = stc_truncate(sd,1,m);
+ y = stc_truncate(sd,2,y);
+ k = stc_truncate(sd,3,k);
+ }
+ }
+
+ rv = c;
+ rv = (rv<<shift) | m;
+ rv = (rv<<shift) | y;
+ rv = (rv<<shift) | k;
+
+ if(rv == gx_no_color_index) rv ^= 1;
+
+ return rv;
+}
+
+/* Modified to be a "decode_color" routine */
+static int
+stc_map_color_cmyk(gx_device *pdev, gx_color_index color,gx_color_value cv[4])
+{
+
+ stcolor_device *sd = (stcolor_device *) pdev;
+ int shift = sd->color_info.depth == 32 ? 8 : sd->stc.bits;
+ gx_color_index l = ((gx_color_index)1<<sd->stc.bits)-1;
+ gx_color_value c,m,y,k;
+
+ k = stc_expand(sd,3, color & l); color >>= shift;
+ y = stc_expand(sd,2, color & l); color >>= shift;
+ m = stc_expand(sd,1, color & l); color >>= shift;
+ c = stc_expand(sd,0, color & l);
+
+ cv[0] = c;
+ cv[1] = m;
+ cv[2] = y;
+ cv[3] = k;
+
+ return 0;
+}
+
+/***
+ *** color-mapping of cmyk10-values
+ ***/
+static gx_color_index
+stc_map_cmyk10_color(gx_device *pdev, const gx_color_value cv[])
+{
+
+ stcolor_device *sd = (stcolor_device *) pdev;
+ int mode;
+ gx_color_index rv = 0;
+
+ gx_color_value c = cv[0];
+ gx_color_value m = cv[1];
+ gx_color_value y = cv[2];
+ gx_color_value k = cv[3];
+
+ if((c == m) && (m == y)) {
+
+ k = c > k ? c : k;
+ c = m = y = 0;
+ mode = 3;
+
+ } else {
+
+ if(sd->stc.am != NULL) {
+
+ float *a,fc,fm,fy,fk,fv;
+
+ k = c < m ? c : m;
+ k = k < y ? k : y;
+ if(k) { /* no black at all */
+ c -= k;
+ m -= k;
+ y -= k;
+ } /* no black at all */
+
+ a = sd->stc.am;
+ fc = c; fm = m; fy = y; fk = k;
+
+ fv = *a++ * fc; fv += *a++ * fm; fv += *a++ * fy; fv += *a++ * fk;
+ if( fv < 0.0) c = 0;
+ else if((fv+0.5) > gx_max_color_value) c = gx_max_color_value;
+ else c = (gx_color_value)(fv+0.5);
+
+ fv = *a++ * fc; fv += *a++ * fm; fv += *a++ * fy; fv += *a++ * fk;
+ if( fv < 0.0) m = 0;
+ else if((fv+0.5) > gx_max_color_value) m = gx_max_color_value;
+ else m = (gx_color_value)(fv+0.5);
+
+ fv = *a++ * fc; fv += *a++ * fm; fv += *a++ * fy; fv += *a++ * fk;
+ if( fv < 0.0) y = 0;
+ else if((fv+0.5) > gx_max_color_value) y = gx_max_color_value;
+ else y = (gx_color_value)(fv+0.5);
+
+ }
+
+ if(c < m) {
+ if(c < y) { k = c; c = 0; mode = 0; }
+ else { k = y; y = 0; mode = 2; }
+ } else {
+ if(m < y) { k = m; m = 0; mode = 1; }
+ else { k = y; y = 0; mode = 2; }
+ }
+ }
+
+/*
+ * truncate only the values that require it
+ */
+ if(c) c = stc_truncate(sd,0,c);
+ if(m) m = stc_truncate(sd,1,m);
+ if(y) y = stc_truncate(sd,2,y);
+ if(k) k = stc_truncate(sd,3,k);
+
+/*
+ * make sure that truncation-white becomes white.
+ */
+ if((c|m|y) == 0) mode = 3;
+
+/*
+ * check wether value-arrays can be bypassed
+ */
+ if(((sd->stc.dither->flags & STC_TYPE) == STC_BYTE) &&
+ ( sd->stc.dither->minmax[0] == 0.0 )) {
+ c = sd->stc.vals[0][c];
+ m = sd->stc.vals[1][m];
+ y = sd->stc.vals[2][y];
+ k = sd->stc.vals[3][k];
+ } else if(((sd->stc.dither->flags & STC_TYPE) == STC_LONG) &&
+ ( sd->stc.dither->minmax[0] == 0.0 ) &&
+ ( sd->stc.dither->minmax[1] <= 1023.0 )) {
+ c = ((long *)(sd->stc.vals[0]))[c];
+ m = ((long *)(sd->stc.vals[1]))[m];
+ y = ((long *)(sd->stc.vals[2]))[y];
+ k = ((long *)(sd->stc.vals[3]))[k];
+ } /* direct */
+/*
+ * compute the long-representation of gx_color_index
+ */
+ switch(mode) {
+ case 0:
+ rv = (((gx_color_index) m)<<22)|
+ (((gx_color_index) y)<<12)|
+ (((gx_color_index) k)<< 2)|mode;
+ break;
+ case 1:
+ rv = (((gx_color_index) c)<<22)|
+ (((gx_color_index) y)<<12)|
+ (((gx_color_index) k)<< 2)|mode;
+ break;
+ case 2:
+ rv = (((gx_color_index) c)<<22)|
+ (((gx_color_index) m)<<12)|
+ (((gx_color_index) k)<< 2)|mode;
+ break;
+ default:
+ rv = (((gx_color_index) k)<< 2)|mode;
+ break;
+ }
+
+/*
+ * We may need some swapping
+ */
+#if !arch_is_big_endian
+ {
+ union { stc_pixel cv; byte bv[4]; } ui,uo;
+ ui.cv = rv;
+ uo.bv[0] = ui.bv[3];
+ uo.bv[1] = ui.bv[2];
+ uo.bv[2] = ui.bv[1];
+ uo.bv[3] = ui.bv[0];
+ rv = uo.cv;
+ }
+#endif
+ return rv;
+}
+
+static int
+stc_map_color_cmyk10(gx_device *pdev, gx_color_index color,
+ gx_color_value cv[3])
+{
+
+ stcolor_device *sd = (stcolor_device *) pdev;
+ gx_color_value c,m,y;
+
+/*
+ * We may need some swapping
+ */
+#if !arch_is_big_endian
+ union { stc_pixel cv; byte bv[4]; } ui,uo;
+ ui.cv = color;
+ uo.bv[0] = ui.bv[3];
+ uo.bv[1] = ui.bv[2];
+ uo.bv[2] = ui.bv[1];
+ uo.bv[3] = ui.bv[0];
+ color = uo.cv;
+#endif
+
+ c = stc_expand(sd,3,(color>>2)&0x3ff);
+
+ /* cast the 64 bit switch argument to work around broken HPUX 10 cc */
+ switch((int)(color & 3)) {
+ case 0:
+ m = stc_expand(sd,1,(color>>22) & 0x3ff);
+ y = stc_expand(sd,2,(color>>12) & 0x3ff);
+ break;
+ case 1:
+ m = c;
+ c = stc_expand(sd,0,(color>>22) & 0x3ff);
+ y = stc_expand(sd,2,(color>>12) & 0x3ff);
+ break;
+ case 2:
+ y = c;
+ c = stc_expand(sd,0,(color>>22) & 0x3ff);
+ m = stc_expand(sd,1,(color>>12) & 0x3ff);
+ break;
+ default:
+ m = c;
+ y = c;
+ break;
+ }
+
+ cv[0] = c;
+ cv[1] = m;
+ cv[2] = y;
+
+ return 0;
+}
+
+/***
+ *** Macros for parameter-handling
+ ***/
+
+#define set_param_array(A, D, S)\
+ {A.data = D; A.size = S; A.persistent = false;}
+
+#define stc_write_null(N) \
+ set_param_array(pfa,defext,countof(defext)) \
+ code = param_write_null(plist,N); \
+ if (code < 0) return code;
+
+#define stc_write_xarray(I,Coding,Transfer) \
+ if(sd->stc.sizc[I] > 0) { \
+ set_param_array(pfa, sd->stc.extc[I],sd->stc.sizc[I]) \
+ code = param_write_float_array(plist,Coding,&pfa); \
+ } else { \
+ code = param_write_null(plist,Coding); \
+ } \
+ if ( code < 0 ) return code; \
+ \
+ if(sd->stc.sizv[I] > 0) \
+ set_param_array(pfa, sd->stc.extv[I],sd->stc.sizv[I]) \
+ else \
+ set_param_array(pfa,defext,countof(defext)) \
+ code = param_write_float_array(plist,Transfer,&pfa); \
+ if ( code < 0 ) return code;
+
+#define stc_read_null(N) \
+ code = param_read_null(plist,N); \
+ if(code == gs_error_typecheck) \
+ code = param_read_float_array(plist,N,&pfa); \
+ if(code < 0) param_signal_error(plist,N,code); \
+ error = error > code ? code : error;
+
+#define stc_read_xarray(I,Coding,Transfer) \
+ code = param_read_float_array(plist,Coding,&pfa); \
+ if((error == 0) && (code == 0)) { \
+ if(pfa.size > 1) { \
+ sd->stc.extc[I] = (float *) pfa.data; \
+ sd->stc.sizc[I] = pfa.size; \
+ } else { \
+ code = gs_error_rangecheck; \
+ } \
+ } else if(code < 0) { \
+ code = param_read_null(plist,Coding); \
+ if(code == 0) { \
+ sd->stc.extc[I] = NULL; \
+ sd->stc.sizc[I] = 0; \
+ } \
+ } \
+ if(code < 0) param_signal_error(plist,Coding,code); \
+ error = error > code ? code : error; \
+ code = param_read_float_array(plist,Transfer,&pfa); \
+ if((error == 0) && (code == 0)) { \
+ sd->stc.extv[I] = (float *) pfa.data; \
+ sd->stc.sizv[I] = pfa.size; \
+ } else if(code < 0) { \
+ code = param_read_null(plist,Transfer); \
+ if(code == 0) { \
+ sd->stc.extv[I] = defext; \
+ sd->stc.sizv[I] = countof(defext); \
+ } \
+ } \
+ if(code < 0) param_signal_error(plist,Transfer,code); \
+ error = error > code ? code : error;
+
+/***
+ *** Get parameters == Make them accessable via PostScript
+ ***/
+
+static int
+stc_get_params(gx_device *pdev, gs_param_list *plist)
+{
+ int code,nc;
+ gs_param_string ps;
+ gs_param_float_array pfa;
+ bool btmp;
+ stcolor_device *sd = (stcolor_device *) pdev;
+
+ code = gdev_prn_get_params(pdev, plist);
+ if ( code < 0 ) return code;
+
+/*
+ * Export some readonly-Parameters, used by stcinfo.ps
+ */
+ param_string_from_string(ps,"1.91");
+ code = param_write_string(plist,"Version",&ps);
+ if ( code < 0 ) return code;
+
+ code = param_write_int(plist,"BitsPerComponent",&sd->stc.bits);
+ if ( code < 0 ) return code;
+
+ if(sd->stc.algorithms.size > 0) {
+ code = param_write_string_array(plist,"Algorithms",&sd->stc.algorithms);
+ } else {
+ code = param_write_null(plist,"Algorithms");
+ }
+ if ( code < 0 ) return code;
+
+/*
+ * Export OutputCode
+ */
+ switch(sd->stc.flags & STCCOMP) {
+ case STCPLAIN: param_string_from_string(ps,"plain"); break;
+ case STCDELTA: param_string_from_string(ps,"deltarow"); break;
+ default: param_string_from_string(ps,"runlength"); break;
+ }
+ code = param_write_string(plist,"OutputCode",&ps);
+ if ( code < 0 ) return code;
+/*
+ * Export Model
+ */
+ switch(sd->stc.flags & STCMODEL) {
+ case STCST800: param_string_from_string(ps,"st800"); break;
+ case STCSTCII: param_string_from_string(ps,"stcii"); break;
+ default: param_string_from_string(ps,"stc"); break;
+ }
+ code = param_write_string(plist,"Model",&ps);
+ if ( code < 0 ) return code;
+
+/*
+ * Export the booleans
+ */
+#define stc_write_flag(Mask,Name) \
+ btmp = sd->stc.flags & (Mask) ? true : false; \
+ code = param_write_bool(plist,Name,&btmp); \
+ if ( code < 0 ) return code;
+
+ stc_write_flag(STCUNIDIR,"Unidirectional")
+ stc_write_flag(STCUWEAVE,"Microweave")
+ btmp = sd->stc.flags & (STCUNIDIR|STCUWEAVE) ? false : true;
+ code = param_write_bool(plist,"Softweave",&btmp);
+ if ( code < 0 ) return code;
+ stc_write_flag(STCNWEAVE,"noWeave")
+ stc_write_flag(STCDFLAG0, "Flag0")
+ stc_write_flag(STCDFLAG1, "Flag1")
+ stc_write_flag(STCDFLAG2, "Flag2")
+ stc_write_flag(STCDFLAG3, "Flag3")
+ stc_write_flag(STCDFLAG4, "Flag4")
+
+#undef stc_write_flag
+
+# define stc_write_int(Mask,Name,Val) \
+ code = param_write_int(plist,Name,&Val); \
+ if ( code < 0 ) return code
+
+ stc_write_int(STCBAND, "escp_Band", sd->stc.escp_m);
+ stc_write_int(STCWIDTH, "escp_Width", sd->stc.escp_width);
+ stc_write_int(STCHEIGHT,"escp_Height",sd->stc.escp_height);
+ stc_write_int(STCTOP, "escp_Top", sd->stc.escp_top);
+ stc_write_int(STCBOTTOM,"escp_Bottom",sd->stc.escp_bottom);
+
+# undef stc_write_int
+
+ code = param_write_string(plist,"escp_Init",&sd->stc.escp_init);
+ code = param_write_string(plist,"escp_Release",&sd->stc.escp_release);
+
+ if(sd->stc.dither != NULL) {
+ param_string_from_string(ps,sd->stc.dither->name);
+ code = param_write_string(plist,"Dithering",&ps);
+ } else {
+ code = param_write_null(plist,"Dithering");
+ }
+ if ( code < 0 ) return code;
+
+ nc = sd->color_info.num_components;
+
+ if(sd->stc.am != NULL) {
+ if( nc == 1) set_param_array(pfa, sd->stc.am, 3)
+ else if(nc == 3) set_param_array(pfa, sd->stc.am, 9)
+ else set_param_array(pfa, sd->stc.am,16)
+ code = param_write_float_array(plist,"ColorAdjustMatrix",&pfa);
+ } else {
+ code = param_write_null(plist,"ColorAdjustMatrix");
+ }
+ if ( code < 0 ) return code;
+
+ if(nc == 1) { /* DeviceGray */
+
+ stc_write_xarray(0,"Kcoding","Ktransfer");
+
+ stc_write_null("Rcoding"); stc_write_null("Rtransfer");
+ stc_write_null("Gcoding"); stc_write_null("Gtransfer");
+ stc_write_null("Bcoding"); stc_write_null("Btransfer");
+
+ stc_write_null("Ccoding"); stc_write_null("Ctransfer");
+ stc_write_null("Mcoding"); stc_write_null("Mtransfer");
+ stc_write_null("Ycoding"); stc_write_null("Ytransfer");
+
+ } else if(nc == 3) { /* DeviceRGB */
+
+ stc_write_xarray(0,"Rcoding","Rtransfer");
+ stc_write_xarray(1,"Gcoding","Gtransfer");
+ stc_write_xarray(2,"Bcoding","Btransfer");
+
+ stc_write_null("Ccoding"); stc_write_null("Ctransfer");
+ stc_write_null("Mcoding"); stc_write_null("Mtransfer");
+ stc_write_null("Ycoding"); stc_write_null("Ytransfer");
+ stc_write_null("Kcoding"); stc_write_null("Ktransfer");
+
+ } else { /* DeviceCMYK */
+
+ stc_write_xarray(0,"Ccoding","Ctransfer");
+ stc_write_xarray(1,"Mcoding","Mtransfer");
+ stc_write_xarray(2,"Ycoding","Ytransfer");
+ stc_write_xarray(3,"Kcoding","Ktransfer");
+
+ stc_write_null("Rcoding"); stc_write_null("Rtransfer");
+ stc_write_null("Gcoding"); stc_write_null("Gtransfer");
+ stc_write_null("Bcoding"); stc_write_null("Btransfer");
+
+ }
+ return code;
+}
+
+/***
+ *** put parameters == Store them in the device-structure
+ ***/
+
+static int
+stc_put_params(gx_device *pdev, gs_param_list *plist)
+{
+ int code,error,i,l;
+ bool b1,b2,b3;
+ float fv,*fp;
+ gs_param_string ps;
+ gs_param_string_array psa;
+ gs_param_float_array pfa;
+ stcolor_device *sd = (stcolor_device *) pdev;
+ gx_device_color_info oldcolor;
+ stc_t oldstc;
+
+/*
+ * save old Values
+ */
+ memcpy(&oldcolor,&sd->color_info,sizeof(oldcolor));
+ memcpy(&oldstc ,&sd->stc ,sizeof(oldstc ));
+
+/*
+ * Arrrgh:
+ * With Version 3.4x and above my simple minded read-only Parameters
+ * do not work any more. So read them here for heavens sake.
+ */
+ code = param_read_string(plist,"Version",&ps);
+ code = param_read_int(plist,"BitsPerComponent",&i);
+ code = param_read_string_array(plist,"Algorithms",&psa);
+
+/*
+ * Fetch Major-Parameters (Model, Dithering, BitsPerPixel/BitsPerComponent)
+ */
+ error = 0;
+
+ code = param_read_string(plist,"Model",&ps);
+ if(code == 0) { /* Analyze the Model-String */
+/*
+ * Arrgh: I should have known, that internal strings are not zero-terminated.
+ */
+ for(l = ps.size; (l > 0) && (ps.data[l-1] == 0); --l);
+# define stc_putcmp(Name) \
+ ((strlen(Name) != l) || (0 != strncmp(Name, (const char *)ps.data,l)))
+
+ sd->stc.flags &= ~STCMODEL;
+ if( !stc_putcmp("st800")) sd->stc.flags |= STCST800;
+ else if(!stc_putcmp("stcii")) sd->stc.flags |= STCSTCII;
+
+ } /* Analyze the Model-String */
+ if(code < 0) param_signal_error(plist,"Model",code);
+ error = error > code ? code : error;
+
+/* If we're running for st800, #components must be 1 */
+ if(((sd->stc.flags & STCMODEL) == STCST800) &&
+ (( sd->color_info.num_components > 1) ||
+ ( sd->stc.dither == NULL) ||
+ ((sd->stc.dither->flags & 7) > 1))) {
+ sd->color_info.num_components = 1;
+ sd->stc.dither = NULL;
+ }
+
+/* Weaving isn't a feature for the st800 */
+ if((sd->stc.flags & STCMODEL) == STCST800) {
+ sd->stc.flags &= ~STCUWEAVE;
+ sd->stc.flags |= STCNWEAVE;
+ } else if((sd->stc.flags & STCMODEL) == STCSTCII) { /* no SoftWeave */
+ sd->stc.flags |= STCNWEAVE;
+ }
+
+ code = param_read_string(plist,"Dithering",&ps);
+ if(code == 0) { /* lookup new value new value */
+
+ for(l = ps.size; (l > 0) && (ps.data[l-1] == 0); --l);
+
+ for(i = 0; stc_dither[i].name != NULL; ++i)
+ if(!stc_putcmp(stc_dither[i].name)) break;
+
+ } else if(sd->stc.dither != NULL) { /* compute index of given value */
+
+ i = sd->stc.dither - stc_dither;
+
+ } else { /* find matching value */
+
+ for(i = 0; stc_dither[i].name != NULL; ++i)
+ if((stc_dither[i].flags & 7) == sd->color_info.num_components) break;
+
+ } /* we've got an index */
+
+ if(stc_dither[i].name != NULL) { /* establish data */
+
+/*
+ * Establish new dithering algorithm & color-model
+ */
+ sd->stc.dither = stc_dither+i;
+ sd->color_info.num_components = sd->stc.dither->flags & 7;
+ STC_TYPESWITCH(sd->stc.dither,stc_sizeofitem)
+# undef stc_sizeofitem
+ if(((sd->stc.flags & STCMODEL) == STCST800) &&
+ ( sd->color_info.num_components > 1 ))
+ code = gs_error_rangecheck;
+
+/*
+ * reset Parameters related to the color-model, if it changed
+ */
+
+ if(sd->color_info.num_components != oldcolor.num_components) {
+
+ for(i = 0; i < sd->color_info.num_components; ++i) {
+ sd->stc.extv[i] = (float *) defext;
+ sd->stc.sizv[i] = countof(defext);
+
+ sd->stc.extc[i] = NULL;
+ sd->stc.sizc[i] = 0;
+
+ }
+
+ sd->stc.am = NULL;
+
+ } else { /* guarantee, that extvals is present */
+
+ for(i = 0; i < sd->color_info.num_components; ++i) {
+ if(sd->stc.sizv[i] < 2) {
+ sd->stc.extv[i] = (float *) defext;
+ sd->stc.sizv[i] = countof(defext);
+ }
+ }
+ }
+
+ for(i = sd->color_info.num_components; i < 4; ++ i) { /* clear unused */
+ sd->stc.extv[i] = NULL;
+ sd->stc.sizv[i] = 0;
+ sd->stc.vals[i] = NULL;
+
+ sd->stc.extc[i] = NULL;
+ sd->stc.sizc[i] = 0;
+ sd->stc.code[i] = NULL;
+
+ } /* clear unused */
+
+/*
+ * Guess default depth from range of values
+ */
+ if((sd->stc.dither != oldstc.dither)||(oldstc.vals[0] == NULL)) {
+
+ if((sd->stc.dither->flags & STC_CMYK10) != 0) {
+
+ sd->stc.flags |= STCCMYK10;
+ sd->stc.bits = 10;
+ sd->color_info.depth = 32;
+
+ } else {
+
+ sd->stc.flags &= ~STCCMYK10;
+
+ if((sd->stc.dither->flags & STC_FLOAT) != STC_FLOAT) {
+ fv = 2.0;
+ for(i = 1;(i < gx_color_value_bits) &&
+ (fv <= (sd->stc.dither->minmax[1]-sd->stc.dither->minmax[0]));
+ ++i) fv *= 2.0;
+
+ } else {
+ i = 8; /* arbitrary */
+ }
+
+ if((i*sd->color_info.num_components) > (sizeof(stc_pixel)*8)) {
+
+ sd->stc.bits = (sizeof(stc_pixel)*8) /
+ sd->color_info.num_components;
+ sd->color_info.depth = sd->stc.bits * sd->color_info.num_components;
+
+ } else {
+
+ sd->stc.bits = i;
+ sd->color_info.depth = sd->stc.bits * sd->color_info.num_components;
+
+ }
+ }
+ }
+
+ } else {
+
+ code = gs_error_rangecheck;
+
+ } /* verify new value */
+ if(code < 0) param_signal_error(plist,"Dithering",code);
+ error = error > code ? code : error;
+
+/*
+ * now fetch the desired depth, if the algorithm allows it
+ */
+/*
+ * Arrrgh: We get code == 0, even if nobody sets BitsPerPixel.
+ * The value is the old one, but this may cause trouble
+ * with CMYK10.
+ */
+ code = param_read_int(plist, "BitsPerPixel", &i);
+ if((error == 0) && (code == 0) &&
+ (((sd->stc.flags & STCCMYK10) == 0) || (i != sd->color_info.depth))) {
+
+ if((1 > i) || (i > (sizeof(stc_pixel)*8)))
+ code = gs_error_rangecheck;
+ else
+ sd->color_info.depth = i;
+
+ sd->stc.bits = i / sd->color_info.num_components;
+
+ if(1 > sd->stc.bits) code = gs_error_rangecheck;
+
+ if((sd->stc.dither->flags & STC_DIRECT) &&
+ (sd->stc.dither->flags & STC_CMYK10))
+ code = gs_error_rangecheck;
+ else
+ sd->stc.flags &= ~STCCMYK10;
+
+ }
+ if(code < 0) param_signal_error(plist,"BitsPerPixel",code);
+ error = error > code ? code : error;
+
+/*
+ * Fetch OutputCode
+ */
+ code = param_read_string(plist,"OutputCode",&ps);
+ if(code == 0) { /* Analyze the OutputCode-String */
+
+ for(l = ps.size; (l > 0) && (ps.data[l-1] == 0); --l);
+
+ sd->stc.flags &= ~STCCOMP;
+ if(!stc_putcmp("plain")) sd->stc.flags |= STCPLAIN;
+ else if(!stc_putcmp("deltarow")) sd->stc.flags |= STCDELTA;
+
+ } /* Analyze the OutputCode-String */
+ if((sd->stc.flags & STCCOMP) == STCDELTA) {
+ sd->stc.flags |= STCUWEAVE;
+ sd->stc.flags &= ~STCNWEAVE;
+ }
+ if(code < 0) param_signal_error(plist,"OutputCode",code);
+ error = error > code ? code : error;
+
+/*
+ * fetch the weave-mode (noWeave wins)
+ */
+ b1 = sd->stc.flags & STCUWEAVE ? true : false;
+ b2 = sd->stc.flags & STCNWEAVE ? true : false;
+ b3 = sd->stc.flags & (STCUWEAVE|STCNWEAVE) ? false : true;
+
+ code = param_read_bool(plist,"Microweave",&b1);
+ if(code < 0) {
+ param_signal_error(plist,"Microweave",code);
+ } else if(code == 0) {
+ if(b1) { b2 = false; b3 = false; }
+ }
+ error = error > code ? code : error;
+
+ code = param_read_bool(plist,"noWeave",&b2);
+ if(code < 0) {
+ param_signal_error(plist,"noWeave",code);
+ } else if (code == 0) {
+ if(b2) { b1 = false; b3 = false; }
+ }
+ error = error > code ? code : error;
+
+ code = param_read_bool(plist,"Softweave",&b3);
+ if(code < 0) {
+ param_signal_error(plist,"Softweave",code);
+ } else if (code == 0) {
+ if(b3) { b1 = false; b2 = false; }
+ }
+ error = error > code ? code : error;
+
+ if(b1) sd->stc.flags |= STCUWEAVE;
+ else sd->stc.flags &= ~STCUWEAVE;
+
+ if(b2) sd->stc.flags |= STCNWEAVE;
+ else sd->stc.flags &= ~STCNWEAVE;
+
+/*
+ * Check the simple Flags
+ */
+# define stc_read_flag(Mask,Name) \
+ code = param_read_bool(plist,Name,&b1); \
+ if(code < 0) { \
+ param_signal_error(plist,Name,code); \
+ } else if(code == 0) { \
+ if(b1 == true) sd->stc.flags |= Mask; \
+ else sd->stc.flags &= ~(Mask); \
+ } \
+ error = error > code ? code : error;
+
+ stc_read_flag(STCUNIDIR,"Unidirectional")
+ stc_read_flag(STCDFLAG0, "Flag0")
+ stc_read_flag(STCDFLAG1, "Flag1")
+ stc_read_flag(STCDFLAG2, "Flag2")
+ stc_read_flag(STCDFLAG3, "Flag3")
+ stc_read_flag(STCDFLAG4, "Flag4")
+
+/*
+ * Now deal with the escp-Stuff
+ */
+# define stc_read_int(Mask,Name,Val) \
+ code = param_read_int(plist,Name,&Val); \
+ if(code < 0) \
+ param_signal_error(plist,Name,code); \
+ else if(code == 0) \
+ sd->stc.flags |= Mask; \
+ error = error > code ? code : error
+
+ stc_read_int(STCBAND, "escp_Band", sd->stc.escp_m);
+ stc_read_int(STCWIDTH, "escp_Width", sd->stc.escp_width);
+ stc_read_int(STCHEIGHT,"escp_Height",sd->stc.escp_height);
+ stc_read_int(STCTOP, "escp_Top", sd->stc.escp_top);
+ stc_read_int(STCBOTTOM,"escp_Bottom",sd->stc.escp_bottom);
+
+# undef stc_read_int
+
+ code = param_read_string(plist,"escp_Init",&sd->stc.escp_init);
+ if(code == 0) sd->stc.flags |= STCINIT;
+ error = error > code ? code : error;
+
+ code = param_read_string(plist,"escp_Release",&sd->stc.escp_release);
+ if(code == 0) sd->stc.flags |= STCRELEASE;
+ error = error > code ? code : error;
+
+/*
+ * ColorAdjustMatrix must match the required size,
+ * setting it explicitly to null, erases old matrix
+ */
+ code = param_read_float_array(plist,"ColorAdjustMatrix",&pfa);
+ if((error == 0) && (code == 0)) {
+ if(((sd->color_info.num_components == 1) && (pfa.size == 3)) ||
+ ((sd->color_info.num_components == 3) && (pfa.size == 9)) ||
+ ((sd->color_info.num_components == 4) && (pfa.size == 16)))
+ sd->stc.am = (float *) pfa.data;
+ else
+ code = gs_error_rangecheck;
+ } else if(code < 0) {
+ code = param_read_null(plist,"ColorAdjustMatrix");
+ if(code == 0) sd->stc.am = NULL;
+ }
+ if(code < 0) param_signal_error(plist,"ColorAdjustMatrix",code);
+ error = error > code ? code : error;
+
+/*
+ * Read the external array-Parameters
+ */
+ if(sd->color_info.num_components == 1) { /* DeviceGray */
+
+ stc_read_xarray(0,"Kcoding","Ktransfer");
+
+ stc_read_null("Rcoding"); stc_read_null("Rtransfer");
+ stc_read_null("Gcoding"); stc_read_null("Gtransfer");
+ stc_read_null("Bcoding"); stc_read_null("Btransfer");
+
+ stc_read_null("Ccoding"); stc_read_null("Ctransfer");
+ stc_read_null("Mcoding"); stc_read_null("Mtransfer");
+ stc_read_null("Ycoding"); stc_read_null("Ytransfer");
+
+ } else if(sd->color_info.num_components == 3) { /* DeviceRGB */
+
+ stc_read_xarray(0,"Rcoding","Rtransfer");
+ stc_read_xarray(1,"Gcoding","Gtransfer");
+ stc_read_xarray(2,"Bcoding","Btransfer");
+
+ stc_read_null("Ccoding"); stc_read_null("Ctransfer");
+ stc_read_null("Mcoding"); stc_read_null("Mtransfer");
+ stc_read_null("Ycoding"); stc_read_null("Ytransfer");
+ stc_read_null("Kcoding"); stc_read_null("Ktransfer");
+
+ } else { /* DeviceCMYK */
+
+ stc_read_xarray(0,"Ccoding","Ctransfer");
+ stc_read_xarray(1,"Mcoding","Mtransfer");
+ stc_read_xarray(2,"Ycoding","Ytransfer");
+ stc_read_xarray(3,"Kcoding","Ktransfer");
+
+ stc_read_null("Rcoding"); stc_read_null("Rtransfer");
+ stc_read_null("Gcoding"); stc_read_null("Gtransfer");
+ stc_read_null("Bcoding"); stc_read_null("Btransfer");
+
+ }
+/*
+ * Update remaining color_info values
+ */
+ if(error == 0) {
+
+/* compute #values from the component-bits */
+ sd->color_info.max_gray = sd->stc.bits < gx_color_value_bits ?
+ (1<<sd->stc.bits)-1 : gx_max_color_value;
+
+/* An integer-algorithm might reduce the number of values */
+ if(((sd->stc.dither->flags & STC_TYPE) != STC_FLOAT) &&
+ ((sd->stc.dither->minmax[1]-sd->stc.dither->minmax[0]) <
+ sd->color_info.max_gray))
+ sd->color_info.max_gray = (gx_color_value)
+ (sd->stc.dither->minmax[1]-sd->stc.dither->minmax[0]+0.5);
+
+ sd->color_info.max_color = sd->color_info.num_components < 3 ? 0 :
+ sd->color_info.max_gray;
+ sd->color_info.dither_grays =
+ sd->color_info.max_gray < gx_max_color_value ?
+ sd->color_info.max_gray+1 : gx_max_color_value;
+ sd->color_info.dither_colors = sd->color_info.num_components < 3 ? 0 :
+ sd->color_info.dither_grays;
+ }
+
+/*
+ * Call superclass-Update
+ */
+
+ code = gdev_prn_put_params(pdev, plist);
+ error = error > code ? code : error;
+
+/*
+ * Arrrgh, writing BitsPerPixel is really *VERY* special:
+ * gdev_prn_put_params verifies, that the external value
+ * is written, if not, it raises a rangecheck-error.
+ * On the other hand ghostscript is quite unhappy with odd
+ * values, so we do the necessary rounding *AFTER* the
+ * "superclass-Update".
+ */
+
+ if(sd->color_info.depth == 3) sd->color_info.depth = 4;
+ else if(sd->color_info.depth > 4)
+ sd->color_info.depth = (sd->color_info.depth+7) & ~7;
+
+/*
+ * Allocate the storage for the arrays in memory
+ */
+ if(error == 0) { /* Allocate new external-arrays */
+
+ for(i = 0; i < sd->color_info.num_components; ++i){ /* Active components */
+ int j;
+
+ if((sd->stc.extv[i] != oldstc.extv[i]) &&
+ (sd->stc.extv[i] != defext )) { /* Value-Arrays */
+
+ for(j = 0; j < i; ++j)
+ if((sd->stc.sizv[j] == sd->stc.sizv[i]) &&
+ (memcmp(sd->stc.extv[j],sd->stc.extv[i],
+ sd->stc.sizv[i]*sizeof(float)) == 0)) break;
+
+ if(j < i) {
+ sd->stc.extv[i] = sd->stc.extv[j];
+ } else {
+ fp = gs_malloc(sd->memory, sd->stc.sizv[i],sizeof(float),"stc_put_params");
+ if(fp != NULL)
+ memcpy(fp,sd->stc.extv[i],sd->stc.sizv[i]*sizeof(float));
+ else
+ code = gs_error_VMerror;
+ sd->stc.extv[i] = fp;
+ }
+ } /* Value-Arrays */
+
+ if((sd->stc.sizc[i] > 1) &&
+ (sd->stc.extc[i] != oldstc.extc[i])) { /* Code-Arrays */
+
+ for(j = 0; j < i; ++j)
+ if((sd->stc.sizc[j] == sd->stc.sizc[i]) &&
+ (memcmp(sd->stc.extc[j],sd->stc.extc[i],
+ sd->stc.sizc[i]*sizeof(float)) == 0)) break;
+
+ if(j < i) {
+ sd->stc.extc[i] = sd->stc.extc[j];
+ } else {
+ fp = gs_malloc(sd->memory, sd->stc.sizc[i],sizeof(float),"stc_put_params");
+ if(fp != NULL)
+ memcpy(fp,sd->stc.extc[i],sd->stc.sizc[i]*sizeof(float));
+ else
+ code = gs_error_VMerror;
+ sd->stc.extc[i] = fp;
+ }
+ } /* Code-Arrays */
+
+ } /* Active components */
+
+ if((sd->stc.am != NULL) && (sd->stc.am != oldstc.am)) {
+ if( sd->color_info.num_components == 1) i = 3;
+ else if(sd->color_info.num_components == 3) i = 9;
+ else i = 16;
+ fp = gs_malloc(sd->memory, i,sizeof(float),"stc_put_params");
+ if(fp != NULL) memcpy(fp,sd->stc.am,i*sizeof(float));
+ else code = gs_error_VMerror;
+ sd->stc.am = fp;
+ }
+
+ if(sd->stc.escp_init.data != oldstc.escp_init.data) {
+ byte *ip = NULL;
+
+ if(sd->stc.escp_init.size > 0) {
+ ip = gs_malloc(sd->memory, sd->stc.escp_init.size,1,"stcolor/init");
+ if(ip == NULL) {
+ code = gs_error_VMerror;
+ sd->stc.escp_init.size = 0;
+ } else {
+ memcpy(ip,sd->stc.escp_init.data,sd->stc.escp_init.size);
+ }
+ }
+ sd->stc.escp_init.data = ip;
+ sd->stc.escp_init.persistent = false;
+ }
+
+ if(sd->stc.escp_release.data != oldstc.escp_release.data) {
+ byte *ip = NULL;
+
+ if(sd->stc.escp_release.size > 0) {
+ ip = gs_malloc(sd->memory, sd->stc.escp_release.size,1,"stcolor/release");
+ if(ip == NULL) {
+ code = gs_error_VMerror;
+ sd->stc.escp_release.size = 0;
+ } else {
+ memcpy(ip,sd->stc.escp_release.data,sd->stc.escp_release.size);
+ }
+ }
+ sd->stc.escp_release.data = ip;
+ sd->stc.escp_release.persistent = false;
+ }
+
+ if(code < 0) { /* free newly allocated arrays */
+
+ if((sd->stc.am != NULL) && (sd->stc.am != oldstc.am)) {
+ if( sd->color_info.num_components == 1) i = 3;
+ else if(sd->color_info.num_components == 3) i = 9;
+ else i = 16;
+ gs_free(sd->memory, sd->stc.am,i,sizeof(float),"stc_put_params");
+ }
+
+ if((sd->stc.escp_init.data != NULL) &&
+ (sd->stc.escp_init.data != oldstc.escp_init.data))
+ gs_free(sd->memory, (byte *) sd->stc.escp_init.data,sd->stc.escp_init.size,1,
+ "stcolor/init");
+
+ if((sd->stc.escp_release.data != NULL) &&
+ (sd->stc.escp_release.data != oldstc.escp_release.data))
+ gs_free(sd->memory, (byte *) sd->stc.escp_release.data,sd->stc.escp_release.
+ size,1,"stcolor/release");
+
+ for(i = 0; i < sd->color_info.num_components; ++i) { /* components */
+ int j;
+
+ if((sd->stc.extc[i] != NULL) &&
+ (sd->stc.extc[i] != defext) &&
+ (sd->stc.extc[i] != oldstc.extc[i])) {
+
+ for(j = 0; j < i; ++j)
+ if(sd->stc.extc[i] == sd->stc.extc[j]) break;
+
+ if(i == j) gs_free(sd->memory, sd->stc.extc[i],sd->stc.sizc[i],sizeof(float),
+ "stc_put_params");
+ }
+
+ if((sd->stc.extv[i] != NULL) &&
+ (sd->stc.extv[i] != oldstc.extv[i]) &&
+ (sd->stc.extv[i] != defext)) {
+
+ for(j = 0; j < i; ++j)
+ if(sd->stc.extv[i] == sd->stc.extv[j]) break;
+
+ if(i == j) gs_free(sd->memory, sd->stc.extv[i],sd->stc.sizv[i],sizeof(float),
+ "stc_put_params");
+ }
+ } /* components */
+ } /* free newly allocated arrays */
+ } /* Allocate new arrays */
+ error = error > code ? code : error;
+
+/*
+ * finally decide upon restore or release of old, unused data
+ */
+ if(error != 0) { /* Undo changes */
+
+ memcpy(&sd->color_info,&oldcolor,sizeof(oldcolor));
+ memcpy(&sd->stc ,&oldstc ,sizeof(oldstc ));
+ } else { /* undo / release */
+
+ if((oldstc.escp_init.data != NULL) &&
+ (oldstc.escp_init.data != sd->stc.escp_init.data)) {
+ gs_free(sd->memory, (byte *)oldstc.escp_init.data,
+ oldstc.escp_init.size,1,"stcolor/init");
+ }
+
+ if((oldstc.escp_release.data != NULL) &&
+ (oldstc.escp_release.data != sd->stc.escp_release.data)) {
+ gs_free(sd->memory, (byte *)oldstc.escp_release.data,
+ oldstc.escp_release.size,1,"stcolor/release");
+ }
+
+ if((oldstc.am != NULL) && (oldstc.am != sd->stc.am)) {
+ if( oldcolor.num_components == 1) i = 3;
+ else if(oldcolor.num_components == 3) i = 9;
+ else i = 16;
+ gs_free(sd->memory, oldstc.am,i,sizeof(float),"stc_put_params");
+ }
+
+ for(i = 0; i < 4; ++i) {
+ int j;
+
+ if((oldstc.extc[i] != NULL) &&
+ (oldstc.extc[i] != sd->stc.extc[i]) &&
+ (oldstc.dither != NULL) &&
+ (oldstc.extc[i] != defext)) {
+
+ for(j = 0; j < i; ++j) if(oldstc.extc[i] == oldstc.extc[j]) break;
+
+ if(i == j) gs_free(sd->memory, oldstc.extc[i],oldstc.sizc[i],sizeof(float),
+ "stc_put_params");
+ }
+
+ if((oldstc.extv[i] != NULL) &&
+ (oldstc.extv[i] != sd->stc.extv[i]) &&
+ (oldstc.extv[i] != defext)) {
+
+ for(j = 0; j < i; ++j) if(oldstc.extv[i] == oldstc.extv[j]) break;
+
+ if(i == j) gs_free(sd->memory, oldstc.extv[i],oldstc.sizv[i],sizeof(float),
+ "stc_put_params");
+ }
+ }
+
+/*
+ * Close the device if colormodel changed or recomputation
+ * of internal arrays is required
+ */
+ if(sd->is_open) { /* we might need to close it */
+ bool doclose = false;
+ if((sd->color_info.num_components != oldcolor.num_components) ||
+ (sd->color_info.depth != oldcolor.depth ) ||
+ (sd->stc.bits != oldstc.bits ) ||
+ (sd->stc.dither != oldstc.dither ))
+ doclose = true;
+
+ for(i = 0; i < sd->color_info.num_components; ++i) {
+ if(sd->stc.extv[i] != oldstc.extv[i]) doclose = true;
+ if(sd->stc.extc[i] != oldstc.extc[i]) doclose = true;
+ }
+ if(doclose) {
+ stc_freedata(pdev->memory, &oldstc);
+ for(i = 0; i < 4; ++i) {
+ sd->stc.vals[i] = NULL;
+ sd->stc.code[i] = NULL;
+ }
+
+ gs_closedevice(pdev);
+ }
+ } /* we might need to close it */
+
+ }
+
+ return error;
+}
+/*
+ * 1Bit CMYK-Algorithm
+ */
+
+static int
+stc_gscmyk(stcolor_device *sdev,int npixel,byte *in,byte *buf,byte *out)
+{
+
+ byte *ip = in;
+ int error = 0;
+
+/* ============================================================= */
+ if(npixel > 0) { /* npixel > 0 -> scanline-processing */
+/* ============================================================= */
+
+ int p;
+
+/*
+ * simply split the two pixels rsiding in a byte
+ */
+ for(p = npixel; p > 0; --p) { /* loop over pixels */
+ byte tmp =*ip++;
+
+ *out++ = (tmp>>4) & 15;
+ if(--p <= 0) break;
+
+ *out++ = tmp & 15;
+
+ } /* loop over pixels */
+
+/* ============================================================= */
+ } else { /* npixel <= 0 -> initialisation */
+/* ============================================================= */
+
+/* we didn't check for the white-calls above, so this may cause errors */
+ if(sdev->stc.dither->flags & STC_WHITE) error = -1;
+
+/* if we're not setup for bytes, this is an error too */
+ if((sdev->stc.dither->flags & STC_TYPE) != STC_BYTE) error = -2;
+
+/* This IS a direct-driver, so STC_DIRECT must be set! */
+ if((sdev->stc.dither->flags & STC_DIRECT) == 0) error = -3;
+
+/* and cmyk-mode is the only supported mode */
+ if(sdev->color_info.num_components != 4) error = -4;
+
+/* and we support only 4Bit-Depth here */
+ if(sdev->color_info.depth != 4) error = -5;
+
+/* ============================================================= */
+ } /* scanline-processing or initialisation */
+/* ============================================================= */
+
+ return error;
+}
+
+/*
+ * The following is an algorithm under test
+ */
+static int
+stc_hscmyk(stcolor_device *sdev,int npixel,byte *in,byte *buf,byte *out)
+{
+
+/* ============================================================= */
+ if(npixel < 0) { /* npixel <= 0 -> initialisation */
+/* ============================================================= */
+
+ int i,i2do;
+ long *lp = (long *) buf;
+
+/* CMYK-only algorithm */
+ if( sdev->color_info.num_components != 4) return -1;
+
+/*
+ * check wether stcdither & TYPE are correct
+ */
+ if(( sdev->stc.dither == NULL) ||
+ ((sdev->stc.dither->flags & STC_TYPE) != STC_LONG)) return -2;
+
+/*
+ * check wether the buffer-size is sufficiently large
+ */
+ if(((sdev->stc.dither->flags/STC_SCAN) < 1) ||
+ ( sdev->stc.dither->bufadd <
+ (1 + 2*sdev->color_info.num_components))) return -3;
+
+/*
+ * must have STC_CMYK10, STC_DIRECT, but not STC_WHITE
+ */
+ if((sdev->stc.dither->flags & STC_CMYK10) == 0) return -4;
+ if((sdev->stc.dither->flags & STC_DIRECT) == 0) return -5;
+ if((sdev->stc.dither->flags & STC_WHITE ) != 0) return -6;
+
+/*
+ * Must have values between 0-1023.0
+ */
+ if((sdev->stc.dither->minmax[0] != 0.0) ||
+ (sdev->stc.dither->minmax[1] != 1023.0)) return -7;
+/*
+ * initialize buffer
+ */
+
+ i2do = 1 + 8 - 4 * npixel;
+ lp[0] = 0;
+
+ if(sdev->stc.flags & STCDFLAG0) {
+ for(i = 1; i < i2do; ++i) lp[i] = 0;
+ } else {
+ for(i = 1; i < i2do; ++i) lp[i] = (rand() % 381) - 190;
+ }
+
+/* ============================================================= */
+ } else { /* npixel > 0 && in != NULL -> scanline-processing */
+/* ============================================================= */
+
+ long errc[4],*errv;
+ int step = buf[0] ? -1 : 1;
+ stc_pixel *ip = (stc_pixel *) in;
+
+ buf[0] = ~ buf[0];
+ errv = (long *) buf + 5;
+
+ if(step < 0) {
+ ip += npixel-1;
+ out += npixel-1;
+ errv += 4*(npixel-1);
+ }
+
+ errc[0] = 0; errc[1] = 0; errc[2] = 0; errc[3] = 0;
+
+ while(npixel-- > 0) {
+
+ register stc_pixel ci,mode;
+ register long k,v,n;
+ register int pixel; /* internal pixel-value */
+
+ ci = *ip; ip += step;
+
+ mode = ci & 3;
+ k = (ci>>2) & 0x3ff;
+ pixel = 0;
+
+ v = k+errv[3]+((7*errc[3])>>4);
+
+ if(mode == 3) { /* only Black allowed to fire */
+
+ if(v > 511) {
+ v -= 1023;
+ pixel = BLACK;
+ }
+ errv[3-(step<<2)] += ((3*v+8)>>4); /* 3/16 */
+ errv[3] = ((5*v+errc[3]+8)>>4);/* 5/16 +1/16 (rest) */
+ errc[3] = v;
+
+ errv[0] = errv[0] < -190 ? -190 : errv[0] < 190 ? errv[0] : 190;
+ errv[1] = errv[1] < -190 ? -190 : errv[1] < 190 ? errv[1] : 190;
+ errv[2] = errv[2] < -190 ? -190 : errv[2] < 190 ? errv[2] : 190;
+
+ errc[0] = 0; errc[1] = 0; errc[2] = 0;
+
+ } else if(v > 511) { /* black known to fire */
+
+ v -= 1023;
+ pixel = BLACK;
+
+ errv[3-(step<<2)] += ((3*v+8)>>4); /* 3/16 */
+ errv[3] = ((5*v+errc[3]+8)>>4);/* 5/16 +1/16 (rest) */
+ errc[3] = v;
+
+ n = (ci>>12) & 0x3ff;
+
+ if(mode == 2) { v = k; }
+ else { v = n; n = (ci>>22) & 0x3ff; }
+
+ v += errv[2]+((7*errc[2])>>4)-1023;
+ if(v < -511) v = -511;
+ errv[2-(step<<2)] += ((3*v+8)>>4); /* 3/16 */
+ errv[2] = ((5*v+errc[2]+8)>>4);/* 5/16 +1/16 (rest) */
+ errc[2] = v;
+
+ if(mode == 1) { v = k; }
+ else { v = n; n = (ci>>22) & 0x3ff; }
+
+ v += errv[1]+((7*errc[1])>>4)-1023;
+ if(v < -511) v = -511;
+ errv[1-(step<<2)] += ((3*v+8)>>4); /* 3/16 */
+ errv[1] = ((5*v+errc[1]+8)>>4);/* 5/16 +1/16 (rest) */
+ errc[1] = v;
+
+ if(mode == 0) v = k;
+ else v = n;
+
+ v += errv[0]+((7*errc[0])>>4)-1023;
+ if(v < -511) v = -511;
+ errv[0-(step<<2)] += ((3*v+8)>>4); /* 3/16 */
+ errv[0] = ((5*v+errc[0]+8)>>4);/* 5/16 +1/16 (rest) */
+ errc[0] = v;
+
+ } else { /* Black does not fire initially */
+
+ long kv = v; /* Black computed after colors */
+
+ n = (ci>>12) & 0x3ff;
+
+ if(mode == 2) { v = k; }
+ else { v = n; n = (ci>>22) & 0x3ff; }
+
+ v += errv[2]+((7*errc[2])>>4);
+ if(v > 511) {
+ pixel |= YELLOW;
+ v -= 1023;
+ }
+ errv[2-(step<<2)] += ((3*v+8)>>4); /* 3/16 */
+ errv[2] = ((5*v+errc[2]+8)>>4);/* 5/16 +1/16 (rest) */
+ errc[2] = v;
+
+ if(mode == 1) { v = k; }
+ else { v = n; n = (ci>>22) & 0x3ff; }
+
+ v += errv[1]+((7*errc[1])>>4);
+ if(v > 511) {
+ pixel |= MAGENTA;
+ v -= 1023;
+ }
+ errv[1-(step<<2)] += ((3*v+8)>>4); /* 3/16 */
+ errv[1] = ((5*v+errc[1]+8)>>4);/* 5/16 +1/16 (rest) */
+ errc[1] = v;
+
+ if(mode == 0) v = k;
+ else v = n;
+
+ v += errv[0]+((7*errc[0])>>4);
+ if(v > 511) {
+ pixel |= CYAN;
+ v -= 1023;
+ }
+ errv[0-(step<<2)] += ((3*v+8)>>4); /* 3/16 */
+ errv[0] = ((5*v+errc[0]+8)>>4);/* 5/16 +1/16 (rest) */
+ errc[0] = v;
+
+ v = kv;
+ if(pixel == (CYAN|MAGENTA|YELLOW)) {
+ pixel = BLACK;
+ v = v > 511 ? v-1023 : -511;
+ }
+ errv[3-(step<<2)] += ((3*v+8)>>4); /* 3/16 */
+ errv[3] = ((5*v+errc[3]+8)>>4);/* 5/16 +1/16 (rest) */
+ errc[3] = v;
+
+ }
+
+ errv += step<<2;
+ *out = pixel; out += step;
+
+ } /* loop over pixels */
+
+/* ============================================================= */
+ } /* initialisation, white or scanline-processing */
+/* ============================================================= */
+
+ return 0;
+}
diff --git a/devices/gdevstc.h b/devices/gdevstc.h
new file mode 100644
index 000000000..a2b730300
--- /dev/null
+++ b/devices/gdevstc.h
@@ -0,0 +1,248 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Epson Stylus-Color Printer-Driver */
+#ifndef gdevstc_INCLUDED
+# define gdevstc_INCLUDED
+
+/***
+ *** This holds most of the declarations used by gdevstc.c/stcolor.
+ *** It should be included by the dithering-routines and should be
+ *** modified to include the separately compilable routines.
+ ***/
+
+/*** Ghostscript-Headers ***/
+
+#include "gdevprn.h"
+#include "gsparam.h"
+#include "gsstate.h"
+
+/*** Private Type for 32Bit-Pixels ***/
+#if arch_log2_sizeof_int < 2 /* int is too small */
+ typedef unsigned long stc_pixel;
+#else /* int is sufficient */
+ typedef unsigned int stc_pixel;
+#endif /* use int or long ? */
+
+/*** Auxillary-Device Structure ***/
+
+typedef struct stc_s {
+ long flags; /* some mode-flags */
+ int bits; /* the number of bits per component */
+ const struct stc_dither_s *dither; /* dithering-mode */
+ float *am; /* 3/9/16-E. vector/matrix */
+
+ float *extc[4]; /* Given arrays for stccode */
+ uint sizc[4]; /* Size of extcode-arrays */
+ gx_color_value *code[4]; /* cv -> internal */
+
+ float *extv[4]; /* Given arrays for stcvals */
+ uint sizv[4]; /* Size of extvals-arrays */
+ byte *vals[4]; /* internal -> dithering */
+
+ stc_pixel white_run[3]; /* the white-pattern */
+ stc_pixel white_end[3]; /* the white-Trailer */
+ gs_param_string_array
+ algorithms; /* Names of the available algorithms */
+
+ gs_param_string escp_init; /* Initialization-Sequence */
+ gs_param_string escp_release; /* Initialization-Sequence */
+ int escp_width; /* Number of Pixels printed */
+ int escp_height;/* Height send to the Printer */
+ int escp_top; /* Top-Margin, send to the printer */
+ int escp_bottom;/* Bottom-Margin, send to the printer */
+
+ int alg_item; /* Size of the items used by the algorithm */
+
+ int prt_buf; /* Number of buffers */
+ int prt_size; /* Size of the Printer-buffer */
+ int escp_size; /* Size of the ESC/P2-buffer */
+ int seed_size; /* Size of the seed-buffers */
+
+ int escp_u; /* print-resolution (3600 / ydpi )*/
+ int escp_c; /* selected color */
+ int escp_v; /* spacing within band */
+ int escp_h; /* 3600 / xdpi */
+ int escp_m; /* number of heads */
+ int escp_lf; /* linefeed in units */
+
+ int prt_y; /* print-coordinate */
+ int stc_y; /* Next line 2b printed */
+ int buf_y; /* Next line 2b loaded into the buffer */
+ int prt_scans; /* number of lines printed */
+
+ int *prt_width; /* Width of buffered lines */
+ byte **prt_data; /* Buffered printer-lines */
+ byte *escp_data; /* Buffer for ESC/P2-Data */
+ byte *seed_row[4];/* Buffer for delta-row compression (prt_size) */
+
+ int dir;
+
+} stc_t;
+
+/*** Main-Device Structure ***/
+
+typedef struct stcolor_device_s {
+ gx_device_common;
+ gx_prn_device_common;
+ stc_t stc;
+} stcolor_device;
+
+#define STCDFLAG0 0x000001L /* Algorithm-Bit 0 */
+#define STCDFLAG1 0x000002L /* Algorithm-Bit 1 */
+#define STCDFLAG2 0x000004L /* Algorithm-Bit 2 */
+#define STCDFLAG3 0x000008L /* Algorithm-Bit 3 */
+#define STCDFLAG4 0x000010L /* Algorithm-Bit 4 */
+#define STCCMYK10 0x000020L /* CMYK10-Coding active */
+
+#define STCUNIDIR 0x000040L /* Unidirectional, if set */
+#define STCUWEAVE 0x000080L /* Hardware Microweave */
+#define STCNWEAVE 0x000100L /* Software Microweave disabled */
+
+#define STCOK4GO 0x000200L /* stc_put_params was o.k. */
+
+#define STCCOMP 0x000C00L /* RLE, Plain (>= 1.18) */
+#define STCPLAIN 0x000400L /* No compression */
+#define STCDELTA 0x000800L /* Delta-Row */
+
+#define STCMODEL 0x00f000L /* STC, ST800 */
+#define STCST800 0x001000L /* Monochrome-Variant */
+#define STCSTCII 0x002000L /* Stylus Color II */
+
+#define STCBAND 0x010000L /* Initialization defined */
+#define STCHEIGHT 0x020000L /* Page-Length set */
+#define STCWIDTH 0x040000L /* Page-Length set */
+#define STCTOP 0x080000L /* Top-Margin set */
+#define STCBOTTOM 0x100000L /* Bottom-Margin set */
+#define STCINIT 0x200000L /* Initialization defined */
+#define STCRELEASE 0x400000L /* Release defined */
+
+#define STCPRINT 0x800000L /* Data printed */
+
+/*** Datatype for the array of dithering-Algorithms ***/
+
+#define stc_proc_dither(name) \
+ int name(stcolor_device *sdev,int npixel,byte *in,byte *buf,byte *out)
+
+typedef struct stc_dither_s {
+ const char *name; /* Mode-Name for Dithering */
+ stc_proc_dither((*fun));
+ uint flags;
+ uint bufadd;
+ double minmax[2];
+} stc_dither_t;
+
+/*
+ * Color-Values for the output
+ */
+#define BLACK 1 /* in monochrome-Mode as well as in CMYK-Mode */
+#define RED 4 /* in RGB-Mode */
+#define GREEN 2
+#define BLUE 1
+#define CYAN 8 /* in CMYK-Mode */
+#define MAGENTA 4
+#define YELLOW 2
+
+/*** A Macro to ease Type-depending things with the stc_p-union ***/
+
+#define STC_TYPESWITCH(Dither,Action) \
+ switch((Dither)->flags & STC_TYPE) { \
+ case STC_BYTE: Action(byte); break; \
+ case STC_LONG: Action(long); break; \
+ default: Action(float); break;}
+
+/***
+ *** MODIFY HERE to include your routine:
+ ***
+ *** 1. Declare it here
+ *** 2. Add it to the definition of STC_MODI
+ *** 3. Add your file to the dependency-list in the Makefile & devices.mak
+ ***/
+
+/* Step 1. */
+stc_proc_dither(stc_gsmono); /* resides in gdevstc1.c */
+stc_proc_dither(stc_fs); /* resides in gdevstc2.c */
+stc_proc_dither(stc_fscmyk); /* resides in gdevstc2.c too */
+stc_proc_dither(stc_gsrgb); /* resides in gdevstc3.c */
+stc_proc_dither(stc_fs2); /* resides in gdevstc4.c */
+
+/* Values used to assemble flags */
+#define DeviceGray 1 /* ProcessColorModel = DeviceGray */
+#define DeviceRGB 3 /* ProcessColorModel = DeviceRGB */
+#define DeviceCMYK 4 /* ProcessColorModel = DeviceCMYK */
+
+#define STC_BYTE 8 /* Pass Bytes to the Dithering-Routine */
+#define STC_LONG 16 /* Pass Longs to the Dithering-Routine */
+#define STC_FLOAT 24 /* Pass Floats to the Dithering-Routine */
+#define STC_TYPE 24 /* all the type-bits */
+
+#define STC_CMYK10 32 /* Special 32-Bit CMYK-Coding */
+#define STC_DIRECT 64 /* Suppress conversion of Scanlines */
+#define STC_WHITE 128 /* Call Algorithm for white lines too (out == NULL) */
+#define STC_SCAN 256 /* multiply by number of scanlines in buffer */
+
+/* Step 2. */
+/* Items: 1. Name to activate it
+ 2. Name of the dithering-function
+ 3. Several flags ored together, including # of buffered scanlines
+ 4. Additional buffer-space (bytes/longs/floats)
+ 5. Array of double with minimum and maximum-value
+ Keep the last line as it is.
+ */
+
+#define STC_MODI \
+{"gsmono", stc_gsmono, DeviceGray|STC_BYTE,0,{0.0,1.0}},\
+{"gsrgb" , stc_gsrgb , DeviceRGB |STC_BYTE,0,{0.0,1.0}},\
+{"fsmono", stc_fs, \
+ DeviceGray|STC_LONG|1*STC_SCAN,3+3*1,{0.0,16777215.0}},\
+{"fsrgb", stc_fs, \
+ DeviceRGB |STC_LONG|1*STC_SCAN,3+3*3,{0.0,16777215.0}},\
+{"fsx4", stc_fs, \
+ DeviceCMYK|STC_LONG|1*STC_SCAN,3+3*4,{0.0,16777215.0}},\
+{"fscmyk", stc_fscmyk, \
+ DeviceCMYK|STC_LONG|1*STC_SCAN,3+3*4,{0.0,16777215.0}},\
+{"fs2", stc_fs2, \
+ DeviceRGB |STC_BYTE|STC_WHITE|1*STC_SCAN,0,{0.0,255.0}},
+
+#ifndef X_DPI
+#define X_DPI 360
+#endif /* X_DPI */
+#ifndef Y_DPI
+#define Y_DPI 360
+#endif /* Y_DPI */
+
+#ifndef STC_L_MARGIN
+# define STC_L_MARGIN 0.125 /* yields 45 Pixel@360DpI */
+#endif /* STC_L_MARGIN */
+#ifndef STC_B_MARGIN
+# define STC_B_MARGIN 0.555 /* yields 198 Pixel@#60DpI (looses 1mm) */
+#endif /* STC_B_MARGIN */
+/*
+ * Right-Margin: Should match maximum print-width of 8".
+ */
+
+#ifndef STC_R_MARGIN
+# ifdef A4
+# define STC_R_MARGIN 0.175 /* Yields 63 Pixel@360DpI */
+# else
+# define STC_R_MARGIN 0.375 /* 135 Pixel */
+# endif
+#endif /* STC_R_MARGIN */
+#ifndef STC_T_MARGIN
+# define STC_T_MARGIN 0.125
+#endif /* STC_T_MARGIN */
+
+#endif
diff --git a/devices/gdevstc1.c b/devices/gdevstc1.c
new file mode 100644
index 000000000..a2241571e
--- /dev/null
+++ b/devices/gdevstc1.c
@@ -0,0 +1,124 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Epson Stylus-Color Printer-Driver */
+
+/***
+ This file holds the sample-implementation of a monochrome-algorithm for
+ the stcolor-driver. It is available via
+
+ gs -sDEVICE=stcolor -sDithering=gsmono ...
+
+ Actually this is no dithering-algorithm, it lets ghostscript do the job.
+ This achieved, by requesting BYTE-Values between 0 and 1 to be delivered,
+ which causes a depth of 1-Bit by default.
+
+ ***/
+
+/*
+ * gdevstc.h holds all the includes and the driver-specific definitions, so
+ * it is the only include you need. To add a new algorthim, STC_MODI in
+ * gdevstc.h should be extended. (see the instructions there)
+ */
+
+#include "gdevstc.h"
+
+/*
+ * the routine required.
+ */
+
+/*ARGSUSED*/
+int
+stc_gsmono(stcolor_device *sdev,int npixel,byte *in,byte *buf,byte *out)
+{
+
+/*
+ * There are basically 3 Types of calls:
+ * npixel < 0 => initialize buf, if this is required
+ * (happens only if requested)
+ * npixel > 0 => process next scanline, if the flag STC_WHITE is set, then
+ * in == NULL signals, that the basic-driver has decided
+ * that this scanline is white. (Useful for really dithering
+ * drivers)
+ */
+
+/* ============================================================= */
+ if(npixel > 0) { /* npixel > 0 -> scanline-processing */
+/* ============================================================= */
+
+/* -----------------------------------------------*/
+ if(in != NULL) { /* normal processing */
+/* -----------------------------------------------*/
+
+ memcpy(out,in,npixel); /* really simple algorithm */
+
+/* -----------------------------------------------*/
+ } else { /* skip-notification */
+/* -----------------------------------------------*/
+
+ /* An algorithm may use the output-line as a buffer.
+ So it might need to be cleared on white-lines.
+ */
+
+ memset(out,0,npixel);
+
+/* -----------------------------------------------*/
+ } /* normal / skip */
+/* -----------------------------------------------*/
+
+/* ============================================================= */
+ } else { /* npixel <= 0 -> initialisation */
+/* ============================================================= */
+/*
+ * the optional buffer is already allocated by the basic-driver, here
+ * you just need to fill it, for instance, set it all to zeros:
+ */
+ int buf_size;
+
+/*
+ * compute the size of the buffer, according to the requested values
+ * the buffer consists of a constant part, e.g. related to the number
+ * of color-components, and a number of arrays, which are multiples of
+ * the size of a scanline times the number of components.
+ * additionally, the size of the scanlines may be expanded by one to the
+ * right and to the left.
+ */
+ buf_size =
+ sdev->stc.dither->bufadd /* scanline-independend size */
+ + (-npixel) /* pixels */
+ * (sdev->stc.dither->flags/STC_SCAN) /* * scanlines */
+ * sdev->color_info.num_components; /* * comp */
+
+ if(buf_size > 0) { /* we obviously have a buffer */
+ memset(buf,0,buf_size * sdev->stc.alg_item);
+ } /* we obviously have a buffer */
+
+/*
+ * Usually one should check parameters upon initializaon
+ */
+ if(sdev->color_info.num_components != 1) return -1;
+
+ if((sdev->stc.dither->flags & STC_TYPE) != STC_BYTE) return -2;
+
+/*
+ * must neither have STC_DIRECT nor STC_WHITE
+ */
+ if((sdev->stc.dither->flags & STC_DIRECT) != 0) return -3;
+
+ } /* scanline-processing or initialisation */
+
+ return 0; /* negative values are error-codes, that abort printing */
+}
diff --git a/devices/gdevstc2.c b/devices/gdevstc2.c
new file mode 100644
index 000000000..c8cf7532b
--- /dev/null
+++ b/devices/gdevstc2.c
@@ -0,0 +1,421 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Epson Stylus-Color Printer-Driver */
+
+/***
+ This file holds two implementations of the Floyd-Steinberg error
+ diffusion-algorithm. This algorithms are intended for high quality
+ printing in conjunction with the PostScript-Header stcolor.ps:
+
+ gs -sDEVICE=stcolor <other options> stcolor.ps ...
+
+ Most prominent option is -sDithering=xxx, to select the algorithm:
+
+ fsmono - monochrome Floyd-Steinberg
+ fsrgb - 3-Component Floyd-Steinberg
+ fsx4 - 4-Component Floyd-Steinberg (Bad results)
+
+ fscmyk - Modified 4-Component Floyd-Steinberg
+ (Algorithmically identical with hscmyk, but slower)
+
+ ***/
+
+#include "gdevstc.h"
+
+#include <stdlib.h> /* for rand */
+
+/*
+ Both algorithms require an error-buffer of
+
+ 3 + 3*num_components +1*scan long-items.
+
+ and must consequently set up to work with longs.
+ It is just a Floyd-Steinberg-algorithm applied to each component.
+
+ */
+
+/*
+ * Due to the -selfdefined- ugly coding of the output-data, we need
+ * some conversion. But since this includes the black-separation, I
+ * did not change the definition.
+ *
+ * This algorithm stores the 1st component in the LSB, thus it
+ * reverts the order used by the basic driver.
+ */
+
+static const byte grayvals[2] = { 0, BLACK };
+
+static const byte rgbvals[8] = {
+ 0, RED, GREEN, RED|GREEN, BLUE, BLUE|RED, BLUE|GREEN, BLUE|RED|GREEN};
+
+static const byte cmykvals[16] = {
+ 0, CYAN,MAGENTA,CYAN|MAGENTA,YELLOW,YELLOW|CYAN,YELLOW|MAGENTA,BLACK,
+ BLACK,BLACK, BLACK, BLACK, BLACK, BLACK, BLACK,BLACK};
+
+static const byte *const pixelconversion[5] = {
+ NULL, grayvals, NULL, rgbvals, cmykvals};
+
+int
+stc_fs(stcolor_device *sdev,int npixel,byte *bin,byte *bbuf,byte *out)
+{
+
+ long *in = (long *) bin;
+ long *buf = (long *) bbuf;
+
+/* ============================================================= */
+ if(npixel > 0) { /* npixel > 0 -> scanline-processing */
+/* ============================================================= */
+
+ int bstep,pstart,pstop,pstep,p;
+ long spotsize,threshold,*errc,*errv;
+ const byte *pixel2stc;
+
+ if(buf[0] >= 0) { /* run forward */
+ buf[0] = -1;
+ bstep = 1;
+ pstep = sdev->color_info.num_components;
+ pstart = 0;
+ pstop = npixel * pstep;
+
+ } else { /* run backward */
+ buf[0] = 1;
+ bstep = -1;
+ pstep = -sdev->color_info.num_components;
+ pstop = pstep;
+ pstart = (1-npixel) * pstep;
+ out += npixel-1;
+ } /* forward / backward */
+
+/* --------------------------------------------------------------------- */
+ if(in == NULL) return 0; /* almost ignore the 'white calls' */
+/* --------------------------------------------------------------------- */
+
+ spotsize = buf[1];
+ threshold = buf[2];
+ errc = buf+3;
+ errv = errc + 2*sdev->color_info.num_components;
+ pixel2stc = pixelconversion[sdev->color_info.num_components];
+
+ for(p = pstart; p != pstop; p += pstep) { /* loop over pixels */
+ int c; /* component-number */
+ int pixel; /* internal pxel-value */
+
+ pixel = 0;
+
+ for(c = 0; c < sdev->color_info.num_components; c++) { /* comp */
+ long cv; /* component value */
+
+ cv = in[p+c] + errv[p+c] + errc[c] - ((errc[c]+4)>>3);
+ if(cv > threshold) {
+ pixel |= 1<<c;
+ cv -= spotsize;
+ }
+ errv[p+c-pstep] += ((3*cv+8)>>4); /* 3/16 */
+ errv[p+c ] = ((5*cv )>>4) /* 5/16 */
+ + ((errc[c]+4)>>3); /* 1/16 (rest) */
+ errc[c] = cv /* 8/16 (neu) */
+ - ((5*cv )>>4)
+ - ((3*cv+8)>>4);
+ } /* comp */
+
+ *out = pixel2stc[pixel];
+ out += bstep;
+ } /* loop over pixels */
+
+/* ============================================================= */
+ } else { /* npixel <= 0 -> initialisation */
+/* ============================================================= */
+
+ int i,i2do;
+ long rand_max;
+ double offset,scale;
+
+/*
+ * check wether the number of components is valid
+ */
+ if((sdev->color_info.num_components < 0) ||
+ (sdev->color_info.num_components >= countof(pixelconversion)) ||
+ (pixelconversion[sdev->color_info.num_components] == NULL)) return -1;
+
+/*
+ * check wether stcdither & TYPE are correct
+ */
+ if(( sdev->stc.dither == NULL) ||
+ ((sdev->stc.dither->flags & STC_TYPE) != STC_LONG)) return -2;
+
+/*
+ * check wether the buffer-size is sufficiently large
+ */
+ if(((sdev->stc.dither->flags/STC_SCAN) < 1) ||
+ ( sdev->stc.dither->bufadd <
+ (3 + 3*sdev->color_info.num_components))) return -3;
+/*
+ * must neither have STC_DIRECT nor STC_WHITE
+ */
+ if(sdev->stc.dither->flags & (STC_DIRECT | STC_WHITE)) return -4;
+
+/*
+ * compute initial values
+ */
+/* -- direction */
+ buf[0] = 1;
+
+/* -- "spotsize" */
+ scale = sdev->stc.dither->minmax[1];
+ buf[1] = (long)(scale + (scale > 0.0 ? 0.5 : -0.5));
+
+/* -- "threshold" */
+ offset = sdev->stc.dither->minmax[0];
+ scale -= offset;
+ if((offset+0.5*scale) > 0.0) buf[2] = (long)(offset + 0.5*scale + 0.5);
+ else buf[2] = (long)(offset + 0.5*scale - 0.5);
+
+/*
+ * random values, that do not exceed half of normal value
+ */
+ i2do = sdev->color_info.num_components * (3-npixel);
+ rand_max = 0;
+
+ if(sdev->stc.flags & STCDFLAG0) {
+
+ for(i = 0; i < i2do; ++i) buf[i+3] = 0;
+
+ } else {
+
+ for(i = 0; i < i2do; ++i) {
+ buf[i+3] = rand();
+ if(buf[i+3] > rand_max) rand_max = buf[i+3];
+ }
+
+ scale = (double) buf[1] / (double) rand_max;
+
+ for(i = 0; i < sdev->color_info.num_components; ++ i)
+ buf[i+3] = (long)(0.25000*scale*(buf[i+3]-rand_max/2));
+
+ for( ; i < i2do; ++i) /* includes 2 additional pixels ! */
+ buf[i+3] = (long)(0.28125*scale*(buf[i+3]-rand_max/2));
+
+ }
+
+/* ============================================================= */
+ } /* scanline-processing or initialisation */
+/* ============================================================= */
+
+ return 0;
+}
+
+/*
+ * Experimental CMYK-Algorithm
+ */
+
+int
+stc_fscmyk(stcolor_device *sdev,int npixel,byte *bin,byte *bbuf,byte *out)
+{
+ long *in = (long *) bin;
+ long *buf = (long *) bbuf;
+
+/* ============================================================= */
+ if(npixel > 0) { /* npixel > 0 -> scanline-processing */
+/* ============================================================= */
+
+ int bstep,pstart,pstop,pstep,p;
+ long spotsize,threshold,*errc,*errv;
+
+ if(buf[0] >= 0) { /* run forward */
+ buf[0] = -1;
+ bstep = 1;
+ pstep = 4;
+ pstart = 0;
+ pstop = npixel * pstep;
+
+ } else { /* run backward */
+ buf[0] = 1;
+ bstep = -1;
+ pstep = -4;
+ pstop = pstep;
+ pstart = (1-npixel) * pstep;
+ out += npixel-1;
+ } /* forward / backward */
+
+ spotsize = buf[1];
+ threshold = buf[2];
+ errc = buf+3;
+ errv = errc + 2*4;
+
+ for(p = 0; p < 4; ++p) errc[p] = 0;
+
+ for(p = pstart; p != pstop; p += pstep) { /* loop over pixels */
+ int c; /* component-number */
+ int pixel; /* internal pxel-value */
+ long cv,k;
+
+/*
+ * Black is treated first, with conventional Floyd-Steinberg
+ */
+ k = in[p+3];
+ cv = k + errv[p+3] + errc[3] - ((errc[3]+4)>>3);
+
+ if(cv > threshold) {
+ pixel = BLACK;
+ cv -= spotsize;
+ } else {
+ pixel = 0;
+ }
+
+ errv[p+3-pstep] += ((3*cv+8)>>4); /* 3/16 */
+ errv[p+3 ] = ((5*cv )>>4) /* 5/16 */
+ + ((errc[3]+4)>>3); /* 1/16 (rest) */
+ errc[3] = cv /* 8/16 (neu) */
+ - ((5*cv )>>4)
+ - ((3*cv+8)>>4);
+
+/*
+ * color-handling changes with black fired or not
+ */
+ if(pixel) {
+
+/* -------- firing of black causes all colors to fire too */
+
+ for(c = 0; c < 3; ++c) {
+ cv = in[p+c] > k ? in[p+c] : k;
+ cv += errv[p+c] + errc[c] - ((errc[c]+4)>>3)-spotsize;
+ if(cv <= (threshold-spotsize)) cv = threshold-spotsize+1;
+
+ errv[p+c-pstep] += ((3*cv+8)>>4); /* 3/16 */
+ errv[p+c ] = ((5*cv )>>4) /* 5/16 */
+ + ((errc[c]+4)>>3); /* 1/16 (rest) */
+ errc[c] = cv /* 8/16 (neu) */
+ - ((5*cv )>>4)
+ - ((3*cv+8)>>4);
+ }
+
+ } else {
+
+/* -------- if black did not fire, only colors w. larger values may fire */
+
+ for(c = 0; c < 3; ++c) {
+
+ cv = in[p+c];
+
+ if(cv > k) { /* May Fire */
+ cv += errv[p+c] + errc[c] - ((errc[c]+4)>>3);
+ if(cv > threshold) {
+ cv -= spotsize;
+ pixel |= CYAN>>c;
+ }
+ } else { /* Must not fire */
+ cv = k + errv[p+c] + errc[c] - ((errc[c]+4)>>3);
+ if(cv > threshold ) cv = threshold;
+ }
+
+ errv[p+c-pstep] += ((3*cv+8)>>4); /* 3/16 */
+ errv[p+c ] = ((5*cv )>>4) /* 5/16 */
+ + ((errc[c]+4)>>3); /* 1/16 (rest) */
+ errc[c] = cv /* 8/16 (neu) */
+ - ((5*cv )>>4)
+ - ((3*cv+8)>>4);
+ }
+ }
+
+ *out = pixel;
+ out += bstep;
+ } /* loop over pixels */
+
+/* ============================================================= */
+ } else { /* npixel <= 0 -> initialisation */
+/* ============================================================= */
+
+ int i,i2do;
+ long rand_max;
+ double offset,scale;
+
+/*
+ * check wether the number of components is valid
+ */
+ if(sdev->color_info.num_components != 4) return -1;
+
+/*
+ * check wether stcdither & TYPE are correct
+ */
+ if(( sdev->stc.dither == NULL) ||
+ ((sdev->stc.dither->flags & STC_TYPE) != STC_LONG)) return -2;
+
+/*
+ * check wether the buffer-size is sufficiently large
+ */
+ if(((sdev->stc.dither->flags/STC_SCAN) < 1) ||
+ ( sdev->stc.dither->bufadd <
+ (3 + 3*sdev->color_info.num_components))) return -3;
+/*
+ * must neither have STC_DIRECT nor STC_WHITE
+ */
+ if(sdev->stc.dither->flags & (STC_DIRECT | STC_WHITE)) return -4;
+
+/*
+ * compute initial values
+ */
+/* -- direction */
+ buf[0] = 1;
+
+/* -- "spotsize" */
+ scale = sdev->stc.dither->minmax[1];
+ buf[1] = (long)(scale + (scale > 0.0 ? 0.5 : -0.5));
+
+/* -- "threshold" */
+ offset = sdev->stc.dither->minmax[0];
+ scale -= offset;
+ if(sdev->stc.flags & STCDFLAG1) {
+ buf[2] = (long)((sdev->stc.extv[0][sdev->stc.sizv[0]-1] -
+ sdev->stc.extv[0][0]) * scale / 2.0 + offset);
+ } else {
+ if((offset+0.5*scale) > 0.0) buf[2] = (long)(offset + 0.5*scale + 0.5);
+ else buf[2] = (long)(offset + 0.5*scale - 0.5);
+ }
+
+/*
+ * random values, that do not exceed half of normal value
+ */
+ i2do = sdev->color_info.num_components * (3-npixel);
+ rand_max = 0;
+
+ if(sdev->stc.flags & STCDFLAG0) {
+
+ for(i = 0; i < i2do; ++i) buf[i+3] = 0;
+
+ } else {
+
+ for(i = 0; i < i2do; ++i) {
+ buf[i+3] = rand();
+ if(buf[i+3] > rand_max) rand_max = buf[i+3];
+ }
+
+ scale = (double) buf[1] / (double) rand_max;
+
+ for(i = 0; i < sdev->color_info.num_components; ++ i)
+ buf[i+3] = (long)(0.25000*scale*(buf[i+3]-rand_max/2));
+
+ for( ; i < i2do; ++i) /* includes 2 additional pixels ! */
+ buf[i+3] = (long)(0.28125*scale*(buf[i+3]-rand_max/2));
+
+ }
+
+/* ============================================================= */
+ } /* scanline-processing or initialisation */
+/* ============================================================= */
+
+ return 0;
+}
diff --git a/devices/gdevstc3.c b/devices/gdevstc3.c
new file mode 100644
index 000000000..1d565d1e0
--- /dev/null
+++ b/devices/gdevstc3.c
@@ -0,0 +1,104 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Epson Stylus-Color Printer-Driver */
+
+/***
+ This file holds the sample-implementation of a RGB-algorithm for
+ the stcolor-driver. It is available via
+
+ gs -sDEVICE=stcolor -sDithering=gsrgb ...
+
+ Actually this is no dithering-algorithm, it lets ghostscript do the job.
+ This achieved, by requesting BYTE-Values between 0 and 1 to be delivered,
+ which causes a depth of 1-Bit by default.
+
+ ***/
+
+/*
+ * gdevstc.h holds all the includes and the driver-specific definitions, so
+ * it is the only include you need. To add a new algorthim, STC_MODI in
+ * gdevstc.h should be extended. (see the instructions there)
+ */
+
+#include "gdevstc.h"
+
+/*
+ * the routine required.
+ */
+
+/*ARGSUSED*/
+int
+stc_gsrgb(stcolor_device *sdev,int npixel,byte *ip,byte *buf,byte *out)
+{
+
+ int error = 0;
+
+/*
+ * There are basically 3 Types of calls:
+ * npixel < 0 => initialize buf, if this is required
+ * (happens only if requested)
+ * npixel > 0 => process next scanline, if the flag STC_WHITE is set, then
+ * in == NULL signals, that the basic-driver has decided
+ * that this scanline is white. (Useful for really dithering
+ * drivers)
+ */
+
+/* ============================================================= */
+ if(npixel > 0) { /* npixel > 0 -> scanline-processing */
+/* ============================================================= */
+
+ int p;
+
+/*
+ * simply merge the color-values into a single byte
+ * (RED, GREEN, BLUE are defined in gdevstc.h)
+ */
+ for(p = 0; p < npixel; ++p,++out) { /* loop over pixels */
+
+ *out = 0;
+ if(*ip++) *out |= RED;
+ if(*ip++) *out |= GREEN;
+ if(*ip++) *out |= BLUE;
+
+ } /* loop over pixels */
+
+/* ============================================================= */
+ } else { /* npixel <= 0 -> initialisation */
+/* ============================================================= */
+/*
+ * besides buffer-Initialisation, one may check the parameters in
+ * the algorithm-table of the driver.
+ */
+
+/* we didn't check for the white-calls above, so they would cause errors */
+ if(sdev->stc.dither->flags & STC_WHITE) error = -1;
+
+/* if we're not setup for bytes, this is an error too */
+ if((sdev->stc.dither->flags & STC_TYPE) != STC_BYTE) error = -2;
+
+/* and rgb-mode is the only supported mode */
+ if(sdev->color_info.num_components != 3) error = -3;
+
+/* we can't deal with ghostscript-data directly. */
+ if(sdev->stc.dither->flags & STC_DIRECT) error = -4;
+
+/* ============================================================= */
+ } /* scanline-processing or initialisation */
+/* ============================================================= */
+
+ return error;
+}
diff --git a/devices/gdevstc4.c b/devices/gdevstc4.c
new file mode 100644
index 000000000..032065567
--- /dev/null
+++ b/devices/gdevstc4.c
@@ -0,0 +1,294 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Epson Stylus-Color Printer-Driver */
+
+/***
+ This file holds a byte-Implementation of the Floyd-Steinberg error
+ diffusion-algorithm. This algorithm is an alternative for high quality
+ printing in conjunction with the PostScript-Header stcolor.ps:
+
+ gs -sDEVICE=stcolor -sDithering=fs2 <other options> stcolor.ps ...
+
+ THIS ALGORIHM WAS WRITTEN BY STEVEN SINGER (S.Singer@ph.surrey.ac.uk)
+ AS PART OF escp2cfs2.
+ THIS IMPLEMENTATION INCORPORATES ONLY FEW CHANGES TO THE ORIGINAL CODE.
+
+ ***/
+
+#include "gdevstc.h"
+
+/*
+ * escp2c_pick best scans for best matching color
+ */
+static byte *
+escp2c_pick_best(byte *col)
+{
+ static byte colour[8][3] = {
+ { 0, 0, 0},{255, 0, 0},{ 0,255, 0},{255,255, 0},
+ { 0, 0,255},{255, 0,255},{ 0,255,255},{255,255,255}};
+ register int x, y, z, dx, dy, dz, dz2, dx2, dx3, dx4;
+ register byte *p;
+ register long md, d;
+
+ md = 16777216; /* plenty */
+
+/*
+ Note that I don't use a simple distance algorithm. That can lead to a block
+ of (130,127,127) being dithered as red-cyan. This algorithm should make
+ it use black-white-red. This is very important, as a coloured block in
+ the middle of a grey block can, via error diffusion, perturb the
+ surrounding colours sufficiently for this to happen.
+*/
+
+/*
+ The next bit is equivalent to this, but faster.
+
+ x = col[0];
+ y = col[1];
+ z = col[2];
+ for(n=8; n--; )
+ {
+ dx = x - colour[n][0];
+ dy = y - colour[n][1];
+ dz = z - colour[n][2];
+ d = dx*(dx-(dy>>1)) + dy*(dy-(dz>>1)) + dz*(dz-(dx>>1));
+ if (d < md)
+ {
+ md = d;
+ p = n;
+ }
+ }
+*/
+
+/*
+ * Test colours in gray code order to reduce number of recalculations.
+ * I bet you can't find an optimiser that would do this automatically.
+ */
+
+ x = col[0];
+ y = col[1];
+ z = col[2];
+ dx = x*(x-(y>>1));
+ dy = y*(y-(z>>1));
+ dz = z*(z-(x>>1));
+ md = dx + dy + dz;
+ p = colour[0];
+ x -= 255;
+ dx2 = x*(x-(y>>1));
+ dz2 = z*(z-(x>>1));
+ if ((d = dx2 + dy + dz2) < md) {md = d; p = colour[1];}
+ y -= 255;
+ dx3 = x*(x-(y>>1));
+ dy = y*(y-(z>>1));
+ if ((d = dx3 + dy + dz2) < md) {md = d; p = colour[3];}
+ x += 255;
+ dx4 = x*(x-(y>>1));
+ if ((d = dx4 + dy + dz) < md) {md = d; p = colour[2];}
+ z -= 255;
+ dy = y*(y-(z>>1));
+ dz = z*(z-(x>>1));
+ if ((d = dx4 + dy + dz) < md) {md = d; p = colour[6];}
+ x -= 255;
+ dz2 = z*(z-(x>>1));
+ if ((d = dx3 + dy + dz2) < md) {md = d; p = colour[7];}
+ y += 255;
+ dy = y*(y-(z>>1));
+ if ((d = dx2 + dy + dz2) < md) {md = d; p = colour[5];}
+ if ((d = dx + dy + dz) < md) {p = colour[4];}
+ return(p);
+}
+
+/*
+ * escp2c_conv_stc converts into the ouput format used by stcolor
+ */
+static void
+escp2c_conv_stc(byte *p, byte *q, int i)
+{
+ for(; i; p+=3, i-=3)
+ *q++ = (*p & RED) | (p[1] & GREEN) | (p[2] & BLUE);
+}
+
+/*
+ * Limit byte-values
+ */
+#define LIMIT(a) if (a > 255) a = 255; if (a < 0) a = 0
+#define LIMIT2(a) if (a > 127) a = 127; if (a < -128) a = -128; \
+ if (a < 0) a += 256
+/*
+ * Main routine of the algorithm
+ */
+int
+stc_fs2(stcolor_device *sd,int npixel,byte *in,byte *buf,byte *out)
+{
+ int fullcolor_line_size = npixel*3;
+
+/* ============================================================= */
+ if(npixel > 0) { /* npixel > 0 -> scanline-processing */
+/* ============================================================= */
+
+/* -------------------------------------------------------------------- */
+ if(in == NULL) { /* clear the error-buffer upon white-lines */
+/* -------------------------------------------------------------------- */
+
+ memset(buf,0,fullcolor_line_size);
+
+/* ------------------------------------------------------------------- */
+ } else { /* do the actual dithering */
+/* ------------------------------------------------------------------- */
+ int i, j, k, e, l, i2, below[3][3], *fb, *b, *bb, *tb;
+ byte *p, *q, *cp;
+
+ p = buf;
+ if (*p != 0 || memcmp((char *) p, (char *) p + 1, fullcolor_line_size - 1))
+ {
+ for(p = in, q=buf, i=fullcolor_line_size;
+ i--; p++, q++ )
+ {
+ j = *p + ((*q & 128) ? *q - 256 : *q);
+ LIMIT(j);
+ *p = j;
+ }
+ }
+
+ p = in;
+
+ fb = below[2];
+ b = below[1];
+ bb = below[0];
+ *b = b[1] = b[2] = *bb = bb[1] = bb[2] = 0;
+
+ if (sd->stc.dir)
+ {
+ for(p = in, q=buf-3,
+ i=fullcolor_line_size; i; i-=3)
+ {
+ cp = escp2c_pick_best(p);
+ for(i2=3; i2--; p++, q++, fb++, b++, bb++)
+ {
+ j = *p;
+ *p = *cp++;
+ j -= *p;
+ if (j != 0)
+ {
+ l = (e = (j>>1)) - (*fb = (j>>4));
+ if (i > 2)
+ {
+ k = p[3] + l;
+ LIMIT(k);
+ p[3] = k;
+ }
+ *b += e - (l = (j>>2) - *fb);
+ if (i < fullcolor_line_size)
+ {
+ l += *bb;
+ LIMIT2(l);
+ *q = l;
+ }
+ }
+ else
+ *fb = 0;
+ }
+ tb = bb-3;
+ bb = b-3;
+ b = fb-3;
+ fb = tb;
+ }
+ *q = *bb;
+ q[1] = bb[1];
+ q[2] = bb[2];
+ sd->stc.dir = 0;
+ }
+ else
+ {
+ for(p = in+fullcolor_line_size-1,
+ q = buf+fullcolor_line_size+2, i=fullcolor_line_size;
+ i; i-=3)
+ {
+ cp = escp2c_pick_best(p-2) + 2;
+ for(i2=3; i2--; p--, q--, fb++, b++, bb++)
+ {
+ j = *p;
+ *p = *cp--;
+ j -= *p;
+ if (j != 0)
+ {
+ l = (e = (j>>1)) - (*fb = (j>>4));
+ if (i > 2)
+ {
+ k = p[-3] + l;
+ LIMIT(k);
+ p[-3] = k;
+ }
+ *b += e - (l = (j>>2) - *fb);
+ if (i < fullcolor_line_size)
+ {
+ l += *bb;
+ LIMIT2(l);
+ *q = l;
+ }
+ }
+ else
+ *fb = 0;
+ }
+ tb = bb-3;
+ bb = b-3;
+ b = fb-3;
+ fb = tb;
+ }
+ *q = *bb;
+ q[1] = bb[1];
+ q[2] = bb[2];
+ sd->stc.dir = 1;
+ }
+
+ escp2c_conv_stc(in, out, fullcolor_line_size);
+
+/* ------------------------------------------------------------------- */
+ } /* buffer-reset | dithering */
+/* ------------------------------------------------------------------- */
+
+/* ============================================================= */
+ } else { /* npixel <= 0 -> initialisation */
+/* ============================================================= */
+
+/*
+ * check wether the number of components is valid
+ */
+ if(sd->color_info.num_components != 3) return -1;
+
+/*
+ * check wether stcdither & TYPE are correct
+ */
+ if(( sd->stc.dither == NULL) ||
+ ((sd->stc.dither->flags & STC_TYPE) != STC_BYTE)) return -2;
+
+/*
+ * check wether the buffer-size is sufficiently large
+ */
+ if((sd->stc.dither->flags/STC_SCAN) < 1) return -3;
+
+/*
+ * finally clear the buffer
+ */
+ memset(buf,0,-fullcolor_line_size);
+
+/* ============================================================= */
+ } /* scanline-processing or initialisation */
+/* ============================================================= */
+
+ return 0;
+}
diff --git a/devices/gdevsun.c b/devices/gdevsun.c
new file mode 100644
index 000000000..1ee520164
--- /dev/null
+++ b/devices/gdevsun.c
@@ -0,0 +1,685 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* SunView driver */
+#include "gx.h" /* for gx_bitmap; includes std.h */
+
+#include <suntool/sunview.h>
+#include <suntool/canvas.h>
+#include <sunwindow/cms_mono.h>
+#include <stdio.h>
+
+#include "gscdefs.h"
+#include "gsmatrix.h" /* needed for gxdevice.h */
+#include "gxdevice.h"
+#include "malloc_.h"
+
+#ifndef DEFAULT_DPI
+# define DEFAULT_DPI 75 /* Sun standard monitor */
+#endif
+
+#ifdef A4
+# define PAPER_X 8.27 /* A4 paper */
+# define PAPER_Y 11.69
+#endif
+
+#ifndef PAPER_X
+# define PAPER_X 8.5 /* US letter paper */
+# define PAPER_Y 11
+#endif
+/* Procedures */
+dev_proc_open_device(sun_open);
+dev_proc_sync_output(sun_sync);
+dev_proc_close_device(sun_close);
+dev_proc_map_rgb_color(sun_map_rgb_color);
+dev_proc_map_color_rgb(sun_map_color_rgb);
+dev_proc_fill_rectangle(sun_fill_rectangle);
+dev_proc_copy_mono(sun_copy_mono);
+dev_proc_copy_color(sun_copy_color);
+dev_proc_draw_line(sun_draw_line);
+
+/* The device descriptor */
+static gx_device_procs sun_procs = {
+ sun_open,
+ NULL, /* get_initial_matrix */
+ sun_sync,
+ NULL, /* output_page */
+ sun_close,
+ sun_map_rgb_color,
+ sun_map_color_rgb,
+ sun_fill_rectangle,
+ NULL, /* tile_rectangle */
+ sun_copy_mono,
+ sun_copy_color,
+ sun_draw_line
+};
+
+#define CMSNAME "GHOSTVIEW" /* SunView colormap name */
+
+/* Define the SunView device */
+typedef struct gx_device_sun {
+ gx_device_common;
+ Frame frame;
+ Canvas canvas;
+ Pixwin *pw;
+ struct mpr_data mpr;
+ Pixrect pr;
+ int truecolor; /* use truecolor mapping */
+ int freecols; /* unallocated colors */
+ byte *red, *green, *blue; /* colormap */
+ char cmsname[sizeof(CMSNAME)+9];/* color map name */
+#if !arch_is_big_endian /* need to swap bits & bytes */
+# define BUF_WIDTH_BYTES (((int)(8.5*DEFAULT_DPI)+15)/16*2)
+ byte swap_buf[BUF_WIDTH_BYTES];
+#endif
+} gx_device_sun;
+
+#if !arch_is_big_endian
+/* Define a table for reversing bit order. */
+static byte reverse_bits[256] = {
+ 0, 128, 64, 192, 32, 160, 96, 224, 16, 144, 80, 208, 48, 176, 112, 240,
+ 8, 136, 72, 200, 40, 168, 104, 232, 24, 152, 88, 216, 56, 184, 120, 248,
+ 4, 132, 68, 196, 36, 164, 100, 228, 20, 148, 84, 212, 52, 180, 116, 244,
+ 12, 140, 76, 204, 44, 172, 108, 236, 28, 156, 92, 220, 60, 188, 124, 252,
+ 2, 130, 66, 194, 34, 162, 98, 226, 18, 146, 82, 210, 50, 178, 114, 242,
+ 10, 138, 74, 202, 42, 170, 106, 234, 26, 154, 90, 218, 58, 186, 122, 250,
+ 6, 134, 70, 198, 38, 166, 102, 230, 22, 150, 86, 214, 54, 182, 118, 246,
+ 14, 142, 78, 206, 46, 174, 110, 238, 30, 158, 94, 222, 62, 190, 126, 254,
+ 1, 129, 65, 193, 33, 161, 97, 225, 17, 145, 81, 209, 49, 177, 113, 241,
+ 9, 137, 73, 201, 41, 169, 105, 233, 25, 153, 89, 217, 57, 185, 121, 249,
+ 5, 133, 69, 197, 37, 165, 101, 229, 21, 149, 85, 213, 53, 181, 117, 245,
+ 13, 141, 77, 205, 45, 173, 109, 237, 29, 157, 93, 221, 61, 189, 125, 253,
+ 3, 131, 67, 195, 35, 163, 99, 227, 19, 147, 83, 211, 51, 179, 115, 243,
+ 11, 139, 75, 203, 43, 171, 107, 235, 27, 155, 91, 219, 59, 187, 123, 251,
+ 7, 135, 71, 199, 39, 167, 103, 231, 23, 151, 87, 215, 55, 183, 119, 247,
+ 15, 143, 79, 207, 47, 175, 111, 239, 31, 159, 95, 223, 63, 191, 127, 255
+};
+#endif
+
+/* The instance is public. */
+gx_device_sun far_data gs_sunview_device = {
+ std_device_std_body(gx_device_sun, &sun_procs, "sunview",
+ (int)(PAPER_X*DEFAULT_DPI), (int)(PAPER_Y*DEFAULT_DPI), /* x and y extent */
+ DEFAULT_DPI, DEFAULT_DPI /* x and y density */
+ ), /* fill in color_info later from display depth */
+ { 0 }, /* std_procs */
+ 0, /* connection not initialized */
+};
+
+/* Macro for casting gx_device argument */
+#define xdev ((gx_device_sun *)dev)
+
+/*
+ * The macros below define the colormap configuration used on 8-bit
+ * pseudo-color displays.
+ */
+/*
+ * The following macros define the number of bits used to represent rgb colors.
+ * The total must not exceed the display depth.
+ * Note that the RGB dimensions could have an uneven number of bits assigned
+ * to them, but that will cause dithering to not work very well, since
+ * gs assumes the dither ramp is the same for all 3 color dimensions.
+ *
+ * Setting RED_BITS to n will pre-allocate a color-cube of 2^(3n) entries.
+ * The remaining entries are allocated on demand for colors requested by
+ * sun_map_rgb_color(), until the color map is full. At that point gs will
+ * fall back onto dithering using the pre-allocated colors.
+ * As a special case, if RED_BITS = GREEN_BITS = BLUE_BITS = 0, only
+ * black and white are pre-allocated.
+ */
+#define RED_BITS 2 /* everything depends on this one */
+#define GREEN_BITS RED_BITS
+#define BLUE_BITS RED_BITS
+#define DEPTH 8 /* don't change this */
+#define RGB_BITS (RED_BITS + GREEN_BITS + BLUE_BITS)
+/*
+ * Smallest # bits per dimension
+ */
+#define MAX_BITS RED_BITS
+#if (GREEN_BITS > MAX_BITS)
+#undef MAX_BITS
+#define MAX_BITS GREEN_BITS
+#endif
+#if (BLUE_BITS > MAX_BITS)
+#undef MAX_BITS
+#define MAX_BITS BLUE_BITS
+#endif
+/*
+ * masks to pull out rgb components
+ */
+#define BLUE_MASK ((1 << BLUE_BITS) - 1)
+#define GREEN_MASK ((1 << (BLUE_BITS + GREEN_BITS)) - 1 - BLUE_MASK)
+#define RED_MASK ((1 << (BLUE_BITS + GREEN_BITS + RED_BITS)) - 1 \
+ - BLUE_MASK - GREEN_MASK)
+/*
+ * number of colors on rgb dimensions
+ */
+#define RED_COLS (1 << RED_BITS)
+#define GREEN_COLS (1 << GREEN_BITS)
+#define BLUE_COLS (1 << BLUE_BITS)
+#define RGB_COLS (RED_COLS * GREEN_COLS * BLUE_COLS)
+#define MAX_COLS (1 << MAX_BITS)
+/*
+ * maximum number of colors in map
+ */
+#define ALL_COLS (1 << DEPTH) /* 256 */
+#define CMS_SIZE ALL_COLS /* cut down to 64 or 128 for
+ more cooperative behaviour */
+
+#if (RGB_COLS > CMS_SIZE) /* one is reserved for the scrollbar */
+CMS_SIZE_too_small_for_color_cube
+#endif
+#if (RGB_BITS < 0) || (RGB_BITS > DEPTH)
+Display_does_not_support_this_many_colors
+#endif
+
+/*
+ * The macros below define the color mapping used on 24-bit true-color
+ * displays.
+ * FAKE_TRUE_COLOR is used for debugging only. It simulates a true-color
+ * type mapping on an 8-bit pseudo-color display.
+#define FAKE_TRUE_COLOR
+ */
+#ifdef FAKE_TRUE_COLOR
+# define TRUE_RED_BITS 3 /* everything depends on this one */
+# define TRUE_GREEN_BITS 2
+# define TRUE_BLUE_BITS (DEPTH - TRUE_RED_BITS - TRUE_GREEN_BITS)
+#else
+# define TRUE_RED_BITS 8 /* everything depends on this one */
+# define TRUE_GREEN_BITS TRUE_RED_BITS
+# define TRUE_BLUE_BITS TRUE_RED_BITS
+#endif ./* FAKE_TRUE_COLOR */
+#define TRUE_DEPTH (TRUE_RED_BITS + TRUE_GREEN_BITS + TRUE_BLUE_BITS)
+/*
+ * Masks to pull out rgb components. Note that the bit order is BGR from
+ * high to low order bits.
+ */
+#define TRUE_RED_MASK ((1 << TRUE_RED_BITS) - 1)
+#define TRUE_GREEN_MASK ((1 << (TRUE_RED_BITS + TRUE_GREEN_BITS)) - 1 \
+ - TRUE_RED_MASK)
+#define TRUE_BLUE_MASK ((1 << (TRUE_RED_BITS + TRUE_GREEN_BITS \
+ + TRUE_BLUE_BITS)) - 1 \
+ - TRUE_GREEN_MASK - TRUE_RED_MASK)
+/*
+ * number of colors on rgb dimensions
+ */
+#define TRUE_RED_COLS (1 << TRUE_RED_BITS)
+#define TRUE_GREEN_COLS (1 << TRUE_GREEN_BITS)
+#define TRUE_BLUE_COLS (1 << TRUE_BLUE_BITS)
+
+/* Initialize the device. */
+static Notify_value destroy_func();
+int
+sun_open(register gx_device *dev)
+{
+#ifdef gs_DEBUG
+if ( gs_debug['X'] )
+ { extern int _Xdebug;
+ _Xdebug = 1;
+ }
+#endif
+ if (xdev->frame == (Frame)0)
+ xdev->frame =
+ window_create(NULL, FRAME, FRAME_LABEL, gs_product,
+ WIN_WIDTH, min(xdev->width + 24, 900),
+ WIN_HEIGHT, min(xdev->height + 36, 900),
+ WIN_Y, 0,
+ WIN_X, 200,
+ 0);
+ if (xdev->frame == (Frame)0)
+ return -1;
+ xdev->canvas = window_create(xdev->frame, CANVAS,
+ CANVAS_AUTO_EXPAND, FALSE,
+ CANVAS_AUTO_SHRINK, FALSE,
+ CANVAS_WIDTH, xdev->width,
+ CANVAS_HEIGHT, xdev->height,
+#ifndef PRE_IBIS /* try to use 24-bit visual if OS supports it */
+ CANVAS_COLOR24, TRUE,
+#endif
+ CANVAS_RETAINED, FALSE,
+ 0);
+ xdev->pw = canvas_pixwin(xdev->canvas);
+
+ switch (xdev->pw->pw_pixrect->pr_depth) {
+ static gx_device_color_info mono_ci =
+ dci_black_and_white;
+ /*
+ * If the pre-allocated color cube leaves room for spare entries,
+ * tell gs we can render colors exactly. Otherwise admit our
+ * limitations.
+ */
+ static gx_device_color_info color_ci =
+#if (RGB_COLS < CMS_SIZE)
+ dci_color(DEPTH, 31, MAX_COLS);
+#else
+ dci_color(DEPTH, MAX_COLS - 1, MAX_COLS);
+#endif
+ static gx_device_color_info truecolor_ci =
+ dci_color(TRUE_DEPTH,31,4);
+ case 1:
+ /* mono display */
+ xdev->color_info = mono_ci;
+ break;
+#ifndef FAKE_TRUE_COLOR
+ case DEPTH:
+ /* pseudo-color display */
+ xdev->color_info = color_ci;
+ xdev->truecolor = 0;
+ break;
+#endif /* FAKE_TRUE_COLOR */
+ case TRUE_DEPTH:
+ case TRUE_DEPTH+8: /* I'm not sure whether the XBGR frame buffer
+ returns depth 24 or 32. */
+ /* pseudo-color display */
+ xdev->color_info = truecolor_ci;
+ xdev->truecolor = 1;
+ break;
+ default:
+ emprintf1(dev->memory,
+ "gs: Cannot handle display of depth %d.\n",
+ xdev->pw->pw_pixrect->pr_depth);
+ return -1;
+ }
+
+ if ( gx_device_has_color(xdev)
+#ifndef FAKE_TRUE_COLOR
+ && !xdev->truecolor
+#endif
+ )
+ {
+ int j;
+ int color;
+
+ /*
+ * Create the pre-allocated colorcube.
+ */
+ xdev->red = (byte *)malloc(CMS_SIZE);
+ xdev->green = (byte *)malloc(CMS_SIZE);
+ xdev->blue = (byte *)malloc(CMS_SIZE);
+ if (!xdev->red || !xdev->green || !xdev->blue) {
+ emprintf(dev->memory, "gs: no memory for colormap\n");
+ return -1;
+ }
+
+#ifdef FAKE_TRUE_COLOR
+ /*
+ * Fit the largest possible color cube into the colormap.
+ */
+ for ( j = 0; j < ALL_COLS; j++ ) {
+ xdev->blue[j] =
+ (double)((j & TRUE_BLUE_MASK)
+ >> (TRUE_GREEN_BITS + TRUE_RED_BITS))
+ / (TRUE_BLUE_COLS - 1)
+ * (ALL_COLS - 1);
+ xdev->green[j] =
+ (double)((j & TRUE_GREEN_MASK) >> TRUE_RED_BITS)
+ / (TRUE_GREEN_COLS - 1)
+ * (ALL_COLS - 1);
+ xdev->red[j] =
+ (double)((j & TRUE_RED_MASK))
+ / (TRUE_RED_COLS - 1)
+ * (ALL_COLS - 1);
+ }
+
+ xdev->freecols = 0;
+#else /* !FAKE_TRUE_COLOR */
+ /*
+ * Black and white are allocated in the last two slots,
+ * so as to be compatible with the monochrome colormap.
+ * This prevents most text etc. to go technicolor as focus
+ * changes into the window.
+ *
+ * The requirement that these two entries be at the end
+ * of the colormap makes it most convenient to allocate
+ * the remmaining entries from back to the front as well.
+ * Therefore xdev->freecols is the minimal allocated
+ * color index, and decreases as new ones are allocated.
+ */
+ j = CMS_SIZE - 2;
+ cms_monochromeload(xdev->red + j,
+ xdev->green + j,
+ xdev->blue + j);
+
+ /*
+ * The remaining slots down to CMS_SIZE - RGB_COLS are filled
+ * with evenly spaced points from the colorcube.
+ */
+ for ( color = 1; color < RGB_COLS - 1; color++ ) {
+ j--;
+ xdev->red[j] =
+ (double)((color & RED_MASK) >> (GREEN_BITS + BLUE_BITS))
+ / (RED_COLS - 1)
+ * (ALL_COLS - 1);
+ xdev->green[j] =
+ (double)((color & GREEN_MASK) >> BLUE_BITS)
+ / (GREEN_COLS - 1)
+ * (ALL_COLS - 1);
+ xdev->blue[j] =
+ (double)((color & BLUE_MASK))
+ / (BLUE_COLS - 1)
+ * (ALL_COLS - 1);
+ }
+
+ /*
+ * Set the low-water mark to the beginning of the colorcube.
+ */
+ xdev->freecols = j;
+
+ /*
+ * The unused entries are filled so that the last entry is
+ * always different from the 0th entry. This is a requirement
+ * for SunWindows.
+ */
+ for (j-- ; j >= 0 ; j--) {
+ xdev->red[j] = xdev->green[j] = xdev->blue[j] =
+ ~xdev->red[CMS_SIZE - 1];
+ }
+#endif /* FAKE_TRUE_COLOR */
+
+ /*
+ * Install the colormap.
+ */
+ gs_sprintf(xdev->cmsname, "%s-%d", CMSNAME, getpid());
+ pw_setcmsname(xdev->pw, xdev->cmsname);
+ pw_putcolormap(xdev->pw, 0, CMS_SIZE,
+ xdev->red, xdev->green, xdev->blue);
+ }
+ else {
+ xdev->freecols = 0;
+ xdev->red = (byte *)0;
+ xdev->green = (byte *)0;
+ xdev->blue = (byte *)0;
+ }
+
+ /*
+ * Reset to retained after colormap length is changed
+ */
+ window_set(xdev->canvas,
+ CANVAS_RETAINED, TRUE,
+ WIN_VERTICAL_SCROLLBAR, scrollbar_create(0),
+ WIN_HORIZONTAL_SCROLLBAR, scrollbar_create(0),
+ 0);
+ window_set(xdev->frame, WIN_SHOW, TRUE, 0);
+ /* Interpose a destroy function to keep the driver bookkeeping */
+ /* machinery from getting confused if the user closes the window. */
+ notify_interpose_destroy_func(xdev->frame, destroy_func);
+ (void) notify_do_dispatch();
+ (void) notify_dispatch();
+ return 0;
+}
+/* Prevent the user from closing the window. */
+static Notify_value
+destroy_func(Frame frame, Destroy_status status)
+{ if ( status == DESTROY_CHECKING )
+ { notify_veto_destroy(frame);
+ return (NOTIFY_DONE);
+ }
+ return (notify_next_destroy_func(frame, status));
+}
+
+/* Close the device. */
+int
+sun_close(gx_device *dev)
+{ window_destroy(xdev->frame);
+ xdev->frame = (Frame)0;
+ xdev->canvas = (Canvas)0;
+ xdev->pw = (Pixwin *)0;
+ xdev->freecols = 0;
+ if (xdev->red)
+ free(xdev->red);
+ if (xdev->green)
+ free(xdev->green);
+ if (xdev->blue)
+ free(xdev->blue);
+ return 0;
+}
+
+/* Synchronize the display with the commands already given */
+int
+sun_sync(register gx_device *dev)
+{ (void) notify_dispatch();
+ return 0;
+}
+
+/* Map RGB to color number -
+ Look for existing entry in colormap, or create a new one, or
+ give up if no free colormap entries (requesting dithering).
+ */
+gx_color_index
+sun_map_rgb_color(gx_device *dev, unsigned short red,
+ unsigned short green, unsigned short blue)
+{ if ( !xdev->frame || !gx_device_has_color(dev) )
+ /*
+ * Invert default color index to match mono display
+ * pixel values (black = 1, white = 0).
+ */
+ return !gx_default_map_rgb_color(dev, red, green, blue);
+ else if ( !xdev->truecolor ) {
+ byte red_val, green_val, blue_val;
+ int i;
+ static int warn = 1;
+
+ /*
+ * Determine the RGB values at display resolution we
+ * ideally would want this color to be mapped into.
+ */
+ red_val = (double)red/gx_max_color_value * (ALL_COLS - 1);
+ green_val = (double)green/gx_max_color_value * (ALL_COLS - 1);
+ blue_val = (double)blue/gx_max_color_value * (ALL_COLS - 1);
+
+ /*
+ * Look for an exact match among the colors already allocated.
+ * This includes the pre-allocated default color cube.
+ */
+ for (i = CMS_SIZE - 1; i >= xdev->freecols; i--) {
+ if (xdev->red[i] == red_val &&
+ xdev->green[i] == green_val &&
+ xdev->blue[i] == blue_val) {
+ return i;
+ }
+ }
+
+ /*
+ * If we run out of space in the color map, let gs know.
+ * It will call us again to request colors to do the
+ * dithering, and hopefully request only RGB values that
+ * match the colorcube entries. IF NOT, WE WILL LOOP
+ * FOREVER!
+ * NOTE: Leave the zero'th colormap entry alone lest the
+ * scrollbar be colored.
+ */
+ if (xdev->freecols <= 1) {
+ if (warn) {
+ emprintf(dev->memory,
+ "gs: last spare color map entry allocated\n");
+ warn = 0;
+ }
+ return gx_no_color_index;
+ }
+
+ /*
+ * Allocate new color in map.
+ */
+ xdev->red[i] = red_val;
+ xdev->green[i] = green_val;
+ xdev->blue[i] = blue_val;
+ pw_setcmsname(xdev->pw, xdev->cmsname);
+ pw_putcolormap(xdev->pw, i, 1,
+ &xdev->red[i], &xdev->green[i], &xdev->blue[i]);
+
+ xdev->freecols = i;
+ return i;
+ }
+ else { /* true color mapping --
+ color index encodes all 3 RGB values */
+ return ((blue >> (gx_color_value_bits - TRUE_BLUE_BITS))
+ << (TRUE_GREEN_BITS + TRUE_RED_BITS)) |
+ ((green >> (gx_color_value_bits - TRUE_GREEN_BITS))
+ << TRUE_RED_BITS) |
+ (red >> (gx_color_value_bits - TRUE_RED_BITS));
+ }
+}
+
+/* Map color number back to RGB values - see sun_map_rgb_color(), above */
+int
+sun_map_color_rgb(gx_device *dev, gx_color_index color,
+ unsigned short rgb[3])
+{ if ( !xdev->frame || !gx_device_has_color(dev) )
+ return gx_default_map_color_rgb(dev, !color, rgb);
+ else if ( !xdev->truecolor ) {
+ /*
+ * We just use the colormap to map back to rgb values.
+ */
+ if (color < xdev->freecols || color >= CMS_SIZE) {
+ emprintf1(dev->memory,
+ "gs: attempt to get RGB values for unallocated color index %d\n",
+ (int)color);
+ return -1;
+ }
+ rgb[0] = (double)xdev->red[color] / (ALL_COLS - 1)
+ * gx_max_color_value;
+ rgb[1] = (double)xdev->green[color] / (ALL_COLS - 1)
+ * gx_max_color_value;
+ rgb[2] = (double)xdev->blue[color] / (ALL_COLS - 1)
+ * gx_max_color_value;
+ return 0;
+ }
+ else { /* true color mapping */
+ rgb[0] = (double)((unsigned short)(color & TRUE_RED_MASK))
+ / (TRUE_RED_COLS - 1)
+ * gx_max_color_value;
+ rgb[1] = (double)((unsigned short)(color & TRUE_GREEN_MASK)
+ >> TRUE_RED_BITS)
+ / (TRUE_GREEN_COLS - 1)
+ * gx_max_color_value;
+ rgb[2] = (double)((unsigned short)(color & TRUE_BLUE_MASK)
+ >> (TRUE_GREEN_BITS + TRUE_RED_BITS))
+ / (TRUE_BLUE_COLS - 1)
+ * gx_max_color_value;
+ return 0;
+ }
+}
+
+/* Fill a rectangle with a color. */
+int
+sun_fill_rectangle(register gx_device *dev,
+ int x, int y, int w, int h, gx_color_index color)
+{ fit_fill(dev, x, y, w, h);
+
+ pw_write(xdev->pw, x, y, w, h, PIX_SRC | PIX_COLOR((int)(color)),
+ (Pixrect *)0, 0, 0);
+ (void) notify_dispatch();
+ return 0;
+}
+
+/* Copy a monochrome bitmap. */
+int
+sun_copy_mono(register gx_device *dev,
+ const byte *base, int sourcex, int raster, gx_bitmap_id id,
+ int x, int y, int w, int h, gx_color_index zero, gx_color_index one)
+{
+/* We define a non-const pointer to the data so we can invert it or */
+/* byte-swap it in place temporarily (we restore it at the end). */
+/* Yes, this is a bad and wicked thing to do! */
+#define non_const_base ((byte *)base)
+
+ register int i;
+ int nbytes;
+ extern struct pixrectops mem_ops;
+#if !arch_is_big_endian /* need to swap bits & bytes */
+# define BUF_WIDTH_BYTES (((int)(8.5*DEFAULT_DPI)+15)/16*2)
+ byte swap_buf[BUF_WIDTH_BYTES];
+#endif
+
+ fit_copy(dev, base, sourcex, raster, id, x, y, w, h);
+ nbytes = h * raster;
+
+ xdev->pr.pr_ops = &mem_ops;
+ xdev->pr.pr_width = w + sourcex + 8;
+ xdev->pr.pr_height = h;
+ xdev->pr.pr_depth = 1;
+ xdev->pr.pr_data = (caddr_t)&(xdev->mpr);
+ xdev->mpr.md_linebytes = raster;
+ xdev->mpr.md_image = (short *)((ulong)base & ~1);
+#if !arch_is_big_endian
+ /* Reverse the bit order in each byte. */
+ for ( i = 0; i < nbytes; i++ )
+ non_const_base[i] = reverse_bits[base[i]];
+#endif
+ pw_batch_on(xdev->pw);
+ if (one != gx_no_color_index)
+ { pw_stencil(xdev->pw, x, y, w, h,
+ PIX_SRC | PIX_COLOR(one), &(xdev->pr),
+ ((int)base & 1) ? sourcex + 8 : sourcex, 0,
+ (Pixrect *)0, 0, 0);
+ }
+ if (zero != gx_no_color_index)
+ { for (i = 0; i < nbytes; i++)
+ non_const_base[i] = ~base[i];
+ pw_stencil(xdev->pw, x, y, w, h,
+ PIX_SRC | PIX_COLOR(zero), &(xdev->pr),
+ ((int)base & 1) ? sourcex + 8 : sourcex, 0,
+ (Pixrect *)0, 0, 0);
+ for (i = 0; i < nbytes; i++)
+ non_const_base[i] = ~base[i];
+ }
+ pw_batch_off(xdev->pw);
+#if !arch_is_big_endian
+ /* Reverse the bits back again. */
+ for ( i = 0; i < nbytes; i++ )
+ non_const_base[i] = reverse_bits[base[i]];
+#endif
+ (void) notify_dispatch();
+ return 0;
+}
+
+/* Copy a color bitmap. */
+int
+sun_copy_color(register gx_device *dev,
+ const byte *base, int sourcex, int raster, gx_bitmap_id id,
+ int x, int y, int w, int h)
+{
+ extern struct pixrectops mem_ops;
+
+ if ( !gx_device_has_color(dev) )
+ return sun_copy_mono(dev, base, sourcex, raster, id,
+ x, y, w, h,
+ (gx_color_index)0, (gx_color_index)1);
+
+ fit_copy(dev, base, sourcex, raster, id, x, y, w, h);
+
+ xdev->pr.pr_ops = &mem_ops;
+ xdev->pr.pr_width = w + sourcex + 8;
+ xdev->pr.pr_height = h;
+ xdev->pr.pr_depth = 8;
+ xdev->pr.pr_data = (caddr_t)&(xdev->mpr);
+ xdev->mpr.md_linebytes = raster;
+ xdev->mpr.md_image = (short *)((ulong)base & ~1);
+ pw_write(xdev->pw, x, y, w, h,
+ PIX_SRC, &(xdev->pr),
+ (((int)base & 1) ? sourcex + 8 : sourcex), 0);
+ (void) notify_dispatch();
+ return 0;
+}
+
+/* Draw a line */
+int
+sun_draw_line(register gx_device *dev,
+ int x0, int y0, int x1, int y1, gx_color_index color)
+{ pw_vector(xdev->pw, x0, y0, x1, y1, PIX_SRC, color);
+ (void) notify_dispatch();
+ return 0;
+}
diff --git a/devices/gdevsunr.c b/devices/gdevsunr.c
new file mode 100644
index 000000000..8dd229ec3
--- /dev/null
+++ b/devices/gdevsunr.c
@@ -0,0 +1,100 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Sun raster file driver */
+#include "gdevprn.h"
+
+/*
+ * Currently, the only variety of this format supported in this file is
+ * Harlequin's 1-bit "SUN_RAS" with no colormap and odd "};\n" at tail.
+ */
+
+#define RAS_MAGIC 0x59a66a95
+#define RT_STANDARD 1 /* Raw pixrect image in 68000 byte order */
+#define RMT_NONE 0 /* ras_maplength is expected to be 0 */
+typedef struct sun_rasterfile_s {
+ int ras_magic; /* magic number */
+ int ras_width; /* width (pixels) of image */
+ int ras_height; /* height (pixels) of image */
+ int ras_depth; /* depth (1, 8, or 24 bits) of pixel */
+ int ras_length; /* length (bytes) of image */
+ int ras_type; /* type of file; see RT_* below */
+ int ras_maptype; /* type of colormap; see RMT_* below */
+ int ras_maplength; /* length (bytes) of following map */
+} sun_rasterfile_t;
+
+#ifndef X_DPI
+# define X_DPI 72
+#endif
+#ifndef Y_DPI
+# define Y_DPI 72
+#endif
+
+static dev_proc_print_page(sunhmono_print_page);
+
+const gx_device_printer gs_sunhmono_device =
+ prn_device(prn_bg_procs, "sunhmono", /* The print_page proc is compatible with allowing bg printing */
+ DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
+ X_DPI, Y_DPI,
+ 0, 0, 0, 0, /* margins */
+ 1, sunhmono_print_page);
+
+static int
+sunhmono_print_page(gx_device_printer * pdev, FILE * prn_stream)
+{
+ int gsLineBytes = gdev_mem_bytes_per_scan_line((gx_device *) pdev);
+ /* Output bytes have to be padded to 16 bits. */
+ int rasLineBytes = ROUND_UP(gsLineBytes, 2);
+ int lineCnt;
+ byte *lineStorage; /* Allocated for passing storage to gdev_prn_get_bits() */
+ byte *data;
+ sun_rasterfile_t ras;
+ int code = 0;
+
+ /*
+ errprintf(pdev->memory, "pdev->width:%d (%d/%d) gsLineBytes:%d rasLineBytes:%d\n",
+ pdev->width, pdev->width/8, pdev->width%8,gsLineBytes,rasLineBytes);
+ */
+ lineStorage = gs_malloc(pdev->memory, gsLineBytes, 1, "rasterfile_print_page(in)");
+ if (lineStorage == 0) {
+ code = gs_note_error(gs_error_VMerror);
+ goto out;
+ }
+ /* Setup values in header */
+ ras.ras_magic = RAS_MAGIC;
+ ras.ras_width = pdev->width;
+ ras.ras_height = pdev->height;
+ ras.ras_depth = 1;
+ ras.ras_length = (rasLineBytes * pdev->height);
+ ras.ras_type = RT_STANDARD;
+ ras.ras_maptype = RMT_NONE;
+ ras.ras_maplength = 0;
+ /* Write header */
+ fwrite(&ras, 1, sizeof(ras), prn_stream);
+ /* For each raster line */
+ for (lineCnt = 0; lineCnt < pdev->height; ++lineCnt) {
+ gdev_prn_get_bits(pdev, lineCnt, lineStorage, &data);
+ fwrite(data, 1, gsLineBytes, prn_stream);
+ if (gsLineBytes % 2)
+ fputc(0, prn_stream); /* pad to even # of bytes with a 0 */
+ }
+ /* The weird file terminator */
+ fwrite("};\n", 1, 3, prn_stream);
+out:
+ /* Clean up... */
+ gs_free(pdev->memory, lineStorage, gsLineBytes, 1, "rasterfile_print_page(in)");
+ return code;
+}
diff --git a/devices/gdevsvga.c b/devices/gdevsvga.c
new file mode 100644
index 000000000..94d9115b5
--- /dev/null
+++ b/devices/gdevsvga.c
@@ -0,0 +1,1033 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* SuperVGA display drivers */
+#include "memory_.h"
+#include "gx.h"
+#include "gserrors.h"
+#include "gxarith.h" /* for ...log2 */
+#include "gxdevice.h"
+#include "gdevpccm.h"
+#include "gdevpcfb.h"
+#include "gdevsvga.h"
+#include "gsparam.h"
+
+/* The color map for dynamically assignable colors. */
+#define first_dc_index 64
+static int next_dc_index;
+
+#define dc_hash_size 293 /* prime, >num_dc */
+typedef struct {
+ ushort rgb, index;
+} dc_entry;
+static dc_entry dynamic_colors[dc_hash_size + 1];
+
+#define num_colors 255
+
+/* Macro for casting gx_device argument */
+#define fb_dev ((gx_device_svga *)dev)
+
+/* Procedure records */
+#define svga_procs(open) {\
+ open, NULL /*get_initial_matrix*/,\
+ NULL /*sync_output*/, NULL /*output_page*/, svga_close,\
+ svga_map_rgb_color, svga_map_color_rgb,\
+ svga_fill_rectangle, NULL /*tile_rectangle*/,\
+ svga_copy_mono, svga_copy_color, NULL /*draw_line*/,\
+ svga_get_bits, NULL /*get_params*/, svga_put_params,\
+ NULL /*map_cmyk_color*/, NULL /*get_xfont_procs*/,\
+ NULL /*get_xfont_device*/, NULL /*map_rgb_alpha_color*/,\
+ gx_page_device_get_page_device, NULL /*get_alpha_bits*/,\
+ svga_copy_alpha\
+}
+
+/* Save the controller mode */
+static int svga_save_mode = -1;
+
+/* ------ Internal routines ------ */
+
+#define regen 0xa000
+
+/* Construct a pointer for writing a pixel. */
+/* Assume 64K pages, 64K granularity. */
+/* We know that y is within bounds. */
+#define set_pixel_ptr(ptr, fbdev, x, y, wnum)\
+{ ulong index = (ulong)(y) * fbdev->raster + (uint)(x);\
+ if ( (uint)(index >> 16) != fbdev->current_page )\
+ { (*fbdev->set_page)(fbdev, (fbdev->current_page = index >> 16), wnum);\
+ }\
+ ptr = (fb_ptr)MK_PTR(regen, (ushort)index);\
+}
+#define set_pixel_write_ptr(ptr, fbdev, x, y)\
+ set_pixel_ptr(ptr, fbdev, x, y, fbdev->wnum_write)
+#define set_pixel_read_ptr(ptr, fbdev, x, y)\
+ set_pixel_ptr(ptr, fbdev, x, y, fbdev->wnum_read)
+
+/* Find the graphics mode for a desired width and height. */
+/* Set the mode in the device structure and return 0, */
+/* or return an error code. */
+int
+svga_find_mode(gx_device * dev, const mode_info * mip)
+{
+ for (;; mip++) {
+ if (mip->width >= fb_dev->width &&
+ mip->height >= fb_dev->height ||
+ mip[1].mode < 0
+ ) {
+ fb_dev->mode = mip;
+ gx_device_adjust_resolution(dev, mip->width, mip->height, 1);
+ fb_dev->raster = fb_dev->width;
+ return 0;
+ }
+ }
+ return_error(gs_error_rangecheck);
+}
+
+/* Set the index for writing into the color DAC. */
+#define svga_dac_set_write_index(i) outportb(0x3c8, i)
+
+/* Write 6-bit R,G,B values into the color DAC. */
+#define svga_dac_write(r, g, b)\
+ (outportb(0x3c9, r), outportb(0x3c9, g), outportb(0x3c9, b))
+
+/* ------ Common procedures ------ */
+
+#define cv_bits(v,n) (v >> (gx_color_value_bits - n))
+
+/* Initialize the dynamic color table, if any. */
+void
+svga_init_colors(gx_device * dev)
+{
+ if (fb_dev->fixed_colors)
+ next_dc_index = num_colors;
+ else {
+ memset(dynamic_colors, 0,
+ (dc_hash_size + 1) * sizeof(dc_entry));
+ next_dc_index = first_dc_index;
+ }
+}
+
+/* Load the color DAC with the predefined colors. */
+static void
+svga_load_colors(gx_device * dev)
+{
+ int ci;
+
+ svga_dac_set_write_index(0);
+ if (fb_dev->fixed_colors)
+ for (ci = 0; ci < num_colors; ci++) {
+ gx_color_value rgb[3];
+
+ pc_8bit_map_color_rgb(dev, (gx_color_index) ci, rgb);
+ svga_dac_write(cv_bits(rgb[0], 6), cv_bits(rgb[1], 6),
+ cv_bits(rgb[2], 6));
+ } else
+ for (ci = 0; ci < 64; ci++) {
+ static const byte c2[10] =
+ {0, 42, 0, 0, 0, 0, 0, 0, 21, 63};
+
+ svga_dac_write(c2[(ci >> 2) & 9], c2[(ci >> 1) & 9],
+ c2[ci & 9]);
+ }
+}
+
+/* Initialize the device structure and the DACs. */
+int
+svga_open(gx_device * dev)
+{
+ fb_dev->x_pixels_per_inch =
+ fb_dev->y_pixels_per_inch =
+ fb_dev->height / PAGE_HEIGHT_INCHES;
+ /* Set the display mode. */
+ if (svga_save_mode < 0)
+ svga_save_mode = (*fb_dev->get_mode) ();
+ (*fb_dev->set_mode) (fb_dev->mode->mode);
+ svga_init_colors(dev);
+ svga_load_colors(dev);
+ fb_dev->current_page = -1;
+ return 0;
+}
+
+/* Close the device; reinitialize the display for text mode. */
+int
+svga_close(gx_device * dev)
+{
+ if (svga_save_mode >= 0)
+ (*fb_dev->set_mode) (svga_save_mode);
+ svga_save_mode = -1;
+ return 0;
+}
+
+/* Map a r-g-b color to a palette index. */
+/* The first 64 entries of the color map are set */
+/* for compatibility with the older display modes: */
+/* these are indexed as 0.0.R0.G0.B0.R1.G1.B1. */
+gx_color_index
+svga_map_rgb_color(gx_device * dev, const gx_color_value cv[])
+{
+ ushort rgb;
+ gx_color_value r = cv[0], g = cv[1], b = cv[2];
+
+ if (fb_dev->fixed_colors) {
+ gx_color_index ci = pc_8bit_map_rgb_color(dev, cv);
+
+ /* Here is where we should permute the index to match */
+ /* the old color map... but we don't yet. */
+ return ci;
+ } {
+ ushort r5 = cv_bits(r, 5), g5 = cv_bits(g, 5), b5 = cv_bits(b, 5);
+ static const byte cube_bits[32] =
+ {0, 128, 128, 128, 128, 128, 128, 128, 128, 128,
+ 8, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,
+ 1, 128, 128, 128, 128, 128, 128, 128, 128, 128,
+ 9
+ };
+ uint cx = ((uint) cube_bits[r5] << 2) +
+ ((uint) cube_bits[g5] << 1) +
+ (uint) cube_bits[b5];
+
+ /* Check for a color on the cube. */
+ if (cx < 64)
+ return (gx_color_index) cx;
+ /* Not on the cube, check the dynamic color table. */
+ rgb = (r5 << 10) + (g5 << 5) + b5;
+ }
+ {
+ register dc_entry *pdc;
+
+ for (pdc = &dynamic_colors[rgb % dc_hash_size];
+ pdc->rgb != 0; pdc++
+ )
+ if (pdc->rgb == rgb)
+ return (gx_color_index) (pdc->index);
+ if (pdc == &dynamic_colors[dc_hash_size]) { /* Wraparound */
+ for (pdc = &dynamic_colors[0]; pdc->rgb != 0; pdc++)
+ if (pdc->rgb == rgb)
+ return (gx_color_index) (pdc->index);
+ }
+ if (next_dc_index == num_colors) { /* No space left, report failure. */
+ return gx_no_color_index;
+ }
+ /* Not on the cube, and not in the dynamic table. */
+ /* Put in the dynamic table if space available. */
+ {
+ int i = next_dc_index++;
+
+ pdc->rgb = rgb;
+ pdc->index = i;
+ svga_dac_set_write_index(i);
+ svga_dac_write(cv_bits(r, 6), cv_bits(g, 6),
+ cv_bits(b, 6));
+ return (gx_color_index) i;
+ }
+ }
+}
+
+/* Map a color code to r-g-b. */
+/* This routine must invert the transformation of the one above. */
+/* Since this is practically never used, we just read the DAC. */
+int
+svga_map_color_rgb(gx_device * dev, gx_color_index color,
+ gx_color_value prgb[3])
+{
+ uint cval;
+
+ outportb(0x3c7, (byte) color);
+#define dacin() (cval = inportb(0x3c9) >> 1,\
+ ((cval << 11) + (cval << 6) + (cval << 1) + (cval >> 4)) >>\
+ (16 - gx_color_value_bits))
+ prgb[0] = dacin();
+ prgb[1] = dacin();
+ prgb[2] = dacin();
+#undef dacin
+ return 0;
+}
+
+/* Fill a rectangle. */
+int
+svga_fill_rectangle(gx_device * dev, int x, int y, int w, int h,
+ gx_color_index color)
+{
+ uint raster = fb_dev->raster;
+ ushort limit = (ushort) - raster;
+ int yi;
+ fb_ptr ptr;
+
+ fit_fill(dev, x, y, w, h);
+ set_pixel_write_ptr(ptr, fb_dev, x, y);
+ /* Most fills are very small and don't cross a page boundary. */
+ yi = h;
+ switch (w) {
+ case 0:
+ return 0; /* no-op */
+ case 1:
+ while (--yi >= 0 && PTR_OFF(ptr) < limit)
+ ptr[0] = (byte) color,
+ ptr += raster;
+ if (!++yi)
+ return 0;
+ break;
+ case 2:
+ while (--yi >= 0 && PTR_OFF(ptr) < limit)
+ ptr[0] = ptr[1] = (byte) color,
+ ptr += raster;
+ if (!++yi)
+ return 0;
+ break;
+ case 3:
+ while (--yi >= 0 && PTR_OFF(ptr) < limit)
+ ptr[0] = ptr[1] = ptr[2] = (byte) color,
+ ptr += raster;
+ if (!++yi)
+ return 0;
+ break;
+ case 4:
+ while (--yi >= 0 && PTR_OFF(ptr) < limit)
+ ptr[0] = ptr[1] = ptr[2] = ptr[3] = (byte) color,
+ ptr += raster;
+ if (!++yi)
+ return 0;
+ break;
+ default:
+ if (w < 0)
+ return 0;
+ /* Check for erasepage. */
+ if (w == dev->width && h == dev->height &&
+ color < first_dc_index
+ )
+ svga_init_colors(dev);
+ }
+ while (--yi >= 0) {
+ if (PTR_OFF(ptr) < limit) {
+ memset(ptr, (byte) color, w);
+ ptr += raster;
+ } else if (PTR_OFF(ptr) <= (ushort) (-w)) {
+ memset(ptr, (byte) color, w);
+ if (yi > 0)
+ set_pixel_write_ptr(ptr, fb_dev, x, y + h - yi);
+ } else {
+ uint left = (uint) 0x10000 - PTR_OFF(ptr);
+
+ memset(ptr, (byte) color, left);
+ set_pixel_write_ptr(ptr, fb_dev, x + left, y + h - 1 - yi);
+ memset(ptr, (byte) color, w - left);
+ ptr += raster - left;
+ }
+ }
+ return 0;
+}
+
+/* Copy a monochrome bitmap. The colors are given explicitly. */
+/* Color = gx_no_color_index means transparent (no effect on the image). */
+int
+svga_copy_mono(gx_device * dev,
+ const byte * base, int sourcex, int sraster, gx_bitmap_id id,
+ int x, int y, int w, int h, gx_color_index czero, gx_color_index cone)
+{
+ uint raster = fb_dev->raster;
+ ushort limit;
+ register int wi;
+ uint skip;
+ int yi;
+ register fb_ptr ptr = (fb_ptr) 0;
+ const byte *srow;
+ uint invert;
+
+ fit_copy(dev, base, sourcex, sraster, id, x, y, w, h);
+ limit = (ushort) - w;
+ skip = raster - w + 1;
+ srow = base + (sourcex >> 3);
+#define izero (int)czero
+#define ione (int)cone
+ if (ione == no_color) {
+ gx_color_index temp;
+
+ if (izero == no_color)
+ return 0; /* no-op */
+ temp = czero;
+ czero = cone;
+ cone = temp;
+ invert = ~0;
+ } else
+ invert = 0;
+ /* Pre-filling saves us a test in the loop, */
+ /* and since tiling is uncommon, we come out ahead. */
+ if (izero != no_color)
+ svga_fill_rectangle(dev, x, y, w, h, czero);
+ for (yi = 0; yi < h; yi++) {
+ const byte *sptr = srow;
+ uint bits;
+ int bitno = sourcex & 7;
+
+ wi = w;
+ if (PTR_OFF(ptr) <= skip) {
+ set_pixel_write_ptr(ptr, fb_dev, x, y + yi);
+ } else if (PTR_OFF(ptr) > limit) { /* We're crossing a page boundary. */
+ /* This is extremely rare, so it doesn't matter */
+ /* how slow it is. */
+ int xi = (ushort) - PTR_OFF(ptr);
+
+ svga_copy_mono(dev, srow, sourcex & 7, sraster,
+ gx_no_bitmap_id, x, y + yi, xi, 1,
+ gx_no_color_index, cone);
+ set_pixel_write_ptr(ptr, fb_dev, x + xi, y + yi);
+ sptr = srow - (sourcex >> 3) + ((sourcex + xi) >> 3);
+ bitno = (sourcex + xi) & 7;
+ wi -= xi;
+ }
+ bits = *sptr ^ invert;
+ switch (bitno) {
+#define ifbit(msk)\
+ if ( bits & msk ) *ptr = (byte)ione;\
+ if ( !--wi ) break; ptr++
+ case 0:
+ bit0:ifbit(0x80);
+ case 1:
+ ifbit(0x40);
+ case 2:
+ ifbit(0x20);
+ case 3:
+ ifbit(0x10);
+ case 4:
+ ifbit(0x08);
+ case 5:
+ ifbit(0x04);
+ case 6:
+ ifbit(0x02);
+ case 7:
+ ifbit(0x01);
+#undef ifbit
+ bits = *++sptr ^ invert;
+ goto bit0;
+ }
+ ptr += skip;
+ srow += sraster;
+ }
+#undef izero
+#undef ione
+ return 0;
+}
+
+/* Copy a color pixelmap. This is just like a bitmap, */
+/* except that each pixel takes 8 bits instead of 1. */
+int
+svga_copy_color(gx_device * dev,
+ const byte * base, int sourcex, int sraster, gx_bitmap_id id,
+ int x, int y, int w, int h)
+{
+ int xi, yi;
+ int skip;
+ const byte *sptr;
+ fb_ptr ptr;
+
+ fit_copy(dev, base, sourcex, sraster, id, x, y, w, h);
+ skip = sraster - w;
+ sptr = base + sourcex;
+ for (yi = y; yi - y < h; yi++) {
+ ptr = 0;
+ for (xi = x; xi - x < w; xi++) {
+ if (PTR_OFF(ptr) == 0)
+ set_pixel_write_ptr(ptr, fb_dev, xi, yi);
+ *ptr++ = *sptr++;
+ }
+ sptr += skip;
+ }
+ return 0;
+}
+
+/* Put parameters. */
+int
+svga_put_params(gx_device * dev, gs_param_list * plist)
+{
+ int ecode = 0;
+ int code;
+ const char *param_name;
+
+ if ((code = ecode) < 0 ||
+ (code = gx_default_put_params(dev, plist)) < 0
+ ) {
+ }
+ return code;
+}
+
+/* Read scan lines back from the frame buffer. */
+int
+svga_get_bits(gx_device * dev, int y, byte * data, byte ** actual_data)
+{
+ uint bytes_per_row = dev->width;
+ ushort limit = (ushort) - bytes_per_row;
+ fb_ptr src;
+
+ if (y < 0 || y >= dev->height)
+ return gs_error_rangecheck;
+ set_pixel_read_ptr(src, fb_dev, 0, y);
+ /* The logic here is similar to fill_rectangle. */
+ if (PTR_OFF(src) <= limit)
+ memcpy(data, src, bytes_per_row);
+ else {
+ uint left = (uint) 0x10000 - PTR_OFF(src);
+
+ memcpy(data, src, left);
+ set_pixel_read_ptr(src, fb_dev, left, y);
+ memcpy(data + left, src, bytes_per_row - left);
+ }
+ if (actual_data != 0)
+ *actual_data = data;
+ return 0;
+}
+
+/* Copy an alpha-map to the screen. */
+/* Depth is 1, 2, or 4. */
+static int
+svga_copy_alpha(gx_device * dev, const byte * base, int sourcex,
+ int sraster, gx_bitmap_id id, int x, int y, int w, int h,
+ gx_color_index color, int depth)
+{
+ int xi, yi;
+ int skip;
+ const byte *sptr;
+ byte mask;
+ int ishift;
+
+ /* We fake alpha by interpreting it as saturation, i.e., */
+ /* alpha = 0 is white, alpha = 1 is the full color. */
+ byte shades[16];
+ gx_color_value rgb[3];
+ int log2_depth = depth >> 1; /* works for 1,2,4 */
+ int n1 = (1 << depth) - 1;
+
+ fit_copy(dev, base, sourcex, sraster, id, x, y, w, h);
+ shades[0] = (byte) svga_map_rgb_color(dev, gx_max_color_value,
+ gx_max_color_value,
+ gx_max_color_value);
+ shades[n1] = (byte) color;
+ if (n1 > 1) {
+ memset(shades + 1, 255, n1 - 1);
+ svga_map_color_rgb(dev, color, rgb);
+ }
+ skip = sraster - ((w * depth) >> 3);
+ sptr = base + (sourcex >> (3 - log2_depth));
+ mask = n1;
+ ishift = (~sourcex & (7 >> log2_depth)) << log2_depth;
+ for (yi = y; yi - y < h; yi++) {
+ fb_ptr ptr = 0;
+ int shift = ishift;
+
+ for (xi = x; xi - x < w; xi++, ptr++) {
+ uint a = (*sptr >> shift) & mask;
+
+ if (PTR_OFF(ptr) == 0)
+ set_pixel_write_ptr(ptr, fb_dev, xi, yi);
+ map:if (a != 0) {
+ byte ci = shades[a];
+
+ if (ci == 255) { /* Map the color now. */
+#define make_shade(v, alpha, n1)\
+ (gx_max_color_value -\
+ ((ulong)(gx_max_color_value - (v)) * (alpha) / (n1)))
+ gx_color_value r =
+ make_shade(rgb[0], a, n1);
+ gx_color_value g =
+ make_shade(rgb[1], a, n1);
+ gx_color_value b =
+ make_shade(rgb[2], a, n1);
+ gx_color_index sci =
+ svga_map_rgb_color(dev, r, g, b);
+
+ if (sci == gx_no_color_index) {
+ a += (n1 + 1 - a) >> 1;
+ goto map;
+ }
+ shades[a] = ci = (byte) sci;
+ }
+ *ptr = ci;
+ }
+ if (shift == 0)
+ shift = 8 - depth, sptr++;
+ else
+ shift -= depth;
+ }
+ sptr += skip;
+ }
+ return 0;
+}
+
+/* ------ The VESA device ------ */
+
+static dev_proc_open_device(vesa_open);
+static const gx_device_procs vesa_procs = svga_procs(vesa_open);
+int vesa_get_mode(void);
+void vesa_set_mode(int);
+static void vesa_set_page(gx_device_svga *, int, int);
+gx_device_svga far_data gs_vesa_device =
+svga_device(vesa_procs, "vesa", vesa_get_mode, vesa_set_mode, vesa_set_page);
+
+/* Define the structures for information returned by the BIOS. */
+#define bits_include(a, m) !(~(a) & (m))
+/* Information about the BIOS capabilities. */
+typedef struct {
+ byte vesa_signature[4]; /* "VESA" */
+ ushort vesa_version;
+ char *product_info; /* product name string */
+ byte capabilities[4]; /* (undefined) */
+ ushort *mode_list; /* supported video modes, -1 ends */
+} vga_bios_info;
+
+/* Information about an individual VESA mode. */
+typedef enum {
+ m_supported = 1,
+ m_graphics = 0x10
+} mode_attribute;
+typedef enum {
+ w_supported = 1,
+ w_readable = 2,
+ w_writable = 4
+} win_attribute;
+typedef struct {
+ ushort mode_attributes;
+ byte win_a_attributes;
+ byte win_b_attributes;
+ ushort win_granularity;
+ ushort win_size;
+ ushort win_a_segment;
+ ushort win_b_segment;
+ void (*win_func_ptr) (int, int);
+ ushort bytes_per_line;
+ /* Optional information */
+ ushort x_resolution;
+ ushort y_resolution;
+ byte x_char_size;
+ byte y_char_size;
+ byte number_of_planes;
+ byte bits_per_pixel;
+ byte number_of_banks;
+ byte memory_model;
+ byte bank_size;
+ /* Padding to 256 bytes */
+ byte _padding[256 - 29];
+} vesa_info;
+
+/* Read the device mode */
+int
+vesa_get_mode(void)
+{
+ registers regs;
+
+ regs.h.ah = 0x4f;
+ regs.h.al = 0x03;
+ int86(0x10, &regs, &regs);
+ return regs.rshort.bx;
+}
+
+/* Set the device mode */
+void
+vesa_set_mode(int mode)
+{
+ registers regs;
+
+ regs.h.ah = 0x4f;
+ regs.h.al = 0x02;
+ regs.rshort.bx = mode;
+ int86(0x10, &regs, &regs);
+}
+
+/* Read information about a device mode */
+static int
+vesa_get_info(const gs_memory_t *mem, int mode, vesa_info _ss * info)
+{
+ registers regs;
+ struct SREGS sregs;
+
+ regs.h.ah = 0x4f;
+ regs.h.al = 0x01;
+ regs.rshort.cx = mode;
+ segread(&sregs);
+ sregs.es = sregs.ss;
+ regs.rshort.di = PTR_OFF(info);
+ int86x(0x10, &regs, &regs, &sregs);
+#ifdef DEBUG
+ if (regs.h.ah == 0 && regs.h.al == 0x4f)
+ dmlprintf8(mem, "vesa_get_info(%x): ma=%x wa=%x/%x wg=%x ws=%x wseg=%x/%x\n",
+ mode, info->mode_attributes,
+ info->win_a_attributes, info->win_b_attributes,
+ info->win_granularity, info->win_size,
+ info->win_a_segment, info->win_b_segment);
+ else
+ dmlprintf3(mem, "vesa_get_info(%x) failed: ah=%x al=%x\n",
+ mode, regs.h.ah, regs.h.al);
+#endif
+ return (regs.h.ah == 0 && regs.h.al == 0x4f ? 0 : -1);
+}
+
+/* Initialize the graphics mode. */
+/* Shared routine to look up a VESA-compatible BIOS mode. */
+static int
+vesa_find_mode(gx_device * dev, const mode_info * mode_table)
+{ /* Select the proper video mode */
+ vesa_info info;
+ const mode_info *mip;
+
+ for (mip = mode_table; mip->mode >= 0; mip++) {
+ if (mip->width >= fb_dev->width &&
+ mip->height >= fb_dev->height &&
+ vesa_get_info(dev->memory, mip->mode, &info) >= 0 &&
+ bits_include(info.mode_attributes,
+ m_supported | m_graphics) &&
+ info.win_granularity <= 64 &&
+ (info.win_granularity & (info.win_granularity - 1)) == 0 &&
+ info.win_size == 64 &&
+ bits_include(info.win_a_attributes,
+ w_supported) &&
+ info.win_a_segment == regen
+ ) { /* Make sure we can both read & write. */
+ /* Initialize for the default case. */
+ fb_dev->wnum_read = 0;
+ fb_dev->wnum_write = 0;
+ if (bits_include(info.win_a_attributes,
+ w_readable | w_writable)
+ )
+ break;
+ else if (info.win_b_segment == regen &&
+ bits_include(info.win_b_attributes,
+ w_supported) &&
+ bits_include(info.win_a_attributes |
+ info.win_b_attributes,
+ w_readable | w_writable)
+ ) { /* Two superimposed windows. */
+ if (!bits_include(info.win_a_attributes,
+ w_writable)
+ )
+ fb_dev->wnum_write = 1;
+ else
+ fb_dev->wnum_read = 1;
+ }
+ break;
+ }
+ }
+ if (mip->mode < 0)
+ return_error(gs_error_rangecheck); /* mode not available */
+ fb_dev->mode = mip;
+ gx_device_adjust_resolution(dev, mip->width, mip->height, 1);
+ fb_dev->info.vesa.bios_set_page = info.win_func_ptr;
+ fb_dev->info.vesa.pn_shift = ilog2(64 / info.win_granularity);
+ /* Reset the raster per the VESA info. */
+ fb_dev->raster = info.bytes_per_line;
+ return 0;
+}
+static int
+vesa_open(gx_device * dev)
+{
+ static const mode_info mode_table[] =
+ {
+ {640, 400, 0x100},
+ {640, 480, 0x101},
+ {800, 600, 0x103},
+ {1024, 768, 0x105},
+ {1280, 1024, 0x107},
+ {-1, -1, -1}
+ };
+ int code = vesa_find_mode(dev, mode_table);
+
+ if (code < 0)
+ return code;
+ return svga_open(dev);
+}
+
+/* Set the current display page. */
+static void
+vesa_set_page(gx_device_svga * dev, int pn, int wnum)
+{
+ registers regs;
+
+ regs.rshort.dx = pn << dev->info.vesa.pn_shift;
+ regs.h.ah = 0x4f;
+ regs.h.al = 5;
+ regs.rshort.bx = wnum;
+ int86(0x10, &regs, &regs);
+}
+
+/* ------ The ATI Wonder device ------ */
+
+static dev_proc_open_device(atiw_open);
+static const gx_device_procs atiw_procs = svga_procs(atiw_open);
+static int atiw_get_mode(void);
+static void atiw_set_mode(int);
+static void atiw_set_page(gx_device_svga *, int, int);
+gx_device_svga far_data gs_atiw_device =
+svga_device(atiw_procs, "atiw", atiw_get_mode, atiw_set_mode, atiw_set_page);
+
+/* Read the device mode */
+static int
+atiw_get_mode(void)
+{
+ registers regs;
+
+ regs.h.ah = 0xf;
+ int86(0x10, &regs, &regs);
+ return regs.h.al;
+}
+
+/* Set the device mode */
+static void
+atiw_set_mode(int mode)
+{
+ registers regs;
+
+ regs.h.ah = 0;
+ regs.h.al = mode;
+ int86(0x10, &regs, &regs);
+}
+
+/* Initialize the graphics mode. */
+static int
+atiw_open(gx_device * dev)
+{ /* Select the proper video mode */
+ {
+ static const mode_info mode_table[] =
+ {
+ {640, 400, 0x61},
+ {640, 480, 0x62},
+ {800, 600, 0x63},
+ {1024, 768, 0x64},
+ {-1, -1, -1}
+ };
+ int code = svga_find_mode(dev, mode_table);
+
+ if (code < 0)
+ return code; /* mode not available */
+ fb_dev->info.atiw.select_reg = *(int *)MK_PTR(0xc000, 0x10);
+ return svga_open(dev);
+ }
+}
+
+/* Set the current display page. */
+static void
+atiw_set_page(gx_device_svga * dev, int pn, int wnum)
+{
+ int select_reg = dev->info.atiw.select_reg;
+ byte reg;
+
+ disable();
+ outportb(select_reg, 0xb2);
+ reg = inportb(select_reg + 1);
+ outportb(select_reg, 0xb2);
+ outportb(select_reg + 1, (reg & 0xe1) + (pn << 1));
+ enable();
+}
+
+/* ------ The Trident device ------ */
+
+static dev_proc_open_device(tvga_open);
+static const gx_device_procs tvga_procs = svga_procs(tvga_open);
+
+/* We can use the atiw_get/set_mode procedures. */
+static void tvga_set_page(gx_device_svga *, int, int);
+gx_device_svga far_data gs_tvga_device =
+svga_device(tvga_procs, "tvga", atiw_get_mode, atiw_set_mode, tvga_set_page);
+
+/* Initialize the graphics mode. */
+static int
+tvga_open(gx_device * dev)
+{
+ fb_dev->wnum_read = 1;
+ fb_dev->wnum_write = 0;
+ /* Select the proper video mode */
+ {
+ static const mode_info mode_table[] =
+ {
+ {640, 400, 0x5c},
+ {640, 480, 0x5d},
+ {800, 600, 0x5e},
+ {1024, 768, 0x62},
+ {-1, -1, -1}
+ };
+ int code = svga_find_mode(dev, mode_table);
+
+ if (code < 0)
+ return code; /* mode not available */
+ return svga_open(dev);
+ }
+}
+
+/* Set the current display page. */
+static void
+tvga_set_page(gx_device_svga * dev, int pn, int wnum)
+{
+ /* new mode */
+ outportb(0x3c4, 0x0b);
+ inportb(0x3c4);
+
+ outportb(0x3c4, 0x0e);
+ outportb(0x3c5, pn ^ 2);
+}
+
+/* ------ The Tseng Labs ET3000/4000 devices ------ */
+
+static dev_proc_open_device(tseng_open);
+static const gx_device_procs tseng_procs =
+svga_procs(tseng_open);
+
+/* We can use the atiw_get/set_mode procedures. */
+static void tseng_set_page(gx_device_svga *, int, int);
+
+/* The 256-color Tseng device */
+gx_device_svga far_data gs_tseng_device =
+svga_device(tseng_procs, "tseng", atiw_get_mode, atiw_set_mode, tseng_set_page);
+
+/* Initialize the graphics mode. */
+static int
+tseng_open(gx_device * dev)
+{
+ fb_dev->wnum_read = 1;
+ fb_dev->wnum_write = 0;
+ /* Select the proper video mode */
+ {
+ static const mode_info mode_table[] =
+ {
+ {640, 350, 0x2d},
+ {640, 480, 0x2e},
+ {800, 600, 0x30},
+ {1024, 768, 0x38},
+ {-1, -1, -1}
+ };
+ int code = svga_find_mode(dev, mode_table);
+ volatile_fb_ptr p0 = (volatile_fb_ptr) MK_PTR(regen, 0);
+
+ if (code < 0)
+ return code; /* mode not available */
+ code = svga_open(dev);
+ if (code < 0)
+ return 0;
+ /* Figure out whether we have an ET3000 or an ET4000 */
+ /* by playing with the segment register. */
+ outportb(0x3cd, 0x44);
+ *p0 = 4; /* byte 0, page 4 */
+ outportb(0x3cd, 0x40);
+ *p0 = 3; /* byte 0, page 0 */
+ fb_dev->info.tseng.et_model = *p0;
+ /* read page 0 if ET3000, */
+ /* page 4 if ET4000 */
+ return 0;
+ }
+}
+
+/* Set the current display page. */
+static void
+tseng_set_page(gx_device_svga * dev, int pn, int wnum)
+{ /* The ET3000 has read page = 5:3, write page = 2:0; */
+ /* the ET4000 has read page = 7:4, write page = 3:0. */
+ int shift = dev->info.tseng.et_model;
+ int mask = (1 << shift) - 1;
+
+ if (wnum)
+ pn <<= shift, mask <<= shift;
+ outportb(0x3cd, (inportb(0x3cd) & ~mask) + pn);
+}
+/* ------ The Cirrus device (CL-GD54XX) ------ */
+/* Written by Piotr Strzelczyk, BOP s.c., Gda\'nsk, Poland, */
+/* e-mail contact via B.Jackowski@GUST.org.pl */
+
+static dev_proc_open_device(cirr_open);
+static gx_device_procs cirr_procs = svga_procs(cirr_open);
+
+/* We can use the atiw_get/set_mode procedures. */
+static void cirr_set_page(gx_device_svga *, int, int);
+gx_device_svga gs_cirr_device =
+svga_device(cirr_procs, "cirr", atiw_get_mode, atiw_set_mode, cirr_set_page);
+
+/* Initialize the graphics mode. */
+static int
+cirr_open(gx_device * dev)
+{
+ fb_dev->wnum_read = 1;
+ fb_dev->wnum_write = 0;
+ /* Select the proper video mode */
+ {
+ static const mode_info mode_table[] =
+ {
+ {640, 400, 0x5e},
+ {640, 480, 0x5f},
+ {800, 600, 0x5c},
+ {1024, 768, 0x60},
+ {-1, -1, -1}
+ };
+ int code = svga_find_mode(dev, mode_table);
+
+ if (code < 0)
+ return code; /* mode not available */
+ outportb(0x3c4, 0x06);
+ outportb(0x3c5, 0x12);
+ outportb(0x3ce, 0x0b);
+ outportb(0x3cf, (inportb(0x3cf) & 0xde));
+ return svga_open(dev);
+ }
+}
+
+/* Set the current display page. */
+static void
+cirr_set_page(gx_device_svga * dev, int pn, int wnum)
+{
+ outportb(0x3ce, 0x09);
+ outportb(0x3cf, pn << 4);
+}
+
+/* ------ The Avance Logic device (mostly experimental) ------ */
+/* For questions about this device, please contact Stefan Freund */
+/* <freund@ikp.uni-koeln.de>. */
+
+static dev_proc_open_device(ali_open);
+static const gx_device_procs ali_procs = svga_procs(ali_open);
+
+/* We can use the atiw_get/set_mode procedures. */
+static void ali_set_page(gx_device_svga *, int, int);
+
+/* The 256-color Avance Logic device */
+gx_device_svga gs_ali_device =
+svga_device(ali_procs, "ali", atiw_get_mode, atiw_set_mode,
+ ali_set_page);
+
+/* Initialize the graphics mode. */
+static int
+ali_open(gx_device * dev)
+{
+ fb_dev->wnum_read = 1;
+ fb_dev->wnum_write = 0;
+ /* Select the proper video mode */
+ {
+ static const mode_info mode_table[] =
+ {
+ {640, 400, 0x29},
+ {640, 480, 0x2a},
+ {800, 600, 0x2c},
+ {1024, 768, 0x31},
+ {-1, -1, -1}
+ };
+ int code = svga_find_mode(dev, mode_table);
+
+ if (code < 0)
+ return code; /* mode not available */
+ return svga_open(dev);
+ }
+
+}
+
+/* Set the current display page. */
+static void
+ali_set_page(gx_device_svga * dev, int pn, int wnum)
+{
+ outportb(0x3d6, pn); /* read */
+ outportb(0x3d7, pn); /* write */
+}
diff --git a/devices/gdevsvga.h b/devices/gdevsvga.h
new file mode 100644
index 000000000..638df432a
--- /dev/null
+++ b/devices/gdevsvga.h
@@ -0,0 +1,92 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Common definitions and procedures for SuperVGA drivers */
+/* Requires gdevpcfb.h */
+
+#ifndef gdevsvga_INCLUDED
+# define gdevsvga_INCLUDED
+
+/* Common procedures */
+
+ /* See gxdevice.h for the definitions of the procedures. */
+
+dev_proc_close_device(svga_close);
+dev_proc_map_rgb_color(svga_map_rgb_color);
+dev_proc_map_color_rgb(svga_map_color_rgb);
+dev_proc_fill_rectangle(svga_fill_rectangle);
+dev_proc_copy_mono(svga_copy_mono);
+dev_proc_copy_color(svga_copy_color);
+dev_proc_get_params(svga_get_params);
+dev_proc_put_params(svga_put_params);
+dev_proc_get_bits(svga_get_bits);
+dev_proc_copy_alpha(svga_copy_alpha);
+
+/* Table structure for looking up graphics modes. */
+typedef struct {
+ int width, height; /* "key" */
+ int mode; /* "value" */
+} mode_info;
+
+/* The device descriptor structure */
+typedef struct gx_device_svga_s gx_device_svga;
+struct gx_device_svga_s {
+ gx_device_common;
+ int (*get_mode) (void);
+ void (*set_mode) (int);
+ void (*set_page) (gx_device_svga * fbdev, int pnum, int wnum);
+ bool fixed_colors; /* if true, used a fixed palette */
+ const mode_info *mode; /* BIOS display mode info */
+ uint raster; /* frame buffer bytes per line */
+ int current_page; /* current page */
+ int wnum_read, wnum_write; /* window #s for read vs. write */
+ /* Following are device-specific. */
+ union {
+ struct {
+ void (*bios_set_page) (int, int); /* set-page function */
+ int pn_shift; /* log2(64K/granularity) */
+ } vesa;
+ struct {
+ int select_reg; /* page-select register */
+ } atiw;
+ struct {
+ int et_model; /* 4 for ET4000, */
+ /* 3 for ET3000 */
+ } tseng;
+ } info;
+};
+
+/* The initial parameters map an appropriate fraction of */
+/* the screen to a full-page coordinate space. */
+/* This may or may not be what is desired! */
+#define svga_color_device(procs, name, depth, maxv, dither, get_mode, set_mode, set_page) {\
+ std_device_color_body(gx_device_svga, &procs, name,\
+ 640, 480,\
+ 480 / PAGE_HEIGHT_INCHES, 480 / PAGE_HEIGHT_INCHES,\
+ /*dci_color(*/depth, maxv, dither/*)*/),\
+ { 0 }, /* std_procs */\
+ get_mode, set_mode, set_page,\
+ 0 /*fixed_colors*/\
+ }
+#define svga_device(procs, name, get_mode, set_mode, set_page)\
+ svga_color_device(procs, name, 8, 31, 4, get_mode, set_mode, set_page)
+
+/* Utility procedures */
+void svga_init_colors(gx_device *);
+int svga_find_mode(gx_device *, const mode_info *);
+int svga_open(gx_device *);
+
+#endif /* gdevsvga_INCLUDED */
diff --git a/devices/gdevtfax.c b/devices/gdevtfax.c
new file mode 100644
index 000000000..20631b9df
--- /dev/null
+++ b/devices/gdevtfax.c
@@ -0,0 +1,395 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* TIFF and TIFF/fax devices */
+
+#include "stdint_.h" /* for tiff.h */
+#include "gdevtifs.h"
+#include "gdevprn.h"
+#include "strimpl.h"
+#include "scfx.h"
+#include "gdevfax.h"
+
+#include "gstiffio.h"
+#include "gdevkrnlsclass.h" /* 'standard' built in subclasses, currently First/Last Page and obejct filter */
+
+/* ---------------- TIFF/fax output ---------------- */
+
+/* The device descriptors */
+
+static dev_proc_open_device(tfax_open);
+static dev_proc_close_device(tfax_close);
+static dev_proc_get_params(tfax_get_params);
+static dev_proc_put_params(tfax_put_params);
+static dev_proc_print_page(tiffcrle_print_page);
+static dev_proc_print_page(tiffg3_print_page);
+static dev_proc_print_page(tiffg32d_print_page);
+static dev_proc_print_page(tiffg4_print_page);
+
+struct gx_device_tfax_s {
+ gx_device_common;
+ gx_prn_device_common;
+ gx_fax_device_common;
+ long MaxStripSize; /* 0 = no limit, other is UNCOMPRESSED limit */
+ /* The type and range of FillOrder follows TIFF 6 spec */
+ int FillOrder; /* 1 = lowest column in the high-order bit, 2 = reverse */
+ bool BigEndian; /* true = big endian; false = little endian*/
+ bool UseBigTIFF;
+ uint16 Compression; /* same values as TIFFTAG_COMPRESSION */
+
+ TIFF *tif; /* For TIFF output only */
+};
+typedef struct gx_device_tfax_s gx_device_tfax;
+
+/* Define procedures that adjust the paper size. */
+static const gx_device_procs gdev_tfax_std_procs =
+/* FIXME: From initial analysis this is NOT safe for bg_printing, but might be fixable */
+ prn_params_procs(tfax_open, gdev_prn_output_page_seekable, tfax_close,
+ tfax_get_params, tfax_put_params);
+
+#define TFAX_DEVICE(dname, print_page, compr)\
+{\
+ FAX_DEVICE_BODY(gx_device_tfax, gdev_tfax_std_procs, dname, print_page),\
+ TIFF_DEFAULT_STRIP_SIZE /* strip size byte count */,\
+ 1 /* lowest column in the high-order bit */,\
+ arch_is_big_endian /* default to native endian (i.e. use big endian iff the platform is so*/,\
+ false, /* default to not using bigtiff */\
+ compr,\
+ NULL\
+}
+
+const gx_device_tfax gs_tiffcrle_device =
+ TFAX_DEVICE("tiffcrle", tiffcrle_print_page, COMPRESSION_CCITTRLE);
+
+const gx_device_tfax gs_tiffg3_device =
+ TFAX_DEVICE("tiffg3", tiffg3_print_page, COMPRESSION_CCITTFAX3);
+
+const gx_device_tfax gs_tiffg32d_device =
+ TFAX_DEVICE("tiffg32d", tiffg32d_print_page, COMPRESSION_CCITTFAX3);
+
+const gx_device_tfax gs_tiffg4_device =
+ TFAX_DEVICE("tiffg4", tiffg4_print_page, COMPRESSION_CCITTFAX4);
+
+static int
+tfax_open(gx_device * pdev)
+{
+ gx_device_printer * const ppdev = (gx_device_printer *)pdev;
+ int code;
+
+ /* Use our own warning and error message handlers in libtiff */
+ tiff_set_handlers();
+
+ ppdev->file = NULL;
+ code = gdev_prn_allocate_memory(pdev, NULL, 0, 0);
+ if (code < 0)
+ return code;
+
+ if (ppdev->OpenOutputFile)
+ if ((code = gdev_prn_open_printer_seekable(pdev, 1, true)) < 0)
+ return code;
+ code = install_internal_subclass_devices((gx_device **)&pdev, NULL);
+ return code;
+}
+
+static int
+tfax_close(gx_device * pdev)
+{
+ gx_device_tfax *const tfdev = (gx_device_tfax *)pdev;
+
+ if (tfdev->tif)
+ TIFFCleanup(tfdev->tif);
+
+ return gdev_prn_close(pdev);
+}
+
+static int
+tfax_get_params(gx_device * dev, gs_param_list * plist)
+{
+ gx_device_tfax *const tfdev = (gx_device_tfax *)dev;
+ int code = gdev_fax_get_params(dev, plist);
+ int ecode = code;
+ gs_param_string comprstr;
+
+ if ((code = param_write_long(plist, "MaxStripSize", &tfdev->MaxStripSize)) < 0)
+ ecode = code;
+ if ((code = param_write_int(plist, "FillOrder", &tfdev->FillOrder)) < 0)
+ ecode = code;
+ if ((code = param_write_bool(plist, "BigEndian", &tfdev->BigEndian)) < 0)
+ ecode = code;
+#if (TIFFLIB_VERSION >= 20111221)
+ if ((code = param_write_bool(plist, "UseBigTiff", &tfdev->UseBigTIFF)) < 0)
+ ecode = code;
+#endif
+ if ((code = tiff_compression_param_string(&comprstr, tfdev->Compression)) < 0 ||
+ (code = param_write_string(plist, "Compression", &comprstr)) < 0)
+ ecode = code;
+
+ return ecode;
+}
+
+static int
+tfax_put_params(gx_device * dev, gs_param_list * plist)
+{
+ gx_device_tfax *const tfdev = (gx_device_tfax *)dev;
+ int ecode = 0;
+ int code;
+ long mss = tfdev->MaxStripSize;
+ int fill_order = tfdev->FillOrder;
+ const char *param_name;
+ bool big_endian = tfdev->BigEndian;
+ bool usebigtiff = tfdev->UseBigTIFF;
+ uint16 compr = tfdev->Compression;
+ gs_param_string comprstr;
+
+ switch (code = param_read_long(plist, (param_name = "MaxStripSize"), &mss)) {
+ case 0:
+ /*
+ * Strip must be large enough to accommodate a raster line.
+ * If the max strip size is too small, we still write a single
+ * line per strip rather than giving an error.
+ */
+ if (mss >= 0)
+ break;
+ code = gs_error_rangecheck;
+ default:
+ ecode = code;
+ param_signal_error(plist, param_name, ecode);
+ case 1:
+ break;
+ }
+
+ /* Following TIFF spec, FillOrder is integer */
+ switch (code = param_read_int(plist, (param_name = "FillOrder"), &fill_order)) {
+ case 0:
+ if (fill_order == 1 || fill_order == 2)
+ break;
+ code = gs_error_rangecheck;
+ default:
+ ecode = code;
+ param_signal_error(plist, param_name, ecode);
+ case 1:
+ break;
+ }
+
+ /* Read BigEndian option as bool */
+ switch (code = param_read_bool(plist, (param_name = "BigEndian"), &big_endian)) {
+ default:
+ ecode = code;
+ param_signal_error(plist, param_name, ecode);
+ case 0:
+ case 1:
+ break;
+ }
+
+ /* Read UseBigTIFF option as bool */
+ switch (code = param_read_bool(plist, (param_name = "UseBigTiff"), &usebigtiff)) {
+ default:
+ ecode = code;
+ param_signal_error(plist, param_name, ecode);
+ case 0:
+ case 1:
+ break;
+ }
+
+#if !(TIFFLIB_VERSION >= 20111221)
+ if (usebigtiff)
+ dmlprintf(dev->memory, "Warning: this version of libtiff does not support BigTIFF, ignoring parameter\n");
+ usebigtiff = false;
+#endif
+
+ /* Read Compression */
+ switch (code = param_read_string(plist, (param_name = "Compression"), &comprstr)) {
+ case 0:
+ if ((ecode = tiff_compression_id(&compr, &comprstr)) < 0 ||
+ !tiff_compression_allowed(compr, dev->color_info.depth))
+ param_signal_error(plist, param_name, ecode);
+ break;
+ case 1:
+ break;
+ default:
+ ecode = code;
+ param_signal_error(plist, param_name, ecode);
+ }
+
+ if (ecode < 0)
+ return ecode;
+ code = gdev_fax_put_params(dev, plist);
+ if (code < 0)
+ return code;
+
+ tfdev->MaxStripSize = mss;
+ tfdev->FillOrder = fill_order;
+ tfdev->BigEndian = big_endian;
+ tfdev->UseBigTIFF = usebigtiff;
+ tfdev->Compression = compr;
+ return code;
+}
+
+/* ---------------- Other TIFF output ---------------- */
+
+#include "slzwx.h"
+#include "srlx.h"
+
+/* Device descriptors for TIFF formats other than fax. */
+static dev_proc_print_page(tifflzw_print_page);
+static dev_proc_print_page(tiffpack_print_page);
+
+const gx_device_tfax gs_tifflzw_device = {
+ prn_device_std_body(gx_device_tfax, gdev_tfax_std_procs, "tifflzw",
+ DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
+ X_DPI, Y_DPI,
+ 0, 0, 0, 0, /* margins */
+ 1, tifflzw_print_page),
+ 0/* AdjustWidth */,
+ 0 /* MinFeatureSize */,
+ TIFF_DEFAULT_STRIP_SIZE /* strip size byte count */,
+ 1 /* lowest column in the high-order bit, not used */,
+ arch_is_big_endian /* default to native endian (i.e. use big endian iff the platform is so*/,
+ false /* defauilt to *not* UseBigTIFF */,
+ COMPRESSION_LZW
+};
+
+const gx_device_tfax gs_tiffpack_device = {
+ prn_device_std_body(gx_device_tfax, gdev_tfax_std_procs, "tiffpack",
+ DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
+ X_DPI, Y_DPI,
+ 0, 0, 0, 0, /* margins */
+ 1, tiffpack_print_page),
+ 0 /* AdjustWidth */,
+ 0 /* MinFeatureSize */,
+ TIFF_DEFAULT_STRIP_SIZE /* strip size byte count */,
+ 1 /* lowest column in the high-order bit, not used */,
+ arch_is_big_endian /* default to native endian (i.e. use big endian iff the platform is so*/,
+ false /* defauilt to *not* UseBigTIFF */,
+ COMPRESSION_PACKBITS
+};
+
+/* Forward references */
+static int tfax_begin_page(gx_device_tfax * tfdev, FILE * file);
+
+static void
+tfax_set_fields(gx_device_tfax *tfdev)
+{
+ short fillorder = tfdev->FillOrder == 1 ? FILLORDER_MSB2LSB : FILLORDER_LSB2MSB;
+
+ TIFFSetField(tfdev->tif, TIFFTAG_BITSPERSAMPLE, 1);
+ TIFFSetField(tfdev->tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISWHITE);
+ TIFFSetField(tfdev->tif, TIFFTAG_FILLORDER, fillorder);
+ TIFFSetField(tfdev->tif, TIFFTAG_SAMPLESPERPIXEL, 1);
+
+ tiff_set_compression((gx_device_printer *)tfdev,
+ tfdev->tif,
+ tfdev->Compression,
+ tfdev->MaxStripSize);
+}
+
+static int
+tiffcrle_print_page(gx_device_printer * dev, FILE * prn_stream)
+{
+ gx_device_tfax *tfdev = (gx_device_tfax *)dev;
+
+ tfax_begin_page(tfdev, prn_stream);
+
+ tfax_set_fields(tfdev);
+
+ return tiff_print_page(dev, tfdev->tif, tfdev->MinFeatureSize);
+}
+
+static int
+tiffg3_print_page(gx_device_printer * dev, FILE * prn_stream)
+{
+ gx_device_tfax *tfdev = (gx_device_tfax *)dev;
+
+ tfax_begin_page(tfdev, prn_stream);
+
+ tfax_set_fields(tfdev);
+ if (tfdev->Compression == COMPRESSION_CCITTFAX3)
+ TIFFSetField(tfdev->tif, TIFFTAG_GROUP3OPTIONS, GROUP3OPT_FILLBITS);
+
+ return tiff_print_page(dev, tfdev->tif, tfdev->MinFeatureSize);
+}
+
+static int
+tiffg32d_print_page(gx_device_printer * dev, FILE * prn_stream)
+{
+ gx_device_tfax *tfdev = (gx_device_tfax *)dev;
+
+ tfax_begin_page(tfdev, prn_stream);
+
+ tfax_set_fields(tfdev);
+ if (tfdev->Compression == COMPRESSION_CCITTFAX3)
+ TIFFSetField(tfdev->tif, TIFFTAG_GROUP3OPTIONS, GROUP3OPT_2DENCODING | GROUP3OPT_FILLBITS);
+
+ return tiff_print_page(dev, tfdev->tif, tfdev->MinFeatureSize);
+}
+
+static int
+tiffg4_print_page(gx_device_printer * dev, FILE * prn_stream)
+{
+ gx_device_tfax *tfdev = (gx_device_tfax *)dev;
+
+ tfax_begin_page(tfdev, prn_stream);
+
+ tfax_set_fields(tfdev);
+ if (tfdev->Compression == COMPRESSION_CCITTFAX4)
+ TIFFSetField(tfdev->tif, TIFFTAG_GROUP4OPTIONS, 0);
+
+ return tiff_print_page(dev, tfdev->tif, tfdev->MinFeatureSize);
+}
+
+/* Print an LZW page. */
+static int
+tifflzw_print_page(gx_device_printer * dev, FILE * prn_stream)
+{
+ gx_device_tfax *const tfdev = (gx_device_tfax *)dev;
+
+ tfax_begin_page(tfdev, prn_stream);
+ tfax_set_fields(tfdev);
+ TIFFSetField(tfdev->tif, TIFFTAG_FILLORDER, FILLORDER_MSB2LSB);
+
+ return tiff_print_page(dev, tfdev->tif, tfdev->MinFeatureSize);
+}
+
+/* Print a PackBits page. */
+static int
+tiffpack_print_page(gx_device_printer * dev, FILE * prn_stream)
+{
+ gx_device_tfax *const tfdev = (gx_device_tfax *)dev;
+
+ tfax_begin_page(tfdev, prn_stream);
+ tfax_set_fields(tfdev);
+ TIFFSetField(tfdev->tif, TIFFTAG_FILLORDER, FILLORDER_MSB2LSB);
+
+ return tiff_print_page(dev, tfdev->tif, tfdev->MinFeatureSize);
+}
+
+/* Begin a TIFF fax page. */
+static int
+tfax_begin_page(gx_device_tfax * tfdev, FILE * file)
+{
+ gx_device_printer *const pdev = (gx_device_printer *)tfdev;
+ int code;
+
+ /* open the TIFF device */
+ if (gdev_prn_file_is_new(pdev)) {
+ tfdev->tif = tiff_from_filep(pdev, pdev->dname, file, tfdev->BigEndian, tfdev->UseBigTIFF);
+ if (!tfdev->tif)
+ return_error(gs_error_invalidfileaccess);
+ }
+
+ code = tiff_set_fields_for_printer(pdev, tfdev->tif, 1, tfdev->AdjustWidth);
+ return code;
+}
diff --git a/devices/gdevtfax.h b/devices/gdevtfax.h
new file mode 100644
index 000000000..a3b26572c
--- /dev/null
+++ b/devices/gdevtfax.h
@@ -0,0 +1,25 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Entry points to the TIFF/fax writing driver */
+
+#ifndef gdevtfax_INCLUDED
+# define gdevtfax_INCLUDED
+
+int gdev_fax_print_page_stripped(gx_device_printer *pdev, FILE *prn_stream,
+ stream_CFE_state *ss, long rows_per_strip);
+
+#endif /* gdevtfax_INCLUDED */
diff --git a/devices/gdevtfnx.c b/devices/gdevtfnx.c
new file mode 100644
index 000000000..585500c71
--- /dev/null
+++ b/devices/gdevtfnx.c
@@ -0,0 +1,202 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* 12-bit & 24-bit RGB uncompressed TIFF driver */
+
+#include "stdint_.h" /* for tiff.h */
+#include "gdevtifs.h"
+#include "gdevprn.h"
+#include "gscms.h"
+
+#include "gstiffio.h"
+
+/*
+ * Thanks to Alan Barclay <alan@escribe.co.uk> for donating the original
+ * version of this code to Ghostscript.
+ */
+
+/* ------ The device descriptors ------ */
+
+/* Default X and Y resolution */
+#define X_DPI 72
+#define Y_DPI 72
+
+static dev_proc_print_page(tiff12_print_page);
+static dev_proc_print_page(tiff_rgb_print_page);
+
+/* FIXME: From initial analysis this is NOT safe for bg_printing, but might be fixable */
+
+static const gx_device_procs tiff12_procs =
+prn_color_params_procs(tiff_open, gdev_prn_output_page_seekable, tiff_close,
+ gx_default_rgb_map_rgb_color, gx_default_rgb_map_color_rgb,
+ tiff_get_params, tiff_put_params);
+static const gx_device_procs tiff24_procs =
+prn_color_params_procs(tiff_open, gdev_prn_output_page_seekable, tiff_close,
+ gx_default_rgb_map_rgb_color, gx_default_rgb_map_color_rgb,
+ tiff_get_params, tiff_put_params);
+
+const gx_device_tiff gs_tiff12nc_device = {
+ prn_device_std_body(gx_device_tiff, tiff12_procs, "tiff12nc",
+ DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
+ X_DPI, Y_DPI,
+ 0, 0, 0, 0,
+ 24, tiff12_print_page),
+ arch_is_big_endian /* default to native endian (i.e. use big endian iff the platform is so*/,
+ false, /* default to not bigtiff */
+ COMPRESSION_NONE,
+ TIFF_DEFAULT_STRIP_SIZE,
+ TIFF_DEFAULT_DOWNSCALE,
+ 0, /* Adjust size */
+ 1 /* MinFeatureSize */
+};
+
+const gx_device_tiff gs_tiff24nc_device = {
+ prn_device_std_body(gx_device_tiff, tiff24_procs, "tiff24nc",
+ DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
+ X_DPI, Y_DPI,
+ 0, 0, 0, 0,
+ 24, tiff_rgb_print_page),
+ arch_is_big_endian /* default to native endian (i.e. use big endian iff the platform is so*/,
+ false, /* default to not bigtiff */
+ COMPRESSION_NONE,
+ TIFF_DEFAULT_STRIP_SIZE,
+ TIFF_DEFAULT_DOWNSCALE,
+ 0, /* Adjust size */
+ 1 /* MinFeatureSize */
+};
+
+const gx_device_tiff gs_tiff48nc_device = {
+ prn_device_std_body(gx_device_tiff, tiff24_procs, "tiff48nc",
+ DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
+ X_DPI, Y_DPI,
+ 0, 0, 0, 0,
+ 48, tiff_rgb_print_page),
+ arch_is_big_endian /* default to native endian (i.e. use big endian iff the platform is so*/,
+ false, /* default to not bigtiff */
+ COMPRESSION_NONE,
+ TIFF_DEFAULT_STRIP_SIZE,
+ TIFF_DEFAULT_DOWNSCALE,
+ 0, /* Adjust size */
+ 1 /* MinFeatureSize */
+};
+
+/* ------ Private functions ------ */
+
+static void
+tiff_set_rgb_fields(gx_device_tiff *tfdev)
+{
+ /* Put in a switch statement in case we want to have others */
+ switch (tfdev->icc_struct->device_profile[0]->data_cs) {
+ case gsRGB:
+ TIFFSetField(tfdev->tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB);
+ break;
+ case gsCIELAB:
+ TIFFSetField(tfdev->tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_ICCLAB);
+ break;
+ default:
+ TIFFSetField(tfdev->tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB);
+ break;
+ }
+ TIFFSetField(tfdev->tif, TIFFTAG_FILLORDER, FILLORDER_MSB2LSB);
+ TIFFSetField(tfdev->tif, TIFFTAG_SAMPLESPERPIXEL, 3);
+
+ tiff_set_compression((gx_device_printer *)tfdev, tfdev->tif,
+ tfdev->Compression, tfdev->MaxStripSize);
+}
+
+static int
+tiff12_print_page(gx_device_printer * pdev, FILE * file)
+{
+ gx_device_tiff *const tfdev = (gx_device_tiff *)pdev;
+ int code;
+
+ /* open the TIFF device */
+ if (gdev_prn_file_is_new(pdev)) {
+ tfdev->tif = tiff_from_filep(pdev, pdev->dname, file, tfdev->BigEndian, tfdev->UseBigTIFF);
+ if (!tfdev->tif)
+ return_error(gs_error_invalidfileaccess);
+ }
+
+ code = gdev_tiff_begin_page(tfdev, file);
+ if (code < 0)
+ return code;
+
+ TIFFSetField(tfdev->tif, TIFFTAG_BITSPERSAMPLE, 4);
+ tiff_set_rgb_fields(tfdev);
+
+ TIFFCheckpointDirectory(tfdev->tif);
+
+ /* Write the page data. */
+ {
+ int y;
+ int size = gdev_prn_raster(pdev);
+ byte *data = gs_alloc_bytes(pdev->memory, size, "tiff12_print_page");
+
+ if (data == 0)
+ return_error(gs_error_VMerror);
+
+ memset(data, 0, size);
+
+ for (y = 0; y < pdev->height; ++y) {
+ const byte *src;
+ byte *dest;
+ int x;
+
+ code = gdev_prn_copy_scan_lines(pdev, y, data, size);
+ if (code < 0)
+ break;
+
+ for (src = data, dest = data, x = 0; x < size;
+ src += 6, dest += 3, x += 6
+ ) {
+ dest[0] = (src[0] & 0xf0) | (src[1] >> 4);
+ dest[1] = (src[2] & 0xf0) | (src[3] >> 4);
+ dest[2] = (src[4] & 0xf0) | (src[5] >> 4);
+ }
+ TIFFWriteScanline(tfdev->tif, data, y, 0);
+ }
+ gs_free_object(pdev->memory, data, "tiff12_print_page");
+
+ TIFFWriteDirectory(tfdev->tif);
+ }
+
+ return code;
+}
+
+static int
+tiff_rgb_print_page(gx_device_printer * pdev, FILE * file)
+{
+ gx_device_tiff *const tfdev = (gx_device_tiff *)pdev;
+ int code;
+
+ /* open the TIFF device */
+ if (gdev_prn_file_is_new(pdev)) {
+ tfdev->tif = tiff_from_filep(pdev, pdev->dname, file, tfdev->BigEndian, tfdev->UseBigTIFF);
+ if (!tfdev->tif)
+ return_error(gs_error_invalidfileaccess);
+ }
+
+ code = gdev_tiff_begin_page(tfdev, file);
+ if (code < 0)
+ return code;
+
+ TIFFSetField(tfdev->tif, TIFFTAG_BITSPERSAMPLE,
+ pdev->color_info.depth / pdev->color_info.num_components);
+ tiff_set_rgb_fields(tfdev);
+
+ /* Write the page data. */
+ return tiff_print_page(pdev, tfdev->tif, 0);
+}
diff --git a/devices/gdevtifs.c b/devices/gdevtifs.c
new file mode 100644
index 000000000..1e9db3247
--- /dev/null
+++ b/devices/gdevtifs.c
@@ -0,0 +1,541 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* TIFF-writing substructure */
+
+#include "stdint_.h" /* for tiff.h */
+#include "stdio_.h"
+#include "time_.h"
+#include "gdevtifs.h"
+#include "gstypes.h"
+#include "gscdefs.h"
+#include "gdevprn.h"
+#include "minftrsz.h"
+#include "gxdownscale.h"
+#include "scommon.h"
+#include "stream.h"
+#include "strmio.h"
+
+#include "gstiffio.h"
+#include "gdevkrnlsclass.h" /* 'standard' built in subclasses, currently First/Last Page and obejct filter */
+
+int
+tiff_open(gx_device *pdev)
+{
+ gx_device_printer *ppdev = (gx_device_printer *)pdev;
+ int code;
+ bool update_procs = false;
+
+ /* Use our own warning and error message handlers in libtiff */
+ tiff_set_handlers();
+
+ code = install_internal_subclass_devices((gx_device **)&pdev, &update_procs);
+ if (code < 0)
+ return code;
+ /* If we've been subclassed, find the terminal device */
+ while(pdev->child)
+ pdev = pdev->child;
+ ppdev = (gx_device_printer *)pdev;
+
+ ppdev->file = NULL;
+ code = gdev_prn_allocate_memory(pdev, NULL, 0, 0);
+ if (code < 0)
+ return code;
+ if (update_procs) {
+ if (pdev->ObjectHandlerPushed) {
+ gx_copy_device_procs(&pdev->parent->procs, &pdev->procs, (gx_device_procs *)&gs_obj_filter_device.procs);
+ pdev = pdev->parent;
+ }
+ if (pdev->PageHandlerPushed)
+ gx_copy_device_procs(&pdev->parent->procs, &pdev->procs, (gx_device_procs *)&gs_flp_device.procs);
+ }
+ if (ppdev->OpenOutputFile)
+ code = gdev_prn_open_printer_seekable(pdev, 1, true);
+ return code;
+}
+
+int
+tiff_close(gx_device * pdev)
+{
+ gx_device_tiff *const tfdev = (gx_device_tiff *)pdev;
+
+ if (tfdev->tif)
+ TIFFCleanup(tfdev->tif);
+
+ return gdev_prn_close(pdev);
+}
+
+static int
+tiff_get_some_params(gx_device * dev, gs_param_list * plist, int which)
+{
+ gx_device_tiff *const tfdev = (gx_device_tiff *)dev;
+ int code = gdev_prn_get_params(dev, plist);
+ int ecode = code;
+ gs_param_string comprstr;
+
+ if ((code = param_write_bool(plist, "BigEndian", &tfdev->BigEndian)) < 0)
+ ecode = code;
+#if (TIFFLIB_VERSION >= 20111221)
+ if ((code = param_write_bool(plist, "UseBigTIFF", &tfdev->UseBigTIFF)) < 0)
+ ecode = code;
+#endif
+ if ((code = tiff_compression_param_string(&comprstr, tfdev->Compression)) < 0 ||
+ (code = param_write_string(plist, "Compression", &comprstr)) < 0)
+ ecode = code;
+ if (which & 1)
+ {
+ if ((code = param_write_long(plist, "DownScaleFactor", &tfdev->DownScaleFactor)) < 0)
+ ecode = code;
+ }
+ if ((code = param_write_long(plist, "MaxStripSize", &tfdev->MaxStripSize)) < 0)
+ ecode = code;
+ if ((code = param_write_long(plist, "AdjustWidth", &tfdev->AdjustWidth)) < 0)
+ ecode = code;
+ if ((code = param_write_long(plist, "MinFeatureSize", &tfdev->MinFeatureSize)) < 0)
+ ecode = code;
+ return ecode;
+}
+
+int
+tiff_get_params(gx_device * dev, gs_param_list * plist)
+{
+ return tiff_get_some_params(dev, plist, 0);
+}
+
+int
+tiff_get_params_downscale(gx_device * dev, gs_param_list * plist)
+{
+ return tiff_get_some_params(dev, plist, 1);
+}
+
+static int
+tiff_put_some_params(gx_device * dev, gs_param_list * plist, int which)
+{
+ gx_device_tiff *const tfdev = (gx_device_tiff *)dev;
+ int ecode = 0;
+ int code;
+ const char *param_name;
+ bool big_endian = tfdev->BigEndian;
+ bool usebigtiff = tfdev->UseBigTIFF;
+ uint16 compr = tfdev->Compression;
+ gs_param_string comprstr;
+ long downscale = tfdev->DownScaleFactor;
+ long mss = tfdev->MaxStripSize;
+ long aw = tfdev->AdjustWidth;
+ long mfs = tfdev->MinFeatureSize;
+
+ /* Read BigEndian option as bool */
+ switch (code = param_read_bool(plist, (param_name = "BigEndian"), &big_endian)) {
+ default:
+ ecode = code;
+ param_signal_error(plist, param_name, ecode);
+ case 0:
+ case 1:
+ break;
+ }
+
+ /* Read UseBigTIFF option as bool */
+ switch (code = param_read_bool(plist, (param_name = "UseBigTIFF"), &usebigtiff)) {
+ default:
+ ecode = code;
+ param_signal_error(plist, param_name, ecode);
+ case 0:
+ case 1:
+ break;
+ }
+
+#if !(TIFFLIB_VERSION >= 20111221)
+ if (usebigtiff)
+ dmlprintf(dev->memory, "Warning: this version of libtiff does not support BigTIFF, ignoring parameter\n");
+ usebigtiff = false;
+#endif
+
+ /* Read Compression */
+ switch (code = param_read_string(plist, (param_name = "Compression"), &comprstr)) {
+ case 0:
+ if ((ecode = tiff_compression_id(&compr, &comprstr)) < 0) {
+
+ errprintf(tfdev->memory, "Unknown compression setting\n");
+ param_signal_error(plist, param_name, ecode);
+ return ecode;
+ }
+
+ if ( !tiff_compression_allowed(compr, (which & 1 ? 1 : (dev->color_info.depth / dev->color_info.num_components)))) {
+
+ errprintf(tfdev->memory, "Invalid compression setting for this bitdepth\n");
+ param_signal_error(plist, param_name, gs_error_rangecheck);
+ return gs_error_rangecheck;
+ }
+ break;
+ case 1:
+ break;
+ default:
+ ecode = code;
+ param_signal_error(plist, param_name, ecode);
+ }
+ /* Read Downscale factor */
+ if (which & 1) {
+ switch (code = param_read_long(plist,
+ (param_name = "DownScaleFactor"),
+ &downscale)) {
+ case 0:
+ if (downscale <= 0)
+ downscale = 1;
+ break;
+ case 1:
+ break;
+ default:
+ ecode = code;
+ param_signal_error(plist, param_name, ecode);
+ }
+ }
+ switch (code = param_read_long(plist, (param_name = "MaxStripSize"), &mss)) {
+ case 0:
+ /*
+ * Strip must be large enough to accommodate a raster line.
+ * If the max strip size is too small, we still write a single
+ * line per strip rather than giving an error.
+ */
+ if (mss >= 0)
+ break;
+ code = gs_error_rangecheck;
+ default:
+ ecode = code;
+ param_signal_error(plist, param_name, ecode);
+ case 1:
+ break;
+ }
+ switch (code = param_read_long(plist, (param_name = "AdjustWidth"), &aw)) {
+ case 0:
+ if (aw >= 0)
+ break;
+ code = gs_error_rangecheck;
+ default:
+ ecode = code;
+ param_signal_error(plist, param_name, ecode);
+ case 1:
+ break;
+ }
+ switch (code = param_read_long(plist, (param_name = "MinFeatureSize"), &mfs)) {
+ case 0:
+ if ((mfs >= 0) && (mfs <= 4))
+ break;
+ code = gs_error_rangecheck;
+ default:
+ ecode = code;
+ param_signal_error(plist, param_name, ecode);
+ case 1:
+ break;
+ }
+
+ if (ecode < 0)
+ return ecode;
+ code = gdev_prn_put_params(dev, plist);
+ if (code < 0)
+ return code;
+
+ tfdev->BigEndian = big_endian;
+ tfdev->UseBigTIFF = usebigtiff;
+ tfdev->Compression = compr;
+ tfdev->MaxStripSize = mss;
+ tfdev->DownScaleFactor = downscale;
+ tfdev->AdjustWidth = aw;
+ tfdev->MinFeatureSize = mfs;
+ return code;
+}
+
+int
+tiff_put_params(gx_device * dev, gs_param_list * plist)
+{
+ return tiff_put_some_params(dev, plist, 0);
+}
+
+int
+tiff_put_params_downscale(gx_device * dev, gs_param_list * plist)
+{
+ return tiff_put_some_params(dev, plist, 1);
+}
+
+int gdev_tiff_begin_page(gx_device_tiff *tfdev,
+ FILE *file)
+{
+ gx_device_printer *const pdev = (gx_device_printer *)tfdev;
+
+ if (gdev_prn_file_is_new(pdev)) {
+ /* open the TIFF device */
+ tfdev->tif = tiff_from_filep(pdev, pdev->dname, file, tfdev->BigEndian, tfdev->UseBigTIFF);
+ if (!tfdev->tif)
+ return_error(gs_error_invalidfileaccess);
+ }
+
+ return tiff_set_fields_for_printer(pdev, tfdev->tif, tfdev->DownScaleFactor,
+ tfdev->AdjustWidth);
+}
+
+int tiff_set_compression(gx_device_printer *pdev,
+ TIFF *tif,
+ uint compression,
+ long max_strip_size)
+{
+ TIFFSetField(tif, TIFFTAG_COMPRESSION, compression);
+
+ if (max_strip_size == 0) {
+ TIFFSetField(tif, TIFFTAG_ROWSPERSTRIP, pdev->height);
+ }
+ else {
+ int rows = max_strip_size /
+ gdev_mem_bytes_per_scan_line((gx_device *)pdev);
+ TIFFSetField(tif,
+ TIFFTAG_ROWSPERSTRIP,
+ TIFFDefaultStripSize(tif, max(1, rows)));
+ }
+
+ return 0;
+}
+
+int tiff_set_fields_for_printer(gx_device_printer *pdev,
+ TIFF *tif,
+ int factor,
+ int adjustWidth)
+{
+ int width = gx_downscaler_scale(pdev->width, factor);
+ int height = gx_downscaler_scale(pdev->height, factor);
+ int xpi = gx_downscaler_scale(pdev->x_pixels_per_inch, factor);
+ int ypi = gx_downscaler_scale(pdev->y_pixels_per_inch, factor);
+ width = fax_adjusted_width(width, adjustWidth);
+ TIFFSetField(tif, TIFFTAG_IMAGEWIDTH, width);
+ TIFFSetField(tif, TIFFTAG_IMAGELENGTH, height);
+ TIFFSetField(tif, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT);
+ TIFFSetField(tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
+
+ TIFFSetField(tif, TIFFTAG_RESOLUTIONUNIT, RESUNIT_INCH);
+ TIFFSetField(tif, TIFFTAG_XRESOLUTION, (float)xpi);
+ TIFFSetField(tif, TIFFTAG_YRESOLUTION, (float)ypi);
+
+ {
+ char revs[10];
+#define maxSoftware 40
+ char softwareValue[maxSoftware];
+
+ strncpy(softwareValue, gs_product, maxSoftware);
+ softwareValue[maxSoftware - 1] = 0;
+ gs_sprintf(revs, " %1.2f", gs_revision / 100.0);
+ strncat(softwareValue, revs,
+ maxSoftware - strlen(softwareValue) - 1);
+
+ TIFFSetField(tif, TIFFTAG_SOFTWARE, softwareValue);
+ }
+ {
+ struct tm tms;
+ time_t t;
+ char dateTimeValue[20];
+
+ time(&t);
+ tms = *localtime(&t);
+ gs_sprintf(dateTimeValue, "%04d:%02d:%02d %02d:%02d:%02d",
+ tms.tm_year + 1900, tms.tm_mon + 1, tms.tm_mday,
+ tms.tm_hour, tms.tm_min, tms.tm_sec);
+
+ TIFFSetField(tif, TIFFTAG_DATETIME, dateTimeValue);
+ }
+
+ TIFFSetField(tif, TIFFTAG_SUBFILETYPE, FILETYPE_PAGE);
+ TIFFSetField(tif, TIFFTAG_PAGENUMBER, pdev->PageCount, 0);
+
+ /* Set the ICC profile. Test to avoid issues with separations and also
+ if the color space is set to LAB, we do that as an enumerated type. Do
+ NOT set the profile if the bit depth is less than 8 or if fast color
+ was used. */
+ if (pdev->color_info.depth >= 8) {
+ if (pdev->icc_struct != NULL && pdev->icc_struct->device_profile[0] != NULL) {
+ cmm_profile_t *icc_profile = pdev->icc_struct->device_profile[0];
+ if (icc_profile->num_comps == pdev->color_info.num_components &&
+ icc_profile->data_cs != gsCIELAB && !(pdev->icc_struct->usefastcolor)) {
+ TIFFSetField(tif, TIFFTAG_ICCPROFILE, icc_profile->buffer_size,
+ icc_profile->buffer);
+ }
+ }
+ }
+ return 0;
+}
+
+int
+tiff_print_page(gx_device_printer *dev, TIFF *tif, int min_feature_size)
+{
+ int code = 0;
+ byte *data;
+ int size = gdev_mem_bytes_per_scan_line((gx_device *)dev);
+ int max_size = max(size, TIFFScanlineSize(tif));
+ int row;
+ int bpc = dev->color_info.depth / dev->color_info.num_components;
+ void *min_feature_data = NULL;
+ int line_lag = 0;
+ int filtered_count;
+
+ data = gs_alloc_bytes(dev->memory, max_size, "tiff_print_page(data)");
+ if (data == NULL)
+ return_error(gs_error_VMerror);
+ if (bpc != 1)
+ min_feature_size = 1;
+ if (min_feature_size > 1) {
+ code = min_feature_size_init(dev->memory, min_feature_size,
+ dev->width, dev->height,
+ &min_feature_data);
+ if (code < 0)
+ goto cleanup;
+ }
+
+ code = TIFFCheckpointDirectory(tif);
+
+ memset(data, 0, max_size);
+ for (row = 0; row < dev->height && code >= 0; row++) {
+ code = gdev_prn_copy_scan_lines(dev, row, data, size);
+ if (code < 0)
+ break;
+ if (min_feature_size > 1) {
+ filtered_count = min_feature_size_process(data, min_feature_data);
+ if (filtered_count == 0)
+ line_lag++;
+ }
+
+ if (row - line_lag >= 0) {
+#if defined(ARCH_IS_BIG_ENDIAN) && (!ARCH_IS_BIG_ENDIAN)
+ if (bpc == 16)
+ TIFFSwabArrayOfShort((uint16 *)data,
+ dev->width * dev->color_info.num_components);
+#endif
+
+ code = TIFFWriteScanline(tif, data, row - line_lag, 0);
+ }
+ }
+ for (row -= line_lag ; row < dev->height && code >= 0; row++)
+ {
+ filtered_count = min_feature_size_process(data, min_feature_data);
+ code = TIFFWriteScanline(tif, data, row, 0);
+ }
+
+ if (code >= 0)
+ code = TIFFWriteDirectory(tif);
+cleanup:
+ if (min_feature_size > 1)
+ min_feature_size_dnit(min_feature_data);
+ gs_free_object(dev->memory, data, "tiff_print_page(data)");
+
+ return code;
+}
+
+/* Special version, called with 8 bit grey input to be downsampled to 1bpp
+ * output. */
+int
+tiff_downscale_and_print_page(gx_device_printer *dev, TIFF *tif, int factor,
+ int mfs, int aw, int bpc, int num_comps)
+{
+ int code = 0;
+ byte *data = NULL;
+ int size = gdev_mem_bytes_per_scan_line((gx_device *)dev);
+ int max_size = max(size, TIFFScanlineSize(tif));
+ int row;
+ int height = dev->height/factor;
+ gx_downscaler_t ds;
+
+ code = TIFFCheckpointDirectory(tif);
+ if (code < 0)
+ return code;
+
+ code = gx_downscaler_init(&ds, (gx_device *)dev, 8, bpc, num_comps,
+ factor, mfs, &fax_adjusted_width, aw);
+ if (code < 0)
+ return code;
+
+ data = gs_alloc_bytes(dev->memory, max_size, "tiff_print_page(data)");
+ if (data == NULL) {
+ gx_downscaler_fin(&ds);
+ return_error(gs_error_VMerror);
+ }
+
+ for (row = 0; row < height && code >= 0; row++) {
+ code = gx_downscaler_copy_scan_lines(&ds, row, data, size);
+ if (code < 0)
+ break;
+
+ code = TIFFWriteScanline(tif, data, row, 0);
+ if (code < 0)
+ break;
+ }
+
+ if (code >= 0)
+ code = TIFFWriteDirectory(tif);
+
+ gx_downscaler_fin(&ds);
+ gs_free_object(dev->memory, data, "tiff_print_page(data)");
+
+ return code;
+}
+
+
+static struct compression_string {
+ uint16 id;
+ const char *str;
+} compression_strings [] = {
+ { COMPRESSION_NONE, "none" },
+ { COMPRESSION_CCITTRLE, "crle" },
+ { COMPRESSION_CCITTFAX3, "g3" },
+ { COMPRESSION_CCITTFAX4, "g4" },
+ { COMPRESSION_LZW, "lzw" },
+ { COMPRESSION_PACKBITS, "pack" },
+
+ { 0, NULL }
+};
+
+int
+tiff_compression_param_string(gs_param_string *param, uint16 id)
+{
+ struct compression_string *c;
+ for (c = compression_strings; c->str; c++)
+ if (id == c->id) {
+ param_string_from_string(*param, c->str);
+ return 0;
+ }
+ return gs_error_undefined;
+}
+
+int
+tiff_compression_id(uint16 *id, gs_param_string *param)
+{
+ struct compression_string *c;
+ for (c = compression_strings; c->str; c++)
+ if (!bytes_compare(param->data, param->size,
+ (const byte *)c->str, strlen(c->str)))
+ {
+ *id = c->id;
+ return 0;
+ }
+ return gs_error_undefined;
+}
+
+int tiff_compression_allowed(uint16 compression, byte depth)
+{
+ return ((depth == 1 && (compression == COMPRESSION_NONE ||
+ compression == COMPRESSION_CCITTRLE ||
+ compression == COMPRESSION_CCITTFAX3 ||
+ compression == COMPRESSION_CCITTFAX4 ||
+ compression == COMPRESSION_LZW ||
+ compression == COMPRESSION_PACKBITS))
+ || ((depth == 8 || depth == 16) && (compression == COMPRESSION_NONE ||
+ compression == COMPRESSION_LZW ||
+ compression == COMPRESSION_PACKBITS)));
+
+}
diff --git a/devices/gdevtifs.h b/devices/gdevtifs.h
new file mode 100644
index 000000000..cbd5b6c95
--- /dev/null
+++ b/devices/gdevtifs.h
@@ -0,0 +1,90 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Definitions for writing TIFF file formats. */
+
+#ifndef gdevtifs_INCLUDED
+# define gdevtifs_INCLUDED
+
+#include <tiffio.h> /* must be first, because gdevio.h re-#defines "printf"
+ which is used in a function __attribute__ by
+ tiffio.h */
+#include "gdevprn.h"
+
+/* ================ Implementation ================ */
+
+typedef struct gx_device_tiff_s {
+ gx_device_common;
+ gx_prn_device_common;
+ bool BigEndian; /* true = big endian; false = little endian*/
+ bool UseBigTIFF; /* true = output big tiff file, false don't */
+ uint16 Compression; /* same values as TIFFTAG_COMPRESSION */
+ long MaxStripSize;
+ long DownScaleFactor;
+ long AdjustWidth; /* 0 = no adjust, 1 = adjust to fax values, >1 = adjust to this */
+ long MinFeatureSize; /* < 2 == no darkening */
+ TIFF *tif; /* TIFF file opened on gx_device_common.file */
+} gx_device_tiff;
+
+dev_proc_open_device(tiff_open);
+dev_proc_close_device(tiff_close);
+dev_proc_get_params(tiff_get_params);
+dev_proc_get_params(tiff_get_params_downscale);
+dev_proc_put_params(tiff_put_params);
+dev_proc_put_params(tiff_put_params_downscale);
+
+int tiff_print_page(gx_device_printer *dev, TIFF *tif, int min_feature_size);
+
+int tiff_downscale_and_print_page(gx_device_printer *dev, TIFF *tif,
+ int factor, int msf, int aw, int bpc,
+ int num_comps);
+void tiff_set_handlers (void);
+
+/*
+ * Sets the compression tag for TIFF and updates the rows_per_strip tag to
+ * reflect max_strip_size under the new compression scheme.
+ */
+#define TIFF_DEFAULT_STRIP_SIZE 1048576
+
+#define TIFF_DEFAULT_DOWNSCALE 1
+
+int tiff_set_compression(gx_device_printer *pdev,
+ TIFF *tif,
+ uint compression,
+ long max_strip_size);
+
+int tiff_set_fields_for_printer(gx_device_printer *pdev, TIFF *tif, int factor,
+ int adjustWidth);
+
+int gdev_tiff_begin_page(gx_device_tiff *tfdev, FILE *file);
+
+/*
+ * Returns the gs_param_string that corresponds to the tiff COMPRESSION_* id.
+ */
+int tiff_compression_param_string(gs_param_string *param, uint16 id);
+
+/*
+ * Returns the COMPRESSION_* id which corresponds to 'str'.
+ */
+int tiff_compression_id(uint16 *id, gs_param_string *param);
+
+/*
+ * Returns true if 'compression' can be used for encoding a data with a bit
+ * depth of 'depth' (crle, g3, and g4 only work on 1-bit devices).
+ */
+int tiff_compression_allowed(uint16 compression, byte depth);
+
+#endif /* gdevtifs_INCLUDED */
diff --git a/devices/gdevtknk.c b/devices/gdevtknk.c
new file mode 100644
index 000000000..28ca27164
--- /dev/null
+++ b/devices/gdevtknk.c
@@ -0,0 +1,254 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/*
+ This driver is written for Tektronix ink-jet 4696 and 4695 plotters.
+
+ It may easily be adopted to the 4393 and 4394 models as well, simply by
+ adding new device descriptors with other geometrical characteristics.
+ */
+#include "gdevprn.h"
+#include "malloc_.h"
+
+/* Thanks to Karsten Spang (spang@nbivax.nbi.dk) for contributing */
+/* this code to Aladdin Enterprises. */
+
+/* The device descriptor */
+/* We need our own color mapping procedures. */
+static dev_proc_map_rgb_color(tekink_map_rgb_color);
+static dev_proc_map_color_rgb(tekink_map_color_rgb);
+static dev_proc_print_page(tekink_print_page);
+/* Since the print_page doesn't alter the device, this device can print in the background */
+static gx_device_procs tekink_procs =
+ prn_color_procs(gdev_prn_open, gdev_prn_bg_output_page, gdev_prn_close,
+ tekink_map_rgb_color, tekink_map_color_rgb);
+
+/*
+ Device descriptor for the Tek 4696.
+ The 4696 plotter uses roll media, thus the y size is arbitrary. The
+ value below is chosen to make the image area A*-format like, i.e. the
+ aspect ratio is close to sqrt(2).
+*/
+const gx_device_printer far_data gs_tek4696_device =
+ prn_device(tekink_procs,"tek4696",
+ 85,120, /* Page size in 10th of inches */
+ 120,120, /* Resolution in DPI */
+ 0.0,0.0,0.0,0.0, /* Margins */
+ 4, /* Bits per pixel */
+ tekink_print_page);
+
+/* Color mapping.
+ The tek inkjets use subtractive colors B=0 M=1 Y=2 C=3. These are
+ represented as 4 bits B=1 M=2 Y=4 C=8 in a byte. This gives:
+ White = 0
+ Black = 1
+ Magenta = 2
+ Yellow = 4
+ Red = 6
+ Cyan = 8
+ Blue = 10
+ Green = 12
+ The remaining values are unused. (They give ugly results if sent to the
+ plotter.) Of course this could have been compressed into 3 bits, but
+ as the palette color memory device uses 8 bits anyway, this is easier,
+ and perhaps faster.
+*/
+
+static gx_color_index rgb_to_index[8]={1,6,12,4,10,2,8,0};
+static ushort index_to_rgb[16][3]={
+ {65535,65535,65535}, /* White */
+ {0,0,0}, /* Black */
+ {65535,0,65535}, /* Magenta */
+ {2,2,2}, /* Unused */
+ {65535,65535,0}, /* Yellow */
+ {2,2,2}, /* Unused */
+ {65535,0,0}, /* Red */
+ {2,2,2}, /* Unused */
+ {0,65535,65535}, /* Cyan */
+ {2,2,2}, /* Unused */
+ {0,0,65535}, /* Blue */
+ {2,2,2}, /* Unused */
+ {0,65535,0}, /* Green */
+ {2,2,2}, /* Unused */
+ {2,2,2}, /* Unused */
+ {2,2,2} /* Unused */
+};
+
+/* Map an RGB color to a printer color. */
+static gx_color_index
+tekink_map_rgb_color(gx_device *dev, const gx_color_value cv[])
+{
+ gx_color_value r = cv[0];
+ gx_color_value g = cv[1];
+ gx_color_value b = cv[2];
+
+ return(rgb_to_index[(((b>32767) << 2) + ((g>32767) << 1) +
+ (r>32767)) & 7]);
+}
+
+/* Map the printer color back to RGB. */
+static int
+tekink_map_color_rgb(gx_device *dev, gx_color_index color, ushort prgb[3])
+{
+ register ushort c = (ushort)color;
+ register int i;
+ if (c>15) return -1;
+ if (index_to_rgb[c][0]==2) return -1;
+ for (i=0;i<3;i++){
+ prgb[i]=index_to_rgb[c][i];
+ }
+ return 0;
+}
+
+/* Send the page to the printer. */
+static int
+tekink_print_page(gx_device_printer *pdev,FILE *prn_stream)
+{
+ int line_size,color_line_size,scan_line,num_bytes,scan_lines,color_plane;
+ int roll_paper,out_line,micro_line,pending_micro_lines,line_blank,
+ blank_lines;
+ byte *outdata,*indata1,*bdata1,*mdata1,*ydata1,*cdata1;
+ register byte *indata,*bdatap,*mdatap,*ydatap,*cdatap;
+ register byte bdata,mdata,ydata,cdata;
+ register byte mask,inbyte;
+ register byte *indataend,*outdataend;
+
+ /* Allocate a temporary buffer for color separation.
+ The buffer is partitioned into an input buffer and four
+ output buffers for the color planes. The output buffers
+ are allocated with an extra sentinel byte. */
+
+ line_size = gdev_mem_bytes_per_scan_line((gx_device *)pdev);
+ color_line_size=(pdev->width+7)/8;
+ indata1=(byte *)malloc(line_size+4*(color_line_size+1));
+ if (indata1==NULL) return -1;
+ /* pointers to the partions */
+ indataend=indata1+line_size;
+ bdata1=indataend;
+ mdata1=bdata1+(color_line_size+1);
+ ydata1=mdata1+(color_line_size+1);
+ cdata1=ydata1+(color_line_size+1);
+
+ /* Does this device use roll paper? */
+ roll_paper=!strcmp(pdev->dname,"tek4696");
+
+ out_line=0;
+ blank_lines=0;
+ scan_lines=pdev->height;
+ for (scan_line=0;scan_line<scan_lines;scan_line++){
+ /* get data */
+ gdev_prn_copy_scan_lines(pdev,scan_line,indata1,line_size);
+ /* Separate data into color planes */
+ bdatap = bdata1+1;
+ mdatap = mdata1+1;
+ ydatap = ydata1+1;
+ cdatap = cdata1+1;
+ bdata=0;
+ mdata=0;
+ cdata=0;
+ ydata=0;
+ mask=0x80;
+ memset(indataend,0,4*(color_line_size+1));
+ for (indata=indata1;indata<indataend;indata++){
+ inbyte = *indata;
+ if (inbyte&0x01) bdata|=mask;
+ if (inbyte&0x02) mdata|=mask;
+ if (inbyte&0x04) ydata|=mask;
+ if (inbyte&0x08) cdata|=mask;
+ mask>>=1;
+ if (!mask){
+ *(bdatap++) = bdata;
+ *(mdatap++) = mdata;
+ *(cdatap++) = cdata;
+ *(ydatap++) = ydata;
+ bdata=0;
+ mdata=0;
+ cdata=0;
+ ydata=0;
+ mask=0x80;
+ }
+ }
+ if (mask!=0x80){
+ *bdatap = bdata;
+ *mdatap = mdata;
+ *cdatap = cdata;
+ *ydatap = ydata;
+ }
+ line_blank=1;
+ /* Output each of the four color planes */
+ for (color_plane=0;color_plane<4;color_plane++){
+ outdata=indataend+(color_plane*(color_line_size+1));
+ outdataend=outdata+color_line_size;
+
+ /* Remove trailing spaces and output the color line if it is
+ not blank */
+ *outdata=0xff;
+ while (!(*outdataend)) outdataend--;
+ num_bytes=(outdataend-outdata);
+ if (num_bytes!=0){
+ line_blank=0;
+ /* On encountering the first non-blank data, output pending
+ blank lines */
+ if (blank_lines){
+ pending_micro_lines=((out_line+blank_lines+1)/4)-
+ (out_line/4);
+ for (micro_line=0;micro_line<pending_micro_lines;
+ micro_line++){
+ fputs("\033A",prn_stream);
+ }
+ out_line+=blank_lines;
+ blank_lines=0;
+ }
+ fprintf(prn_stream,"\033I%c%03d",'0'+(out_line%4)+
+ 4*color_plane,num_bytes);
+ fwrite(outdata+1,1,num_bytes,prn_stream);
+ }
+ } /* loop over color planes */
+
+ /* If this line is blank, and if it is a roll paper model,
+ count the line. Otherwise output the line */
+ if (line_blank&&roll_paper){
+ /* Only increment the blank line count, if non blank lines
+ have been encountered previously, i.e. skip leading blank
+ lines. */
+ if (out_line) blank_lines++;
+ }
+ else{
+ if (out_line%4==3){
+ /* Write micro line feed code */
+ fputs("\033A",prn_stream);
+ }
+ out_line++;
+ }
+ } /* loop over scan lines */
+
+ /* if the number of scan lines written is not a multiple of four,
+ write the final micro line feed code */
+ if (out_line%4){
+ fputs("\033A",prn_stream);
+ }
+ /* Separate this plot from the next */
+ if (roll_paper){
+ fputs("\n\n\n\n\n",prn_stream);
+ }
+ else{
+ fputs("\f",prn_stream);
+ }
+
+ /* Deallocate temp buffer */
+ free(indata1);
+ return 0;
+}
diff --git a/devices/gdevtrac.c b/devices/gdevtrac.c
new file mode 100644
index 000000000..d6dd28f04
--- /dev/null
+++ b/devices/gdevtrac.c
@@ -0,0 +1,703 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Tracing device (including sample high-level implementation) */
+#include "gx.h"
+#include "gserrors.h"
+#include "gscspace.h"
+#include "gxdevice.h"
+#include "gxtmap.h" /* for gxdht.h */
+#include "gxdht.h"
+#include "gxfont.h"
+#include "gxiparam.h"
+#include "gxistate.h"
+#include "gxpaint.h"
+#include "gzpath.h"
+#include "gzcpath.h"
+
+/* Define the default resolution. */
+#ifndef X_DPI
+# define X_DPI 720
+#endif
+#ifndef Y_DPI
+# define Y_DPI 720
+#endif
+
+extern_st(st_gs_text_enum);
+extern_st(st_gx_image_enum_common);
+
+/* ---------------- Internal utilities ---------------- */
+
+static void
+trace_drawing_color(const gs_memory_t *mem, const char *prefix, const gx_drawing_color *pdcolor)
+{
+ dmprintf1(mem, "%scolor=", prefix);
+ if (pdcolor->type == gx_dc_type_none)
+ dmputs(mem, "none");
+ else if (pdcolor->type == gx_dc_type_null)
+ dmputs(mem, "null");
+ else if (pdcolor->type == gx_dc_type_pure)
+ dmprintf1(mem, "0x%lx", (ulong)pdcolor->colors.pure);
+ else if (pdcolor->type == gx_dc_type_ht_binary) {
+ int ci = pdcolor->colors.binary.b_index;
+
+ dmprintf5(mem, "binary(0x%lx, 0x%lx, %d/%d, index=%d)",
+ (ulong)pdcolor->colors.binary.color[0],
+ (ulong)pdcolor->colors.binary.color[1],
+ pdcolor->colors.binary.b_level,
+ (ci < 0 ? pdcolor->colors.binary.b_ht->order.num_bits :
+ pdcolor->colors.binary.b_ht->components[ci].corder.num_bits),
+ ci);
+ } else if (pdcolor->type == gx_dc_type_ht_colored) {
+ ulong plane_mask = pdcolor->colors.colored.plane_mask;
+ int ci;
+
+ dmprintf1(mem, "colored(mask=%lu", plane_mask);
+ for (ci = 0; plane_mask != 0; ++ci, plane_mask >>= 1)
+ if (plane_mask & 1) {
+ dmprintf2(mem, ", (base=%u, level=%u)",
+ pdcolor->colors.colored.c_base[ci],
+ pdcolor->colors.colored.c_level[ci]);
+ } else
+ dmputs(mem, ", -");
+ } else {
+ dmputs(mem, "**unknown**");
+ }
+}
+
+static void
+trace_lop(const gs_memory_t *mem, gs_logical_operation_t lop)
+{
+ dmprintf1(mem, ", lop=0x%x", (uint)lop);
+}
+
+static void
+trace_path(const gx_path *path)
+{
+ gs_path_enum penum;
+
+ gx_path_enum_init(&penum, path);
+ for (;;) {
+ gs_fixed_point pts[3];
+
+ switch (gx_path_enum_next(&penum, pts)) {
+ case gs_pe_moveto:
+ dmprintf2(path->memory," %g %g moveto\n", fixed2float(pts[0].x),
+ fixed2float(pts[0].y));
+ continue;
+ case gs_pe_lineto:
+ dmprintf2(path->memory," %g %g lineto\n", fixed2float(pts[0].x),
+ fixed2float(pts[0].y));
+ continue;
+ case gs_pe_gapto:
+ dmprintf2(path->memory," %g %g gapto\n", fixed2float(pts[0].x),
+ fixed2float(pts[0].y));
+ continue;
+ case gs_pe_curveto:
+ dmprintf6(path->memory," %g %g %g %g %g %g curveto\n", fixed2float(pts[0].x),
+ fixed2float(pts[0].y), fixed2float(pts[1].x),
+ fixed2float(pts[1].y), fixed2float(pts[2].x),
+ fixed2float(pts[2].y));
+ continue;
+ case gs_pe_closepath:
+ dmputs(path->memory," closepath\n");
+ continue;
+ default:
+ break;
+ }
+ break;
+ }
+}
+
+static void
+trace_clip(gx_device *dev, const gx_clip_path *pcpath)
+{
+ if (pcpath == 0)
+ return;
+ if (gx_cpath_includes_rectangle(pcpath, fixed_0, fixed_0,
+ int2fixed(dev->width),
+ int2fixed(dev->height))
+ )
+ return;
+ dmputs(dev->memory, ", clip={");
+ if (pcpath->path_valid)
+ trace_path(&pcpath->path);
+ else
+ dmputs(dev->memory, "NO PATH");
+ dmputs(dev->memory, "}");
+}
+
+/* ---------------- Low-level driver procedures ---------------- */
+
+static int
+trace_fill_rectangle(gx_device * dev, int x, int y, int w, int h,
+ gx_color_index color)
+{
+ dmprintf5(dev->memory,"fill_rectangle(%d, %d, %d, %d, 0x%lx)\n",
+ x, y, w, h, (ulong)color);
+ return 0;
+}
+
+static int
+trace_copy_mono(gx_device * dev, const byte * data,
+ int dx, int raster, gx_bitmap_id id,
+ int x, int y, int w, int h,
+ gx_color_index zero, gx_color_index one)
+{
+ dmprintf7(dev->memory,"copy_mono(x=%d, y=%d, w=%d, h=%d, dx=%d, raster=%d, id=0x%lx,\n",
+ x, y, w, h, dx, raster, (ulong)id);
+ dmprintf2(dev->memory," colors=(0x%lx,0x%lx))\n", (ulong)zero, (ulong)one);
+ return 0;
+}
+
+static int
+trace_copy_color(gx_device * dev, const byte * data,
+ int dx, int raster, gx_bitmap_id id,
+ int x, int y, int w, int h)
+{
+ dmprintf7(dev->memory,"copy_color(x=%d, y=%d, w=%d, h=%d, dx=%d, raster=%d, id=0x%lx)\n",
+ x, y, w, h, dx, raster, (ulong)id);
+ return 0;
+}
+
+static int
+trace_copy_alpha(gx_device * dev, const byte * data, int dx, int raster,
+ gx_bitmap_id id, int x, int y, int w, int h,
+ gx_color_index color, int depth)
+{
+ dmprintf7(dev->memory,"copy_alpha(x=%d, y=%d, w=%d, h=%d, dx=%d, raster=%d, id=0x%lx,\n",
+ x, y, w, h, dx, raster, (ulong)id);
+ dmprintf2(dev->memory," color=0x%lx, depth=%d)\n", (ulong)color, depth);
+ return 0;
+}
+
+static int
+trace_fill_mask(gx_device * dev,
+ const byte * data, int dx, int raster, gx_bitmap_id id,
+ int x, int y, int w, int h,
+ const gx_drawing_color * pdcolor, int depth,
+ gs_logical_operation_t lop, const gx_clip_path * pcpath)
+{
+ dmprintf7(dev->memory,"fill_mask(x=%d, y=%d, w=%d, h=%d, dx=%d, raster=%d, id=0x%lx,\n",
+ x, y, w, h, dx, raster, (ulong)id);
+ trace_drawing_color(dev->memory," ", pdcolor);
+ dmprintf1(dev->memory,", depth=%d", depth);
+ trace_lop(dev->memory,lop);
+ trace_clip(dev, pcpath);
+ dmputs(dev->memory,")\n");
+ return 0;
+}
+
+static int
+trace_fill_trapezoid(gx_device * dev,
+ const gs_fixed_edge * left,
+ const gs_fixed_edge * right,
+ fixed ybot, fixed ytop, bool swap_axes,
+ const gx_drawing_color * pdcolor,
+ gs_logical_operation_t lop)
+{
+ dmputs(dev->memory,"**fill_trapezoid**\n");
+ return 0;
+}
+
+static int
+trace_fill_parallelogram(gx_device * dev,
+ fixed px, fixed py, fixed ax, fixed ay,
+ fixed bx, fixed by, const gx_drawing_color * pdcolor,
+ gs_logical_operation_t lop)
+{
+ dmprintf6(dev->memory,"fill_parallelogram((%g,%g), (%g,%g), (%g,%g)",
+ fixed2float(px), fixed2float(py), fixed2float(ax),
+ fixed2float(ay), fixed2float(bx), fixed2float(by));
+ trace_drawing_color(dev->memory,", ", pdcolor);
+ trace_lop(dev->memory,lop);
+ dmputs(dev->memory,")\n");
+ return 0;
+}
+
+static int
+trace_fill_triangle(gx_device * dev,
+ fixed px, fixed py, fixed ax, fixed ay, fixed bx, fixed by,
+ const gx_drawing_color * pdcolor,
+ gs_logical_operation_t lop)
+{
+ dmprintf6(dev->memory,"fill_triangle((%g,%g), (%g,%g), (%g,%g)",
+ fixed2float(px), fixed2float(py), fixed2float(ax),
+ fixed2float(ay), fixed2float(bx), fixed2float(by));
+ trace_drawing_color(dev->memory,", ", pdcolor);
+ trace_lop(dev->memory,lop);
+ dmputs(dev->memory,")\n");
+ return 0;
+}
+
+static int
+trace_draw_thin_line(gx_device * dev,
+ fixed fx0, fixed fy0, fixed fx1, fixed fy1,
+ const gx_drawing_color * pdcolor,
+ gs_logical_operation_t lop,
+ fixed adjustx, fixed adjusty)
+{
+ dmprintf4(dev->memory,"draw_thin_line((%g,%g), (%g,%g)",
+ fixed2float(fx0), fixed2float(fy0), fixed2float(fx1),
+ fixed2float(fy1));
+ trace_drawing_color(dev->memory,", ", pdcolor);
+ trace_lop(dev->memory,lop);
+ dmputs(dev->memory,")\n");
+ return 0;
+}
+
+static int
+trace_strip_tile_rectangle(gx_device * dev, const gx_strip_bitmap * tiles,
+ int x, int y, int w, int h,
+ gx_color_index color0, gx_color_index color1,
+ int px, int py)
+{
+ dmprintf6(dev->memory,"strip_tile_rectangle(x=%d, y=%d, w=%d, h=%d, colors=(0x%lx,0x%lx),\n",
+ x, y, w, h, (ulong)color0, (ulong)color1);
+ dmprintf8(dev->memory," size=(%d,%d) shift %u, rep=(%u,%u) shift %u, phase=(%d,%d))\n",
+ tiles->size.x, tiles->size.y, tiles->shift,
+ tiles->rep_width, tiles->rep_height, tiles->rep_shift, px, py);
+ return 0;
+}
+
+static int
+trace_strip_copy_rop(gx_device * dev, const byte * sdata, int sourcex,
+ uint sraster, gx_bitmap_id id,
+ const gx_color_index * scolors,
+ const gx_strip_bitmap * textures,
+ const gx_color_index * tcolors,
+ int x, int y, int width, int height,
+ int phase_x, int phase_y, gs_logical_operation_t lop)
+{
+ dmputs(dev->memory,"**strip_copy_rop**\n");
+ return 0;
+}
+
+static int
+trace_strip_copy_rop2(gx_device * dev, const byte * sdata, int sourcex,
+ uint sraster, gx_bitmap_id id,
+ const gx_color_index * scolors,
+ const gx_strip_bitmap * textures,
+ const gx_color_index * tcolors,
+ int x, int y, int width, int height,
+ int phase_x, int phase_y, gs_logical_operation_t lop,
+ uint plane_height)
+{
+ dmputs(dev->memory,"**strip_copy_rop2**\n");
+ return 0;
+}
+
+/* ---------------- High-level driver procedures ---------------- */
+
+static int
+trace_fill_path(gx_device * dev, const gs_imager_state * pis,
+ gx_path * ppath, const gx_fill_params * params,
+ const gx_drawing_color * pdcolor,
+ const gx_clip_path * pcpath)
+{
+ dmputs(dev->memory,"fill_path({\n");
+ trace_path(ppath);
+ trace_drawing_color(dev->memory,"}, ", pdcolor);
+ dmprintf4(dev->memory,", rule=%d, adjust=(%g,%g), flatness=%g",
+ params->rule, fixed2float(params->adjust.x),
+ fixed2float(params->adjust.y), params->flatness);
+ trace_clip(dev, pcpath);
+ /****** pis ******/
+ dmputs(dev->memory,")\n");
+ return 0;
+}
+
+static int
+trace_stroke_path(gx_device * dev, const gs_imager_state * pis,
+ gx_path * ppath, const gx_stroke_params * params,
+ const gx_drawing_color * pdcolor,
+ const gx_clip_path * pcpath)
+{
+ dmputs(dev->memory,"stroke_path({\n");
+ trace_path(ppath);
+ trace_drawing_color(dev->memory,"}, ", pdcolor);
+ dmprintf1(dev->memory,", flatness=%g", params->flatness);
+ trace_clip(dev, pcpath);
+ /****** pis ******/
+ dmputs(dev->memory,")\n");
+ return 0;
+}
+
+typedef struct trace_image_enum_s {
+ gx_image_enum_common;
+ int rows_left;
+} trace_image_enum_t;
+gs_private_st_suffix_add0(st_trace_image_enum, trace_image_enum_t,
+ "trace_image_enum_t", trace_image_enum_enum_ptrs,
+ trace_image_enum_reloc_ptrs,
+ st_gx_image_enum_common);
+static int
+trace_plane_data(gx_image_enum_common_t * info,
+ const gx_image_plane_t * planes, int height,
+ int *rows_used)
+{
+ trace_image_enum_t *pie = (trace_image_enum_t *)info;
+ int i;
+
+ dmprintf1(info->memory,"image_plane_data(height=%d", height);
+ for (i = 0; i < pie->num_planes; ++i) {
+ if (planes[i].data)
+ dmprintf4(info->memory,", {depth=%d, width=%d, dx=%d, raster=%u}",
+ pie->plane_depths[i], pie->plane_widths[i],
+ planes[i].data_x, planes[i].raster);
+ else
+ dmputs(info->memory,", -");
+ }
+ dmputs(info->memory,")\n");
+ *rows_used = height;
+ return (pie->rows_left -= height) <= 0;
+}
+static int
+trace_end_image(gx_image_enum_common_t * info, bool draw_last)
+{
+ trace_image_enum_t *pie = (trace_image_enum_t *)info;
+
+ gx_image_free_enum(&info);
+ return 0;
+}
+static const gx_image_enum_procs_t trace_image_enum_procs = {
+ trace_plane_data, trace_end_image
+};
+static int
+trace_begin_typed_image(gx_device * dev, const gs_imager_state * pis,
+ const gs_matrix * pmat,
+ const gs_image_common_t * pim,
+ const gs_int_rect * prect,
+ const gx_drawing_color * pdcolor,
+ const gx_clip_path * pcpath,
+ gs_memory_t * memory,
+ gx_image_enum_common_t ** pinfo)
+{
+ trace_image_enum_t *pie;
+ const gs_pixel_image_t *ppi = (const gs_pixel_image_t *)pim;
+ int ncomp;
+
+ dmprintf7(dev->memory,"begin_typed_image(type=%d, ImageMatrix=[%g %g %g %g %g %g]",
+ pim->type->index, pim->ImageMatrix.xx, pim->ImageMatrix.xy,
+ pim->ImageMatrix.yx, pim->ImageMatrix.yy,
+ pim->ImageMatrix.tx, pim->ImageMatrix.ty);
+ switch (pim->type->index) {
+ case 1:
+ if (((const gs_image1_t *)ppi)->ImageMask) {
+ ncomp = 1;
+ break;
+ }
+ /* falls through */
+ case 4:
+ ncomp = gs_color_space_num_components(ppi->ColorSpace);
+ break;
+ case 3:
+ ncomp = gs_color_space_num_components(ppi->ColorSpace) + 1;
+ break;
+ case 2: /* no data */
+ dmputs(dev->memory, ")\n");
+ return 1;
+ default:
+ goto dflt;
+ }
+ pie = gs_alloc_struct(memory, trace_image_enum_t, &st_trace_image_enum,
+ "trace_begin_typed_image");
+ if (pie == 0)
+ goto dflt;
+ if (gx_image_enum_common_init((gx_image_enum_common_t *)pie,
+ (const gs_data_image_t *)pim,
+ &trace_image_enum_procs, dev, ncomp,
+ ppi->format) < 0
+ )
+ goto dflt;
+ dmprintf4(dev->memory,"\n Width=%d, Height=%d, BPC=%d, num_components=%d)\n",
+ ppi->Width, ppi->Height, ppi->BitsPerComponent, ncomp);
+ pie->memory = memory;
+ pie->rows_left = ppi->Height;
+ *pinfo = (gx_image_enum_common_t *)pie;
+ return 0;
+ dflt:
+ dmputs(dev->memory,") DEFAULTED\n");
+ return gx_default_begin_typed_image(dev, pis, pmat, pim, prect, pdcolor,
+ pcpath, memory, pinfo);
+}
+
+static int
+trace_text_process(gs_text_enum_t *pte)
+{
+ return 0;
+}
+static const gs_text_enum_procs_t trace_text_procs = {
+ NULL, trace_text_process, NULL, NULL, NULL, NULL,
+ gx_default_text_release
+};
+static int
+trace_text_begin(gx_device * dev, gs_imager_state * pis,
+ const gs_text_params_t * text, gs_font * font,
+ gx_path * path, const gx_device_color * pdcolor,
+ const gx_clip_path * pcpath, gs_memory_t * memory,
+ gs_text_enum_t ** ppenum)
+{
+ static const char *const tags[sizeof(text->operation) * 8] = {
+ "FROM_STRING", "FROM_BYTES", "FROM_CHARS", "FROM_GLYPHS",
+ "FROM_SINGLE_CHAR", "FROM_SINGLE_GLYPH",
+ "ADD_TO_ALL_WIDTHS", "ADD_TO_SPACE_WIDTH",
+ "REPLACE_WIDTHS", "DO_NONE", "DO_DRAW", "DO_CHARWIDTH",
+ "DO_FALSE_CHARPATH", "DO_TRUE_CHARPATH",
+ "DO_FALSE_CHARBOXPATH", "DO_TRUE_CHARBOXPATH",
+ "INTERVENE", "RETURN_WIDTH"
+ };
+ int i;
+ gs_text_enum_t *pte;
+ int code;
+
+ dmputs(dev->memory,"text_begin(");
+ for (i = 0; i < countof(tags); ++i)
+ if (text->operation & (1 << i)) {
+ if (tags[i])
+ dmprintf1(dev->memory,"%s ", tags[i]);
+ else
+ dmprintf1(dev->memory,"%d? ", i);
+ }
+ dmprintf1(dev->memory,"font=%s\n text=(", font->font_name.chars);
+ if (text->operation & TEXT_FROM_SINGLE_CHAR)
+ dmprintf1(dev->memory,"0x%lx", (ulong)text->data.d_char);
+ else if (text->operation & TEXT_FROM_SINGLE_GLYPH)
+ dmprintf1(dev->memory,"0x%lx", (ulong)text->data.d_glyph);
+ else
+ for (i = 0; i < text->size; ++i) {
+ if (text->operation & TEXT_FROM_STRING)
+ dmputc(dev->memory,text->data.bytes[i]);
+ else
+ dmprintf1(dev->memory,"0x%lx ",
+ (text->operation & TEXT_FROM_GLYPHS ?
+ (ulong)text->data.glyphs[i] :
+ (ulong)text->data.chars[i]));
+ }
+ dmprintf1(dev->memory,")\n size=%u", text->size);
+ if (text->operation & TEXT_ADD_TO_ALL_WIDTHS)
+ dmprintf2(dev->memory,", delta_all=(%g,%g)", text->delta_all.x, text->delta_all.y);
+ if (text->operation & TEXT_ADD_TO_SPACE_WIDTH) {
+ dmprintf3(dev->memory,", space=0x%lx, delta_space=(%g,%g)",
+ (text->operation & TEXT_FROM_GLYPHS ?
+ (ulong)text->space.s_glyph : (ulong)text->space.s_char),
+ text->delta_space.x, text->delta_space.y);
+ }
+ if (text->operation & TEXT_REPLACE_WIDTHS) {
+ dmputs(dev->memory,"\n widths=");
+ for (i = 0; i < text->widths_size; ++i) {
+ if (text->x_widths)
+ dmprintf1(dev->memory,"(%g,", text->x_widths[i]);
+ else
+ dmputs(dev->memory,"(,");
+ if (text->y_widths)
+ dmprintf1(dev->memory,"%g)",
+ (text->y_widths == text->x_widths ?
+ text->y_widths[++i] : text->y_widths[i]));
+ else
+ dmputs(dev->memory,")");
+ }
+ }
+ if (text->operation & TEXT_DO_DRAW)
+ trace_drawing_color(dev->memory,", ", pdcolor);
+ /*
+ * We can't do it if CHAR*PATH or INTERVENE, or if (RETURN_WIDTH and not
+ * REPLACE_WIDTHS and we can't get the widths from the font).
+ */
+ if (text->operation &
+ (TEXT_DO_FALSE_CHARPATH | TEXT_DO_TRUE_CHARPATH |
+ TEXT_DO_FALSE_CHARBOXPATH | TEXT_DO_TRUE_CHARBOXPATH |
+ TEXT_INTERVENE)
+ )
+ goto dflt;
+ rc_alloc_struct_1(pte, gs_text_enum_t, &st_gs_text_enum, memory,
+ goto dflt, "trace_text_begin");
+ code = gs_text_enum_init(pte, &trace_text_procs, dev, pis, text, font,
+ path, pdcolor, pcpath, memory);
+ if (code < 0)
+ goto dfree;
+ if ((text->operation & (TEXT_DO_CHARWIDTH | TEXT_RETURN_WIDTH)) &&
+ !(text->operation & TEXT_REPLACE_WIDTHS)
+ ) {
+ /*
+ * Get the widths from the font. This code is mostly copied from
+ * the pdfwrite driver, and should probably be shared with it.
+ * ****** WRONG IF Type 0 FONT ******
+ */
+ int i;
+ gs_point w;
+ double scale = (font->FontType == ft_TrueType ? 0.001 : 1.0);
+ gs_fixed_point origin;
+ gs_point dpt;
+ int num_spaces = 0;
+
+ if (!(text->operation & TEXT_FROM_STRING))
+ goto dfree; /* can't handle yet */
+ if (gx_path_current_point(path, &origin) < 0)
+ goto dfree;
+ w.x = 0, w.y = 0;
+ for (i = 0; i < text->size; ++i) {
+ gs_char ch = text->data.bytes[i];
+ int wmode = font->WMode;
+ gs_glyph glyph =
+ ((gs_font_base *)font)->procs.encode_char(font, ch,
+ GLYPH_SPACE_NAME);
+ gs_glyph_info_t info;
+
+ if (glyph != gs_no_glyph &&
+ (code = font->procs.glyph_info(font, glyph, NULL,
+ GLYPH_INFO_WIDTH0 << wmode,
+ &info)) >= 0
+ ) {
+ w.x += info.width[wmode].x;
+ w.y += info.width[wmode].y;
+ } else
+ goto dfree;
+ if (ch == text->space.s_char)
+ ++num_spaces;
+ }
+ gs_distance_transform(w.x * scale, w.y * scale,
+ &font->FontMatrix, &dpt);
+ if (text->operation & TEXT_ADD_TO_ALL_WIDTHS) {
+ int num_chars = text->size;
+
+ dpt.x += text->delta_all.x * num_chars;
+ dpt.y += text->delta_all.y * num_chars;
+ }
+ if (text->operation & TEXT_ADD_TO_SPACE_WIDTH) {
+ dpt.x += text->delta_space.x * num_spaces;
+ dpt.y += text->delta_space.y * num_spaces;
+ }
+ pte->returned.total_width = dpt;
+ gs_distance_transform(dpt.x, dpt.y, &ctm_only(pis), &dpt);
+ code = gx_path_add_point(path,
+ origin.x + float2fixed(dpt.x),
+ origin.y + float2fixed(dpt.y));
+ if (code < 0)
+ goto dfree;
+ }
+ dmputs(dev->memory,")\n");
+ *ppenum = pte;
+ return 0;
+ dfree:
+ gs_free_object(memory, pte, "trace_text_begin");
+ dflt:
+ dmputs(dev->memory,") DEFAULTED\n");
+ return gx_default_text_begin(dev, pis, text, font, path, pdcolor,
+ pcpath, memory, ppenum);
+}
+
+/* ---------------- The device definition ---------------- */
+
+#define TRACE_DEVICE_BODY(dname, ncomp, depth, map_rgb_color, map_color_rgb, map_cmyk_color, map_rgb_alpha_color)\
+ std_device_dci_body(gx_device, 0, dname,\
+ DEFAULT_WIDTH_10THS * X_DPI / 10,\
+ DEFAULT_HEIGHT_10THS * Y_DPI / 10,\
+ X_DPI, Y_DPI, ncomp, depth,\
+ (1 << (depth / ncomp)) - 1,\
+ (ncomp > 1 ? (1 << (depth / ncomp)) - 1 : 0),\
+ 1 << (depth / ncomp),\
+ (ncomp > 1 ? 1 << (depth / ncomp) : 1)),\
+{\
+ NULL, /* open_device */\
+ NULL, /* get_initial_matrix */\
+ NULL, /* sync_output */\
+ NULL, /* output_page */\
+ NULL, /* close_device */\
+ map_rgb_color, /* differs */\
+ map_color_rgb, /* differs */\
+ trace_fill_rectangle,\
+ NULL, /* tile_rectangle */\
+ trace_copy_mono,\
+ trace_copy_color,\
+ NULL, /* draw_line */\
+ NULL, /* get_bits */\
+ NULL, /* get_params */\
+ NULL, /* put_params */\
+ map_cmyk_color, /* differs */\
+ NULL, /* get_xfont_procs */\
+ NULL, /* get_xfont_device */\
+ map_rgb_alpha_color, /* differs */\
+ gx_page_device_get_page_device,\
+ NULL, /* get_alpha_bits */\
+ trace_copy_alpha,\
+ NULL, /* get_band */\
+ NULL, /* copy_rop */\
+ trace_fill_path,\
+ trace_stroke_path,\
+ trace_fill_mask,\
+ trace_fill_trapezoid,\
+ trace_fill_parallelogram,\
+ trace_fill_triangle,\
+ trace_draw_thin_line,\
+ NULL, /* begin_image */\
+ NULL, /* image_data */\
+ NULL, /* end_image */\
+ trace_strip_tile_rectangle,\
+ trace_strip_copy_rop,\
+ NULL, /* get_clipping_box */\
+ trace_begin_typed_image,\
+ NULL, /* get_bits_rectangle */\
+ NULL, /* map_color_rgb_alpha */\
+ NULL, /* create_compositor */\
+ NULL, /* get_hardware_params */\
+ trace_text_begin,\
+ NULL, /* finish_copydevice */\
+ NULL, /* begin_transparency_group */\
+ NULL, /* end_transparency_group */\
+ NULL, /* begin_transparency_mask */\
+ NULL, /* end_transparency_mask */\
+ NULL, /* discard_transparency_layer */\
+ NULL, /* get_color_mapping_procs */\
+ NULL, /* get_color_comp_index */\
+ NULL, /* encode_color */\
+ NULL, /* decode_color */\
+ NULL, /* pattern_manage */\
+ NULL, /* fill_rectangle_hl_color */\
+ NULL, /* include_color_space */\
+ NULL, /* fill_linear_color_scanline */\
+ NULL, /* fill_linear_color_trapezoid */\
+ NULL, /* fill_linear_color_triangle */\
+ NULL, /* update_spot_equivalent_colors */\
+ NULL, /* ret_devn_params */\
+ NULL, /* fillpage */\
+ NULL, /* push_transparency_state */\
+ NULL, /* pop_transparency_state */\
+ NULL, /* put_image */\
+ NULL, /* dev_spec_op */\
+ NULL, /* copy_planes */\
+ NULL, /* get_profile */\
+ NULL, /* set_graphics_type_tag */\
+ trace_strip_copy_rop2\
+}
+
+const gx_device gs_tr_mono_device = {
+ TRACE_DEVICE_BODY("tr_mono", 1, 1,
+ gx_default_b_w_map_rgb_color,
+ gx_default_b_w_map_color_rgb, NULL, NULL)
+};
+
+const gx_device gs_tr_rgb_device = {
+ TRACE_DEVICE_BODY("tr_rgb", 3, 24,
+ gx_default_rgb_map_rgb_color,
+ gx_default_rgb_map_color_rgb, NULL, NULL)
+};
+
+const gx_device gs_tr_cmyk_device = {
+ TRACE_DEVICE_BODY("tr_cmyk", 4, 4,
+ NULL, cmyk_1bit_map_color_rgb,
+ cmyk_1bit_map_cmyk_color, NULL)
+};
diff --git a/devices/gdevtsep.c b/devices/gdevtsep.c
new file mode 100644
index 000000000..6e649e878
--- /dev/null
+++ b/devices/gdevtsep.c
@@ -0,0 +1,2820 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* tiffgray device: 8-bit Gray uncompressed TIFF device
+ * tiff32nc device: 32-bit CMYK uncompressed TIFF device
+ * tiffsep device: Generate individual TIFF gray files for each separation
+ * as well as a 'composite' 32-bit CMYK for the page.
+ * tiffsep1 device: Generate individual TIFF 1-bit files for each separation
+ * tiffscaled device:Mono TIFF device (error-diffused downscaled output from
+ * 8-bit Gray internal rendering)
+ * tiffscaled8 device:Greyscale TIFF device (downscaled output from
+ * 8-bit Gray internal rendering)
+ * tiffscaled24 device:24-bit RGB TIFF device (dithered downscaled output
+ * from 24-bit RGB internal rendering)
+ * tiffscaled32 device:32-bit CMYK TIFF device (downscaled output
+ * from 32-bit CMYK internal rendering)
+ * tiffscaled4 device:4-bit CMYK TIFF device (dithered downscaled output
+ * from 32-bit CMYK internal rendering)
+ */
+
+#include "stdint_.h" /* for tiff.h */
+#include "gdevtifs.h"
+#include "gdevprn.h"
+#include "gdevdevn.h"
+#include "gsequivc.h"
+#include "gxdht.h"
+#include "gxiodev.h"
+#include "stdio_.h"
+#include "ctype_.h"
+#include "gxgetbit.h"
+#include "gdevppla.h"
+#include "gxdownscale.h"
+#include "gp.h"
+#include "gstiffio.h"
+
+/*
+ * Some of the code in this module is based upon the gdevtfnx.c module.
+ * gdevtfnx.c has the following message:
+ * Thanks to Alan Barclay <alan@escribe.co.uk> for donating the original
+ * version of this code to Ghostscript.
+ */
+
+/* ------ The device descriptors ------ */
+
+/* Default X and Y resolution */
+#define X_DPI 72
+#define Y_DPI 72
+
+/* ------ The tiffgray device ------ */
+
+static dev_proc_print_page(tiffgray_print_page);
+
+/* FIXME: From initial analysis this is NOT safe for bg_printing, but might be fixable */
+
+static const gx_device_procs tiffgray_procs =
+prn_color_params_procs(tiff_open, gdev_prn_output_page_seekable, tiff_close,
+ gx_default_gray_map_rgb_color, gx_default_gray_map_color_rgb,
+ tiff_get_params, tiff_put_params);
+
+const gx_device_tiff gs_tiffgray_device = {
+ prn_device_body(gx_device_tiff, tiffgray_procs, "tiffgray",
+ DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
+ X_DPI, Y_DPI,
+ 0, 0, 0, 0, /* Margins */
+ 1, 8, 255, 0, 256, 0, tiffgray_print_page),
+ arch_is_big_endian /* default to native endian (i.e. use big endian iff the platform is so*/,
+ false, /* default to *not* bigtiff */
+ COMPRESSION_NONE,
+ TIFF_DEFAULT_STRIP_SIZE,
+ TIFF_DEFAULT_DOWNSCALE,
+ 0, /* Adjust size */
+ 1 /* MinFeatureSize */
+};
+
+/* ------ The tiffscaled device ------ */
+
+static dev_proc_print_page(tiffscaled_print_page);
+
+static const gx_device_procs tiffscaled_procs =
+prn_color_params_procs(tiff_open,
+ gdev_prn_output_page_seekable,
+ tiff_close,
+ gx_default_gray_map_rgb_color,
+ gx_default_gray_map_color_rgb,
+ tiff_get_params_downscale,
+ tiff_put_params_downscale);
+
+const gx_device_tiff gs_tiffscaled_device = {
+ prn_device_body(gx_device_tiff,
+ tiffscaled_procs,
+ "tiffscaled",
+ DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
+ 600, 600, /* 600 dpi by default */
+ 0, 0, 0, 0, /* Margins */
+ 1, /* num components */
+ 8, /* bits per sample */
+ 255, 0, 256, 0,
+ tiffscaled_print_page),
+ arch_is_big_endian,/* default to native endian (i.e. use big endian iff the platform is so */
+ false, /* default to not bigtiff */
+ COMPRESSION_NONE,
+ TIFF_DEFAULT_STRIP_SIZE,
+ TIFF_DEFAULT_DOWNSCALE,
+ 0, /* Adjust size */
+ 1 /* MinFeatureSize */
+};
+
+/* ------ The tiffscaled8 device ------ */
+
+static dev_proc_print_page(tiffscaled8_print_page);
+
+static const gx_device_procs tiffscaled8_procs =
+prn_color_params_procs(tiff_open,
+ gdev_prn_output_page_seekable,
+ tiff_close,
+ gx_default_gray_map_rgb_color,
+ gx_default_gray_map_color_rgb,
+ tiff_get_params_downscale,
+ tiff_put_params_downscale);
+
+const gx_device_tiff gs_tiffscaled8_device = {
+ prn_device_body(gx_device_tiff,
+ tiffscaled8_procs,
+ "tiffscaled8",
+ DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
+ 600, 600, /* 600 dpi by default */
+ 0, 0, 0, 0, /* Margins */
+ 1, /* num components */
+ 8, /* bits per sample */
+ 255, 0, 256, 0,
+ tiffscaled8_print_page),
+ arch_is_big_endian,/* default to native endian (i.e. use big endian iff the platform is so */
+ false, /* default to not bigtiff */
+ COMPRESSION_NONE,
+ TIFF_DEFAULT_STRIP_SIZE,
+ TIFF_DEFAULT_DOWNSCALE,
+ 0, /* Adjust size */
+ 1 /* MinFeatureSize */
+};
+
+/* ------ The tiffscaled24 device ------ */
+
+static dev_proc_print_page(tiffscaled24_print_page);
+
+static const gx_device_procs tiffscaled24_procs =
+prn_color_params_procs(tiff_open,
+ gdev_prn_output_page_seekable,
+ tiff_close,
+ gx_default_rgb_map_rgb_color,
+ gx_default_rgb_map_color_rgb,
+ tiff_get_params_downscale,
+ tiff_put_params_downscale);
+
+const gx_device_tiff gs_tiffscaled24_device = {
+ prn_device_body(gx_device_tiff,
+ tiffscaled24_procs,
+ "tiffscaled24",
+ DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
+ 600, 600, /* 600 dpi by default */
+ 0, 0, 0, 0, /* Margins */
+ 3, /* num components */
+ 24, /* bits per sample */
+ 255, 255, 256, 256,
+ tiffscaled24_print_page),
+ arch_is_big_endian,/* default to native endian (i.e. use big endian iff the platform is so */
+ false, /* default to not bigtiff */
+ COMPRESSION_NONE,
+ TIFF_DEFAULT_STRIP_SIZE,
+ TIFF_DEFAULT_DOWNSCALE,
+ 0, /* Adjust size */
+ 1 /* MinFeatureSize */
+};
+
+/* ------ The tiffscaled32 device ------ */
+
+static dev_proc_print_page(tiffscaled32_print_page);
+
+static const gx_device_procs tiffscaled32_procs = {
+ tiff_open, NULL, NULL, gdev_prn_output_page_seekable, tiff_close,
+ NULL, cmyk_8bit_map_color_cmyk, NULL, NULL, NULL, NULL, NULL, NULL,
+ tiff_get_params_downscale, tiff_put_params_downscale,
+ cmyk_8bit_map_cmyk_color, NULL, NULL, NULL, gx_page_device_get_page_device
+};
+
+const gx_device_tiff gs_tiffscaled32_device = {
+ prn_device_body(gx_device_tiff,
+ tiffscaled32_procs,
+ "tiffscaled32",
+ DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
+ 600, 600, /* 600 dpi by default */
+ 0, 0, 0, 0, /* Margins */
+ 4, /* num components */
+ 32, /* bits per sample */
+ 255, 255, 256, 256,
+ tiffscaled32_print_page),
+ arch_is_big_endian,/* default to native endian (i.e. use big endian iff the platform is so */
+ false, /* default to not bigtiff */
+ COMPRESSION_NONE,
+ TIFF_DEFAULT_STRIP_SIZE,
+ TIFF_DEFAULT_DOWNSCALE,
+ 0, /* Adjust size */
+ 1 /* MinFeatureSize */
+};
+
+/* ------ The tiffscaled4 device ------ */
+
+static dev_proc_print_page(tiffscaled4_print_page);
+
+static const gx_device_procs tiffscaled4_procs = {
+ tiff_open, NULL, NULL, gdev_prn_output_page_seekable, tiff_close,
+ NULL, cmyk_8bit_map_color_cmyk, NULL, NULL, NULL, NULL, NULL, NULL,
+ tiff_get_params_downscale, tiff_put_params_downscale,
+ cmyk_8bit_map_cmyk_color, NULL, NULL, NULL, gx_page_device_get_page_device
+};
+
+const gx_device_tiff gs_tiffscaled4_device = {
+ prn_device_body(gx_device_tiff,
+ tiffscaled4_procs,
+ "tiffscaled4",
+ DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
+ 600, 600, /* 600 dpi by default */
+ 0, 0, 0, 0, /* Margins */
+ 4, /* num components */
+ 32, /* bits per sample */
+ 255, 255, 256, 256,
+ tiffscaled4_print_page),
+ arch_is_big_endian,/* default to native endian (i.e. use big endian iff the platform is so */
+ false, /* default to not bigtiff */
+ COMPRESSION_NONE,
+ TIFF_DEFAULT_STRIP_SIZE,
+ TIFF_DEFAULT_DOWNSCALE,
+ 0, /* Adjust size */
+ 1 /* MinFeatureSize */
+};
+
+/* ------ Private functions ------ */
+
+static void
+tiff_set_gray_fields(gx_device_printer *pdev, TIFF *tif,
+ unsigned short bits_per_sample,
+ int compression,
+ long max_strip_size)
+{
+ TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, bits_per_sample);
+ TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK);
+ TIFFSetField(tif, TIFFTAG_FILLORDER, FILLORDER_MSB2LSB);
+ TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, 1);
+
+ tiff_set_compression(pdev, tif, compression, max_strip_size);
+}
+
+static int
+tiffgray_print_page(gx_device_printer * pdev, FILE * file)
+{
+ gx_device_tiff *const tfdev = (gx_device_tiff *)pdev;
+ int code;
+
+ if (tfdev->Compression==COMPRESSION_NONE &&
+ pdev->height > ((unsigned long) 0xFFFFFFFF - ftell(file))/(pdev->width)) /* note width is never 0 in print_page */
+ return_error(gs_error_rangecheck); /* this will overflow 32 bits */
+
+ code = gdev_tiff_begin_page(tfdev, file);
+ if (code < 0)
+ return code;
+
+ tiff_set_gray_fields(pdev, tfdev->tif, 8, tfdev->Compression, tfdev->MaxStripSize);
+
+ return tiff_print_page(pdev, tfdev->tif, 0);
+}
+
+static int
+tiffscaled_print_page(gx_device_printer * pdev, FILE * file)
+{
+ gx_device_tiff *const tfdev = (gx_device_tiff *)pdev;
+ int code;
+
+ code = gdev_tiff_begin_page(tfdev, file);
+ if (code < 0)
+ return code;
+
+ tiff_set_gray_fields(pdev, tfdev->tif, 1, tfdev->Compression, tfdev->MaxStripSize);
+
+ return tiff_downscale_and_print_page(pdev, tfdev->tif,
+ tfdev->DownScaleFactor,
+ tfdev->MinFeatureSize,
+ tfdev->AdjustWidth,
+ 1, 1);
+}
+
+static int
+tiffscaled8_print_page(gx_device_printer * pdev, FILE * file)
+{
+ gx_device_tiff *const tfdev = (gx_device_tiff *)pdev;
+ int code;
+
+ code = gdev_tiff_begin_page(tfdev, file);
+ if (code < 0)
+ return code;
+
+ tiff_set_gray_fields(pdev, tfdev->tif, 8, tfdev->Compression, tfdev->MaxStripSize);
+
+ return tiff_downscale_and_print_page(pdev, tfdev->tif,
+ tfdev->DownScaleFactor,
+ tfdev->MinFeatureSize,
+ tfdev->AdjustWidth,
+ 8, 1);
+}
+
+static void
+tiff_set_rgb_fields(gx_device_tiff *tfdev)
+{
+ /* Put in a switch statement in case we want to have others */
+ switch (tfdev->icc_struct->device_profile[0]->data_cs) {
+ case gsRGB:
+ TIFFSetField(tfdev->tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB);
+ break;
+ case gsCIELAB:
+ TIFFSetField(tfdev->tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_ICCLAB);
+ break;
+ default:
+ TIFFSetField(tfdev->tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB);
+ break;
+ }
+ TIFFSetField(tfdev->tif, TIFFTAG_FILLORDER, FILLORDER_MSB2LSB);
+ TIFFSetField(tfdev->tif, TIFFTAG_SAMPLESPERPIXEL, 3);
+
+ tiff_set_compression((gx_device_printer *)tfdev, tfdev->tif,
+ tfdev->Compression, tfdev->MaxStripSize);
+}
+
+
+static int
+tiffscaled24_print_page(gx_device_printer * pdev, FILE * file)
+{
+ gx_device_tiff *const tfdev = (gx_device_tiff *)pdev;
+ int code;
+
+ code = gdev_tiff_begin_page(tfdev, file);
+ if (code < 0)
+ return code;
+
+ TIFFSetField(tfdev->tif, TIFFTAG_BITSPERSAMPLE, 8);
+ tiff_set_rgb_fields(tfdev);
+
+ return tiff_downscale_and_print_page(pdev, tfdev->tif,
+ tfdev->DownScaleFactor,
+ tfdev->MinFeatureSize,
+ tfdev->AdjustWidth,
+ 8, 3);
+}
+
+static void
+tiff_set_cmyk_fields(gx_device_printer *pdev, TIFF *tif,
+ short bits_per_sample,
+ uint16 compression,
+ long max_strip_size)
+{
+ TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, bits_per_sample);
+ TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_SEPARATED);
+ TIFFSetField(tif, TIFFTAG_FILLORDER, FILLORDER_MSB2LSB);
+ TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, 4);
+
+ tiff_set_compression(pdev, tif, compression, max_strip_size);
+}
+
+static int
+tiffscaled32_print_page(gx_device_printer * pdev, FILE * file)
+{
+ gx_device_tiff *const tfdev = (gx_device_tiff *)pdev;
+ int code;
+
+ code = gdev_tiff_begin_page(tfdev, file);
+ if (code < 0)
+ return code;
+
+ tiff_set_cmyk_fields(pdev,
+ tfdev->tif,
+ 8,
+ tfdev->Compression,
+ tfdev->MaxStripSize);
+
+ return tiff_downscale_and_print_page(pdev, tfdev->tif,
+ tfdev->DownScaleFactor,
+ tfdev->MinFeatureSize,
+ tfdev->AdjustWidth,
+ 8, 4);
+}
+
+static int
+tiffscaled4_print_page(gx_device_printer * pdev, FILE * file)
+{
+ gx_device_tiff *const tfdev = (gx_device_tiff *)pdev;
+ int code;
+
+ code = gdev_tiff_begin_page(tfdev, file);
+ if (code < 0)
+ return code;
+
+ tiff_set_cmyk_fields(pdev,
+ tfdev->tif,
+ 1,
+ tfdev->Compression,
+ tfdev->MaxStripSize);
+
+ return tiff_downscale_and_print_page(pdev, tfdev->tif,
+ tfdev->DownScaleFactor,
+ tfdev->MinFeatureSize,
+ tfdev->AdjustWidth,
+ 1, 4);
+}
+
+/* ------ The cmyk devices ------ */
+
+static dev_proc_print_page(tiffcmyk_print_page);
+
+#define cmyk_procs(p_map_color_rgb, p_map_cmyk_color)\
+ tiff_open, NULL, NULL, gdev_prn_output_page_seekable, tiff_close,\
+ NULL, p_map_color_rgb, NULL, NULL, NULL, NULL, NULL, NULL,\
+ tiff_get_params, tiff_put_params,\
+ p_map_cmyk_color, NULL, NULL, NULL, gx_page_device_get_page_device
+
+/* 8-bit-per-plane separated CMYK color. */
+
+static const gx_device_procs tiffcmyk_procs = {
+ cmyk_procs(cmyk_8bit_map_color_cmyk, cmyk_8bit_map_cmyk_color)
+};
+
+const gx_device_tiff gs_tiff32nc_device = {
+ prn_device_body(gx_device_tiff, tiffcmyk_procs, "tiff32nc",
+ DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
+ X_DPI, Y_DPI,
+ 0, 0, 0, 0, /* Margins */
+ 4, 32, 255, 255, 256, 256, tiffcmyk_print_page),
+ arch_is_big_endian /* default to native endian (i.e. use big endian iff the platform is so*/,
+ false, /* default to not bigtiff */
+ COMPRESSION_NONE,
+ TIFF_DEFAULT_STRIP_SIZE,
+ TIFF_DEFAULT_DOWNSCALE,
+ 0, /* Adjust size */
+ 1 /* MinFeatureSize */
+};
+
+/* 16-bit-per-plane separated CMYK color. */
+
+static const gx_device_procs tiff64nc_procs = {
+ cmyk_procs(cmyk_16bit_map_color_cmyk, cmyk_16bit_map_cmyk_color)
+};
+
+const gx_device_tiff gs_tiff64nc_device = {
+ prn_device_body(gx_device_tiff, tiff64nc_procs, "tiff64nc",
+ DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
+ X_DPI, Y_DPI,
+ 0, 0, 0, 0, /* Margins */
+ 4, 64, 255, 255, 256, 256, tiffcmyk_print_page),
+ arch_is_big_endian /* default to native endian (i.e. use big endian iff the platform is so*/,
+ false, /* default to not bigtiff */
+ COMPRESSION_NONE,
+ TIFF_DEFAULT_STRIP_SIZE,
+ TIFF_DEFAULT_DOWNSCALE,
+ 0, /* Adjust size */
+ 1 /* MinFeatureSize */
+};
+
+/* ------ Private functions ------ */
+
+static int
+tiffcmyk_print_page(gx_device_printer * pdev, FILE * file)
+{
+ gx_device_tiff *const tfdev = (gx_device_tiff *)pdev;
+ int code;
+
+ if (tfdev->Compression==COMPRESSION_NONE &&
+ pdev->height > ((unsigned long) 0xFFFFFFFF - ftell(file))/(pdev->width)) /* note width is never 0 in print_page */
+ return_error(gs_error_rangecheck); /* this will overflow 32 bits */
+
+ code = gdev_tiff_begin_page(tfdev, file);
+ if (code < 0)
+ return code;
+
+ tiff_set_cmyk_fields(pdev,
+ tfdev->tif,
+ pdev->color_info.depth / pdev->color_info.num_components,
+ tfdev->Compression,
+ tfdev->MaxStripSize);
+
+ return tiff_print_page(pdev, tfdev->tif, 0);
+}
+
+/* ---------- The tiffsep device ------------ */
+
+#define NUM_CMYK_COMPONENTS 4
+#define MAX_COLOR_VALUE 255 /* We are using 8 bits per colorant */
+
+/* The device descriptor */
+static dev_proc_open_device(tiffsep_prn_open);
+static dev_proc_close_device(tiffsep_prn_close);
+static dev_proc_get_params(tiffsep_get_params);
+static dev_proc_put_params(tiffsep_put_params);
+static dev_proc_print_page(tiffsep_print_page);
+static dev_proc_get_color_mapping_procs(tiffsep_get_color_mapping_procs);
+static dev_proc_get_color_comp_index(tiffsep_get_color_comp_index);
+static dev_proc_encode_color(tiffsep_encode_color);
+static dev_proc_decode_color(tiffsep_decode_color);
+static dev_proc_update_spot_equivalent_colors(tiffsep_update_spot_equivalent_colors);
+static dev_proc_ret_devn_params(tiffsep_ret_devn_params);
+static dev_proc_open_device(tiffsep1_prn_open);
+static dev_proc_close_device(tiffsep1_prn_close);
+static dev_proc_put_params(tiffsep1_put_params);
+static dev_proc_print_page(tiffsep1_print_page);
+static dev_proc_fill_path(sep1_fill_path);
+
+/* common to tiffsep and tiffsepo1 */
+static dev_proc_print_page_copies(tiffseps_print_page_copies);
+static dev_proc_output_page(tiffseps_output_page);
+
+#define tiffsep_devices_common\
+ gx_device_common;\
+ gx_prn_device_common;\
+ /* tiff state for separation files */\
+ FILE *sep_file[GX_DEVICE_COLOR_MAX_COMPONENTS];\
+ TIFF *tiff[GX_DEVICE_COLOR_MAX_COMPONENTS]; \
+ bool BigEndian; /* true = big endian; false = little endian */\
+ bool UseBigTIFF; /* true = output bigtiff, false don't */ \
+ bool PrintSpotCMYK; /* true = print CMYK equivalents for spot inks; false = do nothing */\
+ uint16 Compression; /* for the separation files, same values as
+ TIFFTAG_COMPRESSION */\
+ bool close_files;\
+ long MaxStripSize;\
+ long DownScaleFactor;\
+ long MinFeatureSize;\
+ long BitsPerComponent;\
+ int max_spots;\
+ bool lock_colorants;\
+ gs_devn_params devn_params; /* DeviceN generated parameters */\
+ equivalent_cmyk_color_params equiv_cmyk_colors
+
+/*
+ * A structure definition for a DeviceN type device
+ */
+typedef struct tiffsep_device_s {
+ tiffsep_devices_common;
+ FILE *comp_file; /* Underlying file for tiff_comp */
+ TIFF *tiff_comp; /* tiff file for comp file */
+ bool warning_given;
+} tiffsep_device;
+
+/* threshold array structure */
+typedef struct threshold_array_s {
+ int dheight, dwidth;
+ byte *dstart;
+} threshold_array_t;
+
+typedef struct tiffsep1_device_s {
+ tiffsep_devices_common;
+ bool warning_given;
+ threshold_array_t thresholds[GX_DEVICE_COLOR_MAX_COMPONENTS + 1]; /* one extra for Default */
+ dev_t_proc_fill_path((*fill_path), gx_device); /* we forward to here */
+} tiffsep1_device;
+
+/* GC procedures */
+static
+ENUM_PTRS_WITH(tiffsep_device_enum_ptrs, tiffsep_device *pdev)
+{
+ if (index == 0)
+ ENUM_RETURN(pdev->devn_params.compressed_color_list);
+ index--;
+ if (index == 0)
+ ENUM_RETURN(pdev->devn_params.pdf14_compressed_color_list);
+ index--;
+ if (index < pdev->devn_params.separations.num_separations)
+ ENUM_RETURN(pdev->devn_params.separations.names[index].data);
+ ENUM_PREFIX(st_device_printer,
+ pdev->devn_params.separations.num_separations);
+ return 0;
+}
+ENUM_PTRS_END
+
+static RELOC_PTRS_WITH(tiffsep_device_reloc_ptrs, tiffsep_device *pdev)
+{
+ RELOC_PREFIX(st_device_printer);
+ {
+ int i;
+
+ for (i = 0; i < pdev->devn_params.separations.num_separations; ++i) {
+ RELOC_PTR(tiffsep_device, devn_params.separations.names[i].data);
+ }
+ }
+ RELOC_PTR(tiffsep_device, devn_params.compressed_color_list);
+ RELOC_PTR(tiffsep_device, devn_params.pdf14_compressed_color_list);
+}
+RELOC_PTRS_END
+
+/* Even though tiffsep_device_finalize is the same as gx_device_finalize, */
+/* we need to implement it separately because st_composite_final */
+/* declares all 3 procedures as private. */
+static void
+tiffsep_device_finalize(const gs_memory_t *cmem, void *vpdev)
+{
+ tiffsep_device * const pdevn = (tiffsep_device *) vpdev;
+
+ /* for safety */
+ pdevn->close_files = true;
+
+ /* We need to deallocate the compressed_color_list.
+ and the names. */
+ devn_free_params((gx_device*) vpdev);
+ gx_device_finalize(cmem, vpdev);
+}
+
+gs_private_st_composite_final(st_tiffsep_device, tiffsep_device,
+ "tiffsep_device", tiffsep_device_enum_ptrs, tiffsep_device_reloc_ptrs,
+ tiffsep_device_finalize);
+
+/*
+ * Macro definition for tiffsep device procedures
+ */
+#define sep_device_procs(open, close, encode_color, decode_color, update_spot_colors,put_params, fill_path) \
+{ open,\
+ gx_default_get_initial_matrix,\
+ NULL, /* sync_output */\
+ tiffseps_output_page, /* output_page */\
+ close, /* close */\
+ NULL, /* map_rgb_color - not used */\
+ tiffsep_decode_color, /* map_color_rgb */\
+ NULL, /* fill_rectangle */\
+ NULL, /* tile_rectangle */\
+ NULL, /* copy_mono */\
+ NULL, /* copy_color */\
+ NULL, /* draw_line */\
+ NULL, /* get_bits */\
+ tiffsep_get_params, /* get_params */\
+ put_params, /* put_params */\
+ NULL, /* map_cmyk_color - not used */\
+ NULL, /* get_xfont_procs */\
+ NULL, /* get_xfont_device */\
+ NULL, /* map_rgb_alpha_color */\
+ gx_page_device_get_page_device, /* get_page_device */\
+ NULL, /* get_alpha_bits */\
+ NULL, /* copy_alpha */\
+ NULL, /* get_band */\
+ NULL, /* copy_rop */\
+ fill_path, /* fill_path */\
+ NULL, /* stroke_path */\
+ NULL, /* fill_mask */\
+ NULL, /* fill_trapezoid */\
+ NULL, /* fill_parallelogram */\
+ NULL, /* fill_triangle */\
+ NULL, /* draw_thin_line */\
+ NULL, /* begin_image */\
+ NULL, /* image_data */\
+ NULL, /* end_image */\
+ NULL, /* strip_tile_rectangle */\
+ NULL, /* strip_copy_rop */\
+ NULL, /* get_clipping_box */\
+ NULL, /* begin_typed_image */\
+ NULL, /* get_bits_rectangle */\
+ NULL, /* map_color_rgb_alpha */\
+ NULL, /* create_compositor */\
+ NULL, /* get_hardware_params */\
+ NULL, /* text_begin */\
+ NULL, /* finish_copydevice */\
+ NULL, /* begin_transparency_group */\
+ NULL, /* end_transparency_group */\
+ NULL, /* begin_transparency_mask */\
+ NULL, /* end_transparency_mask */\
+ NULL, /* discard_transparency_layer */\
+ tiffsep_get_color_mapping_procs,/* get_color_mapping_procs */\
+ tiffsep_get_color_comp_index, /* get_color_comp_index */\
+ encode_color, /* encode_color */\
+ decode_color, /* decode_color */\
+ NULL, /* pattern_manage */\
+ NULL, /* fill_rectangle_hl_color */\
+ NULL, /* include_color_space */\
+ NULL, /* fill_linear_color_scanline */\
+ NULL, /* fill_linear_color_trapezoid */\
+ NULL, /* fill_linear_color_triangle */\
+ update_spot_colors, /* update_spot_equivalent_colors */\
+ tiffsep_ret_devn_params /* ret_devn_params */\
+}
+
+
+#define tiffsep_devices_body(dtype, procs, dname, ncomp, pol, depth, mg, mc, sl, cn, print_page, print_page_copies, compr)\
+ std_device_full_body_type_extended(dtype, &procs, dname,\
+ &st_tiffsep_device,\
+ (int)((long)(DEFAULT_WIDTH_10THS) * (X_DPI) / 10),\
+ (int)((long)(DEFAULT_HEIGHT_10THS) * (Y_DPI) / 10),\
+ X_DPI, Y_DPI,\
+ ncomp, /* MaxComponents */\
+ ncomp, /* NumComp */\
+ pol, /* Polarity */\
+ depth, 0, /* Depth, GrayIndex */\
+ mg, mc, /* MaxGray, MaxColor */\
+ mg + 1, mc + 1, /* DitherGray, DitherColor */\
+ sl, /* Linear & Separable? */\
+ cn, /* Process color model name */\
+ 0, 0, /* offsets */\
+ 0, 0, 0, 0 /* margins */\
+ ),\
+ prn_device_body_rest2_(print_page, print_page_copies, -1),\
+ { 0 }, /* tiff state for separation files */\
+ { 0 }, /* separation files */\
+ arch_is_big_endian /* true = big endian; false = little endian */,\
+ false, /* UseBigTIFF */\
+ false, /* PrintSpotCMYK */\
+ compr /* COMPRESSION_* */,\
+ true, /* close_files */ \
+ TIFF_DEFAULT_STRIP_SIZE,/* MaxStripSize */\
+ 1, /* DownScaleFactor */\
+ 0, /* MinFeatureSize */\
+ 8, /* BitsPerComponent */\
+ GS_SOFT_MAX_SPOTS, /* max_spots */\
+ false /* Colorants not locked */
+
+#define GCIB (ARCH_SIZEOF_GX_COLOR_INDEX * 8)
+
+/*
+ * TIFF devices with CMYK process color model and spot color support.
+ */
+static const gx_device_procs spot_cmyk_procs =
+ sep_device_procs(tiffsep_prn_open, tiffsep_prn_close, tiffsep_encode_color, tiffsep_decode_color,
+ tiffsep_update_spot_equivalent_colors, tiffsep_put_params, NULL);
+
+static const gx_device_procs spot1_cmyk_procs =
+ sep_device_procs(tiffsep1_prn_open, tiffsep1_prn_close, tiffsep_encode_color, tiffsep_decode_color,
+ tiffsep_update_spot_equivalent_colors, tiffsep1_put_params, sep1_fill_path);
+
+const tiffsep_device gs_tiffsep_device =
+{
+ tiffsep_devices_body(tiffsep_device, spot_cmyk_procs, "tiffsep", ARCH_SIZEOF_GX_COLOR_INDEX, GX_CINFO_POLARITY_SUBTRACTIVE, GCIB, MAX_COLOR_VALUE, MAX_COLOR_VALUE, GX_CINFO_SEP_LIN, "DeviceCMYK", tiffsep_print_page, tiffseps_print_page_copies, COMPRESSION_LZW),
+ /* devn_params specific parameters */
+ { 8, /* Not used - Bits per color */
+ DeviceCMYKComponents, /* Names of color model colorants */
+ 4, /* Number colorants for CMYK */
+ 0, /* MaxSeparations has not been specified */
+ -1, /* PageSpotColors has not been specified */
+ {0}, /* SeparationNames */
+ 0, /* SeparationOrder names */
+ {0, 1, 2, 3, 4, 5, 6, 7 } /* Initial component SeparationOrder */
+ },
+ { true }, /* equivalent CMYK colors for spot colors */
+};
+
+const tiffsep1_device gs_tiffsep1_device =
+{
+ tiffsep_devices_body(tiffsep1_device, spot1_cmyk_procs, "tiffsep1", ARCH_SIZEOF_GX_COLOR_INDEX, GX_CINFO_POLARITY_SUBTRACTIVE, GCIB, MAX_COLOR_VALUE, MAX_COLOR_VALUE, GX_CINFO_SEP_LIN, "DeviceCMYK", tiffsep1_print_page, tiffseps_print_page_copies, COMPRESSION_CCITTFAX4),
+ /* devn_params specific parameters */
+ { 8, /* Not used - Bits per color */
+ DeviceCMYKComponents, /* Names of color model colorants */
+ 4, /* Number colorants for CMYK */
+ 0, /* MaxSeparations has not been specified */
+ -1, /* PageSpotColors has not been specified */
+ {0}, /* SeparationNames */
+ 0, /* SeparationOrder names */
+ {0, 1, 2, 3, 4, 5, 6, 7 } /* Initial component SeparationOrder */
+ },
+ { true }, /* equivalent CMYK colors for spot colors */
+ false, /* warning_given */
+ { {0} }, /* threshold arrays */
+ 0, /* fill_path */
+};
+
+#undef NC
+#undef SL
+#undef ENCODE_COLOR
+#undef DECODE_COLOR
+
+static const uint32_t bit_order[32]={
+#if arch_is_big_endian
+ 0x80000000, 0x40000000, 0x20000000, 0x10000000, 0x08000000, 0x04000000, 0x02000000, 0x01000000,
+ 0x00800000, 0x00400000, 0x00200000, 0x00100000, 0x00080000, 0x00040000, 0x00020000, 0x00010000,
+ 0x00008000, 0x00004000, 0x00002000, 0x00001000, 0x00000800, 0x00000400, 0x00000200, 0x00000100,
+ 0x00000080, 0x00000040, 0x00000020, 0x00000010, 0x00000008, 0x00000004, 0x00000002, 0x00000001
+#else
+ 0x00000080, 0x00000040, 0x00000020, 0x00000010, 0x00000008, 0x00000004, 0x00000002, 0x00000001,
+ 0x00008000, 0x00004000, 0x00002000, 0x00001000, 0x00000800, 0x00000400, 0x00000200, 0x00000100,
+ 0x00800000, 0x00400000, 0x00200000, 0x00100000, 0x00080000, 0x00040000, 0x00020000, 0x00010000,
+ 0x80000000, 0x40000000, 0x20000000, 0x10000000, 0x08000000, 0x04000000, 0x02000000, 0x01000000
+#endif
+ };
+
+/*
+ * The following procedures are used to map the standard color spaces into
+ * the color components for the tiffsep device.
+ */
+static void
+tiffsep_gray_cs_to_cm(gx_device * dev, frac gray, frac out[])
+{
+ int * map = ((tiffsep_device *) dev)->devn_params.separation_order_map;
+
+ gray_cs_to_devn_cm(dev, map, gray, out);
+}
+
+static void
+tiffsep_rgb_cs_to_cm(gx_device * dev, const gs_imager_state *pis,
+ frac r, frac g, frac b, frac out[])
+{
+ int * map = ((tiffsep_device *) dev)->devn_params.separation_order_map;
+
+ rgb_cs_to_devn_cm(dev, map, pis, r, g, b, out);
+}
+
+static void
+tiffsep_cmyk_cs_to_cm(gx_device * dev,
+ frac c, frac m, frac y, frac k, frac out[])
+{
+ const gs_devn_params *devn = tiffsep_ret_devn_params(dev);
+ const int *map = devn->separation_order_map;
+ int j;
+
+ if (devn->num_separation_order_names > 0) {
+ /* This is to set only those that we are using */
+ for (j = 0; j < devn->num_separation_order_names; j++) {
+ switch (map[j]) {
+ case 0 :
+ out[0] = c;
+ break;
+ case 1:
+ out[1] = m;
+ break;
+ case 2:
+ out[2] = y;
+ break;
+ case 3:
+ out[3] = k;
+ break;
+ default:
+ break;
+ }
+ }
+ } else {
+ cmyk_cs_to_devn_cm(dev, map, c, m, y, k, out);
+ }
+}
+
+static const gx_cm_color_map_procs tiffsep_cm_procs = {
+ tiffsep_gray_cs_to_cm,
+ tiffsep_rgb_cs_to_cm,
+ tiffsep_cmyk_cs_to_cm
+};
+
+/*
+ * These are the handlers for returning the list of color space
+ * to color model conversion routines.
+ */
+static const gx_cm_color_map_procs *
+tiffsep_get_color_mapping_procs(const gx_device * dev)
+{
+ return &tiffsep_cm_procs;
+}
+
+/*
+ * Encode a list of colorant values into a gx_color_index_value.
+ * With 32 bit gx_color_index values, we simply pack values.
+ */
+static gx_color_index
+tiffsep_encode_color(gx_device *dev, const gx_color_value colors[])
+{
+ int bpc = ((tiffsep_device *)dev)->devn_params.bitspercomponent;
+ gx_color_index color = 0;
+ int i = 0;
+ int ncomp = dev->color_info.num_components;
+ COLROUND_VARS;
+
+ COLROUND_SETUP(bpc);
+ for (; i < ncomp; i++) {
+ color <<= bpc;
+ color |= COLROUND_ROUND(colors[i]);
+ }
+ return (color == gx_no_color_index ? color ^ 1 : color);
+}
+
+/*
+ * Decode a gx_color_index value back to a list of colorant values.
+ * With 32 bit gx_color_index values, we simply pack values.
+ */
+static int
+tiffsep_decode_color(gx_device * dev, gx_color_index color, gx_color_value * out)
+{
+ int bpc = ((tiffsep_device *)dev)->devn_params.bitspercomponent;
+ int drop = sizeof(gx_color_value) * 8 - bpc;
+ int mask = (1 << bpc) - 1;
+ int i = 0;
+ int ncomp = dev->color_info.num_components;
+
+ for (; i < ncomp; i++) {
+ out[ncomp - i - 1] = (gx_color_value) ((color & mask) << drop);
+ color >>= bpc;
+ }
+ return 0;
+}
+
+/*
+ * Device proc for updating the equivalent CMYK color for spot colors.
+ */
+static int
+tiffsep_update_spot_equivalent_colors(gx_device * dev, const gs_state * pgs)
+{
+ tiffsep_device * pdev = (tiffsep_device *)dev;
+
+ update_spot_equivalent_cmyk_colors(dev, pgs,
+ &pdev->devn_params, &pdev->equiv_cmyk_colors);
+ return 0;
+}
+
+/*
+ * Device proc for returning a pointer to DeviceN parameter structure
+ */
+static gs_devn_params *
+tiffsep_ret_devn_params(gx_device * dev)
+{
+ tiffsep_device * pdev = (tiffsep_device *)dev;
+
+ return &pdev->devn_params;
+}
+
+/* Get parameters. We provide a default CRD. */
+static int
+tiffsep_get_params(gx_device * pdev, gs_param_list * plist)
+{
+ tiffsep_device * const pdevn = (tiffsep_device *) pdev;
+ int code = gdev_prn_get_params(pdev, plist);
+ int ecode = code;
+ gs_param_string comprstr;
+
+ if (code < 0)
+ return code;
+
+ code = devn_get_params(pdev, plist,
+ &(((tiffsep_device *)pdev)->devn_params),
+ &(((tiffsep_device *)pdev)->equiv_cmyk_colors));
+ if (code < 0)
+ return code;
+
+ if ((code = param_write_bool(plist, "BigEndian", &pdevn->BigEndian)) < 0)
+ ecode = code;
+ if ((code = tiff_compression_param_string(&comprstr, pdevn->Compression)) < 0 ||
+ (code = param_write_string(plist, "Compression", &comprstr)) < 0)
+ ecode = code;
+ if ((code = param_write_long(plist, "MaxStripSize", &pdevn->MaxStripSize)) < 0)
+ ecode = code;
+ if ((code = param_write_long(plist, "DownScaleFactor", &pdevn->DownScaleFactor)) < 0)
+ ecode = code;
+ if ((code = param_write_long(plist, "MinFeatureSize", &pdevn->MinFeatureSize)) < 0)
+ ecode = code;
+ if ((code = param_write_long(plist, "BitsPerComponent", &pdevn->BitsPerComponent)) < 0)
+ ecode = code;
+ if ((code = param_write_int(plist, "MaxSpots", &pdevn->max_spots)) < 0)
+ ecode = code;
+ if ((code = param_write_bool(plist, "LockColorants", &pdevn->lock_colorants)) < 0)
+ ecode = code;
+ if ((code = param_write_bool(plist, "PrintSpotCMYK", &pdevn->PrintSpotCMYK)) < 0)
+ ecode = code;
+
+ return ecode;
+}
+
+/* Set parameters. We allow setting the number of bits per component. */
+static int
+tiffsep_put_params(gx_device * pdev, gs_param_list * plist)
+{
+ tiffsep_device * const pdevn = (tiffsep_device *) pdev;
+ int code;
+ const char *param_name;
+ gs_param_string comprstr;
+ bool save_close_files = pdevn->close_files;
+ long downscale = pdevn->DownScaleFactor;
+ long mfs = pdevn->MinFeatureSize;
+ long bpc = pdevn->BitsPerComponent;
+ int max_spots = pdevn->max_spots;
+
+ /* Read BigEndian option as bool */
+ switch (code = param_read_bool(plist, (param_name = "BigEndian"), &pdevn->BigEndian)) {
+ default:
+ param_signal_error(plist, param_name, code);
+ return code;
+ case 0:
+ case 1:
+ break;
+ }
+ switch (code = param_read_bool(plist, (param_name = "PrintSpotCMYK"), &pdevn->PrintSpotCMYK)) {
+ default:
+ param_signal_error(plist, param_name, code);
+ return code;
+ case 0:
+ case 1:
+ break;
+ }
+
+ switch (code = param_read_long(plist, (param_name = "BitsPerComponent"), &bpc)) {
+ case 0:
+ if ((bpc == 1) || (bpc == 8)) {
+ pdevn->BitsPerComponent = bpc;
+ break;
+ }
+ code = gs_error_rangecheck;
+ default:
+ param_signal_error(plist, param_name, code);
+ return code;
+ case 1:
+ break;
+ }
+
+ /* Read Compression */
+ switch (code = param_read_string(plist, (param_name = "Compression"), &comprstr)) {
+ case 0:
+ if ((code = tiff_compression_id(&pdevn->Compression, &comprstr)) < 0) {
+
+ errprintf(pdevn->memory, "Unknown compression setting\n");
+
+ param_signal_error(plist, param_name, code);
+ return code;
+ }
+
+ if (!tiff_compression_allowed(pdevn->Compression, pdevn->BitsPerComponent)) {
+ errprintf(pdevn->memory, "Invalid compression setting for this bitdepth\n");
+
+ param_signal_error(plist, param_name, gs_error_rangecheck);
+ return gs_error_rangecheck;
+ }
+ break;
+ case 1:
+ break;
+ default:
+ param_signal_error(plist, param_name, code);
+ return code;
+ }
+ switch (code = param_read_long(plist, (param_name = "MaxStripSize"), &pdevn->MaxStripSize)) {
+ case 0:
+ /*
+ * Strip must be large enough to accommodate a raster line.
+ * If the max strip size is too small, we still write a single
+ * line per strip rather than giving an error.
+ */
+ if (pdevn->MaxStripSize >= 0)
+ break;
+ code = gs_error_rangecheck;
+ default:
+ param_signal_error(plist, param_name, code);
+ return code;
+ case 1:
+ break;
+ }
+ switch (code = param_read_long(plist,
+ (param_name = "DownScaleFactor"),
+ &downscale)) {
+ case 0:
+ if (downscale <= 0)
+ downscale = 1;
+ pdevn->DownScaleFactor = downscale;
+ break;
+ case 1:
+ break;
+ default:
+ param_signal_error(plist, param_name, code);
+ return code;
+ }
+ switch (code = param_read_long(plist, (param_name = "MinFeatureSize"), &mfs)) {
+ case 0:
+ if ((mfs >= 0) && (mfs <= 4)) {
+ pdevn->MinFeatureSize = mfs;
+ break;
+ }
+ code = gs_error_rangecheck;
+ case 1:
+ break;
+ default:
+ param_signal_error(plist, param_name, code);
+ return code;
+ }
+ switch (code = param_read_bool(plist, (param_name = "LockColorants"),
+ &(pdevn->lock_colorants))) {
+ case 0:
+ break;
+ case 1:
+ break;
+ default:
+ param_signal_error(plist, param_name, code);
+ return code;
+ }
+ switch (code = param_read_int(plist, (param_name = "MaxSpots"), &max_spots)) {
+ case 0:
+ if ((max_spots >= 0) || (max_spots <= GS_CLIENT_COLOR_MAX_COMPONENTS-4)) {
+ pdevn->max_spots = max_spots;
+ break;
+ }
+ emprintf1(pdev->memory, "MaxSpots must be between 0 and %d\n",
+ GS_CLIENT_COLOR_MAX_COMPONENTS-4);
+ code = gs_error_rangecheck;
+ case 1:
+ break;
+ default:
+ param_signal_error(plist, param_name, code);
+ return code;
+ }
+
+ pdevn->close_files = false;
+
+ code = devn_printer_put_params(pdev, plist,
+ &(pdevn->devn_params), &(pdevn->equiv_cmyk_colors));
+
+ pdevn->close_files = save_close_files;
+
+ return(code);
+}
+
+static int
+tiffsep1_put_params(gx_device * pdev, gs_param_list * plist)
+{
+ tiffsep1_device * const tfdev = (tiffsep1_device *) pdev;
+ int code;
+
+ if ((code = tiffsep_put_params(pdev, plist)) < 0)
+ return code;
+
+ /* put_params may have changed the fill_path proc -- we need it set to ours */
+ if (pdev->procs.fill_path != sep1_fill_path) {
+ tfdev->fill_path = pdev->procs.fill_path;
+ pdev->procs.fill_path = sep1_fill_path;
+ }
+ return code;
+
+}
+
+/* common print_page_copies method for both tiff separation devices */
+static int
+tiffseps_print_page_copies(gx_device_printer * pdev, FILE * prn_stream, int num_copies)
+{
+ int i = 1;
+ int code = 0;
+ (void) prn_stream;
+
+ for (; i < num_copies; ++i) {
+ code = (*pdev->printer_procs.print_page) (pdev, NULL);
+ if (code < 0)
+ return code;
+
+ pdev->PageCount++;
+
+ }
+ /* Print the last (or only) page. */
+ pdev->PageCount -= num_copies - 1;
+ return (*pdev->printer_procs.print_page) (pdev, NULL);
+}
+
+int
+tiffseps_output_page(gx_device *pdev, int num_copies, int flush)
+{
+ gx_device_printer * const ppdev = (gx_device_printer *)pdev;
+ int outcode = 0, closecode = 0, endcode;
+
+ if (num_copies > 0 || !flush) {
+ /* Print the accumulated page description. */
+ outcode = (*ppdev->printer_procs.print_page_copies)(ppdev, ppdev->file, num_copies);
+ }
+ endcode = (PRINTER_IS_CLIST(ppdev) &&
+ !((gx_device_clist_common *)ppdev)->do_not_open_or_close_bandfiles ?
+ clist_finish_page(pdev, flush) : 0);
+
+ if (outcode < 0)
+ return (outcode);
+ if (closecode < 0)
+ return (closecode);
+ if (endcode < 0)
+ return (endcode);
+
+ endcode = gx_finish_output_page(pdev, num_copies, flush);
+ return (endcode);
+}
+
+static void build_comp_to_sep_map(tiffsep_device *, short *);
+static int number_output_separations(int, int, int, int);
+static int create_separation_file_name(tiffsep_device *, char *, uint, int, bool);
+static byte * threshold_from_order( gx_ht_order *, int *, int *, gs_memory_t *);
+static int sep1_ht_order_to_thresholds(gx_device *pdev, const gs_imager_state *pis);
+static void sep1_free_thresholds(tiffsep1_device *);
+dev_proc_fill_path(clist_fill_path);
+
+/* Open the tiffsep1 device. This will now be using planar buffers so that
+ we are not limited to 64 bit chunky */
+int
+tiffsep1_prn_open(gx_device * pdev)
+{
+ gx_device_printer *ppdev = (gx_device_printer *)pdev;
+ tiffsep1_device *pdev_sep = (tiffsep1_device *) pdev;
+ int code, k;
+
+ /* Use our own warning and error message handlers in libtiff */
+ tiff_set_handlers();
+
+ /* With planar the depth can be more than 64. Update the color
+ info to reflect the proper depth and number of planes */
+ pdev_sep->warning_given = false;
+ if (pdev_sep->devn_params.page_spot_colors >= 0) {
+ pdev->color_info.num_components =
+ (pdev_sep->devn_params.page_spot_colors
+ + pdev_sep->devn_params.num_std_colorant_names);
+ if (pdev->color_info.num_components > pdev->color_info.max_components)
+ pdev->color_info.num_components = pdev->color_info.max_components;
+ } else {
+ /* We do not know how many spots may occur on the page.
+ For this reason we go ahead and allocate the maximum that we
+ have available. Note, lack of knowledge only occurs in the case
+ of PS files. With PDF we know a priori the number of spot
+ colorants. */
+ int num_comp = pdev_sep->max_spots + 4; /* Spots + CMYK */
+ if (num_comp > GS_CLIENT_COLOR_MAX_COMPONENTS)
+ num_comp = GS_CLIENT_COLOR_MAX_COMPONENTS;
+ pdev->color_info.num_components = num_comp;
+ pdev->color_info.max_components = num_comp;
+ }
+ /* Push this to the max amount as a default if someone has not set it */
+ if (pdev_sep->devn_params.num_separation_order_names == 0)
+ for (k = 0; k < GS_CLIENT_COLOR_MAX_COMPONENTS; k++) {
+ pdev_sep->devn_params.separation_order_map[k] = k;
+ }
+ pdev->color_info.depth = pdev->color_info.num_components *
+ pdev_sep->devn_params.bitspercomponent;
+ pdev->color_info.separable_and_linear = GX_CINFO_SEP_LIN;
+ code = gdev_prn_open_planar(pdev, true);
+ while (pdev->child)
+ pdev = pdev->child;
+ ppdev = (gx_device_printer *)pdev;
+ pdev_sep = (tiffsep1_device *)pdev;
+
+ ppdev->file = NULL;
+ pdev->icc_struct->supports_devn = true;
+
+ /* gdev_prn_open_planae may have changed the fill_path proc -- we need it set to ours */
+ if (pdev->procs.fill_path != sep1_fill_path) {
+ pdev_sep->fill_path = pdev->procs.fill_path;
+ pdev->procs.fill_path = sep1_fill_path;
+ }
+
+ return code;
+}
+
+/* Close the tiffsep device */
+int
+tiffsep1_prn_close(gx_device * pdev)
+{
+ tiffsep1_device * const tfdev = (tiffsep1_device *) pdev;
+ int num_dev_comp = tfdev->color_info.num_components;
+ int num_std_colorants = tfdev->devn_params.num_std_colorant_names;
+ int num_order = tfdev->devn_params.num_separation_order_names;
+ int num_spot = tfdev->devn_params.separations.num_separations;
+ char *name= NULL;
+ int code = gdev_prn_close(pdev);
+ short map_comp_to_sep[GX_DEVICE_COLOR_MAX_COMPONENTS];
+ int comp_num;
+ int num_comp = number_output_separations(num_dev_comp, num_std_colorants,
+ num_order, num_spot);
+ const char *fmt;
+ gs_parsed_file_name_t parsed;
+
+ if (code < 0)
+ return code;
+
+ name = (char *)gs_alloc_bytes(pdev->memory, gp_file_name_sizeof, "tiffsep1_prn_close(name)");
+ if (!name)
+ return_error(gs_error_VMerror);
+
+ code = gx_parse_output_file_name(&parsed, &fmt, tfdev->fname,
+ strlen(tfdev->fname), pdev->memory);
+ if (code < 0) {
+ goto done;
+ }
+
+ /* If we are doing separate pages, delete the old default file */
+ if (parsed.iodev == iodev_default(pdev->memory)) { /* filename includes "%nnd" */
+ char *compname = (char *)gs_alloc_bytes(pdev->memory, gp_file_name_sizeof, "tiffsep1_prn_close(compname)");
+ if (!compname) {
+ code = gs_note_error(gs_error_VMerror);
+ goto done;
+ }
+
+ if (fmt) {
+ long count1 = pdev->PageCount;
+
+ while (*fmt != 'l' && *fmt != '%')
+ --fmt;
+ if (*fmt == 'l')
+ gs_sprintf(compname, parsed.fname, count1);
+ else
+ gs_sprintf(compname, parsed.fname, (int)count1);
+ parsed.iodev->procs.delete_file(parsed.iodev, compname);
+ } else {
+ parsed.iodev->procs.delete_file(parsed.iodev, tfdev->fname);
+ }
+ gs_free_object(pdev->memory, compname, "tiffsep1_prn_close(compname)");
+ }
+
+ if (tfdev->close_files) {
+ build_comp_to_sep_map((tiffsep_device *)tfdev, map_comp_to_sep);
+ /* Close the separation files */
+ for (comp_num = 0; comp_num < num_comp; comp_num++ ) {
+ if (tfdev->sep_file[comp_num] != NULL) {
+ int sep_num = map_comp_to_sep[comp_num];
+
+ code = create_separation_file_name((tiffsep_device *)tfdev, name,
+ gp_file_name_sizeof, sep_num, true);
+ if (code < 0) {
+ goto done;
+ }
+ code = gx_device_close_output_file(pdev, name, tfdev->sep_file[comp_num]);
+ if (code < 0) {
+ goto done;
+ }
+ tfdev->sep_file[comp_num] = NULL;
+ }
+ if (tfdev->tiff[comp_num]) {
+ TIFFCleanup(tfdev->tiff[comp_num]);
+ tfdev->tiff[comp_num] = NULL;
+ }
+ }
+ }
+ /* If we have thresholds, free them and clear the pointers */
+ if( tfdev->thresholds[0].dstart != NULL) {
+ sep1_free_thresholds(tfdev);
+ }
+
+done:
+
+ if (name)
+ gs_free_object(pdev->memory, name, "tiffsep1_prn_close(name)");
+ return code;
+}
+
+
+static int
+sep1_fill_path(gx_device * pdev, const gs_imager_state * pis,
+ gx_path * ppath, const gx_fill_params * params,
+ const gx_device_color * pdevc, const gx_clip_path * pcpath)
+{
+ tiffsep1_device * const tfdev = (tiffsep1_device *)pdev;
+
+ /* If we haven't already converted the ht into thresholds, do it now */
+ if( tfdev->thresholds[0].dstart == NULL) {
+ int code = sep1_ht_order_to_thresholds(pdev, pis);
+
+ if (code < 0)
+ return code;
+ }
+ return (tfdev->fill_path)( pdev, pis, ppath, params, pdevc, pcpath);
+}
+
+/*
+ * This routine will check to see if the color component name match those
+ * that are available amoung the current device's color components.
+ *
+ * Parameters:
+ * dev - pointer to device data structure.
+ * pname - pointer to name (zero termination not required)
+ * nlength - length of the name
+ *
+ * This routine returns a positive value (0 to n) which is the device colorant
+ * number if the name is found. It returns GX_DEVICE_COLOR_MAX_COMPONENTS if
+ * the colorant is not being used due to a SeparationOrder device parameter.
+ * It returns a negative value if not found.
+ */
+static int
+tiffsep_get_color_comp_index(gx_device * dev, const char * pname,
+ int name_size, int component_type)
+{
+ tiffsep_device * pdev = (tiffsep_device *) dev;
+ int index;
+
+ if (strncmp(pname, "None", name_size) == 0) return -1;
+ index = devn_get_color_comp_index(dev,
+ &(pdev->devn_params), &(pdev->equiv_cmyk_colors),
+ pname, name_size, component_type, ENABLE_AUTO_SPOT_COLORS);
+ /* This is a one shot deal. That is it will simply post a notice once that
+ some colorants will be converted due to a limit being reached. It will
+ not list names of colorants since then I would need to keep track of
+ which ones I have already mentioned. Also, if someone is fooling with
+ num_order, then this warning is not given since they should know what
+ is going on already */
+ if (index < 0 && component_type == SEPARATION_NAME &&
+ pdev->warning_given == false &&
+ pdev->devn_params.num_separation_order_names == 0) {
+ dmlprintf(dev->memory, "**** Max spot colorants reached.\n");
+ dmlprintf(dev->memory, "**** Some colorants will be converted to equivalent CMYK values.\n");
+ dmlprintf(dev->memory, "**** If this is a Postscript file, try using the -dMaxSpots= option.\n");
+ pdev->warning_given = true;
+ }
+ return index;
+}
+
+/*
+ * There can be a conflict if a separation name is used as part of the file
+ * name for a separation output file. PostScript and PDF do not restrict
+ * the characters inside separation names. However most operating systems
+ * have some sort of restrictions. For instance: /, \, and : have special
+ * meaning under Windows. This implies that there should be some sort of
+ * escape sequence for special characters. This routine exists as a place
+ * to put the handling of that escaping. However it is not actually
+ * implemented. Instead we just map them to '_'.
+ */
+static void
+copy_separation_name(tiffsep_device * pdev,
+ char * buffer, int max_size, int sep_num)
+{
+ int sep_size = pdev->devn_params.separations.names[sep_num].size;
+ const byte *p = pdev->devn_params.separations.names[sep_num].data;
+ int i;
+
+ /* If name is too long then clip it. */
+ if (sep_size > max_size - 1)
+ sep_size = max_size - 1;
+ /* Avoid the use of '%' in names here. This is checked for here, rather
+ * than in gp_file_name_good_char, as this is NOT a requirement of the
+ * underlying file system, but rather a requirement of the code handling
+ * the separation names (where % is interpreted as a format specifier).
+ */
+ for (i=0; i < sep_size; i++)
+ buffer[i] = (gp_file_name_good_char(p[i]) && p[i] != '%') ? p[i] : '_';
+ buffer[sep_size] = 0; /* Terminate string */
+}
+
+/*
+ * Determine the length of the base file name. If the file name includes
+ * the extension '.tif', then we remove it from the length of the file
+ * name.
+ */
+static int
+length_base_file_name(tiffsep_device * pdev)
+{
+ int base_filename_length = strlen(pdev->fname);
+
+#define REMOVE_TIF_FROM_BASENAME 1
+#if REMOVE_TIF_FROM_BASENAME
+ if (base_filename_length > 4 &&
+ pdev->fname[base_filename_length - 4] == '.' &&
+ toupper(pdev->fname[base_filename_length - 3]) == 'T' &&
+ toupper(pdev->fname[base_filename_length - 2]) == 'I' &&
+ toupper(pdev->fname[base_filename_length - 1]) == 'F')
+ base_filename_length -= 4;
+#endif
+#undef REMOVE_TIF_FROM_BASENAME
+
+ return base_filename_length;
+}
+
+/*
+ * Create a name for a separation file.
+ */
+static int
+create_separation_file_name(tiffsep_device * pdev, char * buffer,
+ uint max_size, int sep_num, bool use_sep_name)
+{
+ uint base_filename_length = length_base_file_name(pdev);
+
+ /*
+ * In most cases it is more convenient if we append '.tif' to the end
+ * of the file name.
+ */
+#define APPEND_TIF_TO_NAME 1
+#define SUFFIX_SIZE (4 * APPEND_TIF_TO_NAME)
+
+ memcpy(buffer, pdev->fname, base_filename_length);
+ buffer[base_filename_length++] = use_sep_name ? '(' : '.';
+ buffer[base_filename_length] = 0; /* terminate the string */
+
+ if (sep_num < pdev->devn_params.num_std_colorant_names) {
+ if (max_size < strlen(pdev->devn_params.std_colorant_names[sep_num]))
+ return_error(gs_error_rangecheck);
+ strcat(buffer, pdev->devn_params.std_colorant_names[sep_num]);
+ }
+ else {
+ sep_num -= pdev->devn_params.num_std_colorant_names;
+ if (use_sep_name) {
+ copy_separation_name(pdev, buffer + base_filename_length,
+ max_size - SUFFIX_SIZE - 2, sep_num);
+ } else {
+ /* Max of 10 chars in %d format */
+ if (max_size < base_filename_length + 11)
+ return_error(gs_error_rangecheck);
+ gs_sprintf(buffer + base_filename_length, "s%d", sep_num);
+ }
+ }
+ if (use_sep_name)
+ strcat(buffer, ")");
+
+#if APPEND_TIF_TO_NAME
+ if (max_size < strlen(buffer) + SUFFIX_SIZE)
+ return_error(gs_error_rangecheck);
+ strcat(buffer, ".tif");
+#endif
+ return 0;
+}
+
+/*
+ * Determine the number of output separations for the tiffsep device.
+ *
+ * There are several factors which affect the number of output separations
+ * for the tiffsep device.
+ *
+ * Due to limitations on the size of a gx_color_index, we are limited to a
+ * maximum of 8 colors per pass. Thus the tiffsep device is set to 8
+ * components. However this is not usually the number of actual separation
+ * files to be created.
+ *
+ * If the SeparationOrder parameter has been specified, then we use it to
+ * select the number and which separation files are created.
+ *
+ * If the SeparationOrder parameter has not been specified, then we use the
+ * nuber of process colors (CMYK) and the number of spot colors unless we
+ * exceed the 8 component maximum for the device.
+ *
+ * Note: Unlike most other devices, the tiffsep device will accept more than
+ * four spot colors. However the extra spot colors will not be imaged
+ * unless they are selected by the SeparationOrder parameter. (This does
+ * allow the user to create more than 8 separations by a making multiple
+ * passes and using the SeparationOrder parameter.)
+*/
+static int
+number_output_separations(int num_dev_comp, int num_std_colorants,
+ int num_order, int num_spot)
+{
+ int num_comp = num_std_colorants + num_spot;
+
+ if (num_comp > num_dev_comp)
+ num_comp = num_dev_comp;
+ if (num_order)
+ num_comp = num_order;
+ return num_comp;
+}
+
+/*
+ * This routine creates a list to map the component number to a separation number.
+ * Values less than 4 refer to the CMYK colorants. Higher values refer to a
+ * separation number.
+ *
+ * This is the inverse of the separation_order_map.
+ */
+static void
+build_comp_to_sep_map(tiffsep_device * pdev, short * map_comp_to_sep)
+{
+ int num_sep = pdev->devn_params.separations.num_separations;
+ int num_std_colorants = pdev->devn_params.num_std_colorant_names;
+ int sep_num;
+ int num_channels;
+
+ /* since both proc colors and spot colors are packed in same encoded value we
+ need to have this limit */
+
+ num_channels =
+ ( (num_std_colorants + num_sep) < (GX_DEVICE_COLOR_MAX_COMPONENTS) ? (num_std_colorants + num_sep) : (GX_DEVICE_COLOR_MAX_COMPONENTS) );
+
+ for (sep_num = 0; sep_num < num_channels; sep_num++) {
+ int comp_num = pdev->devn_params.separation_order_map[sep_num];
+
+ if (comp_num >= 0 && comp_num < GX_DEVICE_COLOR_MAX_COMPONENTS)
+ map_comp_to_sep[comp_num] = sep_num;
+ }
+}
+
+/* Open the tiffsep device. This will now be using planar buffers so that
+ we are not limited to 64 bit chunky */
+int
+tiffsep_prn_open(gx_device * pdev)
+{
+ gx_device_printer *ppdev = (gx_device_printer *)pdev;
+ tiffsep_device *pdev_sep = (tiffsep_device *) pdev;
+ int code, k;
+ bool force_pdf, limit_icc, force_ps;
+ cmm_dev_profile_t *profile_struct;
+
+ /* Use our own warning and error message handlers in libtiff */
+ tiff_set_handlers();
+
+ /* There are 2 approaches to the use of a DeviceN ICC output profile.
+ One is to simply limit our device to only output the colorants
+ defined in the output ICC profile. The other is to use the
+ DeviceN ICC profile to color manage those N colorants and
+ to let any other separations pass through unmolested. The define
+ LIMIT_TO_ICC sets the option to limit our device to only the ICC
+ colorants defined by -sICCOutputColors (or to the ones that are used
+ as default names if ICCOutputColors is not used). The pass through option
+ (LIMIT_TO_ICC set to 0) makes life a bit more difficult since we don't
+ know if the page_spot_colors overlap with any spot colorants that exist
+ in the DeviceN ICC output profile. Hence we don't know how many planes
+ to use for our device. This is similar to the issue when processing
+ a PostScript file. So that I remember, the cases are
+ DeviceN Profile? limit_icc Result
+ 0 0 force_pdf 0 force_ps 0 (no effect)
+ 0 0 force_pdf 0 force_ps 0 (no effect)
+ 1 0 force_pdf 0 force_ps 1 (colorants not known)
+ 1 1 force_pdf 1 force_ps 0 (colorants known)
+ */
+#if LIMIT_TO_ICC
+ limit_icc = true;
+#else
+ limit_icc = false;
+#endif
+ code = dev_proc(pdev, get_profile)((gx_device *)pdev, &profile_struct);
+ if (profile_struct->spotnames == NULL) {
+ force_pdf = false;
+ force_ps = false;
+ } else {
+ if (limit_icc) {
+ force_pdf = true;
+ force_ps = false;
+ } else {
+ force_pdf = false;
+ force_ps = true;
+ }
+ }
+ /* With planar the depth can be more than 64. Update the color
+ info to reflect the proper depth and number of planes */
+ pdev_sep->warning_given = false;
+ if ((pdev_sep->devn_params.page_spot_colors >= 0 || force_pdf) && !force_ps) {
+ if (force_pdf) {
+ /* Use the information that is in the ICC profle. We will be here
+ anytime that we have limited ourselves to a fixed number
+ of colorants specified by the DeviceN ICC profile */
+ pdev->color_info.num_components =
+ (pdev_sep->devn_params.separations.num_separations
+ + pdev_sep->devn_params.num_std_colorant_names);
+ if (pdev->color_info.num_components > pdev->color_info.max_components)
+ pdev->color_info.num_components = pdev->color_info.max_components;
+ /* Limit us only to the ICC colorants */
+ pdev->color_info.max_components = pdev->color_info.num_components;
+ } else {
+ /* Do not allow the spot count to update if we have specified the
+ colorants already */
+ if (!(pdev_sep->lock_colorants)) {
+ pdev->color_info.num_components =
+ (pdev_sep->devn_params.page_spot_colors
+ + pdev_sep->devn_params.num_std_colorant_names);
+ if (pdev->color_info.num_components > pdev->color_info.max_components)
+ pdev->color_info.num_components = pdev->color_info.max_components;
+ }
+ }
+ } else {
+ /* We do not know how many spots may occur on the page.
+ For this reason we go ahead and allocate the maximum that we
+ have available. Note, lack of knowledge only occurs in the case
+ of PS files. With PDF we know a priori the number of spot
+ colorants. */
+ if (!(pdev_sep->lock_colorants)) {
+ int num_comp = pdev_sep->max_spots + 4; /* Spots + CMYK */
+ if (num_comp > GS_CLIENT_COLOR_MAX_COMPONENTS)
+ num_comp = GS_CLIENT_COLOR_MAX_COMPONENTS;
+ pdev->color_info.num_components = num_comp;
+ pdev->color_info.max_components = num_comp;
+ }
+ }
+ /* Push this to the max amount as a default if someone has not set it */
+ if (pdev_sep->devn_params.num_separation_order_names == 0)
+ for (k = 0; k < GS_CLIENT_COLOR_MAX_COMPONENTS; k++) {
+ pdev_sep->devn_params.separation_order_map[k] = k;
+ }
+ pdev->color_info.depth = pdev->color_info.num_components *
+ pdev_sep->devn_params.bitspercomponent;
+ pdev->color_info.separable_and_linear = GX_CINFO_SEP_LIN;
+ code = gdev_prn_open_planar(pdev, true);
+ while (pdev->child)
+ pdev = pdev->child;
+ ppdev = (gx_device_printer *)pdev;
+
+ ppdev->file = NULL;
+ pdev->icc_struct->supports_devn = true;
+ return code;
+}
+
+static int
+tiffsep_close_sep_file(tiffsep_device *tfdev, const char *fn, int comp_num)
+{
+ int code;
+
+ if (tfdev->tiff[comp_num]) {
+ TIFFCleanup(tfdev->tiff[comp_num]);
+ tfdev->tiff[comp_num] = NULL;
+ }
+
+ code = gx_device_close_output_file((gx_device *)tfdev,
+ fn,
+ tfdev->sep_file[comp_num]);
+ tfdev->sep_file[comp_num] = NULL;
+ tfdev->tiff[comp_num] = NULL;
+
+ return code;
+}
+
+static int
+tiffsep_close_comp_file(tiffsep_device *tfdev, const char *fn)
+{
+ int code;
+
+ if (tfdev->tiff_comp) {
+ TIFFCleanup(tfdev->tiff_comp);
+ tfdev->tiff_comp = NULL;
+ }
+
+ code = gx_device_close_output_file((gx_device *)tfdev,
+ fn,
+ tfdev->comp_file);
+ tfdev->comp_file = NULL;
+
+ return code;
+}
+
+/* Close the tiffsep device */
+int
+tiffsep_prn_close(gx_device * pdev)
+{
+ tiffsep_device * const pdevn = (tiffsep_device *) pdev;
+ int num_dev_comp = pdevn->color_info.num_components;
+ int num_std_colorants = pdevn->devn_params.num_std_colorant_names;
+ int num_order = pdevn->devn_params.num_separation_order_names;
+ int num_spot = pdevn->devn_params.separations.num_separations;
+ char *name = NULL;
+ int code;
+ int comp_num;
+ int num_comp = number_output_separations(num_dev_comp, num_std_colorants,
+ num_order, num_spot);
+
+ name = (char *)gs_alloc_bytes(pdev->memory, gp_file_name_sizeof, "tiffsep_prn_close(name)");
+ if (!name)
+ return_error(gs_error_VMerror);
+
+ if (pdevn->tiff_comp && pdevn->close_files) {
+ TIFFCleanup(pdevn->tiff_comp);
+ pdevn->tiff_comp = NULL;
+ }
+ code = gdev_prn_close(pdev);
+ if (code < 0) {
+ goto done;
+ }
+
+ if (pdevn->close_files) {
+ /* Close the separation files */
+ for (comp_num = 0; comp_num < num_comp; comp_num++ ) {
+ if (pdevn->sep_file[comp_num] != NULL) {
+ int sep_num = pdevn->devn_params.separation_order_map[comp_num];
+
+ code = create_separation_file_name(pdevn, name,
+ gp_file_name_sizeof, sep_num, true);
+ if (code < 0) {
+ goto done;
+ }
+ code = tiffsep_close_sep_file(pdevn, name, comp_num);
+ if (code < 0) {
+ goto done;
+ }
+ }
+ }
+ }
+
+done:
+
+ if (name)
+ gs_free_object(pdev->memory, name, "tiffsep_prn_close(name)");
+ return code;
+}
+
+/*
+ * Element for a map to convert colorants to a CMYK color.
+ */
+typedef struct cmyk_composite_map_s {
+ frac c, m, y, k;
+} cmyk_composite_map;
+
+/*
+ * Build the map to be used to create a CMYK equivalent to the current
+ * device components.
+ */
+static void
+build_cmyk_map(tiffsep_device * pdev, int num_comp,
+ cmyk_composite_map * cmyk_map)
+{
+ int comp_num;
+
+ for (comp_num = 0; comp_num < num_comp; comp_num++ ) {
+ int sep_num = pdev->devn_params.separation_order_map[comp_num];
+
+ cmyk_map[comp_num].c = cmyk_map[comp_num].m =
+ cmyk_map[comp_num].y = cmyk_map[comp_num].k = frac_0;
+ /* The tiffsep device has 4 standard colors: CMYK */
+ if (sep_num < pdev->devn_params.num_std_colorant_names) {
+ switch (sep_num) {
+ case 0: cmyk_map[comp_num].c = frac_1; break;
+ case 1: cmyk_map[comp_num].m = frac_1; break;
+ case 2: cmyk_map[comp_num].y = frac_1; break;
+ case 3: cmyk_map[comp_num].k = frac_1; break;
+ }
+ }
+ else {
+ sep_num -= pdev->devn_params.num_std_colorant_names;
+ if (pdev->equiv_cmyk_colors.color[sep_num].color_info_valid) {
+ cmyk_map[comp_num].c = pdev->equiv_cmyk_colors.color[sep_num].c;
+ cmyk_map[comp_num].m = pdev->equiv_cmyk_colors.color[sep_num].m;
+ cmyk_map[comp_num].y = pdev->equiv_cmyk_colors.color[sep_num].y;
+ cmyk_map[comp_num].k = pdev->equiv_cmyk_colors.color[sep_num].k;
+ }
+ }
+ }
+}
+
+/*
+ * Build a CMYK equivalent to a raster line from planar buffer
+ */
+static void
+build_cmyk_raster_line_fromplanar(gs_get_bits_params_t params, byte * dest,
+ int width, int num_comp,
+ cmyk_composite_map * cmyk_map, int num_order,
+ tiffsep_device * const tfdev)
+{
+ int pixel, comp_num;
+ uint temp, cyan, magenta, yellow, black;
+ cmyk_composite_map * cmyk_map_entry;
+
+ for (pixel = 0; pixel < width; pixel++) {
+ cmyk_map_entry = cmyk_map;
+ temp = *(params.data[tfdev->devn_params.separation_order_map[0]] + pixel);
+ cyan = cmyk_map_entry->c * temp;
+ magenta = cmyk_map_entry->m * temp;
+ yellow = cmyk_map_entry->y * temp;
+ black = cmyk_map_entry->k * temp;
+ cmyk_map_entry++;
+ for (comp_num = 1; comp_num < num_comp; comp_num++) {
+ temp =
+ *(params.data[tfdev->devn_params.separation_order_map[comp_num]] + pixel);
+ cyan += cmyk_map_entry->c * temp;
+ magenta += cmyk_map_entry->m * temp;
+ yellow += cmyk_map_entry->y * temp;
+ black += cmyk_map_entry->k * temp;
+ cmyk_map_entry++;
+ }
+ cyan /= frac_1;
+ magenta /= frac_1;
+ yellow /= frac_1;
+ black /= frac_1;
+ if (cyan > MAX_COLOR_VALUE)
+ cyan = MAX_COLOR_VALUE;
+ if (magenta > MAX_COLOR_VALUE)
+ magenta = MAX_COLOR_VALUE;
+ if (yellow > MAX_COLOR_VALUE)
+ yellow = MAX_COLOR_VALUE;
+ if (black > MAX_COLOR_VALUE)
+ black = MAX_COLOR_VALUE;
+ *dest++ = cyan;
+ *dest++ = magenta;
+ *dest++ = yellow;
+ *dest++ = black;
+ }
+}
+
+static void
+build_cmyk_raster_line_fromplanar_1bpc(gs_get_bits_params_t params, byte * dest,
+ int width, int num_comp,
+ cmyk_composite_map * cmyk_map, int num_order,
+ tiffsep_device * const tfdev)
+{
+ int pixel, comp_num;
+ uint temp, cyan, magenta, yellow, black;
+ cmyk_composite_map * cmyk_map_entry;
+
+ for (pixel = 0; pixel < width; pixel++) {
+ cmyk_map_entry = cmyk_map;
+ temp = *(params.data[tfdev->devn_params.separation_order_map[0]] + (pixel>>3));
+ temp = ((temp<<(pixel & 7))>>7) & 1;
+ cyan = cmyk_map_entry->c * temp;
+ magenta = cmyk_map_entry->m * temp;
+ yellow = cmyk_map_entry->y * temp;
+ black = cmyk_map_entry->k * temp;
+ cmyk_map_entry++;
+ for (comp_num = 1; comp_num < num_comp; comp_num++) {
+ temp =
+ *(params.data[tfdev->devn_params.separation_order_map[comp_num]] + (pixel>>3));
+ temp = ((temp<<(pixel & 7))>>7) & 1;
+ cyan += cmyk_map_entry->c * temp;
+ magenta += cmyk_map_entry->m * temp;
+ yellow += cmyk_map_entry->y * temp;
+ black += cmyk_map_entry->k * temp;
+ cmyk_map_entry++;
+ }
+ cyan /= frac_1;
+ magenta /= frac_1;
+ yellow /= frac_1;
+ black /= frac_1;
+ if (cyan > 1)
+ cyan = 1;
+ if (magenta > 1)
+ magenta = 1;
+ if (yellow > 1)
+ yellow = 1;
+ if (black > 1)
+ black = 1;
+ if ((pixel & 1) == 0)
+ *dest = (cyan<<7) | (magenta<<6) | (yellow<<5) | (black<<4);
+ else
+ *dest++ |= (cyan<<3) | (magenta<<2) | (yellow<<1) | black;
+ }
+}
+static void
+build_cmyk_raster_line_fromplanar_2bpc(gs_get_bits_params_t params, byte * dest,
+ int width, int num_comp,
+ cmyk_composite_map * cmyk_map, int num_order,
+ tiffsep_device * const tfdev)
+{
+ int pixel, comp_num;
+ uint temp, cyan, magenta, yellow, black;
+ cmyk_composite_map * cmyk_map_entry;
+
+ for (pixel = 0; pixel < width; pixel++) {
+ cmyk_map_entry = cmyk_map;
+ temp = *(params.data[tfdev->devn_params.separation_order_map[0]] + (pixel>>2));
+ temp = (((temp<<((pixel & 3)<<1))>>6) & 3) * 85;
+ cyan = cmyk_map_entry->c * temp;
+ magenta = cmyk_map_entry->m * temp;
+ yellow = cmyk_map_entry->y * temp;
+ black = cmyk_map_entry->k * temp;
+ cmyk_map_entry++;
+ for (comp_num = 1; comp_num < num_comp; comp_num++) {
+ temp =
+ *(params.data[tfdev->devn_params.separation_order_map[comp_num]] + (pixel>>2));
+ temp = (((temp<<((pixel & 3)<<1))>>6) & 3) * 85;
+ cyan += cmyk_map_entry->c * temp;
+ magenta += cmyk_map_entry->m * temp;
+ yellow += cmyk_map_entry->y * temp;
+ black += cmyk_map_entry->k * temp;
+ cmyk_map_entry++;
+ }
+ cyan /= frac_1;
+ magenta /= frac_1;
+ yellow /= frac_1;
+ black /= frac_1;
+ if (cyan > 3)
+ cyan = 3;
+ if (magenta > 3)
+ magenta = 3;
+ if (yellow > 3)
+ yellow = 3;
+ if (black > 3)
+ black = 3;
+ *dest++ = (cyan<<6) | (magenta<<4) | (yellow<<2) | black;
+ }
+}
+
+static void
+build_cmyk_raster_line_fromplanar_4bpc(gs_get_bits_params_t params, byte * dest,
+ int width, int num_comp,
+ cmyk_composite_map * cmyk_map, int num_order,
+ tiffsep_device * const tfdev)
+{
+ int pixel, comp_num;
+ uint temp, cyan, magenta, yellow, black;
+ cmyk_composite_map * cmyk_map_entry;
+
+ for (pixel = 0; pixel < width; pixel++) {
+ cmyk_map_entry = cmyk_map;
+ temp = *(params.data[tfdev->devn_params.separation_order_map[0]] + (pixel>>1));
+ if (pixel & 1)
+ temp >>= 4;
+ temp &= 15;
+ cyan = cmyk_map_entry->c * temp;
+ magenta = cmyk_map_entry->m * temp;
+ yellow = cmyk_map_entry->y * temp;
+ black = cmyk_map_entry->k * temp;
+ cmyk_map_entry++;
+ for (comp_num = 1; comp_num < num_comp; comp_num++) {
+ temp =
+ *(params.data[tfdev->devn_params.separation_order_map[comp_num]] + (pixel>>1));
+ if (pixel & 1)
+ temp >>= 4;
+ temp &= 15;
+ cyan += cmyk_map_entry->c * temp;
+ magenta += cmyk_map_entry->m * temp;
+ yellow += cmyk_map_entry->y * temp;
+ black += cmyk_map_entry->k * temp;
+ cmyk_map_entry++;
+ }
+ cyan /= frac_1;
+ magenta /= frac_1;
+ yellow /= frac_1;
+ black /= frac_1;
+ if (cyan > 15)
+ cyan = 15;
+ if (magenta > 15)
+ magenta = 15;
+ if (yellow > 15)
+ yellow = 15;
+ if (black > 15)
+ black = 15;
+ *dest++ = (cyan<<4) | magenta;
+ *dest++ = (yellow<<4) | black;
+ }
+}
+
+static int
+sep1_ht_order_to_thresholds(gx_device *pdev, const gs_imager_state *pis)
+{
+ tiffsep1_device * const tfdev = (tiffsep1_device *)pdev;
+ gs_memory_t *mem = pdev->memory;
+
+ /* If we have thresholds, free them and clear the pointers */
+ if( tfdev->thresholds[0].dstart != NULL) {
+ sep1_free_thresholds(tfdev);
+ } else {
+ int nc, j;
+ gx_ht_order *d_order;
+ threshold_array_t *dptr;
+
+ if (pis->dev_ht == NULL) {
+ emprintf(mem, "sep1_order_to_thresholds: no dev_ht available\n");
+ return_error(gs_error_rangecheck); /* error condition */
+ }
+ nc = pis->dev_ht->num_comp;
+ for( j=0; j<nc; j++ ) {
+ d_order = &(pis->dev_ht->components[j].corder);
+ dptr = &(tfdev->thresholds[j]);
+ dptr->dstart = threshold_from_order( d_order, &(dptr->dwidth), &(dptr->dheight), mem);
+ if( dptr->dstart == NULL ) {
+ emprintf(mem,
+ "sep1_order_to_thresholds: conversion to thresholds failed.\n");
+ return_error(gs_error_rangecheck); /* error condition */
+ }
+ }
+ }
+ return 0;
+}
+
+static void
+sep1_free_thresholds(tiffsep1_device *tfdev)
+{
+ int i;
+
+ for (i=0; i < GX_DEVICE_COLOR_MAX_COMPONENTS + 1; i++) {
+ threshold_array_t *dptr = &(tfdev->thresholds[i]);
+
+ if (dptr->dstart != NULL) {
+ gs_free(tfdev->memory, dptr->dstart, dptr->dwidth * dptr->dheight, 1,
+ "tiffsep1_threshold_array");
+ dptr->dstart = NULL;
+ }
+ }
+}
+
+/************************************************************************/
+/* This routine generates a threshold matrix for use in */
+/* the color dithering routine from the "order" info in */
+/* the current graphics state. */
+/* */
+/************************************************************************/
+
+static byte*
+threshold_from_order( gx_ht_order *d_order, int *Width, int *Height, gs_memory_t *memory)
+{
+ int i, j, l, prev_l;
+ unsigned char *thresh;
+ gx_ht_bit *bits = (gx_ht_bit *)d_order->bit_data;
+ int num_repeat, shift;
+ int row_kk, col_kk, kk;
+
+ /* We can have simple or complete orders. Simple ones tile the threshold
+ with shifts. To handle those we simply loop over the number of
+ repeats making sure to shift columns when we set our threshold values */
+ num_repeat = d_order->full_height / d_order->height;
+ shift = d_order->shift;
+
+#ifdef DEBUG
+if ( gs_debug_c('h') ) {
+ dmprintf2(memory, " width=%d, height=%d,",
+ d_order->width, d_order->height );
+ dmprintf2(memory, " num_levels=%d, raster=%d\n",
+ d_order->num_levels, d_order->raster );
+}
+#endif
+
+ thresh = (byte *)gs_malloc(memory, d_order->width * d_order->full_height, 1,
+ "tiffsep1_threshold_array");
+ if( thresh == NULL ) {
+#ifdef DEBUG
+ emprintf(memory, "threshold_from_order, malloc failed\n");
+ emprintf2(memory, " width=%d, height=%d,",
+ d_order->width, d_order->height );
+ emprintf2(memory, " num_levels=%d, raster=%d\n",
+ d_order->num_levels, d_order->raster );
+#endif
+ return thresh ; /* error if allocation failed */
+ }
+ for( i=0; i<d_order->num_bits; i++ )
+ thresh[i] = 1;
+
+ *Width = d_order->width;
+ *Height = d_order->full_height;
+
+ prev_l = 0;
+ l = 1;
+ while( l < d_order->num_levels ) {
+ if( d_order->levels[l] > d_order->levels[prev_l] ) {
+ int t_level = (256*l)/d_order->num_levels;
+ int row, col;
+#ifdef DEBUG
+ if ( gs_debug_c('h') )
+ dmprintf2(memory, " level[%3d]=%3d\n", l, d_order->levels[l]);
+#endif
+ for( j=d_order->levels[prev_l]; j<d_order->levels[l]; j++) {
+#ifdef DEBUG
+ if ( gs_debug_c('h') )
+ dmprintf2(memory, " bits.offset=%3d, bits.mask=%8x ",
+ bits[j].offset, bits[j].mask);
+#endif
+ row = bits[j].offset / d_order->raster;
+ for( col=0; col < (8*sizeof(ht_mask_t)); col++ ) {
+ if( bits[j].mask & bit_order[col] )
+ break;
+ }
+ col += 8 * ( bits[j].offset - (row * d_order->raster) );
+#ifdef DEBUG
+ if ( gs_debug_c('h') )
+ dmprintf3(memory, "row=%2d, col=%2d, t_level=%3d\n",
+ row, col, t_level);
+#endif
+ if( col < (int)d_order->width ) {
+ for (kk = 0; kk < num_repeat; kk++) {
+ row_kk = row + kk * d_order->height;
+ col_kk = col + kk * shift;
+ col_kk = col_kk % d_order->width;
+ *(thresh + col_kk + (row_kk * d_order->width)) = t_level;
+ }
+ }
+ }
+ prev_l = l;
+ }
+ l++;
+ }
+
+#ifdef DEBUG
+ if (gs_debug_c('h')) {
+ for( i=0; i<(int)d_order->height; i++ ) {
+ dmprintf1(memory, "threshold array row %3d= ", i);
+ for( j=(int)d_order->width-1; j>=0; j-- )
+ dmprintf1(memory, "%3d ", *(thresh+j+(i*d_order->width)) );
+ dmprintf(memory, "\n");
+ }
+ }
+#endif
+
+ return thresh;
+}
+
+ /*
+ * This function prints out CMYK value with separation name for every
+ * separation. Where the original alternate colour space was DeviceCMYK, and the output
+ * ICC profile is CMYK, no transformation takes place. Where the original alternate space
+ * was not DeviceCMYK, the colour management system will be used to generate CMYK values
+ * from the original tint transform.
+ * NB if the output profile is DeviceN then we will use the DeviceCMYK profile to map the
+ * equivalents, *not* the DeviceN profile. This is a peculiar case.....
+ */
+static int
+print_cmyk_equivalent_colors(tiffsep_device *tfdev, int num_comp, cmyk_composite_map *cmyk_map)
+{
+ int comp_num;
+ char *name = (char *)gs_alloc_bytes(tfdev->memory, gp_file_name_sizeof,
+ "tiffsep_print_cmyk_equivalent_colors(name)");
+
+ if (!name) {
+ return_error(gs_error_VMerror);
+ }
+
+ for (comp_num = 0; comp_num < num_comp; comp_num++) {
+ int sep_num = tfdev->devn_params.separation_order_map[comp_num];
+
+ if (sep_num < tfdev->devn_params.num_std_colorant_names) {
+ if (gp_file_name_sizeof < strlen(tfdev->devn_params.std_colorant_names[sep_num])) {
+ if (name)
+ gs_free_object(tfdev->memory, name, "tiffsep_print_cmyk_equivalent_colors(name)");
+ return_error(gs_error_rangecheck);
+ }
+ strcpy(name, tfdev->devn_params.std_colorant_names[sep_num]);
+ } else {
+ sep_num -= tfdev->devn_params.num_std_colorant_names;
+ if (gp_file_name_sizeof < tfdev->devn_params.separations.names[sep_num].size) {
+ if (name)
+ gs_free_object(tfdev->memory, name, "tiffsep_print_cmyk_equivalent_colors(name)");
+ return_error(gs_error_rangecheck);
+ }
+
+ memcpy(name, (char *)tfdev->devn_params.separations.names[sep_num].data, tfdev->devn_params.separations.names[sep_num].size);
+ name[tfdev->devn_params.separations.names[sep_num].size] = '\0';
+ }
+
+ dmlprintf5(tfdev->memory, "%%%%SeparationColor: \"%s\" 100%% ink = %hd %hd %hd %hd CMYK\n",
+ name,
+ cmyk_map[comp_num].c,
+ cmyk_map[comp_num].m,
+ cmyk_map[comp_num].y,
+ cmyk_map[comp_num].k);
+ }
+
+ if (name) {
+ gs_free_object(tfdev->memory, name, "tiffsep_print_cmyk_equivalent_colors(name)");
+ }
+
+ return 0;
+}
+
+/*
+ * Output the image data for the tiff separation (tiffsep) device. The data
+ * for the tiffsep device is written in separate planes to separate files.
+ *
+ * The DeviceN parameters (SeparationOrder, SeparationColorNames, and
+ * MaxSeparations) are applied to the tiffsep device.
+ */
+static int
+tiffsep_print_page(gx_device_printer * pdev, FILE * file)
+{
+ tiffsep_device * const tfdev = (tiffsep_device *)pdev;
+ int num_std_colorants = tfdev->devn_params.num_std_colorant_names;
+ int num_order = tfdev->devn_params.num_separation_order_names;
+ int num_spot = tfdev->devn_params.separations.num_separations;
+ int num_comp, comp_num, sep_num, code = 0, code1 = 0;
+ cmyk_composite_map cmyk_map[GX_DEVICE_COLOR_MAX_COMPONENTS];
+ char *name = NULL;
+ int base_filename_length = length_base_file_name(tfdev);
+ int save_depth = pdev->color_info.depth;
+ int save_numcomps = pdev->color_info.num_components;
+ const char *fmt;
+ gs_parsed_file_name_t parsed;
+ int plane_count = 0; /* quiet compiler */
+ int factor = tfdev->DownScaleFactor;
+ int mfs = tfdev->MinFeatureSize;
+ int dst_bpc = tfdev->BitsPerComponent;
+ gx_downscaler_t ds;
+ int width = gx_downscaler_scale(tfdev->width, factor);
+ int height = gx_downscaler_scale(tfdev->height, factor);
+
+ name = (char *)gs_alloc_bytes(pdev->memory, gp_file_name_sizeof, "tiffsep_print_page(name)");
+ if (!name)
+ return_error(gs_error_VMerror);
+
+ /* Print the names of the spot colors */
+ if (num_order == 0) {
+ for (sep_num = 0; sep_num < num_spot; sep_num++) {
+ copy_separation_name(tfdev, name,
+ gp_file_name_sizeof - base_filename_length - SUFFIX_SIZE, sep_num);
+ dmlprintf1(pdev->memory, "%%%%SeparationName: %s\n", name);
+ }
+ }
+
+ /*
+ * Check if the file name has a numeric format. If so then we want to
+ * create individual separation files for each page of the input.
+ */
+ code = gx_parse_output_file_name(&parsed, &fmt, tfdev->fname,
+ strlen(tfdev->fname), pdev->memory);
+
+ /* Write the page directory for the CMYK equivalent file. */
+ if (!tfdev->comp_file) {
+ pdev->color_info.depth = dst_bpc*4; /* Create directory for 32 bit cmyk */
+ if (tfdev->Compression==COMPRESSION_NONE &&
+ height > ((unsigned long) 0xFFFFFFFF - (file ? ftell(file) : 0))/(width*4)) { /* note width is never 0 in print_page */
+ dmprintf(pdev->memory, "CMYK composite file would be too large! Reduce resolution or enable compression.\n");
+ return_error(gs_error_rangecheck); /* this will overflow 32 bits */
+ }
+
+ code = gx_device_open_output_file((gx_device *)pdev, pdev->fname, true, true, &(tfdev->comp_file));
+ if (code < 0) {
+ goto done;
+ }
+
+ tfdev->tiff_comp = tiff_from_filep(pdev, pdev->dname, tfdev->comp_file, tfdev->BigEndian, tfdev->UseBigTIFF);
+ if (!tfdev->tiff_comp) {
+ code = gs_note_error(gs_error_invalidfileaccess);
+ goto done;
+ }
+
+ }
+ code = tiff_set_fields_for_printer(pdev, tfdev->tiff_comp, factor, 0);
+
+ if (dst_bpc == 1 || dst_bpc == 8) {
+ tiff_set_cmyk_fields(pdev, tfdev->tiff_comp, dst_bpc, tfdev->Compression, tfdev->MaxStripSize);
+ }
+ else {
+ /* Catch-all just for safety's sake */
+ tiff_set_cmyk_fields(pdev, tfdev->tiff_comp, dst_bpc, COMPRESSION_NONE, tfdev->MaxStripSize);
+ }
+
+ pdev->color_info.depth = save_depth;
+ if (code < 0) {
+ goto done;
+ }
+
+ /* Set up the separation output files */
+ num_comp = number_output_separations( tfdev->color_info.num_components,
+ num_std_colorants, num_order, num_spot);
+
+ if (!num_order && num_comp < num_std_colorants + num_spot) {
+ dmlprintf(pdev->memory, "Warning: skipping one or more colour separations, see: Devices.htm#TIFF\n");
+ }
+
+ for (comp_num = 0; comp_num < num_comp; comp_num++ ) {
+ int sep_num = tfdev->devn_params.separation_order_map[comp_num];
+
+ code = create_separation_file_name(tfdev, name, gp_file_name_sizeof,
+ sep_num, true);
+ if (code < 0) {
+ goto done;
+ }
+
+ /* Open the separation file, if not already open */
+ if (tfdev->sep_file[comp_num] == NULL) {
+ code = gx_device_open_output_file((gx_device *)pdev, name,
+ true, true, &(tfdev->sep_file[comp_num]));
+ if (code < 0) {
+ goto done;
+ }
+ tfdev->tiff[comp_num] = tiff_from_filep(pdev, name,
+ tfdev->sep_file[comp_num],
+ tfdev->BigEndian, tfdev->UseBigTIFF);
+ if (!tfdev->tiff[comp_num]) {
+ code = gs_note_error(gs_error_ioerror);
+ goto done;
+ }
+ }
+
+ pdev->color_info.depth = dst_bpc; /* Create files for 8 bit gray */
+ pdev->color_info.num_components = 1;
+ if (tfdev->Compression==COMPRESSION_NONE &&
+ height*8/dst_bpc > ((unsigned long) 0xFFFFFFFF - (file ? ftell(file): 0))/width) /* note width is never 0 in print_page */
+ {
+ code = gs_note_error(gs_error_rangecheck); /* this will overflow 32 bits */
+ goto done;
+ }
+
+
+ code = tiff_set_fields_for_printer(pdev, tfdev->tiff[comp_num], factor, 0);
+ tiff_set_gray_fields(pdev, tfdev->tiff[comp_num], dst_bpc, tfdev->Compression, tfdev->MaxStripSize);
+ pdev->color_info.depth = save_depth;
+ pdev->color_info.num_components = save_numcomps;
+ if (code < 0) {
+ goto done;
+ }
+ }
+
+ build_cmyk_map(tfdev, num_comp, cmyk_map);
+ if (tfdev->PrintSpotCMYK) {
+ code = print_cmyk_equivalent_colors(tfdev, num_comp, cmyk_map);
+ if (code < 0) {
+ goto done;
+ }
+ }
+
+ {
+ int raster_plane = bitmap_raster(width * 8);
+ byte *planes[GS_CLIENT_COLOR_MAX_COMPONENTS] = { 0 };
+ int cmyk_raster = width * NUM_CMYK_COMPONENTS;
+ int pixel, y;
+ byte * sep_line;
+ int plane_index;
+ int offset_plane = 0;
+
+ sep_line =
+ gs_alloc_bytes(pdev->memory, cmyk_raster, "tiffsep_print_page");
+ if (!sep_line) {
+ code = gs_note_error(gs_error_VMerror);
+ goto done;
+ }
+
+ for (comp_num = 0; comp_num < num_comp; comp_num++ )
+ TIFFCheckpointDirectory(tfdev->tiff[comp_num]);
+ TIFFCheckpointDirectory(tfdev->tiff_comp);
+
+ /* Write the page data. */
+ {
+ gs_get_bits_params_t params;
+ int byte_width;
+
+ /* Return planar data */
+ params.options = (GB_RETURN_POINTER | GB_RETURN_COPY |
+ GB_ALIGN_STANDARD | GB_OFFSET_0 | GB_RASTER_STANDARD |
+ GB_PACKING_PLANAR | GB_COLORS_NATIVE | GB_ALPHA_NONE);
+ params.x_offset = 0;
+ params.raster = bitmap_raster(width * pdev->color_info.depth);
+
+ if (num_order > 0) {
+ /* In this case, there was a specification for a separation
+ color order, which indicates what colorants we will
+ actually creat individual separation files for. We need
+ to allocate for the standard colorants. This is due to the
+ fact that even when we specify a single spot colorant, we
+ still create the composite CMYK output file. */
+ for (comp_num = 0; comp_num < num_std_colorants; comp_num++) {
+ planes[comp_num] = gs_alloc_bytes(pdev->memory, raster_plane,
+ "tiffsep_print_page");
+ params.data[comp_num] = planes[comp_num];
+ if (params.data[comp_num] == NULL) {
+ code = gs_note_error(gs_error_VMerror);
+ goto cleanup;
+ }
+ }
+ offset_plane = num_std_colorants;
+ /* Now we need to make sure that we do not allocate extra
+ planes if any of the colorants in the order list are
+ one of the standard colorant names */
+ plane_index = plane_count = num_std_colorants;
+ for (comp_num = 0; comp_num < num_comp; comp_num++) {
+ int temp_pos;
+
+ temp_pos = tfdev->devn_params.separation_order_map[comp_num];
+ if (temp_pos >= num_std_colorants) {
+ /* We have one that is not a standard colorant name
+ so allocate a new plane */
+ planes[plane_count] = gs_alloc_bytes(pdev->memory, raster_plane,
+ "tiffsep_print_page");
+ /* Assign the new plane to the appropriate position */
+ params.data[plane_index] = planes[plane_count];
+ if (params.data[plane_index] == NULL) {
+ code = gs_note_error(gs_error_VMerror);
+ goto cleanup;
+ }
+ plane_count += 1;
+ } else {
+ /* Assign params.data with the appropriate std.
+ colorant plane position */
+ params.data[plane_index] = planes[temp_pos];
+ }
+ plane_index += 1;
+ }
+ } else {
+ /* Sep color order number was not specified so just render all
+ the planes that we can */
+ for (comp_num = 0; comp_num < num_comp; comp_num++) {
+ planes[comp_num] = gs_alloc_bytes(pdev->memory, raster_plane,
+ "tiffsep_print_page");
+ params.data[comp_num] = planes[comp_num];
+ if (params.data[comp_num] == NULL) {
+ code = gs_note_error(gs_error_VMerror);
+ goto cleanup;
+ }
+ }
+ }
+ code = gx_downscaler_init_planar(&ds, (gx_device *)pdev, &params,
+ num_comp, factor, mfs, 8, dst_bpc);
+ if (code < 0)
+ goto cleanup;
+ byte_width = (width * dst_bpc + 7)>>3;
+ for (y = 0; y < height; ++y) {
+ code = gx_downscaler_get_bits_rectangle(&ds, &params, y);
+ if (code < 0)
+ goto cleanup;
+ /* Write separation data (tiffgray format) */
+ for (comp_num = 0; comp_num < num_comp; comp_num++ ) {
+ byte *src;
+ byte *dest = sep_line;
+
+ if (num_order > 0) {
+ src = params.data[tfdev->devn_params.separation_order_map[comp_num]];
+ } else
+ src = params.data[comp_num];
+ for (pixel = 0; pixel < byte_width; pixel++, dest++, src++)
+ *dest = MAX_COLOR_VALUE - *src; /* Gray is additive */
+ TIFFWriteScanline(tfdev->tiff[comp_num], (tdata_t)sep_line, y, 0);
+ }
+ /* Write CMYK equivalent data */
+ switch(dst_bpc)
+ {
+ default:
+ case 8:
+ build_cmyk_raster_line_fromplanar(params, sep_line, width,
+ num_comp, cmyk_map, num_order,
+ tfdev);
+ break;
+ case 4:
+ build_cmyk_raster_line_fromplanar_4bpc(params, sep_line, width,
+ num_comp, cmyk_map, num_order,
+ tfdev);
+ break;
+ case 2:
+ build_cmyk_raster_line_fromplanar_2bpc(params, sep_line, width,
+ num_comp, cmyk_map, num_order,
+ tfdev);
+ break;
+ case 1:
+ build_cmyk_raster_line_fromplanar_1bpc(params, sep_line, width,
+ num_comp, cmyk_map, num_order,
+ tfdev);
+ break;
+ }
+ TIFFWriteScanline(tfdev->tiff_comp, (tdata_t)sep_line, y, 0);
+ }
+cleanup:
+ if (num_order > 0) {
+ /* Free up the standard colorants if num_order was set.
+ In this process, we need to make sure that none of them
+ were the standard colorants. plane_count should have
+ the sum of the std. colorants plus any non-standard
+ ones listed in separation color order */
+ for (comp_num = 0; comp_num < plane_count; comp_num++) {
+ gs_free_object(pdev->memory, planes[comp_num],
+ "tiffsep_print_page");
+ }
+ } else {
+ for (comp_num = 0; comp_num < num_comp; comp_num++) {
+ gs_free_object(pdev->memory, planes[comp_num + offset_plane],
+ "tiffsep_print_page");
+ }
+ }
+ gx_downscaler_fin(&ds);
+ gs_free_object(pdev->memory, sep_line, "tiffsep_print_page");
+ }
+ code1 = code;
+ for (comp_num = 0; comp_num < num_comp; comp_num++ ) {
+ TIFFWriteDirectory(tfdev->tiff[comp_num]);
+ if (fmt) {
+ int sep_num = tfdev->devn_params.separation_order_map[comp_num];
+
+ code = create_separation_file_name(tfdev, name, gp_file_name_sizeof, sep_num, false);
+ if (code < 0) {
+ code1 = code;
+ continue;
+ }
+ code = tiffsep_close_sep_file(tfdev, name, comp_num);
+ if (code < 0) {
+ code1 = code;
+ }
+ }
+ }
+
+ TIFFWriteDirectory(tfdev->tiff_comp);
+ if (fmt) {
+ code = tiffsep_close_comp_file(tfdev, pdev->fname);
+ }
+ if (code1 < 0) {
+ code = code1;
+ }
+ }
+
+done:
+ if (name)
+ gs_free_object(pdev->memory, name, "tiffsep_print_page(name)");
+ return code;
+}
+
+/*
+ * Output the image data for the tiff separation (tiffsep1) device. The data
+ * for the tiffsep1 device is written in separate planes to separate files.
+ *
+ * The DeviceN parameters (SeparationOrder, SeparationColorNames, and
+ * MaxSeparations) are applied to the tiffsep device.
+ */
+static int
+tiffsep1_print_page(gx_device_printer * pdev, FILE * file)
+{
+ tiffsep1_device * const tfdev = (tiffsep1_device *)pdev;
+ int num_std_colorants = tfdev->devn_params.num_std_colorant_names;
+ int num_order = tfdev->devn_params.num_separation_order_names;
+ int num_spot = tfdev->devn_params.separations.num_separations;
+ int num_comp, comp_num, code = 0, code1 = 0;
+ short map_comp_to_sep[GX_DEVICE_COLOR_MAX_COMPONENTS];
+ char *name = NULL;
+ int save_depth = pdev->color_info.depth;
+ int save_numcomps = pdev->color_info.num_components;
+ const char *fmt;
+ gs_parsed_file_name_t parsed;
+ int non_encodable_count = 0;
+
+ if (tfdev->thresholds[0].dstart == NULL)
+ return_error(gs_error_rangecheck);
+
+ name = (char *)gs_alloc_bytes(pdev->memory, gp_file_name_sizeof, "tiffsep1_print_page(name)");
+ if (!name)
+ return_error(gs_error_VMerror);
+
+ build_comp_to_sep_map((tiffsep_device *)tfdev, map_comp_to_sep);
+
+ /*
+ * Check if the file name has a numeric format. If so then we want to
+ * create individual separation files for each page of the input.
+ */
+ code = gx_parse_output_file_name(&parsed, &fmt, pdev->fname,
+ strlen(pdev->fname), pdev->memory);
+
+ /* If the output file is on disk and the name contains a page #, */
+ /* then delete the previous file. */
+ if (pdev->file != NULL && parsed.iodev == iodev_default(pdev->memory) && fmt) {
+ long count1 = pdev->PageCount;
+ char *compname = (char *)gs_alloc_bytes(pdev->memory, gp_file_name_sizeof, "tiffsep1_print_page(compname)");
+ if (!compname) {
+ code = gs_note_error(gs_error_VMerror);
+ goto done;
+ }
+
+ gx_device_close_output_file((gx_device *)pdev, pdev->fname, pdev->file);
+ pdev->file = NULL;
+
+ while (*fmt != 'l' && *fmt != '%')
+ --fmt;
+ if (*fmt == 'l')
+ gs_sprintf(compname, parsed.fname, count1);
+ else
+ gs_sprintf(compname, parsed.fname, (int)count1);
+ parsed.iodev->procs.delete_file(parsed.iodev, compname);
+ /* we always need an open printer (it will get deleted in tiffsep1_prn_close */
+ code = gdev_prn_open_printer((gx_device *)pdev, 1);
+
+ gs_free_object(pdev->memory, compname, "tiffsep_print_page(compname)");
+ if (code < 0) {
+ goto done;
+ }
+ }
+
+ /* Set up the separation output files */
+ num_comp = number_output_separations( tfdev->color_info.num_components,
+ num_std_colorants, num_order, num_spot);
+ for (comp_num = 0; comp_num < num_comp; comp_num++ ) {
+ int sep_num = map_comp_to_sep[comp_num];
+
+ code = create_separation_file_name((tiffsep_device *)tfdev, name,
+ gp_file_name_sizeof, sep_num, true);
+ if (code < 0) {
+ goto done;
+ }
+
+ /* Open the separation file, if not already open */
+ if (tfdev->sep_file[comp_num] == NULL) {
+ code = gx_device_open_output_file((gx_device *)pdev, name,
+ true, true, &(tfdev->sep_file[comp_num]));
+ if (code < 0) {
+ goto done;
+ }
+ tfdev->tiff[comp_num] = tiff_from_filep(pdev, name,
+ tfdev->sep_file[comp_num],
+ tfdev->BigEndian, tfdev->UseBigTIFF);
+ if (!tfdev->tiff[comp_num]) {
+ code = gs_note_error(gs_error_ioerror);
+ goto done;
+ }
+ }
+
+ pdev->color_info.depth = 8; /* Create files for 8 bit gray */
+ pdev->color_info.num_components = 1;
+ code = tiff_set_fields_for_printer(pdev, tfdev->tiff[comp_num], 1, 0);
+ tiff_set_gray_fields(pdev, tfdev->tiff[comp_num], 1, tfdev->Compression, tfdev->MaxStripSize);
+ pdev->color_info.depth = save_depth;
+ pdev->color_info.num_components = save_numcomps;
+ if (code < 0) {
+ goto done;
+ }
+
+ } /* end initialization of separation files */
+
+
+ { /* Get the expanded contone line, halftone and write out the dithered separations */
+ byte *planes[GS_CLIENT_COLOR_MAX_COMPONENTS];
+ int width = tfdev->width;
+ int raster_plane = bitmap_raster(width * 8);
+ int dithered_raster = ((7 + width) / 8) + ARCH_SIZEOF_LONG;
+ int pixel, y;
+ gs_get_bits_params_t params;
+ gs_int_rect rect;
+ /* the dithered_line is assumed to be 32-bit aligned by the alloc */
+ uint32_t *dithered_line = (uint32_t *)gs_alloc_bytes(pdev->memory, dithered_raster,
+ "tiffsep1_print_page");
+
+ memset(planes, 0, sizeof(*planes) * GS_CLIENT_COLOR_MAX_COMPONENTS);
+
+ /* Return planar data */
+ params.options = (GB_RETURN_POINTER | GB_RETURN_COPY |
+ GB_ALIGN_STANDARD | GB_OFFSET_0 | GB_RASTER_STANDARD |
+ GB_PACKING_PLANAR | GB_COLORS_NATIVE | GB_ALPHA_NONE);
+ params.x_offset = 0;
+ params.raster = bitmap_raster(width * pdev->color_info.depth);
+
+ code = 0;
+ for (comp_num = 0; comp_num < num_comp; comp_num++) {
+ planes[comp_num] = gs_alloc_bytes(pdev->memory, raster_plane,
+ "tiffsep1_print_page");
+ if (planes[comp_num] == NULL) {
+ code = gs_error_VMerror;
+ break;
+ }
+ }
+
+ if (code < 0 || dithered_line == NULL) {
+ code = gs_note_error(gs_error_VMerror);
+ goto cleanup;
+ }
+
+ for (comp_num = 0; comp_num < num_comp; comp_num++ )
+ TIFFCheckpointDirectory(tfdev->tiff[comp_num]);
+
+ rect.p.x = 0;
+ rect.q.x = pdev->width;
+ /* Loop for the lines */
+ for (y = 0; y < pdev->height; ++y) {
+ rect.p.y = y;
+ rect.q.y = y + 1;
+ /* We have to reset the pointers since get_bits_rect will have moved them */
+ for (comp_num = 0; comp_num < num_comp; comp_num++)
+ params.data[comp_num] = planes[comp_num];
+ code = (*dev_proc(pdev, get_bits_rectangle))((gx_device *)pdev, &rect, &params, NULL);
+ if (code < 0)
+ break;
+
+ /* Dither the separation and write it out */
+ for (comp_num = 0; comp_num < num_comp; comp_num++ ) {
+
+/***** #define SKIP_HALFTONING_FOR_TIMING *****/ /* uncomment for timing test */
+#ifndef SKIP_HALFTONING_FOR_TIMING
+
+ /*
+ * Define 32-bit writes by default. Testing shows that while this is more
+ * complex code, it runs measurably and consistently faster than the more
+ * obvious 8-bit code. The 8-bit code is kept to help future optimization
+ * efforts determine what affects tight loop optimization. Subtracting the
+ * time when halftoning is skipped shows that the 32-bit halftoning is
+ * 27% faster.
+ */
+#define USE_32_BIT_WRITES
+ byte *thresh_line_base = tfdev->thresholds[comp_num].dstart +
+ ((y % tfdev->thresholds[comp_num].dheight) *
+ tfdev->thresholds[comp_num].dwidth) ;
+ byte *thresh_ptr = thresh_line_base;
+ byte *thresh_limit = thresh_ptr + tfdev->thresholds[comp_num].dwidth;
+ byte *src = params.data[comp_num];
+#ifdef USE_32_BIT_WRITES
+ uint32_t *dest = dithered_line;
+ uint32_t val = 0;
+ const uint32_t *mask = &bit_order[0];
+#else /* example 8-bit code */
+ byte *dest = dithered_line;
+ byte val = 0;
+ byte mask = 0x80;
+#endif /* USE_32_BIT_WRITES */
+
+ for (pixel = 0; pixel < width; pixel++, src++) {
+#ifdef USE_32_BIT_WRITES
+ if (*src < *thresh_ptr++)
+ val |= *mask;
+ if (++mask == &(bit_order[32])) {
+ *dest++ = val;
+ val = 0;
+ mask = &bit_order[0];
+ }
+#else /* example 8-bit code */
+ if (*src < *thresh_ptr++)
+ val |= mask;
+ mask >>= 1;
+ if (mask == 0) {
+ *dest++ = val;
+ val = 0;
+ mask = 0x80;
+ }
+#endif /* USE_32_BIT_WRITES */
+ if (thresh_ptr >= thresh_limit)
+ thresh_ptr = thresh_line_base;
+ } /* end src pixel loop - collect last bits if any */
+ /* the following relies on their being enough 'pad' in dithered_line */
+#ifdef USE_32_BIT_WRITES
+ if (mask != &bit_order[0]) {
+ *dest = val;
+ }
+#else /* example 8-bit code */
+ if (mask != 0x80) {
+ *dest = val;
+ }
+#endif /* USE_32_BIT_WRITES */
+#endif /* SKIP_HALFTONING_FOR_TIMING */
+ TIFFWriteScanline(tfdev->tiff[comp_num], (tdata_t)dithered_line, y, 0);
+ } /* end component loop */
+ }
+
+ /* Update the strip data */
+ code1 = 0;
+ for (comp_num = 0; comp_num < num_comp; comp_num++ ) {
+ TIFFWriteDirectory(tfdev->tiff[comp_num]);
+
+ if (fmt) {
+ int sep_num = map_comp_to_sep[comp_num];
+
+ code = create_separation_file_name((tiffsep_device *)tfdev, name, gp_file_name_sizeof, sep_num, false);
+ if (code < 0) {
+ code1 = code;
+ continue;
+ }
+ code = tiffsep_close_sep_file((tiffsep_device *)tfdev, name, comp_num);
+ if (code < 0) {
+ code1 = code;
+ }
+ }
+ }
+ code = code1;
+
+ /* free any allocations and exit with code */
+cleanup:
+ gs_free_object(pdev->memory, dithered_line, "tiffsep1_print_page");
+ for (comp_num = 0; comp_num < num_comp; comp_num++) {
+ gs_free_object(pdev->memory, planes[comp_num], "tiffsep1_print_page");
+ }
+ }
+ /*
+ * If we have any non encodable pixels then signal an error.
+ */
+ if (non_encodable_count) {
+ dmlprintf1(pdev->memory, "WARNING: Non encodable pixels = %d\n", non_encodable_count);
+ code = gs_note_error(gs_error_rangecheck);
+ }
+
+done:
+ if (name)
+ gs_free_object(pdev->memory, name, "tiffsep1_print_page(name)");
+ return code;
+}
diff --git a/devices/gdevupd.c b/devices/gdevupd.c
new file mode 100644
index 000000000..efffd99d8
--- /dev/null
+++ b/devices/gdevupd.c
@@ -0,0 +1,7620 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* gdevupd.c Revision: 1.88 */
+/* "uniprint" -- Ugly Printer Driver by Gunther Hess (ghess@elmos.de) */
+
+/* Revision-History:
+ 23-Mar-1997 - 1.43: First published version
+ 24-Mar-1997 - 1.44: gs4.03 compatible version on the web
+ 31-Mar-1997 - 1.53: First Version inside gs-fileset (limited)
+ 28-Apr-1997 - 1.54: Version intended for public gs-release
+ 4-May-1997 - 1.55: Deactivated an accidentially active Debug-Option
+ 14-Jun-1997 - 1.56: Bug-Workaround for White on White Printing (gs5.0)
+ 17-Jun-1997 - 1.57: More reasonable Fix for the above Bug
+ ...
+ 7-Jul-1997 - 1.68: NULL-Param-BUG, HR's BJC, Pwidth/-height BUG, YFlip
+ 25-Jul-1997 - 1.69: Bug-Fix: incomplete Change of PHEIGHT-Treatment
+ 4-Aug-1997 - 1.70: Arrgh: still incomplete Change of PHEIGHT-Treatment
+ 17-AUG-1997 - 1.71: Fix of BSD-sprintf bug. (returns char * there)
+ ...
+ 28-Sep-1997 - 1.77: Fixed the byte<>char and casted-lvalue Problems
+ ...
+ 12-Mar-1998 - 1.80: Some PJL-Functions, Map-Bug-Fix (by Wonder-Wolfgang)
+ 21-Oct-1998 - 1.81: Added RGB2CMY[_]K Modi (Eric Domenjoud)
+ ...
+ 27-Feb-2000 - 1.84: CMYKgenerate with forced K-Control [distributed]
+ 2-Apr-2000 - Unofficial modifications for Epson Stylus Color 300. GR
+ 5-Apr-2000 - GR fixed last row not filled bug in wrtescnm
+ 7-May-2000 - 1.85: Always BOP/EOP-Massaging for RTL-Output (Dan Coby)
+ ...
+ 7-May-2000 - 1.87: integrated stc300-code by Glenn Ramsey
+ " - 1.88: reduced "cast discards `const'" warnings to 1
+
+*/
+
+/* Canon BJC 610 additions from (hr)
+ Helmut Riegler <helmut-riegler@net4you.co.at>
+
+ The BJC-4000 can be supported very easily, only by creating the right .upp
+ parameter file. If you have this printer and you are willing to do this,
+ contact me, I'll give you the technical details (ESC codes).
+*/
+
+/* Epson Stylus Color 300 (FMT_ESCNMY) additions 2-Apr-2000.
+ Glenn Ramsey <glennr@es.co.nz>
+*/
+
+/* ------------------------------------------------------------------- */
+/* Compile-Time-Options */
+/* ------------------------------------------------------------------- */
+
+/**
+There are two compile-time options for this driver:
+ 1. UPD_SIGNAL enables interrupt detection, that aborts printing and
+ 2. UPD_MESSAGES controls the amount of messages generated by the driver
+*/
+
+#ifndef UPD_SIGNAL
+#ifdef __unix__
+#define UPD_SIGNAL 1 /** Activated, if undefined, on UNIX-Systems */
+#else /* !__unix__ */
+#define UPD_SIGNAL 0 /** Inactive on others, by default */
+#endif /* ?__unix__ */
+#endif /* UPD_SIGNAL */
+
+#ifndef UPD_MESSAGES
+#define UPD_MESSAGES UPD_M_ERROR /** Error-messages only, if not defined */
+#endif /* UPD_MESSAGES */
+
+/* ------------------------------------------------------------------- */
+/* Required Header-Files */
+/* ------------------------------------------------------------------- */
+
+#include "stdint_.h"
+
+#ifndef hess_test_INCLUDED /* A private test-Option */
+
+#include "gdevprn.h" /** Printer-superclass header */
+#include "gsparam.h" /** For the Parameter-Handling (optional) */
+
+#include <stdlib.h> /** for rand */
+#include <limits.h> /** for INT_MIN */
+#include <ctype.h> /** for isupper */
+
+#endif /* hess_test_INCLUDED A private test-Option */
+
+#if UPD_SIGNAL
+#include <signal.h> /** Only included, if UPD_SIGNAL is active (true) */
+#endif /* UPD_SIGNAL */
+
+/* ------------------------------------------------------------------- */
+/* Device-Structure (including an additional Structure-Pointer-Type) */
+/* ------------------------------------------------------------------- */
+
+typedef struct upd_s upd_t,*upd_p; /** Type & Pointer of device-specifics */
+typedef const upd_t *upd_pc; /** Pointer to constant device-specfics */
+
+typedef struct upd_device_s { /** The driver must typedef ... */
+ gx_device_common; /** common fields for all devices */
+ gx_prn_device_common; /** common fields for printing-devices */
+ gs_param_string upd_version; /** Source-Code Version */
+ upd_p upd; /** uniprint-specific extension */
+} upd_device; /** some type usually <name>_device> */
+
+/* ------------------------------------------------------------------- */
+/* Major Driver-Functions */
+/* ------------------------------------------------------------------- */
+
+static dev_proc_print_page(upd_print_page); /** print a page (required) */
+
+static dev_proc_open_device(upd_open); /** device-initialization (opt) */
+static dev_proc_close_device(upd_close); /** device-release (opt) */
+
+static dev_proc_get_params(upd_get_params); /** export parameters (opt) */
+static dev_proc_put_params(upd_put_params); /** import parameters (opt) */
+
+/**
+A `normal' Device-Driver wil only implement one of the following pairs
+of functions for the colormapping. But "uniprint" is something special and
+it really provides all four reasonable pairs and in addition to that
+a fifth set of functions, that delivers better FS-Results with KCMY.
+
+The first pair is for the mapping into a single stored component, that
+usually represents a grayscale. But nevertheless GHOSTSCRIPT deals with
+RGB-Values, but promises to deal with R==G==B-Values when asking to map.
+
+The second pair deals with RGB-Values.
+*/
+
+static dev_proc_encode_color( upd_rgb_1color); /** Gray-Gray->Index */
+static dev_proc_decode_color( upd_1color_rgb); /** Gray-Index->Gray */
+
+static dev_proc_encode_color( upd_rgb_3color); /** RGB->RGB-Index */
+static dev_proc_decode_color( upd_3color_rgb); /** RGB-Index->RGB */
+
+/**
+The third pair maps RGB-Values into four components, which one might
+expect to be KCMY-Values, but they are not: "uniprint" considers this four
+Values as White+RGB Values!
+*/
+
+static dev_proc_encode_color( upd_rgb_4color); /** RGB->WRGB-Index */
+static dev_proc_decode_color(upd_4color_rgb); /** WRGB-Index->RGB */
+
+/**
+The fourth pair deals with KCMY-Values. The Mapping-Function
+is of a different type, due to the additional argument, but the
+inverse-Function is of the same type, and expects RGB-Values to be
+deliverd into the receiving 3-Component-Array!
+*/
+
+static dev_proc_encode_color(upd_cmyk_icolor); /** KCMY->KCMY-Index */
+static dev_proc_decode_color( upd_icolor_rgb); /** KCMY->RGB-Index */
+
+/**
+The difference between the icolor-pair and the kcolor-pair is the enforced
+black-generation in the forward-mapping. that is taken into account by the
+reverse-mapping too.
+*/
+
+static dev_proc_encode_color(upd_cmyk_kcolor); /** adds black generation */
+static dev_proc_decode_color( upd_kcolor_rgb); /** watches black-gen */
+
+/**
+"ovcolor" is CMYK with Black-Generation and Undercolor-Removal, which
+is suitable for overprinting:
+ CMY' = (CMY-K')/(1-K')
+with
+ K' = min(C,M,Y)
+*/
+
+static dev_proc_encode_color(upd_rgb_ovcolor); /** RGB->CMYK-Index */
+#define upd_ovcolor_rgb upd_icolor_rgb /** CMYK-Index->RGB */
+
+/**
+"novcolor" is CMYK with Black-Generation and Undercolor-Removal, which
+is suitable for CMY / K - Printing:
+ CMY' = CMY-K'
+with
+ K' = min(C,M,Y)
+*/
+
+static dev_proc_encode_color(upd_rgb_novcolor); /** RGB->CMYK-Index */
+#define upd_novcolor_rgb upd_icolor_rgb /** CMYK-Index->RGB */
+
+/**
+For the sake of efficiency there is that bunch of functions and they
+perform no validity checks, thus it has to be assured that they are
+only active, if there is a valid device-structure for then.
+upd_procs_map performs this task.
+*/
+
+static int upd_procs_map( upd_device *udev);
+
+/* ------------------------------------------------------------------- */
+/* Prototype of the Device-Structure (the only thing exported!) */
+/* ------------------------------------------------------------------- */
+
+/**
+"uniprint" needs a procedure-table of its own, since it provides several
+optional procedures. Simpler-Drivers (e.g. non-color-drivers) may use
+prn_std_procs instead of defining their own procedure-table.
+*/
+
+#define upd_set_dev_proc(dev, p, proc) \
+ ((dev)->std_procs.p = (dev)->orig_procs.p = (proc))
+
+static gx_device_procs upd_procs = { /** Table of procedures */
+ upd_open, /** open-function, upd-special */
+ gx_default_get_initial_matrix, /** retrieve matrix */
+ gx_default_sync_output, /** sync display */
+ gdev_prn_output_page, /** superclass-print (calls back) */
+ upd_close, /** close-function, upd-special */
+ gx_default_map_rgb_color, /** RGB-mapping */
+ gx_default_map_color_rgb, /** reverse mapping */
+ NULL, /** fill_rectangle */
+ NULL, /** tile_rectangle */
+ NULL, /** copy_mono */
+ NULL, /** copy_color */
+ NULL, /** draw_line */
+ gx_default_get_bits, /** reads scanlines, e.g. for the driver */
+ upd_get_params, /** Export parameters, upd-special */
+ upd_put_params, /** Import parameters, upd-special */
+ gx_default_map_cmyk_color /** KCMY-mapping */
+}; /** */
+
+/**
+The prototype-instance of the device-structure _must_ have the name
+"gs_uniprint_device", where "uniprint" is the external name of the driver.
+This notice is bluntly copied from drivers.txt, which a potential
+driver-author should carefully read.
+
+Just to mention: this prototype is quite similar to the one, that
+"prn_device" produces and it identifies "uniprint" as a monochrome 1Bit
+device to GHOSTSCRIPT. But during the lifetime of a driver-instance
+this might change.
+
+This is the end of the part of declarations, that are common for
+color-drivers. The next sections address "uniprint"-specific data-types
+and the reader might directly skip to the section titled
+
+ upd_print_page: The main workhorse
+*/
+
+upd_device far_data gs_uniprint_device = { /** */
+ prn_device_body(upd_device, upd_procs, /** The Type and Procedures */
+ "uniprint", /** External name of the Device */
+ DEFAULT_WIDTH_10THS, /** X-Size (1/10") */
+ DEFAULT_HEIGHT_10THS, /** Y-Size (1/10") */
+ 72, 72, /** X,Y-DpI */
+ 0.0, 0.0, 0.0, 0.0, /** L,B,R,T-Margin */
+ 1, /** color_info.num_components 1/3/4 */
+ 1, /** color_info.depth 1/2/4/8/16/24/32 */
+ 1, /** color_info.max_gray # of distinct gray levels -1 (255/1) */
+ 0, /** color_info.max_color # of distinct color levels -1 (255/1/0)*/
+ 2, /** color_info.dither_grays size of gray ramp for dithering (256/2) */
+ 0, /** color_info.dither_colors size of color cube ditto (256/2/0) */
+ upd_print_page), /** Print-procedure */
+ { NULL, 0, true }, /** Driver-Version */
+ NULL /** upd-field: Initially none */
+}; /** */
+
+/* ------------------------------------------------------------------- */
+/* UPD-Data- and Prototypes */
+/* ------------------------------------------------------------------- */
+
+/*@ gdevupd.h < */
+/* ------------------------------------------------------------------- */
+/* External names of the UPD-Parameters */
+/* ------------------------------------------------------------------- */
+
+/** UPD-Parameters
+
+"uniprint" supports a hole bunch of external parameters. This Parameters
+fall into the following categories:
+
+ 0. special-string the upd_version, readonly upd_version
+ 1. choice name-indices, stored in upd->choice
+ 2. boolean single bits, stored in upd->flags
+ 3. integers single numbers, stored in upd->ints
+ 4. integer-Arrays arrays of numbers, stored in upd->int_a
+ 5. string device-commands, stored in upd->strings
+ 6. string-Arrays arrayed device-commands, stored in upd->string_a
+ 7. float-Arrays arrays of floats, stored in upd->float_a
+
+Currently there is no need for single floats, but they may be introduced in
+future versions. Since "uniprint" somtimes manipulates the contents of the
+array-variables it dynamically allocates storage for all this parameters.
+
+The following sections defines the names for this parameters in the order,
+they are stored within the mentioned dynamic fields of the upd-structure.
+A NULL-name means that the corresponding parameter is not externally visible.
+Besides the name, there is always a symbolic index #defined, that MUST match
+the Index-Number of the name.
+Actually
+*/
+
+static const char *const upd_version = "upVersion"; /** Readonly Version */
+
+/** Names for the multiple-choice-Parameters
+
+Currently there are three Parameters, that are handled as named choices.
+For each of them, there is an array of constant strings that consists of
+
+1. the Parameter-Name
+2. - n-1 the available choices.
+n. A terminating NULL
+*/
+
+static const char *const upd_mapper[] = { "upColorModel",
+#define MAP_GRAY 1 /** Monochrome & Grayscale Devices */
+"DeviceGray", /** Monochrome & Grayscale Devices */
+#define MAP_RGBW 2 /** RGB with White-Generation */
+"DeviceRGBW", /** RGB with White-Generation */
+#define MAP_RGB 3 /** RGB-Mapping */
+"DeviceRGB", /** RGB-Mapping */
+#define MAP_CMYK 4 /** CMYK-Mapping */
+"DeviceCMYK", /** CMYK-Mapping */
+#define MAP_CMYKGEN 5 /** CMYK-Mapping with Black-Generation */
+"DeviceCMYKgenerate", /** CMYK-Mapping with Black-Generation */
+#define MAP_RGBOV 6 /** RGB->CMYK with BG and UCR for CMYK */
+"DeviceRGB2CMYK", /** RGB->CMYK with BG and UCR for CMYK */
+#define MAP_RGBNOV 7 /** RGB->CMYK with BG and UCR for CMY + K */
+"DeviceRGB2CMY_K", /** RGB->CMYK with BG and UCR for CMY + K */
+NULL
+};
+
+static const char *const upd_render[] = { "upRendering",
+#define RND_FSCOMP 1 /** Componentwise Floyd-Steinberg */
+"ErrorDiffusion", /** Componentwise Floyd-Steinberg */
+#define RND_FSCMYK 2 /** CMYK-specialized 32Bit Floyd-Steinberg */
+"FSCMYK32", /** CMYK-specialized 32Bit Floyd-Steinberg */
+#define RND_FSCMY_K 3 /** CMY_K Rendering */
+"FSCMY_K",
+NULL
+};
+
+static const char *const upd_format[] = { "upOutputFormat",
+#define FMT_RAS 1 /** Generates SUN-Rasterfiles */
+"SunRaster", /** Generates SUN-Rasterfiles */
+#define FMT_EPSON 2 /** Generates X+Y-Weaved ESC/P-Output */
+"Epson", /** Generates X+Y-Weaved ESC/P-Output */
+#define FMT_ESCP2Y 3 /** Generates Y-Weaved ESC/P2-Output */
+"EscP2", /** Generates Y-Weaved ESC/P2-Output */
+#define FMT_ESCP2XY 4 /** Generates X+Y-Weaved ESC/P2-Output */
+"EscP2XY", /** Generates X+Y-Weaved ESC/P2-Output */
+#define FMT_RTL 5 /** Generates HP-PCL/RTL-Output */
+"Pcl", /** Generates HP-PCL/RTL-Output */
+#define FMT_CANON 6 /** Generates Output for Canon extended mode (hr) */
+"Canon", /** Generates Output for Canon extended mode (hr) */
+#define FMT_ESCNMY 7 /** Generates Output for Epson Stylus Color 300 (GR) */
+"EscNozzleMap", /** Generates Output for Epson Stylus Color 300 (GR) */
+NULL
+};
+
+static const char *const *const upd_choice[] = {
+#define C_MAPPER 0 /** the selected Mapper */
+ upd_mapper,
+#define C_RENDER 1 /** the selected Rendering */
+ upd_render,
+#define C_FORMAT 2 /** the selected Choice */
+ upd_format
+};
+
+/** Names for the flags (bool)
+*/
+
+static const char *const upd_flags[] = { /** */
+#define B_REVDIR ((uint32_t) 1<<0) /** FS-Dir-Flag */
+"upFSReverseDirection", /** FS-Dir-Flag */
+#define B_FIXDIR ((uint32_t) 1<<1) /** Do not alter FS-direction */
+"upFSFixedDirection", /** Do not alter FS-direction */
+#define B_FSWHITE ((uint32_t) 1<<2) /** Process white in FS */
+"upFSProcessWhiteSpace", /** Process white in FS */
+#define B_FSZERO ((uint32_t) 1<<3) /** Zero FS-Initialization */
+"upFSZeroInit", /** Zero FS-Initialization */
+
+#define B_PAGEWIDTH ((uint32_t) 1<<4) /** Adjust Width in BOP */
+"upAdjustPageWidthCommand", /** Adjust Page-Width in BOP */
+#define B_PAGELENGTH ((uint32_t) 1<<5) /** Adjust Length in BOP */
+"upAdjustPageLengthCommand", /** Adjust Page-Length in BOP */
+#define B_TOPMARGIN ((uint32_t) 1<<6) /** Adjust Top-Margin in BOP */
+"upAdjustTopMarginCommand", /** Adjust Top-Margin in BOP */
+#define B_BOTTOMMARGIN ((uint32_t) 1<<7) /** Adjust Bottom-Margin in BOP */
+"upAdjustBottomMarginCommand", /** Adjust Bottom-Margin in BOP */
+#define B_RESOLUTION ((uint32_t) 1<<8) /** Adjust Resolution in BOP */
+"upAdjustResolutionCommand", /** Adjust Resolution in BOP */
+#define B_MEDIASIZE ((uint32_t) 1<<9) /** Adjust Mediasize in BOP */
+"upAdjustMediaSize", /** Adjust Mediasize in BOP */
+
+#define B_XABS ((uint32_t) 1<<10) /** Use Absolute X-Values */
+"upFormatXabsolute", /** Use Absolute X-Values */
+#define B_YABS ((uint32_t) 1<<11) /** Use Absolute Y-Values */
+"upFormatYabsolute", /** Use Absolute Y-Values */
+
+#define B_MAP ((uint32_t) 1<<12) /** Mapping Initialized */
+"upColorModelInitialized", /** Mapping Initialized */
+#define B_BUF ((uint32_t) 1<<13) /** Raster-Buffer Initialized */
+"upRasterBufferInitialized", /** Raster-Buffer Initialized */
+#define B_RENDER ((uint32_t) 1<<14) /** Rendering Initialized */
+"upRenderingInitialized", /** Rendering Initialized */
+#define B_FORMAT ((uint32_t) 1<<15) /** Formatter Initialized */
+"upOutputFormatInitialized", /** Formatter Initialized */
+#define B_ABORT ((uint32_t) 1<<16) /** Abort on Interrupt */
+"upOutputAborted", /** Abort on Interrupt */
+#define B_ERROR ((uint32_t) 1<<17) /** Severe Error detected */
+"upErrorDetected", /** Severe Error detected */
+
+#define B_OPEN ((uint32_t) 1<<18) /** Open-Command written */
+"upWroteData", /** Open-Command written */
+
+#define B_YFLIP ((uint32_t) 1<<19) /** Mirrored printing (hr) */
+"upYFlip", /** Mirrored printing (hr) */
+
+#define B_REDUCEK ((uint32_t) 1<<20) /** CMY->Black Reduction */
+"upFSReduceK"
+
+};
+
+/** B_OK4GO: Bits required to execute the print-loop */
+
+#define B_OK4GO (B_MAP | B_BUF | B_RENDER | B_FORMAT)
+
+/** Names for the ints
+*/
+
+static const char *const upd_ints[] = {
+#define I_PWIDTH 0 /** Output-Width */
+"upOutputWidth",
+#define I_PHEIGHT 1 /** Output-Height */
+"upOutputHeight",
+#define I_OCOMP 2 /** Output-Components */
+"upOutputComponents",
+#define I_NSCNBUF 3 /** Output-Buffers */
+"upOutputBuffers",
+#define I_XSTEP 4 /** Unit-Step */
+"upOutputXStep", /* > 0 -> divide Raster-X, < 0 muliply Raster-X */
+#define I_XOFS 5 /** abs. X-Offset */
+"upOutputXOffset",
+#define I_YSTEP 6 /** Unit-Step */
+"upOutputYStep", /* > 0 -> divide Raster-Y, < 0 muliply Raster-Y */
+#define I_YOFS 7 /** abs. Y-Offset */
+"upOutputYOffset",
+#define I_PINS2WRITE 8 /** Number of Pins */
+"upOutputPins",
+
+#define I_NXPASS 9 /** X-Passes */
+"upWeaveXPasses",
+#define I_NYPASS 10 /** Y-Passes */
+"upWeaveYPasses",
+#define I_NPASS 11 /** Total # Passes */
+"upWeavePasses",
+#define I_BEG_Y 12 /** Start of normal Weaving */
+"upWeaveInitialScan",
+#define I_END_Y 13 /** End of normal Weaving */
+"upWeaveFinalScan",
+#define I_BEGSKIP 14 /** A Scan-Offset */
+"upWeaveYOffset",
+#define I_ROWS 15 /** Output rows per pass */
+"upNozzleMapRowsPerPass",
+#define I_PATRPT 16 /** mask pattern repeat interval */
+"upNozzleMapPatternRepeat"
+};
+
+/** Names for the Integer-Arrays
+*/
+
+static const char *const upd_int_a[] = { /** */
+#define IA_COLOR_INFO 0 /** external color_info */
+"upColorInfo", /** external color_info */
+
+#define IA_COMPBITS 1 /** Bits stored per Component */
+"upComponentBits", /** Bits stored per Component */
+#define IA_COMPSHIFT 2 /** Shift for the stored Bits */
+"upComponentShift", /** Shift for the stored Bits */
+#define IA_COMPORDER 3 /** Order of Output-Components */
+"upOutputComponentOrder", /** Order of Output-Components */
+
+#define IA_STD_DY 4 /** Standard-Weave Feeds */
+"upWeaveYFeeds", /** Standard-Weave Feeds */
+#define IA_STD_IX 5 /** Standard-Weave X-Passes */
+"upWeaveXStarts", /** Standard-Weave X-Start */
+#define IA_BEG_DY 6 /** Initial-Weave Feeds */
+"upWeaveInitialYFeeds", /** Initial-Weave Feeds */
+#define IA_BEG_IX 7 /** Initial-Weave X-Start */
+"upWeaveInitialXStarts", /** Initial-Weave X-Start */
+#define IA_BEGBOT 8 /** Initial-Weave #Pins */
+"upWeaveInitialPins", /** Initial-Weave #Pins */
+#define IA_END_DY 9 /** Final-Weave Feeds */
+"upWeaveFinalYFeeds", /** Final-Weave Feeds */
+#define IA_END_IX 10 /** Final-Weave X-Start */
+"upWeaveFinalXStarts", /** Final-Weave X-Start */
+#define IA_ENDTOP 11 /** Final-Weave #Pins */
+"upWeaveFinalPins", /** Final-Weave #Pins */
+#define IA_ROWMASK 12 /** The nozzle to row map */
+"upNozzleMapRowMask",
+#define IA_SCNOFS 13 /** Mask to scan map */
+"upNozzleMapMaskScanOffset"
+};
+
+/** Names of the String-Parameters
+*/
+
+static const char *const upd_strings[] = { /** */
+#define S_MODEL 0 /** Name of the Printer-Model */
+"upModel", /** Name of the Printer-Model */
+#define S_OPEN 1 /** Printer-Begin-Job */
+"upBeginJobCommand", /** Printer-Begin-Job */
+#define S_CLOSE 2 /** Printer-End-Job */
+"upEndJobCommand", /** Printer-End-Job */
+#define S_BEGIN 3 /** Printer-Begin-Page */
+"upBeginPageCommand", /** Printer-Begin-Page */
+#define S_END 4 /** Printer-End-Page */
+"upEndPageCommand", /** Printer-End-Page */
+#define S_ABORT 5 /** Printer-Abort-Command */
+"upAbortCommand", /** Printer-Abort-Command */
+
+#define S_XMOVE 6 /** X-Positioning-Command */
+"upXMoveCommand", /** X-Positioning-Command */
+#define S_XSTEP 7 /** X-Step Command (1<I_XSTEP) */
+"upXStepCommand", /** X-Step Command (1<I_XSTEP) */
+#define S_SETLF 8 /** Set-Linefeed-Command */
+"upSetLineFeedCommand", /** Set-Linefeed-Command */
+#define S_YMOVE 9 /** Y-Positioning-Command */
+"upYMoveCommand", /** Y-Positioning-Command */
+#define S_YSTEP 10 /** Y-Step Command (1<I_YSTEP) */
+"upYStepCommand" /** Y-Step Command (1<I_YSTEP) */
+}; /** */
+
+/** Names for the String-Arrays
+*/
+
+static const char *const upd_string_a[] = { /** */
+#define SA_SETCOMP 0 /** Select Components */
+"upSelectComponentCommands", /** Select Components */
+#define SA_WRITECOMP 1 /** Write Component Comands */
+"upWriteComponentCommands" /** Write Component Commands */
+}; /** */
+
+/** Names for the float-Arrays
+*/
+static const char *const upd_float_a[] = { /** */
+#define FA_WXFER 0 /** White-Transfer */
+"upWhiteTransfer", /** White-Transfer */
+#define FA_RXFER 1 /** Red-Transfer */
+"upRedTransfer", /** Red-Transfer */
+#define FA_GXFER 2 /** Green-Transfer */
+"upGreenTransfer", /** Green-Transfer */
+#define FA_BXFER 3 /** Blue-Transfer */
+"upBlueTransfer", /** Blue-Transfer */
+#define FA_KXFER 4 /** Black-Transfer */
+"upBlackTransfer", /** Black-Transfer */
+#define FA_CXFER 5 /** Cyan-Transfer */
+"upCyanTransfer", /** Cyan-Transfer */
+#define FA_MXFER 6 /** Magenta-Transfer */
+"upMagentaTransfer", /** Magenta-Transfer */
+#define FA_YXFER 7 /** Yellow-Transfer */
+"upYellowTransfer", /** Yellow-Transfer */
+#define FA_MARGINS 8 /** private Margins */
+"upMargins", /** private Margins */
+#define FA_MAP 9 /** Color-Map */
+"upColorMap" /** Color-Map */
+}; /** */
+
+/* ------------------------------------------------------------------- */
+/* UPD-specific datatypes */
+/* ------------------------------------------------------------------- */
+
+/**
+int32_t and uint32_t are 32Bit-Integer-Types used in the
+Floyd-Steinberg Algorithm and instead of gx_color_index. The
+8-Byte long's on some 64Bit-Machines are apparently useless,
+since gdevprn.c does (currently) support only 32-Bit Rasterdata.
+*/
+
+#undef INT32_MIN
+#undef INT32_MAX
+#undef UINT32_MAX
+
+#if arch_log2_sizeof_int < 2 /* int is too small */
+#define INT32_MIN LONG_MIN
+#define INT32_MAX LONG_MAX
+#define UINT32_MAX ULONG_MAX
+#else /* int is sufficient */
+#define INT32_MIN INT_MIN
+#define INT32_MAX INT_MAX
+#define UINT32_MAX UINT_MAX
+#endif /* use int or long ? */
+
+/**
+"updcmap" is used by the color-mapping functions of the driver.
+there are four cmaps in the "uniprint"-structure, one for each component.
+To be exact, it's not "4" but rather "UPD_CMAP_MAX", which is a synonym.
+*/
+
+typedef struct updcmap_s { /** */
+ gx_color_value *code; /** Values related to codes */
+ uint32_t bitmsk; /** Mask, right justified */
+ int bitshf; /** Shift to right-justify */
+ int xfer; /** Index to the Xfer-Array */
+ int bits; /** # of Bits */
+ int comp; /** Output-Number */
+ bool rise; /* Rising/Falling Curve */
+} updcmap_t, *updcmap_p; /** */
+typedef const updcmap_t *updcmap_pc;
+
+/**
+"updcomp" holds similar informations, but is used for the rendering
+*/
+
+typedef struct updcomp_s { /* Parameters for Floyd-Steinberg */
+ int32_t offset; /* Offset added to scaled values */
+ int32_t scale; /* Scale for the raw values */
+ int32_t threshold; /* Val must be larger than this to fire */
+ int32_t spotsize; /* subtracted from Val when fired */
+ uint32_t bitmsk; /* Mask */
+ int bitshf; /* shift */
+ int bits; /* # of Bits */
+ int cmap; /* Index for the Parameter-name */
+} updcomp_t, *updcomp_p; /* Parameters for Floyd-Steinberg */
+
+/** updscan is the Element of the scan-buffer. */
+
+typedef struct updscan_s { /* Single Scanline (1 Bit/Pixel) */
+ byte *bytes; /* Buffer used w. 32-Bit Words */
+ int *xbegin; /* 1st Pixel set (or nbytes<<3 if none) */
+ int *xend; /* last Pixel set (or -1, if none) */
+} updscan_t, *updscan_p; /* Single Scanline (1 Bit/Pixel) */
+
+/** Main upd-Structure ***/
+
+#define UPD_CMAP_MAX 4 /** Number of Colormaps provided */
+#define UPD_VALPTR_MAX 32 /** Number of valbuf-Pointers */
+
+#define upd_proc_pxlget(name) uint32_t name(upd_p upd)
+#define upd_proc_render(name) int name(upd_p upd)
+#define upd_proc_writer(name) int name(upd_p upd,FILE *out)
+
+struct upd_s { /* All upd-specific data */
+
+ int *choice; /** Named-Choices */
+ int *ints; /** Integers */
+ gs_param_int_array *int_a; /** Integer-Arrays */
+ gs_param_string *strings; /** Strings */
+ gs_param_string_array *string_a; /** String-Arrays */
+ gs_param_float_array *float_a; /** Float-Arrays */
+
+ updcmap_t cmap[UPD_CMAP_MAX]; /** Mapping-Data */
+
+ byte *gsbuf; /* Storage for GS-Rasterdata */
+ byte *gsscan; /* Begin of GS-Rasterdata */
+
+ byte *pxlptr; /* Source for pxlget */
+ upd_proc_pxlget( (*pxlget)); /* The Pixel-Reader */
+ upd_proc_render( (*render)); /* Actual Rendering */
+ upd_proc_writer( (*writer));
+
+ updscan_p *scnbuf; /* Output-Values */
+ int32_t *valbuf; /* Floyd-Steinberg-Buffer */
+ void *valptr[UPD_VALPTR_MAX];
+
+ byte *outbuf; /* Output-Buffer */
+ upd_proc_render( (*start_render)); /* Setup for rendering */
+ upd_proc_writer( (*start_writer)); /* Setup for writilg */
+
+ uint32_t flags; /** Some flags */
+ int pdwidth; /** pdev-width upon open */
+ int pdheight; /** pdev-height upon open */
+
+ uint ngsbuf; /* Size of gsbuf */
+ int gswidth; /* Width in GS-Pixels */
+ int gsheight; /* Height in GS-Pixels */
+
+ int rwidth; /* Rendering-Width */
+
+ int pwidth; /* Printing-Width */
+ int pheight; /* # scanlines printed */
+
+ int ncomp; /* # Components in gsbuf */
+ int nmap; /* # Entries in color-map */
+
+ uint nvalbuf; /* Size of valbuf */
+ int nscnbuf; /* Number of entries in scnbuf. */
+
+ int ocomp; /* # Components written */
+ int nbytes; /* Size of scnbuf[][].words */
+ int nlimits; /* Size of scnbuf[][].xbegin/end */
+ int scnmsk; /* Size of scanbuf - 1 */
+ uint noutbuf; /* Size of the Output-Buffer */
+
+ int ixpass; /* Current X-pass (0 ... nxpass-1) */
+ int ipass; /* Current pass (0 ... npass-1) */
+ int icomp; /* Selected Component */
+ int lf; /* Selected Line-Space */
+
+ int xprinter; /* Actual X-Position */
+
+ int yscan; /* Top-Scan (page-vari) */
+ int yprinter; /* Actual Y-Position (page-vari) */
+ int yscnbuf; /* Y not yet buffered */
+ const gs_memory_t *memory; /* Memory pointer - for errprintf */
+}; /* All upd-specific data */
+
+/* ------------------------------------------------------------------- */
+/* Various Message-Levels */
+/* ------------------------------------------------------------------- */
+
+/**
+UPD_MESSAGES, Is collection of Bits, that controls Messages
+*/
+
+#define UPD_M_NONE 0x0000 /** No Messages at all */
+#define UPD_M_ERROR 0x0001 /** Errors */
+#define UPD_M_WARNING 0x0002 /** Warnings */
+#define UPD_M_TOPCALLS 0x0004 /** Log Calls to main Functions */
+#define UPD_M_MAPCALLS 0x0008 /** Log Color-Mapping-Calls */
+#define UPD_M_SETUP 0x0010 /** Log Setup-Activity */
+#define UPD_M_FSBUF 0x0020 /** Error-Summary for valbuf */
+#define UPD_M_FMTVARS 0x0040 /** (GR) Formatting variables */
+
+/* ------------------------------------------------------------------- */
+/* The UPD-Routines */
+/* ------------------------------------------------------------------- */
+
+/**
+Besides the main routines required for the color-mapping, that were
+declared near the beginning, there are some auxillary functions.
+Most prominent are "upd_open_map" and "upd_close_map", which
+do the proper actions when opening and closing the device.
+*/
+
+static int upd_open_map( upd_device *udev);
+static int upd_close_map(upd_device *udev);
+
+/**
+But "upd_truncate" and "upd_expand" are also mentionable. They are
+the actual workhorses for the component-oriented mapping. When mapping
+the 16Bit Component-Values to the indices, some truncation takes place
+and this is what "upd_truncate" does, in the most general manner i can
+think of and with O(log(n)) in time. "upd_expand" is required for the
+reverse mapping-functions and is a constant-time `algorithm'.
+*/
+static inline uint32_t upd_truncate(upd_pc,int,gx_color_value);
+
+/* ------------------------------------------------------------------- */
+/* Return the gx_color_value for a given component */
+/* ------------------------------------------------------------------- */
+static inline gx_color_value
+upd_expand(upd_pc upd,int i,gx_color_index ci0)
+{
+ const updcmap_pc cmap = upd->cmap + i; /* Writing-Shortcut */
+ uint32_t ci = (uint32_t)((ci0 >> cmap->bitshf) & cmap->bitmsk); /* Extract the component */
+
+ if(!cmap->rise) ci = cmap->bitmsk - ci; /* Invert, if necessary */
+/* no Truncation/Expansion on full range */
+ if(gx_color_value_bits > cmap->bits) return cmap->code[ci];
+ else return (gx_color_value) ci;
+}
+/* That's simple, isn't it? */
+
+/**
+The next group of internal functions adresses the rendering. Besides
+the main-functions "upd_open_render" and "upd_close_render", there
+are groups of up to 3 Functions, for each algorithm available with
+UPD. Two routines are invoked during open and close and the third
+is called for each scanline. Actually a fourth function is provided,
+that is invoked at the beginning of each page to be printed, but the
+current algorithms do not need it.
+*/
+static void upd_open_render( upd_device *udev);
+static void upd_close_render( upd_device *udev);
+
+static void upd_open_fscomp( upd_device *udev);
+static int upd_fscomp( upd_p upd);
+static void upd_close_fscomp( upd_device *udev);
+
+static void upd_open_fscmyk( upd_device *udev);
+static int upd_fscmyk( upd_p upd);
+
+static void upd_open_fscmy_k( upd_device *udev);
+static int upd_fscmy_k( upd_p upd);
+
+/**
+I hope that the formatting stuff can be kept simple and thus most
+of the work is done inside the general open and close-functions.
+During open, there is a call to a format-specific open-function, but
+this is only for checking and determining the amount of of bytes required
+for the output-buffer (and limit-values in the scan-buffer).
+*/
+static int upd_open_writer( upd_device *udev);
+static void upd_close_writer( upd_device *udev);
+#if UPD_SIGNAL
+static void upd_signal_handler(int sig);
+#endif
+
+/**
+The first format are the uncompressed! SUN-Rasterfiles. The primary intention
+of this format is testing, but it might turn out to be useful for other
+purposes, even if the amount of generated data is huge. On the other hand
+it is a violation of UPD's rules: the start-routine computes the Begin-Page
+sequence (the Rasterfile header) since it would be a nuisance to provide
+this code within each (test-)personalization in PostScript.
+*/
+static int upd_open_rascomp( upd_device *udev);
+static int upd_start_rascomp( upd_p upd, FILE *out);
+static int upd_rascomp( upd_p upd, FILE *out);
+
+/**
+The second format is ESC/P, the format introduced with the first Epson
+impact printers. This format is used by a lot of other printers too.
+It is also uncompressed. This formatter supports X- and Y-Weaving,
+which makes it the most sophisticated one inside this driver.
+*/
+
+static void upd_limits( upd_p upd, bool check);
+static int upd_open_wrtescp( upd_device *udev);
+static int upd_wrtescp( upd_p upd, FILE *out);
+
+/**
+The third format is ESC/P2, the format use by the newer Epson-Printers.
+It allows runlength-Compression similar to the RTL/PCL-Family of Printers.
+This formatter does not allow for X-Weaving.
+
+The fourth writer is a ESC/P2-Writer, that supports X-Weaving
+*/
+static int upd_rle(byte *out,const byte *in,int nbytes);
+static int upd_open_wrtescp2( upd_device *udev);
+static int upd_wrtescp2( upd_p upd, FILE *out);
+static int upd_wrtescp2x( upd_p upd, FILE *out);
+
+/**
+The fifth writer is a HP-RTL/PCL-Writer
+*/
+
+static int upd_open_wrtrtl( upd_device *udev);
+static int upd_wrtrtl( upd_p upd, FILE *out);
+
+/**
+The sixth writer is for Canon Extended Mode (currently BJC610) (hr)
+*/
+
+static int upd_open_wrtcanon( upd_device *udev);
+static int upd_wrtcanon( upd_p upd, FILE *out);
+
+/**
+The seventh writer is for ESC P/2 Nozzle Map Mode (currently Stylus Color 300) (GR)
+*/
+
+static int upd_wrtescnm( upd_p upd, FILE *out);
+
+/**
+Generalized Pixel Get & Read
+*/
+static uint32_t upd_pxlfwd(upd_p upd);
+static uint32_t upd_pxlrev(upd_p upd);
+#define upd_pxlget(UPD) (*UPD->pxlget)(UPD)
+
+static void *upd_cast(const void *);
+
+/* ------------------------------------------------------------------- */
+/* Macros to deal with the Parameter-Memory */
+/* ------------------------------------------------------------------- */
+
+/**
+Usually the creation of copies of external parameters is not necessary,
+at least with gs-versions > 4.03. But uniprint writes to the parameters
+in some cases or creates some by itself, thus to get a unified interface
+all parameter-data are copied and thus it is legal to manipulate them.
+
+Here are several Macros, named "UPD_MM_*" to deal with that.
+*/
+
+/** UPD_MM_GET_ARRAY allocates & initializes an array of values */
+#define UPD_MM_GET_ARRAY(mem, Which,Nelts) \
+ Which = NULL; \
+ if(0 < (Nelts)) { \
+ byte *tmp = gs_malloc(mem, Nelts,sizeof(Which[0]),"uniprint/params");\
+ if(tmp) { \
+ memset(tmp,0,(Nelts)*sizeof(Which[0])); \
+ Which = (void *) tmp; \
+ } else { \
+ return_error(gs_error_VMerror); \
+ } \
+ }
+
+/** UPD_MM_DEL_ARRAY frees an array of values */
+#define UPD_MM_DEL_ARRAY(mem, Which,Nelts,Delete) \
+ if(Which && 0 < (Nelts)) { \
+ uint ii; \
+ for(ii = 0; (Nelts) > ii; ++ii) Delete(mem, Which[ii]); \
+ gs_free(mem, upd_cast(Which),Nelts,sizeof(Which[0]),"uniprint/params");\
+ } \
+ Which = 0
+
+/** UPD_MM_DEL_VALUE deletes a value, does nothing */
+#define UPD_MM_DEL_VALUE(mem, Which) /* */
+
+/** UPD_MM_DEL_PARAM deletes a single gs-array-parameter */
+#define UPD_MM_DEL_PARAM(mem, Which) { \
+ if(Which.data && Which.size) \
+ gs_free(mem, upd_cast(Which.data),Which.size,sizeof(Which.data[0]),\
+ "uniprint/params"); \
+}
+
+/** UPD_MM_DEL_APARAM deletes a nested gs-array-parameter */
+#define UPD_MM_DEL_APARAM(mem, Which) { \
+ if(Which.data && Which.size) { \
+ uint iii; \
+ for(iii = 0; iii < Which.size; ++iii) \
+ UPD_MM_DEL_PARAM(mem, Which.data[iii]); \
+ gs_free(mem, upd_cast(Which.data),Which.size,sizeof(Which.data[0]),\
+ "uniprint/params"); \
+ } \
+}
+
+/** UPD_MM_CPY_ARRAY creates a new copy of an array of values */
+#define UPD_MM_CPY_ARRAY(mem, To,From,Nelts,Copy) \
+ UPD_MM_GET_ARRAY(mem, To,Nelts); \
+ if(To && From) { \
+ uint ii; \
+ for(ii = 0; (Nelts) > ii; ++ii) Copy(mem, To[ii],From[ii]);\
+ }
+
+/** UPD_MM_CPY_VALUE Copies a simple Value */
+#define UPD_MM_CPY_VALUE(mem,To,From) To = From
+
+#define UPD_MM_CPY_VALUE_3(mem,To,From) To = From
+
+/** UPD_MM_CPY_PARAM Creates a copy of a gs-parameter */
+#define UPD_MM_CPY_PARAM(mem, To, From) \
+ if(From.data && From.size) { \
+ UPD_MM_GET_ARRAY(mem, To.data,From.size); \
+ if(To.data) { \
+ To.size = From.size; \
+ memcpy(upd_cast(To.data),From.data,To.size*sizeof(To.data[0]));\
+ } \
+ }
+
+/** UPD_MM_CPY_APARAM Creates a copy of a nested gs-parameter */
+#define UPD_MM_CPY_APARAM(mem, To,From) \
+ if(From.data && From.size) { \
+ UPD_MM_GET_ARRAY(mem, To.data,From.size); \
+ if(To.data) { \
+ gs_param_string *tmp2 = (gs_param_string *) upd_cast(To.data);\
+ uint iii; \
+ To.size = From.size; \
+ for(iii = 0; To.size > iii; ++iii) \
+ UPD_MM_CPY_PARAM(mem, tmp2[iii],From.data[iii]); \
+ } \
+ }
+
+/* ------------------------------------------------------------------- */
+/* UPD-Initialized-Data */
+/* ------------------------------------------------------------------- */
+
+/** Version-String */
+
+static const char rcsid[] = "$Revision: 5215 $";
+
+/** Default-Transfer-curve */
+
+static const float upd_data_xfer[2] = { 0.0, 1.0 };
+
+/*@ > */
+
+/* ------------------------------------------------------------------- */
+/* upd_cast: keeps some compilers more happy [dangerous] */
+/* ------------------------------------------------------------------- */
+
+static void *
+upd_cast(const void *data)
+{
+ return (void *) data;
+}
+
+/* ------------------------------------------------------------------- */
+/* upd_signal_handler: Catch interrupts */
+/* ------------------------------------------------------------------- */
+
+#if UPD_SIGNAL
+static upd_p sigupd = NULL;
+static void
+upd_signal_handler(int sig)
+{
+ if(sigupd) sigupd->flags |= B_ABORT;
+}
+#endif
+
+/* ------------------------------------------------------------------- */
+/* upd_print_page: The main workhorse */
+/* ------------------------------------------------------------------- */
+
+/**
+Function: upd_print_page
+
+This is the top-level printing routine. It works through this
+steps:
+
+ 1. Once for each generated file, the "device-open-sequence" is written.
+ 2. The "page-begin-sequence" is written.
+
+ 3. The data are generated and written:
+ 3.1: Data are converted into a "printer-family"-specific format.
+ This step includes the halftoning, if selected.
+ 3.2: Data are written with a printer-specific function.
+ There is not much code-compression inside theese functions,
+ since i observed to improvments in print-speed. Other
+ drivers do a better job in this.
+
+ 4. The "page-end-sequence" is written.
+ 5. If a one-page-per-file mode is selected, the "device-close-sequence"
+ is added to the output. For multi-page files, this writing is
+ performed in "upd_close", the drivers close-function.
+
+The routine is quite short, since all the allocation and checking
+occur in upd_open and upd_putparams. The only test, that upd_print_page
+does, is the verification wether the device is in a sane state. This
+must be done here, since during the initialisation, the device is
+usually opened several times, before obtaining a valid state.
+*/
+
+static int
+upd_print_page(gx_device_printer *pdev, FILE *out)
+{
+ upd_device *const udev = (upd_device *) pdev;
+ const upd_p upd = udev->upd;
+ const int *const ints = upd ? upd->ints : NULL;
+ int error,need,yfill;
+
+#if UPD_SIGNAL /* variables required for signal-handling only */
+ void (*oldint )(int) = NULL;
+ void (*oldterm)(int) = NULL;
+ upd_p oldupd = sigupd;
+#endif /* variables required for signal-handling only */
+
+/*
+ * Refuse to work, if not explicitly enabled during open
+ * (some/lot of allocated memory is required)
+ */
+ if(!upd || B_OK4GO != (upd->flags & (B_OK4GO | B_ERROR))) {
+#if UPD_MESSAGES & (UPD_M_ERROR | UPD_M_TOPCALLS)
+ errprintf(pdev->memory, "CALL-REJECTED upd_print_page(0x%05lx,0x%05lx)\n",
+ (long) udev,(long) out);
+#endif
+ return gs_error_undefined;
+ }
+
+#if UPD_MESSAGES & UPD_M_TOPCALLS
+ errprintf(pdev->memory, "CALL: upd_print_page(0x%05lx,0x%05lx)\n",
+ (long) udev,(long) out);
+#endif
+
+#if UPD_SIGNAL /* Setup of signal-handling */
+ sigupd = upd;
+ oldint = signal(SIGINT, upd_signal_handler);
+ oldterm = signal(SIGTERM,upd_signal_handler);
+#endif /* Setup of signal-handling */
+
+/*
+ * If the OutputFile was just opened, transfer the Open-Sequence to it.
+ */
+ if(!(upd->flags & B_OPEN)) {
+
+ if(0 < upd->strings[S_OPEN].size)
+ fwrite(upd->strings[S_OPEN].data,1,upd->strings[S_OPEN].size,out);
+ upd->flags |= B_OPEN;
+ }
+/*
+ * Always write the the Page-begin-sequence
+ */
+ if(0 < upd->strings[S_BEGIN].size)
+ fwrite(upd->strings[S_BEGIN].data,1,upd->strings[S_BEGIN].size,out);
+/*
+ * Establish page-variables
+ */
+
+/* Positions */
+ upd->xprinter = 0;
+ upd->yscan = 0; /* Position we are processing */
+ upd->yprinter = 0; /* Actual Printer-Positions */
+ upd->yscnbuf = 0; /* Next free scnbuf-Line */
+
+/* Rendering & Writing Setup, if available */
+ if(upd->start_render) (*upd->start_render)(upd);
+ if(upd->start_writer) (*upd->start_writer)(upd,out);
+
+/* How many scanlines do we need ? */
+ need = ints[I_NYPASS] * ints[I_PINS2WRITE];
+ if(0 >= need) need = 1;
+
+/* The Weave-counters */
+ upd->ipass = 0;
+ upd->ixpass = 0;
+ upd->icomp = -1; /* Enforces initial selection */
+ upd->lf = -1; /* Enforces initial selection */
+/*
+ * Main Loop
+ */
+ while(upd->pheight > upd->yscan) { /* Main-Loop */
+
+/*
+ * Load as much data into the scan-buffer as possible
+ * (this is done in scan-sequence, the printing not necessarily.)
+ */
+ if(ints[I_BEGSKIP] > upd->yscan) yfill = 0;
+ else yfill = upd->yscan - ints[I_BEGSKIP];
+
+ for(yfill += upd->nscnbuf; upd->yscnbuf < yfill; upd->yscnbuf++) {
+
+ if(upd->gsheight > upd->yscnbuf) {
+
+ if(0 > (*dev_proc(udev,get_bits))((gx_device *) udev,
+ upd->yscnbuf,upd->gsbuf,&upd->gsscan)) {
+#if UPD_MESSAGES & UPD_M_WARNING
+ errprintf(udev->memory, "get_bits aborted with error, yscnbuf = %4d\n",
+ upd->yscnbuf);
+#endif
+ break;
+ }
+ } else {
+
+ memset(upd->gsscan = upd->gsbuf,0,upd->ngsbuf);
+
+ }
+
+ if(0 > (*upd->render)(upd)) {
+#if UPD_MESSAGES & UPD_M_WARNING
+ errprintf(udev->memory, "Rendering aborted with error, yscnbuf = %4d\n",
+ upd->yscnbuf);
+#endif
+ break;
+ }
+
+ }
+/*
+ * Did the buffering loop take an error exit ?
+ */
+ if((upd->yscnbuf ^ yfill) & upd->scnmsk) break;
+/*
+ * Print as much as possible
+ */
+ while((upd->yscan - ints[I_BEGSKIP] + need) < upd->yscnbuf) {
+
+/* first write the scan(s) */
+ (*upd->writer)(upd,out);
+
+/* Check for termination */
+ if(upd->yscan >= upd->pheight) break;
+ if(upd->flags & B_ABORT ) {
+#if UPD_MESSAGES & UPD_M_WARNING
+ errprintf(udev->memory, "Printing aborted upon interrupt, yscan = %4d\n",
+ upd->yscan);
+#endif
+ break;
+ }
+ }
+/*
+ * Did the print-Loop take an error exit ?
+ */
+ if((upd->yscan - ints[I_BEGSKIP] + need) < upd->yscnbuf) break;
+ } /* Main-Loop */
+
+/*
+ * If we aborted for some reason, use the dedicated sequence
+ */
+
+ if((upd->pheight > upd->yscan) &&
+ (0 < upd->strings[S_ABORT].size)) { /* Only This! */
+ fwrite(upd->strings[S_ABORT].data,1,upd->strings[S_ABORT].size,out);
+
+ upd->flags &= ~B_OPEN; /* Inhibit Close-Sequence ! */
+/*
+ * If there is no special sequence, or we came to normal end,
+ * write the normal sequence, if any
+ */
+
+ } else if(0 < upd->strings[S_END].size) {
+ fwrite(upd->strings[S_END].data,1,upd->strings[S_END].size,out);
+ }
+/*
+ * If necessary, write the close-sequence
+ */
+ {
+ gs_parsed_file_name_t parsed;
+ const char *fmt;
+
+ if (NULL != udev->fname &&
+ 0 <= gx_parse_output_file_name(&parsed, &fmt, udev->fname,
+ strlen(udev->fname), udev->memory) &&
+ fmt
+ ) {
+ if (0 < upd->strings[S_CLOSE].size)
+ fwrite(upd->strings[S_CLOSE].data,1,upd->strings[S_CLOSE].size,out);
+ upd->flags &= ~B_OPEN;
+ }
+ }
+
+/*
+ * clean up, and return status
+ */
+
+ fflush(out); /* just to prepare for ferror */
+
+ if(upd->pheight > upd->yscan) error = gs_error_interrupt;
+ else if(ferror(out)) error = gs_error_ioerror;
+ else error = 0;
+
+#if UPD_MESSAGES & UPD_M_TOPCALLS
+ errprintf(udev->memory, "RETURN: %d = upd_print_page(0x%05lx,0x%05lx)\n",
+ error,(long) udev,(long)out);
+#endif
+
+#if UPD_SIGNAL /* Restore Interrupt-state */
+ sigupd = oldupd;
+ (void) signal(SIGINT ,oldint);
+ (void) signal(SIGTERM,oldterm);
+#endif /* Restore Interrupt-state */
+
+ return error;
+}
+
+/* ------------------------------------------------------------------- */
+/* upd_open: Initialize everything for printing */
+/* ------------------------------------------------------------------- */
+/**
+"upd_open" is -through the specified table of procedures- called instead
+of the normal open-procedures for printer-devices, that performs quite
+a complex job. Thus it is necessary to call this `superclass-open´
+here.
+
+Besides that, this routine does quite a complex job too, in initializes
+everything required to print a page. This might be time-consuming, the
+alternative would be "upd_print_page", but i often print 100 pages or
+more, but i never experienced more than 5-6 open-calls.
+*/
+
+static int
+upd_open(gx_device *pdev)
+{
+ upd_device *udev = (upd_device *) pdev;
+ upd_p upd = udev->upd;
+ int error;
+
+#if UPD_MESSAGES & UPD_M_TOPCALLS
+ errprintf(udev->memory, "CALL: upd_open(0x%05lx)\n",(long) pdev);
+#endif
+
+/** enforce the UPD-Margins */
+
+ if((NULL != upd) &&
+ (NULL != upd->float_a[FA_MARGINS].data) &&
+ (4 == upd->float_a[FA_MARGINS].size) ) {
+ float m[4];
+ m[1] = upd->float_a[FA_MARGINS].data[1] / 72.0;
+ m[3] = upd->float_a[FA_MARGINS].data[3] / 72.0;
+ if(B_YFLIP & upd->flags) {
+ m[0] = upd->float_a[FA_MARGINS].data[2] / 72.0;
+ m[2] = upd->float_a[FA_MARGINS].data[0] / 72.0;
+ } else {
+ m[0] = upd->float_a[FA_MARGINS].data[0] / 72.0;
+ m[2] = upd->float_a[FA_MARGINS].data[2] / 72.0;
+ }
+ gx_device_set_margins((gx_device *) udev, m, true);
+ }
+
+/** call the super-class open **/
+ error = gdev_prn_open(pdev);
+ while (pdev->child)
+ pdev = pdev->child;
+
+ udev = (upd_device *) pdev;
+ upd = udev->upd;
+
+/** invoke the subroutines, if an upd is present. */
+
+ if(upd) {
+
+ upd->flags &= ~B_OK4GO;
+
+/**
+The following initializations are run, even in case of an error in
+the super-class open, just to bring our upd into a sane state.
+*/
+ if(0 > error) upd->flags |= B_ERROR;
+
+ if(gs_error_VMerror == upd_open_map(udev)) error = gs_error_VMerror;
+
+/**
+The following piece of code is here for demonstration-purposes:
+It determines the size of the printed image and allocates the
+buffer for the raw raster-data
+*/
+ upd->gswidth = udev->width -
+ (int)((dev_l_margin(udev)+dev_r_margin(udev))*udev->x_pixels_per_inch);
+
+ upd->gsheight = udev->height -
+ (int)((dev_t_margin(udev)+dev_b_margin(udev))*udev->y_pixels_per_inch);
+
+ upd->ngsbuf = 0; /* Ensure sane values */
+ upd->gsbuf = NULL; /* Ensure sane values */
+
+ if(B_MAP & upd->flags) { /* Only if prerequisites were met */
+ uint want = gx_device_raster(pdev,true);
+ upd->gsbuf = gs_malloc(pdev->memory, want,1,"upd/gsbuf");
+
+ if(upd->gsbuf) {
+ upd->ngsbuf = want;
+ upd->flags |= B_BUF; /* Signal Success */
+ } else {
+ error = gs_error_VMerror; /* Signal Error */
+ upd->flags |= B_ERROR;
+ }
+
+ } /* Only if prerequisites were met */
+
+ upd_open_render(udev); /* First subloop in printing */
+
+ if(gs_error_VMerror == upd_open_writer(udev)) error = gs_error_VMerror;
+
+ udev->upd->pdwidth = udev->width;
+ udev->upd->pdheight = udev->height;
+
+#if UPD_MESSAGES & UPD_M_SETUP
+ if((upd->flags & (B_OK4GO | B_ERROR)) == B_OK4GO) {
+ int i,j,l,ln,lv;
+ errprintf(udev->memory,"\nupd->flags = 0x%05lx\n",(unsigned long)upd->flags);
+ errprintf(udev->memory, "upd->pdwidth = %5d\n",upd->pdwidth);
+ errprintf(udev->memory, "upd->pdheight = %5d\n",upd->pdheight);
+ errprintf(udev->memory, "upd->ngsbuf = %5u\n",upd->ngsbuf);
+ errprintf(udev->memory, "upd->gswidth = %5d\n",upd->gswidth);
+ errprintf(udev->memory, "upd->gsheight = %5d\n",upd->gsheight);
+ errprintf(udev->memory, "upd->rwidth = %5d\n",upd->rwidth);
+ errprintf(udev->memory, "upd->pwidth = %5d\n",upd->pwidth);
+ errprintf(udev->memory, "upd->pheight = %5d\n",upd->pheight);
+ errprintf(udev->memory, "upd->nvalbuf = %5u\n",upd->nvalbuf);
+ errprintf(udev->memory, "upd->nscnbuf = %5d\n",upd->nscnbuf);
+ errprintf(udev->memory, "upd->ncomp = %5d\n",upd->ncomp);
+ errprintf(udev->memory, "upd->ocomp = %5d\n",upd->ocomp);
+ errprintf(udev->memory, "upd->nbytes = %5d\n",upd->nbytes);
+ errprintf(udev->memory, "upd->nlimits = %5d\n",upd->nlimits);
+ errprintf(udev->memory, "upd->scnmsk = %5d\n",upd->scnmsk);
+ errprintf(udev->memory, "upd->noutbuf = %5u\n",upd->noutbuf);
+ errprintf(udev->memory, "upd->ixpass = %5d\n",upd->ixpass);
+ errprintf(udev->memory, "upd->ipass = %5d\n",upd->ipass);
+ errprintf(udev->memory, "upd->icomp = %5d\n",upd->icomp);
+ errprintf(udev->memory, "upd->lf = %5d\n",upd->lf);
+ errprintf(udev->memory, "upd->xprinter = %5d\n",upd->xprinter);
+ errprintf(udev->memory, "upd->yscan = %5d\n",upd->yscan);
+ errprintf(udev->memory, "upd->yprinter = %5d\n",upd->yprinter);
+ errprintf(udev->memory, "upd->yscnbuf = %5d\n",upd->yscnbuf);
+
+ ln = 13;
+ lv = 5;
+ for(i = 0; countof(upd_choice) > i; ++i) {
+ if(!upd_choice[i]) continue;
+ l = strlen(upd_choice[i][0]);
+ if(ln < l) ln = l;
+ for(j = 1; upd_choice[i][j]; ++j) {
+ l = strlen(upd_choice[i][j]);
+ if(lv < l) lv = l;
+ }
+ }
+
+ for(i = 0; countof(upd_flags) > i; ++i) {
+ if(upd_flags[i]) {
+ l = strlen(upd_flags[i]);
+ if(ln < l) ln = l;
+ }
+ }
+
+ for(i = 0; countof(upd_ints) > i; ++i) {
+ if(upd_ints[i]) {
+ l = strlen(upd_ints[i]);
+ if(ln < l) ln = l;
+ }
+ }
+
+ for(i = 0; countof(upd_int_a) > i; ++i) {
+ if(upd_int_a[i]) {
+ l = strlen(upd_int_a[i]);
+ if(ln < l) ln = l;
+ }
+ }
+
+ for(i = 0; countof(upd_strings) > i; ++i) {
+ if(upd_strings[i]) {
+ l = strlen(upd_strings[i]);
+ if(ln < l) ln = l;
+ }
+ }
+
+ for(i = 0; countof(upd_string_a) > i; ++i) {
+ if(upd_string_a[i]) {
+ l = strlen(upd_string_a[i]);
+ if(ln < l) ln = l;
+ }
+ }
+
+ for(i = 0; countof(upd_float_a) > i; ++i) {
+ if(upd_float_a[i]) {
+ l = strlen(upd_float_a[i]);
+ if(ln < l) ln = l;
+ }
+ }
+
+ for(i = 0; countof(upd_choice) > i; ++i) {
+ if(upd_choice[i]) {
+ errprintf(udev->memory,"%*s = %-*s (%2d)\n",ln,upd_choice[i][0],
+ lv,upd_choice[i][upd->choice[i]],upd->choice[i]);
+ } else {
+ errprintf(udev->memory,"%*s[%2d] = %2d\n",ln-4,"upd_choice",i,
+ upd->choice[i]);
+ }
+ }
+
+ for(i = 0; countof(upd_flags) > i; ++i) {
+ if(upd_flags[i]) {
+ errprintf(udev->memory,"%*s = %s\n",ln,upd_flags[i],
+ ((uint32_t) 1 << i) & upd->flags ? "true" : "false");
+ } else {
+ errprintf(udev->memory,"%*s[%2d] = %s\n",ln-4,"upd_flags",i,
+ ((uint32_t) 1 << i) & upd->flags ? "true" : "false");
+
+ }
+ }
+
+ for(i = 0; countof(upd_ints) > i; ++i) {
+ if(upd_ints[i]) {
+ errprintf(udev->memory,"%*s = %5d\n",ln,upd_ints[i],upd->ints[i]);
+ } else {
+ errprintf(udev->memory,"%*s[%2d] = %5d\n",ln-4,"upd_ints",i,upd->ints[i]);
+ }
+ }
+
+ }
+
+ errprintf(udev->memory,"\n%sready to print\n\n",
+ B_OK4GO != (upd->flags & (B_OK4GO | B_ERROR)) ?
+ "NOT " : "");
+#endif
+
+ }
+
+#if UPD_MESSAGES & UPD_M_TOPCALLS
+ errprintf(udev->memory,"RETURN: %d = upd_open(0x%05lx)\n",
+ error,(long) pdev);
+#endif
+
+ return error;
+}
+
+/* ------------------------------------------------------------------- */
+/* upd_close: Release everything allocated in upd_open */
+/* ------------------------------------------------------------------- */
+
+static int
+upd_close(gx_device *pdev)
+{
+ upd_device *const udev = (upd_device *) pdev;
+ const upd_p upd = udev->upd;
+ int error = 0;
+ int code;
+
+#if UPD_MESSAGES & UPD_M_TOPCALLS
+ errprintf(udev->memory,"CALL: upd_close(0x%05lx)\n",(long)pdev);
+#endif
+
+/** If necessary, write the close-sequence **/
+
+ if( upd && (( B_OPEN | B_OK4GO) ==
+ ((B_OPEN | B_OK4GO | B_ERROR) & upd->flags))) {
+
+ if(udev->file && upd->strings && 0 < upd->strings[S_CLOSE].size)
+ fwrite(upd->strings[S_CLOSE].data,1,
+ upd->strings[S_CLOSE].size,udev->file);
+
+ upd->flags &= ~B_OPEN;
+ }
+
+/** Then release the open-allocated memory */
+ if(upd) {
+
+ upd_close_writer(udev);
+
+ if(upd->gsbuf)
+ gs_free(pdev->memory, upd->gsbuf,upd->ngsbuf,1,"uniprint/gsbuf");
+ upd->gsbuf = NULL;
+ upd->ngsbuf = 0;
+ upd->flags &= ~B_BUF;
+
+ upd_close_render(udev);
+ upd_close_map(udev);
+
+ UPD_MM_DEL_ARRAY(pdev->memory, upd->choice, countof(upd_choice), UPD_MM_DEL_VALUE);
+ UPD_MM_DEL_ARRAY(pdev->memory, upd->ints, countof(upd_ints), UPD_MM_DEL_VALUE);
+ UPD_MM_DEL_ARRAY(pdev->memory, upd->int_a, countof(upd_int_a), UPD_MM_DEL_PARAM);
+ UPD_MM_DEL_ARRAY(pdev->memory, upd->strings, countof(upd_strings), UPD_MM_DEL_PARAM);
+ UPD_MM_DEL_ARRAY(pdev->memory, upd->string_a,countof(upd_string_a),UPD_MM_DEL_APARAM);
+ UPD_MM_DEL_ARRAY(pdev->memory, upd->float_a, countof(upd_float_a), UPD_MM_DEL_PARAM);
+
+ gs_free(pdev->memory, upd,sizeof(upd[0]),1,"uniprint");
+
+ udev->upd = NULL;
+ }
+
+/** Then call the superclass close **/
+ code = gdev_prn_close(pdev);
+ error = error > code ? code : error;
+
+#if UPD_MESSAGES & UPD_M_TOPCALLS
+ errprintf(pdev->memory,"RETURN: %d = upd_close(0x%05lx)\n",
+ error,(long) pdev);
+#endif
+
+ return error;
+}
+
+/* ------------------------------------------------------------------- */
+/* upd_get_params: Export Parameters to the Interpreter */
+/* ------------------------------------------------------------------- */
+
+#if UPD_MESSAGES & UPD_M_TOPCALLS
+#define UPD_EXIT_GET(Err,Dev,List) \
+ if(0 > Err) { \
+ errprintf(Dev->memory,"RETURN-%d: %d upd_get_params(0x%05lx,0x%05lx)\n", \
+ __LINE__,Err,(long) Dev,(long) List); \
+ return_error(Err); \
+ }
+#else
+#define UPD_EXIT_GET(Err,Dev,List) if(0 > Err) return_error(Err);
+#endif
+
+static int
+upd_get_params(gx_device *pdev, gs_param_list *plist)
+{
+ upd_device *const udev = (upd_device *) pdev;
+ const upd_p upd = udev->upd;
+ int error,i;
+
+#if UPD_MESSAGES & UPD_M_TOPCALLS
+ errprintf(udev->memory,"CALL: upd_get_params(0x%05lx,0x%05lx)\n",
+ (long) udev,(long) plist);
+#endif
+
+/** Call the SuperClass-get_params at the beginning */
+ error = gdev_prn_get_params((gx_device *)udev,plist);
+ UPD_EXIT_GET(error,udev,plist);
+
+/** Export the version */
+ if(upd_version) { /* Version-Export enabled */
+ udev->upd_version.data = (const byte *) rcsid;
+ udev->upd_version.size = strlen(rcsid);
+ udev->upd_version.persistent = true;
+ error = param_write_string(plist,upd_version,&udev->upd_version);
+ UPD_EXIT_GET(error,udev,plist);
+ } /* Version-Export enabled */
+
+/** Export the Named choices */
+ for(i = 0; i < countof(upd_choice); ++i) {
+ if(!upd_choice[i]) continue; /* Choice-Export disabled */
+ if(upd && upd->choice && upd->choice[i]) {
+ gs_param_string name;
+ name.data = (const byte *) upd_choice[i][upd->choice[i]];
+ name.size = strlen((const char *) name.data);
+ name.persistent = true;
+ error = param_write_name(plist,upd_choice[i][0],&name);
+ } else {
+ error = param_write_null(plist,upd_choice[i][0]);
+ }
+ UPD_EXIT_GET(error,udev,plist);
+ }
+
+/** Export the flags (bool) */
+ for(i = 0; i < countof(upd_flags); ++i) {
+ if(!upd_flags[i]) continue; /* Flag-Export disabled */
+ if(upd) {
+ bool value = upd->flags & ((uint32_t) 1 << i);
+ error = param_write_bool(plist,upd_flags[i],&value);
+ } else {
+ error = param_write_null(plist,upd_flags[i]);
+ }
+ UPD_EXIT_GET(error,udev,plist);
+ }
+
+/** Export the ints */
+ for(i = 0; i < countof(upd_ints); ++i) {
+ if(!upd_ints[i]) continue; /* int-Export disabled */
+ if(upd && upd->ints && upd->ints[i]) {
+ int value = upd->ints[i];
+ error = param_write_int( plist,upd_ints[i],&value);
+ } else {
+ error = param_write_null(plist,upd_ints[i]);
+ }
+ UPD_EXIT_GET(error,udev,plist);
+ }
+
+/** Export the int-arrays */
+ for(i = 0; i < countof(upd_int_a); ++i) {
+ if(!upd_int_a[i]) continue; /* int-Array-Export disabled */
+ if(upd && upd->int_a && upd->int_a[i].size) {
+ error = param_write_int_array( plist,upd_int_a[i],(upd->int_a+i));
+ } else {
+ error = param_write_null(plist,upd_int_a[i]);
+ }
+ UPD_EXIT_GET(error,udev,plist);
+ }
+
+/** Export the strings */
+ for(i = 0; i < countof(upd_strings); ++i) {
+ if(!upd_strings[i]) continue; /* String-Export disabled */
+ if(upd && upd->strings && upd->strings[i].size) {
+ error = param_write_string( plist,upd_strings[i],(upd->strings+i));
+ } else {
+ error = param_write_null(plist,upd_strings[i]);
+ }
+ UPD_EXIT_GET(error,udev,plist);
+ }
+
+/** Export the string-Arrays */
+ for(i = 0; i < countof(upd_string_a); ++i) {
+ if(!upd_string_a[i]) continue; /* String-Array-Export disabled */
+ if(upd && upd->string_a && upd->string_a[i].size) {
+ error =
+ param_write_string_array( plist,upd_string_a[i],(upd->string_a+i));
+ } else {
+ error = param_write_null(plist,upd_string_a[i]);
+ }
+ UPD_EXIT_GET(error,udev,plist);
+ }
+
+/** Export the float-Arrays */
+ for(i = 0; i < countof(upd_float_a); ++i) {
+ if(!upd_float_a[i]) continue; /* Float-Array-Export disabled */
+ if(upd && upd->float_a && upd->float_a[i].size) {
+ error =
+ param_write_float_array( plist,upd_float_a[i],(upd->float_a+i));
+ } else {
+ error = param_write_null(plist,upd_float_a[i]);
+ }
+ UPD_EXIT_GET(error,udev,plist);
+ }
+
+#if UPD_MESSAGES & UPD_M_TOPCALLS
+ errprintf(udev->memory,"RETURN: %d = upd_get_params(0x%05lx,0x%05lx)\n",
+ error,(long) udev,(long) plist);
+#endif
+
+ return error;
+}
+
+#undef UPD_EXIT_GET
+
+/* ------------------------------------------------------------------- */
+/* upd_put_params: Load Parameters into the device-structure */
+/* ------------------------------------------------------------------- */
+
+static int
+upd_put_params(gx_device *pdev, gs_param_list *plist)
+{
+ upd_device *const udev = (upd_device *) pdev;
+ upd_p upd = udev->upd;
+ int error = 0, code,i;
+
+ float MarginsHWResolution[2],Margins[2];
+ gx_device_color_info color_info;
+ uint32_t flags = 0;
+ int *choice = NULL;
+ int *ints = NULL;
+ gs_param_int_array *int_a = NULL;
+ gs_param_string *strings = NULL;
+ gs_param_string_array *string_a = NULL;
+ gs_param_float_array *float_a = NULL, mfa;
+
+/**
+Error is used for two purposes: either it holds a negative error
+code or it is used as a bitfield, that tells, which parameters
+were actually loaded. If any of the important parameters changed
+upd_put_params closes the device, since the real parameter-evaluation
+is carried out by upd_open.
+*/
+
+#define UPD_PUT_FLAGS 0x0002
+#define UPD_PUT_CHOICE 0x0004
+#define UPD_PUT_INTS 0x0008
+#define UPD_PUT_INT_A 0x0010
+#define UPD_PUT_STRINGS 0x0020
+#define UPD_PUT_STRING_A 0x0040
+#define UPD_PUT_FLOAT_A 0x0080
+#define UPD_PUT_CHANGEDSIZE 0x0100
+
+#if UPD_MESSAGES & UPD_M_TOPCALLS
+ errprintf(udev->memory,"CALL: upd_put_params(0x%05lx,0x%05lx)\n",
+ (long)udev,(long)plist);
+#endif
+
+/**
+I consider the following part of upd_put_params a bad-nasty-hack-hack
+and i am uncertain, wether it really works in the intended way. I provide it
+just for the case someone is performing nasty-parameter-changes on the
+active device, especially switching the OutputFile. If this happens in
+a situation, where data were written to the file, but the termination
+sequence is required, the driver does it now. (If you want to know, why
+i am writing bad-nasty-hack-hack, visit http://www.zark.com )
+*/
+ if(upd && (B_OPEN & udev->upd->flags) && (NULL != udev->file)) {
+
+ gs_param_string fname = { NULL, 0, false };
+
+ code = param_read_string(plist,"OutputFile",&fname);
+ if((1 != code) && (0 != code)) {
+ code = param_read_null(plist,"OutputFile");
+ if(0 == code) {
+ fname.data = (const byte *) "";
+ fname.size = 0;
+ }
+ }
+
+ if((0 == code) &&
+ strncmp((const char *)fname.data,udev->fname,fname.size)) {
+ if(upd->strings && 0 < udev->upd->strings[S_CLOSE].size)
+ fwrite(upd->strings[S_CLOSE].data,1,
+ upd->strings[S_CLOSE].size,udev->file);
+
+ upd->flags &= ~B_OPEN;
+ }
+ }
+/* Done with the bad-nasty-hack-hack */
+
+/**
+The next thing "upd_put_params" does, is a little strange too. It imports
+a readonly-parameter, the version-string. I do not know wether it is still
+required, but some versions of GHOSTSCRIPT disliked it very much, if an
+existing parameter was not touched by the put-operation.
+
+On the other hand it is the right time to show the basic-outline of the
+parameter-importing flow. Basically the proper "param_read"-procedure
+is called. If it indicated, that the parameter was present, but of the
+wrong type, a read for the null-type is attempted, which is by convention
+somehow an reset to default. This sequence is applied to all the parameters
+and in case of the array-parameters, a succesful null-read is marked by
+setting data and size to 0.
+*/
+#if UPD_MESSAGES & UPD_M_SETUP
+#define UPD_PARAM_READ(Param_read,Name,Object,Mem) \
+ code = Param_read(plist,Name,&Object); \
+ if(0 > code) { \
+ code = param_read_null(plist,Name); \
+ if(0 == code) memset(&Object,0,sizeof(Object));\
+ } \
+ if(!code) errprintf(Mem, \
+ "upd_put_params: retrieved parameter \"%s\"\n",\
+ Name); \
+ if(0 > code) { \
+ param_signal_error(plist,Name,code); \
+ if(error > code) error = code; \
+ }
+#else
+#define UPD_PARAM_READ(Param_read,Name,Object,Mem) \
+ code = Param_read(plist,Name,&Object); \
+ if(0 > code) { \
+ code = param_read_null(plist,Name); \
+ if(0 == code) memset(&Object,0,sizeof(Object));\
+ } \
+ if(0 > code) { \
+ param_signal_error(plist,Name,code); \
+ if(error > code) error = code; \
+ }
+#endif
+
+ UPD_PARAM_READ(param_read_string,upd_version,udev->upd_version,udev->memory)
+
+/**
+upd_put_params begins it's normal work by creating a copy, of
+the data, that it might change, except for color_info that might
+be changed in the device-structure, all manipulations are carried
+out on this copies.
+*/
+ MarginsHWResolution[0] = udev->MarginsHWResolution[0];
+ MarginsHWResolution[1] = udev->MarginsHWResolution[1];
+ Margins[0] = udev->Margins[0];
+ Margins[1] = udev->Margins[1];
+
+ color_info = udev->color_info;
+ if(upd) {
+ flags = upd->flags;
+ UPD_MM_CPY_ARRAY(udev->memory, choice, upd->choice, countof(upd_choice),
+ UPD_MM_CPY_VALUE);
+ UPD_MM_CPY_ARRAY(udev->memory, ints, upd->ints, countof(upd_ints),
+ UPD_MM_CPY_VALUE);
+ UPD_MM_CPY_ARRAY(udev->memory, int_a, upd->int_a, countof(upd_int_a),
+ UPD_MM_CPY_PARAM);
+ UPD_MM_CPY_ARRAY(udev->memory, strings, upd->strings, countof(upd_strings),
+ UPD_MM_CPY_PARAM);
+ UPD_MM_CPY_ARRAY(udev->memory, string_a,upd->string_a,countof(upd_string_a),
+ UPD_MM_CPY_APARAM);
+ UPD_MM_CPY_ARRAY(udev->memory, float_a, upd->float_a, countof(upd_float_a),
+ UPD_MM_CPY_PARAM);
+ } else {
+ flags = 0;
+ UPD_MM_GET_ARRAY(udev->memory, choice, countof(upd_choice));
+ UPD_MM_GET_ARRAY(udev->memory, ints, countof(upd_ints));
+ UPD_MM_GET_ARRAY(udev->memory, int_a, countof(upd_int_a));
+ UPD_MM_GET_ARRAY(udev->memory, strings, countof(upd_strings));
+ UPD_MM_GET_ARRAY(udev->memory, string_a,countof(upd_string_a));
+ UPD_MM_GET_ARRAY(udev->memory, float_a, countof(upd_float_a));
+ }
+
+/** Import the Multiple-Choices */
+ for(i = 0; countof(upd_choice) > i; ++i) {
+ gs_param_string value = { NULL, 0, false};
+ if(!upd_choice[i][0]) continue;
+ UPD_PARAM_READ(param_read_name,upd_choice[i][0],value,udev->memory);
+ if(0 == code) {
+ if(0 <= error) error |= UPD_PUT_CHOICE;
+ choice[i] = 0;
+ if(0 < value.size) {
+ int j;
+ for(j = 1; upd_choice[i][j]; ++j) {
+ if((strlen(upd_choice[i][j]) == value.size) &&
+ (0 == strncmp(upd_choice[i][j],
+ (const char *) value.data,value.size))) {
+ choice[i] = j;
+ break;
+ }
+ }
+ }
+ }
+ }
+
+/** Import the Boolean Values */
+ for(i = 0; countof(upd_flags) > i; ++i) {
+ uint32_t bit = (uint32_t) 1 << i;
+ bool flag = flags & bit ? true : false;
+ if(!upd_flags[i]) continue;
+ UPD_PARAM_READ(param_read_bool,upd_flags[i],flag,udev->memory);
+ if(0 == code) {
+ if(0 <= error) error |= UPD_PUT_FLAGS;
+ if(flag) flags |= bit;
+ else flags &= ~bit;
+ }
+ }
+
+/** Import the Integer Values */
+ for(i = 0; countof(upd_ints) > i; ++i) {
+ int value = ints[i];
+ if(!upd_ints[i]) continue;
+ UPD_PARAM_READ(param_read_int,upd_ints[i],value,udev->memory);
+ if(0 == code) {
+ if(0 <= error) error |= UPD_PUT_INTS;
+ ints[i] = value;
+ }
+ }
+
+/** Import the Integer Arrays */
+ for(i = 0; countof(upd_int_a) > i; ++i) {
+ gs_param_int_array value = int_a[i];
+ if(!upd_int_a[i]) continue;
+ UPD_PARAM_READ(param_read_int_array,upd_int_a[i],value,udev->memory);
+ if(0 == code) {
+ if(0 <= error) error |= UPD_PUT_INT_A;
+ UPD_MM_DEL_PARAM(udev->memory, int_a[i]);
+ if(!value.size) {
+ value.data = NULL;
+ int_a[i] = value;
+ } else {
+ UPD_MM_CPY_PARAM(udev->memory, int_a[i],value);
+ }
+ }
+ }
+
+/** Import the Strings */
+ for(i = 0; countof(upd_strings) > i; ++i) {
+ gs_param_string value = strings[i];
+ if(!upd_strings[i]) continue;
+ UPD_PARAM_READ(param_read_string,upd_strings[i],value,udev->memory);
+ if(0 == code) {
+ if(0 <= error) error |= UPD_PUT_STRINGS;
+ UPD_MM_DEL_PARAM(udev->memory, strings[i]);
+ if(!value.size) {
+ value.data = NULL;
+ strings[i] = value;
+ } else {
+ UPD_MM_CPY_PARAM(udev->memory, strings[i],value);
+ }
+ }
+ }
+
+/** Import the String Arrays */
+ for(i = 0; countof(upd_string_a) > i; ++i) {
+ gs_param_string_array value = string_a[i];
+ if(!upd_string_a[i]) continue;
+ UPD_PARAM_READ(param_read_string_array,upd_string_a[i],value,udev->memory);
+ if(0 == code) {
+ if(0 <= error) error |= UPD_PUT_STRING_A;
+ UPD_MM_DEL_APARAM(udev->memory, string_a[i]);
+ if(!value.size) {
+ value.data = NULL;
+ string_a[i] = value;
+ } else {
+ UPD_MM_CPY_APARAM(udev->memory, string_a[i],value);
+ }
+ }
+ }
+
+/** Import the Float Arrays */
+ for(i = 0; countof(upd_float_a) > i; ++i) {
+ gs_param_float_array value = float_a[i];
+ if(!upd_float_a[i]) continue;
+ UPD_PARAM_READ(param_read_float_array,upd_float_a[i],value,udev->memory);
+ if(0 == code) {
+ if(0 <= error) error |= UPD_PUT_FLOAT_A;
+ UPD_MM_DEL_PARAM(udev->memory, float_a[i]);
+ if(!value.size) {
+ value.data = NULL;
+ float_a[i] = value;
+ } else {
+ UPD_MM_CPY_PARAM(udev->memory, float_a[i],value);
+ }
+ }
+ }
+
+/**
+Prior to the call to the superclass-put_params, the memory-layout and
+the color-model needs adjustment. This is performed here, if any parameters
+were set.
+In addition to that, Resolution & Margin-Parameters are tested & adjusted.
+*/
+ if(0 < error) {
+
+ int *ip,*ip2,ncomp,nbits;
+
+ if(6 > int_a[IA_COLOR_INFO].size) {
+ UPD_MM_DEL_PARAM(udev->memory, int_a[IA_COLOR_INFO]);
+ UPD_MM_GET_ARRAY(udev->memory, int_a[IA_COLOR_INFO].data,6);
+ int_a[IA_COLOR_INFO].size = 6;
+ }
+ ip = (int *) upd_cast(int_a[IA_COLOR_INFO].data);
+
+ if(0 == ip[0]) { /* Try to obtain num_components */
+ switch(choice[C_MAPPER]) {
+ case MAP_GRAY: ip[0] = 1; break;
+ case MAP_RGBW: ip[0] = 3; break;
+ case MAP_RGB: ip[0] = 3; break;
+ case MAP_CMYK: ip[0] = 4; break;
+ case MAP_CMYKGEN: ip[0] = 4; break;
+ case MAP_RGBOV: ip[0] = 3; break;
+ case MAP_RGBNOV: ip[0] = 3; break;
+ default: ip[0] = color_info.num_components; break;
+ }
+ } /* Try to obtain num_components */
+
+ switch(choice[C_MAPPER]) {
+ case MAP_GRAY: ncomp = 1; break;
+ case MAP_RGBW: ncomp = 4; break;
+ case MAP_RGB: ncomp = 3; break;
+ case MAP_CMYK: ncomp = 4; break;
+ case MAP_CMYKGEN: ncomp = 4; break;
+ case MAP_RGBOV: ncomp = 4; break;
+ case MAP_RGBNOV: ncomp = 4; break;
+ default: ncomp = ip[0]; break;
+ }
+ if(UPD_CMAP_MAX < ncomp) ncomp = UPD_CMAP_MAX;
+
+ if(ncomp > int_a[IA_COMPBITS].size) { /* Default ComponentBits */
+ UPD_MM_GET_ARRAY(udev->memory, ip2,ncomp);
+ nbits = 32 / ncomp;
+ if(8 < nbits) nbits = 8;
+ for(i = 0; i < ncomp; ++i) ip2[i] = nbits;
+ UPD_MM_DEL_PARAM(udev->memory, int_a[IA_COMPBITS]);
+ int_a[IA_COMPBITS].data = ip2;
+ int_a[IA_COMPBITS].size = ncomp;
+ } /* Default ComponentBits */
+
+ if(ncomp > int_a[IA_COMPSHIFT].size) { /* Default ComponentShift */
+ nbits = 0;
+ for(i = 0; i < ncomp; ++i) nbits += int_a[IA_COMPBITS].data[i];
+ UPD_MM_GET_ARRAY(udev->memory, ip2,ncomp);
+ for(i = 0; i < ncomp; ++i) {
+ ip2[i] = nbits - int_a[IA_COMPBITS].data[i];
+ nbits -= int_a[IA_COMPBITS].data[i];
+ }
+ UPD_MM_DEL_PARAM(udev->memory, int_a[IA_COMPSHIFT]);
+ int_a[IA_COMPSHIFT].data = ip2;
+ int_a[IA_COMPSHIFT].size = ncomp;
+ } /* Default ComponentShift */
+
+ if(0 == ip[1]) { /* Try to compute the depth */
+ nbits = 0;
+ for(i = 0; i < ncomp; ++i) {
+ if(nbits < (int_a[IA_COMPBITS].data[i] +
+ int_a[IA_COMPSHIFT].data[i]))
+ nbits = int_a[IA_COMPBITS].data[i] +
+ int_a[IA_COMPSHIFT].data[i];
+ }
+ if( 1 >= nbits) nbits = 1;
+ else if( 2 >= nbits) nbits = 2;
+ else if( 4 >= nbits) nbits = 4;
+ else if( 8 >= nbits) nbits = 8;
+ else if(16 >= nbits) nbits = 16;
+ else if(24 >= nbits) nbits = 24;
+ else nbits = 32;
+
+ ip[1] = nbits;
+
+ } /* Try to compute the depth */
+
+ if(0 == ip[2]) { /* Number of Gray-Levels */
+ nbits = 0;
+ for(i = 0; i < ncomp; ++i) if(nbits < int_a[IA_COMPBITS].data[i])
+ nbits = int_a[IA_COMPBITS].data[i];
+ if(nbits > 8) nbits = 8;
+ ip[2] = (1 << nbits) - 1;
+ } /* Number of Gray-Levels */
+
+ if(0 == ip[3] && 1 < ip[0]) { /* Number of Colors */
+ nbits = 0;
+ for(i = 0; i < ip[0]; ++i) nbits += int_a[IA_COMPBITS].data[i];
+ if(nbits > 8) nbits = 8;
+ ip[3] = (1 << nbits) - 1;
+ } /* Number of Colors */
+
+ if(0 == ip[4]) { /* Gray-Ramp */
+ nbits = 0;
+ for(i = 0; i < ncomp; ++i) if(nbits < int_a[IA_COMPBITS].data[i])
+ nbits = int_a[IA_COMPBITS].data[i];
+ if(2 < nbits) ip[4] = 256;
+ else ip[4] = 2;
+ } /* Gray-Ramp */
+
+ if(0 == ip[5] && 1 < ip[0]) { /* Color-Ramp */
+ nbits = 0;
+ for(i = 0; i < ncomp; ++i) if(nbits < int_a[IA_COMPBITS].data[i])
+ nbits = int_a[IA_COMPBITS].data[i];
+ if(2 < nbits) ip[5] = 256;
+ else ip[5] = 2;
+ } /* Color-Ramp */
+
+ udev->color_info.num_components = ip[0];
+ udev->color_info.depth = ip[1];
+ udev->color_info.max_gray = (gx_color_value) ip[2];
+ udev->color_info.max_color = (gx_color_value) ip[3];
+ udev->color_info.dither_grays = (gx_color_value) ip[4];
+ udev->color_info.dither_colors = (gx_color_value) ip[5];
+
+/*
+ * Now we're dealing with the Resolution- & Margin-Stuff
+ * (This is close to be a bad-nasty-hack-hack)
+ */
+ if((0 == param_read_float_array(plist,"HWResolution",&mfa)) &&
+ (2 == mfa.size) && (0 != mfa.data)) {
+ udev->MarginsHWResolution[0] = mfa.data[0];
+ udev->MarginsHWResolution[1] = mfa.data[1];
+ } else {
+ udev->MarginsHWResolution[0] = udev->HWResolution[0];
+ udev->MarginsHWResolution[1] = udev->HWResolution[1];
+ }
+
+ if((0 == param_read_float_array(plist,".HWMargins",&mfa)) &&
+ (4 == mfa.size) && (0 != mfa.data)) {
+ udev->Margins[0] = -mfa.data[0] * udev->MarginsHWResolution[0] / 72.0;
+ udev->Margins[1] = -mfa.data[3] * udev->MarginsHWResolution[1] / 72.0;
+ }
+ } /* Change the color-Info */
+
+/* Call the superclass-put_params now */
+ code = gdev_prn_put_params((gx_device *)udev,plist);
+ if(0 > code) error = code;
+
+/**
+If the superclass-"put_params" went o.k. too, then the new parameters are
+transferred into the device-structure. In the case of "uniprint", this may
+
+ 1. Close the device, which might fail.
+ 2. Allocate new memory for the upd-specific structure, that might fail too.
+
+*/
+/* *HGS* recognize a changed device geometry */
+ if( udev->upd && /* HGS */
+ ((udev->width != udev->upd->pdwidth) || /* HGS */
+ (udev->height != udev->upd->pdheight) )) /* HGS */
+ error |= UPD_PUT_CHANGEDSIZE; /* HGS */
+
+ if(0 < error && udev->is_open) {
+ code = gs_closedevice((gx_device *)udev);
+ if(0 > code) error = code;
+ }
+
+ if(0 < error) { /* Actually something loaded without error */
+
+ if(!(upd = udev->upd)) {
+ UPD_MM_GET_ARRAY(udev->memory, udev->upd,1);
+ upd = udev->upd;
+ } else {
+ UPD_MM_DEL_ARRAY(udev->memory, upd->choice, countof(upd_choice), UPD_MM_DEL_VALUE);
+ UPD_MM_DEL_ARRAY(udev->memory, upd->ints, countof(upd_ints), UPD_MM_DEL_VALUE);
+ UPD_MM_DEL_ARRAY(udev->memory, upd->int_a, countof(upd_int_a), UPD_MM_DEL_PARAM);
+ UPD_MM_DEL_ARRAY(udev->memory, upd->strings, countof(upd_strings), UPD_MM_DEL_PARAM);
+ UPD_MM_DEL_ARRAY(udev->memory, upd->string_a,countof(upd_string_a),UPD_MM_DEL_APARAM);
+ UPD_MM_DEL_ARRAY(udev->memory, upd->float_a, countof(upd_float_a), UPD_MM_DEL_PARAM);
+ }
+
+ upd->choice = choice;
+ upd->flags = flags;
+ upd->ints = ints;
+ upd->int_a = int_a;
+ upd->strings = strings;
+ upd->string_a = string_a;
+ upd->float_a = float_a;
+ upd->memory = udev->memory;
+
+ if(0 < error) error = 0;
+
+ } else {
+
+ udev->Margins[0] = Margins[0];
+ udev->Margins[1] = Margins[1];
+ udev->MarginsHWResolution[0] = MarginsHWResolution[0];
+ udev->MarginsHWResolution[1] = MarginsHWResolution[1];
+
+ udev->color_info = color_info;
+ UPD_MM_DEL_ARRAY(udev->memory, choice, countof(upd_choice), UPD_MM_DEL_VALUE);
+ UPD_MM_DEL_ARRAY(udev->memory, ints, countof(upd_ints), UPD_MM_DEL_VALUE);
+ UPD_MM_DEL_ARRAY(udev->memory, int_a, countof(upd_int_a), UPD_MM_DEL_PARAM);
+ UPD_MM_DEL_ARRAY(udev->memory, strings, countof(upd_strings), UPD_MM_DEL_PARAM);
+ UPD_MM_DEL_ARRAY(udev->memory, string_a,countof(upd_string_a),UPD_MM_DEL_APARAM);
+ UPD_MM_DEL_ARRAY(udev->memory, float_a, countof(upd_float_a), UPD_MM_DEL_PARAM);
+
+ }
+
+/*
+ * upd_put_params keeps the Procedures upd to date
+ */
+
+ upd_procs_map(udev);
+
+#if UPD_MESSAGES & UPD_M_TOPCALLS
+ errprintf(udev->memory,"RETURN: %d = upd_put_params(0x%05lx,0x%05lx)\n",
+ error,(long) udev, (long) plist);
+#endif
+
+ return error;
+}
+
+/* ------------------------------------------------------------------- */
+/* upd_cmyk_icolor: KCMY->KCMY-Index Mapping */
+/* ------------------------------------------------------------------- */
+/**
+The next Routines, that follow, are the color-mapping routines.
+GHOSTSCRIPT talks about "gx_color_values" and the driver has
+to merge the 1, 3 or four values into up to 32 Bits, that means
+it is necessary to do some truncation and shifting. For the truncation
+"uniprint" uses the internal function "upd_truncate" and "upd_expand"
+reverses this in the reverse-mapping procedures.
+*/
+
+static gx_color_index
+upd_cmyk_icolor(gx_device *pdev, const gx_color_value cv[])
+{
+ const upd_p upd = ((upd_device *)pdev)->upd;
+ gx_color_index rv;
+ gx_color_value c, m, y, k;
+ c = cv[0]; m = cv[1]; y = cv[2]; k = cv[3];
+
+/**
+All 4-Component-Modi have to deal with the Problem, that a value
+with all bits set can be produced, which is treated as an error-return
+from the mapping-functions. But with RGBW or KCMY, there is a neat
+trick: Grayscale are transferred as RGB/CMY=0 and holding Data only
+in the W- or K-Component.
+*/
+
+ if((c == m) && (m == y)) {
+
+ rv = upd_truncate(upd,0,(gx_color_value)(c > k ? c : k));
+
+ } else {
+
+ rv = upd_truncate(upd,0,k) | upd_truncate(upd,1,c)
+ | upd_truncate(upd,2,m) | upd_truncate(upd,3,y);
+
+/* It might still become a "gx_no_color_value" due to truncation, thus: */
+
+ if(rv == gx_no_color_index) rv ^= 1;
+ }
+
+#if UPD_MESSAGES & UPD_M_MAPCALLS
+ errprintf(pdev->memory,
+"cmyk_icolor: (%5.1f,%5.1f,%5.1f,%5.1f) : (%5.1f,%5.1f,%5.1f,%5.1f) : 0x%0*lx\n",
+ 255.0 * (double) c / (double) gx_max_color_value,
+ 255.0 * (double) m / (double) gx_max_color_value,
+ 255.0 * (double) y / (double) gx_max_color_value,
+ 255.0 * (double) k / (double) gx_max_color_value,
+ 255.0 * (double) ((rv >> upd->cmap[1].bitshf) & upd->cmap[1].bitmsk)
+ / (double) upd->cmap[1].bitmsk,
+ 255.0 * (double) ((rv >> upd->cmap[2].bitshf) & upd->cmap[2].bitmsk)
+ / (double) upd->cmap[2].bitmsk,
+ 255.0 * (double) ((rv >> upd->cmap[3].bitshf) & upd->cmap[3].bitmsk)
+ / (double) upd->cmap[3].bitmsk,
+ 255.0 * (double) ((rv >> upd->cmap[0].bitshf) & upd->cmap[0].bitmsk)
+ / (double) upd->cmap[0].bitmsk,
+ (pdev->color_info.depth + 3)>>2,rv);
+#endif
+
+ return rv;
+}
+
+/* ------------------------------------------------------------------- */
+/* upd_icolor_rgb: Stored KCMY back to a RGB */
+/* ------------------------------------------------------------------- */
+
+static int
+upd_icolor_rgb(gx_device *pdev, gx_color_index color, gx_color_value prgb[3])
+{
+ const upd_p upd = ((upd_device *)pdev)->upd;
+ gx_color_value c,m,y,k;
+
+/*
+ * Expand to the Component-Values
+ */
+ k = upd_expand(upd,0,color);
+ c = upd_expand(upd,1,color);
+ m = upd_expand(upd,2,color);
+ y = upd_expand(upd,3,color);
+
+/*
+ * Then Invert and subtract K from the colors
+ */
+ prgb[0] = gx_max_color_value - c;
+ if(prgb[0] > k) prgb[0] -= k;
+ else prgb[0] = 0;
+
+ prgb[1] = gx_max_color_value - m;
+ if(prgb[1] > k) prgb[1] -= k;
+ else prgb[1] = 0;
+
+ prgb[2] = gx_max_color_value - y;
+ if(prgb[2] > k) prgb[2] -= k;
+ else prgb[2] = 0;
+
+#if UPD_MESSAGES & UPD_M_MAPCALLS
+ errprintf(pdev->memory,
+ "icolor_rgb: 0x%0*lx -> (%5.1f,%5.1f,%5.1f,%5.1f) -> (%5.1f,%5.1f,%5.1f,%5.1f) -> (%5.1f,%5.1f,%5.1f)\n",
+ (pdev->color_info.depth + 3)>>2,color,
+ 255.0 * (double) ((color >> upd->cmap[1].bitshf) & upd->cmap[1].bitmsk)
+ / (double) upd->cmap[1].bitmsk,
+ 255.0 * (double) ((color >> upd->cmap[2].bitshf) & upd->cmap[2].bitmsk)
+ / (double) upd->cmap[2].bitmsk,
+ 255.0 * (double) ((color >> upd->cmap[3].bitshf) & upd->cmap[3].bitmsk)
+ / (double) upd->cmap[3].bitmsk,
+ 255.0 * (double) ((color >> upd->cmap[0].bitshf) & upd->cmap[0].bitmsk)
+ / (double) upd->cmap[0].bitmsk,
+ 255.0 * (double) c / (double) gx_max_color_value,
+ 255.0 * (double) m / (double) gx_max_color_value,
+ 255.0 * (double) y / (double) gx_max_color_value,
+ 255.0 * (double) k / (double) gx_max_color_value,
+ 255.0 * (double) prgb[0] / (double) gx_max_color_value,
+ 255.0 * (double) prgb[1] / (double) gx_max_color_value,
+ 255.0 * (double) prgb[2] / (double) gx_max_color_value);
+#endif
+
+ return 0;
+}
+
+/* ------------------------------------------------------------------- */
+/* upd_rgb_1color: Grayscale->Grayscale-index-Mapping */
+/* ------------------------------------------------------------------- */
+
+static gx_color_index
+upd_rgb_1color(gx_device *pdev, const gx_color_value cv[])
+{
+ const upd_p upd = ((upd_device *)pdev)->upd;
+ gx_color_index rv;
+ gx_color_value g;
+
+ g = cv[0];
+ rv = upd_truncate(upd,0,g);
+
+#if UPD_MESSAGES & UPD_M_MAPCALLS
+ errprintf(pdev->memory,
+ "rgb_1color: (%5.1f) : (%5.1f) : 0x%0*lx\n",
+ 255.0 * (double) g / (double) gx_max_color_value,
+ 255.0 * (double) ((rv >> upd->cmap[0].bitshf) & upd->cmap[0].bitmsk)
+ / (double) upd->cmap[0].bitmsk,
+ (pdev->color_info.depth + 3)>>2,rv);
+#endif
+
+ return rv;
+}
+
+/* ------------------------------------------------------------------- */
+/* upd_1color_rgb: reversal of the above */
+/* ------------------------------------------------------------------- */
+
+static int
+upd_1color_rgb(gx_device *pdev, gx_color_index color, gx_color_value cv[1])
+{
+ const upd_p upd = ((upd_device *)pdev)->upd;
+/*
+ * Actual task: expand to full range of gx_color_value
+ */
+ cv[0] = upd_expand(upd,0,color);
+
+#if UPD_MESSAGES & UPD_M_MAPCALLS
+ errprintf(pdev->memory,"1color_rgb: 0x%0*lx -> %5.1f -> (%5.1f,%5.1f,%5.1f)\n",
+ (pdev->color_info.depth + 3)>>2,color,
+ 255.0 * (double) ((color >> upd->cmap[0].bitshf) & upd->cmap[0].bitmsk)
+ / (double) upd->cmap[0].bitmsk,
+ 255.0 * (double) prgb[0] / (double) gx_max_color_value,
+ 255.0 * (double) prgb[0] / (double) gx_max_color_value,
+ 255.0 * (double) prgb[0] / (double) gx_max_color_value);
+#endif
+
+ return 0;
+}
+
+/* ------------------------------------------------------------------- */
+/* upd_rgb_3color: component-wise RGB->RGB-Mapping */
+/* ------------------------------------------------------------------- */
+
+static gx_color_index
+upd_rgb_3color(gx_device *pdev, const gx_color_value cv[])
+{
+ const upd_p upd = ((upd_device *)pdev)->upd;
+ gx_color_index rv;
+ gx_color_value r, g, b;
+ r = cv[0]; g = cv[1]; b = cv[2];
+
+ rv = upd_truncate(upd,0,r) | upd_truncate(upd,1,g) | upd_truncate(upd,2,b);
+ if(rv == gx_no_color_index) rv ^= 1;
+
+#if UPD_MESSAGES & UPD_M_MAPCALLS
+ errprintf(pdev->memory,
+ "rgb_3color: (%5.1f,%5.1f,%5.1f) : (%5.1f,%5.1f,%5.1f) : 0x%0*lx\n",
+ 255.0 * (double) r / (double) gx_max_color_value,
+ 255.0 * (double) g / (double) gx_max_color_value,
+ 255.0 * (double) b / (double) gx_max_color_value,
+ 255.0 * (double) ((rv >> upd->cmap[0].bitshf) & upd->cmap[0].bitmsk)
+ / (double) upd->cmap[0].bitmsk,
+ 255.0 * (double) ((rv >> upd->cmap[1].bitshf) & upd->cmap[1].bitmsk)
+ / (double) upd->cmap[1].bitmsk,
+ 255.0 * (double) ((rv >> upd->cmap[2].bitshf) & upd->cmap[2].bitmsk)
+ / (double) upd->cmap[2].bitmsk,
+ (pdev->color_info.depth + 3)>>2,rv);
+#endif
+
+ return rv;
+}
+
+/* ------------------------------------------------------------------- */
+/* upd_3color_rgb: reversal of the above */
+/* ------------------------------------------------------------------- */
+
+static int
+upd_3color_rgb(gx_device *pdev, gx_color_index color, gx_color_value prgb[3])
+{
+ const upd_p upd = ((upd_device *)pdev)->upd;
+
+ prgb[0] = upd_expand(upd,0,color);
+ prgb[1] = upd_expand(upd,1,color);
+ prgb[2] = upd_expand(upd,2,color);
+
+#if UPD_MESSAGES & UPD_M_MAPCALLS
+ errprintf(pdev->memory,
+ "3color_rgb: 0x%0*lx -> (%5.1f,%5.1f,%5.1f) -> (%5.1f,%5.1f,%5.1f)\n",
+ (pdev->color_info.depth + 3)>>2,color,
+ 255.0 * (double) ((color >> upd->cmap[0].bitshf) & upd->cmap[0].bitmsk)
+ / (double) upd->cmap[0].bitmsk,
+ 255.0 * (double) ((color >> upd->cmap[1].bitshf) & upd->cmap[1].bitmsk)
+ / (double) upd->cmap[1].bitmsk,
+ 255.0 * (double) ((color >> upd->cmap[2].bitshf) & upd->cmap[2].bitmsk)
+ / (double) upd->cmap[2].bitmsk,
+
+ 255.0 * (double) prgb[0] / (double) gx_max_color_value,
+ 255.0 * (double) prgb[1] / (double) gx_max_color_value,
+ 255.0 * (double) prgb[2] / (double) gx_max_color_value);
+#endif
+
+ return 0;
+}
+
+/* ------------------------------------------------------------------- */
+/* upd_rgb_4color: Create an WRGB-Index from RGB */
+/* ------------------------------------------------------------------- */
+
+static gx_color_index
+upd_rgb_4color(gx_device *pdev, const gx_color_value cv[])
+{
+ const upd_p upd = ((upd_device *)pdev)->upd;
+ gx_color_index rv;
+ gx_color_value r, g, b;
+
+ r = cv[0]; g = cv[1]; b = cv[2];
+
+ if((r == g) && (g == b)) {
+
+ rv = upd_truncate(upd,0,r);
+
+ } else {
+
+ gx_color_value w = g < r ? g : r; w = w < b ? w : b; /* Minimum */
+
+ rv = upd_truncate(upd,0,w) | upd_truncate(upd,1,r) |
+ upd_truncate(upd,2,g) | upd_truncate(upd,3,b);
+
+/* It might still become a "gx_no_color_value" due to truncation, thus: */
+
+ if(rv == gx_no_color_index) rv ^= 1;
+ }
+
+#if UPD_MESSAGES & UPD_M_MAPCALLS
+ errprintf(pdev->memory,
+ "rgb_4color: (%5.1f,%5.1f,%5.1f) : (%5.1f,%5.1f,%5.1f,%5.1f) : 0x%0*lx\n",
+ 255.0 * (double) r / (double) gx_max_color_value,
+ 255.0 * (double) g / (double) gx_max_color_value,
+ 255.0 * (double) b / (double) gx_max_color_value,
+ 255.0 * (double) ((rv >> upd->cmap[1].bitshf) & upd->cmap[1].bitmsk)
+ / (double) upd->cmap[1].bitmsk,
+ 255.0 * (double) ((rv >> upd->cmap[2].bitshf) & upd->cmap[2].bitmsk)
+ / (double) upd->cmap[2].bitmsk,
+ 255.0 * (double) ((rv >> upd->cmap[3].bitshf) & upd->cmap[3].bitmsk)
+ / (double) upd->cmap[3].bitmsk,
+ 255.0 * (double) ((rv >> upd->cmap[0].bitshf) & upd->cmap[0].bitmsk)
+ / (double) upd->cmap[0].bitmsk,
+ (pdev->color_info.depth + 3)>>2,rv);
+#endif
+
+ return rv;
+}
+
+/* ------------------------------------------------------------------- */
+/* upd_4color_rgb: Stored WRGB-Index back to a RGB */
+/* ------------------------------------------------------------------- */
+
+static int
+upd_4color_rgb(gx_device *pdev, gx_color_index color, gx_color_value prgb[3])
+{
+ const upd_p upd = ((upd_device *)pdev)->upd;
+
+/*
+ * Expand to the Component-Values
+ */
+ prgb[0] = upd_expand(upd,1,color);
+ prgb[1] = upd_expand(upd,2,color);
+ prgb[2] = upd_expand(upd,3,color);
+
+/* Revert our Grayscale-Trick: */
+ if(!(prgb[0] || prgb[1] || prgb[2]))
+ prgb[0] = prgb[1] = prgb[2] = upd_expand(upd,0,color);
+
+#if UPD_MESSAGES & UPD_M_MAPCALLS
+ errprintf(pdev->memory,
+ "4color_rgb: 0x%0*lx -> (%5.1f,%5.1f,%5.1f,%5.1f) -> (%5.1f,%5.1f,%5.1f)\n",
+ (pdev->color_info.depth + 3)>>2,color,
+ 255.0 * (double) ((color >> upd->cmap[1].bitshf) & upd->cmap[1].bitmsk)
+ / (double) upd->cmap[1].bitmsk,
+ 255.0 * (double) ((color >> upd->cmap[2].bitshf) & upd->cmap[2].bitmsk)
+ / (double) upd->cmap[2].bitmsk,
+ 255.0 * (double) ((color >> upd->cmap[3].bitshf) & upd->cmap[3].bitmsk)
+ / (double) upd->cmap[3].bitmsk,
+ 255.0 * (double) ((color >> upd->cmap[0].bitshf) & upd->cmap[0].bitmsk)
+ / (double) upd->cmap[0].bitmsk,
+ 255.0 * (double) prgb[0] / (double) gx_max_color_value,
+ 255.0 * (double) prgb[1] / (double) gx_max_color_value,
+ 255.0 * (double) prgb[2] / (double) gx_max_color_value);
+#endif
+
+ return 0;
+}
+
+/* ------------------------------------------------------------------- */
+/* upd_cmyk_kcolor: KCMY->KCMY-Index Mapping with Black Generation */
+/* ------------------------------------------------------------------- */
+
+static gx_color_index
+upd_cmyk_kcolor(gx_device *pdev, const gx_color_value cv[])
+{
+ const upd_p upd = ((upd_device *)pdev)->upd;
+ gx_color_index rv;
+ gx_color_value black;
+
+ gx_color_value c, m, y, k;
+ c = cv[0]; m = cv[1]; y = cv[2]; k = cv[3];
+
+ if((c == m) && (m == y)) {
+
+ black = c > k ? c : k;
+ rv = upd_truncate(upd,0,black);
+
+ } else {
+
+ if(k && !(c | m | y)) {
+ black = k;
+ } else {
+ black = c < m ? c : m;
+ black = black < y ? black : y;
+ }
+
+ rv = upd_truncate(upd,0,black) | upd_truncate(upd,1,c)
+ | upd_truncate(upd,2,m) | upd_truncate(upd,3,y);
+
+/* It might still become a "gx_no_color_value" due to truncation, thus: */
+
+ if(rv == gx_no_color_index) rv ^= 1;
+ }
+
+#if UPD_MESSAGES & UPD_M_MAPCALLS
+ errprintf(pdev->memory,
+"cmyk_kcolor: (%5.1f,%5.1f,%5.1f,%5.1f) : (%5.1f,%5.1f,%5.1f,%5.1f) : 0x%0*lx\n",
+ 255.0 * (double) c / (double) gx_max_color_value,
+ 255.0 * (double) m / (double) gx_max_color_value,
+ 255.0 * (double) y / (double) gx_max_color_value,
+ 255.0 * (double) k / (double) gx_max_color_value,
+ 255.0 * (double) ((rv >> upd->cmap[1].bitshf) & upd->cmap[1].bitmsk)
+ / (double) upd->cmap[1].bitmsk,
+ 255.0 * (double) ((rv >> upd->cmap[2].bitshf) & upd->cmap[2].bitmsk)
+ / (double) upd->cmap[2].bitmsk,
+ 255.0 * (double) ((rv >> upd->cmap[3].bitshf) & upd->cmap[3].bitmsk)
+ / (double) upd->cmap[3].bitmsk,
+ 255.0 * (double) ((rv >> upd->cmap[0].bitshf) & upd->cmap[0].bitmsk)
+ / (double) upd->cmap[0].bitmsk,
+ (pdev->color_info.depth + 3)>>2,rv);
+#endif
+
+ return rv;
+}
+
+/* ------------------------------------------------------------------- */
+/* upd_kcolor_rgb: Stored CMY+generated K back to a RGB */
+/* ------------------------------------------------------------------- */
+
+static int
+upd_kcolor_rgb(gx_device *pdev, gx_color_index color, gx_color_value prgb[3])
+{
+ const upd_p upd = ((upd_device *)pdev)->upd;
+ gx_color_value c,m,y,k;
+
+/*
+ * Expand to the Component-Values
+ */
+ k = upd_expand(upd,0,color);
+ c = upd_expand(upd,1,color);
+ m = upd_expand(upd,2,color);
+ y = upd_expand(upd,3,color);
+
+/*
+ * Check for plain Gray-Values
+ */
+ if(!(c | m | y )) {
+
+ prgb[2] = prgb[1] = prgb[0] = gx_max_color_value - k;
+
+ } else {
+ prgb[0] = gx_max_color_value - c;
+ prgb[1] = gx_max_color_value - m;
+ prgb[2] = gx_max_color_value - y;
+ }
+
+#if UPD_MESSAGES & UPD_M_MAPCALLS
+ errprintf(pdev->memory,
+ "kcolor_rgb: 0x%0*lx -> (%5.1f,%5.1f,%5.1f,%5.1f) -> (%5.1f,%5.1f,%5.1f,%5.1f) -> (%5.1f,%5.1f,%5.1f)\n",
+ (pdev->color_info.depth + 3)>>2,color,
+ 255.0 * (double) ((color >> upd->cmap[1].bitshf) & upd->cmap[1].bitmsk)
+ / (double) upd->cmap[1].bitmsk,
+ 255.0 * (double) ((color >> upd->cmap[2].bitshf) & upd->cmap[2].bitmsk)
+ / (double) upd->cmap[2].bitmsk,
+ 255.0 * (double) ((color >> upd->cmap[3].bitshf) & upd->cmap[3].bitmsk)
+ / (double) upd->cmap[3].bitmsk,
+ 255.0 * (double) ((color >> upd->cmap[0].bitshf) & upd->cmap[0].bitmsk)
+ / (double) upd->cmap[0].bitmsk,
+ 255.0 * (double) c / (double) gx_max_color_value,
+ 255.0 * (double) m / (double) gx_max_color_value,
+ 255.0 * (double) y / (double) gx_max_color_value,
+ 255.0 * (double) k / (double) gx_max_color_value,
+ 255.0 * (double) prgb[0] / (double) gx_max_color_value,
+ 255.0 * (double) prgb[1] / (double) gx_max_color_value,
+ 255.0 * (double) prgb[2] / (double) gx_max_color_value);
+#endif
+
+ return 0;
+}
+
+/* ------------------------------------------------------------------- */
+/* upd_rgb_ovcolor: Create an KCMY-Index from RGB */
+/* ------------------------------------------------------------------- */
+
+static gx_color_index
+upd_rgb_ovcolor(gx_device *pdev, const gx_color_value cv[])
+{
+ const upd_p upd = ((upd_device *)pdev)->upd;
+ gx_color_index rv;
+ gx_color_value c,m,y,black;
+ gx_color_value r, g, b;
+ r = cv[0]; g = cv[1]; b = cv[2];
+ if((r == g) && (g == b)) {
+
+ black = gx_max_color_value - r;
+ rv = upd_truncate(upd,0,black);
+ c = m = y = 0;
+
+ } else {
+
+ c = gx_max_color_value - r;
+ m = gx_max_color_value - g;
+ y = gx_max_color_value - b;
+
+ black = c < m ? c : m;
+ black = black < y ? black : y;
+
+ if(black != gx_max_color_value) {
+ float tmp,d;
+
+ d = (float)(gx_max_color_value - black);
+
+ tmp = (float) (c-black) / d;
+ if( 0.0 > tmp) tmp = 0.0;
+ else if( 1.0 < tmp) tmp = 1.0;
+ c = (gx_color_value)(tmp * gx_max_color_value + 0.499);
+
+ tmp = (float) (m-black) / d;
+ if( 0.0 > tmp) tmp = 0.0;
+ else if( 1.0 < tmp) tmp = 1.0;
+ m = (gx_color_value)(tmp * gx_max_color_value + 0.499);
+
+ tmp = (float) (y-black) / d;
+ if( 0.0 > tmp) tmp = 0.0;
+ else if( 1.0 < tmp) tmp = 1.0;
+ y = (gx_color_value)(tmp * gx_max_color_value + 0.499);
+
+ } else {
+
+ c = m = y = gx_max_color_value;
+
+ }
+
+ rv = upd_truncate(upd,0,black) | upd_truncate(upd,1,c) |
+ upd_truncate(upd,2,m) | upd_truncate(upd,3,y);
+
+/* It might still become a "gx_no_color_value" due to truncation, thus: */
+
+ if(rv == gx_no_color_index) rv ^= 1;
+ }
+
+#if UPD_MESSAGES & UPD_M_MAPCALLS
+ errprintf(pdev->memory,
+ "rgb_ovcolor: (%5.1f,%5.1f,%5.1f) : (%5.1f,%5.1f,%5.1f,%5.1f) : 0x%0*lx\n",
+ 255.0 * (double) r / (double) gx_max_color_value,
+ 255.0 * (double) g / (double) gx_max_color_value,
+ 255.0 * (double) b / (double) gx_max_color_value,
+ 255.0 * (double) ((rv >> upd->cmap[1].bitshf) & upd->cmap[1].bitmsk)
+ / (double) upd->cmap[1].bitmsk,
+ 255.0 * (double) ((rv >> upd->cmap[2].bitshf) & upd->cmap[2].bitmsk)
+ / (double) upd->cmap[2].bitmsk,
+ 255.0 * (double) ((rv >> upd->cmap[3].bitshf) & upd->cmap[3].bitmsk)
+ / (double) upd->cmap[3].bitmsk,
+ 255.0 * (double) ((rv >> upd->cmap[0].bitshf) & upd->cmap[0].bitmsk)
+ / (double) upd->cmap[0].bitmsk,
+ (pdev->color_info.depth + 3)>>2,rv);
+#endif
+
+ return rv;
+}
+
+/* ------------------------------------------------------------------- */
+/* upd_rgb_novcolor: Create an KCMY-Index from RGB */
+/* ------------------------------------------------------------------- */
+
+static gx_color_index
+upd_rgb_novcolor(gx_device *pdev, const gx_color_value cv[])
+{
+ const upd_p upd = ((upd_device *)pdev)->upd;
+ gx_color_index rv;
+ gx_color_value c,m,y,black;
+ gx_color_value r, g, b;
+ r = cv[0]; g = cv[1]; b = cv[2];
+
+ if((r == g) && (g == b)) {
+
+ black = gx_max_color_value - r;
+ rv = upd_truncate(upd,0,black);
+ c = m = y = 0;
+
+ } else {
+
+ c = gx_max_color_value - r;
+ m = gx_max_color_value - g;
+ y = gx_max_color_value - b;
+
+ black = c < m ? c : m;
+ black = black < y ? black : y;
+ c = c - black;
+ m = m - black;
+ y = y - black;
+
+ rv = upd_truncate(upd,0,black) | upd_truncate(upd,1,c) |
+ upd_truncate(upd,2,m) | upd_truncate(upd,3,y);
+
+/* It might still become a "gx_no_color_value" due to truncation, thus: */
+
+ if(rv == gx_no_color_index) rv ^= 1;
+ }
+
+#if UPD_MESSAGES & UPD_M_MAPCALLS
+ errprintf(pdev->memory,
+ "rgb_ovcolor: (%5.1f,%5.1f,%5.1f) : (%5.1f,%5.1f,%5.1f,%5.1f) : 0x%0*lx\n",
+ 255.0 * (double) r / (double) gx_max_color_value,
+ 255.0 * (double) g / (double) gx_max_color_value,
+ 255.0 * (double) b / (double) gx_max_color_value,
+ 255.0 * (double) ((rv >> upd->cmap[1].bitshf) & upd->cmap[1].bitmsk)
+ / (double) upd->cmap[1].bitmsk,
+ 255.0 * (double) ((rv >> upd->cmap[2].bitshf) & upd->cmap[2].bitmsk)
+ / (double) upd->cmap[2].bitmsk,
+ 255.0 * (double) ((rv >> upd->cmap[3].bitshf) & upd->cmap[3].bitmsk)
+ / (double) upd->cmap[3].bitmsk,
+ 255.0 * (double) ((rv >> upd->cmap[0].bitshf) & upd->cmap[0].bitmsk)
+ / (double) upd->cmap[0].bitmsk,
+ (pdev->color_info.depth + 3)>>2,rv);
+#endif
+
+ return rv;
+}
+
+/* ------------------------------------------------------------------- */
+/* NOTE: Beyond this point only "uniprint"-special-items. */
+/* ------------------------------------------------------------------- */
+
+/* ------------------------------------------------------------------- */
+/* Truncate a gx_color_value to the desired number of bits. */
+/* ------------------------------------------------------------------- */
+
+static uint32_t
+upd_truncate(upd_pc upd,int i,gx_color_value v) {
+ const updcmap_pc cmap = upd->cmap + i;
+ int32_t s; /* step size */
+ gx_color_value *p; /* value-pointer */
+
+ if(0 == cmap->bits) { /* trivial case */
+
+ v = 0;
+
+ } else if(gx_color_value_bits > cmap->bits) { /* really truncate ? */
+
+ p = cmap->code + ((cmap->bitmsk + 1) >> 1);
+ s = ((cmap->bitmsk + 1) >> 2);
+/*
+ * Perform search in monotonic code-array
+ */
+ while(s > 0) {
+ if(v > *p) { /* we're below */
+ p += s;
+ } else if(v < p[-1]) { /* we're ahead for sure */
+ p -= s;
+ } else {
+/* years ago, i knew what this was good for */
+ if((v-p[-1]) < (p[0]-v)) p -= 1;
+ break;
+ }
+ s >>= 1;
+ }
+ if((v-p[-1]) < (p[0]-v)) p -= 1;
+ v = p - cmap->code;
+ }
+
+ if(!cmap->rise) v = cmap->bitmsk - v; /* Again reverse, if necessary */
+
+ return ((uint32_t) v) << cmap->bitshf;
+}
+
+/* ------------------------------------------------------------------- */
+/* upd_open_map: install the color-mapping */
+/* ------------------------------------------------------------------- */
+
+static int
+upd_open_map(upd_device *udev)
+{
+ const upd_p upd = udev->upd;
+ int imap;
+
+/** _always_ initialize crucial Values! */
+ for(imap = 0; UPD_CMAP_MAX > imap; ++imap) upd->cmap[imap].code = NULL;
+ upd->ncomp = 0;
+
+/** There should not be an error yet */
+ if(B_ERROR & upd->flags) imap = 0;
+
+/** Establish the xfer-Indices */
+ if(imap) {
+ for(imap = 0; UPD_CMAP_MAX > imap; ++imap) {
+ upd->cmap[imap].xfer = -1;
+ upd->cmap[imap].bits = 0;
+ }
+ switch(upd->choice[C_MAPPER]) {
+ case MAP_GRAY:
+ upd->cmap[0].xfer = FA_WXFER;
+ break;
+ case MAP_RGBW:
+ upd->cmap[0].xfer = FA_WXFER;
+ upd->cmap[1].xfer = FA_RXFER;
+ upd->cmap[2].xfer = FA_GXFER;
+ upd->cmap[3].xfer = FA_BXFER;
+ break;
+ case MAP_RGB:
+ upd->cmap[0].xfer = FA_RXFER;
+ upd->cmap[1].xfer = FA_GXFER;
+ upd->cmap[2].xfer = FA_BXFER;
+ break;
+ case MAP_CMYK:
+ upd->cmap[0].xfer = FA_KXFER;
+ upd->cmap[1].xfer = FA_CXFER;
+ upd->cmap[2].xfer = FA_MXFER;
+ upd->cmap[3].xfer = FA_YXFER;
+ break;
+ case MAP_CMYKGEN:
+ upd->cmap[0].xfer = FA_KXFER;
+ upd->cmap[1].xfer = FA_CXFER;
+ upd->cmap[2].xfer = FA_MXFER;
+ upd->cmap[3].xfer = FA_YXFER;
+ break;
+ case MAP_RGBOV:
+ upd->cmap[0].xfer = FA_KXFER;
+ upd->cmap[1].xfer = FA_CXFER;
+ upd->cmap[2].xfer = FA_MXFER;
+ upd->cmap[3].xfer = FA_YXFER;
+ break;
+ case MAP_RGBNOV:
+ upd->cmap[0].xfer = FA_KXFER;
+ upd->cmap[1].xfer = FA_CXFER;
+ upd->cmap[2].xfer = FA_MXFER;
+ upd->cmap[3].xfer = FA_YXFER;
+ break;
+ default:
+#if UPD_MESSAGES & UPD_M_WARNING
+ if(upd_choice[C_MAPPER][0])
+ errprintf(udev->memory,
+ "upd_open_map: unsupported %s=%d\n",
+ upd_choice[C_MAPPER][0],upd->choice[C_MAPPER]);
+ else
+ errprintf(udev->memory,
+ "upd_open_map: unsupported choce[%d]=%d\n",
+ C_MAPPER,upd->choice[C_MAPPER]);
+#endif
+ imap = 0;
+ break;
+ }
+ }
+
+/** The bit number sould be positive & fit into the storage */
+
+ if(imap) { /* Check number of Bits & Shifts */
+
+#if UPD_MESSAGES & UPD_M_WARNING
+ uint32_t used = 0,bitmsk;
+#endif
+ bool success = true;
+
+ for(imap = 0; UPD_CMAP_MAX > imap; ++imap) {
+ if(0 > upd->cmap[imap].xfer) continue;
+
+ if((0 > upd->int_a[IA_COMPBITS].data[imap]) ||
+ (gx_color_value_bits < upd->int_a[IA_COMPBITS].data[imap]) ||
+ (0 > upd->int_a[IA_COMPSHIFT].data[imap]) ||
+ (upd->int_a[IA_COMPBITS].data[imap] >
+ (udev->color_info.depth - upd->int_a[IA_COMPSHIFT].data[imap]))) {
+#if UPD_MESSAGES & UPD_M_WARNING
+ errprintf(udev->memory,
+ "upd_open_map: %d Bits << %d is illegal for %d. Component\n",
+ upd->int_a[IA_COMPBITS].data[imap],
+ upd->int_a[IA_COMPSHIFT].data[imap],imap+1);
+#endif
+
+ success = false;
+
+ } else {
+
+ int n;
+ const float *now;
+ float last;
+
+ if((NULL == upd->float_a[upd->cmap[imap].xfer].data) ||
+ (2 > upd->float_a[upd->cmap[imap].xfer].size) ) {
+ float *fp;
+ UPD_MM_DEL_PARAM(udev->memory, upd->float_a[upd->cmap[imap].xfer]);
+ UPD_MM_GET_ARRAY(udev->memory, fp,2);
+ fp[0] = 0.0;
+ fp[1] = 1.0;
+ upd->float_a[upd->cmap[imap].xfer].data = fp;
+ upd->float_a[upd->cmap[imap].xfer].size = 2;
+ }
+ n = upd->float_a[upd->cmap[imap].xfer].size-1;
+ now = upd->float_a[upd->cmap[imap].xfer].data;
+ last = now[n];
+
+ if( *now < last) { /* Rising */
+ last = *now++;
+ while(n--) {
+ if(last >= *now) break;
+ last = *now++;
+ }
+ } else if(*now > last) { /* Falling */
+ last = *now++;
+ while(n--) {
+ if(last <= *now) break;
+ last = *now++;
+ }
+ } /* Monotony-check */
+
+ if(0 <= n) {
+#if UPD_MESSAGES & UPD_M_WARNING
+ errprintf(udev->memory,
+ "upd_open_map: %d. Component has non monoton Xfer\n",imap+1);
+#endif
+ success = false;
+
+ } else {
+
+#if UPD_MESSAGES & UPD_M_WARNING
+
+ bitmsk = ((uint32_t) 1 << upd->int_a[IA_COMPBITS].data[imap]) -1;
+ bitmsk <<= upd->int_a[IA_COMPSHIFT].data[imap];
+
+ if(used & bitmsk) errprintf(udev->memory,
+ "upd_open_map: %d. Component overlaps with others\n",imap+1);
+
+ used |= bitmsk;
+#endif
+ }
+ }
+ }
+
+ if(!success) imap = 0;
+
+ } /* Check number of Bits */
+
+/** Do the allocation */
+
+ if(imap) {
+
+ for(imap = 0; UPD_CMAP_MAX > imap; ++imap) {
+ if(0 > upd->cmap[imap].xfer) continue;
+
+ upd->cmap[imap].bits = upd->int_a[IA_COMPBITS].data[imap];
+ upd->cmap[imap].bitshf = upd->int_a[IA_COMPSHIFT].data[imap];
+ upd->cmap[imap].bitmsk = 1;
+ upd->cmap[imap].bitmsk <<= upd->cmap[imap].bits;
+ upd->cmap[imap].bitmsk -= 1;
+ upd->cmap[imap].rise =
+ upd->float_a[upd->cmap[imap].xfer].data[0] <
+ upd->float_a[upd->cmap[imap].xfer].data[
+ upd->float_a[upd->cmap[imap].xfer].size-1] ?
+ true : false;
+ upd->cmap[imap].code = gs_malloc(udev->memory, upd->cmap[imap].bitmsk+1,
+ sizeof(upd->cmap[imap].code[0]),"upd/code");
+ if(!upd->cmap[imap].code) break;
+ }
+
+ if(UPD_CMAP_MAX > imap) {
+
+ imap = 0;
+
+#if UPD_MESSAGES & UPD_M_ERROR
+ errprintf(udev->memory,
+ "upd_open_map: could not allocate code-arrays\n");
+# endif
+
+ }
+ }
+
+/** then fill the code-arrays */
+
+ if(imap) {
+/*
+ * Try making things easier: (than with stcolor)
+ * normalize values to 0.0/1.0-Range
+ * X-Axis: Color-Values (implied)
+ * Y-Values: Indices (given)
+ */
+
+ for(imap = 0; UPD_CMAP_MAX > imap; ++imap) {
+
+ const updcmap_p cmap = upd->cmap + imap;
+ uint32_t ly,iy;
+ float ystep,xstep,fx,fy;
+
+/* Variables & Macro for Range-Normalization */
+ double offset,scale;
+#define XFVAL(I) ((upd->float_a[cmap->xfer].data[I]-offset)*scale)
+
+ if(0 > cmap->xfer) continue;
+
+ cmap->code[cmap->bitmsk] = gx_max_color_value;
+
+ if(!cmap->bits) continue;
+
+ offset = upd->float_a[cmap->xfer].data[0];
+ if( 0.0 > offset) offset = 0.0;
+ else if(1.0 < offset) offset = 1.0;
+
+ scale = upd->float_a[cmap->xfer].data[upd->float_a[cmap->xfer].size-1];
+ if( 0.0 > scale ) scale = 0.0;
+ else if(1.0 < scale ) scale = 1.0;
+
+ if(scale != offset) scale = 1.0 / (scale - offset);
+ else scale = 0.0;
+
+/* interpolate */
+ ystep = (float) 1.0 / (float) cmap->bitmsk;
+ xstep = (float) 1.0 / (float)(upd->float_a[cmap->xfer].size - 1);
+
+ iy = 0;
+ for(ly = 0; ly <= cmap->bitmsk; ++ly) {
+
+ fy = ystep * ly; /* Target-Value */
+
+ while(((iy+2) < upd->float_a[cmap->xfer].size) &&
+ (fy > XFVAL(iy+1))) ++iy;
+
+ fx = iy + (fy - XFVAL(iy))/(XFVAL(iy+1) - XFVAL(iy));
+
+ fx *= xstep * gx_max_color_value;
+
+ fx = fx < 0.0 ? 0.0 :
+ (fx > gx_max_color_value ? gx_max_color_value : fx);
+
+ cmap->code[ly] = (gx_color_value)fx;
+ if((fx - cmap->code[ly]) >= 0.5) cmap->code[ly] += 1;
+ }
+
+#undef XFVAL
+
+ }
+ }
+
+/** If we're ok, massage upd->ncomp */
+
+ if(imap) {
+ switch(upd->choice[C_MAPPER]) {
+ case MAP_GRAY:
+ if(1 > imap) imap = 0;
+ upd->ncomp = 1;
+ break;
+ case MAP_RGBW: /* RGB->RGBW */
+ if(4 > imap) imap = 0;
+ upd->ncomp = 4;
+ break;
+ case MAP_RGB: /* Plain RGB */
+ if(3 > imap) imap = 0;
+ upd->ncomp = 3;
+ break;
+ case MAP_CMYK: /* Plain KCMY */
+ if(4 > imap) imap = 0;
+ upd->ncomp = 4;
+ break;
+ case MAP_CMYKGEN: /* KCMY with black-generation */
+ if(4 > imap) imap = 0;
+ upd->ncomp = 4;
+ break;
+ case MAP_RGBOV: /* RGB->KCMY with black-generation */
+ if(4 > imap) imap = 0;
+ upd->ncomp = 4;
+ break;
+ case MAP_RGBNOV: /* RGB->KCMY with black-generation */
+ if(4 > imap) imap = 0;
+ upd->ncomp = 4;
+ break;
+
+ default:
+ imap = 0;
+#if UPD_MESSAGES & UPD_M_WARNING
+ errprintf(udev->memory,
+ "upd_open: Mapping %d unknown\n",upd->choice[C_MAPPER]);
+#endif
+
+ break;
+ }
+ }
+
+/** If unsuccesful, install the default routines */
+
+ if(!imap) {
+ upd_close_map(udev);
+ } else {
+ upd->flags |= B_MAP;
+ upd_procs_map(udev);
+ }
+
+ return imap ? 1 : -1;
+}
+
+/* ------------------------------------------------------------------- */
+/* upd_procs_map: (de-) install the color-mapping-procedures */
+/* ------------------------------------------------------------------- */
+
+static int
+upd_procs_map(upd_device *udev)
+{
+ int imap;
+
+ if( udev->upd &&
+ (udev->upd->flags & B_MAP)) imap = udev->upd->choice[C_MAPPER];
+ else imap = 0;
+
+ switch(imap) {
+ case MAP_GRAY: /* Grayscale -> Grayscale */
+ set_dev_proc(udev,encode_color, upd_rgb_1color);
+ set_dev_proc(udev,decode_color, upd_1color_rgb);
+ set_dev_proc(udev,map_rgb_color, upd_rgb_1color);
+ set_dev_proc(udev,map_cmyk_color,gx_default_map_cmyk_color);
+ set_dev_proc(udev,map_color_rgb, upd_1color_rgb);
+ break;
+ case MAP_RGBW: /* RGB->RGBW */
+ set_dev_proc(udev,encode_color, upd_rgb_4color);
+ set_dev_proc(udev,decode_color, upd_4color_rgb);
+ set_dev_proc(udev,map_rgb_color, upd_rgb_4color);
+ set_dev_proc(udev,map_cmyk_color,gx_default_map_cmyk_color);
+ set_dev_proc(udev,map_color_rgb, upd_4color_rgb);
+ break;
+ case MAP_RGB: /* Plain RGB */
+ set_dev_proc(udev,encode_color, upd_rgb_3color);
+ set_dev_proc(udev,decode_color, upd_3color_rgb);
+ set_dev_proc(udev,map_rgb_color, upd_rgb_3color);
+ set_dev_proc(udev,map_cmyk_color,gx_default_map_cmyk_color);
+ set_dev_proc(udev,map_color_rgb, upd_3color_rgb);
+ break;
+ case MAP_CMYK: /* Plain KCMY */
+ set_dev_proc(udev,encode_color, upd_cmyk_icolor);
+ set_dev_proc(udev,decode_color, upd_icolor_rgb);
+ set_dev_proc(udev,map_rgb_color, gx_default_map_rgb_color);
+ set_dev_proc(udev,map_cmyk_color,upd_cmyk_icolor);
+ set_dev_proc(udev,map_color_rgb, upd_icolor_rgb);
+ break;
+ case MAP_CMYKGEN: /* KCMY with black-generation */
+ set_dev_proc(udev,encode_color, upd_cmyk_kcolor);
+ set_dev_proc(udev,decode_color, upd_kcolor_rgb);
+ set_dev_proc(udev,map_rgb_color, gx_default_map_rgb_color);
+ set_dev_proc(udev,map_cmyk_color,upd_cmyk_kcolor);
+ set_dev_proc(udev,map_color_rgb, upd_kcolor_rgb);
+ break;
+ case MAP_RGBOV: /* RGB -> KCMY with BG and UCR for CMYK-Output */
+ set_dev_proc(udev,encode_color, upd_rgb_ovcolor);
+ set_dev_proc(udev,decode_color, upd_ovcolor_rgb);
+ set_dev_proc(udev,map_rgb_color, upd_rgb_ovcolor);
+ set_dev_proc(udev,map_cmyk_color,gx_default_map_cmyk_color);
+ set_dev_proc(udev,map_color_rgb, upd_ovcolor_rgb);
+ break;
+ case MAP_RGBNOV: /* RGB -> KCMY with BG and UCR for CMY+K-Output */
+ set_dev_proc(udev,encode_color, upd_rgb_novcolor);
+ set_dev_proc(udev,decode_color, upd_novcolor_rgb);
+ set_dev_proc(udev,map_rgb_color, upd_rgb_novcolor);
+ set_dev_proc(udev,map_cmyk_color,gx_default_map_cmyk_color);
+ set_dev_proc(udev,map_color_rgb, upd_novcolor_rgb);
+ break;
+
+ default:
+ set_dev_proc(udev,encode_color, gx_default_map_rgb_color);
+ set_dev_proc(udev,decode_color, gx_default_map_color_rgb);
+ set_dev_proc(udev,map_rgb_color, gx_default_map_rgb_color);
+ set_dev_proc(udev,map_cmyk_color,gx_default_map_cmyk_color);
+ set_dev_proc(udev,map_color_rgb, gx_default_map_color_rgb);
+ break;
+ }
+ return 0;
+
+}
+
+/* ------------------------------------------------------------------- */
+/* upd_close_map: remove color mapping */
+/* ------------------------------------------------------------------- */
+
+static int
+upd_close_map(upd_device *udev)
+{
+ const upd_p upd = udev->upd;
+ int imap;
+
+ if(upd) {
+
+ for(imap = 0; UPD_CMAP_MAX > imap; ++imap) {
+
+ if(upd->cmap[imap].code)
+ gs_free(udev->memory, upd->cmap[imap].code,sizeof(upd->cmap[imap].code[0]),
+ upd->cmap[imap].bitmsk+1,"upd/code");
+ upd->cmap[imap].code = NULL;
+
+ upd->cmap[imap].bitmsk = 0;
+ upd->cmap[imap].bitshf = 0;
+ upd->cmap[imap].bits = 0;
+ upd->cmap[imap].rise = false;
+ }
+ upd->flags &= ~B_MAP;
+ }
+
+ upd_procs_map(udev);
+
+ return 0;
+}
+
+/* ------------------------------------------------------------------- */
+/* Functions for the rendering of data */
+/* ------------------------------------------------------------------- */
+
+/**
+Inside the main-upd-type are a "valbuf" and some unidentified
+pointers. This stuff is used in conjunction with the rendering,
+which is the process of converting gx_color_indices into something
+suitable for the device.
+
+*/
+
+/* ------------------------------------------------------------------- */
+/* upd_open_render: Initialize rendering */
+/* ------------------------------------------------------------------- */
+
+static void
+upd_open_render(upd_device *udev)
+{
+ const upd_p upd = udev->upd;
+ int icomp;
+
+/** Reset everything related to rendering */
+ upd->flags &= ~B_RENDER;
+ upd->valbuf = NULL;
+ upd->nvalbuf = 0;
+ upd->render = NULL;
+ upd->start_render = NULL;
+ for(icomp = 0; UPD_VALPTR_MAX > icomp; ++icomp) upd->valptr[icomp] = NULL;
+
+ if( (B_BUF | B_MAP) ==
+ ((B_BUF | B_MAP | B_ERROR) & upd->flags)) {
+
+/** Establish the renderingwidth in upd */
+ upd->rwidth = upd->gswidth;
+ if((0 < upd->ints[I_PWIDTH]) &&
+ (upd->gswidth > upd->ints[I_PWIDTH]) )
+ upd->rwidth = upd->ints[I_PWIDTH];
+
+/** Call the Render-specific Open-Function */
+ switch(upd->choice[C_RENDER]) {
+ case RND_FSCOMP:
+ upd_open_fscomp(udev);
+ break;
+ case RND_FSCMYK:
+ upd_open_fscmyk(udev);
+ break;
+ case RND_FSCMY_K:
+ upd_open_fscmy_k(udev);
+ break;
+ default:
+#if UPD_MESSAGES & UPD_M_WARNING
+ errprintf(udev->memory, "upd_open_render: Unknown rendering type %d\n",
+ upd->choice[C_RENDER]);
+#endif
+ break;
+ }
+ }
+
+ if(B_RENDER != ((B_ERROR | B_RENDER) & upd->flags))
+ upd_close_render(udev);
+
+ return;
+}
+
+/* ------------------------------------------------------------------- */
+/* upd_close_render: Deinitialize rendering */
+/* ------------------------------------------------------------------- */
+
+static void
+upd_close_render(upd_device *udev)
+{
+ const upd_p upd = udev->upd;
+
+ if(upd) {
+ int icomp;
+
+ if((upd->render == upd_fscomp) ||
+ (upd->render == upd_fscmyk) ) upd_close_fscomp(udev);
+
+ if((0 < upd->nvalbuf) && upd->valbuf)
+ gs_free(udev->memory, upd->valbuf,upd->nvalbuf,sizeof(upd->valbuf[0]),"upd/valbuf");
+ upd->valbuf = NULL;
+ upd->nvalbuf = 0;
+
+ upd->flags &= ~B_RENDER;
+ upd->render = NULL;
+ upd->start_render = NULL;
+ for(icomp = 0; UPD_VALPTR_MAX > icomp; ++icomp) upd->valptr[icomp] = NULL;
+
+ }
+ return;
+}
+
+/* ------------------------------------------------------------------- */
+/* upd_open_fscomp: Initialize Component-Floyd-Steinberg */
+/* ------------------------------------------------------------------- */
+#if UPD_MESSAGES & UPD_M_FSBUF
+static int32_t fs_emin[UPD_VALPTR_MAX],fs_emax[UPD_VALPTR_MAX];
+#endif
+static void
+upd_open_fscomp(upd_device *udev)
+{
+ const upd_p upd = udev->upd;
+ int icomp,order[UPD_CMAP_MAX];
+
+#if UPD_MESSAGES & UPD_M_FSBUF
+ for(icomp = 0; UPD_VALPTR_MAX < icomp; ++icomp)
+ fs_emin[icomp] = fs_emax[icomp] = 0;
+#endif
+
+ icomp = upd->ncomp;
+
+ if((0 >= icomp) ||
+ (UPD_VALPTR_MAX < icomp) ||
+ (UPD_CMAP_MAX < icomp) ) icomp = 0;
+
+/**
+This Version of the FS-algorithm works on the mapped components, but
+the printing-order might be different from the order dictated by the
+mapping-routines. The optional COMPORDER-Array is used for that. The
+initial test checks it's integrity.
+*/
+ if(icomp) {
+ if(upd->ncomp <= upd->int_a[IA_COMPORDER].size) { /* Reordering */
+ bool success = true;
+ for(icomp = 0; upd->ncomp > icomp; ++icomp) {
+ order[icomp] = upd->int_a[IA_COMPORDER].data[icomp];
+ if((0 > order[icomp]) ||
+ (UPD_CMAP_MAX <= order[icomp]) ) {
+ success = false;
+#if UPD_MESSAGES & UPD_M_WARNING
+ errprintf(udev->memory,
+ "upd_open_fscomp: %d is illegal component-index\n",
+ order[icomp]);
+#endif
+ }
+ }
+ if(!success) icomp = 0;
+ } else { /* Default-Ordering */
+ for(icomp = 0; UPD_CMAP_MAX > icomp; ++icomp) order[icomp] = icomp;
+ } /* Ordering defined */
+ }
+
+/**
+If anything was ok. up to now, memory get's allocated.
+*/
+ if(icomp) {
+
+ for(icomp = 0; upd->ncomp > icomp; ++icomp) {
+ upd->valptr[icomp] = gs_malloc(udev->memory, 1,sizeof(updcomp_t),"upd/fscomp");
+ if(NULL == upd->valptr[icomp]) {
+#if UPD_MESSAGES & UPD_M_ERROR
+ errprintf(udev->memory,
+ "upd_open_fscomp: could not allocate %d. updcomp\n",
+ icomp);
+#endif
+ icomp = 0;
+ break;
+ }
+ }
+ }
+
+ if(icomp) {
+ uint need;
+
+ need = (2 + upd->rwidth) * upd->ncomp;
+ upd->valbuf = gs_malloc(udev->memory, need,sizeof(upd->valbuf[0]),"upd/valbuf");
+
+ if(upd->valbuf) {
+ upd->nvalbuf = need;
+ memset(upd->valbuf,0,need*sizeof(upd->valbuf[0]));
+ } else {
+#if UPD_MESSAGES & UPD_M_ERROR
+ errprintf(udev->memory,
+ "upd_open_fscomp: could not allocate %u words for valbuf\n",
+ need);
+#endif
+ icomp = 0;
+ }
+ }
+
+/* Still happy? then compute component-values */
+
+ if(icomp) {
+ for(icomp = 0; upd->ncomp > icomp; ++icomp) {
+
+ const updcomp_p comp = upd->valptr[icomp];
+ const int32_t nsteps = upd->cmap[order[icomp]].bitmsk;
+ float ymin,ymax;
+ int32_t highmod,highval;
+ int i;
+
+ comp->threshold = nsteps;
+ comp->spotsize = nsteps;
+ comp->offset = 0;
+ comp->scale = 1;
+ comp->cmap = order[icomp];
+ upd->cmap[comp->cmap].comp = icomp;
+ comp->bits = upd->cmap[comp->cmap].bits;
+ comp->bitshf = upd->cmap[comp->cmap].bitshf;
+ comp->bitmsk = upd->cmap[comp->cmap].bitmsk;
+
+ if(!nsteps) continue; /* A 0-Bit component is legal! */
+
+ if(upd->cmap[comp->cmap].rise) {
+ ymin = upd->float_a[upd->cmap[comp->cmap].xfer].data[0];
+ ymax = upd->float_a[upd->cmap[comp->cmap].xfer].data[
+ upd->float_a[upd->cmap[comp->cmap].xfer].size-1];
+ } else {
+ ymax = upd->float_a[upd->cmap[comp->cmap].xfer].data[0];
+ ymin = upd->float_a[upd->cmap[comp->cmap].xfer].data[
+ upd->float_a[upd->cmap[comp->cmap].xfer].size-1];
+ }
+
+ if(0.0 > ymin) {
+ ymin = 0.0;
+ if(0.0 > ymax) ymax = 1.0 / (float) (nsteps+1);
+ }
+ if(1.0 < ymax) ymax = 1.0;
+
+ comp->spotsize = ((int32_t) 1 << 28) - 1;
+
+ for(i = 0; i < 32; ++i) { /* Attempt Ideal */
+
+ highval = (int32_t)((ymax-ymin) * (double) comp->spotsize + 0.5);
+
+ if(!(highmod = highval % nsteps)) break; /* Gotcha */
+
+ highval += nsteps - highmod;
+ comp->spotsize = (int32_t)((double) highval / (ymax-ymin) + 0.5);
+
+ if(!(comp->spotsize % 2)) comp->spotsize++;
+
+ } /* Attempt Ideal */
+
+ comp->offset = (int32_t)(ymin * (double) comp->spotsize + (double) 0.5);
+ comp->scale = highval / nsteps;
+ comp->threshold = comp->spotsize / 2;
+
+#if UPD_MESSAGES & UPD_M_SETUP
+ errprintf(udev->memory,
+ "Values for %d. Component after %d iterations\n",comp->cmap+1,i);
+ errprintf(udev->memory,
+ "steps: %10ld, Bits: %d\n",(long) comp->bitmsk,comp->bits);
+ errprintf(udev->memory,
+ "xfer: %10d Points, %s\n",
+ upd->float_a[upd->cmap[comp->cmap].xfer].size,
+ upd->cmap[comp->cmap].rise ? "rising" : "falling");
+ errprintf(udev->memory,
+ "offset: %10d 0x%08x\n",comp->offset,comp->offset);
+ errprintf(udev->memory,
+ "scale: %10d 0x%08x\n",comp->scale,comp->scale);
+ errprintf(udev->memory,
+ "threshold: %10d 0x%08x\n",comp->threshold,comp->threshold);
+ errprintf(udev->memory,
+ "spotsize: %10d 0x%08x\n",comp->spotsize,comp->spotsize);
+#endif
+ }
+ }
+/**
+Optional Random Initialization of the value-Buffer
+*/
+ if(icomp && !(B_FSZERO & upd->flags)) {
+ for(icomp = 0; icomp < upd->ncomp; ++icomp) {
+ const updcomp_p comp = upd->valptr[icomp];
+ int i;
+ int32_t lv = INT32_MAX, hv = INT32_MIN, v;
+ float scale;
+ for(i = icomp; i < upd->nvalbuf; i += upd->ncomp) {
+ v = rand();
+ if(lv > v) lv = v;
+ if(hv < v) hv = v;
+ upd->valbuf[i] = v;
+ }
+ scale = (float) comp->threshold / (float) (hv - lv);
+ lv += (int32_t)(comp->threshold / (2*scale));
+ for(i = icomp; i < upd->nvalbuf; i += upd->ncomp)
+ upd->valbuf[i] = (int32_t)(scale * (upd->valbuf[i] - lv));
+ }
+ }
+
+/**
+The render-Routine acts as an indicator, which render-close is to use!
+*/
+ upd->render = upd_fscomp;
+
+ if(icomp) upd->flags |= B_RENDER;
+ else upd->flags &= ~B_RENDER;
+
+ return;
+}
+
+/* ------------------------------------------------------------------- */
+/* upd_close_fscomp: Deinitialize Component-Floyd-Steinberg */
+/* ------------------------------------------------------------------- */
+
+static void
+upd_close_fscomp(upd_device *udev)
+{
+ const upd_p upd = udev->upd;
+ int icomp;
+
+#if UPD_MESSAGES & UPD_M_FSBUF
+ if(upd && (upd->flags & B_RENDER)) {
+
+ for(icomp = 0; icomp < upd->ncomp; ++icomp) {
+ updcomp_p comp = upd->valptr[icomp];
+ if(!comp) continue;
+ if(!comp->spotsize) continue;
+ errprintf(udev->memory,"%d. Component: %6.3f <= error <= %6.3f\n",
+ icomp+1,
+ (double) fs_emin[icomp] / (double) comp->spotsize,
+ (double) fs_emax[icomp] / (double) comp->spotsize);
+ }
+
+ }
+#endif
+
+ for(icomp = 0; UPD_VALPTR_MAX > icomp; ++icomp) {
+ if(!upd->valptr[icomp]) continue;
+ gs_free(udev->memory, upd->valptr[icomp],1,sizeof(updcomp_t),"upd/fscomp");
+ upd->valptr[icomp] = NULL;
+ }
+}
+
+/* ------------------------------------------------------------------- */
+/* upd_fscomp: Apply Floyd-Steinberg to each component */
+/* ------------------------------------------------------------------- */
+
+/**
+ With UPD_M_FSBUF Max/Min-Values for the Errors are computed
+*/
+#if UPD_MESSAGES & UPD_M_FSBUF
+#define FS_M_ROWERR(I) \
+ if(fs_emin[I] > rowerr[I]) fs_emin[I] = rowerr[I]; \
+ if(fs_emax[I] < rowerr[I]) fs_emax[I] = rowerr[I];
+#else
+#define FS_M_ROWERR(I) ;
+#endif
+/**
+ FS_GOAL computes the desired Pixel-Value
+*/
+#define FS_GOAL(Raw,I) \
+ pixel[I] = (int32_t)(Raw) * comp[I]->scale + comp[I]->offset \
+ + rowerr[I] + colerr[I] - ((colerr[I]+4)>>3); \
+ if( pixel[I] < 0) pixel[I] = 0; \
+ else if( pixel[I] > comp[I]->spotsize) pixel[I] = comp[I]->spotsize;
+
+/*
+ * Distribute the error: prev now next
+ * X 7/16 Y
+ * 3/16 5/16 1/16 Y+1
+ */
+#define FS_DIST(I) \
+ if(!first) rowerr[I-dir] += ((3*pixel[I]+8)>>4); /* 3/16 */ \
+ rowerr[I ] = ((5*pixel[I] )>>4) /* 5/16 */ \
+ + (( colerr[I]+4)>>3); /* 1/16 (rest) */ \
+ colerr[I ] = pixel[I] /* 8/16 (neu) */ \
+ - ((5*pixel[I] )>>4) \
+ - ((3*pixel[I]+8)>>4);
+/**
+ S_FSTEP adjusts the Indices (rowerr, bit and iword)
+*/
+#define S_FSTEP \
+ rowerr += dir; \
+ first = false; \
+ if(0 > dir) { /* Reverse */ \
+ if(!(bit <<= 1)) { bit = 0x01; ibyte--; }\
+ } else { /* Forward */ \
+ if(!(bit >>= 1)) { bit = 0x80; ibyte++; }\
+ } /* Inc/Dec Bit */
+
+static int
+upd_fscomp(upd_p upd)
+{
+ const updscan_p scan = upd->scnbuf[upd->yscnbuf & upd->scnmsk];
+ const updcomp_p *comp = (updcomp_p *) upd->valptr;
+ int32_t *const pixel = upd->valbuf;
+ int32_t *const colerr = pixel + upd->ncomp;
+ int32_t *rowerr = colerr + upd->ncomp;
+ int pwidth = upd->rwidth;
+ int dir,ibyte;
+ int iblack,bblack,pxlset;
+ uint32_t ci;
+ byte bit;
+ bool first = true;
+/*
+ * Erase the component-Data
+ */
+ switch(upd->ncomp) {
+ case 4: memset(scan[3].bytes,0,upd->nbytes);
+ case 3: memset(scan[2].bytes,0,upd->nbytes);
+ memset(scan[1].bytes,0,upd->nbytes);
+ default: memset(scan[0].bytes,0,upd->nbytes);
+ }
+/*
+ * determine the direction
+ */
+ if(upd->flags & B_REVDIR) { /* This one reverse */
+
+ if(upd->flags & B_YFLIP) {
+ dir = upd->ncomp;
+ bit = 0x80;
+ ibyte = 0;
+ } else {
+ dir = -upd->ncomp;
+ rowerr += upd->ncomp * (pwidth-1);
+ bit = 0x80 >> ((pwidth-1) & 7);
+ ibyte = (pwidth-1) >> 3;
+ }
+
+ if(!(upd->flags & B_FSWHITE)) {
+ upd_pxlfwd(upd);
+ while((0 < pwidth) && !upd_pxlget(upd)) pwidth--;
+ }
+
+ upd_pxlrev(upd);
+
+ } else { /* This one forward */
+
+ if(upd->flags & B_YFLIP) {
+ dir = -upd->ncomp;
+ rowerr += upd->ncomp * (pwidth-1);
+ bit = 0x80 >> ((pwidth-1) & 7);
+ ibyte = (pwidth-1) >> 3;
+ } else {
+ dir = upd->ncomp;
+ bit = 0x80;
+ ibyte = 0;
+ }
+
+ if(!(upd->flags & B_FSWHITE)) {
+ upd_pxlrev(upd);
+ while((0 < pwidth) && !upd_pxlget(upd)) pwidth--;
+ }
+
+ upd_pxlfwd(upd);
+
+ } /* reverse or forward */
+/*
+ * Toggle Direction, if not fixed
+ */
+ if(!(upd->flags & B_FIXDIR)) upd->flags ^= B_REVDIR;
+/*
+ * Skip over leading white-space
+ */
+ if(!(upd->flags & B_FSWHITE)) {
+ upd_proc_pxlget((*fun)) = upd->pxlget;
+ byte *ptr = upd->pxlptr;
+ while((0 < pwidth) && !upd_pxlget(upd)) {
+ pwidth--;
+ fun = upd->pxlget;
+ ptr = upd->pxlptr;
+ S_FSTEP
+ }
+ upd->pxlget = fun;
+ upd->pxlptr = ptr;
+ }
+/*
+ * Set iblack, if black-reduction is active
+ */
+ iblack = -1;
+ bblack = 0;
+ if((4 == upd->ncomp) && (B_REDUCEK & upd->flags)) {
+ iblack = upd->cmap[0].comp;
+ bblack = 1<<iblack;
+ }
+/*
+ * Process all Pixels
+ */
+ first = true;
+ while(0 < pwidth--) {
+/*
+ * Execute FS-Algorithm for each active component
+ */
+ pxlset = 0;
+ ci = upd_pxlget(upd);
+ switch(upd->ncomp) {
+ case 4: FS_M_ROWERR(3)
+ FS_GOAL(comp[3]->bitmsk & (ci >> comp[3]->bitshf),3)
+ if(pixel[3] > comp[3]->threshold) { /* "Fire" */
+ pixel[3] -= comp[3]->spotsize;
+ scan[3].bytes[ibyte] |= bit;
+ pxlset |= 8;
+ } /* "Fire" */
+ FS_DIST(3)
+
+ case 3: FS_M_ROWERR(2)
+ FS_GOAL(comp[2]->bitmsk & (ci >> comp[2]->bitshf),2)
+ if(pixel[2] > comp[2]->threshold) { /* "Fire" */
+ pixel[2] -= comp[2]->spotsize;
+ scan[2].bytes[ibyte] |= bit;
+ pxlset |= 4;
+ } /* "Fire" */
+ FS_DIST(2)
+
+ FS_M_ROWERR(1)
+ FS_GOAL(comp[1]->bitmsk & (ci >> comp[1]->bitshf),1)
+ if(pixel[1] > comp[1]->threshold) { /* "Fire" */
+ pixel[1] -= comp[1]->spotsize;
+ scan[1].bytes[ibyte] |= bit;
+ pxlset |= 2;
+ } /* "Fire" */
+ FS_DIST(1)
+
+ default: FS_M_ROWERR(0)
+ FS_GOAL(comp[0]->bitmsk & (ci >> comp[0]->bitshf),0)
+ if(pixel[0] > comp[0]->threshold) { /* "Fire" */
+ pixel[0] -= comp[0]->spotsize;
+ scan[0].bytes[ibyte] |= bit;
+ pxlset |= 1;
+ } /* "Fire" */
+ FS_DIST(0)
+ }
+/*
+ * Black-Reduction
+ */
+ if(bblack) {
+ if(pxlset & bblack) pxlset |= 15;
+ switch(pxlset) {
+ case 0:
+ case 1:
+ case 2:
+ case 4:
+ case 8:
+ case 3:
+ case 5:
+ case 9:
+ case 6:
+ case 10:
+ case 12:
+ break;
+ default:
+ scan[0].bytes[ibyte] &= ~bit;
+ scan[1].bytes[ibyte] &= ~bit;
+ scan[2].bytes[ibyte] &= ~bit;
+ scan[3].bytes[ibyte] &= ~bit;
+ scan[iblack].bytes[ibyte] |= bit;
+ break;
+ }
+ }
+/*
+ * Adjust rowerr, bit & iword, depending on direction
+ */
+ S_FSTEP
+ }
+/*
+ * Finally call the limits-Routine
+ */
+ if(0 < upd->nlimits) upd_limits(upd,true);
+ return 0;
+}
+
+/* ------------------------------------------------------------------- */
+/* upd_open_fscmyk: Initialize Component-Floyd-Steinberg */
+/* ------------------------------------------------------------------- */
+
+static void
+upd_open_fscmyk(upd_device *udev)
+{
+ const upd_p upd = udev->upd;
+
+ upd_open_fscomp(udev);
+
+ if((B_RENDER & upd->flags) &&
+ (4 == upd->ncomp) &&
+ (8 <= upd->cmap[0].bits) && (24 == upd->cmap[0].bitshf) &&
+ (8 <= upd->cmap[1].bits) && (16 == upd->cmap[1].bitshf) &&
+ (8 <= upd->cmap[2].bits) && ( 8 == upd->cmap[2].bitshf) &&
+ (8 <= upd->cmap[3].bits) && ( 0 == upd->cmap[3].bitshf) ) {
+ upd->render = upd_fscmyk;
+ } else {
+ upd->flags &= ~B_RENDER;
+ }
+
+}
+
+/* ------------------------------------------------------------------- */
+/* upd_fscmyk: 32 Bit, K-CMY-Order Dithering */
+/* ------------------------------------------------------------------- */
+
+static int
+upd_fscmyk(upd_p upd)
+{
+ const updscan_p scan = upd->scnbuf[upd->yscnbuf & upd->scnmsk];
+ int32_t *const pixel = upd->valbuf;
+ const updcomp_p *comp = (updcomp_p *) upd->valptr;
+ int32_t *const colerr = pixel + 4;
+ int32_t *rowerr = colerr + 4;
+ int32_t pwidth = upd->rwidth;
+ int dir,ibyte;
+ byte bit,*data;
+ bool first = false;
+/*
+ * Erase the component-Data
+ */
+ memset(scan[0].bytes,0,upd->nbytes);
+ memset(scan[1].bytes,0,upd->nbytes);
+ memset(scan[2].bytes,0,upd->nbytes);
+ memset(scan[3].bytes,0,upd->nbytes);
+
+/*
+ * determine the direction
+ */
+ if(upd->flags & B_REVDIR) { /* This one reverse */
+
+ if(!(upd->flags & B_FSWHITE)) {
+ data = upd->gsscan;
+ while(0 < pwidth && !*(uint32_t *)data) pwidth--, data += 4;
+ if(0 >= pwidth) {
+ if(0 < upd->nlimits) upd_limits(upd,false);
+ return 0;
+ }
+ }
+
+ data = upd->gsscan + 4 * (upd->rwidth-1);
+
+ } else { /* This one forward */
+
+ if(!(upd->flags & B_FSWHITE)) {
+ data = upd->gsscan + 4 * (upd->rwidth-1);
+ while(0 < pwidth && !*(uint32_t *)data) pwidth--, data -= 4;
+ if(0 >= pwidth) {
+ if(0 < upd->nlimits) upd_limits(upd,false);
+ return 0;
+ }
+ }
+
+ data = upd->gsscan;
+
+ } /* reverse or forward */
+/*
+ * Bits depend on FLIP & Direction
+ */
+ if(!(B_REVDIR & upd->flags) == !(B_YFLIP & upd->flags)) {
+ dir = 4;
+ bit = 0x80;
+ ibyte = 0;
+ } else {
+ dir = -4;
+ rowerr += 4 * (upd->rwidth-1);
+ bit = 0x80 >> ((upd->rwidth-1) & 7);
+ ibyte = (upd->rwidth-1) >> 3;
+ }
+
+/*
+ * Toggle Direction, if not fixed
+ */
+ if(!(upd->flags & B_FIXDIR)) upd->flags ^= B_REVDIR;
+/*
+ * Skip over leading white-space
+ */
+ if(!(upd->flags & B_FSWHITE)) {
+ while(0 < pwidth && !*((uint32_t *)data)) {
+ pwidth--;
+ if(B_YFLIP & upd->flags) data -= dir;
+ else data += dir;
+ S_FSTEP
+ }
+ }
+/*
+ * Process all Pixels
+ */
+ first = true;
+ while(0 < pwidth--) {
+/*
+ * Compute the Black-Value first
+ */
+ FS_M_ROWERR(upd->cmap[0].comp) FS_GOAL(data[0],upd->cmap[0].comp);
+
+/*
+ * Decide wether this is a color value
+ */
+ if(data[1] || data[2] || data[3]) {
+
+ FS_M_ROWERR(upd->cmap[1].comp) FS_GOAL(data[1],upd->cmap[1].comp)
+ FS_M_ROWERR(upd->cmap[2].comp) FS_GOAL(data[2],upd->cmap[2].comp)
+ FS_M_ROWERR(upd->cmap[3].comp) FS_GOAL(data[3],upd->cmap[3].comp)
+/*
+ * if black fires, then all other components fire logically too
+ */
+ if(pixel[upd->cmap[0].comp] > comp[upd->cmap[0].comp]->threshold) {
+
+ pixel[0] -= comp[0]->spotsize;
+ pixel[1] -= comp[1]->spotsize;
+ pixel[2] -= comp[2]->spotsize;
+ pixel[3] -= comp[3]->spotsize;
+ scan[upd->cmap[0].comp].bytes[ibyte] |= bit;
+
+/*
+ * if black is below threshold, only components with larger data-values
+ * are allowed to fire
+ */
+ } else { /* Restricted firing */
+
+ if(( data[0] < data[1]) &&
+ (pixel[upd->cmap[1].comp] >
+ comp[upd->cmap[1].comp]->threshold)) { /* "Fire" */
+ pixel[upd->cmap[1].comp] -= comp[upd->cmap[1].comp]->spotsize;
+ scan[upd->cmap[1].comp].bytes[ibyte] |= bit;
+ } /* "Fire" */
+
+ if(( data[0] < data[2]) &&
+ (pixel[upd->cmap[2].comp] >
+ comp[upd->cmap[2].comp]->threshold)) { /* "Fire" */
+ pixel[upd->cmap[2].comp] -= comp[upd->cmap[2].comp]->spotsize;
+ scan[upd->cmap[2].comp].bytes[ibyte] |= bit;
+ } /* "Fire" */
+
+ if(( data[0] < data[3]) &&
+ (pixel[upd->cmap[3].comp] >
+ comp[upd->cmap[3].comp]->threshold)) { /* "Fire" */
+ pixel[upd->cmap[3].comp] -= comp[upd->cmap[3].comp]->spotsize;
+ scan[upd->cmap[3].comp].bytes[ibyte] |= bit;
+ } /* "Fire" */
+
+ } /* Fire-Mode */
+
+/*
+ * Handle Color-Errors
+ */
+ FS_DIST(upd->cmap[3].comp)
+ FS_DIST(upd->cmap[2].comp)
+ FS_DIST(upd->cmap[1].comp)
+
+ } else if(pixel[upd->cmap[0].comp] > comp[upd->cmap[0].comp]->threshold) {
+ scan[upd->cmap[0].comp].bytes[ibyte] |= bit;
+ pixel[upd->cmap[0].comp] -= comp[upd->cmap[0].comp]->spotsize;
+ }
+
+ FS_DIST(upd->cmap[0].comp)
+/*
+ * Adjust bit & iword, depending on direction
+ */
+ S_FSTEP
+ if(upd->flags & B_YFLIP) data -= dir;
+ else data += dir;
+ }
+/*
+ * Finally call the limits-Routine
+ */
+ if(0 < upd->nlimits) upd_limits(upd,true);
+ return 0;
+}
+
+/* ------------------------------------------------------------------- */
+/* upd_open_fscmy_k: Initialize for CMY_K Printing */
+/* ------------------------------------------------------------------- */
+
+static void
+upd_open_fscmy_k(upd_device *udev)
+{
+ const upd_p upd = udev->upd;
+
+ upd_open_fscomp(udev);
+
+ if((B_RENDER & upd->flags) &&
+ (4 == upd->ncomp)) {
+ upd->render = upd_fscmy_k;
+ } else {
+ upd->flags &= ~B_RENDER;
+ }
+
+}
+
+/* ------------------------------------------------------------------- */
+/* upd_fscmy_k: CMY_K rendering */
+/* ------------------------------------------------------------------- */
+
+static int
+upd_fscmy_k(upd_p upd)
+{
+ const updscan_p scan = upd->scnbuf[upd->yscnbuf & upd->scnmsk];
+ const updcomp_p *comp = (updcomp_p *) upd->valptr;
+ int32_t *const pixel = upd->valbuf;
+ int32_t *const colerr = pixel + upd->ncomp;
+ int32_t *rowerr = colerr + upd->ncomp;
+ int pwidth = upd->rwidth;
+ int dir,ibyte;
+ uint32_t ci;
+ byte bit;
+ bool first = true;
+/*
+ * Erase the component-Data
+ */
+ memset(scan[3].bytes,0,upd->nbytes);
+ memset(scan[2].bytes,0,upd->nbytes);
+ memset(scan[1].bytes,0,upd->nbytes);
+ memset(scan[0].bytes,0,upd->nbytes);
+/*
+ * determine the direction
+ */
+ if(upd->flags & B_REVDIR) { /* This one reverse */
+
+ if(upd->flags & B_YFLIP) {
+ dir = 4;
+ bit = 0x80;
+ ibyte = 0;
+ } else {
+ dir = -4;
+ rowerr += 4 * (pwidth-1);
+ bit = 0x80 >> ((pwidth-1) & 7);
+ ibyte = (pwidth-1) >> 3;
+ }
+
+ if(!(upd->flags & B_FSWHITE)) {
+ upd_pxlfwd(upd);
+ while((0 < pwidth) && !upd_pxlget(upd)) pwidth--;
+ }
+
+ upd_pxlrev(upd);
+
+ } else { /* This one forward */
+
+ if(upd->flags & B_YFLIP) {
+ dir = -4;
+ rowerr += 4 * (pwidth-1);
+ bit = 0x80 >> ((pwidth-1) & 7);
+ ibyte = (pwidth-1) >> 3;
+ } else {
+ dir = 4;
+ bit = 0x80;
+ ibyte = 0;
+ }
+
+ if(!(upd->flags & B_FSWHITE)) {
+ upd_pxlrev(upd);
+ while((0 < pwidth) && !upd_pxlget(upd)) pwidth--;
+ }
+
+ upd_pxlfwd(upd);
+
+ } /* reverse or forward */
+/*
+ * Toggle Direction, if not fixed
+ */
+ if(!(upd->flags & B_FIXDIR)) upd->flags ^= B_REVDIR;
+/*
+ * Skip over leading white-space
+ */
+ if(!(upd->flags & B_FSWHITE)) {
+ upd_proc_pxlget((*fun)) = upd->pxlget;
+ byte *ptr = upd->pxlptr;
+ while((0 < pwidth) && !upd_pxlget(upd)) {
+ pwidth--;
+ fun = upd->pxlget;
+ ptr = upd->pxlptr;
+ S_FSTEP
+ }
+ upd->pxlget = fun;
+ upd->pxlptr = ptr;
+ }
+/*
+ * Process all Pixels
+ */
+ first = true;
+ while(0 < pwidth--) {
+
+/* get the Pixel-Value */
+
+ ci = upd_pxlget(upd);
+
+/* process all components */
+
+ FS_M_ROWERR(0) FS_GOAL(comp[0]->bitmsk & (ci >> comp[0]->bitshf),0)
+ FS_M_ROWERR(1) FS_GOAL(comp[1]->bitmsk & (ci >> comp[1]->bitshf),1)
+ FS_M_ROWERR(2) FS_GOAL(comp[2]->bitmsk & (ci >> comp[2]->bitshf),2)
+ FS_M_ROWERR(3) FS_GOAL(comp[3]->bitmsk & (ci >> comp[3]->bitshf),3)
+
+ if(pixel[0] > comp[0]->threshold) { /* Black fires */
+
+ pixel[0] -= comp[0]->spotsize;
+ scan[0].bytes[ibyte] |= bit;
+
+ } else { /* Colors may fire */
+
+ if((pixel[1] <= comp[1]->threshold) ||
+ (pixel[2] <= comp[2]->threshold) ||
+ (pixel[3] <= comp[3]->threshold) ) { /* Really a Color */
+
+ if(pixel[1] > comp[1]->threshold) {
+ pixel[1] -= comp[1]->spotsize;
+ scan[1].bytes[ibyte] |= bit;
+ }
+
+ if(pixel[2] > comp[2]->threshold) {
+ pixel[2] -= comp[2]->spotsize;
+ scan[2].bytes[ibyte] |= bit;
+ }
+
+ if(pixel[3] > comp[3]->threshold) {
+ pixel[3] -= comp[3]->spotsize;
+ scan[3].bytes[ibyte] |= bit;
+ }
+
+ } else {
+ pixel[1] -= comp[1]->spotsize;
+ pixel[2] -= comp[2]->spotsize;
+ pixel[3] -= comp[3]->spotsize;
+ scan[0].bytes[ibyte] |= bit;
+ }
+ }
+
+ FS_DIST(0)
+ FS_DIST(1)
+ FS_DIST(2)
+ FS_DIST(3)
+
+/*
+ * Adjust rowerr, bit & iword, depending on direction
+ */
+ S_FSTEP
+ }
+/*
+ * Finally call the limits-Routine
+ */
+ if(0 < upd->nlimits) upd_limits(upd,true);
+ return 0;
+}
+
+/* ------------------------------------------------------------------- */
+/* upd_open_writer: Initialize rendering */
+/* ------------------------------------------------------------------- */
+
+static int
+upd_open_writer(upd_device *udev)
+{
+ const upd_p upd = udev->upd;
+ bool success = true;
+
+/** Reset the crucial values */
+ upd->start_writer = NULL;
+ upd->writer = NULL;
+ upd->scnbuf = NULL;
+ upd->nscnbuf = 0;
+ upd->nbytes = 0;
+ upd->nlimits = 0;
+ upd->outbuf = NULL;
+ upd->noutbuf = 0;
+
+/** Rendering should be succesfully initialized */
+ if(B_RENDER != ((B_RENDER | B_ERROR) & upd->flags))
+ success = false;
+
+/** Create number of components */
+ upd->ocomp = upd->ncomp;
+ if(0 < upd->ints[I_OCOMP]) upd->ocomp = upd->ints[I_OCOMP];
+
+/** Massage some Parameters */
+ if(success) {
+
+/* Make sure, that Pass & Pin-Numbers are at least 1 */
+ if(1 > upd->ints[I_NYPASS]) upd->ints[I_NYPASS] = 1;
+ if(1 > upd->ints[I_NXPASS]) upd->ints[I_NXPASS] = 1;
+ if(1 > upd->ints[I_PINS2WRITE]) upd->ints[I_PINS2WRITE] = 1;
+
+ if((upd->ints[I_NXPASS] * upd->ints[I_NYPASS]) > upd->ints[I_NPASS])
+ upd->ints[I_NPASS] = upd->ints[I_NXPASS] * upd->ints[I_NYPASS];
+
+/* Create Default noWeave-Feeds */
+
+ if(upd->ints[I_NPASS] > upd->int_a[IA_STD_DY].size) {
+ int ix,iy,*ip;
+ UPD_MM_DEL_PARAM(udev->memory, upd->int_a[IA_STD_DY]);
+ UPD_MM_GET_ARRAY(udev->memory, ip,upd->ints[I_NPASS]);
+ upd->int_a[IA_STD_DY].data = ip;
+ upd->int_a[IA_STD_DY].size = upd->ints[I_NPASS];
+
+ for(iy = 1; iy < upd->ints[I_NYPASS]; ++iy) {
+ for(ix = 1; ix < upd->ints[I_NXPASS]; ++ix) *ip++ = 0;
+ *ip++ = 1;
+ }
+ for(ix = 1; ix < upd->ints[I_NXPASS]; ++ix) *ip++ = 0;
+ *ip = upd->ints[I_NYPASS] * upd->ints[I_PINS2WRITE]
+ - upd->ints[I_NYPASS] + 1;
+
+ upd->ints[I_BEG_Y] = 0;
+ upd->ints[I_END_Y] = upd->ints[I_PHEIGHT] ?
+ upd->ints[I_PHEIGHT] : upd->gsheight;
+ }
+
+/* Adjust BEG_Y */
+ if(0 >= upd->ints[I_BEG_Y]) {
+ if(0 < upd->int_a[IA_BEG_DY].size) {
+ int i,sum = 0;
+ for(i = 0; i < upd->int_a[IA_BEG_DY].size; ++i)
+ sum += upd->int_a[IA_BEG_DY].data[i];
+ upd->ints[I_BEG_Y] = sum;
+ } else {
+ upd->ints[I_BEG_Y] = 0;
+ }
+ }
+
+/* Adjust END_Y */
+/* Arrgh, I knew, why I refused to provide defaults for crucial */
+/* parameters in uniprint. But o.k. it's nice for size-changing */
+/* PostScript-Code. Nevertheless, it's still not perfect. */
+
+ if(0 >= upd->int_a[IA_ENDTOP].size ||
+ 0 >= upd->int_a[IA_END_DY].size ) upd->ints[I_END_Y] =
+ upd->ints[I_PHEIGHT] ? upd->ints[I_PHEIGHT] : upd->gsheight;
+
+ if(0 >= upd->ints[I_END_Y]) upd->ints[I_END_Y] = upd->ints[I_PHEIGHT] ?
+ upd->ints[I_PHEIGHT] : upd->gsheight;
+
+/* Create Default X-Passes */
+
+ if(0 >= upd->int_a[IA_STD_IX].size) {
+ int ix,i,*ip;
+ UPD_MM_DEL_PARAM(udev->memory, upd->int_a[IA_STD_IX]);
+ UPD_MM_GET_ARRAY(udev->memory, ip,upd->int_a[IA_STD_DY].size);
+ upd->int_a[IA_STD_IX].data = ip;
+ upd->int_a[IA_STD_IX].size = upd->int_a[IA_STD_DY].size;
+
+ for(i = 0, ix = 0; i < upd->int_a[IA_STD_IX].size; ++i) {
+ *ip++ = ix++;
+ if(ix == upd->ints[I_NXPASS]) ix = 0;
+ }
+ }
+
+ if((0 >= upd->int_a[IA_BEG_IX].size) &&
+ (0 < upd->int_a[IA_BEG_DY].size) ) {
+ int ix,i,*ip;
+ UPD_MM_DEL_PARAM(udev->memory, upd->int_a[IA_BEG_IX]);
+ UPD_MM_GET_ARRAY(udev->memory, ip,upd->int_a[IA_BEG_DY].size);
+ upd->int_a[IA_BEG_IX].data = ip;
+ upd->int_a[IA_BEG_IX].size = upd->int_a[IA_BEG_DY].size;
+
+ for(i = 0, ix = 0; i < upd->int_a[IA_BEG_IX].size; ++i) {
+ *ip++ = ix++;
+ if(ix == upd->ints[I_NXPASS]) ix = 0;
+ }
+ }
+
+ if((0 >= upd->int_a[IA_END_IX].size) &&
+ (0 < upd->int_a[IA_END_DY].size) ) {
+ int ix,i,*ip;
+ UPD_MM_DEL_PARAM(udev->memory, upd->int_a[IA_END_IX]);
+ UPD_MM_GET_ARRAY(udev->memory, ip,upd->int_a[IA_END_DY].size);
+ upd->int_a[IA_END_IX].data = ip;
+ upd->int_a[IA_END_IX].size = upd->int_a[IA_END_DY].size;
+
+ for(i = 0, ix = 0; i < upd->int_a[IA_END_IX].size; ++i) {
+ *ip++ = ix++;
+ if(ix == upd->ints[I_NXPASS]) ix = 0;
+ }
+ }
+ }
+
+ if(upd->ints[I_NPASS] > upd->int_a[IA_STD_DY].size) {
+#if UPD_MESSAGES & UPD_M_WARNING
+ errprintf(udev->memory,
+ "upd_open_writer: Only %d instead of %d normal Feeds\n",
+ (int) upd->int_a[IA_STD_DY].size,upd->ints[I_NPASS]);
+#endif
+ success = false;
+
+ } else if(upd->int_a[IA_STD_IX].size < upd->int_a[IA_STD_DY].size) {
+#if UPD_MESSAGES & UPD_M_WARNING
+ errprintf(udev->memory,
+ "upd_open_writer: Only %d instead of %d normal Xstarts\n",
+ (int) upd->int_a[IA_STD_IX].size,
+ (int) upd->int_a[IA_STD_DY].size);
+#endif
+ success = false;
+ }
+
+/** The sum of Values in STD_DY should equal NYPASS * PINS2WRITE (diagnostic) */
+
+#if UPD_MESSAGES & UPD_M_WARNING
+ if(success) {
+ int i,sum = 0;
+ for(i = 0; upd->ints[I_NPASS] > i; ++i)
+ sum += upd->int_a[IA_STD_DY].data[i];
+ if((upd->ints[I_NYPASS]*upd->ints[I_PINS2WRITE]) != sum)
+ errprintf(udev->memory,
+ "upd_open_writer: Sum of normal Feeds is %d rather than %d\n",
+ sum,upd->ints[I_NYPASS]*upd->ints[I_PINS2WRITE]);
+ }
+#endif
+
+ if(upd->int_a[IA_BEG_IX].size < upd->int_a[IA_BEG_DY].size) {
+#if UPD_MESSAGES & UPD_M_WARNING
+ errprintf(udev->memory,
+ "upd_open_writer: Only %d instead of %d initial Xstarts\n",
+ (int) upd->int_a[IA_BEG_IX].size,
+ (int) upd->int_a[IA_BEG_DY].size);
+#endif
+ success = false;
+ }
+
+ if(upd->int_a[IA_BEGBOT].size < upd->int_a[IA_BEG_DY].size) {
+#if UPD_MESSAGES & UPD_M_WARNING
+ errprintf(udev->memory,
+ "upd_open_writer: Only %d instead of %d initial Pins\n",
+ (int) upd->int_a[IA_BEGBOT].size,
+ (int) upd->int_a[IA_BEG_DY].size);
+#endif
+ success = false;
+
+ } else {
+
+ int i;
+ for(i = 0; i < upd->int_a[IA_BEG_DY].size; ++i)
+ if((upd->int_a[IA_BEGBOT].data[i] > upd->ints[I_PINS2WRITE]) ||
+ (upd->int_a[IA_BEGBOT].data[i] < 0 ) ) break;
+
+ if(i < upd->int_a[IA_BEG_DY].size) {
+#if UPD_MESSAGES & UPD_M_WARNING
+ errprintf(udev->memory,
+ "upd_open_writer: Only %d is invalid initial Pins\n",
+ upd->int_a[IA_BEGBOT].data[i]);
+#endif
+ success = false;
+ }
+ }
+
+/** The sum of Values in BEG_DY should equal BEG_Y */
+
+#if UPD_MESSAGES & UPD_M_WARNING
+ if(success) {
+ int i,sum = 0;
+ for(i = 0; upd->int_a[IA_BEG_DY].size > i; ++i)
+ sum += upd->int_a[IA_BEG_DY].data[i];
+ if(upd->ints[I_BEG_Y] != sum)
+ errprintf(udev->memory,
+ "upd_open_writer: Sum of initial Feeds is %d rather than %d\n",
+ sum,upd->ints[I_BEG_Y]);
+ }
+#endif
+
+ if(upd->int_a[IA_END_IX].size < upd->int_a[IA_END_DY].size) {
+#if UPD_MESSAGES & UPD_M_WARNING
+ errprintf(udev->memory,
+ "upd_open_writer: Only %d instead of %d final Xstarts\n",
+ (int) upd->int_a[IA_END_IX].size,
+ (int) upd->int_a[IA_END_DY].size);
+#endif
+ success = false;
+ }
+
+ if(upd->int_a[IA_ENDTOP].size < upd->int_a[IA_END_DY].size) {
+#if UPD_MESSAGES & UPD_M_WARNING
+ errprintf(udev->memory,
+ "upd_open_writer: Only %d instead of %d Final Pins\n",
+ (int) upd->int_a[IA_ENDTOP].size,
+ (int) upd->int_a[IA_END_DY].size);
+#endif
+ success = false;
+
+ } else {
+
+ int i;
+ for(i = 0; i < upd->int_a[IA_END_DY].size; ++i)
+ if((upd->int_a[IA_ENDTOP].data[i] > upd->ints[I_PINS2WRITE]) ||
+ (upd->int_a[IA_ENDTOP].data[i] < 0 ) ) break;
+
+ if(i < upd->int_a[IA_END_DY].size) {
+#if UPD_MESSAGES & UPD_M_WARNING
+ errprintf(udev->memory,
+ "upd_open_writer: Only %d is invalid initial Pins\n",
+ upd->int_a[IA_ENDTOP].data[i]);
+#endif
+ success = false;
+ }
+ }
+
+/** SA_SETCOMP must be valid, if present */
+ if((0 < upd->string_a[SA_SETCOMP].size) &&
+ (upd->ocomp > upd->string_a[SA_SETCOMP].size)) {
+#if UPD_MESSAGES & UPD_M_WARNING
+ errprintf(udev->memory,
+ "upd_open_writer: Only %d SETCOMP-Commands (%d required)\n",
+ (int) upd->string_a[SA_SETCOMP].size,upd->ocomp);
+#endif
+ success = false;
+ }
+
+/** Determine required number of scan-Buffers */
+
+ if(success) { /* Compute nscnbuf */
+ int32_t want,use;
+
+ want = upd->ints[I_NYPASS];
+ want *= upd->ints[I_PINS2WRITE];
+
+ if(upd->ints[I_NSCNBUF] > want) want = upd->ints[I_NSCNBUF];
+
+ if(1 > want) want = 1;
+
+ for(use = 1; 0 < use; use <<= 1) if(use > want) break;
+
+ if(use <= INT_MAX) upd->nscnbuf = upd->ints[I_NSCNBUF] = use;
+ else success = false;
+
+ } /* Compute nscnbuf */
+
+/** Determine number of words in scan-buffers */
+
+ if(success) { /* Compute pwidth, scnmsk, nbytes, pheight */
+
+ if(0 < upd->ints[I_PWIDTH]) upd->pwidth = upd->ints[I_PWIDTH];
+ else upd->pwidth = upd->gswidth;
+
+ upd->nbytes = (upd->pwidth+CHAR_BIT*sizeof(upd->scnbuf[0]->bytes[0]) - 1)
+ / (CHAR_BIT*sizeof(upd->scnbuf[0]->bytes[0]));
+
+ upd->scnmsk = upd->nscnbuf - 1;
+
+ if(0 < upd->ints[I_PHEIGHT]) upd->pheight = upd->ints[I_PHEIGHT];
+ else upd->pheight = upd->gsheight;
+
+ } /* Compute pwidth, scnmsk, nbytes */
+
+/** Call the writer-specific open-function */
+
+ if(success) { /* Determine sizes */
+ switch(upd->choice[C_FORMAT]) {
+ case FMT_RAS:
+ if(0 > upd_open_rascomp(udev)) success = false;
+ break;
+ case FMT_EPSON:
+ if(0 > upd_open_wrtescp(udev)) success = false;
+ break;
+ case FMT_ESCP2Y:
+ case FMT_ESCP2XY:
+ case FMT_ESCNMY: /* (GR) */
+ if(0 > upd_open_wrtescp2(udev)) success = false;
+ break;
+ case FMT_RTL:
+ if(0 > upd_open_wrtrtl(udev)) success = false;
+ break;
+ case FMT_CANON: /* (hr) */
+ if(0 > upd_open_wrtcanon(udev)) success = false;
+ break;
+ default:
+ success = false;
+#if UPD_MESSAGES & UPD_M_WARNING
+ errprintf(udev->memory,"upd_open_writer: Unknown writer-type %d\n",
+ upd->choice[C_FORMAT]);
+#endif
+ break;
+ }
+ } /* Determine sizes*/
+
+/** Allocate the Outputbuffer */
+ if(success && (0 < upd->noutbuf)) { /* Allocate outbuf */
+ upd->outbuf = gs_malloc(udev->memory, upd->noutbuf,sizeof(upd->outbuf[0]),"upd/outbuf");
+ if(!upd->outbuf) success = false;
+ } /* Allocate outbuf */
+
+/** Allocate the desired scan-buffer-pointers */
+ if(success) {
+ upd->scnbuf = gs_malloc(udev->memory, upd->nscnbuf,sizeof(upd->scnbuf[0]),"upd/scnbuf");
+ if(NULL == upd->scnbuf) {
+ success = false;
+ } else {
+ int ibuf;
+ for(ibuf = 0; ibuf < upd->nscnbuf; ++ibuf) {
+ if(success) upd->scnbuf[ibuf] =
+ gs_malloc(udev->memory, upd->ocomp,sizeof(upd->scnbuf[0][0]),"upd/scnbuf[]");
+ else upd->scnbuf[ibuf] = NULL;
+
+ if(!upd->scnbuf[ibuf]) {
+ success = false;
+ } else {
+ int icomp;
+ for(icomp = 0; icomp < upd->ocomp; ++icomp) {
+ if(success) upd->scnbuf[ibuf][icomp].bytes =
+ gs_malloc(udev->memory, upd->nbytes,sizeof(upd->scnbuf[0][0].bytes[0]),
+ "upd/bytes");
+ else upd->scnbuf[ibuf][icomp].bytes = NULL;
+ if(!upd->scnbuf[ibuf][icomp].bytes) success = false;
+
+ if(0 < upd->nlimits) {
+
+ upd->scnbuf[ibuf][icomp].xbegin = gs_malloc(udev->memory, upd->nlimits,
+ sizeof(upd->scnbuf[0][0].xbegin[0]),"upd/xbegin");
+ if(!upd->scnbuf[ibuf][icomp].xbegin) success = false;
+
+ upd->scnbuf[ibuf][icomp].xend = gs_malloc(udev->memory, upd->nlimits,
+ sizeof(upd->scnbuf[0][0].xend[0]),"upd/xend");
+ if(!upd->scnbuf[ibuf][icomp].xbegin) success = false;
+
+ } else {
+
+ upd->scnbuf[ibuf][icomp].xbegin = NULL;
+ upd->scnbuf[ibuf][icomp].xend = NULL;
+
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if(success) upd->flags |= B_FORMAT;
+ else upd_close_writer(udev);
+
+ return success ? 1 : -1;
+}
+
+/* ------------------------------------------------------------------- */
+/* upd_close_writer: Deinitialize rendering */
+/* ------------------------------------------------------------------- */
+
+static void
+upd_close_writer(upd_device *udev)
+{
+ const upd_p upd = udev->upd;
+
+ if(upd) {
+ int ibuf,icomp;
+
+ if((0 < upd->noutbuf) && upd->outbuf)
+ gs_free(udev->memory, upd->outbuf,upd->noutbuf,sizeof(upd->outbuf[0]),"upd/outbuf");
+ upd->noutbuf = 0;
+ upd->outbuf = NULL;
+
+ if((0 < upd->nscnbuf) && upd->scnbuf) {
+ for(ibuf = 0; upd->nscnbuf > ibuf; ++ibuf) {
+
+ if(!upd->scnbuf[ibuf]) continue;
+
+ for(icomp = 0; icomp < upd->ocomp; ++icomp) {
+
+ if((0 < upd->nbytes) && upd->scnbuf[ibuf][icomp].bytes)
+ gs_free(udev->memory, upd->scnbuf[ibuf][icomp].bytes,upd->nbytes,
+ sizeof(upd->scnbuf[ibuf][icomp].words[0]),"upd/bytes");
+ upd->scnbuf[ibuf][icomp].bytes = NULL;
+
+ if((0 < upd->nlimits) && upd->scnbuf[ibuf][icomp].xbegin)
+ gs_free(udev->memory, upd->scnbuf[ibuf][icomp].xbegin,upd->nlimits,
+ sizeof(upd->scnbuf[ibuf][icomp].xbegin[0]),"upd/xbegin");
+ upd->scnbuf[ibuf][icomp].xbegin = NULL;
+
+ if((0 < upd->nlimits) && upd->scnbuf[ibuf][icomp].xend)
+ gs_free(udev->memory, upd->scnbuf[ibuf][icomp].xend,upd->nlimits,
+ sizeof(upd->scnbuf[ibuf][icomp].xend[0]),"upd/xend");
+ upd->scnbuf[ibuf][icomp].xend = NULL;
+ }
+
+ if(icomp)
+ gs_free(udev->memory, upd->scnbuf[ibuf],upd->ocomp,sizeof(upd->scnbuf[0][0]),
+ "upd/scnbuf[]");
+ upd->scnbuf[ibuf] = NULL;
+
+ }
+ gs_free(udev->memory, upd->scnbuf,upd->nscnbuf,sizeof(upd->scnbuf[0]),"upd/scnbuf");
+ }
+
+ upd->flags &= ~B_FORMAT;
+ }
+}
+
+/* ------------------------------------------------------------------- */
+/* upd_limits: Establish passwise limits, after rendering */
+/* ------------------------------------------------------------------- */
+
+static void
+upd_limits(upd_p upd, bool check)
+{
+ updscan_p scans = upd->scnbuf[upd->yscnbuf & upd->scnmsk], scan;
+ int xs,x,xe,icomp,pass;
+ byte *bytes,bit;
+
+ for(icomp = 0; icomp < upd->ocomp; ++icomp) {
+ scan = scans + icomp;
+ for(pass = 0; pass < upd->nlimits; ++pass) {
+ scan->xbegin[pass] = upd->pwidth;
+ scan->xend[ pass] = -1;
+ }
+ }
+
+ if(check) { /* Really check */
+ for(icomp = 0; icomp < upd->ocomp; ++icomp) { /* Check Components */
+ scan = scans + icomp;
+ bytes = scan->bytes;
+
+ for(xs = 0; xs < upd->nbytes && !bytes[xs]; ++xs);
+
+ if(xs < upd->nbytes) { /* Has Data */
+ for(xe = upd->nbytes; xs < xe && !bytes[xe-1]; --xe);
+
+ for(pass = 0; pass < upd->nlimits; ++pass) { /* limit (pass) loop */
+
+ x = ((xs<<3)/upd->nlimits)*upd->nlimits + pass;
+ while((x >> 3) < xs) x += upd->nlimits;
+
+ bit = 0x80 >> (x & 7);
+ while(x < scan->xbegin[pass]) {
+ if(bytes[x>>3] & bit) scan->xbegin[pass] = x;
+ x += upd->nlimits;
+ bit = 0x80 >> (x & 7);
+ }
+
+ x = (((xe<<3)|7)/upd->nlimits)*upd->nlimits + pass;
+
+ while((x >> 3) < xe) x += upd->nlimits;
+ while((x >> 3) > xe) x -= upd->nlimits;
+
+ bit = 0x80 >> (xs & 7);
+ while(x > scan->xend[pass]) {
+ if(bytes[x>>3] & bit) scan->xend[pass] = x;
+ x -= upd->nlimits;
+ bit = 0x80 >> (x & 7);
+ }
+
+ } /* limit (pass) loop */
+
+ } /* Has Data */
+
+ } /* Check Components */
+
+ } /* Really check */
+
+}
+
+/* ------------------------------------------------------------------- */
+/* upd_open_rascomp: ncomp * 1Bit Raster-Writer */
+/* ------------------------------------------------------------------- */
+
+static int
+upd_open_rascomp(upd_device *udev)
+{
+ const upd_p upd = udev->upd;
+ int32_t noutbuf;
+ int error = 0;
+
+ noutbuf = upd->pwidth;
+
+ if(1 < upd->ncomp) noutbuf *= 8; /* ??? upd->ocomp */
+
+ noutbuf = ((noutbuf+15)>>4)<<1;
+
+ if(INT_MAX >= noutbuf) {
+ upd->noutbuf = noutbuf;
+ upd->start_writer = upd_start_rascomp;
+ upd->writer = upd_rascomp;
+ } else {
+ error = -1;
+ }
+
+ return error;
+}
+
+/* ------------------------------------------------------------------- */
+/* upd_start_rascomp: write appropiate raster-header */
+/* ------------------------------------------------------------------- */
+#if arch_is_big_endian
+#define put32(I32,Out) \
+ fwrite(&I32,1,4,Out)
+#else
+#define put32(I32,Out) \
+ putc(((I32)>>24)&255,Out),\
+ putc(((I32)>>16)&255,Out),\
+ putc(((I32)>> 8)&255,Out),\
+ putc( (I32) &255,Out)
+#endif
+
+static int
+upd_start_rascomp(upd_p upd, FILE *out) {
+
+/** if no begin-sequence externally set */
+ if(0 == upd->strings[S_BEGIN].size) {
+ int32_t val;
+
+/** ras_magic */
+ val = 0x59a66a95;
+ put32(val,out);
+
+/** ras_width */
+ val = upd->pwidth;
+ put32(val,out);
+
+/** ras_height */
+ val = upd->pheight;
+ put32(val,out);
+
+/** ras_depth */
+ if(1 < upd->ncomp) val = 8; /* ??? upd->ocomp */
+ else val = 1;
+ put32(val,out);
+
+/** ras_length */
+ val *= upd->pwidth;
+ val = ((val+15)>>4)<<1;
+ val *= upd->pheight;
+ put32(val,out);
+
+/** ras_type */
+ val = 1;
+ put32(val,out);
+
+/** ras_maptype */
+ val = 1;
+ put32(val,out);
+
+/** ras_maplength */
+ val = 3 * (1 << upd->ncomp); /* ??? upd->ocomp */
+ put32(val,out);
+
+/** R,G,B-Map */
+ if(1 == upd->ncomp) { /* ??? upd->ocomp */
+ const updcomp_p comp = upd->valptr[0];
+
+ if(upd->cmap[comp->cmap].rise) {
+ putc((char) 0x00,out); putc((char) 0xff,out);
+ putc((char) 0x00,out); putc((char) 0xff,out);
+ putc((char) 0x00,out); putc((char) 0xff,out);
+ } else {
+ putc((char) 0xff,out); putc((char) 0x00,out);
+ putc((char) 0xff,out); putc((char) 0x00,out);
+ putc((char) 0xff,out); putc((char) 0x00,out);
+ }
+
+ } else if(3 == upd->ncomp) { /* ??? upd->ocomp */
+ int rgb;
+
+ for( rgb = 0; rgb < 3; ++rgb) {
+ int entry;
+ for(entry = 0; entry < 8; ++entry) {
+ byte xval = upd->cmap[rgb].rise ? 0x00 : 0xff;
+ if(entry & (1<<upd->cmap[rgb].comp)) xval ^= 0xff;
+ putc(xval,out);
+ }
+ }
+ } else { /* we have 4 components */
+ int rgb;
+
+ for(rgb = 16; 0 <= rgb; rgb -= 8) {
+ int entry;
+ for(entry = 0; entry < 16; ++entry) {
+ uint32_t rgbval = 0;
+
+ if(entry & (1<<upd->cmap[0].comp)) {
+
+ rgbval = 0xffffff;
+
+ } else {
+
+ if(entry & (1<<upd->cmap[1].comp)) rgbval |= 0xff0000;
+ if(entry & (1<<upd->cmap[2].comp)) rgbval |= 0x00ff00;
+ if(entry & (1<<upd->cmap[3].comp)) rgbval |= 0x0000ff;
+ }
+
+ if(!upd->cmap[1].rise) rgbval ^= 0xff0000;
+ if(!upd->cmap[2].rise) rgbval ^= 0x00ff00;
+ if(!upd->cmap[3].rise) rgbval ^= 0x0000ff;
+
+ if(!(upd->choice[C_MAPPER] == MAP_RGBW)) rgbval ^= 0xffffff;
+
+ putc((rgbval>>rgb)&255,out);
+ }
+ }
+ }
+ }
+ memset(upd->outbuf,0,upd->noutbuf);
+
+ return 0;
+}
+
+/* ------------------------------------------------------------------- */
+/* upd_rascomp: assemble & write a scanline */
+/* ------------------------------------------------------------------- */
+static int
+upd_rascomp(upd_p upd, FILE *out) {
+ updscan_p scan = upd->scnbuf[upd->yscan & upd->scnmsk];
+ uint bits = upd->pwidth;
+
+ if(1 == upd->ncomp) { /* ??? upd->ocomp */
+ uint nbytes;
+
+ nbytes = (bits+7)>>3;
+ memcpy(upd->outbuf,scan->bytes,nbytes);
+ if((bits &= 7)) upd->outbuf[nbytes-1] &= ((byte) 0xff) << (8-bits);
+
+ } else {
+
+ byte *buf = upd->outbuf, bit = 0x80;
+ int ibyte = 0;
+
+ while(0 < bits--) {
+ byte val = 0;
+ switch(upd->ncomp) { /* ??? upd->ocomp */
+ case 4: if(scan[3].bytes[ibyte] & bit) val |= 8;
+ case 3: if(scan[2].bytes[ibyte] & bit) val |= 4;
+ if(scan[1].bytes[ibyte] & bit) val |= 2;
+ case 1: if(scan[0].bytes[ibyte] & bit) val |= 1;
+ }
+ *buf++ = val;
+ if(!(bit >>= 1)) {
+ bit = 0x80;
+ ibyte += 1;
+ }
+ }
+ }
+
+ fwrite(upd->outbuf,1,upd->noutbuf,out);
+ upd->yscan += 1;
+
+ return 0;
+}
+
+/* ------------------------------------------------------------------- */
+/* upd_open_wrtescp: ESC/P Writer intended for ESC * m commands */
+/* ------------------------------------------------------------------- */
+
+static int
+upd_open_wrtescp(upd_device *udev)
+{
+ const upd_p upd = udev->upd;
+ int error = 0;
+
+/** Adjust the PageLength, If Requested */
+ if((B_PAGELENGTH & upd->flags) &&
+ (0 < upd->strings[S_BEGIN].size)) { /* BOP-Checker */
+ int i,state = 0,value = 0;
+ byte *bp = (byte *) upd_cast(upd->strings[S_BEGIN].data);
+ for(i = 0; i < upd->strings[S_BEGIN].size; ++i) {
+ switch(state) {
+ case 0:
+ if(0x1b == bp[i]) state = 1;
+ break;
+ case 1:
+ if('C' == bp[i]) state = 2;
+ else state = 0;
+ break;
+ case 2:
+ if(bp[i]) {
+ value = (int)(0.5 + udev->height * (float) bp[i]
+ / udev->y_pixels_per_inch);
+ if( 0 >= value) bp[i] = 1;
+ else if(128 > value) bp[i] = value;
+ else bp[i] = 127;
+ state = 0;
+ } else {
+ state = 3;
+ }
+ break;
+ case 3:
+ value = (int)(0.5 + udev->height / udev->y_pixels_per_inch);
+ if( 0 >= value) bp[i] = 1;
+ else if( 22 > value) bp[i] = value;
+ else bp[i] = 22;
+ state = 0;
+ break;
+ }
+ }
+ } /* BOP-Checker */
+
+/** Either SETLF or YMOVE must be set */
+ if((0 == upd->strings[S_SETLF].size) &&
+ (0 == upd->strings[S_YMOVE].size) ) {
+#if UPD_MESSAGES & UPD_M_WARNING
+ errprintf(udev->memory,
+ "ESC/P-Open: Either SETLF- or YMOVE-Command must be present\n");
+#endif
+ error = -1;
+ }
+
+/** X-Positioning must be set too */
+ if(((1 < upd->ints[I_XSTEP] ) &&
+ (0 == upd->strings[S_XSTEP].size) ) ||
+ ((1 < upd->ints[I_NXPASS] ) &&
+ (0 == upd->strings[S_XMOVE].size) &&
+ (0 == upd->strings[S_XSTEP].size) ) ) {
+#if UPD_MESSAGES & UPD_M_WARNING
+ errprintf(udev->memory,
+ "ESC/P-Open: Missing XSTEP- and/or XMOVE-Command\n");
+#endif
+ error = -1;
+ }
+
+/** SA_WRITECOMP must be valid */
+ if(upd->ncomp > upd->string_a[SA_WRITECOMP].size) { /* ??? upd->ocomp */
+#if UPD_MESSAGES & UPD_M_WARNING
+ errprintf(udev->memory,
+ "ESC/P-Open: WRITECOMP-Commands must be given\n");
+#endif
+ error = -1;
+ }
+
+/**
+If all this is correct, it's time to coumput the size of the output-buffer.
+It must hold:
+ 1. Y-Positioning
+ 2. X-Positioning
+ 3. Component-Selection
+ 4. The Raster-Command
+ 5. The Data
+*/
+ if(0 <= error) {
+ int32_t i,noutbuf,need;
+
+ if(0 < upd->strings[S_YMOVE].size) {
+ noutbuf = upd->strings[S_YMOVE].size + 2;
+ } else {
+ int nmax = upd->pheight;
+ if( 1 < upd->ints[I_YSTEP]) nmax /= upd->ints[I_YSTEP];
+ else if(-1 > upd->ints[I_YSTEP]) nmax *= -upd->ints[I_YSTEP];
+ noutbuf = 2 * upd->strings[S_SETLF].size + 2;
+ noutbuf += nmax/255 + 1;
+ }
+
+ if(1 < upd->ints[I_YSTEP])
+ noutbuf += (upd->ints[I_YSTEP]-1) * upd->strings[S_YSTEP].size;
+
+ noutbuf += upd->strings[S_XMOVE].size + 2;
+
+ if(1 < upd->ints[I_XSTEP])
+ noutbuf += (upd->ints[I_XSTEP]-1) * upd->strings[S_XSTEP].size;
+
+ if(0 < upd->string_a[SA_SETCOMP].size) {
+ need = 0;
+ for(i = 0; i < upd->ocomp; ++i)
+ if(need < upd->string_a[SA_SETCOMP].data[i].size)
+ need = upd->string_a[SA_SETCOMP].data[i].size;
+ noutbuf += need;
+ }
+
+ need = 0;
+ for(i = 0; i < upd->ocomp; ++i)
+ if(need < upd->string_a[SA_WRITECOMP].data[i].size)
+ need = upd->string_a[SA_WRITECOMP].data[i].size;
+ noutbuf += need + 2;
+
+ noutbuf += ((upd->ints[I_PINS2WRITE] + 7) / 8)
+ * ((upd->pwidth + upd->ints[I_NXPASS] - 1)/upd->ints[I_NXPASS]);
+
+ if((0 < noutbuf) && (noutbuf <= INT_MAX)) {
+ upd->noutbuf = noutbuf;
+ upd->writer = upd_wrtescp;
+ upd->nlimits = upd->ints[I_NXPASS];
+ error = 1;
+ } else {
+ error = -1;
+#if UPD_MESSAGES & UPD_M_WARNING
+ errprintf(udev->memory,
+ "ESC/P-Open: %ld is unreasonable size of Outputbuffer\n",
+ (long) noutbuf);
+#endif
+ }
+ }
+
+ return error;
+}
+
+/* ------------------------------------------------------------------- */
+/* upd_wrtescp: Write a pass */
+/* ------------------------------------------------------------------- */
+
+static int
+upd_wrtescp(upd_p upd, FILE *out)
+{
+ int pinbot,pin,pintop,xbegin,x,xend,icomp,ybegin,yend,y,ioutbuf,n,ixpass;
+ byte *obytes,bit;
+ updscan_p scan;
+
+/** Determine the number of pins to write */
+
+ if(upd->yscan < upd->ints[I_BEG_Y]) {
+ ixpass = upd->int_a[IA_BEG_IX].data[upd->ipass];
+ pintop = 0;
+ pinbot = upd->int_a[IA_BEGBOT].data[upd->ipass];
+ } else if(upd->yscan >= upd->ints[I_END_Y]) {
+ ixpass = upd->int_a[IA_END_IX].data[upd->ipass];
+ pinbot = upd->ints[I_PINS2WRITE];
+ pintop = pinbot - upd->int_a[IA_ENDTOP].data[upd->ipass];
+ } else {
+ ixpass = upd->int_a[IA_STD_IX].data[upd->ipass];
+ pintop = 0;
+ pinbot = upd->ints[I_PINS2WRITE];
+ }
+
+ ybegin = pintop * upd->ints[I_NYPASS] + upd->yscan - upd->ints[I_BEGSKIP];
+ yend = pinbot * upd->ints[I_NYPASS] + upd->yscan - upd->ints[I_BEGSKIP];
+
+/** Determine Width of this scan */
+
+ xbegin = upd->pwidth;
+ xend = -1;
+
+ for(y = ybegin; y < yend; y += upd->ints[I_NYPASS]) { /* Pin-testloop */
+
+ if(0 > y) continue; /* Inserted Scanlines */
+
+ scan = upd->scnbuf[y & upd->scnmsk];
+
+ for(icomp = 0; icomp < upd->ocomp; ++icomp) { /* Compwise test */
+ if(xbegin > scan[icomp].xbegin[ixpass])
+ xbegin = scan[icomp].xbegin[ixpass];
+ if(xend < scan[icomp].xend[ ixpass])
+ xend = scan[icomp].xend[ ixpass];
+ } /* Compwise test */
+
+ } /* Pin-testloop */
+
+ if(xbegin <= xend) { /* Some data to write */
+
+ ioutbuf = 0;
+
+ if(0 == upd->strings[S_XMOVE].size) xbegin = ixpass;
+
+/*
+ * Adjust the Printers Y-Position
+ */
+ if(upd->yscan != upd->yprinter) { /* Adjust Y-Position */
+ if(B_YABS & upd->flags) y = upd->yscan + upd->ints[I_YOFS];
+ else y = upd->yscan - upd->yprinter;
+
+ if( 1 < upd->ints[I_YSTEP]) {
+ n = y / upd->ints[I_YSTEP]; /* Major-Steps */
+ y -= n * upd->ints[I_YSTEP]; /* Minor-Steps */
+ } else if(-1 > upd->ints[I_YSTEP]) {
+ n = y * -upd->ints[I_YSTEP]; /* May this work? */
+ y = 0;
+ } else {
+ n = y;
+ y = 0;
+ }
+
+ if(n) { /* Coarse Positioning */
+ if(0 < upd->strings[S_YMOVE].size) {
+
+ memcpy(upd->outbuf+ioutbuf,
+ upd->strings[S_YMOVE].data,
+ upd->strings[S_YMOVE].size);
+ ioutbuf += upd->strings[S_YMOVE].size;
+
+ upd->outbuf[ioutbuf++] = n & 0xff;
+ upd->outbuf[ioutbuf++] = (n>>8) & 0xff;
+
+ } else {
+
+ while(n) {
+ int n2do = n > 255 ? 255 : n;
+ if(upd->lf != n2do) {
+ memcpy(upd->outbuf+ioutbuf,
+ upd->strings[S_SETLF].data,
+ upd->strings[S_SETLF].size);
+ ioutbuf += upd->strings[S_SETLF].size;
+ upd->outbuf[ioutbuf++] = n2do;
+ upd->lf = n2do;
+ }
+ upd->outbuf[ioutbuf++] = '\n';
+ n -= n2do;
+ }
+ }
+ } /* Coarse Positioning */
+
+ if(0 < upd->strings[S_YSTEP].size) {
+ while(y--) {
+ memcpy(upd->outbuf+ioutbuf,
+ upd->strings[S_YSTEP].data,
+ upd->strings[S_YSTEP].size);
+ ioutbuf += upd->strings[S_YSTEP].size;
+ }
+ }
+
+ upd->yprinter = upd->yscan;
+ } /* Adjust Y-Position */
+
+/*
+ * Now write the required components
+ */
+ for(icomp = 0; icomp < upd->ocomp; ++icomp) { /* Component-Print */
+/*
+ * First check, wether this Component needs printing
+ */
+ for(y = ybegin; y < yend; y += upd->ints[I_NYPASS]) { /* Comp-Test */
+ if(0 > y) continue;
+ scan = upd->scnbuf[y & upd->scnmsk]+icomp;
+ if(0 <= scan->xend[ixpass]) break;
+ } /* Comp-Test */
+ if(y >= yend) continue; /* Component not required */
+/*
+ * Select the Component
+ */
+ if((0 < upd->string_a[SA_SETCOMP].size) &&
+ (upd->icomp != icomp ) ) { /* Selection enabled */
+ upd->icomp = icomp;
+ if(0 < upd->string_a[SA_SETCOMP].data[icomp].size) {
+ memcpy(upd->outbuf+ioutbuf,
+ upd->string_a[SA_SETCOMP].data[icomp].data,
+ upd->string_a[SA_SETCOMP].data[icomp].size);
+ ioutbuf += upd->string_a[SA_SETCOMP].data[icomp].size;
+ }
+ } /* Selection enabled */
+/*
+ * Establish the X-Position
+ */
+ if(xbegin != upd->xprinter) {
+
+ if(0 == upd->strings[S_XMOVE].size) {
+
+ upd->outbuf[ioutbuf++] = '\r';
+ upd->xprinter = 0;
+ n = 0;
+ x = ixpass;
+
+ } else {
+
+ if(B_XABS & upd->flags) n = x = xbegin + upd->ints[I_XOFS];
+ else n = x = xbegin - upd->xprinter;
+
+ if( 1 < upd->ints[I_XSTEP]) {
+ if(0 > n) {
+ n -= upd->ints[I_XSTEP];
+ x -= n;
+ }
+ if(n) n /= upd->ints[I_XSTEP]; /* Major-Steps */
+ if(x) x %= upd->ints[I_XSTEP]; /* Minor-Steps */
+
+ } else if(-1 > upd->ints[I_XSTEP]) {
+ n *= -upd->ints[I_XSTEP]; /* May this work? */
+ x = 0;
+ }
+
+ if(n) { /* Adjust X-Position */
+
+ memcpy(upd->outbuf+ioutbuf,
+ upd->strings[S_XMOVE].data,
+ upd->strings[S_XMOVE].size);
+ ioutbuf += upd->strings[S_XMOVE].size;
+
+ upd->outbuf[ioutbuf++] = n & 0xff;
+ upd->outbuf[ioutbuf++] = (n>>8) & 0xff;
+
+ } /* Adjust X-Position */
+
+ }
+
+ if(x && 0 < upd->strings[S_XSTEP].size) { /* Fine-Adjust X */
+ while(x--) {
+ memcpy(upd->outbuf+ioutbuf,
+ upd->strings[S_XSTEP].data,
+ upd->strings[S_XSTEP].size);
+ ioutbuf += upd->strings[S_XSTEP].size;
+ }
+ } /* Fine-Adjust X */
+ }
+ upd->xprinter = xend+1;
+/*
+ * Send the Write-Command
+ */
+ if(0 < upd->string_a[SA_WRITECOMP].data[icomp].size) {
+ memcpy(upd->outbuf+ioutbuf,
+ upd->string_a[SA_WRITECOMP].data[icomp].data,
+ upd->string_a[SA_WRITECOMP].data[icomp].size);
+ ioutbuf += upd->string_a[SA_WRITECOMP].data[icomp].size;
+ }
+ n = (xend - xbegin) / upd->ints[I_NXPASS] + 1;;
+ upd->outbuf[ioutbuf++] = n & 255;
+ upd->outbuf[ioutbuf++] = (n>>8) & 255;
+/*
+ * Clear the data-Part
+ */
+ obytes = upd->outbuf+ioutbuf;
+ n *= (upd->ints[I_PINS2WRITE]+7)>>3;
+ memset(obytes,0,n);
+ ioutbuf += n;
+/*
+ * Set the Pixels
+ */
+ for(x = xbegin; x <= xend; x += upd->ints[I_NXPASS]) {
+
+ bit = 0x80 >> (pintop & 7);
+ obytes += pintop>>3;
+
+ for(pin = pintop, y = ybegin; pin < pinbot;
+ pin++, y += upd->ints[I_NYPASS]) {
+ if(0 <= y) {
+ scan = upd->scnbuf[y & upd->scnmsk]+icomp;
+ if(scan->bytes[x>>3] & (0x80 >> (x & 7))) *obytes |= bit;
+ }
+ if(!(bit >>= 1)) { obytes++; bit = 0x80; }
+ }
+
+ obytes += (upd->ints[I_PINS2WRITE]-pinbot+7)>>3;
+ }
+/*
+ * Send this Component to the Printer
+ */
+ fwrite(upd->outbuf,1,ioutbuf,out);
+ ioutbuf = 0;
+ } /* Component-Print */
+ } /* Some data to write */
+
+/** Advance counters in upd, change modi */
+
+ if(upd->yscan < upd->ints[I_BEG_Y]) {
+ upd->yscan += upd->int_a[IA_BEG_DY].data[upd->ipass++];
+ if( upd->ints[I_BEG_Y] <= upd->yscan) upd->ipass = 0;
+ else if(upd->int_a[IA_BEG_DY].size <= upd->ipass) upd->ipass = 0;
+ } else if(upd->yscan >= upd->ints[I_END_Y]) {
+ upd->yscan += upd->int_a[IA_END_DY].data[upd->ipass++];
+ if(upd->int_a[IA_END_DY].size <= upd->ipass) upd->ipass = 0;
+ } else {
+ upd->yscan += upd->int_a[IA_STD_DY].data[upd->ipass++];
+ if(upd->int_a[IA_STD_DY].size <= upd->ipass) upd->ipass = 0;
+ if(upd->yscan >= upd->ints[I_END_Y]) upd->ipass = 0;
+ }
+
+ return 0;
+}
+
+/* ------------------------------------------------------------------- */
+/* upd_open_wrtescp2: ESC/P2 Writer intended for ESC . 1 commands */
+/* ------------------------------------------------------------------- */
+
+static int
+upd_open_wrtescp2(upd_device *udev)
+{
+ const upd_p upd = udev->upd;
+ int error = 0;
+ float pixels_per_inch = 360.0;
+
+/** Analyze (and optionally adjust) the BOP-Sequence */
+ if(0 < upd->strings[S_BEGIN].size) { /* BOP-Checker */
+ int i,state = 0,value = 0;
+ byte *bp = (byte *) upd_cast(upd->strings[S_BEGIN].data);
+ for(i = 0; i < upd->strings[S_BEGIN].size; ++i) {
+ switch(state) {
+ case 0:
+ if(0x1b == bp[i]) state = 1;
+ break;
+ case 1:
+ if('(' == bp[i]) state = 2;
+ else state = 0;
+ break;
+ case 2:
+ switch(bp[i]) {
+ case 'U': state = 3; break; /* Printer-Resolution */
+ case 'C': state = 6; break; /* Page-Length */
+ case 'c': state = 10; break; /* Top/Bottom Margin */
+ default: state = 0; break;
+ }
+ break;
+ case 3:
+ if(1 == bp[i]) state = 4;
+ else state = 0;
+ break;
+ case 4:
+ if(0 == bp[i]) state = 5;
+ else state = 0;
+ break;
+ case 5:
+ pixels_per_inch = 3600.0 / (float) bp[i];
+ state = 0;
+ break;
+ case 6:
+ if(2 == bp[i]) state = 7;
+ else state = 0;
+ break;
+ case 7:
+ if(0 == bp[i]) state = 8;
+ else state = 0;
+ break;
+ case 8:
+ if(B_PAGELENGTH & upd->flags) {
+ value = (int)(0.5 + udev->height
+ * pixels_per_inch / udev->y_pixels_per_inch);
+ bp[i] = value & 0xff;
+ }
+ state = 9;
+ break;
+ case 9:
+ if(B_PAGELENGTH & upd->flags) {
+ bp[i] = (value>>8) & 0xff;
+ }
+ state = 0;
+ break;
+ case 10:
+ if(4 == bp[i]) state = 11;
+ else state = 0;
+ break;
+ case 11:
+ if(0 == bp[i]) state = 12;
+ else state = 0;
+ break;
+ case 12:
+ if(B_TOPMARGIN & upd->flags) {
+ value = (int)(dev_t_margin(udev) * pixels_per_inch);
+ bp[i] = value & 0xff;
+ }
+ state = 13;
+ break;
+ case 13:
+ if(B_TOPMARGIN & upd->flags) {
+ bp[i] = (value>>8) & 0xff;
+ }
+ state = 14;
+ break;
+ case 14:
+ if(B_BOTTOMMARGIN & upd->flags) {
+ value = (int)(0.5 + udev->height
+ * pixels_per_inch / udev->y_pixels_per_inch
+ - dev_b_margin(udev) * pixels_per_inch);
+ bp[i] = value & 0xff;
+ }
+ state = 15;
+ break;
+ case 15:
+ if(B_BOTTOMMARGIN & upd->flags) {
+ bp[i] = (value>>8) & 0xff;
+ }
+ state = 0;
+ break;
+ }
+ }
+ } /* BOP-Checker */
+
+/** Create Y-Move-Command, if not given */
+ if(0 == upd->strings[S_YMOVE].size) {
+ byte *bp;
+ UPD_MM_DEL_PARAM(udev->memory, upd->strings[S_YMOVE]);
+ UPD_MM_GET_ARRAY(udev->memory, bp,5);
+ upd->strings[S_YMOVE].data = bp;
+ upd->strings[S_YMOVE].size = 5;
+ *bp++ = 0x1b; /* ESC */
+ *bp++ = '(';
+ *bp++ = upd->flags & B_YABS ? 'V' : 'v';
+ *bp++ = 2;
+ *bp++ = 0;
+ }
+
+/** X-Positioning must be set too, sometimes */
+ if((1 < upd->ints[I_XSTEP]) && (0 == upd->strings[S_XSTEP].size)) {
+
+#if UPD_MESSAGES & UPD_M_WARNING
+ errprintf(udev->memory,
+ "ESC/P2-Open: XSTEP-Command required for XSTEP=%d\n",
+ upd->ints[I_XSTEP]);
+#endif
+ error = -1;
+
+ } else if((1 < upd->ints[I_NXPASS] ) &&
+ (0 == upd->strings[S_XMOVE].size) &&
+ (0 == upd->strings[S_XSTEP].size) ) {
+ byte *bp;
+ int ratio;
+
+ ratio = (int)((udev->y_pixels_per_inch + 0.5) / udev->x_pixels_per_inch);
+
+ if(0 == upd->ints[I_XSTEP]) { /* Adjust scale-factor too! */
+ if(ratio > 1) upd->ints[I_XSTEP] = -ratio;
+ } else { /* Adjust scale-factor too! */
+ ratio = -upd->ints[I_XSTEP];
+ }
+
+ if(2 == upd->ints[I_NXPASS]) { /* Use a relative Step */
+
+ UPD_MM_DEL_PARAM(udev->memory, upd->strings[S_XSTEP]);
+ UPD_MM_GET_ARRAY(udev->memory, bp,4);
+ upd->strings[S_XSTEP].size = 4;
+ upd->strings[S_XSTEP].data = bp;
+ *bp++ = 0x1b;
+ *bp++ = '\\';
+ *bp++ = ratio & 0xff;
+ *bp++ = (ratio>>8) & 0xff;
+
+ } else { /* Use relative or absolute Move */
+
+ UPD_MM_DEL_PARAM(udev->memory, upd->strings[S_XMOVE]);
+ UPD_MM_GET_ARRAY(udev->memory, bp,2);
+ upd->strings[S_XMOVE].size = 2;
+ upd->strings[S_XMOVE].data = bp;
+ *bp++ = 0x1b;
+ *bp++ = upd->flags & B_XABS ? '$' : '\\';
+
+ }
+ }
+
+ /* Check the Nozzle Map parameters and set some defaults */
+ /* Used a switch construct in case FMT_ESCNMXY is added later */
+ switch(upd->choice[C_FORMAT]){
+ case FMT_ESCNMY:
+ /* RowsPerPass */
+ if( 0 == upd->ints[I_ROWS] ){
+ upd->ints[I_ROWS] = 1;
+ }
+ /* PatternRepeat */
+ if( 0 == upd->ints[I_PATRPT] ){
+ upd->ints[I_PATRPT] = 1;
+ }
+ /* RowMask - default is all 1's */
+ if( upd->ints[I_PATRPT] != upd->int_a[IA_ROWMASK].size ) {
+ int i, *bp;
+ UPD_MM_DEL_PARAM(udev->memory, upd->int_a[IA_ROWMASK]);
+ UPD_MM_GET_ARRAY(udev->memory, bp,upd->ints[I_PATRPT]);
+ upd->int_a[IA_ROWMASK].size = upd->ints[I_PATRPT];
+ upd->int_a[IA_ROWMASK].data = bp;
+ for (i = 0 ; i < upd->ints[I_PATRPT] ; i++){
+ *bp++ = 1; /* black */
+ }
+ }
+ /* MaskScanOffset - default is 0-patternRepeat */
+ if( upd->ints[I_PATRPT] != upd->int_a[IA_SCNOFS].size ) {
+ int i, *bp;
+ UPD_MM_DEL_PARAM(udev->memory, upd->int_a[IA_SCNOFS]);
+ UPD_MM_GET_ARRAY(udev->memory, bp,upd->ints[I_PATRPT]);
+ upd->int_a[IA_SCNOFS].size = upd->ints[I_PATRPT];
+ upd->int_a[IA_SCNOFS].data = bp;
+ for (i = 0 ; i < upd->ints[I_PATRPT] ; i++){
+ *bp++ = i;
+ }
+ }
+ break;
+ case FMT_ESCP2Y:
+ case FMT_ESCP2XY:
+ /* Nozzle map parameters are not valid for these formats
+ so ignore them*/
+ break;
+ }
+
+/** If there is neither a writecomp nor a setcomp-command, generate both */
+ if((0 == upd->string_a[SA_WRITECOMP].size) &&
+ (0 == upd->string_a[SA_SETCOMP].size ) ) { /* Default-commands */
+ byte *bp;
+ gs_param_string *ap;
+ int i;
+
+ if(4 == upd->ocomp) { /* Establish Component-Selection */
+ UPD_MM_DEL_APARAM(udev->memory, upd->string_a[SA_SETCOMP]);
+ UPD_MM_GET_ARRAY(udev->memory, ap,4);
+ upd->string_a[SA_SETCOMP].data = ap;
+ upd->string_a[SA_SETCOMP].size = 4;
+ for(i = 0; i < 4; ++i) {
+ UPD_MM_GET_ARRAY(udev->memory, bp,3);
+ ap[i].size = 3;
+ ap[i].data = bp;
+ *bp++ = 0x1b;
+ *bp++ = 'r';
+ switch(((updcomp_p)upd->valptr[i])->cmap) { /* use COMPORDER! */
+ case 0: *bp++ = 0; break; /* Black */
+ case 1: *bp++ = 2; break; /* Cyan */
+ case 2: *bp++ = 1; break; /* Magenta */
+ case 3: *bp++ = 4; break; /* Yellow */
+ } /* use COMPORDER! */
+ }
+ } /* Establish Component-Selection */
+
+ UPD_MM_DEL_APARAM(udev->memory, upd->string_a[SA_WRITECOMP]);
+ UPD_MM_GET_ARRAY(udev->memory, ap,upd->ocomp);
+ upd->string_a[SA_WRITECOMP].data = ap;
+ upd->string_a[SA_WRITECOMP].size = upd->ncomp;
+ for(i = 0; i < upd->ocomp; ++i) {
+ UPD_MM_GET_ARRAY(udev->memory, bp,6);
+ ap[i].size = 6;
+ ap[i].data = bp;
+ *bp++ = 0x1b;
+ *bp++ = '.';
+ *bp++ = 1; /* RLE */
+ switch(upd->choice[C_FORMAT]){
+ case FMT_ESCP2Y:
+ case FMT_ESCP2XY:
+ *bp++ = (byte)(3600.0 * upd->ints[I_NYPASS] /
+ udev->y_pixels_per_inch + 0.5);
+ *bp++ = (byte)(3600.0 * upd->ints[I_NXPASS] /
+ udev->x_pixels_per_inch + 0.5);
+ *bp++ = upd->ints[I_PINS2WRITE];
+ break;
+ case FMT_ESCNMY:
+ /*
+ *bp++ = 3600.0 / udev->y_pixels_per_inch + 0.5;
+ *bp++ = 3600.0 / udev->x_pixels_per_inch + 0.5;
+ */
+ *bp++ = 10; /* needs to always be this for esc300 */
+ *bp++ = 10;
+ *bp++ = upd->ints[I_ROWS];
+ break;
+ }
+ }
+ } /* Default-commands */
+
+/** SA_WRITECOMP must be valid */
+ if(upd->ocomp > upd->string_a[SA_WRITECOMP].size) {
+#if UPD_MESSAGES & UPD_M_WARNING
+ errprintf(udev->memory,
+ "ESC/P2-Open: WRITECOMP-Commands must be given\n");
+#endif
+ error = -1;
+ }
+
+/** Check Validity of X-Pass */
+ switch(upd->choice[C_FORMAT]) {
+ case FMT_ESCP2Y:
+ if(1 < upd->ints[I_NXPASS]) {
+#if UPD_MESSAGES & UPD_M_WARNING
+ errprintf(udev->memory,
+ "ESC/P2-Open: FMT_ESCP2Y cannot handle multiple X-Passes\n");
+#endif
+ error = -1;
+ } else {
+ upd->writer = upd_wrtescp2;
+ }
+ break;
+ case FMT_ESCP2XY:
+ upd->writer = upd_wrtescp2x;
+ upd->nlimits = upd->ints[I_NXPASS];
+#if UPD_MESSAGES & UPD_M_WARNING
+ if(1 == upd->ints[I_NXPASS])
+ errprintf(udev->memory,
+ "ESC/P2-Open: FMT_ESCP2XY should not be used with 1X-Pass\n");
+#endif
+ break;
+ case FMT_ESCNMY:
+ if(1 < upd->ints[I_NXPASS]) {
+#if UPD_MESSAGES & UPD_M_WARNING
+ errprintf(udev->memory,
+ "ESC/P2-Open: FMT_ESCNMY cannot handle multiple X-Passes\n");
+#endif
+ error = -1;
+ } else {
+ upd->writer = upd_wrtescnm;
+ }
+ break;
+ default:
+#if UPD_MESSAGES & UPD_M_WARNING
+ errprintf(udev->memory,
+ "ESC/P2-Open: %d is not a ESC/P2-Format\n",
+ upd->choice[C_FORMAT]);
+#endif
+ error = - 1;
+ break;
+ }
+
+/**
+If all this is correct, it's time to compute the size of the output-buffer.
+It must hold:
+ 1. Y-Positioning
+ 2. X-Positioning
+ 3. Component-Selection
+ 4. The Raster-Command
+ 5. The Data
+*/
+ if(0 <= error) {
+ int32_t i,noutbuf,need;
+ /* Y-Positioning */
+ if(0 < upd->strings[S_YMOVE].size) {
+ noutbuf = upd->strings[S_YMOVE].size + 2;
+ } else {
+ int nmax = upd->pheight;
+ if( 1 < upd->ints[I_YSTEP]) nmax /= upd->ints[I_YSTEP];
+ else if(-1 > upd->ints[I_YSTEP]) nmax *= -upd->ints[I_YSTEP];
+ noutbuf = 2 * upd->strings[S_SETLF].size + 2;
+ noutbuf += nmax/255 + 1;
+ }
+
+ if(1 < upd->ints[I_YSTEP])
+ noutbuf += (upd->ints[I_YSTEP]-1) * upd->strings[S_YSTEP].size;
+
+ /* X-Positioning */
+ if(0 == upd->strings[S_XMOVE].size) {
+ noutbuf += 1; /* The CR */
+ noutbuf += (upd->ints[I_NXPASS]-1) * upd->strings[S_XSTEP].size;
+ } else {
+ noutbuf += upd->strings[S_XMOVE].size + 2;
+
+ if(1 < upd->ints[I_XSTEP])
+ noutbuf += (upd->ints[I_XSTEP]-1) * upd->strings[S_XSTEP].size;
+ }
+
+ /* Component-Selection */
+ if(0 < upd->string_a[SA_SETCOMP].size) {
+ need = 0;
+ for(i = 0; i < upd->ocomp; ++i)
+ if(need < upd->string_a[SA_SETCOMP].data[i].size)
+ need = upd->string_a[SA_SETCOMP].data[i].size;
+ noutbuf += need;
+ }
+
+ /* The Raster-Command */
+ need = 0;
+ for(i = 0; i < upd->ocomp; ++i)
+ if(need < upd->string_a[SA_WRITECOMP].data[i].size)
+ need = upd->string_a[SA_WRITECOMP].data[i].size;
+ noutbuf += need + 2;
+
+ /* The Data */
+ noutbuf += 2*upd->nbytes + (upd->nbytes + 127) / 128;
+
+ upd->noutbuf = noutbuf;
+ error = 1;
+ }
+
+ return error;
+}
+
+/* ------------------------------------------------------------------- */
+/* upd_wrtescp2: Write a pass */
+/* ------------------------------------------------------------------- */
+
+static int
+upd_wrtescp2(upd_p upd, FILE *out)
+{
+ int pinbot,pin,pintop,xbegin,x,xend,icomp,ybegin,yend,y,ioutbuf,n;
+ byte *obytes;
+ updscan_p scan;
+
+/** Determine the number of pins to write */
+
+ if(upd->yscan < upd->ints[I_BEG_Y]) {
+ pintop = 0;
+ pinbot = upd->int_a[IA_BEGBOT].data[upd->ipass];
+ } else if(upd->yscan >= upd->ints[I_END_Y]) {
+ pinbot = upd->ints[I_PINS2WRITE];
+ pintop = pinbot - upd->int_a[IA_ENDTOP].data[upd->ipass];
+ } else {
+ pintop = 0;
+ pinbot = upd->ints[I_PINS2WRITE];
+ }
+
+ ybegin = pintop * upd->ints[I_NYPASS] + upd->yscan - upd->ints[I_BEGSKIP];
+ yend = pinbot * upd->ints[I_NYPASS] + upd->yscan - upd->ints[I_BEGSKIP];
+
+/** Determine Width of this scan */
+
+ xbegin = upd->nbytes;
+ xend = -1;
+
+ for(y = ybegin; y < yend; y += upd->ints[I_NYPASS]) { /* Pin-testloop */
+
+ if(0 > y) continue; /* Inserted Scanlines */
+
+ scan = upd->scnbuf[y & upd->scnmsk];
+
+ for(icomp = 0; icomp < upd->ocomp; ++icomp) { /* Compwise test */
+ obytes = scan[icomp].bytes;
+
+ for(x = 0; x < xbegin && !obytes[x]; x++);
+ if(x < xbegin) xbegin = x;
+
+ if(x < upd->nbytes) {
+ for(x = upd->nbytes-1; x > xend && !obytes[x]; x--);
+ if(x > xend) xend = x;
+ }
+ } /* Compwise test */
+
+ } /* Pin-testloop */
+
+ if(xbegin <= xend) { /* Some data to write */
+
+ ioutbuf = 0;
+
+ if(0 == upd->strings[S_XMOVE].size) xbegin = 0;
+
+/*
+ * Adjust the Printers Y-Position
+ */
+ if(upd->yscan != upd->yprinter) { /* Adjust Y-Position */
+ if(B_YABS & upd->flags) y = upd->yscan + upd->ints[I_YOFS];
+ else y = upd->yscan - upd->yprinter;
+
+ if( 1 < upd->ints[I_YSTEP]) {
+ n = y / upd->ints[I_YSTEP]; /* Major-Steps */
+ y -= n * upd->ints[I_YSTEP]; /* Minor-Steps */
+ } else if(-1 > upd->ints[I_YSTEP]) {
+ n = y * -upd->ints[I_YSTEP]; /* May this work? */
+ y = 0;
+ } else {
+ n = y;
+ y = 0;
+ }
+
+ if(n) { /* Coarse Positioning */
+ memcpy(upd->outbuf+ioutbuf,
+ upd->strings[S_YMOVE].data,upd->strings[S_YMOVE].size);
+ ioutbuf += upd->strings[S_YMOVE].size;
+
+ upd->outbuf[ioutbuf++] = n & 0xff;
+ upd->outbuf[ioutbuf++] = (n>>8) & 0xff;
+
+ } /* Coarse Positioning */
+
+ if(0 < upd->strings[S_YSTEP].size) {
+ while(y--) {
+ memcpy(upd->outbuf+ioutbuf,
+ upd->strings[S_YSTEP].data,
+ upd->strings[S_YSTEP].size);
+ ioutbuf += upd->strings[S_YSTEP].size;
+ }
+ }
+
+ upd->yprinter = upd->yscan;
+ } /* Adjust Y-Position */
+/*
+ * Now write the required components
+ */
+ for(icomp = 0; icomp < upd->ocomp; ++icomp) { /* Component-Print */
+/*
+ * First check, wether this Component needs printing
+ */
+ for(y = ybegin; y < yend; y += upd->ints[I_NYPASS]) { /* Comp-Test */
+ if(0 > y) continue;
+ obytes = upd->scnbuf[y & upd->scnmsk][icomp].bytes;
+ for(x = xbegin; x <= xend && !obytes[x]; ++x);
+ if( x <= xend) break;
+ } /* Comp-Test */
+ if(y >= yend) continue; /* Component not required */
+/*
+ * Select the Component
+ */
+ if((0 < upd->string_a[SA_SETCOMP].size) &&
+ (upd->icomp != icomp ) ) { /* Selection enabled */
+ upd->icomp = icomp;
+ if(0 < upd->string_a[SA_SETCOMP].data[icomp].size) {
+ memcpy(upd->outbuf+ioutbuf,
+ upd->string_a[SA_SETCOMP].data[icomp].data,
+ upd->string_a[SA_SETCOMP].data[icomp].size);
+ ioutbuf += upd->string_a[SA_SETCOMP].data[icomp].size;
+ }
+ } /* Selection enabled */
+/*
+ * Establish the X-Position
+ */
+ if(xbegin != upd->xprinter) {
+
+ if(0 == upd->strings[S_XMOVE].size) {
+
+ upd->outbuf[ioutbuf++] = '\r';
+ upd->xprinter = 0;
+ n = 0;
+ x = 0;
+
+ } else {
+
+ if(B_XABS & upd->flags) n = x = xbegin + upd->ints[I_XOFS];
+ else n = x = xbegin - upd->xprinter;
+
+ if( 1 < upd->ints[I_XSTEP]) {
+ if(0 > n) {
+ n -= upd->ints[I_XSTEP];
+ x -= n;
+ }
+ if(n) n /= upd->ints[I_XSTEP]; /* Major-Steps */
+ if(x) x %= upd->ints[I_XSTEP]; /* Minor-Steps */
+
+ } else if(-1 > upd->ints[I_XSTEP]) {
+ n *= -upd->ints[I_XSTEP]; /* May this work? */
+ x = 0;
+ }
+
+ if(n) { /* Adjust X-Position */
+
+ memcpy(upd->outbuf+ioutbuf,
+ upd->strings[S_XMOVE].data,
+ upd->strings[S_XMOVE].size);
+ ioutbuf += upd->strings[S_XMOVE].size;
+
+ upd->outbuf[ioutbuf++] = n & 0xff;
+ upd->outbuf[ioutbuf++] = (n>>8) & 0xff;
+
+ } /* Adjust X-Position */
+
+ }
+
+ if(x && 0 < upd->strings[S_XSTEP].size) { /* Fine-Adjust X */
+ while(x--) {
+ memcpy(upd->outbuf+ioutbuf,
+ upd->strings[S_XSTEP].data,
+ upd->strings[S_XSTEP].size);
+ ioutbuf += upd->strings[S_XSTEP].size;
+ }
+ } /* Fine-Adjust X */
+ }
+ upd->xprinter = xend+1;
+
+/*
+ * Send the Write-Command
+ */
+ if(0 < upd->string_a[SA_WRITECOMP].data[icomp].size) {
+ memcpy(upd->outbuf+ioutbuf,
+ upd->string_a[SA_WRITECOMP].data[icomp].data,
+ upd->string_a[SA_WRITECOMP].data[icomp].size);
+ ioutbuf += upd->string_a[SA_WRITECOMP].data[icomp].size;
+ }
+ n = xend + 1 - xbegin;
+ upd->outbuf[ioutbuf++] = (n<<3) & 255;
+ upd->outbuf[ioutbuf++] = (n>>5) & 255;
+/*
+ * Set the Pixels
+ */
+ for(pin = 0; pin < pintop; ++pin) {
+ ioutbuf += upd_rle(upd->outbuf+ioutbuf,NULL,n);
+ fwrite(upd->outbuf,1,ioutbuf,out);
+ ioutbuf = 0;
+ }
+
+ for(y = ybegin; 0 > y; y += upd->ints[I_NYPASS]) {
+ ioutbuf += upd_rle(upd->outbuf+ioutbuf,NULL,n);
+ fwrite(upd->outbuf,1,ioutbuf,out);
+ ioutbuf = 0;
+ }
+
+ for(; y < yend; y += upd->ints[I_NYPASS]) {
+ ioutbuf += upd_rle(upd->outbuf+ioutbuf,
+ upd->scnbuf[y & upd->scnmsk][icomp].bytes+xbegin,n);
+ fwrite(upd->outbuf,1,ioutbuf,out);
+ ioutbuf = 0;
+ }
+
+ for(pin = pinbot; pin < upd->ints[I_PINS2WRITE]; ++pin) {
+ ioutbuf += upd_rle(upd->outbuf+ioutbuf,NULL,n);
+ fwrite(upd->outbuf,1,ioutbuf,out);
+ ioutbuf = 0;
+ }
+ } /* Component-Print */
+ } /* Some data to write */
+
+/** Advance counters in upd, change modi */
+ if(upd->yscan < upd->ints[I_BEG_Y]) {
+ upd->yscan += upd->int_a[IA_BEG_DY].data[upd->ipass++];
+ if( upd->ints[I_BEG_Y] <= upd->yscan) upd->ipass = 0;
+ else if(upd->int_a[IA_BEG_DY].size <= upd->ipass) upd->ipass = 0;
+ } else if(upd->yscan >= upd->ints[I_END_Y]) {
+ upd->yscan += upd->int_a[IA_END_DY].data[upd->ipass++];
+ if(upd->int_a[IA_END_DY].size <= upd->ipass) upd->ipass = 0;
+ } else {
+ upd->yscan += upd->int_a[IA_STD_DY].data[upd->ipass++];
+ if(upd->int_a[IA_STD_DY].size <= upd->ipass) upd->ipass = 0;
+ if(upd->yscan >= upd->ints[I_END_Y]) upd->ipass = 0;
+ }
+
+ return 0;
+}
+
+/* ------------------------------------------------------------------- */
+/* upd_wrtescnm: Write a pass */
+/* ------------------------------------------------------------------- */
+
+/*GR copied from upd_wrtescp2 and modified */
+
+static int
+upd_wrtescnm(upd_p upd, FILE *out)
+{
+ int pinbot,pin,pintop,xbegin,x,xend,icomp,ybegin,yend,y,ioutbuf,n;
+ int irow,imask,iyofs;
+ byte *obytes;
+ updscan_p scan;
+
+/** Determine the number of pins to write */
+
+ if(upd->yscan < upd->ints[I_BEG_Y]) {
+ pintop = 0;
+ pinbot = upd->int_a[IA_BEGBOT].data[upd->ipass];
+ } else if(upd->yscan >= upd->ints[I_END_Y]) {
+ pinbot = upd->ints[I_PINS2WRITE];
+ pintop = pinbot - upd->int_a[IA_ENDTOP].data[upd->ipass];
+ } else {
+ pintop = 0;
+ pinbot = upd->ints[I_PINS2WRITE];
+ }
+
+ ybegin = pintop * upd->ints[I_NYPASS] + upd->yscan - upd->ints[I_BEGSKIP];
+ yend = pinbot * upd->ints[I_NYPASS] + upd->yscan - upd->ints[I_BEGSKIP];
+
+/** Determine Width of this scan */
+
+ xbegin = upd->nbytes;
+ xend = -1;
+
+ for(y = ybegin; y < yend; y += upd->ints[I_NYPASS]) { /* Pin-testloop */
+
+ if(0 > y) continue; /* Inserted Scanlines */
+
+ scan = upd->scnbuf[y & upd->scnmsk];
+
+ for(icomp = 0; icomp < upd->ocomp; ++icomp) { /* Compwise test */
+ obytes = scan[icomp].bytes;
+
+ for(x = 0; x < xbegin && !obytes[x]; x++);
+ if(x < xbegin) xbegin = x;
+
+ if(x < upd->nbytes) {
+ for(x = upd->nbytes-1; x > xend && !obytes[x]; x--);
+ if(x > xend) xend = x;
+ }
+ } /* Compwise test */
+ } /* Pin-testloop */
+
+ if(xbegin <= xend) { /* Some data to write */
+
+ ioutbuf = 0;
+
+ if(0 == upd->strings[S_XMOVE].size) xbegin = 0;
+
+/*
+ * Adjust the Printers Y-Position
+ */
+ if(upd->yscan != upd->yprinter) { /* Adjust Y-Position */
+ if(B_YABS & upd->flags) y = upd->yscan + upd->ints[I_YOFS];
+ else y = upd->yscan - upd->yprinter;
+
+ if( 1 < upd->ints[I_YSTEP]) {
+ n = y / upd->ints[I_YSTEP]; /* Major-Steps */
+ y -= n * upd->ints[I_YSTEP]; /* Minor-Steps */
+ } else if(-1 > upd->ints[I_YSTEP]) {
+ n = y * -upd->ints[I_YSTEP]; /* May this work? */
+ y = 0;
+ } else {
+ n = y;
+ y = 0;
+ }
+
+ if(n) { /* Coarse Positioning */
+ memcpy(upd->outbuf+ioutbuf,
+ upd->strings[S_YMOVE].data,upd->strings[S_YMOVE].size);
+ ioutbuf += upd->strings[S_YMOVE].size;
+
+ upd->outbuf[ioutbuf++] = n & 0xff;
+ upd->outbuf[ioutbuf++] = (n>>8) & 0xff;
+
+ } /* Coarse Positioning */
+
+ if(0 < upd->strings[S_YSTEP].size) {
+ while(y--) {
+ memcpy(upd->outbuf+ioutbuf,
+ upd->strings[S_YSTEP].data,
+ upd->strings[S_YSTEP].size);
+ ioutbuf += upd->strings[S_YSTEP].size;
+ }
+ }
+
+ upd->yprinter = upd->yscan;
+ } /* Adjust Y-Position */
+/*
+ * Now write the required components
+ */
+
+/*
+* Select the Component
+*
+* Always issue an ESC 'r' 0 - don't know why - that
+* is just what the windows driver does.
+*/
+ icomp=0;
+ if((0 < upd->string_a[SA_SETCOMP].size) /* &&
+ (upd->icomp != icomp ) */) { /* Selection enabled */
+ upd->icomp = icomp;
+ if(0 < upd->string_a[SA_SETCOMP].data[icomp].size) {
+ memcpy(upd->outbuf+ioutbuf,
+ upd->string_a[SA_SETCOMP].data[icomp].data,
+ upd->string_a[SA_SETCOMP].data[icomp].size);
+ ioutbuf += upd->string_a[SA_SETCOMP].data[icomp].size;
+ }
+ } /* Selection enabled */
+/*
+* Establish the X-Position
+*/
+ if(xbegin != upd->xprinter) {
+
+ if(0 == upd->strings[S_XMOVE].size) {
+
+ upd->outbuf[ioutbuf++] = '\r';
+ upd->xprinter = 0;
+ n = 0;
+ x = 0;
+
+ } else {
+
+ if(B_XABS & upd->flags) n = x = xbegin + upd->ints[I_XOFS];
+ else n = x = xbegin - upd->xprinter;
+
+ if( 1 < upd->ints[I_XSTEP]) {
+ if(0 > n) {
+ n -= upd->ints[I_XSTEP];
+ x -= n;
+ }
+ if(n) n /= upd->ints[I_XSTEP]; /* Major-Steps */
+ if(x) x %= upd->ints[I_XSTEP]; /* Minor-Steps */
+
+ } else if(-1 > upd->ints[I_XSTEP]) {
+ n *= -upd->ints[I_XSTEP]; /* May this work? */
+ x = 0;
+ }
+
+ if(n) { /* Adjust X-Position */
+
+ memcpy(upd->outbuf+ioutbuf,
+ upd->strings[S_XMOVE].data,
+ upd->strings[S_XMOVE].size);
+ ioutbuf += upd->strings[S_XMOVE].size;
+
+ upd->outbuf[ioutbuf++] = n & 0xff;
+ upd->outbuf[ioutbuf++] = (n>>8) & 0xff;
+
+ } /* Adjust X-Position */
+
+ }
+
+ if(x && 0 < upd->strings[S_XSTEP].size) { /* Fine-Adjust X */
+ while(x--) {
+ memcpy(upd->outbuf+ioutbuf,
+ upd->strings[S_XSTEP].data,
+ upd->strings[S_XSTEP].size);
+ ioutbuf += upd->strings[S_XSTEP].size;
+ }
+ } /* Fine-Adjust X */
+ }
+ upd->xprinter = xend+1;
+
+/*
+* Send the Write-Command - the default is ESC '.' 1
+*/
+ if(0 < upd->string_a[SA_WRITECOMP].data[icomp].size) {
+ memcpy(upd->outbuf+ioutbuf,
+ upd->string_a[SA_WRITECOMP].data[icomp].data,
+ upd->string_a[SA_WRITECOMP].data[icomp].size);
+ ioutbuf += upd->string_a[SA_WRITECOMP].data[icomp].size;
+ }
+ n = xend + 1 - xbegin;
+ upd->outbuf[ioutbuf++] = (n<<3) & 255;
+ upd->outbuf[ioutbuf++] = (n>>5) & 255;
+/*
+* Set the Pixels
+*/
+ irow=0; /* row counter for output data */
+
+ /* pins at the top of the head that don't print */
+ for(pin = 0; pin < pintop; ++pin) {
+ int i;
+ for(i=0 ; i < upd->ints[I_PATRPT]; i++){
+ if(irow >= upd->ints[I_ROWS]) break;
+ ioutbuf += upd_rle(upd->outbuf+ioutbuf,NULL,n);
+ fwrite(upd->outbuf,1,ioutbuf,out);
+ irow++;
+ ioutbuf = 0;
+ }
+ }
+
+ /* I'm not really sure what this does */
+ /* it looks like we're filling in empty rows */
+ for(y = ybegin; 0 > y; y += upd->ints[I_NYPASS]) {
+
+ int i;
+ for(i=0 ; i < upd->ints[I_PATRPT]; i++){
+ if(irow >= upd->ints[I_ROWS]) break;
+ ioutbuf += upd_rle(upd->outbuf+ioutbuf,NULL,n);
+ fwrite(upd->outbuf,1,ioutbuf,out);
+ ioutbuf = 0;
+ irow++;
+ }
+ }
+
+ for(; y < yend; y += upd->ints[I_NYPASS]) {
+
+ int i,masklen=upd->ints[I_PATRPT],yinc=0;
+
+ for(i=0 ; (i < upd->ints[I_PATRPT]); i++){
+ if(irow >= upd->ints[I_ROWS]) break;
+ imask = irow%masklen;
+ icomp = upd->int_a[IA_ROWMASK].data[imask];
+ if(icomp == 0) {
+ ioutbuf += upd_rle(upd->outbuf+ioutbuf,NULL,n);
+ } else {
+ --icomp;
+ iyofs = upd->int_a[IA_SCNOFS].data[imask];
+ ioutbuf += upd_rle(upd->outbuf+ioutbuf,
+ upd->scnbuf[(y+iyofs) & upd->scnmsk][icomp].bytes+xbegin,n);
+ yinc+=upd->ints[I_NYPASS];
+ }
+ fwrite(upd->outbuf,1,ioutbuf,out);
+ ioutbuf = 0;
+ irow++;
+ }
+
+ if (upd->ints[I_NYPASS] < upd->ints[I_PATRPT]) {
+ y+=yinc;
+ if (y > 0)
+ y-=upd->ints[I_NYPASS];
+ }
+ }
+
+ /* I think this is the pins at the bottom of the head that don't print */
+ for(pin = pinbot; pin < upd->ints[I_PINS2WRITE]; ++pin) {
+ int i;
+ for(i=0 ; i < upd->ints[I_PATRPT]; i++){
+ if(irow >= upd->ints[I_ROWS]) break;
+ ioutbuf += upd_rle(upd->outbuf+ioutbuf,NULL,n);
+ fwrite(upd->outbuf,1,ioutbuf,out);
+ ioutbuf = 0;
+ irow++;
+ }
+ }
+
+ /* pad empty rows that haven't been filled yet*/
+ if (irow < upd->ints[I_ROWS]) {
+ for( ; irow < upd->ints[I_ROWS]; irow++){
+ ioutbuf += upd_rle(upd->outbuf+ioutbuf,NULL,n);
+ fwrite(upd->outbuf,1,ioutbuf,out);
+ ioutbuf = 0;
+ }
+ }
+
+ } /* Some data to write */
+
+/** Advance counters in upd, change modi */
+ if(upd->yscan < upd->ints[I_BEG_Y]) {
+ upd->yscan += upd->int_a[IA_BEG_DY].data[upd->ipass++];
+ if( upd->ints[I_BEG_Y] <= upd->yscan) upd->ipass = 0;
+ else if(upd->int_a[IA_BEG_DY].size <= upd->ipass) upd->ipass = 0;
+ } else if(upd->yscan >= upd->ints[I_END_Y]) {
+ upd->yscan += upd->int_a[IA_END_DY].data[upd->ipass++];
+ if(upd->int_a[IA_END_DY].size <= upd->ipass) upd->ipass = 0;
+ } else {
+ upd->yscan += upd->int_a[IA_STD_DY].data[upd->ipass++];
+ if(upd->int_a[IA_STD_DY].size <= upd->ipass) upd->ipass = 0;
+ if(upd->yscan >= upd->ints[I_END_Y]) upd->ipass = 0;
+ }
+
+ return 0;
+}
+
+/* ------------------------------------------------------------------- */
+/* upd_wrtescp2x: Write an ESC/P2-pass with X-Weaving */
+/* ------------------------------------------------------------------- */
+
+static int
+upd_wrtescp2x(upd_p upd, FILE *out)
+{
+ int pinbot,pin,pintop,xbegin,x,xend,icomp,ybegin,yend,y,ioutbuf,n,ixpass;
+ byte *obytes,bit;
+ updscan_p scan;
+
+/** Determine the number of pins to write */
+
+ if(upd->yscan < upd->ints[I_BEG_Y]) {
+ ixpass = upd->int_a[IA_BEG_IX].data[upd->ipass];
+ pintop = 0;
+ pinbot = upd->int_a[IA_BEGBOT].data[upd->ipass];
+ } else if(upd->yscan >= upd->ints[I_END_Y]) {
+ ixpass = upd->int_a[IA_END_IX].data[upd->ipass];
+ pinbot = upd->ints[I_PINS2WRITE];
+ pintop = pinbot - upd->int_a[IA_ENDTOP].data[upd->ipass];
+ } else {
+ ixpass = upd->int_a[IA_STD_IX].data[upd->ipass];
+ pintop = 0;
+ pinbot = upd->ints[I_PINS2WRITE];
+ }
+
+ ybegin = pintop * upd->ints[I_NYPASS] + upd->yscan - upd->ints[I_BEGSKIP];
+ yend = pinbot * upd->ints[I_NYPASS] + upd->yscan - upd->ints[I_BEGSKIP];
+
+/** Determine Width of this scan */
+
+ xbegin = upd->pwidth;
+ xend = -1;
+
+ for(y = ybegin; y < yend; y += upd->ints[I_NYPASS]) { /* Pin-testloop */
+
+ if(0 > y) continue; /* Inserted Scanlines */
+
+ scan = upd->scnbuf[y & upd->scnmsk];
+
+ for(icomp = 0; icomp < upd->ocomp; ++icomp) { /* Compwise test */
+ if(xbegin > scan[icomp].xbegin[ixpass])
+ xbegin = scan[icomp].xbegin[ixpass];
+ if(xend < scan[icomp].xend[ ixpass])
+ xend = scan[icomp].xend[ ixpass];
+ } /* Compwise test */
+
+ } /* Pin-testloop */
+
+ if(xbegin <= xend) { /* Some data to write */
+
+ ioutbuf = upd->nbytes;
+
+ if(0 == upd->strings[S_XMOVE].size) xbegin = ixpass;
+
+/*
+ * Adjust the Printers Y-Position
+ */
+ if(upd->yscan != upd->yprinter) { /* Adjust Y-Position */
+ if(B_YABS & upd->flags) y = upd->yscan + upd->ints[I_YOFS];
+ else y = upd->yscan - upd->yprinter;
+
+ if( 1 < upd->ints[I_YSTEP]) {
+ n = y / upd->ints[I_YSTEP]; /* Major-Steps */
+ y -= n * upd->ints[I_YSTEP]; /* Minor-Steps */
+ } else if(-1 > upd->ints[I_YSTEP]) {
+ n = y * -upd->ints[I_YSTEP]; /* May this work? */
+ y = 0;
+ } else {
+ n = y;
+ y = 0;
+ }
+
+ if(n) { /* Coarse Positioning */
+ memcpy(upd->outbuf+ioutbuf,
+ upd->strings[S_YMOVE].data,upd->strings[S_YMOVE].size);
+ ioutbuf += upd->strings[S_YMOVE].size;
+
+ upd->outbuf[ioutbuf++] = n & 0xff;
+ upd->outbuf[ioutbuf++] = (n>>8) & 0xff;
+
+ } /* Coarse Positioning */
+
+ if(0 < upd->strings[S_YSTEP].size) {
+ while(y--) {
+ memcpy(upd->outbuf+ioutbuf,
+ upd->strings[S_YSTEP].data,
+ upd->strings[S_YSTEP].size);
+ ioutbuf += upd->strings[S_YSTEP].size;
+ }
+ }
+
+ upd->yprinter = upd->yscan;
+ } /* Adjust Y-Position */
+
+/*
+ * Now write the required components
+ */
+ for(icomp = 0; icomp < upd->ocomp; ++icomp) { /* Component-Print */
+/*
+ * First check, wether this Component needs printing
+ */
+ for(y = ybegin; y < yend; y += upd->ints[I_NYPASS]) { /* Comp-Test */
+ if(0 > y) continue;
+ scan = upd->scnbuf[y & upd->scnmsk]+icomp;
+ if(0 <= scan->xend[ixpass]) break;
+ } /* Comp-Test */
+ if(y >= yend) continue; /* Component not required */
+/*
+ * Select the Component
+ */
+ if((0 < upd->string_a[SA_SETCOMP].size) &&
+ (upd->icomp != icomp ) ) { /* Selection enabled */
+ upd->icomp = icomp;
+ if(0 < upd->string_a[SA_SETCOMP].data[icomp].size) {
+ memcpy(upd->outbuf+ioutbuf,
+ upd->string_a[SA_SETCOMP].data[icomp].data,
+ upd->string_a[SA_SETCOMP].data[icomp].size);
+ ioutbuf += upd->string_a[SA_SETCOMP].data[icomp].size;
+ }
+ } /* Selection enabled */
+/*
+ * Establish the X-Position
+ */
+ if(xbegin != upd->xprinter) {
+
+ if(0 == upd->strings[S_XMOVE].size) {
+
+ upd->outbuf[ioutbuf++] = '\r';
+ upd->xprinter = 0;
+ n = 0;
+ x = ixpass;
+
+ } else {
+
+ if(B_XABS & upd->flags) n = x = xbegin + upd->ints[I_XOFS];
+ else n = x = xbegin - upd->xprinter;
+
+ if( 1 < upd->ints[I_XSTEP]) {
+ if(0 > n) {
+ n -= upd->ints[I_XSTEP];
+ x -= n;
+ }
+ if(n) n /= upd->ints[I_XSTEP]; /* Major-Steps */
+ if(x) x %= upd->ints[I_XSTEP]; /* Minor-Steps */
+
+ } else if(-1 > upd->ints[I_XSTEP]) {
+ n *= -upd->ints[I_XSTEP]; /* May this work? */
+ x = 0;
+ }
+
+ if(n) { /* Adjust X-Position */
+
+ memcpy(upd->outbuf+ioutbuf,
+ upd->strings[S_XMOVE].data,
+ upd->strings[S_XMOVE].size);
+ ioutbuf += upd->strings[S_XMOVE].size;
+
+ upd->outbuf[ioutbuf++] = n & 0xff;
+ upd->outbuf[ioutbuf++] = (n>>8) & 0xff;
+
+ } /* Adjust X-Position */
+
+ }
+
+ if(x && 0 < upd->strings[S_XSTEP].size) { /* Fine-Adjust X */
+ while(x--) {
+ memcpy(upd->outbuf+ioutbuf,
+ upd->strings[S_XSTEP].data,
+ upd->strings[S_XSTEP].size);
+ ioutbuf += upd->strings[S_XSTEP].size;
+ }
+ } /* Fine-Adjust X */
+ }
+ upd->xprinter = xend+1;
+
+/*
+ * Send the Write-Command
+ */
+ if(0 < upd->string_a[SA_WRITECOMP].data[icomp].size) {
+ memcpy(upd->outbuf+ioutbuf,
+ upd->string_a[SA_WRITECOMP].data[icomp].data,
+ upd->string_a[SA_WRITECOMP].data[icomp].size);
+ ioutbuf += upd->string_a[SA_WRITECOMP].data[icomp].size;
+ }
+ n = ((xend - xbegin) / upd->ints[I_NXPASS] + 8) & ~7;
+ upd->outbuf[ioutbuf++] = n & 255;
+ upd->outbuf[ioutbuf++] = (n>>8) & 255;
+ n >>= 3;
+/*
+ * Set the Pixels
+ */
+ for(pin = 0; pin < pintop; ++pin) {
+ ioutbuf += upd_rle(upd->outbuf+ioutbuf,NULL,n);
+ fwrite(upd->outbuf+upd->nbytes,1,ioutbuf-upd->nbytes,out);
+ ioutbuf = upd->nbytes;
+ }
+
+ for(y = ybegin; 0 > y; y += upd->ints[I_NYPASS]) {
+ ioutbuf += upd_rle(upd->outbuf+ioutbuf,NULL,n);
+ fwrite(upd->outbuf+upd->nbytes,1,ioutbuf-upd->nbytes,out);
+ ioutbuf = upd->nbytes;
+ }
+
+ for(; y < yend; y += upd->ints[I_NYPASS]) {
+ byte * ibytes = upd->scnbuf[y & upd->scnmsk][icomp].bytes;
+ obytes = upd->outbuf;
+ memset(obytes,0,upd->nbytes);
+ bit = 0x80;
+ for(x = xbegin; x <= xend; x += upd->ints[I_NXPASS]) {
+ if(ibytes[x>>3] & (0x80 >> (x & 7))) *obytes |= bit;
+ if(!(bit >>= 1)) { obytes++; bit = 0x80; }
+ }
+ ioutbuf += upd_rle(upd->outbuf+ioutbuf,upd->outbuf,n);
+ fwrite(upd->outbuf+upd->nbytes,1,ioutbuf-upd->nbytes,out);
+ ioutbuf = upd->nbytes;
+ }
+
+ for(pin = pinbot; pin < upd->ints[I_PINS2WRITE]; ++pin) {
+ ioutbuf += upd_rle(upd->outbuf+ioutbuf,NULL,n);
+ fwrite(upd->outbuf+upd->nbytes,1,ioutbuf-upd->nbytes,out);
+ ioutbuf = upd->nbytes;
+ }
+ } /* Component-Print */
+ } /* Some data to write */
+
+/** Advance counters in upd, change modi */
+
+ if(upd->yscan < upd->ints[I_BEG_Y]) {
+ upd->yscan += upd->int_a[IA_BEG_DY].data[upd->ipass++];
+ if( upd->ints[I_BEG_Y] <= upd->yscan) upd->ipass = 0;
+ else if(upd->int_a[IA_BEG_DY].size <= upd->ipass) upd->ipass = 0;
+ } else if(upd->yscan >= upd->ints[I_END_Y]) {
+ upd->yscan += upd->int_a[IA_END_DY].data[upd->ipass++];
+ if(upd->int_a[IA_END_DY].size <= upd->ipass) upd->ipass = 0;
+ } else {
+ upd->yscan += upd->int_a[IA_STD_DY].data[upd->ipass++];
+ if(upd->int_a[IA_STD_DY].size <= upd->ipass) upd->ipass = 0;
+ if(upd->yscan >= upd->ints[I_END_Y]) upd->ipass = 0;
+ }
+
+ return 0;
+}
+
+/* ------------------------------------------------------------------- */
+/* upd_rle: The Runlength-Compressor */
+/* ------------------------------------------------------------------- */
+
+static int
+upd_rle(byte *out,const byte *in,int nbytes)
+{
+
+ int used = 0;
+ int crun,cdata;
+ byte run;
+
+ if(in != NULL) { /* Data present */
+
+ crun = 1;
+
+ while(nbytes > 0) { /* something to compress */
+
+ run = in[0];
+
+ while((nbytes > crun) && (run == in[crun])) if(++crun == 128) break;
+
+ if((crun > 2) || (crun == nbytes)) { /* use this run */
+
+ *out++ = (257 - crun) & 0xff; *out++ = run; used += 2;
+
+ nbytes -= crun; in += crun;
+ crun = 1;
+
+ } else { /* ignore this run */
+
+ for(cdata = crun; (nbytes > cdata) && (crun < 4);) {
+ if(run == in[cdata]) crun += 1;
+ else run = in[cdata], crun = 1;
+ if(++cdata == 128) break;
+ }
+
+ if(crun < 3) crun = 0; /* ignore trailing run */
+ else cdata -= crun;
+
+ *out++ = cdata-1; used++;
+ memcpy(out,in,cdata); used += cdata; out += cdata;
+
+ nbytes -= cdata; in += cdata;
+
+ } /* use/ignore run */
+
+ } /* something to compress */
+
+ } else { /* Empty scans to fill bands */
+
+ while(nbytes > 0) {
+ crun = nbytes > 128 ? 128 : nbytes;
+ nbytes -= crun;
+ *out++ = (257 - crun) & 0xff;
+ *out++ = 0;
+ used += 2;
+ }
+ } /* Data present or empty */
+ return used;
+}
+
+/* ------------------------------------------------------------------- */
+/* upd_open_wrtrtl: Basic HP-RTL Writer */
+/* ------------------------------------------------------------------- */
+
+static int
+upd_open_wrtrtl(upd_device *udev)
+{
+ const upd_p upd = udev->upd;
+ int error = 0;
+
+/** Adjust the Raster-Width */
+
+ if(0 < upd->strings[S_BEGIN].size) { /* BOP-Checker */
+
+ int i,j,state;
+ char cv[24];
+ byte *bp;
+ uint ncv,nbp;
+
+ j = -1;
+ state = 0;
+ for(i = 0; i < upd->strings[S_BEGIN].size; ++i) {
+ const int c = upd->strings[S_BEGIN].data[i];
+
+ switch(state) {
+/* ----- any character */
+ case 0:
+ if( c == 0x1b) state = 1; /* ESC */
+ break;
+
+/* ----- last was ESC */
+ case 1:
+ if( c == 0x2a) state = 2; /* ESC * */
+ else if( c == 0x25) state = 5; /* ESC % */
+ else state = 0;
+ break;
+
+/* ----- got ESC * */
+ case 2:
+ j = i; /* This character is not part of the replaced text */
+ if( c == 0x72) state = 3; /* ESC * r */
+ else if( c == 0x74) state = 4; /* ESC * t */
+ else state = 0;
+ break;
+
+/* ----- got ESC * r */
+/* Pagewidth and Pagelength might be replaced */
+ case 3:
+
+ if( (B_PAGEWIDTH & upd->flags) &&
+ ((c == 0x73) || (c == 0x53)) ) { /* esc * r # S */
+
+ gs_sprintf(cv,"%d",upd->pwidth);
+ ncv = strlen(cv);
+
+ nbp = (j+1) + ncv + (upd->strings[S_BEGIN].size-i);
+ UPD_MM_GET_ARRAY(udev->memory, bp,nbp);
+
+ if(0 <= j) memcpy(bp,upd->strings[S_BEGIN].data,j+1);
+ memcpy(bp+j+1, cv,ncv);
+ memcpy(bp+j+1+ncv,upd->strings[S_BEGIN].data+i,
+ upd->strings[S_BEGIN].size-i);
+ i = j+1+ncv;
+ UPD_MM_DEL_PARAM(udev->memory, upd->strings[S_BEGIN]);
+ upd->strings[S_BEGIN].data = bp;
+ upd->strings[S_BEGIN].size = nbp;
+
+ } else if((B_PAGELENGTH & upd->flags) &&
+ ((c == 0x74) || (c == 0x54)) ) { /* esc * r # T */
+
+ gs_sprintf(cv,"%d",upd->pheight);
+ ncv = strlen(cv);
+
+ nbp = (j+1) + ncv + (upd->strings[S_BEGIN].size-i);
+ UPD_MM_GET_ARRAY(udev->memory, bp,nbp);
+
+ if(0 <= j) memcpy(bp,upd->strings[S_BEGIN].data,j+1);
+ memcpy(bp+j+1, cv,ncv);
+ memcpy(bp+j+1+ncv,upd->strings[S_BEGIN].data+i,
+ upd->strings[S_BEGIN].size-i);
+ i = j+1+ncv;
+ UPD_MM_DEL_PARAM(udev->memory, upd->strings[S_BEGIN]);
+ upd->strings[S_BEGIN].data = bp;
+ upd->strings[S_BEGIN].size = nbp;
+
+ }
+
+ if( (0x40 < c) && (c < 0x5b)) state = 0; /* Term. cmd. */
+ else if(!((0x2f < c) && (c < 0x3a))) j = i; /* Non-Number */
+
+ break;
+
+/* ----- got ESC * t */
+/* Resolution might be replaced */
+ case 4: /* esc * t */
+
+ if( (B_RESOLUTION & upd->flags) &&
+ ((c == 0x72) || (c == 0x52)) ) { /* esc * t # R */
+
+ gs_sprintf(cv,"%d",(int)
+ ((udev->y_pixels_per_inch < udev->x_pixels_per_inch ?
+ udev->x_pixels_per_inch : udev->y_pixels_per_inch)
+ +0.5));
+ ncv = strlen(cv);
+
+ nbp = (j+1) + ncv + (upd->strings[S_BEGIN].size-i);
+ UPD_MM_GET_ARRAY(udev->memory, bp,nbp);
+
+ if(0 <= j) memcpy(bp,upd->strings[S_BEGIN].data,j+1);
+ memcpy(bp+j+1, cv,ncv);
+ memcpy(bp+j+1+ncv,upd->strings[S_BEGIN].data+i,
+ upd->strings[S_BEGIN].size-i);
+ i = j+1+ncv;
+ UPD_MM_DEL_PARAM(udev->memory, upd->strings[S_BEGIN]);
+ upd->strings[S_BEGIN].data = bp;
+ upd->strings[S_BEGIN].size = nbp;
+
+ }
+
+ if( (0x40 < c) && (c < 0x5b)) state = 0; /* Term. cmd. */
+ else if(!((0x2f < c) && (c < 0x3a))) j = i; /* Non-Number */
+
+ break;
+
+ case 5: /* ESC % - 1 2 3 4 5 X */
+ if( c == 0x2d) state = 6; /* ESC % - */
+ else state = 0;
+ break;
+
+ case 6: /* ESC % - 1 2 3 4 5 X */
+ if( c == 0x31) state = 7; /* ESC % - 1 */
+ else state = 0;
+ break;
+
+ case 7: /* ESC % - 1 2 3 4 5 X */
+ if( c == 0x32) state = 8; /* ESC % - 1 2 */
+ else state = 0;
+ break;
+
+ case 8: /* ESC % - 1 2 3 4 5 X */
+ if( c == 0x33) state = 9; /* ESC % - 1 2 3 */
+ else state = 0;
+ break;
+
+ case 9: /* ESC % - 1 2 3 4 5 X */
+ if( c == 0x34) state = 10; /* ESC % - 1 2 3 4 */
+ else state = 0;
+ break;
+
+ case 10: /* ESC % - 1 2 3 4 5 X */
+ if( c == 0x35) state = 11; /* ESC % - 1 2 3 4 5 */
+ else state = 0;
+ break;
+
+ case 11: /* ESC % - 1 2 3 4 5 X */
+ if( c == 0x58) state = 12; /* ESC % - 1 2 3 4 5 X */
+ else state = 0;
+ break;
+
+ case 12: /* PJL-BOL: @ P J L ws */
+ if( c == 0x40) state = 13; /* @ */
+ else state = 0;
+ break;
+
+ case 13: /* PJL-BOL @ P J L ws */
+ if( c == 0x50) state = 14; /* @ P */
+ else state = 0;
+ break;
+
+ case 14: /* PJL-BOL @ P J L ws */
+ if( c == 0x4a) state = 15; /* @ P J */
+ else state = 0;
+ break;
+
+ case 15: /* PJL-BOL @ P J L ws */
+ if( c == 0x4c) state = 16; /* @ P J L */
+ else state = 0;
+ break;
+
+ case 16: /* PJL-BOL @ P J L ws */
+ if((c == 0x20) || (c == 0x09)) state = 19; /* @ P J L ws */
+ else if( c == 0x0d ) state = 17;
+ else if( c == 0x0a ) state = 12;
+ else state = 0; /* PJL-Error */
+ break;
+
+ case 17: /* PJL-EOL */
+ if( c == 0x0a) state = 12; /* Next PJL-Command */
+ else state = 0; /* PJL-Error */
+ break;
+
+ case 18: /* PJL-Eatup: Expect Newline */
+ if( c == 0x0a) state = 12;
+ break;
+
+ case 19: /* Begin of PJL-Command */
+ if( (c == 0x53) || (c == 0x73)) state = 20; /* S E T*/
+ else if( c == 0x0a ) state = 12; /* BOL */
+ else if( c == 0x0d ) state = 17;
+ break;
+
+ case 20: /* PJL-Set: S E T */
+ if( (c == 0x45) || (c == 0x65)) state = 21; /* S E */
+ else if( c == 0x0a ) state = 12; /* BOL */
+ else state = 18;
+ break;
+
+ case 21: /* PJL-Set: S E T */
+ if( (c == 0x54) || (c == 0x74)) state = 22; /* S E T */
+ else if( c == 0x0a ) state = 12; /* BOL */
+ else state = 18;
+ break;
+
+ case 22: /* PJL-Set: S E T ws */
+ if( (c == 0x20) || (c == 0x09)) state = 23; /* S E T ws */
+ else if( c == 0x0a ) state = 12; /* BOL */
+ else state = 18;
+ break;
+
+ case 23: /* PJL-Set: S E T ws */
+ if( (c == 0x50) || (c == 0x70)) state = 24; /* set paper... */
+ else if((c == 0x52) || (c == 0x72)) state = 41; /* set resolution */
+ else if( c == 0x0a ) state = 12; /* BOL */
+ else state = 18;
+ break;
+
+ case 24: /* PJL-Set: set paper... */
+ if( (c == 0x41) || (c == 0x61)) state = 25; /* set pa */
+ else if( c == 0x0a ) state = 12; /* BOL */
+ else state = 18;
+ break;
+
+ case 25: /* PJL-Set: set paper... */
+ if( (c == 0x50) || (c == 0x70)) state = 26; /* set pap */
+ else if( c == 0x0a ) state = 12; /* BOL */
+ else state = 18;
+ break;
+
+ case 26: /* PJL-Set: set paper... */
+ if( (c == 0x45) || (c == 0x65)) state = 27; /* set pape */
+ else if( c == 0x0a ) state = 12; /* BOL */
+ else state = 18;
+ break;
+
+ case 27: /* PJL-Set: set paper... */
+ if( (c == 0x52) || (c == 0x72)) state = 28; /* set paper */
+ else if( c == 0x0a ) state = 12; /* BOL */
+ else state = 18;
+ break;
+
+ case 28: /* PJL-Set: set paper? */
+ if( (c == 0x4c) || (c == 0x6c)) state = 29; /* set paperlength */
+ else if((c == 0x57) || (c == 0x77)) state = 36; /* set paperwidth */
+ else if( c == 0x0a ) state = 12; /* BOL */
+ else state = 18;
+ break;
+
+ case 29: /* PJL: set paperlength */
+ if( (c == 0x45) || (c == 0x65)) state = 30; /* set paperle */
+ else if( c == 0x0a ) state = 12; /* BOL */
+ else state = 18;
+ break;
+
+ case 30: /* PJL: set paperlength */
+ if( (c == 0x4e) || (c == 0x6e)) state = 31; /* set paperlen */
+ else if( c == 0x0a ) state = 12; /* BOL */
+ else state = 18;
+ break;
+
+ case 31: /* PJL: set paperlength */
+ if( (c == 0x47) || (c == 0x67)) state = 32; /* set paperleng */
+ else if( c == 0x0a ) state = 12; /* BOL */
+ else state = 18;
+ break;
+
+ case 32: /* PJL: set paperlength */
+ if( (c == 0x54) || (c == 0x74)) state = 33; /* set paperlengt */
+ else if( c == 0x0a ) state = 12; /* BOL */
+ else state = 18;
+ break;
+
+ case 33: /* PJL: set paperlength */
+ if( (c == 0x48) || (c == 0x68)) state = 34; /* set paperlength */
+ else if( c == 0x0a ) state = 12; /* BOL */
+ else state = 18;
+ break;
+
+ case 34: /* PJL: set paperlength */
+ j = i; /* This character is not part of the replaced text */
+ if( c == 0x3d ) state = 51; /* set paperlength */
+ else if( c == 0x0a ) state = 12; /* BOL */
+ else if((c != 0x20) && (c != 0x09)) state = 18;
+ break;
+
+ case 51: /* PJL: set paperlength = ws */
+ if( c == 0x0a) state = 12;
+ else if((c == 0x20) || (c == 0x09)) j = i;
+ else if(( 0x30 > c) || ( c > 0x39)) state = 18;
+ else state = 35;
+ break;
+
+ case 35: /* PJL: set paperlength */
+ if((0x30 > c) || (c > 0x39)) { /* End of number */
+
+ if(B_PAGELENGTH & upd->flags) { /* insert new number */
+
+ gs_sprintf(cv,"%d",(int)
+ (720.0 * udev->height / udev->y_pixels_per_inch + 0.5));
+ ncv = strlen(cv);
+
+ nbp = (j+1) + ncv + (upd->strings[S_BEGIN].size-i);
+ UPD_MM_GET_ARRAY(udev->memory, bp,nbp);
+
+ if(0 <= j) memcpy(bp,upd->strings[S_BEGIN].data,j+1);
+ memcpy(bp+j+1, cv,ncv);
+ memcpy(bp+j+1+ncv,upd->strings[S_BEGIN].data+i,
+ upd->strings[S_BEGIN].size-i);
+ i = j+1+ncv;
+ UPD_MM_DEL_PARAM(udev->memory, upd->strings[S_BEGIN]);
+ upd->strings[S_BEGIN].data = bp;
+ upd->strings[S_BEGIN].size = nbp;
+ } /* insert new number */
+
+ if( c == 0x0a ) state = 12;
+ else state = 18;
+ }
+ break;
+
+ case 36: /* PJL: set paperwidth */
+ if( (c == 0x49) || (c == 0x69)) state = 37; /* set paperwi */
+ else if( c == 0x0a ) state = 12; /* BOL */
+ else state = 18;
+ break;
+
+ case 37: /* PJL: set paperwidth */
+ if( (c == 0x44) || (c == 0x64)) state = 38; /* set paperwid */
+ else if( c == 0x0a ) state = 12; /* BOL */
+ else state = 18;
+ break;
+
+ case 38: /* PJL: set paperwidth */
+ if( (c == 0x54) || (c == 0x74)) state = 39; /* set paperwidt */
+ else if( c == 0x0a ) state = 12; /* BOL */
+ else state = 18;
+ break;
+
+ case 39: /* PJL: set paperwidth */
+ if( (c == 0x48) || (c == 0x68)) state = 52; /* set paperwidth */
+ else if( c == 0x0a ) state = 12; /* BOL */
+ else state = 18;
+ break;
+
+ case 52: /* PJL: set paperwidth */
+ j = i; /* This character is not part of the replaced text */
+ if( c == 0x3d ) state = 53; /* set paperwidth */
+ else if( c == 0x0a ) state = 12; /* BOL */
+ else if((c != 0x20) && (c != 0x09)) state = 18;
+ break;
+
+ case 53: /* PJL: set paperwidth = ws */
+ if( c == 0x0a) state = 12;
+ else if((c == 0x20) || (c == 0x09)) j = i;
+ else if(( 0x30 > c) || ( c > 0x39)) state = 18;
+ else state = 40;
+ break;
+
+ case 40: /* PJL: set paperlength */
+ if((0x30 > c) || (c > 0x39)) { /* End of number */
+
+ if(B_PAGEWIDTH & upd->flags) { /* insert new number */
+
+ gs_sprintf(cv,"%d",(int)
+ (720.0 * udev->width / udev->x_pixels_per_inch + 0.5));
+ ncv = strlen(cv);
+
+ nbp = (j+1) + ncv + (upd->strings[S_BEGIN].size-i);
+ UPD_MM_GET_ARRAY(udev->memory, bp,nbp);
+
+ if(0 <= j) memcpy(bp,upd->strings[S_BEGIN].data,j+1);
+ memcpy(bp+j+1, cv,ncv);
+ memcpy(bp+j+1+ncv,upd->strings[S_BEGIN].data+i,
+ upd->strings[S_BEGIN].size-i);
+ i = j+1+ncv;
+ UPD_MM_DEL_PARAM(udev->memory, upd->strings[S_BEGIN]);
+ upd->strings[S_BEGIN].data = bp;
+ upd->strings[S_BEGIN].size = nbp;
+ } /* insert new number */
+
+ if( c == 0x0a ) state = 12;
+ else state = 18;
+ }
+ break;
+
+ case 41: /* PJL: set resolution */
+ if( (c == 0x45) || (c == 0x65)) state = 42; /* set re */
+ else if( c == 0x0a ) state = 12; /* BOL */
+ else state = 18;
+ break;
+
+ case 42: /* PJL: set resolution */
+ if( (c == 0x53) || (c == 0x73)) state = 43; /* set res */
+ else if( c == 0x0a ) state = 12; /* BOL */
+ else state = 18;
+ break;
+
+ case 43: /* PJL: set resolution */
+ if( (c == 0x4f) || (c == 0x6f)) state = 44; /* set reso */
+ else if( c == 0x0a ) state = 12; /* BOL */
+ else state = 18;
+ break;
+
+ case 44: /* PJL: set resolution */
+ if( (c == 0x4c) || (c == 0x6c)) state = 45; /* set resol */
+ else if( c == 0x0a ) state = 12; /* BOL */
+ else state = 18;
+ break;
+
+ case 45: /* PJL: set resolution */
+ if( (c == 0x55) || (c == 0x75)) state = 46; /* set resolu */
+ else if( c == 0x0a ) state = 12; /* BOL */
+ else state = 18;
+ break;
+
+ case 46: /* PJL: set resolution */
+ if( (c == 0x54) || (c == 0x74)) state = 47; /* set resolut */
+ else if( c == 0x0a ) state = 12; /* BOL */
+ else state = 18;
+ break;
+
+ case 47: /* PJL: set resolution */
+ if( (c == 0x49) || (c == 0x69)) state = 48; /* set resoluti */
+ else if( c == 0x0a ) state = 12; /* BOL */
+ else state = 18;
+ break;
+
+ case 48: /* PJL: set resolution */
+ if( (c == 0x4f) || (c == 0x6f)) state = 49; /* set resolutio */
+ else if( c == 0x0a ) state = 12; /* BOL */
+ else state = 18;
+ break;
+
+ case 49: /* PJL: set resolution */
+ if( (c == 0x4e) || (c == 0x6e)) state = 54; /* set resolution */
+ else if( c == 0x0a ) state = 12; /* BOL */
+ else state = 18;
+ break;
+
+ case 54: /* PJL: set resolution */
+ j = i; /* This character is not part of the replaced text */
+ if( c == 0x3d ) state = 55; /* set resolution */
+ else if( c == 0x0a ) state = 12; /* BOL */
+ else if((c != 0x20) && (c != 0x09)) state = 18;
+ break;
+
+ case 55: /* PJL: set resolution = ws */
+ if( c == 0x0a) state = 12;
+ else if((c == 0x20) || (c == 0x09)) j = i;
+ else if(( 0x30 > c) || ( c > 0x39)) state = 18;
+ else state = 50;
+ break;
+
+ case 50: /* PJL: set resolution */
+ if((0x30 > c) || (c > 0x39)) { /* End of number */
+
+ if(B_RESOLUTION & upd->flags) { /* insert new number */
+
+ gs_sprintf(cv,"%d",(int)
+ ((udev->y_pixels_per_inch < udev->x_pixels_per_inch ?
+ udev->x_pixels_per_inch : udev->y_pixels_per_inch)
+ +0.5));
+ ncv = strlen(cv);
+
+ nbp = (j+1) + ncv + (upd->strings[S_BEGIN].size-i);
+ UPD_MM_GET_ARRAY(udev->memory, bp,nbp);
+
+ if(0 <= j) memcpy(bp,upd->strings[S_BEGIN].data,j+1);
+ memcpy(bp+j+1, cv,ncv);
+ memcpy(bp+j+1+ncv,upd->strings[S_BEGIN].data+i,
+ upd->strings[S_BEGIN].size-i);
+ i = j+1+ncv;
+ UPD_MM_DEL_PARAM(udev->memory, upd->strings[S_BEGIN]);
+ upd->strings[S_BEGIN].data = bp;
+ upd->strings[S_BEGIN].size = nbp;
+ } /* insert new number */
+
+ if( c == 0x0a ) state = 12;
+ else state = 18;
+ }
+ break;
+
+ default:
+#if UPD_MESSAGES & UPD_M_ERROR
+ errprintf(udev->memory,
+ "UNIPRINT-Coding error, wrrtl, state = %d\n",state);
+#endif
+ state = 0;
+ break;
+ }
+ }
+ } /* BOP-Checker */
+
+/** SA_WRITECOMP must be valid */
+ if(upd->ocomp > upd->string_a[SA_WRITECOMP].size) {
+#if UPD_MESSAGES & UPD_M_WARNING
+ errprintf(udev->memory,
+ "PCL-Open: WRITECOMP-Commands must be given\n");
+#endif
+ error = -1;
+ }
+
+/**
+If all this is correct, it's time to compute the size of the output-buffer.
+It must hold:
+ 1. Y-Positioning
+ 2. Component-Data
+*/
+ if(0 <= error) {
+ int32_t ny,noutbuf;
+ char tmp[16];
+
+ if(0 < upd->strings[S_YMOVE].size) {
+ gs_sprintf(tmp,"%d",upd->pheight);
+ ny = upd->strings[S_YMOVE].size + strlen(tmp);
+ } else {
+ ny = 1 + upd->string_a[SA_WRITECOMP].data[upd->ocomp-1].size;
+ ny *= upd->pheight;
+ }
+
+ noutbuf = upd->nbytes + (upd->nbytes + 127) / 128;
+
+ if(ny > noutbuf) noutbuf = ny;
+ noutbuf += 16;
+
+ if((0 < noutbuf) && (noutbuf <= INT_MAX)) {
+ upd->noutbuf = noutbuf;
+ upd->writer = upd_wrtrtl;
+ error = 1;
+ } else {
+ error = -1;
+#if UPD_MESSAGES & UPD_M_WARNING
+ errprintf(udev->memory,
+ "PCL-Open: %ld is unreasonable size of Outputbuffer\n",
+ (long) noutbuf);
+#endif
+ }
+ }
+
+ return error;
+}
+
+/* ------------------------------------------------------------------- */
+/* upd_wrtrtl: Write a pass */
+/* ------------------------------------------------------------------- */
+
+static int
+upd_wrtrtl(upd_p upd, FILE *out)
+{
+ const updscan_p scan = upd->scnbuf[upd->yscan & upd->scnmsk];
+
+ int x,xend,icomp,ioutbuf;
+ byte *data;
+
+/** Determine Width of this scan */
+
+ xend = -1;
+ for(icomp = 0; icomp < upd->ocomp; ++icomp) {
+
+ data = scan[icomp].bytes;
+
+ for(x = upd->nbytes-1; 0 <= x; --x) if(data[x]) break;
+ if(x > xend) xend = x;
+ }
+
+ if(0 <= xend) { /* Some data to write */
+
+ ioutbuf = 0;
+ xend += 1;
+/*
+ * Adjust the Printers Y-Position
+ */
+ if(upd->yscan != upd->yprinter) { /* Adjust Y-Position */
+ if(1 < upd->strings[S_YMOVE].size) {
+ gs_sprintf((char *)upd->outbuf+ioutbuf,
+ (const char *) upd->strings[S_YMOVE].data,
+ upd->yscan - upd->yprinter);
+ ioutbuf += strlen((char *)upd->outbuf+ioutbuf);
+ } else {
+ while(upd->yscan > upd->yprinter) {
+ for(icomp = 0; icomp < upd->ocomp; ++icomp) {
+ gs_sprintf((char *)upd->outbuf+ioutbuf,
+ (const char *) upd->string_a[SA_WRITECOMP].data[icomp].data,0);
+ ioutbuf += strlen((char *)upd->outbuf+ioutbuf);
+ }
+ fwrite(upd->outbuf,1,ioutbuf,out);
+ ioutbuf = 0;
+ upd->yprinter += 1;
+ }
+ }
+ upd->yprinter = upd->yscan;
+ fwrite(upd->outbuf,1,ioutbuf,out);
+ ioutbuf = 0;
+ } /* Adjust Y-Position */
+/*
+ * Now write the all! components
+ */
+ for(icomp = 0; icomp < upd->ocomp; ++icomp) { /* Component-Print */
+ data = scan[icomp].bytes;
+ for(x = 0; x <= xend; ++x) if(data[x]) break;
+ if(x <= xend) {
+ ioutbuf = upd_rle(upd->outbuf,scan[icomp].bytes,xend);
+ fprintf(out,
+ (const char *)upd->string_a[SA_WRITECOMP].data[icomp].data,ioutbuf);
+ fwrite(upd->outbuf,1,ioutbuf,out);
+ } else {
+ fprintf(out,
+ (const char *)upd->string_a[SA_WRITECOMP].data[icomp].data,0);
+ }
+ }
+
+ upd->yprinter += 1;
+
+ } /* Some data to write */
+
+/** Advance scan by one */
+
+ upd->yscan += 1;
+
+ return 0;
+}
+
+/* ------------------------------------------------------------------- */
+/* upd_open_wrtcanon: Basic Canon Extended Mode Writer (hr) */
+/* ------------------------------------------------------------------- */
+
+static int
+upd_open_wrtcanon(upd_device *udev)
+{
+ const upd_p upd = udev->upd;
+ int error = 0;
+
+ /* max length of one printer line */
+ upd->noutbuf = upd->nbytes + (upd->nbytes + 127) / 128;
+ upd->writer = upd_wrtcanon;
+ error = 1;
+
+ return error;
+}
+
+/* ------------------------------------------------------------------- */
+/* upd_wrtcanon: Write a pass (hr) */
+/* ------------------------------------------------------------------- */
+
+#define LOW(b) ((b)&0xFF)
+#define HIGH(b) ((b)>>8)
+#define ESC 0x1B
+#define CR 0x0D
+
+static int
+upd_wrtcanon(upd_p upd, FILE *out)
+{
+ const updscan_p scan = upd->scnbuf[upd->yscan & upd->scnmsk];
+
+ int x, xend, icomp, ioutbuf, step, ioutbuf1;
+ byte *data;
+
+ /* Check length of the printable date */
+ xend = -1;
+ for(icomp = 0; icomp < upd->ocomp; ++icomp) {
+ data = scan[icomp].bytes;
+
+ for(x = upd->nbytes-1; 0 <= x; --x) if(data[x]) break;
+
+ if(x > xend) xend = x;
+ }
+
+ /* If some date to print */
+ if(0 <= xend) { /* Some data to write */
+ ioutbuf = 0;
+ xend += 1;
+
+ /* Perform vertical tab */
+ if(upd->yscan != upd->yprinter) {
+ step = upd->yscan - upd->yprinter;
+
+ fputc(ESC, out);
+ fputc('(', out);
+ fputc('e', out);
+ fputc(2, out);
+ fputc(0, out);
+ fputc(HIGH(step), out);
+ fputc(LOW(step), out);
+
+ upd->yprinter = upd->yscan;
+ }
+
+ for(icomp = 0; icomp < upd->ocomp; ++icomp) {
+
+ /* Are there date to print for the selected color component */
+ data = scan[icomp].bytes;
+ for(x = 0; x <= xend; ++x) if(data[x]) break;
+
+ /* Compressing of the scan line */
+ if(x <= xend) {
+ ioutbuf = upd_rle(upd->outbuf, scan[icomp].bytes, xend);
+ } else {
+ ioutbuf = 0;
+ }
+
+ ioutbuf1 = ioutbuf + 1;
+
+ /* prints the scan line */
+ fputc(ESC, out);
+ fputc('(', out);
+ fputc('A', out);
+ fputc(LOW(ioutbuf1), out);
+ fputc(HIGH(ioutbuf1), out);
+ switch(upd->ocomp) {
+ case 1: fputc('K',out); break;
+ case 3:
+ case 4: fputc("YMCK"[icomp],out); break;
+/*
+ * Please Note:
+ * the validity of the NCOMP-setting should be checked
+ * in the put_params-routine, thus the default-case is
+ * just a matter of coding-style.
+ */
+ default: fputc('K',out); break;
+ }
+
+ fwrite(upd->outbuf, 1, ioutbuf, out);
+
+ fputc(CR, out);
+ }
+
+ /* Printer advances one raster line */
+ fputc(ESC, out);
+ fputc('(', out);
+ fputc('e', out);
+ fputc(2, out);
+ fputc(0, out);
+ fputc(HIGH(1), out);
+ fputc(LOW(1), out);
+
+ upd->yprinter += 1;
+
+ }
+
+ /* Advance scan by one */
+ upd->yscan += 1;
+
+ return 0;
+}
+
+/* ------------------------------------------------------------------- */
+/* All the Pixel-Get Routines */
+/* ------------------------------------------------------------------- */
+
+/* That bunch of Pixel-Get Routines */
+
+static upd_proc_pxlget(upd_pxlgetnix); /* A Dummy */
+
+static upd_proc_pxlget(upd_pxlget1f1); /* 1 Bit Forward */
+static upd_proc_pxlget(upd_pxlget1f2);
+static upd_proc_pxlget(upd_pxlget1f3);
+static upd_proc_pxlget(upd_pxlget1f4);
+static upd_proc_pxlget(upd_pxlget1f5);
+static upd_proc_pxlget(upd_pxlget1f6);
+static upd_proc_pxlget(upd_pxlget1f7);
+static upd_proc_pxlget(upd_pxlget1f8);
+
+static upd_proc_pxlget(upd_pxlget1r1); /* 1 Bit Reverse */
+static upd_proc_pxlget(upd_pxlget1r2);
+static upd_proc_pxlget(upd_pxlget1r3);
+static upd_proc_pxlget(upd_pxlget1r4);
+static upd_proc_pxlget(upd_pxlget1r5);
+static upd_proc_pxlget(upd_pxlget1r6);
+static upd_proc_pxlget(upd_pxlget1r7);
+static upd_proc_pxlget(upd_pxlget1r8);
+
+static upd_proc_pxlget(upd_pxlget2f1); /* 2 Bit Forward */
+static upd_proc_pxlget(upd_pxlget2f2);
+static upd_proc_pxlget(upd_pxlget2f3);
+static upd_proc_pxlget(upd_pxlget2f4);
+
+static upd_proc_pxlget(upd_pxlget2r1); /* 2 Bit Reverse */
+static upd_proc_pxlget(upd_pxlget2r2);
+static upd_proc_pxlget(upd_pxlget2r3);
+static upd_proc_pxlget(upd_pxlget2r4);
+
+static upd_proc_pxlget(upd_pxlget4f1); /* 4 Bit Forward */
+static upd_proc_pxlget(upd_pxlget4f2);
+
+static upd_proc_pxlget(upd_pxlget4r1); /* 4 Bit Reverse */
+static upd_proc_pxlget(upd_pxlget4r2);
+
+static upd_proc_pxlget(upd_pxlget8f); /* 8 Bit Forward */
+static upd_proc_pxlget(upd_pxlget8r); /* 8 Bit Reverse */
+
+static upd_proc_pxlget(upd_pxlget16f); /* 16 Bit Forward */
+static upd_proc_pxlget(upd_pxlget16r); /* 16Bit Reverse */
+
+static upd_proc_pxlget(upd_pxlget24f); /* 24 Bit Forward */
+static upd_proc_pxlget(upd_pxlget24r); /* 24 Bit Reverse */
+
+static upd_proc_pxlget(upd_pxlget32f); /* 32 Bit Forward */
+static upd_proc_pxlget(upd_pxlget32r); /* 32 Bit Reverse */
+
+/* Initialize Forward-Run */
+
+static uint32_t
+upd_pxlfwd(upd_p upd)
+{
+ if(!(upd->pxlptr = upd->gsscan)) {
+
+ upd->pxlget = upd_pxlgetnix;
+
+ } else {
+ switch(upd->int_a[IA_COLOR_INFO].data[1]) {
+ case 1: upd->pxlget = upd_pxlget1f1; break;
+ case 2: upd->pxlget = upd_pxlget2f1; break;
+ case 4: upd->pxlget = upd_pxlget4f1; break;
+ case 8: upd->pxlget = upd_pxlget8f; break;
+ case 16: upd->pxlget = upd_pxlget16f; break;
+ case 24: upd->pxlget = upd_pxlget24f; break;
+ case 32: upd->pxlget = upd_pxlget32f; break;
+ default:
+#if UPD_MESSAGES & UPD_M_ERROR
+ errprintf(upd->memory, "upd_pxlfwd: unsupported depth (%d)\n",
+ upd->int_a[IA_COLOR_INFO].data[1]);
+#endif
+ upd->pxlget = upd_pxlgetnix;
+ break;
+ }
+ }
+ return (uint32_t) 0;
+}
+
+/* 1 Bit Forward */
+
+static uint32_t
+upd_pxlget1f1(upd_p upd)
+{
+ upd->pxlget = upd_pxlget1f2;
+ return *upd->pxlptr & 0x80 ? (uint32_t) 1 : (uint32_t) 0;
+}
+
+static uint32_t
+upd_pxlget1f2(upd_p upd)
+{
+ upd->pxlget = upd_pxlget1f3;
+ return *upd->pxlptr & 0x40 ? (uint32_t) 1 : (uint32_t) 0;
+}
+
+static uint32_t
+upd_pxlget1f3(upd_p upd)
+{
+ upd->pxlget = upd_pxlget1f4;
+ return *upd->pxlptr & 0x20 ? (uint32_t) 1 : (uint32_t) 0;
+}
+
+static uint32_t
+upd_pxlget1f4(upd_p upd)
+{
+ upd->pxlget = upd_pxlget1f5;
+ return *upd->pxlptr & 0x10 ? (uint32_t) 1 : (uint32_t) 0;
+}
+
+static uint32_t
+upd_pxlget1f5(upd_p upd)
+{
+ upd->pxlget = upd_pxlget1f6;
+ return *upd->pxlptr & 0x08 ? (uint32_t) 1 : (uint32_t) 0;
+}
+
+static uint32_t
+upd_pxlget1f6(upd_p upd)
+{
+ upd->pxlget = upd_pxlget1f7;
+ return *upd->pxlptr & 0x04 ? (uint32_t) 1 : (uint32_t) 0;
+}
+
+static uint32_t
+upd_pxlget1f7(upd_p upd)
+{
+ upd->pxlget = upd_pxlget1f8;
+ return *upd->pxlptr & 0x02 ? (uint32_t) 1 : (uint32_t) 0;
+}
+
+static uint32_t
+upd_pxlget1f8(upd_p upd)
+{
+ upd->pxlget = upd_pxlget1f1;
+ return *upd->pxlptr++ & 0x01 ? (uint32_t) 1 : (uint32_t) 0;
+}
+
+/* 2 Bit Forward */
+
+static uint32_t
+upd_pxlget2f1(upd_p upd)
+{
+ upd->pxlget = upd_pxlget2f2;
+ return ((uint32_t) (*upd->pxlptr ) & (uint32_t) 0xC0) >> 6;
+}
+
+static uint32_t
+upd_pxlget2f2(upd_p upd)
+{
+ upd->pxlget = upd_pxlget2f3;
+ return ((uint32_t) (*upd->pxlptr ) & (uint32_t) 0x30) >> 4;
+}
+
+static uint32_t
+upd_pxlget2f3(upd_p upd)
+{
+ upd->pxlget = upd_pxlget2f4;
+ return ((uint32_t) (*upd->pxlptr ) & (uint32_t) 0x0C) >> 2;
+}
+
+static uint32_t
+upd_pxlget2f4(upd_p upd)
+{
+ upd->pxlget = upd_pxlget2f1;
+ return (uint32_t) (*upd->pxlptr++) & (uint32_t) 0x03;
+}
+
+/* 4 Bit Forward */
+static uint32_t
+upd_pxlget4f1(upd_p upd)
+{
+ upd->pxlget = upd_pxlget4f2;
+ return ((uint32_t) (*upd->pxlptr ) & (uint32_t) 0xF0) >> 4;
+}
+
+static uint32_t
+upd_pxlget4f2(upd_p upd)
+{
+ upd->pxlget = upd_pxlget4f1;
+ return (uint32_t) (*upd->pxlptr++) & (uint32_t) 0x0F;
+}
+
+/* 8 Bit Forward */
+static uint32_t
+upd_pxlget8f(upd_p upd)
+{
+ return (uint32_t) (*upd->pxlptr++);
+}
+
+/* 16 Bit Forward */
+static uint32_t
+upd_pxlget16f(upd_p upd)
+{
+ uint32_t ci = (uint32_t) (*upd->pxlptr++) << 8;
+ ci |= *upd->pxlptr++;
+ return ci;
+}
+
+/* 24 Bit Forward */
+static uint32_t
+upd_pxlget24f(upd_p upd)
+{
+ uint32_t ci = (uint32_t) (*upd->pxlptr++) << 16;
+ ci |= (uint32_t) (*upd->pxlptr++) << 8;
+ ci |= *upd->pxlptr++;
+ return ci;
+}
+
+/* 32 Bit Forward */
+static uint32_t
+upd_pxlget32f(upd_p upd)
+{
+ uint32_t ci = (uint32_t) (*upd->pxlptr++) << 24;
+ ci |= (uint32_t) (*upd->pxlptr++) << 16;
+ ci |= (uint32_t) (*upd->pxlptr++) << 8;
+ ci |= *upd->pxlptr++;
+ return ci;
+}
+
+/* Dummy-Routine */
+
+static uint32_t
+upd_pxlgetnix(upd_p upd)
+{
+ return (uint32_t) 0;
+}
+
+/* Initialize Reverse-Run */
+
+static uint32_t
+upd_pxlrev(upd_p upd)
+{
+ const uint width = upd->pwidth < upd->gswidth ? upd->pwidth : upd->gswidth;
+
+ if(!(upd->pxlptr = upd->gsscan)) {
+
+ upd->pxlget = upd_pxlgetnix;
+
+ } else {
+ uint32_t ofs = (uint32_t) upd->int_a[IA_COLOR_INFO].data[1] * (width-1);
+
+ upd->pxlptr += ofs>>3;
+
+ ofs &= 7;
+
+ switch(upd->int_a[IA_COLOR_INFO].data[1]) {
+ case 1: switch(ofs) {
+ case 0: upd->pxlget = upd_pxlget1r1; break;
+ case 1: upd->pxlget = upd_pxlget1r2; break;
+ case 2: upd->pxlget = upd_pxlget1r3; break;
+ case 3: upd->pxlget = upd_pxlget1r4; break;
+ case 4: upd->pxlget = upd_pxlget1r5; break;
+ case 5: upd->pxlget = upd_pxlget1r6; break;
+ case 6: upd->pxlget = upd_pxlget1r7; break;
+ case 7: upd->pxlget = upd_pxlget1r8; break;
+ } break;
+ case 2: switch(ofs) {
+ case 0: upd->pxlget = upd_pxlget2r1; break;
+ case 2: upd->pxlget = upd_pxlget2r2; break;
+ case 4: upd->pxlget = upd_pxlget2r3; break;
+ case 6: upd->pxlget = upd_pxlget2r4; break;
+ } break;
+ case 4: switch(ofs) {
+ case 0: upd->pxlget = upd_pxlget4r1; break;
+ case 4: upd->pxlget = upd_pxlget4r2; break;
+ } break;
+ case 8: upd->pxlget = upd_pxlget8r; break;
+ case 16:
+ upd->pxlget = upd_pxlget16r;
+ upd->pxlptr += 1;
+ break;
+ case 24:
+ upd->pxlget = upd_pxlget24r;
+ upd->pxlptr += 2;
+ break;
+ case 32:
+ upd->pxlget = upd_pxlget32r;
+ upd->pxlptr += 3;
+ break;
+ default:
+#if UPD_MESSAGES & UPD_M_ERROR
+ errprintf(upd->memory, "upd_pxlrev: unsupported depth (%d)\n",
+ upd->int_a[IA_COLOR_INFO].data[1]);
+#endif
+ upd->pxlget = upd_pxlgetnix;
+ break;
+ }
+ }
+ return (uint32_t) 0;
+}
+
+/* 1 Bit Reverse */
+
+static uint32_t
+upd_pxlget1r1(upd_p upd)
+{
+ upd->pxlget = upd_pxlget1r8;
+ return *upd->pxlptr-- & 0x80 ? (uint32_t) 1 : (uint32_t) 0;
+}
+
+static uint32_t
+upd_pxlget1r2(upd_p upd)
+{
+ upd->pxlget = upd_pxlget1r1;
+ return *upd->pxlptr & 0x40 ? (uint32_t) 1 : (uint32_t) 0;
+}
+
+static uint32_t
+upd_pxlget1r3(upd_p upd)
+{
+ upd->pxlget = upd_pxlget1r2;
+ return *upd->pxlptr & 0x20 ? (uint32_t) 1 : (uint32_t) 0;
+}
+
+static uint32_t
+upd_pxlget1r4(upd_p upd)
+{
+ upd->pxlget = upd_pxlget1r3;
+ return *upd->pxlptr & 0x10 ? (uint32_t) 1 : (uint32_t) 0;
+}
+
+static uint32_t
+upd_pxlget1r5(upd_p upd)
+{
+ upd->pxlget = upd_pxlget1r4;
+ return *upd->pxlptr & 0x08 ? (uint32_t) 1 : (uint32_t) 0;
+}
+
+static uint32_t
+upd_pxlget1r6(upd_p upd)
+{
+ upd->pxlget = upd_pxlget1r5;
+ return *upd->pxlptr & 0x04 ? (uint32_t) 1 : (uint32_t) 0;
+}
+
+static uint32_t
+upd_pxlget1r7(upd_p upd)
+{
+ upd->pxlget = upd_pxlget1r6;
+ return *upd->pxlptr & 0x02 ? (uint32_t) 1 : (uint32_t) 0;
+}
+
+static uint32_t
+upd_pxlget1r8(upd_p upd)
+{
+ upd->pxlget = upd_pxlget1r7;
+ return *upd->pxlptr & 0x01 ? (uint32_t) 1 : (uint32_t) 0;
+}
+
+/* 2 Bit Reverse */
+
+static uint32_t
+upd_pxlget2r1(upd_p upd)
+{
+ upd->pxlget = upd_pxlget2r4;
+ return ((uint32_t) (*upd->pxlptr--) & (uint32_t) 0xC0) >> 6;
+}
+
+static uint32_t
+upd_pxlget2r2(upd_p upd)
+{
+ upd->pxlget = upd_pxlget2r1;
+ return ((uint32_t) (*upd->pxlptr ) & (uint32_t) 0x30) >> 4;
+}
+
+static uint32_t
+upd_pxlget2r3(upd_p upd)
+{
+ upd->pxlget = upd_pxlget2r2;
+ return ((uint32_t) (*upd->pxlptr ) & (uint32_t) 0x0C) >> 2;
+}
+
+static uint32_t
+upd_pxlget2r4(upd_p upd)
+{
+ upd->pxlget = upd_pxlget2r3;
+ return (uint32_t) (*upd->pxlptr ) & (uint32_t) 0x03;
+}
+
+/* 4 Bit Reverse */
+
+static uint32_t
+upd_pxlget4r1(upd_p upd)
+{
+ upd->pxlget = upd_pxlget4r2;
+ return ((uint32_t) (*upd->pxlptr--) & (uint32_t) 0xF0) >> 4;
+}
+
+static uint32_t
+upd_pxlget4r2(upd_p upd)
+{
+ upd->pxlget = upd_pxlget4r1;
+ return (uint32_t) (*upd->pxlptr ) & (uint32_t) 0x0F;
+}
+
+/* 8 Bit Reverse */
+static uint32_t
+upd_pxlget8r(upd_p upd)
+{
+ return (uint32_t) (*upd->pxlptr--);
+}
+
+/* 16 Bit Reverse */
+static uint32_t
+upd_pxlget16r(upd_p upd)
+{
+ uint32_t ci = *upd->pxlptr--;
+ ci |= (uint32_t) (*upd->pxlptr--) << 8;
+ return ci;
+}
+
+/* 24 Bit Reverse */
+static uint32_t
+upd_pxlget24r(upd_p upd)
+{
+ uint32_t ci = *upd->pxlptr--;
+ ci |= (uint32_t) (*upd->pxlptr--) << 8;
+ ci |= (uint32_t) (*upd->pxlptr--) << 16;
+ return ci;
+}
+
+/* 32 Bit Reverse */
+static uint32_t
+upd_pxlget32r(upd_p upd)
+{
+ uint32_t ci = *upd->pxlptr--;
+ ci |= (uint32_t) (*upd->pxlptr--) << 8;
+ ci |= (uint32_t) (*upd->pxlptr--) << 16;
+ ci |= (uint32_t) (*upd->pxlptr--) << 24;
+ return ci;
+}
diff --git a/devices/gdevvglb.c b/devices/gdevvglb.c
new file mode 100644
index 000000000..931fafd51
--- /dev/null
+++ b/devices/gdevvglb.c
@@ -0,0 +1,379 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/*
+ * This is a driver for 386 PCs using vgalib for graphics on the console
+ * display. Note that this driver only works with 16-color modes.
+ *
+ * Written by Sigfrid Lundberg, siglun@euler.teorekol.lu.se.
+ * Modified by Erik Talvola, talvola@gnu.ai.mit.edu
+ * Updated 9/28/96 by L. Peter Deutsch, ghost@aladdin.com: allow setting
+ * the display mode as a device parameter.
+ * Updated 2/13/97 by ghost@aladdin.com: make the device identify itself
+ * as a page device.
+ * Updated 5/2/97 by ghost@aladdin.com: copy_mono computed some parameters
+ * before doing fit_copy.
+ * Update 1997-06-28 by ghost@aladdin.com: get_bits wasn't implemented.
+ */
+
+#include "gx.h"
+#include "gserrors.h"
+#include "gsparam.h"
+#include "gxdevice.h"
+#include "gdevpccm.h"
+
+#include <errno.h>
+#include <vga.h>
+
+typedef struct gx_device_vgalib {
+ gx_device_common;
+ int display_mode;
+} gx_device_vgalib;
+
+#define vga_dev ((gx_device_vgalib *)dev)
+
+#define XDPI 60 /* to get a more-or-less square aspect ratio */
+#define YDPI 60
+
+#ifndef A4 /*Letter size */
+#define YSIZE (20.0 * YDPI / 2.5)
+#define XSIZE (8.5 / 11)*YSIZE /* 8.5 x 11 inch page, by default */
+#else /* A4 paper */
+#define XSIZE 8.27
+#define YSIZE 11.69
+#endif
+
+static dev_proc_open_device(vgalib_open);
+static dev_proc_close_device(vgalib_close);
+static dev_proc_map_rgb_color(vgalib_map_rgb_color);
+static dev_proc_map_color_rgb(vgalib_map_color_rgb);
+static dev_proc_fill_rectangle(vgalib_fill_rectangle);
+static dev_proc_tile_rectangle(vgalib_tile_rectangle);
+static dev_proc_copy_mono(vgalib_copy_mono);
+static dev_proc_copy_color(vgalib_copy_color);
+static dev_proc_get_bits(vgalib_get_bits);
+static dev_proc_get_params(vgalib_get_params);
+static dev_proc_put_params(vgalib_put_params);
+
+const gx_device_vgalib gs_vgalib_device =
+{
+ std_device_std_body(gx_device_vgalib, 0, "vgalib",
+ 0, 0, 1, 1),
+ {vgalib_open,
+ NULL, /* get_initial_matrix */
+ NULL, /* sync_output */
+ NULL, /* output_page */
+ vgalib_close,
+ vgalib_map_rgb_color,
+ vgalib_map_color_rgb,
+ vgalib_fill_rectangle,
+ vgalib_tile_rectangle,
+ vgalib_copy_mono,
+ vgalib_copy_color,
+ NULL, /* draw_line (obsolete) */
+ vgalib_get_bits,
+ vgalib_get_params,
+ vgalib_put_params,
+ NULL, /* map_cmyk_color */
+ NULL, /* get_xfont_procs */
+ NULL, /* get_xfont_device */
+ NULL, /* map_rgb_alpha_color */
+ gx_page_device_get_page_device
+ },
+ -1 /* display_mode */
+};
+
+static int
+vgalib_open(gx_device * dev)
+{
+ int VGAMODE = vga_dev->display_mode;
+ int width = dev->width, height = dev->height;
+
+ if (VGAMODE == -1)
+ VGAMODE = vga_getdefaultmode();
+ if (VGAMODE == -1)
+ vga_setmode(G640x480x16);
+ else
+ vga_setmode(VGAMODE);
+ vga_clear();
+ if (width == 0)
+ width = vga_getxdim() + 1;
+ if (height == 0)
+ height = vga_getydim() + 1;
+
+ /*vgalib provides no facilities for finding out aspect ratios */
+ if (dev->y_pixels_per_inch == 1) {
+ dev->y_pixels_per_inch = height / 11.0;
+ dev->x_pixels_per_inch = dev->y_pixels_per_inch;
+ }
+ gx_device_set_width_height(dev, width, height);
+
+ /* Find out if the device supports color */
+ /* (default initialization is monochrome). */
+ /* We only recognize 16-color devices right now. */
+ if (vga_getcolors() > 1) {
+ int index;
+
+ static const gx_device_color_info vgalib_16color = dci_pc_4bit;
+
+ dev->color_info = vgalib_16color;
+
+ for (index = 0; index < 16; ++index) {
+ gx_color_value rgb[3];
+
+ (*dev_proc(dev, map_color_rgb)) (dev, (gx_color_index) index, rgb);
+#define cv2pv(cv) ((cv) >> (gx_color_value_bits - 8))
+ vga_setpalette(index, cv2pv(rgb[0]), cv2pv(rgb[1]), cv2pv(rgb[2]));
+#undef cv2pv
+ }
+ }
+ return 0;
+}
+
+static int
+vgalib_close(gx_device * dev)
+{
+ vga_setmode(TEXT);
+ return 0;
+}
+
+static gx_color_index
+vgalib_map_rgb_color(gx_device * dev, const gx_color_value cv[])
+{
+ return pc_4bit_map_rgb_color(dev, cv);
+}
+
+static int
+vgalib_map_color_rgb(gx_device * dev, gx_color_index index,
+ unsigned short rgb[3])
+{
+ return pc_4bit_map_color_rgb(dev, index, rgb);
+}
+
+static int
+vgalib_tile_rectangle(gx_device * dev, const gx_tile_bitmap * tile,
+ int x, int y, int w, int h, gx_color_index czero,
+ gx_color_index cone, int px, int py)
+{
+ if (czero != gx_no_color_index && cone != gx_no_color_index) {
+ vgalib_fill_rectangle(dev, x, y, w, h, czero);
+ czero = gx_no_color_index;
+ }
+ return gx_default_tile_rectangle(dev, tile, x, y, w, h, czero, cone, px,
+ py);
+}
+
+static int
+vgalib_fill_rectangle(gx_device * dev, int x, int y, int w, int h,
+ gx_color_index color)
+{
+ int i, j;
+
+ fit_fill(dev, x, y, w, h);
+ vga_setcolor((int)color);
+ if ((w | h) > 3) { /* Draw larger rectangles as lines. */
+ if (w > h)
+ for (i = y; i < y + h; ++i)
+ vga_drawline(x, i, x + w - 1, i);
+ else
+ for (j = x; j < x + w; ++j)
+ vga_drawline(j, y, j, y + h - 1);
+ } else { /* Draw small rectangles point-by-point. */
+ for (i = y; i < y + h; i++)
+ for (j = x; j < x + w; j++)
+ vga_drawpixel(j, i);
+ }
+ return 0;
+}
+
+static int
+vgalib_copy_mono(gx_device * dev, const byte * base, int sourcex,
+ int raster, gx_bitmap_id id, int x, int y, int width,
+ int height, gx_color_index zero, gx_color_index one)
+{
+ const byte *ptr_line;
+ int left_bit, dest_y, end_x;
+ int invert = 0;
+ int color;
+
+ fit_copy(dev, base, sourcex, raster, id, x, y, width, height);
+ ptr_line = base + (sourcex >> 3);
+ left_bit = 0x80 >> (sourcex & 7);
+ dest_y = y, end_x = x + width;
+
+ if (zero == gx_no_color_index) {
+ if (one == gx_no_color_index)
+ return 0;
+ color = (int)one;
+ } else {
+ if (one == gx_no_color_index) {
+ color = (int)zero;
+ invert = -1;
+ } else { /* Pre-clear the rectangle to zero */
+ vgalib_fill_rectangle(dev, x, y, width, height, zero);
+ color = (int)one;
+ }
+ }
+
+ vga_setcolor(color);
+ while (height--) { /* for each line */
+ const byte *ptr_source = ptr_line;
+ register int dest_x = x;
+ register int bit = left_bit;
+
+ while (dest_x < end_x) { /* for each bit in the line */
+ if ((*ptr_source ^ invert) & bit)
+ vga_drawpixel(dest_x, dest_y);
+ dest_x++;
+ if ((bit >>= 1) == 0)
+ bit = 0x80, ptr_source++;
+ }
+
+ dest_y++;
+ ptr_line += raster;
+ }
+ return 0;
+}
+
+/* Copy a color pixel map. This is just like a bitmap, except that */
+/* each pixel takes 4 bits instead of 1 when device driver has color. */
+static int
+vgalib_copy_color(gx_device * dev, const byte * base, int sourcex,
+ int raster, gx_bitmap_id id, int x, int y,
+ int width, int height)
+{
+
+ fit_copy(dev, base, sourcex, raster, id, x, y, width, height);
+
+ if (gx_device_has_color(dev)) { /* color device, four bits per pixel */
+ const byte *line = base + (sourcex >> 1);
+ int dest_y = y, end_x = x + width;
+
+ if (width <= 0)
+ return 0;
+ while (height--) { /* for each line */
+ const byte *source = line;
+ register int dest_x = x;
+
+ if (sourcex & 1) { /* odd nibble first */
+ int color = *source++ & 0xf;
+
+ vga_setcolor(color);
+ vga_drawpixel(dest_x, dest_y);
+ dest_x++;
+ }
+ /* Now do full bytes */
+ while (dest_x < end_x) {
+ int color = *source >> 4;
+
+ vga_setcolor(color);
+ vga_drawpixel(dest_x, dest_y);
+ dest_x++;
+
+ if (dest_x < end_x) {
+ color = *source++ & 0xf;
+ vga_setcolor(color);
+ vga_drawpixel(dest_x, dest_y);
+ dest_x++;
+ }
+ }
+
+ dest_y++;
+ line += raster;
+ }
+ } else { /* monochrome device: one bit per pixel */
+ /* bitmap is the same as bgi_copy_mono: one bit per pixel */
+ vgalib_copy_mono(dev, base, sourcex, raster, id, x, y, width, height,
+ (gx_color_index) 0, (gx_color_index) 7);
+ }
+
+ return 0;
+}
+
+/* Read bits back from the device. */
+static int
+vgalib_get_bits(gx_device * dev, int y, byte * data, byte ** actual_data)
+{
+ int x;
+ byte *dest = data;
+ int b = 0;
+ int depth = dev->color_info.depth; /* 1 or 4 */
+ int mask = (1 << depth) - 1;
+ int left = 8;
+
+ if (actual_data)
+ *actual_data = data;
+ for (x = 0; x < dev->width; ++x) {
+ int color = vga_getpixel(x, y);
+
+ if ((left -= depth) < 0)
+ *dest++ = b, b = 0, left += 8;
+ b += (color & mask) << left;
+ }
+ if (left < 8)
+ *dest = b;
+ return 0;
+}
+
+/* Get/put the display mode parameter. */
+static int
+vgalib_get_params(gx_device * dev, gs_param_list * plist)
+{
+ int code = gx_default_get_params(dev, plist);
+
+ if (code < 0)
+ return code;
+ return param_write_int(plist, "DisplayMode", &vga_dev->display_mode);
+}
+static int
+vgalib_put_params(gx_device * dev, gs_param_list * plist)
+{
+ int ecode = 0;
+ int code;
+ int imode = vga_dev->display_mode;
+ const char *param_name;
+
+ switch (code = param_read_int(plist, (param_name = "DisplayMode"), &imode)) {
+ default:
+ ecode = code;
+ param_signal_error(plist, param_name, ecode);
+ case 0:
+ case 1:
+ break;
+ }
+
+ if (ecode < 0)
+ return ecode;
+ code = gx_default_put_params(dev, plist);
+ if (code < 0)
+ return code;
+
+ if (imode != vga_dev->display_mode) {
+ if (dev->is_open)
+ gs_closedevice(dev);
+ vga_dev->display_mode = imode;
+ }
+ return 0;
+}
+
+#ifdef GS_DEVS_SHARED
+extern void gs_lib_register_device(const gx_device *dev);
+void
+gs_shared_init(void)
+{
+ gs_lib_register_device(&gs_vgalib_device);
+}
+#endif
diff --git a/devices/gdevwddb.c b/devices/gdevwddb.c
new file mode 100644
index 000000000..8ebd5d049
--- /dev/null
+++ b/devices/gdevwddb.c
@@ -0,0 +1,619 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/*
+ * Microsoft Windows 3.n driver for Ghostscript,
+ * using device-dependent bitmap.
+ *
+ * Original version by Russell Lang and Maurice Castro with help from
+ * Programming Windows, 2nd Ed., Charles Petzold, Microsoft Press;
+ * created from gdevbgi.c and gnuplot/term/win.trm 5th June 1992.
+ * Extensively modified by L. Peter Deutsch, Aladdin Enterprises.
+ */
+#include "gdevmswn.h"
+
+/* Make sure we cast to the correct structure type. */
+typedef struct gx_device_win_ddb_s gx_device_win_ddb;
+
+#undef wdev
+#define wdev ((gx_device_win_ddb *)dev)
+
+/* Forward references */
+static void near win_addtool(gx_device_win_ddb *, int);
+static void near win_maketools(gx_device_win_ddb *, HDC);
+static void near win_destroytools(gx_device_win_ddb *);
+
+/* Device procedures */
+
+/* See gxdevice.h for the definitions of the procedures. */
+static dev_proc_open_device(win_ddb_open);
+static dev_proc_close_device(win_ddb_close);
+static dev_proc_map_rgb_color(win_ddb_map_rgb_color);
+static dev_proc_fill_rectangle(win_ddb_fill_rectangle);
+static dev_proc_tile_rectangle(win_ddb_tile_rectangle);
+static dev_proc_copy_mono(win_ddb_copy_mono);
+static dev_proc_copy_color(win_ddb_copy_color);
+
+/* Windows-specific procedures */
+static win_proc_copy_to_clipboard(win_ddb_copy_to_clipboard);
+static win_proc_repaint(win_ddb_repaint);
+static win_proc_alloc_bitmap(win_ddb_alloc_bitmap);
+static win_proc_free_bitmap(win_ddb_free_bitmap);
+
+/* The device descriptor */
+struct gx_device_win_ddb_s {
+ gx_device_common;
+ gx_device_win_common;
+
+ /* Handles */
+
+ HBITMAP FAR hbitmap;
+ HDC FAR hdcbit;
+ HPEN hpen, *hpens;
+ uint hpensize;
+ HBRUSH hbrush, *hbrushs;
+ uint hbrushsize;
+#define select_brush(color)\
+ if (wdev->hbrush != wdev->hbrushs[color])\
+ { wdev->hbrush = wdev->hbrushs[color];\
+ SelectObject(wdev->hdcbit,wdev->hbrush);\
+ }
+ HPALETTE hpalette;
+ LPLOGPALETTE lpalette;
+
+ /* A staging bitmap for copy_mono. */
+ /* We want one big enough to handle the standard 16x16 halftone; */
+ /* this is also big enough for ordinary-size characters. */
+
+#define bmWidthBytes 4 /* must be even */
+#define bmWidthBits (bmWidthBytes * 8)
+#define bmHeight 32
+ HBITMAP FAR hbmmono;
+ HDC FAR hdcmono;
+ gx_bitmap_id bm_id;
+};
+static const gx_device_procs win_ddb_procs =
+{
+ win_ddb_open,
+ NULL, /* get_initial_matrix */
+ win_sync_output,
+ win_output_page,
+ win_ddb_close,
+ win_ddb_map_rgb_color,
+ win_map_color_rgb,
+ win_ddb_fill_rectangle,
+ win_ddb_tile_rectangle,
+ win_ddb_copy_mono,
+ win_ddb_copy_color,
+ NULL, /* draw_line */
+ NULL, /* get_bits */
+ win_get_params,
+ win_put_params,
+ NULL, /* map_cmyk_color */
+ win_get_xfont_procs
+};
+gx_device_win_ddb far_data gs_mswin_device =
+{
+ std_device_std_body(gx_device_win_ddb, &win_ddb_procs, "mswin",
+ INITIAL_WIDTH, INITIAL_HEIGHT, /* win_open() fills these in later */
+ INITIAL_RESOLUTION, INITIAL_RESOLUTION /* win_open() fills these in later */
+ ),
+ {0}, /* std_procs */
+ 0, /* BitsPerPixel - not used */
+ 5000, /* UpdateInterval (in milliseconds) */
+ "\0", /* GSVIEW_STR */
+ 0, /* not a DLL device */
+ 2, /* nColors */
+ 0, /* mapped_color_flags */
+ win_ddb_copy_to_clipboard,
+ win_ddb_repaint,
+ win_ddb_alloc_bitmap,
+ win_ddb_free_bitmap
+};
+
+/* Open the win_ddb driver */
+static int
+win_ddb_open(gx_device * dev)
+{
+ int code = win_open(dev);
+ HDC hdc;
+
+ if (code < 0)
+ return code;
+
+ if (wdev->BitsPerPixel > 8)
+ return gs_error_limitcheck; /* don't support 24 bit/pixel */
+
+ /* Create the backing bitmap. */
+ code = win_ddb_alloc_bitmap((gx_device_win *) dev, dev);
+ if (code < 0)
+ return code;
+
+ /* Create the bitmap and DC for copy_mono. */
+ hdc = GetDC(wdev->hwndimg);
+ wdev->hbmmono = CreateBitmap(bmWidthBits, bmHeight, 1, 1, NULL);
+ wdev->hdcmono = CreateCompatibleDC(hdc);
+ if (wdev->hbmmono == NULL || wdev->hdcmono == NULL) {
+ win_ddb_free_bitmap((gx_device_win *) dev);
+ ReleaseDC(wdev->hwndimg, hdc);
+ return win_nomemory();
+ }
+ SetMapMode(wdev->hdcmono, GetMapMode(hdc));
+ SelectObject(wdev->hdcmono, wdev->hbmmono);
+ wdev->bm_id = gx_no_bitmap_id;
+ ReleaseDC(wdev->hwndimg, hdc);
+
+ /* create palette and tools for bitmap */
+ if ((wdev->lpalette = win_makepalette((gx_device_win *) dev))
+ == (LPLOGPALETTE) NULL)
+ return win_nomemory();
+ wdev->hpalette = CreatePalette(wdev->lpalette);
+ (void)SelectPalette(wdev->hdcbit, wdev->hpalette, NULL);
+ RealizePalette(wdev->hdcbit);
+ win_maketools(wdev, wdev->hdcbit);
+
+ wdev->hdctext = wdev->hdcbit; /* draw text here */
+
+ return 0;
+}
+
+/* Close the win_ddb driver */
+static int
+win_ddb_close(gx_device * dev)
+{
+ /* Free resources */
+
+ win_destroytools(wdev);
+ DeleteDC(wdev->hdcmono);
+ win_ddb_free_bitmap((gx_device_win *) dev);
+ DeleteObject(wdev->hpalette);
+ DeleteObject(wdev->hbmmono);
+ gs_free((char *)(wdev->lpalette), 1, sizeof(LOGPALETTE) +
+ (1 << (wdev->color_info.depth)) * sizeof(PALETTEENTRY),
+ "win_ddb_close");
+
+ return win_close(dev);
+}
+
+/* Map a r-g-b color to the colors available under Windows */
+static gx_color_index
+win_ddb_map_rgb_color(gx_device * dev, gx_color_value r, gx_color_value g,
+ gx_color_value b)
+{
+ int i = wdev->nColors;
+ gx_color_index color = win_map_rgb_color(dev, r, g, b);
+ LPLOGPALETTE lipal = wdev->limgpalette;
+ LPLOGPALETTE lpal = wdev->lpalette;
+
+ if (color != i)
+ return color;
+
+ /* We just added a color to the window palette. */
+ /* Add it to the bitmap palette as well. */
+
+ DeleteObject(wdev->hpalette);
+ lpal->palPalEntry[i].peFlags = NULL;
+ lpal->palPalEntry[i].peRed = lipal->palPalEntry[i].peRed;
+ lpal->palPalEntry[i].peGreen = lipal->palPalEntry[i].peGreen;
+ lpal->palPalEntry[i].peBlue = lipal->palPalEntry[i].peBlue;
+ lpal->palNumEntries = i + 1;
+ wdev->hpalette = CreatePalette(lpal);
+ (void)SelectPalette(wdev->hdcbit, wdev->hpalette, NULL);
+ RealizePalette(wdev->hdcbit);
+ win_addtool(wdev, i);
+
+ return color;
+}
+
+/* Macro for filling a rectangle with a color. */
+/* Note that it starts with a declaration. */
+#define fill_rect(x, y, w, h, color)\
+RECT rect;\
+rect.left = x, rect.top = y;\
+rect.right = x + w, rect.bottom = y + h;\
+FillRect(wdev->hdcbit, &rect, wdev->hbrushs[(int)color])
+
+/* Fill a rectangle. */
+static int
+win_ddb_fill_rectangle(gx_device * dev, int x, int y, int w, int h,
+ gx_color_index color)
+{
+ fit_fill(dev, x, y, w, h);
+ /* Use PatBlt for filling. Special-case black. */
+ if (color == 0)
+ PatBlt(wdev->hdcbit, x, y, w, h, rop_write_0s);
+ else {
+ select_brush((int)color);
+ PatBlt(wdev->hdcbit, x, y, w, h, rop_write_pattern);
+ }
+ win_update((gx_device_win *) dev);
+
+ return 0;
+}
+
+/* Tile a rectangle. If neither color is transparent, */
+/* pre-clear the rectangle to color0 and just tile with color1. */
+/* This is faster because of how win_copy_mono is implemented. */
+/* Note that this also does the right thing for colored tiles. */
+static int
+win_ddb_tile_rectangle(gx_device * dev, const gx_tile_bitmap * tile,
+ int x, int y, int w, int h, gx_color_index czero, gx_color_index cone,
+ int px, int py)
+{
+ fit_fill(dev, x, y, w, h);
+ if (czero != gx_no_color_index && cone != gx_no_color_index) {
+ fill_rect(x, y, w, h, czero);
+ czero = gx_no_color_index;
+ }
+ if (tile->raster == bmWidthBytes && tile->size.y <= bmHeight &&
+ (px | py) == 0 && cone != gx_no_color_index
+ ) { /* We can do this much more efficiently */
+ /* by using the internal algorithms of copy_mono */
+ /* and gx_default_tile_rectangle. */
+ int width = tile->size.x;
+ int height = tile->size.y;
+ int rwidth = tile->rep_width;
+ int irx = ((rwidth & (rwidth - 1)) == 0 ? /* power of 2 */
+ x & (rwidth - 1) :
+ x % rwidth);
+ int ry = y % tile->rep_height;
+ int icw = width - irx;
+ int ch = height - ry;
+ int ex = x + w, ey = y + h;
+ int fex = ex - width, fey = ey - height;
+ int cx, cy;
+
+ select_brush((int)cone);
+
+ if (tile->id != wdev->bm_id || tile->id == gx_no_bitmap_id) {
+ wdev->bm_id = tile->id;
+ SetBitmapBits(wdev->hbmmono,
+ (DWORD) (bmWidthBytes * tile->size.y),
+ (BYTE *) tile->data);
+ }
+#define copy_tile(srcx, srcy, tx, ty, tw, th)\
+ BitBlt(wdev->hdcbit, tx, ty, tw, th, wdev->hdcmono, srcx, srcy, rop_write_at_1s)
+
+ if (ch > h)
+ ch = h;
+ for (cy = y;;) {
+ if (w <= icw)
+ copy_tile(irx, ry, x, cy, w, ch);
+ else {
+ copy_tile(irx, ry, x, cy, icw, ch);
+ cx = x + icw;
+ while (cx <= fex) {
+ copy_tile(0, ry, cx, cy, width, ch);
+ cx += width;
+ }
+ if (cx < ex) {
+ copy_tile(0, ry, cx, cy, ex - cx, ch);
+ }
+ }
+ if ((cy += ch) >= ey)
+ break;
+ ch = (cy > fey ? ey - cy : height);
+ ry = 0;
+ }
+
+ win_update((gx_device_win *) dev);
+ return 0;
+ }
+ return gx_default_tile_rectangle(dev, tile, x, y, w, h, czero, cone, px, py);
+}
+
+/* Copy a monochrome bitmap. The colors are given explicitly. */
+/* Color = gx_no_color_index means transparent (no effect on the image). */
+static int
+win_ddb_copy_mono(gx_device * dev,
+ const byte * base, int sourcex, int raster, gx_bitmap_id id,
+ int x, int y, int w, int h,
+ gx_color_index zero, gx_color_index one)
+{
+ int endx;
+ const byte *ptr_line;
+ int width_bytes, height;
+ DWORD rop = rop_write_at_1s;
+ int color;
+ BYTE aBit[bmWidthBytes * bmHeight];
+ BYTE *aptr = aBit;
+
+ fit_copy(dev, base, sourcex, raster, id, x, y, w, h);
+
+ if (sourcex & ~7) {
+ base += sourcex >> 3;
+ sourcex &= 7;
+ }
+ /* Break up large transfers into smaller ones. */
+ while ((endx = sourcex + w) > bmWidthBits) {
+ int lastx = (endx - 1) & -bmWidthBits;
+ int subw = endx - lastx;
+ int code = win_ddb_copy_mono(dev, base, lastx,
+ raster, gx_no_bitmap_id,
+ x + lastx - sourcex, y,
+ subw, h, zero, one);
+
+ if (code < 0)
+ return code;
+ w -= subw;
+ }
+ while (h > bmHeight) {
+ int code;
+
+ h -= bmHeight;
+ code = win_ddb_copy_mono(dev, base + h * raster, sourcex,
+ raster, gx_no_bitmap_id,
+ x, y + h, w, bmHeight, zero, one);
+ if (code < 0)
+ return code;
+ }
+
+ width_bytes = (sourcex + w + 7) >> 3;
+ ptr_line = base;
+
+ if (zero == gx_no_color_index) {
+ if (one == gx_no_color_index)
+ return 0;
+ color = (int)one;
+ if (color == 0)
+ rop = rop_write_0_at_1s;
+ else
+ select_brush(color);
+ } else {
+ if (one == gx_no_color_index) {
+ color = (int)zero;
+ rop = rop_write_at_0s;
+ } else { /* Pre-clear the rectangle to zero */
+ fill_rect(x, y, w, h, zero);
+ color = (int)one;
+ }
+ select_brush(color);
+ }
+
+ if (id != wdev->bm_id || id == gx_no_bitmap_id) {
+ wdev->bm_id = id;
+ if (raster == bmWidthBytes) { /* We can do the whole thing in a single transfer! */
+ SetBitmapBits(wdev->hbmmono,
+ (DWORD) (bmWidthBytes * h),
+ (BYTE *) base);
+ } else {
+ for (height = h; height--;
+ ptr_line += raster, aptr += bmWidthBytes
+ ) { /* Pack the bits into the bitmap. */
+ switch (width_bytes) {
+ default:
+ memcpy(aptr, ptr_line, width_bytes);
+ break;
+ case 4:
+ aptr[3] = ptr_line[3];
+ case 3:
+ aptr[2] = ptr_line[2];
+ case 2:
+ aptr[1] = ptr_line[1];
+ case 1:
+ aptr[0] = ptr_line[0];
+ }
+ }
+ SetBitmapBits(wdev->hbmmono,
+ (DWORD) (bmWidthBytes * h),
+ &aBit[0]);
+ }
+ }
+ BitBlt(wdev->hdcbit, x, y, w, h, wdev->hdcmono, sourcex, 0, rop);
+ win_update((gx_device_win *) dev);
+ return 0;
+}
+
+/* Copy a color pixel map. This is just like a bitmap, except that */
+/* each pixel takes 8 or 4 bits instead of 1 when device driver has color. */
+static int
+win_ddb_copy_color(gx_device * dev,
+ const byte * base, int sourcex, int raster, gx_bitmap_id id,
+ int x, int y, int w, int h)
+{
+ fit_copy(dev, base, sourcex, raster, id, x, y, w, h);
+
+ if (gx_device_has_color(dev)) {
+ switch (dev->color_info.depth) {
+ case 8:
+ {
+ int xi, yi;
+ int skip = raster - w;
+ const byte *sptr = base + sourcex;
+
+ if (w <= 0)
+ return 0;
+ if (x < 0 || x + w > dev->width)
+ return_error(gs_error_rangecheck);
+ for (yi = y; yi - y < h; yi++) {
+ for (xi = x; xi - x < w; xi++) {
+ int color = *sptr++;
+
+ SetPixel(wdev->hdcbit, xi, yi, PALETTEINDEX(color));
+ }
+ sptr += skip;
+ }
+ }
+ break;
+ case 4:
+ { /* color device, four bits per pixel */
+ const byte *line = base + (sourcex >> 1);
+ int dest_y = y, end_x = x + w;
+
+ if (w <= 0)
+ return 0;
+ while (h--) { /* for each line */
+ const byte *source = line;
+ register int dest_x = x;
+
+ if (sourcex & 1) { /* odd nibble first */
+ int color = *source++ & 0xf;
+
+ SetPixel(wdev->hdcbit, dest_x, dest_y, PALETTEINDEX(color));
+ dest_x++;
+ }
+ /* Now do full bytes */
+ while (dest_x < end_x) {
+ int color = *source >> 4;
+
+ SetPixel(wdev->hdcbit, dest_x, dest_y, PALETTEINDEX(color));
+ dest_x++;
+ if (dest_x < end_x) {
+ color = *source++ & 0xf;
+ SetPixel(wdev->hdcbit, dest_x, dest_y, PALETTEINDEX(color));
+ dest_x++;
+ }
+ }
+ dest_y++;
+ line += raster;
+ }
+ }
+ break;
+ default:
+ return (-1); /* panic */
+ }
+ } else
+ /* monochrome device: one bit per pixel */
+ { /* bitmap is the same as win_copy_mono: one bit per pixel */
+ win_ddb_copy_mono(dev, base, sourcex, raster, id, x, y, w, h,
+ (gx_color_index) 0,
+ (gx_color_index) (dev->color_info.depth == 8 ? 63 : dev->color_info.max_gray));
+ }
+ win_update((gx_device_win *) dev);
+ return 0;
+}
+
+/* ------ Windows-specific device procedures ------ */
+
+/* Copy the bitmap to the clipboard. */
+static void
+win_ddb_copy_to_clipboard(gx_device_win * dev)
+{ /* make somewhere to put it and copy */
+ HDC hdcbit = wdev->hdcbit;
+ HBITMAP bitmap = CreateCompatibleBitmap(hdcbit, dev->width,
+ dev->height);
+
+ if (bitmap) {
+ /* there is enough memory and the bitmaps OK */
+ HDC mem = CreateCompatibleDC(hdcbit);
+
+ SelectObject(mem, bitmap);
+ BitBlt(mem, 0, 0, dev->width, dev->height,
+ hdcbit, 0, 0, SRCCOPY);
+ DeleteDC(mem);
+ /* copy it to the clipboard */
+ OpenClipboard(wdev->hwndimg);
+ EmptyClipboard();
+ SetClipboardData(CF_BITMAP, bitmap);
+ SetClipboardData(CF_PALETTE, CreatePalette(wdev->limgpalette));
+ CloseClipboard();
+ }
+}
+
+/* Repaint a section of the window. */
+static void
+win_ddb_repaint(gx_device_win * dev, HDC hdc, int dx, int dy, int wx, int wy,
+ int sx, int sy)
+{
+ BitBlt(hdc, dx, dy, wx, wy, wdev->hdcbit, sx, sy, SRCCOPY);
+}
+
+/* Allocate the backing bitmap. */
+static int
+win_ddb_alloc_bitmap(gx_device_win * dev, gx_device * param_dev)
+{
+ HDC hdc;
+ int i;
+
+ hdc = GetDC(wdev->hwndimg);
+ for (i = 0;; i++) {
+ wdev->hbitmap = CreateCompatibleBitmap(hdc,
+ param_dev->width, param_dev->height);
+ if (wdev->hbitmap != (HBITMAP) NULL)
+ break;
+ if (i >= 4) {
+ ReleaseDC(wdev->hwndimg, hdc);
+ return win_nomemory();
+ }
+ errprintf(param_dev->memory, "\nNot enough memory for bitmap. Halving resolution... ");
+ param_dev->x_pixels_per_inch /= 2;
+ param_dev->y_pixels_per_inch /= 2;
+ param_dev->width /= 2;
+ param_dev->height /= 2;
+ }
+
+ wdev->hdcbit = CreateCompatibleDC(hdc); /* create Device Context for drawing */
+ SelectObject(wdev->hdcbit, wdev->hbitmap);
+ ReleaseDC(wdev->hwndimg, hdc);
+ return 0;
+}
+
+/* Free the backing bitmap. */
+static void
+win_ddb_free_bitmap(gx_device_win * dev)
+{
+ DeleteDC(wdev->hdcbit); /* must do this first */
+ DeleteObject(wdev->hbitmap);
+}
+
+/* ------ Internal routines ------ */
+
+#undef wdev
+
+static void near
+win_addtool(gx_device_win_ddb * wdev, int i)
+{
+ wdev->hpens[i] = CreatePen(PS_SOLID, 1, PALETTEINDEX(i));
+ wdev->hbrushs[i] = CreateSolidBrush(PALETTEINDEX(i));
+}
+
+static void near
+win_maketools(gx_device_win_ddb * wdev, HDC hdc)
+{
+ int i;
+
+ wdev->hpensize = (1 << (wdev->color_info.depth)) * sizeof(HPEN);
+ wdev->hpens = (HPEN *) gs_malloc(wdev->memory, 1, wdev->hpensize,
+ "win_maketools(pens)");
+ wdev->hbrushsize = (1 << (wdev->color_info.depth)) * sizeof(HBRUSH);
+ wdev->hbrushs = (HBRUSH *) gs_malloc(wdev->memory, 1, wdev->hbrushsize,
+ "win_maketools(brushes)");
+ if (wdev->hpens && wdev->hbrushs) {
+ for (i = 0; i < wdev->nColors; i++)
+ win_addtool(wdev, i);
+
+ wdev->hpen = wdev->hpens[0];
+ SelectObject(hdc, wdev->hpen);
+
+ wdev->hbrush = wdev->hbrushs[0];
+ SelectObject(hdc, wdev->hbrush);
+ }
+}
+
+static void near
+win_destroytools(gx_device_win_ddb * wdev)
+{
+ int i;
+
+ for (i = 0; i < wdev->nColors; i++) {
+ DeleteObject(wdev->hpens[i]);
+ DeleteObject(wdev->hbrushs[i]);
+ }
+ gs_free(wdev->memory, (char *)wdev->hbrushs, 1, wdev->hbrushsize,
+ "win_destroytools(brushes)");
+ gs_free(wdev->memory, (char *)wdev->hpens, 1, wdev->hpensize,
+ "win_destroytools(pens)");
+}
diff --git a/devices/gdevwdib.c b/devices/gdevwdib.c
new file mode 100644
index 000000000..30131c993
--- /dev/null
+++ b/devices/gdevwdib.c
@@ -0,0 +1,736 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* MS Windows 3.n driver for Ghostscript using a DIB for buffering. */
+#include "gdevmswn.h"
+#include "gxdevmem.h"
+#include "gsdll.h"
+#include "gsdllwin.h"
+#include "gdevkrnlsclass.h" /* 'standard' built in subclasses, currently First/Last Page and obejct filter */
+
+#ifdef __WIN32__
+# define USE_SEGMENTS 0
+#else
+# define USE_SEGMENTS 1
+#endif
+
+/* Make sure we cast to the correct structure type. */
+typedef struct gx_device_win_dib_s gx_device_win_dib;
+
+#undef wdev
+#define wdev ((gx_device_win_dib *)dev)
+
+/* Device procedures */
+
+/* See gxdevice.h for the definitions of the procedures. */
+static dev_proc_open_device(win_dib_open);
+static dev_proc_get_initial_matrix(win_dib_get_initial_matrix);
+static dev_proc_close_device(win_dib_close);
+static dev_proc_fill_rectangle(win_dib_fill_rectangle);
+static dev_proc_copy_mono(win_dib_copy_mono);
+static dev_proc_copy_color(win_dib_copy_color);
+static dev_proc_get_bits(win_dib_get_bits);
+static dev_proc_put_params(win_dib_put_params);
+
+/* Windows-specific procedures */
+static win_proc_repaint(win_dib_repaint);
+static win_proc_alloc_bitmap(win_dib_alloc_bitmap);
+static win_proc_free_bitmap(win_dib_free_bitmap);
+
+/* The device descriptor */
+struct gx_device_win_dib_s {
+ gx_device_common;
+ gx_device_win_common;
+
+#if USE_SEGMENTS
+ /* The following help manage the division of the DIB */
+ /* into 64K segments. Each block of y_block scan lines */
+ /* starting at y_base mod 64K falls in a single segment. */
+ /* Since the raster is a power of 2, y_block is a power of 2. */
+
+ int y_block;
+ int y_base;
+ int y_mask; /* y_block - 1 */
+#endif /* USE_SEGMENTS */
+
+ HGLOBAL hmdata;
+#ifdef __WIN32__
+ HANDLE hmtx;
+#endif
+ int lock_count;
+ gx_device_memory mdev;
+};
+static const gx_device_procs win_dib_procs =
+{
+ win_dib_open,
+ win_dib_get_initial_matrix,
+ win_sync_output,
+ win_output_page,
+ win_dib_close,
+ win_map_rgb_color,
+ win_map_color_rgb,
+ win_dib_fill_rectangle,
+ NULL, /* tile_rectangle */
+ win_dib_copy_mono,
+ win_dib_copy_color,
+ NULL, /* draw_line */
+ win_dib_get_bits /* NULL */ , /* get_bits */
+ win_get_params,
+ win_dib_put_params,
+ NULL, /* map_cmyk_color */
+ win_get_xfont_procs,
+ NULL, /* get_xfont_device */
+ NULL, /* map_rgb_alpha_color */
+ gx_page_device_get_page_device
+};
+gx_device_win_dib far_data gs_mswindll_device =
+{
+ std_device_std_body(gx_device_win_dib, &win_dib_procs, "mswindll",
+ INITIAL_WIDTH, INITIAL_HEIGHT,/* win_open() fills these in later */
+ INITIAL_RESOLUTION, INITIAL_RESOLUTION /* win_open() fills these in later */
+ ),
+ {0}, /* std_procs */
+ 0, /* BitsPerPixel */
+ 2, /* nColors */
+ 0, /* mapped_color_flags */
+ win_dib_alloc_bitmap,
+ win_dib_free_bitmap
+};
+
+/* forward declarations */
+static HGLOBAL win_dib_make_dib(gx_device_win * dev, int orgx, int orgy, int wx, int wy);
+static int win_dib_lock_device(unsigned char *device, int flag);
+
+/* Open the win_dib driver */
+static int
+win_dib_open(gx_device * dev)
+{
+ int code = win_open(dev);
+
+ if (code < 0)
+ return code;
+
+#ifdef __WIN32__
+ if (!is_win32s)
+ wdev->hmtx = CreateMutex(NULL, FALSE, NULL); /* unnamed mutex, initially unowned */
+#endif
+ if (gdev_mem_device_for_bits(dev->color_info.depth) == 0) {
+ win_close(dev);
+ return gs_error_rangecheck;
+ }
+ code = win_dib_alloc_bitmap((gx_device_win *) dev, dev);
+ if (code < 0) {
+ win_close(dev);
+ return code;
+ }
+ /* notify caller about new device */
+ if (pgsdll_callback) {
+ (*pgsdll_callback) (GSDLL_DEVICE, (unsigned char *)dev, 1);
+ (*pgsdll_callback) (GSDLL_SIZE, (unsigned char *)dev,
+ (dev->width & 0xffff) +
+ ((ulong) (dev->height & 0xffff) << 16));
+ }
+ code = install_internal_subclass_devices((gx_device **)&dev, NULL);
+ return code;
+}
+
+/* Get the initial matrix. DIBs, unlike most displays, */
+/* put (0,0) in the lower left corner. */
+static void
+win_dib_get_initial_matrix(gx_device * dev, gs_matrix * pmat)
+{
+ pmat->xx = dev->x_pixels_per_inch / 72.0;
+ pmat->xy = 0.0;
+ pmat->yx = 0.0;
+ pmat->yy = dev->y_pixels_per_inch / 72.0;
+ pmat->tx = 0.0;
+ pmat->ty = 0.0;
+}
+
+/* Close the win_dib driver */
+static int
+win_dib_close(gx_device * dev)
+{
+ int code;
+
+ /* wait until bitmap is not being used by caller */
+ win_dib_lock_device((unsigned char *)dev, 1);
+ if (pgsdll_callback)
+ (*pgsdll_callback) (GSDLL_DEVICE, (unsigned char *)dev, 0);
+ win_dib_lock_device((unsigned char *)dev, 0);
+ win_dib_free_bitmap((gx_device_win *) dev);
+#ifdef __WIN32__
+ if (!is_win32s)
+ CloseHandle(wdev->hmtx);
+#endif
+ code = win_close(dev);
+ return code;
+}
+
+#define wmdev ((gx_device *)&wdev->mdev)
+#define wmproc(proc) (*dev_proc(&wdev->mdev, proc))
+
+#if USE_SEGMENTS
+
+/* The drawing routines must all be careful not to cross */
+/* a segment boundary. */
+
+#define single_block(y, h)\
+ !(((y - wdev->y_base) ^ (y - wdev->y_base + h - 1)) & ~wdev->y_mask)
+
+#define BEGIN_BLOCKS\
+{ int by, bh, left = h;\
+ for ( by = y; left > 0; by += bh, left -= bh )\
+ { bh = wdev->y_block - (by & wdev->y_mask);\
+ if ( bh > left ) bh = left;
+#define END_BLOCKS\
+ }\
+}
+
+#endif /* (!)USE_SEGMENTS */
+
+/* Fill a rectangle. */
+static int
+win_dib_fill_rectangle(gx_device * dev, int x, int y, int w, int h,
+ gx_color_index color)
+{
+#if USE_SEGMENTS
+ if (single_block(y, h)) {
+ wmproc(fill_rectangle) (wmdev, x, y, w, h, color);
+ } else { /* Divide the transfer into blocks. */
+ BEGIN_BLOCKS
+ wmproc(fill_rectangle) (wmdev, x, by, w, bh, color);
+ END_BLOCKS
+ }
+#else
+ wmproc(fill_rectangle) (wmdev, x, y, w, h, color);
+#endif
+ return 0;
+}
+
+/* Copy a monochrome bitmap. The colors are given explicitly. */
+/* Color = gx_no_color_index means transparent (no effect on the image). */
+static int
+win_dib_copy_mono(gx_device * dev,
+ const byte * base, int sourcex, int raster, gx_bitmap_id id,
+ int x, int y, int w, int h,
+ gx_color_index zero, gx_color_index one)
+{
+#if USE_SEGMENTS
+ if (single_block(y, h)) {
+ wmproc(copy_mono) (wmdev, base, sourcex, raster, id,
+ x, y, w, h, zero, one);
+ } else { /* Divide the transfer into blocks. */
+ const byte *source = base;
+
+ BEGIN_BLOCKS
+ wmproc(copy_mono) (wmdev, source, sourcex, raster,
+ gx_no_bitmap_id, x, by, w, bh,
+ zero, one);
+ source += bh * raster;
+ END_BLOCKS
+ }
+#else
+ wmproc(copy_mono) (wmdev, base, sourcex, raster, id,
+ x, y, w, h, zero, one);
+#endif
+ return 0;
+}
+
+/* Copy a color pixel map. This is just like a bitmap, except that */
+/* each pixel takes 8 or 4 bits instead of 1 when device driver has color. */
+static int
+win_dib_copy_color(gx_device * dev,
+ const byte * base, int sourcex, int raster, gx_bitmap_id id,
+ int x, int y, int w, int h)
+{
+#if USE_SEGMENTS
+ if (single_block(y, h)) {
+ wmproc(copy_color) (wmdev, base, sourcex, raster, id,
+ x, y, w, h);
+ } else { /* Divide the transfer into blocks. */
+ const byte *source = base;
+
+ BEGIN_BLOCKS
+ wmproc(copy_color) (wmdev, source, sourcex, raster,
+ gx_no_bitmap_id, x, by, w, bh);
+ source += by * raster;
+ END_BLOCKS
+ }
+#else
+ wmproc(copy_color) (wmdev, base, sourcex, raster, id,
+ x, y, w, h);
+#endif
+ return 0;
+}
+
+int
+win_dib_get_bits(gx_device * dev, int y, byte * str, byte ** actual_data)
+{
+ return wmproc(get_bits) (wmdev, y, str, actual_data);
+}
+
+int
+win_dib_put_params(gx_device * dev, gs_param_list * plist)
+{
+ int code;
+
+ win_dib_lock_device((unsigned char *)dev, 1);
+ code = win_put_params(dev, plist);
+ win_dib_lock_device((unsigned char *)dev, 0);
+ return code;
+}
+
+/* ------ DLL device procedures ------ */
+
+/* make a copy of the device bitmap and return shared memory handle to it */
+/* device is a pointer to Ghostscript device from GSDLL_DEVICE message */
+HGLOBAL GSDLLAPI
+gsdll_copy_dib(unsigned char *device)
+{
+ gx_device_win_dib *dev = (gx_device_win_dib *) device;
+
+ if (!dev || !dev->is_open || dev->mdev.width == 0 || dev->mdev.height == 0)
+ return (HGLOBAL) NULL;
+ return win_dib_make_dib((gx_device_win *) dev, 0, 0, dev->width, dev->height);
+}
+
+/* make a copy of the device palette and return a handle to it */
+/* device is a pointer to Ghostscript device from GSDLL_DEVICE message */
+HPALETTE GSDLLAPI
+gsdll_copy_palette(unsigned char *device)
+{
+ gx_device_win_dib *dev = (gx_device_win_dib *) device;
+
+ if (!dev || !dev->is_open || dev->mdev.width == 0 || dev->mdev.height == 0)
+ return (HPALETTE) NULL;
+ if (wdev->nColors > 0)
+ return CreatePalette(dev->limgpalette);
+ return (HPALETTE) NULL;
+}
+
+/* copy the rectangle src from the device bitmap */
+/* to the rectangle dest on the device given by hdc */
+/* hdc must be a device context for a device (NOT a bitmap) */
+/* device is a pointer to Ghostscript device from GSDLL_DEVICE message */
+void GSDLLAPI
+gsdll_draw(unsigned char *device, HDC hdc, LPRECT dest, LPRECT src)
+{
+ gx_device_win_dib *dev = (gx_device_win_dib *) device;
+ HPALETTE oldpalette;
+
+ if (!dev || !dev->is_open || dev->mdev.width == 0 || dev->mdev.height == 0)
+ return;
+ if (dev->nColors > 0) {
+ oldpalette = SelectPalette(hdc, dev->himgpalette, FALSE);
+ RealizePalette(hdc);
+ }
+ win_dib_repaint((gx_device_win *) dev, hdc, dest->left, dest->top,
+ dest->right - dest->left, dest->bottom - dest->top,
+ src->left, src->top);
+ if (dev->nColors > 0) {
+ SelectPalette(hdc, oldpalette, FALSE);
+ }
+ return;
+}
+
+/* ------ Windows-specific device procedures ------ */
+
+/* Repaint a section of the window. */
+static void
+win_dib_repaint(gx_device_win * dev, HDC hdc, int dx, int dy, int wx, int wy,
+ int sx, int sy)
+{
+ struct bmi_s {
+ BITMAPINFOHEADER h;
+ ushort pal_index[256];
+ } bmi;
+ int i;
+ UINT which_colors;
+
+ memset(&bmi.h, 0, sizeof(bmi.h));
+
+ bmi.h.biSize = sizeof(bmi.h);
+ bmi.h.biWidth = wdev->mdev.width;
+ bmi.h.biHeight = wy;
+ bmi.h.biPlanes = 1;
+ bmi.h.biBitCount = dev->color_info.depth;
+ bmi.h.biCompression = 0;
+ bmi.h.biSizeImage = 0; /* default */
+ bmi.h.biXPelsPerMeter = 0; /* default */
+ bmi.h.biYPelsPerMeter = 0; /* default */
+
+ if (dev->BitsPerPixel <= 8) {
+ bmi.h.biClrUsed = wdev->nColors;
+ bmi.h.biClrImportant = wdev->nColors;
+ for (i = 0; i < wdev->nColors; i++)
+ bmi.pal_index[i] = i;
+ which_colors = DIB_PAL_COLORS;
+ } else if (dev->BitsPerPixel == 15) { /* 5-5-5 RGB mode */
+ DWORD* bmi_colors = (DWORD*)(&bmi.pal_index[0]);
+ bmi.h.biCompression = BI_BITFIELDS;
+ bmi_colors[0] = 0x7c00;
+ bmi_colors[1] = 0x03e0;
+ bmi_colors[2] = 0x001f;
+ which_colors = DIB_RGB_COLORS;
+ } else if (dev->BitsPerPixel == 16) { /* 5-6-5 RGB mode */
+ DWORD* bmi_colors = (DWORD*)(&bmi.pal_index[0]);
+ bmi.h.biCompression = BI_BITFIELDS;
+ bmi_colors[0] = 0xf800;
+ bmi_colors[1] = 0x07e0;
+ bmi_colors[2] = 0x001f;
+ which_colors = DIB_RGB_COLORS;
+ } else {
+ bmi.h.biClrUsed = 0;
+ bmi.h.biClrImportant = 0;
+ which_colors = DIB_RGB_COLORS;
+ }
+ /*
+ * Windows apparently limits the size of a single transfer
+ * to 2 Mb, which can be exceeded on 24-bit displays.
+ * Deal with this here.
+ */
+#define max_transfer 2000000
+ if (wdev->mdev.raster > 0) { /* just in case! */
+ long ny = max_transfer / wdev->mdev.raster;
+
+ for (; wy > ny; dy += ny, wy -= ny, sy += ny)
+ SetDIBitsToDevice(hdc, dx, dy, wx, ny,
+ sx, 0, 0, ny,
+ wdev->mdev.line_ptrs[wdev->height - (sy + ny)],
+ (BITMAPINFO FAR *) & bmi, which_colors);
+ }
+#undef max_transfer
+ SetDIBitsToDevice(hdc, dx, dy, wx, wy,
+ sx, 0, 0, wy,
+ wdev->mdev.line_ptrs[wdev->height - (sy + wy)],
+ (BITMAPINFO FAR *) & bmi, which_colors);
+}
+
+/* This makes a DIB that contains all or part of the bitmap. */
+/* The bitmap pixel orgx must start on a byte boundary. */
+static HGLOBAL
+win_dib_make_dib(gx_device_win * dev, int orgx, int orgy, int wx, int wy)
+{
+#define xwdev ((gx_device_win_dib *)dev)
+ gx_color_value prgb[3];
+ HGLOBAL hglobal;
+ BYTE FAR *pDIB;
+ BITMAPINFOHEADER FAR *pbmih;
+ RGBQUAD FAR *pColors;
+ BYTE FAR *pBits;
+ BYTE FAR *pLine;
+ ulong bitmapsize;
+ int palcount;
+ int i;
+ UINT lwidth; /* line width in bytes rounded up to multiple of 4 bytes */
+ int loffset; /* byte offset to start of line */
+
+#if USE_SEGMENTS
+ UINT lseg; /* bytes remaining in this segment */
+#endif
+
+ if (orgx + wx > wdev->width)
+ wx = wdev->width - orgx;
+ if (orgy + wy > wdev->height)
+ wy = wdev->height - orgy;
+
+ loffset = orgx * wdev->color_info.depth / 8;
+ lwidth = ((wx * wdev->color_info.depth + 31) & ~31) >> 3;
+ bitmapsize = (long)lwidth *wy;
+
+ if (wdev->color_info.depth > 16)
+ palcount = 0;
+ else if (wdev->color_info.depth > 8)
+ palcount = 3; /* 16-bit BI_BITFIELDS */
+ else
+ palcount = wdev->nColors;
+
+ hglobal = GlobalAlloc(GHND | GMEM_SHARE, sizeof(BITMAPINFOHEADER)
+ + sizeof(RGBQUAD) * palcount + bitmapsize);
+ if (hglobal == (HGLOBAL) NULL) {
+ MessageBeep(-1);
+ return (HGLOBAL) NULL;
+ }
+ pDIB = (BYTE FAR *) GlobalLock(hglobal);
+ if (pDIB == (BYTE FAR *) NULL) {
+ MessageBeep(-1);
+ return (HGLOBAL) NULL;
+ }
+ pbmih = (BITMAPINFOHEADER FAR *) (pDIB);
+ pColors = (RGBQUAD FAR *) (pDIB + sizeof(BITMAPINFOHEADER));
+ pBits = (BYTE FAR *) (pDIB + sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * palcount);
+
+ pbmih->biSize = sizeof(BITMAPINFOHEADER);
+ pbmih->biWidth = wx;
+ pbmih->biHeight = wy;
+ pbmih->biPlanes = 1;
+ pbmih->biBitCount = wdev->color_info.depth;
+ pbmih->biCompression = 0;
+ pbmih->biSizeImage = 0; /* default */
+ pbmih->biXPelsPerMeter = (DWORD) (dev->x_pixels_per_inch / 25.4 * 1000);
+ pbmih->biYPelsPerMeter = (DWORD) (dev->y_pixels_per_inch / 25.4 * 1000);
+ pbmih->biClrUsed = palcount;
+ pbmih->biClrImportant = palcount;
+
+ if (dev->BitsPerPixel == 15) { /* 5-5-5 RGB mode */
+ DWORD* bmi_colors = (DWORD*)(pColors);
+ pbmih->biCompression = BI_BITFIELDS;
+ bmi_colors[0] = 0x7c00;
+ bmi_colors[1] = 0x03e0;
+ bmi_colors[2] = 0x001f;
+ }
+ else if (dev->BitsPerPixel == 16) { /* 5-6-5 RGB mode */
+ DWORD* bmi_colors = (DWORD*)(pColors);
+ pbmih->biCompression = BI_BITFIELDS;
+ bmi_colors[0] = 0xf800;
+ bmi_colors[1] = 0x07e0;
+ bmi_colors[2] = 0x001f;
+ }
+ else {
+ for (i = 0; i < palcount; i++) {
+ win_map_color_rgb((gx_device *) wdev, (gx_color_index) i, prgb);
+ pColors[i].rgbRed = win_color_value(prgb[0]);
+ pColors[i].rgbGreen = win_color_value(prgb[1]);
+ pColors[i].rgbBlue = win_color_value(prgb[2]);
+ pColors[i].rgbReserved = 0;
+ }
+ }
+
+ pLine = pBits;
+ for (i = orgy; i < orgy + wy; i++) {
+#if USE_SEGMENTS
+ /* Window 3.1 has hmemcpy, but 3.0 doesn't */
+ lseg = (UINT) (-OFFSETOF(pLine)); /* remaining bytes in this segment */
+ if (lseg >= lwidth) {
+ _fmemcpy(pLine, xwdev->mdev.line_ptrs[i] + loffset, lwidth);
+ } else { /* break up transfer to avoid crossing segment boundary */
+ _fmemcpy(pLine, xwdev->mdev.line_ptrs[i] + loffset, lseg);
+ _fmemcpy(pLine + lseg, xwdev->mdev.line_ptrs[i] + loffset + lseg, lwidth - lseg);
+ }
+#else
+ memcpy(pLine, xwdev->mdev.line_ptrs[i], lwidth);
+#endif
+ pLine += lwidth;
+ }
+
+ GlobalUnlock(hglobal);
+ return hglobal;
+}
+
+/* Allocate the backing bitmap. */
+static int
+win_dib_alloc_bitmap(gx_device_win * dev, gx_device * param_dev)
+{
+ int width;
+ gx_device_memory mdev;
+ HGLOBAL hmdata;
+ byte FAR *base;
+ uint ptr_size;
+ uint raster;
+ ulong data_size;
+
+#if USE_SEGMENTS
+ byte FAR *ptr_base;
+#endif
+
+#ifdef __WIN32__
+ if (is_win32s) {
+#endif
+ /* Round up the width so that the scan line size is a power of 2. */
+ if (dev->color_info.depth == 24) {
+ width = param_dev->width * 3 - 1;
+ while (width & (width + 1))
+ width |= width >> 1;
+ width = (width + 1) / 3;
+ } else {
+ width = param_dev->width - 1;
+ while (width & (width + 1))
+ width |= width >> 1;
+ width++;
+ }
+#ifdef __WIN32__
+ } else { /* don't have to worry about segments so use less memory */
+ width = param_dev->width;
+ }
+#endif
+
+ /* Finish initializing the DIB. */
+
+ gs_make_mem_device(&mdev, gdev_mem_device_for_bits(dev->color_info.depth), 0, 0, (gx_device *) dev);
+ mdev.width = width;
+ mdev.height = param_dev->height;
+ raster = gdev_mem_raster(&mdev);
+ data_size = (ulong) raster *mdev.height;
+
+ ptr_size = sizeof(byte **) * mdev.height;
+ hmdata = GlobalAlloc(0, raster + data_size + ptr_size * 2);
+ if (hmdata == 0) {
+ return win_nomemory();
+ }
+ /* Nothing can go wrong now.... */
+
+ wdev->hmdata = hmdata;
+ base = GlobalLock(hmdata);
+#if USE_SEGMENTS
+ /* Adjust base so scan lines, and the pointer table, */
+ /* don't cross a segment boundary. */
+ base += (-PTR_OFF(base) & (raster - 1));
+ ptr_base = base + data_size;
+ if (PTR_OFF(ptr_base + ptr_size) < ptr_size)
+ base += (uint) - PTR_OFF(ptr_base);
+ wdev->y_block = 0x10000L / raster;
+ wdev->y_mask = wdev->y_block - 1;
+ if ((wdev->y_base = PTR_OFF(base)) != 0)
+ wdev->y_base = -(PTR_OFF(base) / raster);
+#endif
+ wdev->mdev = mdev;
+ wdev->mdev.base = (byte *) base;
+ wmproc(open_device) ((gx_device *) & wdev->mdev);
+
+ if (wdev->is_open && pgsdll_callback)
+ (*pgsdll_callback) (GSDLL_SIZE, (unsigned char *)dev,
+ (dev->width & 0xffff) +
+ ((ulong) (dev->height & 0xffff) << 16));
+
+ return 0;
+}
+
+/* Free the backing bitmap. */
+static void
+win_dib_free_bitmap(gx_device_win * dev)
+{
+ HGLOBAL hmdata = wdev->hmdata;
+
+ GlobalUnlock(hmdata);
+ GlobalFree(hmdata);
+}
+
+/* Lock the device (so it's size cannot be changed) if flag = TRUE */
+/* or unlock the device if flag = FALSE */
+/* device is a pointer to Ghostscript device from GSDLL_DEVICE message */
+static int
+win_dib_lock_device(unsigned char *device, int flag)
+{
+ gx_device *dev = (gx_device *) device;
+
+#ifdef __WIN32__
+ if (!is_win32s) {
+ if (flag) {
+ if (WaitForSingleObject(wdev->hmtx, 60000) == WAIT_TIMEOUT)
+ return 2;
+ return 1;
+ }
+ ReleaseMutex(wdev->hmtx);
+ return 0;
+ }
+#endif
+ if (flag)
+ wdev->lock_count++;
+ else
+ wdev->lock_count--;
+ if (wdev->lock_count < 0)
+ wdev->lock_count = 0;
+ return wdev->lock_count;
+}
+
+int GSDLLAPI _export
+gsdll_lock_device(unsigned char *device, int flag)
+{
+ return win_dib_lock_device(device, flag);
+}
+
+/* Copy bitmap
+ * If pbmih nonzero, copy the BITMAPINFOHEADER.
+ * If prgbquad nonzero, copy the palette.
+ * number of entries copied is given by pbmih->biClrUsed
+ * If ppbyte nonzero, return pointer to row.
+ * pointer is only valid while device is locked
+ * GS can change the palette while the device is locked.
+ * Do not call this function while GS is busy.
+ * If all pbmih and prgbquad and ppbyte are all NULL,
+ * return value is byte count needed for BITMAPINFOHEADER
+ * and palette and one bitmap row.
+ * Otherwise return value is 0;
+ *
+ * This function exists to allow the bitmap to be copied to a file
+ * or structured storage, without the overhead of having two copies
+ * of the bitmap in memory at the same time.
+ */
+int GSDLLAPI _export
+gsdll_get_bitmap_row(unsigned char *device, LPBITMAPINFOHEADER pbmih,
+ LPRGBQUAD prgbquad, LPBYTE * ppbyte, unsigned int row)
+{
+ int palcount;
+ gx_device_win_dib *dev = (gx_device_win_dib *) device;
+
+ palcount = (dev->color_info.depth == 24) ? 0 : dev->nColors;
+
+ if (pbmih) {
+ pbmih->biSize = sizeof(BITMAPINFOHEADER);
+ pbmih->biWidth = dev->width;
+ pbmih->biHeight = dev->mdev.height;
+ pbmih->biPlanes = 1;
+ pbmih->biBitCount = dev->color_info.depth;
+ if ((dev->BitsPerPixel == 15) || (dev->BitsPerPixel == 16))
+ pbmih->biCompression = BI_BITFIELDS;
+ else
+ pbmih->biCompression = 0;
+ pbmih->biSizeImage = 0; /* default */
+ pbmih->biXPelsPerMeter = (DWORD) (dev->x_pixels_per_inch / 25.4 * 1000);
+ pbmih->biYPelsPerMeter = (DWORD) (dev->y_pixels_per_inch / 25.4 * 1000);
+ pbmih->biClrUsed = palcount;
+ pbmih->biClrImportant = palcount;
+ }
+ if (prgbquad) {
+ int i;
+ gx_color_value prgb[3];
+
+ if (dev->BitsPerPixel == 15) { /* 5-5-5 RGB mode */
+ DWORD* bmi_colors = (DWORD*)(prgbquad);
+ pbmih->biCompression = BI_BITFIELDS;
+ bmi_colors[0] = 0x7c00;
+ bmi_colors[1] = 0x03e0;
+ bmi_colors[2] = 0x001f;
+ }
+ else if (dev->BitsPerPixel == 16) { /* 5-6-5 RGB mode */
+ DWORD* bmi_colors = (DWORD*)(prgbquad);
+ pbmih->biCompression = BI_BITFIELDS;
+ bmi_colors[0] = 0xf800;
+ bmi_colors[1] = 0x07e0;
+ bmi_colors[2] = 0x001f;
+ }
+ else {
+ for (i = 0; i < palcount; i++) {
+ win_map_color_rgb((gx_device *) wdev, (gx_color_index) i, prgb);
+ prgbquad[i].rgbRed = win_color_value(prgb[0]);
+ prgbquad[i].rgbGreen = win_color_value(prgb[1]);
+ prgbquad[i].rgbBlue = win_color_value(prgb[2]);
+ prgbquad[i].rgbReserved = 0;
+ }
+ }
+ }
+ if (ppbyte) {
+ if (row < dev->mdev.height)
+ *ppbyte = dev->mdev.line_ptrs[row];
+ else
+ *ppbyte = NULL;
+ }
+ if ((pbmih == NULL) && (prgbquad == NULL) && (ppbyte == NULL))
+ return sizeof(BITMAPINFOHEADER) + 256 * sizeof(RGBQUAD)
+ + gdev_mem_raster(&(dev->mdev));
+ return 0;
+}
diff --git a/devices/gdevwpr2.c b/devices/gdevwpr2.c
new file mode 100644
index 000000000..81910621d
--- /dev/null
+++ b/devices/gdevwpr2.c
@@ -0,0 +1,1637 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/*
+ * Microsoft Windows 3.n printer driver for Ghostscript.
+ * Original version by Russell Lang and
+ * L. Peter Deutsch, Aladdin Enterprises.
+ * Modified by rjl 1995-03-29 to use BMP printer code
+ * Modified by Pierre Arnaud 1999-02-18 (see description below)
+ * Modified by lpd 1999-04-03 for compatibility with Borland C++ 4.5.
+ * Modified by Pierre Arnaud 1999-10-03 (accept b&w printing on color printers).
+ * Modified by Pierre Arnaud 1999-11-20 (accept lower resolution)
+ * Bug fixed by Pierre Arnaud 2000-03-09 (win_pr2_put_params error when is_open)
+ * Bug fixed by Pierre Arnaud 2000-03-20 (win_pr2_set_bpp did not set anti_alias)
+ * Bug fixed by Pierre Arnaud 2000-03-22 (win_pr2_set_bpp depth was wrong)
+ * Modified by Pierre Arnaud 2000-12-12 (mainly added support for Tumble)
+ * Bug fixed by Pierre Arnaud 2000-12-18 (-dQueryUser now works from cmd line)
+ */
+
+/* This driver uses the printer default size and resolution and
+ * ignores page size and resolution set using -gWIDTHxHEIGHT and
+ * -rXxY. You must still set the correct PageSize to get the
+ * correct clipping path.
+ * The code in win_pr2_getdc() does try to set the printer page
+ * size from the PostScript PageSize, but it isn't working
+ * reliably at the moment.
+ *
+ * This driver doesn't work with some Windows printer drivers.
+ * The reason is unknown. All printers to which I have access
+ * work.
+ *
+ * rjl 1997-11-20
+ */
+
+/* Additions by Pierre Arnaud (Pierre.Arnaud@opac.ch)
+ *
+ * The driver has been extended in order to provide some run-time
+ * feed-back about the default Windows printer and to give the user
+ * the opportunity to select the printer's properties before the
+ * device gets opened (and any spooling starts).
+ *
+ * The driver returns an additional property named "UserSettings".
+ * This is a dictionary which contens are valid only after setting
+ * the QueryUser property (see below). The UserSettings dict contains
+ * the following keys:
+ *
+ * DocumentRange [begin end] (int array, can be set)
+ * Defines the range of pages in the document; [1 10] would
+ * describe a document starting at page 1 and ending at page 10.
+ *
+ * SelectedRange [begin end] (int array, can be set)
+ * Defines the pages the user wants to print.
+ *
+ * MediaSize [width height] (float array, read only)
+ * Current printer's media size.
+ *
+ * Copies n (integer, can be set)
+ * User selected number of copies.
+ *
+ * PrintCopies n (integer, read only)
+ * Number of copies which must be printed by Ghostscript itself.
+ * This is still experimental.
+ *
+ * DocumentName name (string, can be set)
+ * Name to be associated with the print job.
+ *
+ * UserChangedSettings (bool, read only)
+ * Set to 'true' if the last QueryUser operation succeeded.
+ *
+ * Paper n (integer, can be set)
+ * Windows paper selection (0 = automatic).
+ *
+ * Orient n (integer, can be set)
+ * Windows paper orientation (0 = automatic).
+ *
+ * Color n (integer, can be set)
+ * Windows color (0 = automatic, 1 = monochrome, 2 = color).
+ *
+ * MaxResolution n (integer, can be set)
+ * Maximum resolution in pixels pet inch (0 = no maximum). If
+ * the printer has a higher resolution than the maximum, trim
+ * the used resolution to the best one (dpi_chosen <= dpi_max,
+ * with dpi_chosen = dpi_printer / ratio).
+ */
+
+/* Supported printer parameters are :
+ *
+ * -dBitsPerPixel=n
+ * Override what the Window printer driver returns.
+ *
+ * -dNoCancel
+ * Don't display cancel dialog box. Useful for unattended or
+ * console EXE operation.
+ *
+ * -dQueryUser=n
+ * Query user interactively for the destination printer, before
+ * the device gets opened. This fills in the UserSettings dict
+ * and the OutputFile name properties. The following values are
+ * supported for n:
+ * 1 => show standard Print dialog
+ * 2 => show Print Setup dialog instead
+ * 3 => select default printer
+ * other, does nothing
+ *
+ * The /Duplex & /Tumble keys of the setpagedevice dict are supported
+ * if the Windows printer supports duplex printing.
+ */
+
+#include "gdevprn.h"
+#include "gdevpccm.h"
+#include "string_.h"
+#include "windows_.h"
+#include <shellapi.h>
+#include "gp_mswin.h"
+
+#include "gp.h"
+#include "gpcheck.h"
+#include "commdlg.h"
+#include "gsicc_manage.h"
+
+/* Make sure we cast to the correct structure type. */
+typedef struct gx_device_win_pr2_s gx_device_win_pr2;
+
+#undef wdev
+#define wdev ((gx_device_win_pr2 *)dev)
+
+/* Device procedures */
+
+/* See gxdevice.h for the definitions of the procedures. */
+static dev_proc_open_device(win_pr2_open);
+static dev_proc_close_device(win_pr2_close);
+static dev_proc_print_page(win_pr2_print_page);
+static dev_proc_map_rgb_color(win_pr2_map_rgb_color);
+static dev_proc_map_color_rgb(win_pr2_map_color_rgb);
+static dev_proc_get_params(win_pr2_get_params);
+static dev_proc_put_params(win_pr2_put_params);
+
+static int win_pr2_set_bpp(gx_device * dev, int depth);
+
+static const gx_device_procs win_pr2_procs =
+prn_color_params_procs(win_pr2_open, gdev_prn_output_page, win_pr2_close,
+ win_pr2_map_rgb_color, win_pr2_map_color_rgb,
+ win_pr2_get_params, win_pr2_put_params);
+
+#define PARENT_WINDOW HWND_DESKTOP
+BOOL CALLBACK CancelDlgProc(HWND, UINT, WPARAM, LPARAM);
+BOOL CALLBACK AbortProc2(HDC, int);
+
+/* The device descriptor */
+typedef struct gx_device_win_pr2_s gx_device_win_pr2;
+struct gx_device_win_pr2_s {
+ gx_device_common;
+ gx_prn_device_common;
+ HDC hdcprn;
+ bool nocancel;
+
+ int doc_page_begin; /* first page number in document */
+ int doc_page_end; /* last page number in document */
+ int user_page_begin; /* user's choice: first page to print */
+ int user_page_end; /* user's choice: last page to print */
+ int user_copies; /* user's choice: number of copies */
+ int print_copies; /* number of times GS should print each page */
+ float user_media_size[2]; /* width/height of media selected by user */
+ char doc_name[200]; /* name of document for the spooler */
+ char paper_name[64]; /* name of selected paper format */
+ bool user_icc; /* User specified device icc profile */
+ bool user_changed_settings; /* true if user validated dialog */
+ int user_paper; /* user's choice: paper format */
+ int user_orient; /* user's choice: paper orientation */
+ int user_color; /* user's choice: color format */
+ int max_dpi; /* maximum resolution in DPI */
+ int ratio; /* stretch ratio when printing */
+ int selected_bpp; /* selected bpp, memorised by win_pr2_set_bpp */
+ bool tumble; /* tumble setting (with duplex) */
+ int query_user; /* query user (-dQueryUser) */
+
+ HANDLE win32_hdevmode; /* handle to device mode information */
+ HANDLE win32_hdevnames; /* handle to device names information */
+
+ DLGPROC lpfnAbortProc;
+ DLGPROC lpfnCancelProc;
+ HWND hDlgModeless;
+
+ bool use_old_spool_name; /* user prefers old \\spool\ name */
+ gx_device_win_pr2* original_device; /* used to detect copies */
+};
+
+gx_device_win_pr2 far_data gs_mswinpr2_device =
+{
+ prn_device_std_body(gx_device_win_pr2, win_pr2_procs, "mswinpr2",
+ DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS, 72.0, 72.0,
+ 0, 0, 0, 0,
+ 0, win_pr2_print_page), /* depth = 0 */
+ 0, /* hdcprn */
+ 0, /* nocancel */
+ 0, /* doc_page_begin */
+ 0, /* doc_page_end */
+ 0, /* user_page_begin */
+ 0, /* user_page_end */
+ 1, /* user_copies */
+ 1, /* print_copies */
+ { 0.0, 0.0 }, /* user_media_size */
+ { 0 }, /* doc_name */
+ { 0 }, /* paper_name */
+ false, /* user_icc */
+ 0, /* user_changed_settings */
+ 0, /* user_paper */
+ 0, /* user_orient */
+ 0, /* user_color */
+ 0, /* max_dpi */
+ 0, /* ratio */
+ 0, /* selected_bpp */
+ false, /* tumble */
+ -1, /* query_user */
+ NULL, /* win32_hdevmode */
+ NULL, /* win32_hdevnames */
+ NULL, /* lpfnAbortProc */
+ NULL, /* lpfnCancelProc */
+ NULL, /* hDlgModeless */
+ false, /* use_old_spool_name */
+ NULL /* original_device */
+};
+
+/********************************************************************************/
+
+static int win_pr2_getdc(gx_device_win_pr2 * dev);
+static int win_pr2_update_dev(gx_device_win_pr2 * dev, LPDEVMODE pdevmode);
+static int win_pr2_update_win(gx_device_win_pr2 * dev, LPDEVMODE pdevmode);
+static int win_pr2_print_setup_interaction(gx_device_win_pr2 * dev, int mode);
+static int win_pr2_write_user_settings(gx_device_win_pr2 * dev, gs_param_list * plist);
+static int win_pr2_read_user_settings(gx_device_win_pr2 * dev, gs_param_list * plist);
+static void win_pr2_copy_check(gx_device_win_pr2 * dev);
+
+/********************************************************************************/
+
+/* Open the win_pr2 driver */
+static int
+win_pr2_open(gx_device * dev)
+{
+ int code, code1;
+ int depth;
+ PRINTDLG pd;
+ POINT offset;
+ POINT size;
+ float m[4];
+ FILE *pfile;
+ DOCINFO docinfo;
+ float ratio = 1.0;
+
+ win_pr2_copy_check(wdev);
+
+ /* get a HDC for the printer */
+ if ((wdev->win32_hdevmode) &&
+ (wdev->win32_hdevnames)) {
+ /* The user has already had the opportunity to choose the output */
+ /* file interactively. Just use the specified parameters. */
+
+ LPDEVMODE devmode = (LPDEVMODE) GlobalLock(wdev->win32_hdevmode);
+ LPDEVNAMES devnames = (LPDEVNAMES) GlobalLock(wdev->win32_hdevnames);
+
+ const char* driver = (char*)(devnames)+(devnames->wDriverOffset);
+ const char* device = (char*)(devnames)+(devnames->wDeviceOffset);
+ const char* output = (char*)(devnames)+(devnames->wOutputOffset);
+
+ wdev->hdcprn = CreateDC(driver, device, output, devmode);
+
+ GlobalUnlock(wdev->win32_hdevmode);
+ GlobalUnlock(wdev->win32_hdevnames);
+
+ if (wdev->hdcprn == NULL) {
+ return gs_error_Fatal;
+ }
+
+ } else if (!win_pr2_getdc(wdev)) {
+ /* couldn't get a printer from -sOutputFile= */
+ /* Prompt with dialog box */
+
+ LPDEVMODE devmode = NULL;
+ memset(&pd, 0, sizeof(pd));
+
+ pd.lStructSize = sizeof(pd);
+ pd.hwndOwner = PARENT_WINDOW;
+ pd.Flags = PD_RETURNDC;
+ pd.nMinPage = wdev->doc_page_begin;
+ pd.nMaxPage = wdev->doc_page_end;
+ pd.nFromPage = wdev->user_page_begin;
+ pd.nToPage = wdev->user_page_end;
+ pd.nCopies = wdev->user_copies;
+
+ if (!PrintDlg(&pd)) {
+ /* device not opened - exit ghostscript */
+ return gs_error_Fatal; /* exit Ghostscript cleanly */
+ }
+
+ devmode = GlobalLock(pd.hDevMode);
+ win_pr2_update_dev(wdev,devmode);
+ GlobalUnlock(pd.hDevMode);
+
+ if (wdev->win32_hdevmode)
+ GlobalFree(wdev->win32_hdevmode);
+ if (wdev->win32_hdevnames)
+ GlobalFree(wdev->win32_hdevnames);
+
+ wdev->hdcprn = pd.hDC;
+ wdev->win32_hdevmode = pd.hDevMode;
+ wdev->win32_hdevnames = pd.hDevNames;
+
+ pd.hDevMode = NULL;
+ pd.hDevNames = NULL;
+ }
+ if (!(GetDeviceCaps(wdev->hdcprn, RASTERCAPS) != RC_DIBTODEV)) {
+ errprintf(dev->memory, "Windows printer does not have RC_DIBTODEV\n");
+ DeleteDC(wdev->hdcprn);
+ return gs_error_limitcheck;
+ }
+ /* initialise printer, install abort proc */
+ wdev->lpfnAbortProc = (DLGPROC) AbortProc2;
+ SetAbortProc(wdev->hdcprn, (ABORTPROC) wdev->lpfnAbortProc);
+
+ /*
+ * Some versions of the Windows headers include lpszDatatype and fwType,
+ * and some don't. Since we want to set these fields to zero anyway,
+ * we just start by zeroing the whole structure.
+ */
+ memset(&docinfo, 0, sizeof(docinfo));
+ docinfo.cbSize = sizeof(docinfo);
+ docinfo.lpszDocName = wdev->doc_name;
+ /*docinfo.lpszOutput = NULL;*/
+ /*docinfo.lpszDatatype = NULL;*/
+ /*docinfo.fwType = 0;*/
+
+ if (docinfo.lpszDocName[0] == 0) {
+ docinfo.lpszDocName = "Ghostscript output";
+ }
+
+ if (StartDoc(wdev->hdcprn, &docinfo) <= 0) {
+ errprintf(dev->memory,
+ "Printer StartDoc failed (error %08x)\n", GetLastError());
+ DeleteDC(wdev->hdcprn);
+ return gs_error_limitcheck;
+ }
+
+ dev->x_pixels_per_inch = (float)GetDeviceCaps(wdev->hdcprn, LOGPIXELSX);
+ dev->y_pixels_per_inch = (float)GetDeviceCaps(wdev->hdcprn, LOGPIXELSY);
+
+ wdev->ratio = 1;
+
+ if (wdev->max_dpi > 50) {
+
+ float dpi_x = dev->x_pixels_per_inch;
+ float dpi_y = dev->y_pixels_per_inch;
+
+ while ((dev->x_pixels_per_inch > wdev->max_dpi)
+ || (dev->y_pixels_per_inch > wdev->max_dpi)) {
+ ratio += 1.0;
+ wdev->ratio ++;
+ dev->x_pixels_per_inch = dpi_x / ratio;
+ dev->y_pixels_per_inch = dpi_y / ratio;
+ }
+ }
+
+ size.x = GetDeviceCaps(wdev->hdcprn, PHYSICALWIDTH);
+ size.y = GetDeviceCaps(wdev->hdcprn, PHYSICALHEIGHT);
+ gx_device_set_width_height(dev, (int)(size.x / ratio), (int)(size.y / ratio));
+ offset.x = GetDeviceCaps(wdev->hdcprn, PHYSICALOFFSETX);
+ offset.y = GetDeviceCaps(wdev->hdcprn, PHYSICALOFFSETY);
+
+ /* m[] gives margins in inches */
+ m[0] /*left */ = offset.x / dev->x_pixels_per_inch / ratio;
+ m[3] /*top */ = offset.y / dev->y_pixels_per_inch / ratio;
+ m[2] /*right */ = (size.x - offset.x - GetDeviceCaps(wdev->hdcprn, HORZRES)) / dev->x_pixels_per_inch / ratio;
+ m[1] /*bottom */ = (size.y - offset.y - GetDeviceCaps(wdev->hdcprn, VERTRES)) / dev->y_pixels_per_inch / ratio;
+ gx_device_set_margins(dev, m, true);
+
+ depth = dev->color_info.depth;
+ if (depth == 0) {
+ /* Set parameters that were unknown before opening device */
+ /* Find out if the device supports color */
+ /* We recognize 1, 4 (but use only 3), 8 and 24 bit color devices */
+ depth = GetDeviceCaps(wdev->hdcprn, PLANES) * GetDeviceCaps(wdev->hdcprn, BITSPIXEL);
+ }
+ code1 = win_pr2_set_bpp(dev, depth);
+
+ /* gdev_prn_open opens a temporary file which we don't want */
+ /* so we specify the name now so we can delete it later */
+ wdev->fname[0] = '\0';
+ pfile = gp_open_scratch_file(dev->memory,
+ gp_scratch_file_name_prefix,
+ wdev->fname, "wb");
+ fclose(pfile);
+ code = gdev_prn_open(dev);
+
+ /* If we subclassed the device, with a FirstPage LastPage device,
+ * update the stored pointer copy here, if we don't then this whole
+ * device stops working, not sure why.
+ */
+ if (dev->child) {
+ gx_device_win_pr2 *windev;
+
+ while (dev->child)
+ dev = dev->child;
+
+ windev = (gx_device_win_pr2 *)dev;
+
+ windev->original_device = (gx_device_win_pr2 *)dev;
+ }
+
+ if ((code < 0) && wdev->fname[0])
+ unlink(wdev->fname);
+
+ if (!wdev->nocancel) {
+ /* inform user of progress with dialog box and allow cancel */
+ wdev->lpfnCancelProc = (DLGPROC) CancelDlgProc;
+ wdev->hDlgModeless = CreateDialog(phInstance, "CancelDlgBox",
+ PARENT_WINDOW, wdev->lpfnCancelProc);
+ ShowWindow(wdev->hDlgModeless, SW_HIDE);
+ }
+ if (code1 < 0 && code >= 0) {
+ code = code1;
+ }
+
+ return code;
+};
+
+/* Close the win_pr2 driver */
+static int
+win_pr2_close(gx_device * dev)
+{
+ int code;
+ int aborted = FALSE;
+
+ win_pr2_copy_check(wdev);
+
+ /* Free resources */
+
+ if (!wdev->nocancel) {
+ if (!wdev->hDlgModeless)
+ aborted = TRUE;
+ else
+ DestroyWindow(wdev->hDlgModeless);
+ wdev->hDlgModeless = 0;
+ }
+ if (aborted)
+ AbortDoc(wdev->hdcprn);
+ else
+ EndDoc(wdev->hdcprn);
+
+ DeleteDC(wdev->hdcprn);
+
+ if (wdev->win32_hdevmode != NULL) {
+ GlobalFree(wdev->win32_hdevmode);
+ wdev->win32_hdevmode = NULL;
+ }
+ if (wdev->win32_hdevnames != NULL) {
+ GlobalFree(wdev->win32_hdevnames);
+ wdev->win32_hdevnames = NULL;
+ }
+
+ code = gdev_prn_close(dev);
+
+ /* delete unwanted temporary file */
+ if (wdev->fname[0])
+ unlink(wdev->fname);
+
+ return code;
+}
+
+/* ------ Internal routines ------ */
+
+#undef wdev
+#define wdev ((gx_device_win_pr2 *)pdev)
+
+/********************************************************************************/
+
+/* ------ Private definitions ------ */
+
+/* new win_pr2_print_page routine */
+
+/* Write BMP header to memory, then send bitmap to printer */
+/* one scan line at a time */
+static int
+win_pr2_print_page(gx_device_printer * pdev, FILE * file)
+{
+ int raster = gdev_prn_raster(pdev);
+
+ /* BMP scan lines are padded to 32 bits. */
+ ulong bmp_raster = raster + (-raster & 3);
+ ulong bmp_raster_multi;
+ int scan_lines, yslice, lines, i;
+ int width;
+ int depth = pdev->color_info.depth;
+ byte *row;
+ int y;
+ int code = 0; /* return code */
+ MSG msg;
+ char dlgtext[32];
+ HGLOBAL hrow;
+ int ratio = ((gx_device_win_pr2 *)pdev)->ratio;
+
+ struct bmi_s {
+ BITMAPINFOHEADER h;
+ RGBQUAD pal[256];
+ } bmi;
+
+ scan_lines = dev_print_scan_lines(pdev);
+ width = (int)(pdev->width - ((dev_l_margin(pdev) + dev_r_margin(pdev) -
+ dev_x_offset(pdev)) * pdev->x_pixels_per_inch));
+
+ yslice = 65535 / bmp_raster; /* max lines in 64k */
+ bmp_raster_multi = bmp_raster * yslice;
+ hrow = GlobalAlloc(0, bmp_raster_multi);
+ row = GlobalLock(hrow);
+ if (row == 0) /* can't allocate row buffer */
+ return_error(gs_error_VMerror);
+
+ /* Write the info header. */
+
+ bmi.h.biSize = sizeof(bmi.h);
+ bmi.h.biWidth = pdev->width; /* wdev->mdev.width; */
+ bmi.h.biHeight = yslice;
+ bmi.h.biPlanes = 1;
+ bmi.h.biBitCount = pdev->color_info.depth;
+ bmi.h.biCompression = 0;
+ bmi.h.biSizeImage = 0; /* default */
+ bmi.h.biXPelsPerMeter = 0; /* default */
+ bmi.h.biYPelsPerMeter = 0; /* default */
+
+ StartPage(wdev->hdcprn);
+
+ /* Write the palette. */
+
+ if (depth <= 8) {
+ int i;
+ gx_color_value rgb[3];
+ LPRGBQUAD pq;
+
+ bmi.h.biClrUsed = 1 << depth;
+ bmi.h.biClrImportant = 1 << depth;
+ for (i = 0; i != 1 << depth; i++) {
+ (*dev_proc(pdev, map_color_rgb)) ((gx_device *) pdev,
+ (gx_color_index) i, rgb);
+ pq = &bmi.pal[i];
+ pq->rgbRed = gx_color_value_to_byte(rgb[0]);
+ pq->rgbGreen = gx_color_value_to_byte(rgb[1]);
+ pq->rgbBlue = gx_color_value_to_byte(rgb[2]);
+ pq->rgbReserved = 0;
+ }
+ } else {
+ bmi.h.biClrUsed = 0;
+ bmi.h.biClrImportant = 0;
+ }
+
+ if (!wdev->nocancel) {
+ gs_sprintf(dlgtext, "Printing page %d", (int)(pdev->PageCount) + 1);
+ SetWindowText(GetDlgItem(wdev->hDlgModeless, CANCEL_PRINTING), dlgtext);
+ ShowWindow(wdev->hDlgModeless, SW_SHOW);
+ }
+ for (y = 0; y < scan_lines;) {
+ /* copy slice to row buffer */
+ if (y > scan_lines - yslice)
+ lines = scan_lines - y;
+ else
+ lines = yslice;
+ for (i = 0; i < lines; i++)
+ gdev_prn_copy_scan_lines(pdev, y + i,
+ row + (bmp_raster * (lines - 1 - i)), raster);
+
+ if (ratio > 1) {
+ StretchDIBits(wdev->hdcprn, 0, y*ratio, pdev->width*ratio, lines*ratio,
+ 0, 0, pdev->width, lines,
+ row,
+ (BITMAPINFO FAR *) & bmi, DIB_RGB_COLORS, SRCCOPY);
+ } else {
+ SetDIBitsToDevice(wdev->hdcprn, 0, y, pdev->width, lines,
+ 0, 0, 0, lines,
+ row,
+ (BITMAPINFO FAR *) & bmi, DIB_RGB_COLORS);
+ }
+ y += lines;
+
+ if (!wdev->nocancel) {
+ /* inform user of progress */
+ gs_sprintf(dlgtext, "%d%% done", (int)(y * 100L / scan_lines));
+ SetWindowText(GetDlgItem(wdev->hDlgModeless, CANCEL_PCDONE), dlgtext);
+ }
+ /* process message loop */
+ while (PeekMessage(&msg, wdev->hDlgModeless, 0, 0, PM_REMOVE)) {
+ if ((wdev->hDlgModeless == 0) || !IsDialogMessage(wdev->hDlgModeless, &msg)) {
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+ }
+ if ((!wdev->nocancel) && (wdev->hDlgModeless == 0)) {
+ /* user pressed cancel button */
+ break;
+ }
+ }
+
+ if ((!wdev->nocancel) && (wdev->hDlgModeless == 0))
+ code = gs_error_Fatal; /* exit Ghostscript cleanly */
+ else {
+ /* push out the page */
+ if (!wdev->nocancel)
+ SetWindowText(GetDlgItem(wdev->hDlgModeless, CANCEL_PCDONE),
+ "Ejecting page...");
+ EndPage(wdev->hdcprn);
+ if (!wdev->nocancel)
+ ShowWindow(wdev->hDlgModeless, SW_HIDE);
+ }
+
+ GlobalUnlock(hrow);
+ GlobalFree(hrow);
+
+ return code;
+}
+
+/* combined color mappers */
+
+/* 24-bit color mappers (taken from gdevmem2.c). */
+/* Note that Windows expects RGB values in the order B,G,R. */
+
+/* Map a r-g-b color to a color index. */
+static gx_color_index
+win_pr2_map_rgb_color(gx_device * dev, const gx_color_value cv[])
+{
+ gx_color_value r = cv[0];
+ gx_color_value g = cv[1];
+ gx_color_value b = cv[2];
+ switch (dev->color_info.depth) {
+ case 1:
+ return gdev_prn_map_rgb_color(dev, cv);
+ case 4:
+ /* use only 8 colors */
+ return (r > (gx_max_color_value / 2 + 1) ? 4 : 0) +
+ (g > (gx_max_color_value / 2 + 1) ? 2 : 0) +
+ (b > (gx_max_color_value / 2 + 1) ? 1 : 0);
+ case 8:
+ return pc_8bit_map_rgb_color(dev, cv);
+ case 24:
+ return gx_color_value_to_byte(r) +
+ ((uint) gx_color_value_to_byte(g) << 8) +
+ ((ulong) gx_color_value_to_byte(b) << 16);
+ }
+ return 0; /* error */
+}
+
+/* Map a color index to a r-g-b color. */
+static int
+win_pr2_map_color_rgb(gx_device * dev, gx_color_index color,
+ gx_color_value prgb[3])
+{
+ switch (dev->color_info.depth) {
+ case 1:
+ gdev_prn_map_color_rgb(dev, color, prgb);
+ break;
+ case 4:
+ /* use only 8 colors */
+ prgb[0] = (color & 4) ? gx_max_color_value : 0;
+ prgb[1] = (color & 2) ? gx_max_color_value : 0;
+ prgb[2] = (color & 1) ? gx_max_color_value : 0;
+ break;
+ case 8:
+ pc_8bit_map_color_rgb(dev, color, prgb);
+ break;
+ case 24:
+ prgb[2] = gx_color_value_from_byte(color >> 16);
+ prgb[1] = gx_color_value_from_byte((color >> 8) & 0xff);
+ prgb[0] = gx_color_value_from_byte(color & 0xff);
+ break;
+ }
+ return 0;
+}
+
+static int
+win_pr2_set_bpp(gx_device * dev, int depth)
+{
+ int code = 0;
+ gx_device_printer *pdev = (gx_device_printer *)dev;
+
+ if (depth > 8) {
+ static const gx_device_color_info win_pr2_24color = dci_std_color(24);
+
+ dev->color_info = win_pr2_24color;
+ depth = 24;
+
+ if (!wdev->user_icc && (!wdev->icc_struct || (wdev->icc_struct &&
+ wdev->icc_struct->device_profile[gsDEFAULTPROFILE]->data_cs != gsRGB))) {
+
+ if (wdev->icc_struct) {
+ rc_decrement(wdev->icc_struct, "win_pr2_set_bpp");
+ }
+ wdev->icc_struct = gsicc_new_device_profile_array(wdev->memory);
+
+ if (wdev->icc_struct) {
+ code = gsicc_set_device_profile(dev, dev->memory,
+ (char *)DEFAULT_RGB_ICC, gsDEFAULTPROFILE);
+ }
+ else {
+ code = gs_error_VMerror;
+ }
+ }
+
+ } else if (depth >= 8) {
+ /* 8-bit (SuperVGA-style) color. */
+ /* (Uses a fixed palette of 3,3,2 bits.) */
+ static const gx_device_color_info win_pr2_8color = dci_pc_8bit;
+
+ dev->color_info = win_pr2_8color;
+ depth = 8;
+
+ if (!wdev->user_icc && (!wdev->icc_struct || (wdev->icc_struct &&
+ wdev->icc_struct->device_profile[gsDEFAULTPROFILE]->data_cs != gsRGB))) {
+
+ if (wdev->icc_struct) {
+ rc_decrement(wdev->icc_struct, "win_pr2_set_bpp");
+ }
+ wdev->icc_struct = gsicc_new_device_profile_array(wdev->memory);
+
+ if (wdev->icc_struct) {
+ code = gsicc_set_device_profile(dev, dev->memory,
+ (char *)DEFAULT_RGB_ICC, gsDEFAULTPROFILE);
+ }
+ else {
+ code = gs_error_VMerror;
+ }
+ }
+ } else if (depth >= 3) {
+ /* 3 plane printer */
+ /* suitable for impact dot matrix CMYK printers */
+ /* create 4-bit bitmap, but only use 8 colors */
+ static const gx_device_color_info win_pr2_4color = dci_values(3, 4, 1, 1, 2, 2);
+
+ dev->color_info = win_pr2_4color;
+ depth = 4;
+
+ if (!wdev->user_icc && (!wdev->icc_struct || (wdev->icc_struct &&
+ wdev->icc_struct->device_profile[gsDEFAULTPROFILE]->data_cs != gsCMYK))) {
+
+ if (wdev->icc_struct) {
+ rc_decrement(wdev->icc_struct, "win_pr2_set_bpp");
+ }
+ wdev->icc_struct = gsicc_new_device_profile_array(wdev->memory);
+
+ if (wdev->icc_struct) {
+ code = gsicc_set_device_profile(dev, dev->memory,
+ (char *)DEFAULT_CMYK_ICC, gsDEFAULTPROFILE);
+ }
+ else {
+ code = gs_error_VMerror;
+ }
+ }
+ } else { /* default is black_and_white */
+ static const gx_device_color_info win_pr2_1color = dci_std_color(1);
+
+ dev->color_info = win_pr2_1color;
+ depth = 1;
+
+ if (!wdev->user_icc && (!wdev->icc_struct || (wdev->icc_struct &&
+ wdev->icc_struct->device_profile[gsDEFAULTPROFILE]->data_cs != gsGRAY))) {
+
+ if (wdev->icc_struct) {
+ rc_decrement(wdev->icc_struct, "win_pr2_set_bpp");
+ }
+ wdev->icc_struct = gsicc_new_device_profile_array(wdev->memory);
+
+ if (wdev->icc_struct) {
+ code = gsicc_set_device_profile(dev, dev->memory,
+ (char *)DEFAULT_GRAY_ICC, gsDEFAULTPROFILE);
+ }
+ else {
+ code = gs_error_VMerror;
+ }
+ }
+ }
+
+ wdev->selected_bpp = depth;
+
+ /* copy encode/decode procedures */
+ dev->procs.encode_color = dev->procs.map_rgb_color;
+ dev->procs.decode_color = dev->procs.map_color_rgb;
+ if (depth == 1) {
+ dev->procs.get_color_mapping_procs =
+ gx_default_DevGray_get_color_mapping_procs;
+ dev->procs.get_color_comp_index =
+ gx_default_DevGray_get_color_comp_index;
+ }
+ else {
+ dev->procs.get_color_mapping_procs =
+ gx_default_DevRGB_get_color_mapping_procs;
+ dev->procs.get_color_comp_index =
+ gx_default_DevRGB_get_color_comp_index;
+ }
+ return(code);
+}
+
+/********************************************************************************/
+
+/* Get device parameters */
+int
+win_pr2_get_params(gx_device * pdev, gs_param_list * plist)
+{
+ int code = gdev_prn_get_params(pdev, plist);
+
+ win_pr2_copy_check(wdev);
+
+ if (code >= 0)
+ code = param_write_bool(plist, "NoCancel",
+ &(wdev->nocancel));
+ if (code >= 0)
+ code = param_write_int(plist, "QueryUser",
+ &(wdev->query_user));
+ if (code >= 0)
+ code = win_pr2_write_user_settings(wdev, plist);
+
+ if ((code >= 0) && (wdev->Duplex_set > 0))
+ code = param_write_bool(plist, "Tumble",
+ &(wdev->tumble));
+
+ return code;
+}
+
+/* We implement this ourselves so that we can change BitsPerPixel */
+/* before the device is opened */
+int
+win_pr2_put_params(gx_device * pdev, gs_param_list * plist)
+{
+ int ecode = 0, code;
+ int old_bpp = pdev->color_info.depth;
+ int bpp = old_bpp;
+ bool tumble = wdev->tumble;
+ bool nocancel = wdev->nocancel;
+ int queryuser = 0;
+ bool old_duplex = wdev->Duplex;
+ bool old_tumble = wdev->tumble;
+ int old_orient = wdev->user_orient;
+ int old_color = wdev->user_color;
+ int old_paper = wdev->user_paper;
+ int old_mx_dpi = wdev->max_dpi;
+
+ if (wdev->Duplex_set < 0) {
+ wdev->Duplex_set = 0;
+ wdev->Duplex = false;
+ wdev->tumble = false;
+ }
+
+ win_pr2_copy_check(wdev);
+
+ code = win_pr2_read_user_settings(wdev, plist);
+
+ switch (code = param_read_int(plist, "BitsPerPixel", &bpp)) {
+ case 0:
+ if (pdev->is_open) {
+ if (wdev->selected_bpp == bpp) {
+ break;
+ }
+ ecode = gs_error_rangecheck;
+ } else { /* change dev->color_info is valid before device is opened */
+ code = win_pr2_set_bpp(pdev, bpp);
+ if (code < 0) {
+ ecode = code;
+ }
+ break;
+ }
+ goto bppe;
+ default:
+ ecode = code;
+ bppe:param_signal_error(plist, "BitsPerPixel", ecode);
+ case 1:
+ break;
+ }
+
+ switch (code = param_read_bool(plist, "NoCancel", &nocancel)) {
+ case 0:
+ if (pdev->is_open) {
+ if (wdev->nocancel == nocancel) {
+ break;
+ }
+ ecode = gs_error_rangecheck;
+ } else {
+ wdev->nocancel = nocancel;
+ break;
+ }
+ goto nocancele;
+ default:
+ ecode = code;
+ nocancele:param_signal_error(plist, "NoCancel", ecode);
+ case 1:
+ break;
+ }
+
+ switch (code = param_read_bool(plist, "Tumble", &tumble)) {
+ case 0:
+ wdev->tumble = tumble;
+ break;
+ default:
+ ecode = code;
+ param_signal_error(plist, "Tumble", ecode);
+ case 1:
+ break;
+ }
+
+ switch (code = param_read_int(plist, "QueryUser", &queryuser)) {
+ case 0:
+ if ((queryuser > 0) &&
+ (queryuser < 4)) {
+ win_pr2_print_setup_interaction(wdev, queryuser);
+ }
+ break;
+ default:
+ ecode = code;
+ param_signal_error(plist, "QueryUser", ecode);
+ case 1:
+ break;
+ }
+
+ /* Record if the user has specified a custom profile, so we don't replace it
+ with a default when we change color space - see win_pr2_set_bpp()
+ Here, we only want to know *if* we have a user specced profile, actually
+ setting it will be handled by gdev_prn_put_params()
+ */
+ if (!wdev->user_icc) {
+ gs_param_string icc_pro_dummy;
+
+ wdev->user_icc = param_read_string(plist, "OutputICCProfile", &icc_pro_dummy) == 0;
+ }
+
+ if (ecode >= 0)
+ ecode = gdev_prn_put_params(pdev, plist);
+
+ if (wdev->win32_hdevmode && wdev->hdcprn) {
+ if ( (old_duplex != wdev->Duplex)
+ || (old_tumble != wdev->tumble)
+ || (old_orient != wdev->user_orient)
+ || (old_color != wdev->user_color)
+ || (old_paper != wdev->user_paper)
+ || (old_mx_dpi != wdev->max_dpi) ) {
+
+ LPDEVMODE pdevmode = GlobalLock(wdev->win32_hdevmode);
+
+ if (pdevmode) {
+ win_pr2_update_win(wdev, pdevmode);
+ ResetDC(wdev->hdcprn, pdevmode);
+ GlobalUnlock(pdevmode);
+ }
+ }
+ }
+
+ return ecode;
+}
+
+#undef wdev
+
+/********************************************************************************/
+
+/* Get Device Context for printer */
+static int
+win_pr2_getdc(gx_device_win_pr2 * wdev)
+{
+ char *device;
+ char driverbuf[512], *dbuflast = NULL;
+ char *driver;
+ char *output;
+ char *devcap;
+ int devcapsize;
+ int devmode_size;
+
+ int i, n;
+ POINT *pp;
+ int paperindex;
+ int paperwidth, paperheight;
+ int orientation;
+ int papersize;
+ char papername[64];
+ LPDEVMODE podevmode, pidevmode;
+ HANDLE hprinter;
+
+ /* first try to derive the printer name from -sOutputFile= */
+ /* is printer if name prefixed by \\spool\ or by %printer% */
+ if (is_spool(wdev->fname)) {
+ device = wdev->fname + 8; /* skip over \\spool\ */
+ wdev->use_old_spool_name = true;
+ } else if (strncmp("%printer%",wdev->fname,9) == 0) {
+ device = wdev->fname + 9; /* skip over %printer% */
+ wdev->use_old_spool_name = false;
+ } else {
+ return FALSE;
+ }
+
+ /* now try to match the printer name against the [Devices] section */
+#ifdef GS_NO_UTF8
+ {
+ char *devices = gs_malloc(wdev->memory, 4096, 1, "win_pr2_getdc");
+ char *p;
+ if (devices == (char *)NULL)
+ return FALSE;
+ GetProfileString("Devices", NULL, "", devices, 4096);
+ p = devices;
+ while (*p) {
+ if (stricmp(p, device) == 0)
+ break;
+ p += strlen(p) + 1;
+ }
+ if (*p == '\0')
+ p = NULL;
+ gs_free(wdev->memory, devices, 4096, 1, "win_pr2_getdc");
+ if (p == NULL)
+ return FALSE; /* doesn't match an available printer */
+
+ /* the printer exists, get the remaining information from win.ini */
+ GetProfileString("Devices", device, "", driverbuf, sizeof(driverbuf));
+ }
+#else
+ {
+ wchar_t unidrvbuf[sizeof(driverbuf)];
+ wchar_t *devices;
+ wchar_t *p;
+ wchar_t *unidev = malloc(utf8_to_wchar(NULL, device)*sizeof(wchar_t));
+ if (unidev == NULL)
+ return FALSE;
+ utf8_to_wchar(unidev, device);
+ devices = gs_malloc(wdev->memory, 8192, 1, "win_pr2_getdc");
+ if (devices == (wchar_t *)NULL) {
+ free(unidev);
+ return FALSE;
+ }
+ GetProfileStringW(L"Devices", NULL, L"", devices, 8192);
+ p = devices;
+ while (*p) {
+ if (wcsicmp(p, unidev) == 0)
+ break;
+ p += wcslen(p) + 1;
+ }
+ if (*p == '\0')
+ p = NULL;
+ gs_free(wdev->memory, devices, 8192, 1, "win_pr2_getdc");
+ if (p == NULL) {
+ free(unidev);
+ return FALSE; /* doesn't match an available printer */
+ }
+
+ /* the printer exists, get the remaining information from win.ini */
+ GetProfileStringW(L"Devices", unidev, L"", unidrvbuf, sizeof(unidrvbuf));
+ free(unidev);
+ i = wchar_to_utf8(NULL, unidrvbuf);
+ if (i < 0 || i > sizeof(driverbuf))
+ return FALSE;
+ wchar_to_utf8(driverbuf, unidrvbuf);
+ }
+#endif
+ driver = gs_strtok(driverbuf, ",", &dbuflast);
+ output = gs_strtok(NULL, ",", dbuflast);
+
+ if (!gp_OpenPrinter(device, &hprinter))
+ return FALSE;
+ devmode_size = DocumentProperties(NULL, hprinter, device, NULL, NULL, 0);
+ if ((podevmode = gs_malloc(wdev->memory, devmode_size, 1, "win_pr2_getdc"))
+ == (LPDEVMODE) NULL) {
+ ClosePrinter(hprinter);
+ return FALSE;
+ }
+ if ((pidevmode = gs_malloc(wdev->memory, devmode_size, 1, "win_pr2_getdc")) == (LPDEVMODE) NULL) {
+ gs_free(wdev->memory, podevmode, devmode_size, 1, "win_pr2_getdc");
+ ClosePrinter(hprinter);
+ return FALSE;
+ }
+ DocumentProperties(NULL, hprinter, device, podevmode, NULL, DM_OUT_BUFFER);
+
+ /* now find out what paper sizes are available */
+ devcapsize = DeviceCapabilities(device, output, DC_PAPERSIZE, NULL, NULL);
+ devcapsize *= sizeof(POINT);
+ if ((devcap = gs_malloc(wdev->memory, devcapsize, 1, "win_pr2_getdc")) == (LPBYTE) NULL)
+ return FALSE;
+ n = DeviceCapabilities(device, output, DC_PAPERSIZE, devcap, NULL);
+ paperwidth = (int)(wdev->MediaSize[0] * 254 / 72);
+ paperheight = (int)(wdev->MediaSize[1] * 254 / 72);
+ papername[0] = '\0';
+ papersize = 0;
+ paperindex = -1;
+ orientation = 0;
+ pp = (POINT *) devcap;
+ for (i = 0; i < n; i++, pp++) {
+ if ((pp->x < paperwidth + 20) && (pp->x > paperwidth - 20) &&
+ (pp->y < paperheight + 20) && (pp->y > paperheight - 20)) {
+ paperindex = i;
+ paperwidth = pp->x;
+ paperheight = pp->y;
+ orientation = DMORIENT_PORTRAIT;
+ break;
+ }
+ }
+ if (paperindex < 0) {
+ /* try again in landscape */
+ pp = (POINT *) devcap;
+ for (i = 0; i < n; i++, pp++) {
+ if ((pp->x < paperheight + 20) && (pp->x > paperheight - 20) &&
+ (pp->y < paperwidth + 20) && (pp->y > paperwidth - 20)) {
+ paperindex = i;
+ paperwidth = pp->x;
+ paperheight = pp->y;
+ orientation = DMORIENT_LANDSCAPE;
+ break;
+ }
+ }
+ }
+ gs_free(wdev->memory, devcap, devcapsize, 1, "win_pr2_getdc");
+
+ /* get the dmPaperSize */
+ devcapsize = DeviceCapabilities(device, output, DC_PAPERS, NULL, NULL);
+ devcapsize *= sizeof(WORD);
+ if ((devcap = gs_malloc(wdev->memory, devcapsize, 1, "win_pr2_getdc")) == (LPBYTE) NULL)
+ return FALSE;
+ n = DeviceCapabilities(device, output, DC_PAPERS, devcap, NULL);
+ if ((paperindex >= 0) && (paperindex < n))
+ papersize = ((WORD *) devcap)[paperindex];
+ gs_free(wdev->memory, devcap, devcapsize, 1, "win_pr2_getdc");
+
+ /* get the paper name */
+ devcapsize = DeviceCapabilities(device, output, DC_PAPERNAMES, NULL, NULL);
+ devcapsize *= 64;
+ if ((devcap = gs_malloc(wdev->memory, devcapsize, 1, "win_pr2_getdc")) == (LPBYTE) NULL)
+ return FALSE;
+ n = DeviceCapabilities(device, output, DC_PAPERNAMES, devcap, NULL);
+ if ((paperindex >= 0) && (paperindex < n))
+ strcpy(papername, devcap + paperindex * 64);
+ gs_free(wdev->memory, devcap, devcapsize, 1, "win_pr2_getdc");
+
+ memcpy(pidevmode, podevmode, devmode_size);
+
+ pidevmode->dmFields = 0;
+
+ wdev->paper_name[0] = 0;
+
+ if ( (wdev->user_paper)
+ && (wdev->user_paper != papersize) ) {
+ papersize = wdev->user_paper;
+ paperheight = 0;
+ paperwidth = 0;
+ papername[0] = 0;
+ }
+ if (wdev->user_orient) {
+ orientation = wdev->user_orient;
+ }
+
+ pidevmode->dmFields &= ~(DM_PAPERSIZE | DM_ORIENTATION | DM_COLOR | DM_PAPERLENGTH | DM_PAPERWIDTH | DM_DUPLEX);
+ pidevmode->dmFields |= DM_DEFAULTSOURCE;
+ pidevmode->dmDefaultSource = 0;
+
+ if (orientation) {
+ wdev->user_orient = orientation;
+ }
+ if (papersize) {
+ wdev->user_paper = papersize;
+ strcpy (wdev->paper_name, papername);
+ }
+
+ if (paperheight && paperwidth) {
+ pidevmode->dmFields |= (DM_PAPERLENGTH | DM_PAPERWIDTH);
+ pidevmode->dmPaperWidth = paperwidth;
+ pidevmode->dmPaperLength = paperheight;
+ wdev->user_media_size[0] = paperwidth / 254.0 * 72.0;
+ wdev->user_media_size[1] = paperheight / 254.0 * 72.0;
+ }
+
+ if (DeviceCapabilities(device, output, DC_DUPLEX, NULL, NULL)) {
+ wdev->Duplex_set = 1;
+ }
+
+ win_pr2_update_win(wdev, pidevmode);
+
+ /* merge the entries */
+ DocumentProperties(NULL, hprinter, device, podevmode, pidevmode, DM_IN_BUFFER | DM_OUT_BUFFER);
+ ClosePrinter(hprinter);
+
+ /* now get a DC */
+ wdev->hdcprn = CreateDC(driver, device, NULL, podevmode);
+
+ if (wdev->win32_hdevmode == NULL)
+ wdev->win32_hdevmode = GlobalAlloc(0, devmode_size);
+
+ if (wdev->win32_hdevmode) {
+ LPDEVMODE pdevmode = (LPDEVMODE) GlobalLock(wdev->win32_hdevmode);
+ if (pdevmode) {
+ memcpy(pdevmode, podevmode, devmode_size);
+ GlobalUnlock(wdev->win32_hdevmode);
+ }
+ }
+
+ gs_free(wdev->memory, pidevmode, devmode_size, 1, "win_pr2_getdc");
+ gs_free(wdev->memory, podevmode, devmode_size, 1, "win_pr2_getdc");
+
+ if (wdev->hdcprn != (HDC) NULL)
+ return TRUE; /* success */
+
+ /* fall back to prompting user */
+ return FALSE;
+}
+
+/*
+ * Minimalist update of the wdev parameters (mainly for the
+ * UserSettings parameters).
+ */
+
+static int
+win_pr2_update_dev(gx_device_win_pr2 * dev, LPDEVMODE pdevmode)
+{
+ if (pdevmode == 0)
+ return FALSE;
+
+ if (pdevmode->dmFields & DM_COLOR) {
+ dev->user_color = pdevmode->dmColor;
+ }
+ if (pdevmode->dmFields & DM_ORIENTATION) {
+ dev->user_orient = pdevmode->dmOrientation;
+ }
+ if (pdevmode->dmFields & DM_PAPERSIZE) {
+ dev->user_paper = pdevmode->dmPaperSize;
+ dev->user_media_size[0] = pdevmode->dmPaperWidth / 254.0 * 72.0;
+ dev->user_media_size[1] = pdevmode->dmPaperLength / 254.0 * 72.0;
+ dev->paper_name[0] = 0; /* unknown paper size */
+ }
+ if (pdevmode->dmFields & DM_DUPLEX) {
+ dev->Duplex_set = 1;
+ dev->Duplex = pdevmode->dmDuplex == DMDUP_SIMPLEX ? false : true;
+ dev->tumble = pdevmode->dmDuplex == DMDUP_HORIZONTAL ? true : false;
+ }
+
+ return TRUE;
+}
+
+static int
+win_pr2_update_win(gx_device_win_pr2 * dev, LPDEVMODE pdevmode)
+{
+ if (dev->Duplex_set > 0) {
+ pdevmode->dmFields |= DM_DUPLEX;
+ pdevmode->dmDuplex = DMDUP_SIMPLEX;
+ if (dev->Duplex) {
+ if (dev->tumble == false) {
+ pdevmode->dmDuplex = DMDUP_VERTICAL;
+ } else {
+ pdevmode->dmDuplex = DMDUP_HORIZONTAL;
+ }
+ }
+ }
+
+ if (dev->user_color) {
+ pdevmode->dmColor = dev->user_color;
+ pdevmode->dmFields |= DM_COLOR;
+ }
+
+ if (dev->user_orient) {
+ pdevmode->dmFields |= DM_ORIENTATION;
+ pdevmode->dmOrientation = dev->user_orient;
+ }
+
+ if (dev->user_paper) {
+ pdevmode->dmFields |= DM_PAPERSIZE;
+ pdevmode->dmPaperSize = dev->user_paper;
+ }
+ return 0;
+}
+
+/********************************************************************************/
+
+#define BEGIN_ARRAY_PARAM(pread, pname, pa, psize, e)\
+ switch ( code = pread(dict.list, (param_name = pname), &(pa)) )\
+ {\
+ case 0:\
+ if ( (pa).size != psize )\
+ ecode = gs_note_error(gs_error_rangecheck);\
+ else {
+/* The body of the processing code goes here. */
+/* If it succeeds, it should do a 'break'; */
+/* if it fails, it should set ecode and fall through. */
+#define END_ARRAY_PARAM(pa, e)\
+ }\
+ goto e;\
+ default:\
+ ecode = code;\
+e: param_signal_error(dict.list, param_name, ecode);\
+ case 1:\
+ (pa).data = 0; /* mark as not filled */\
+ }
+
+/* Put the user params from UserSettings into our */
+/* internal variables. */
+static int
+win_pr2_read_user_settings(gx_device_win_pr2 * wdev, gs_param_list * plist)
+{
+ gs_param_dict dict;
+ gs_param_string docn = { 0 };
+ const char* dict_name = "UserSettings";
+ const char* param_name = "";
+ int code = 0;
+ int ecode = 0;
+
+ switch (code = param_begin_read_dict(plist, dict_name, &dict, false)) {
+ default:
+ param_signal_error(plist, dict_name, code);
+ return code;
+ case 1:
+ break;
+ case 0:
+ {
+ gs_param_int_array ia;
+
+ BEGIN_ARRAY_PARAM(param_read_int_array, "DocumentRange", ia, 2, ia)
+ if ((ia.data[0] < 0) ||
+ (ia.data[1] < 0) ||
+ (ia.data[0] > ia.data[1]))
+ ecode = gs_note_error(gs_error_rangecheck);
+ wdev->doc_page_begin = ia.data[0];
+ wdev->doc_page_end = ia.data[1];
+ END_ARRAY_PARAM(ia, doc_range_error)
+
+ BEGIN_ARRAY_PARAM(param_read_int_array, "SelectedRange", ia, 2, ia)
+ if ((ia.data[0] < 0) ||
+ (ia.data[1] < 0) ||
+ (ia.data[0] > ia.data[1]))
+ ecode = gs_note_error(gs_error_rangecheck);
+ wdev->user_page_begin = ia.data[0];
+ wdev->user_page_end = ia.data[1];
+ END_ARRAY_PARAM(ia, sel_range_error)
+
+ param_read_int(dict.list, "Copies", &wdev->user_copies);
+ param_read_int(dict.list, "Paper", &wdev->user_paper);
+ param_read_int(dict.list, "Orientation", &wdev->user_orient);
+ param_read_int(dict.list, "Color", &wdev->user_color);
+ param_read_int(dict.list, "MaxResolution", &wdev->max_dpi);
+
+ switch (code = param_read_string(dict.list, (param_name = "DocumentName"), &docn)) {
+ case 0:
+ if (docn.size < sizeof(wdev->doc_name))
+ break;
+ code = gs_error_rangecheck;
+ /* fall through */
+ default:
+ ecode = code;
+ param_signal_error(plist, param_name, ecode);
+ /* fall through */
+ case 1:
+ docn.data = 0;
+ break;
+ }
+
+ param_end_read_dict(plist, dict_name, &dict);
+
+ if (docn.data) {
+ memcpy(wdev->doc_name, docn.data, docn.size);
+ wdev->doc_name[docn.size] = 0;
+ }
+
+ wdev->print_copies = 1;
+
+ if (wdev->win32_hdevmode) {
+ LPDEVMODE devmode = (LPDEVMODE) GlobalLock(wdev->win32_hdevmode);
+ if (devmode) {
+ devmode->dmCopies = wdev->user_copies;
+ devmode->dmPaperSize = wdev->user_paper;
+ devmode->dmOrientation = wdev->user_orient;
+ devmode->dmColor = wdev->user_color;
+ GlobalUnlock(wdev->win32_hdevmode);
+ }
+ }
+ }
+ break;
+ }
+
+ return code;
+}
+
+static int
+win_pr2_write_user_settings(gx_device_win_pr2 * wdev, gs_param_list * plist)
+{
+ gs_param_dict dict;
+ gs_param_int_array range;
+ gs_param_float_array box;
+ gs_param_string docn;
+ gs_param_string papn;
+ int array[2];
+ const char* pname = "UserSettings";
+ int code;
+
+ dict.size = 12;
+ code = param_begin_write_dict(plist, pname, &dict, false);
+ if (code < 0) return code;
+
+ array[0] = wdev->doc_page_begin;
+ array[1] = wdev->doc_page_end;
+ range.data = array;
+ range.size = 2;
+ range.persistent = false;
+ code = param_write_int_array(dict.list, "DocumentRange", &range);
+ if (code < 0) goto error;
+
+ array[0] = wdev->user_page_begin;
+ array[1] = wdev->user_page_end;
+ range.data = array;
+ range.size = 2;
+ range.persistent = false;
+ code = param_write_int_array(dict.list, "SelectedRange", &range);
+ if (code < 0) goto error;
+
+ box.data = wdev->user_media_size;
+ box.size = 2;
+ box.persistent = false;
+ code = param_write_float_array(dict.list, "MediaSize", &box);
+ if (code < 0) goto error;
+
+ code = param_write_int(dict.list, "Copies", &wdev->user_copies);
+ if (code < 0) goto error;
+
+ code = param_write_int(dict.list, "Paper", &wdev->user_paper);
+ if (code < 0) goto error;
+
+ code = param_write_int(dict.list, "Orientation", &wdev->user_orient);
+ if (code < 0) goto error;
+
+ code = param_write_int(dict.list, "Color", &wdev->user_color);
+ if (code < 0) goto error;
+
+ code = param_write_int(dict.list, "MaxResolution", &wdev->max_dpi);
+ if (code < 0) goto error;
+
+ code = param_write_int(dict.list, "PrintCopies", &wdev->print_copies);
+ if (code < 0) goto error;
+
+ docn.data = (const byte*)wdev->doc_name;
+ docn.size = strlen(wdev->doc_name);
+ docn.persistent = false;
+
+ code = param_write_string(dict.list, "DocumentName", &docn);
+ if (code < 0) goto error;
+
+ papn.data = (const byte*)wdev->paper_name;
+ papn.size = strlen(wdev->paper_name);
+ papn.persistent = false;
+
+ code = param_write_string(dict.list, "PaperName", &papn);
+ if (code < 0) goto error;
+
+ code = param_write_bool(dict.list, "UserChangedSettings", &wdev->user_changed_settings);
+
+error:
+ param_end_write_dict(plist, pname, &dict);
+ return code;
+}
+
+/********************************************************************************/
+
+/* Show up a dialog for the user to choose a printer and a paper size.
+ * If mode == 3, then automatically select the default Windows printer
+ * instead of asking the user.
+ */
+
+static int
+win_pr2_print_setup_interaction(gx_device_win_pr2 * wdev, int mode)
+{
+ PRINTDLG pd;
+ LPDEVMODE devmode;
+ LPDEVNAMES devnames;
+
+ wdev->user_changed_settings = FALSE;
+ wdev->query_user = mode;
+
+ memset(&pd, 0, sizeof(pd));
+ pd.lStructSize = sizeof(pd);
+ pd.hwndOwner = PARENT_WINDOW;
+
+ switch (mode) {
+ case 2: pd.Flags = PD_PRINTSETUP; break;
+ case 3: pd.Flags = PD_RETURNDEFAULT; break;
+ default: pd.Flags = 0; break;
+ }
+
+ pd.Flags |= PD_USEDEVMODECOPIES;
+
+ pd.nMinPage = wdev->doc_page_begin;
+ pd.nMaxPage = wdev->doc_page_end;
+ pd.nFromPage = wdev->user_page_begin;
+ pd.nToPage = wdev->user_page_end;
+ pd.nCopies = wdev->user_copies;
+
+ /* Show the Print Setup dialog and let the user choose a printer
+ * and a paper size/orientation.
+ */
+
+ if (!PrintDlg(&pd)) return FALSE;
+
+ devmode = (LPDEVMODE) GlobalLock(pd.hDevMode);
+ devnames = (LPDEVNAMES) GlobalLock(pd.hDevNames);
+
+ wdev->user_changed_settings = TRUE;
+ if (wdev->use_old_spool_name) {
+ gs_sprintf(wdev->fname, "\\\\spool\\%s", (char*)(devnames)+(devnames->wDeviceOffset));
+ } else {
+ gs_sprintf(wdev->fname, "%%printer%%%s", (char*)(devnames)+(devnames->wDeviceOffset));
+ }
+
+ if (mode == 3) {
+ devmode->dmCopies = wdev->user_copies * wdev->print_copies;
+ pd.nCopies = 1;
+ }
+
+ wdev->user_page_begin = pd.nFromPage;
+ wdev->user_page_end = pd.nToPage;
+ wdev->user_copies = devmode->dmCopies;
+ wdev->print_copies = pd.nCopies;
+ wdev->user_media_size[0] = devmode->dmPaperWidth / 254.0 * 72.0;
+ wdev->user_media_size[1] = devmode->dmPaperLength / 254.0 * 72.0;
+ wdev->user_paper = devmode->dmPaperSize;
+ wdev->user_orient = devmode->dmOrientation;
+ wdev->user_color = devmode->dmColor;
+
+ if (devmode->dmFields & DM_DUPLEX) {
+ wdev->Duplex_set = 1;
+ wdev->Duplex = devmode->dmDuplex == DMDUP_SIMPLEX ? false : true;
+ wdev->tumble = devmode->dmDuplex == DMDUP_HORIZONTAL ? true : false;
+ }
+
+ {
+ float xppinch = 0;
+ float yppinch = 0;
+ const char* driver = (char*)(devnames)+(devnames->wDriverOffset);
+ const char* device = (char*)(devnames)+(devnames->wDeviceOffset);
+ const char* output = (char*)(devnames)+(devnames->wOutputOffset);
+
+ HDC hic = CreateIC(driver, device, output, devmode);
+
+ if (hic) {
+ xppinch = (float)GetDeviceCaps(hic, LOGPIXELSX);
+ yppinch = (float)GetDeviceCaps(hic, LOGPIXELSY);
+ wdev->user_media_size[0] = GetDeviceCaps(hic, PHYSICALWIDTH) * 72.0 / xppinch;
+ wdev->user_media_size[1] = GetDeviceCaps(hic, PHYSICALHEIGHT) * 72.0 / yppinch;
+ DeleteDC(hic);
+ }
+ }
+
+ devmode = NULL;
+ devnames = NULL;
+
+ GlobalUnlock(pd.hDevMode);
+ GlobalUnlock(pd.hDevNames);
+
+ if (wdev->win32_hdevmode != NULL) {
+ GlobalFree(wdev->win32_hdevmode);
+ }
+ if (wdev->win32_hdevnames != NULL) {
+ GlobalFree(wdev->win32_hdevnames);
+ }
+
+ wdev->win32_hdevmode = pd.hDevMode;
+ wdev->win32_hdevnames = pd.hDevNames;
+
+ return TRUE;
+}
+
+/* Check that we are dealing with an original device. If this
+ * happens to be a copy made by "copydevice", we will have to
+ * copy the original's handles to the associated Win32 params.
+ */
+
+static void
+win_pr2_copy_check(gx_device_win_pr2 * wdev)
+{
+ HGLOBAL hdevmode = wdev->win32_hdevmode;
+ HGLOBAL hdevnames = wdev->win32_hdevnames;
+ DWORD devmode_len = (hdevmode) ? GlobalSize(hdevmode) : 0;
+ DWORD devnames_len = (hdevnames) ? GlobalSize(hdevnames) : 0;
+
+ if (wdev->original_device == wdev)
+ return;
+
+ wdev->hdcprn = NULL;
+ wdev->win32_hdevmode = NULL;
+ wdev->win32_hdevnames = NULL;
+
+ wdev->original_device = wdev;
+
+ if (devmode_len) {
+ wdev->win32_hdevmode = GlobalAlloc(0, devmode_len);
+ if (wdev->win32_hdevmode) {
+ memcpy(GlobalLock(wdev->win32_hdevmode), GlobalLock(hdevmode), devmode_len);
+ GlobalUnlock(wdev->win32_hdevmode);
+ GlobalUnlock(hdevmode);
+ }
+ }
+
+ if (devnames_len) {
+ wdev->win32_hdevnames = GlobalAlloc(0, devnames_len);
+ if (wdev->win32_hdevnames) {
+ memcpy(GlobalLock(wdev->win32_hdevnames), GlobalLock(hdevnames), devnames_len);
+ GlobalUnlock(wdev->win32_hdevnames);
+ GlobalUnlock(hdevnames);
+ }
+ }
+}
+
+/* Modeless dialog box - Cancel printing */
+BOOL CALLBACK
+CancelDlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
+{
+ switch (message) {
+ case WM_INITDIALOG:
+ SetWindowText(hDlg, szAppName);
+ return TRUE;
+ case WM_COMMAND:
+ switch (LOWORD(wParam)) {
+ case IDCANCEL:
+ DestroyWindow(hDlg);
+ EndDialog(hDlg, 0);
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+BOOL CALLBACK
+AbortProc2(HDC hdcPrn, int code)
+{
+ process_interrupts(NULL);
+ if (code == SP_OUTOFDISK)
+ return (FALSE); /* cancel job */
+ return (TRUE);
+}
diff --git a/devices/gdevwprn.c b/devices/gdevwprn.c
new file mode 100644
index 000000000..3c7346669
--- /dev/null
+++ b/devices/gdevwprn.c
@@ -0,0 +1,680 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/*
+ * Microsoft Windows 3.n printer driver for Ghostscript.
+ *
+ * Original version by Russell Lang and
+ * L. Peter Deutsch, Aladdin Enterprises.
+ */
+
+/* This driver is very slow and as of 2002-09-14 it does not work. */
+
+#include "gdevmswn.h"
+#include "gp.h"
+#include "gpcheck.h"
+#include "commdlg.h"
+
+#define PARENT_WINDOW HWND_DESKTOP
+
+/*
+ ****** NOTE: this module and gdevwddb should be refactored.
+ * The drawing routines are almost identical.
+ * The differences are that the mswinprn doesn't use an extra
+ * palette (gdevwddb.c could probably be made to work with
+ * one palette also), mswinprn doesn't call win_update() because
+ * hwndimg doesn't exist, and the HDC is hdcmf not hdcbit.
+ ******/
+
+/* Make sure we cast to the correct structure type. */
+typedef struct gx_device_win_prn_s gx_device_win_prn;
+
+#undef wdev
+#define wdev ((gx_device_win_prn *)dev)
+
+/* Forward references */
+static void near win_prn_addtool(gx_device_win_prn *, int);
+static void near win_prn_maketools(gx_device_win_prn *, HDC);
+static void near win_prn_destroytools(gx_device_win_prn *);
+BOOL CALLBACK _export AbortProc(HDC, int);
+
+/* Device procedures */
+
+/* See gxdevice.h for the definitions of the procedures. */
+static dev_proc_open_device(win_prn_open);
+static dev_proc_close_device(win_prn_close);
+static dev_proc_sync_output(win_prn_sync_output);
+static dev_proc_output_page(win_prn_output_page);
+static dev_proc_map_rgb_color(win_prn_map_rgb_color);
+static dev_proc_fill_rectangle(win_prn_fill_rectangle);
+static dev_proc_tile_rectangle(win_prn_tile_rectangle);
+static dev_proc_copy_mono(win_prn_copy_mono);
+static dev_proc_copy_color(win_prn_copy_color);
+static dev_proc_draw_line(win_prn_draw_line);
+
+/* The device descriptor */
+struct gx_device_win_prn_s {
+ gx_device_common;
+ gx_device_win_common;
+
+ /* Handles */
+
+ HPEN hpen, *hpens;
+ uint hpensize;
+ HBRUSH hbrush, *hbrushs;
+ uint hbrushsize;
+#define select_brush(color)\
+ if (wdev->hbrush != wdev->hbrushs[color])\
+ { wdev->hbrush = wdev->hbrushs[color];\
+ SelectObject(wdev->hdcmf,wdev->hbrush);\
+ }
+ /* A staging bitmap for copy_mono. */
+ /* We want one big enough to handle the standard 16x16 halftone; */
+ /* this is also big enough for ordinary-size characters. */
+
+#define bmWidthBytes 4 /* must be even */
+#define bmWidthBits (bmWidthBytes * 8)
+#define bmHeight 32
+ HBITMAP FAR hbmmono;
+ HDC FAR hdcmono;
+ gx_bitmap_id bm_id;
+
+ HDC hdcprn;
+ HDC hdcmf;
+ char mfname[gp_file_name_sizeof];
+ DLGPROC lpfnAbortProc;
+};
+static const gx_device_procs win_prn_procs =
+{
+ win_prn_open,
+ NULL, /* get_initial_matrix */
+ win_prn_sync_output,
+ win_prn_output_page,
+ win_prn_close,
+ win_prn_map_rgb_color,
+ win_map_color_rgb,
+ win_prn_fill_rectangle,
+ win_prn_tile_rectangle,
+ win_prn_copy_mono,
+ win_prn_copy_color,
+ win_prn_draw_line,
+ NULL, /* get_bits */
+ NULL, /* get_params */
+ NULL, /* put_params */
+ NULL, /* map_cmyk_color */
+ win_get_xfont_procs
+};
+gx_device_win_prn far_data gs_mswinprn_device =
+{
+ std_device_std_body(gx_device_win_prn, &win_prn_procs, "mswinprn",
+ INITIAL_WIDTH, INITIAL_HEIGHT, /* win_open() fills these in later */
+ INITIAL_RESOLUTION, INITIAL_RESOLUTION /* win_open() fills these in later */
+ ),
+ {0}, /* std_procs */
+ 0, /* BitsPerPixel */
+ 2, /* nColors */
+};
+
+/* Open the win_prn driver */
+static int
+win_prn_open(gx_device * dev)
+{
+ int depth;
+ PRINTDLG pd;
+ FILE *f;
+ POINT offset;
+ POINT size;
+ float m[4];
+
+ memset(&pd, 0, sizeof(PRINTDLG));
+ pd.lStructSize = sizeof(PRINTDLG);
+ pd.hwndOwner = PARENT_WINDOW;
+ pd.Flags = PD_PRINTSETUP | PD_RETURNDC;
+ if (!PrintDlg(&pd)) {
+ /* device not opened - exit ghostscript */
+ return gs_error_limitcheck;
+ }
+ GlobalFree(pd.hDevMode);
+ GlobalFree(pd.hDevNames);
+ pd.hDevMode = pd.hDevNames = NULL;
+ wdev->hdcprn = pd.hDC;
+ if (!(GetDeviceCaps(wdev->hdcprn, RASTERCAPS) != RC_BITBLT)) {
+ DeleteDC(wdev->hdcprn);
+ return gs_error_limitcheck;
+ }
+ wdev->lpfnAbortProc = (DLGPROC) AbortProc;
+ Escape(wdev->hdcprn, SETABORTPROC, 0, (LPSTR) wdev->lpfnAbortProc, NULL);
+ if (Escape(wdev->hdcprn, STARTDOC, strlen(szAppName), szAppName, NULL) <= 0) {
+ DeleteDC(wdev->hdcprn);
+ return gs_error_limitcheck;
+ }
+ f = gp_open_scratch_file(dev->memory,
+ gp_scratch_file_name_prefix,
+ wdev->mfname, "wb");
+ if (f == (FILE *) NULL) {
+ Escape(wdev->hdcprn, ENDDOC, 0, NULL, NULL);
+ DeleteDC(wdev->hdcprn);
+ return gs_error_limitcheck;
+ }
+ unlink(wdev->mfname);
+ wdev->hdcmf = CreateMetaFile(wdev->mfname);
+
+ dev->x_pixels_per_inch = (float)GetDeviceCaps(wdev->hdcprn, LOGPIXELSX);
+ dev->y_pixels_per_inch = (float)GetDeviceCaps(wdev->hdcprn, LOGPIXELSY);
+ Escape(wdev->hdcprn, GETPHYSPAGESIZE, 0, NULL, (LPPOINT) & size);
+ dev->width = size.x;
+ dev->height = size.y;
+ Escape(wdev->hdcprn, GETPRINTINGOFFSET, 0, NULL, (LPPOINT) & offset);
+ m[0] /*left */ = offset.x / dev->x_pixels_per_inch;
+ m[3] /*top */ = offset.y / dev->y_pixels_per_inch;
+ m[2] /*right */ =
+ (size.x - offset.x - GetDeviceCaps(wdev->hdcprn, HORZRES))
+ / dev->x_pixels_per_inch;
+ m[1] /*bottom */ =
+ (size.y - offset.y - GetDeviceCaps(wdev->hdcprn, VERTRES))
+ / dev->y_pixels_per_inch
+ + 0.15; /* hack to add a bit more margin for deskjet printer */
+ gx_device_set_margins(dev, m, true);
+
+ /* Set parameters that were unknown before opening device */
+ /* Find out if the device supports color */
+ /* We recognize 2, 16 or 256 color devices */
+ depth = GetDeviceCaps(wdev->hdcprn, PLANES) * GetDeviceCaps(wdev->hdcprn, BITSPIXEL);
+ if (depth >= 8) { /* use 64 static colors and 166 dynamic colors from 8 planes */
+ static const gx_device_color_info win_256color = dci_color(8, 31, 4);
+
+ dev->color_info = win_256color;
+ wdev->nColors = 64;
+ } else if (depth >= 4) {
+ static const gx_device_color_info win_16ega_color = dci_color(4, 2, 3);
+
+ dev->color_info = win_16ega_color;
+ wdev->nColors = 16;
+ } else { /* default is black_and_white */
+ wdev->nColors = 2;
+ }
+
+ /* copy encode/decode procedures */
+ wdev->procs.encode_color = wdev->procs.map_rgb_color;
+ wdev->procs.decode_color = wdev->procs.map_color_rgb;
+ if (dev->color_info.depth == 1) {
+ wdev->procs.get_color_mapping_procs =
+ gx_default_DevGray_get_color_mapping_procs;
+ wdev->procs.get_color_comp_index =
+ gx_default_DevGray_get_color_comp_index;
+ }
+ else {
+ wdev->procs.get_color_mapping_procs =
+ gx_default_DevRGB_get_color_mapping_procs;
+ wdev->procs.get_color_comp_index =
+ gx_default_DevRGB_get_color_comp_index;
+ }
+
+ /* create palette for display */
+ if ((wdev->limgpalette = win_makepalette((gx_device_win *) dev))
+ == (LPLOGPALETTE) NULL) {
+ HMETAFILE hmf = CloseMetaFile(wdev->hdcmf);
+
+ DeleteMetaFile(hmf);
+ unlink(wdev->mfname);
+ Escape(wdev->hdcprn, ENDDOC, 0, NULL, NULL);
+ DeleteDC(wdev->hdcprn);
+ return win_nomemory();
+ }
+ wdev->himgpalette = CreatePalette(wdev->limgpalette);
+
+ /* Create the bitmap and DC for copy_mono. */
+ wdev->hbmmono = CreateBitmap(bmWidthBits, bmHeight, 1, 1, NULL);
+ wdev->hdcmono = CreateCompatibleDC(wdev->hdcprn);
+ if (wdev->hbmmono == NULL || wdev->hdcmono == NULL) {
+ HMETAFILE hmf = CloseMetaFile(wdev->hdcmf);
+
+ DeleteMetaFile(hmf);
+ unlink(wdev->mfname);
+ Escape(wdev->hdcprn, ENDDOC, 0, NULL, NULL);
+ DeleteDC(wdev->hdcprn);
+ gs_free((char *)(wdev->limgpalette), 1, sizeof(LOGPALETTE) +
+ (1 << (wdev->color_info.depth)) * sizeof(PALETTEENTRY),
+ "win_prn_open");
+ return win_nomemory();
+ }
+ SetMapMode(wdev->hdcmono, GetMapMode(wdev->hdcprn));
+ SelectObject(wdev->hdcmono, wdev->hbmmono);
+ (void)SelectPalette(wdev->hdcmf, wdev->himgpalette, FALSE);
+ RealizePalette(wdev->hdcmf);
+ win_prn_maketools(wdev, wdev->hdcmf);
+ wdev->bm_id = gx_no_bitmap_id;
+
+ return 0;
+}
+
+/* Close the win_prn driver */
+static int
+win_prn_close(gx_device * dev)
+{
+ HMETAFILE hmf;
+
+ /* Free resources */
+ Escape(wdev->hdcprn, ENDDOC, 0, NULL, NULL);
+ DeleteDC(wdev->hdcprn);
+ hmf = CloseMetaFile(wdev->hdcmf);
+ DeleteMetaFile(hmf);
+ unlink(wdev->mfname);
+
+ win_prn_destroytools(wdev);
+ DeleteDC(wdev->hdcmono);
+ DeleteObject(wdev->hbmmono);
+ DeleteObject(wdev->himgpalette);
+ gs_free((char *)(wdev->limgpalette), 1, sizeof(LOGPALETTE) +
+ (1 << (wdev->color_info.depth)) * sizeof(PALETTEENTRY),
+ "win_prn_close");
+ return (0);
+}
+
+/* Do nothing */
+int
+win_prn_sync_output(gx_device * dev)
+{
+ return 0;
+}
+
+/* Write page to printer */
+int
+win_prn_output_page(gx_device * dev, int num_copies, int flush)
+{
+ RECT rect;
+ HMETAFILE hmf;
+
+ hmf = CloseMetaFile(wdev->hdcmf);
+
+ Escape(wdev->hdcprn, NEXTBAND, 0, NULL, (LPRECT) & rect);
+ while (!IsRectEmpty(&rect)) {
+ PlayMetaFile(wdev->hdcprn, hmf);
+ if (Escape(wdev->hdcprn, NEXTBAND, 0, NULL, (LPRECT) & rect) <= 0)
+ break;
+ }
+ DeleteMetaFile(hmf);
+ unlink(wdev->mfname);
+ wdev->hdcmf = CreateMetaFile(wdev->mfname);
+ (void)SelectPalette(wdev->hdcmf, wdev->himgpalette, FALSE);
+ RealizePalette(wdev->hdcmf);
+ SelectObject(wdev->hdcmf, wdev->hpen);
+ SelectObject(wdev->hdcmf, wdev->hbrush);
+
+ return gx_finish_output_page(dev, num_copies, flush);
+}
+
+/* Map a r-g-b color to the colors available under Windows */
+static gx_color_index
+win_prn_map_rgb_color(gx_device * dev, const gx_color_value cv[])
+{
+ int i = wdev->nColors;
+ gx_color_index color = win_map_rgb_color(dev, cv);
+
+ if (color != i)
+ return color;
+ (void)SelectPalette(wdev->hdcmf, wdev->himgpalette, FALSE);
+ RealizePalette(wdev->hdcmf);
+ win_prn_addtool(wdev, i);
+
+ return color;
+}
+
+/* Macro for filling a rectangle with a color. */
+/* Note that it starts with a declaration. */
+#define fill_rect(x, y, w, h, color)\
+RECT rect;\
+rect.left = x, rect.top = y;\
+rect.right = x + w, rect.bottom = y + h;\
+FillRect(wdev->hdcmf, &rect, wdev->hbrushs[(int)color])
+
+/* Fill a rectangle. */
+static int
+win_prn_fill_rectangle(gx_device * dev, int x, int y, int w, int h,
+ gx_color_index color)
+{
+ fit_fill(dev, x, y, w, h);
+ /* Use PatBlt for filling. Special-case black. */
+ if (color == 0)
+ PatBlt(wdev->hdcmf, x, y, w, h, rop_write_0s);
+ else {
+ select_brush((int)color);
+ PatBlt(wdev->hdcmf, x, y, w, h, rop_write_pattern);
+ }
+
+ return 0;
+}
+
+/* Tile a rectangle. If neither color is transparent, */
+/* pre-clear the rectangle to color0 and just tile with color1. */
+/* This is faster because of how win_copy_mono is implemented. */
+/* Note that this also does the right thing for colored tiles. */
+static int
+win_prn_tile_rectangle(gx_device * dev, const gx_tile_bitmap * tile,
+ int x, int y, int w, int h, gx_color_index czero, gx_color_index cone,
+ int px, int py)
+{
+ fit_fill(dev, x, y, w, h);
+ if (czero != gx_no_color_index && cone != gx_no_color_index) {
+ fill_rect(x, y, w, h, czero);
+ czero = gx_no_color_index;
+ }
+ if (tile->raster == bmWidthBytes && tile->size.y <= bmHeight &&
+ (px | py) == 0 && cone != gx_no_color_index
+ ) { /* We can do this much more efficiently */
+ /* by using the internal algorithms of copy_mono */
+ /* and gx_default_tile_rectangle. */
+ int width = tile->size.x;
+ int height = tile->size.y;
+ int rwidth = tile->rep_width;
+ int irx = ((rwidth & (rwidth - 1)) == 0 ? /* power of 2 */
+ x & (rwidth - 1) :
+ x % rwidth);
+ int ry = y % tile->rep_height;
+ int icw = width - irx;
+ int ch = height - ry;
+ int ex = x + w, ey = y + h;
+ int fex = ex - width, fey = ey - height;
+ int cx, cy;
+
+ select_brush((int)cone);
+
+ if (tile->id != wdev->bm_id || tile->id == gx_no_bitmap_id) {
+ wdev->bm_id = tile->id;
+ SetBitmapBits(wdev->hbmmono,
+ (DWORD) (bmWidthBytes * tile->size.y),
+ (BYTE *) tile->data);
+ }
+#define copy_tile(srcx, srcy, tx, ty, tw, th)\
+ BitBlt(wdev->hdcmf, tx, ty, tw, th, wdev->hdcmono, srcx, srcy, rop_write_at_1s)
+
+ if (ch > h)
+ ch = h;
+ for (cy = y;;) {
+ if (w <= icw)
+ copy_tile(irx, ry, x, cy, w, ch);
+ else {
+ copy_tile(irx, ry, x, cy, icw, ch);
+ cx = x + icw;
+ while (cx <= fex) {
+ copy_tile(0, ry, cx, cy, width, ch);
+ cx += width;
+ }
+ if (cx < ex) {
+ copy_tile(0, ry, cx, cy, ex - cx, ch);
+ }
+ }
+ if ((cy += ch) >= ey)
+ break;
+ ch = (cy > fey ? ey - cy : height);
+ ry = 0;
+ }
+
+ return 0;
+ }
+ return gx_default_tile_rectangle(dev, tile, x, y, w, h, czero, cone, px, py);
+}
+
+/* Draw a line */
+static int
+win_prn_draw_line(gx_device * dev, int x0, int y0, int x1, int y1,
+ gx_color_index color)
+{
+ if (wdev->hpen != wdev->hpens[(int)color]) {
+ wdev->hpen = wdev->hpens[(int)color];
+ SelectObject(wdev->hdcmf, wdev->hpen);
+ }
+ MoveToEx(wdev->hdcmf, x0, y0, NULL);
+ LineTo(wdev->hdcmf, x1, y1);
+ return 0;
+}
+
+/* Copy a monochrome bitmap. The colors are given explicitly. */
+/* Color = gx_no_color_index means transparent (no effect on the image). */
+static int
+win_prn_copy_mono(gx_device * dev,
+ const byte * base, int sourcex, int raster, gx_bitmap_id id,
+ int x, int y, int w, int h,
+ gx_color_index zero, gx_color_index one)
+{
+ int endx;
+ const byte *ptr_line;
+ int width_bytes, height;
+ DWORD rop = rop_write_at_1s;
+ int color;
+ BYTE aBit[bmWidthBytes * bmHeight];
+ BYTE *aptr = aBit;
+
+ fit_copy(dev, base, sourcex, raster, id, x, y, w, h);
+
+ if (sourcex & ~7) {
+ base += sourcex >> 3;
+ sourcex &= 7;
+ }
+ /* Break up large transfers into smaller ones. */
+ while ((endx = sourcex + w) > bmWidthBits) {
+ int lastx = (endx - 1) & -bmWidthBits;
+ int subw = endx - lastx;
+ int code = win_prn_copy_mono(dev, base, lastx,
+ raster, gx_no_bitmap_id,
+ x + lastx - sourcex, y,
+ subw, h, zero, one);
+
+ if (code < 0)
+ return code;
+ w -= subw;
+ }
+ while (h > bmHeight) {
+ int code;
+
+ h -= bmHeight;
+ code = win_prn_copy_mono(dev, base + h * raster, sourcex,
+ raster, gx_no_bitmap_id,
+ x, y + h, w, bmHeight, zero, one);
+ if (code < 0)
+ return code;
+ }
+
+ width_bytes = (sourcex + w + 7) >> 3;
+ ptr_line = base;
+
+ if (zero == gx_no_color_index) {
+ if (one == gx_no_color_index)
+ return 0;
+ color = (int)one;
+ if (color == 0)
+ rop = rop_write_0_at_1s;
+ else
+ select_brush(color);
+ } else {
+ if (one == gx_no_color_index) {
+ color = (int)zero;
+ rop = rop_write_at_0s;
+ } else { /* Pre-clear the rectangle to zero */
+ fill_rect(x, y, w, h, zero);
+ color = (int)one;
+ }
+ select_brush(color);
+ }
+
+ if (id != wdev->bm_id || id == gx_no_bitmap_id) {
+ wdev->bm_id = id;
+ if (raster == bmWidthBytes) { /* We can do the whole thing in a single transfer! */
+ SetBitmapBits(wdev->hbmmono,
+ (DWORD) (bmWidthBytes * h),
+ (BYTE *) base);
+ } else {
+ for (height = h; height--;
+ ptr_line += raster, aptr += bmWidthBytes
+ ) { /* Pack the bits into the bitmap. */
+ switch (width_bytes) {
+ default:
+ memcpy(aptr, ptr_line, width_bytes);
+ break;
+ case 4:
+ aptr[3] = ptr_line[3];
+ case 3:
+ aptr[2] = ptr_line[2];
+ case 2:
+ aptr[1] = ptr_line[1];
+ case 1:
+ aptr[0] = ptr_line[0];
+ }
+ }
+ SetBitmapBits(wdev->hbmmono,
+ (DWORD) (bmWidthBytes * h),
+ &aBit[0]);
+ }
+ }
+ BitBlt(wdev->hdcmf, x, y, w, h, wdev->hdcmono, sourcex, 0, rop);
+ return 0;
+}
+
+/* Copy a color pixel map. This is just like a bitmap, except that */
+/* each pixel takes 8 or 4 bits instead of 1 when device driver has color. */
+static int
+win_prn_copy_color(gx_device * dev,
+ const byte * base, int sourcex, int raster, gx_bitmap_id id,
+ int x, int y, int w, int h)
+{
+ fit_copy(dev, base, sourcex, raster, id, x, y, w, h);
+
+ if (gx_device_has_color(dev)) {
+ switch (dev->color_info.depth) {
+ case 8:
+ {
+ int xi, yi;
+ int skip = raster - w;
+ const byte *sptr = base + sourcex;
+
+ if (w <= 0)
+ return 0;
+ if (x < 0 || x + w > dev->width)
+ return_error(gs_error_rangecheck);
+ for (yi = y; yi - y < h; yi++) {
+ for (xi = x; xi - x < w; xi++) {
+ int color = *sptr++;
+
+ SetPixel(wdev->hdcmf, xi, yi, PALETTEINDEX(color));
+ }
+ sptr += skip;
+ }
+ }
+ break;
+ case 4:
+ { /* color device, four bits per pixel */
+ const byte *line = base + (sourcex >> 1);
+ int dest_y = y, end_x = x + w;
+
+ if (w <= 0)
+ return 0;
+ while (h--) { /* for each line */
+ const byte *source = line;
+ register int dest_x = x;
+
+ if (sourcex & 1) { /* odd nibble first */
+ int color = *source++ & 0xf;
+
+ SetPixel(wdev->hdcmf, dest_x, dest_y, PALETTEINDEX(color));
+ dest_x++;
+ }
+ /* Now do full bytes */
+ while (dest_x < end_x) {
+ int color = *source >> 4;
+
+ SetPixel(wdev->hdcmf, dest_x, dest_y, PALETTEINDEX(color));
+ dest_x++;
+ if (dest_x < end_x) {
+ color = *source++ & 0xf;
+ SetPixel(wdev->hdcmf, dest_x, dest_y, PALETTEINDEX(color));
+ dest_x++;
+ }
+ }
+ dest_y++;
+ line += raster;
+ }
+ }
+ break;
+ default:
+ return (-1); /* panic */
+ }
+ } else
+ /* monochrome device: one bit per pixel */
+ { /* bitmap is the same as win_copy_mono: one bit per pixel */
+ win_prn_copy_mono(dev, base, sourcex, raster, id, x, y, w, h,
+ (gx_color_index) 0,
+ (gx_color_index) (dev->color_info.depth == 8 ? 63 : dev->color_info.max_gray));
+ }
+ return 0;
+}
+
+/* ------ Internal routines ------ */
+
+#undef wdev
+
+static void near
+win_prn_addtool(gx_device_win_prn * wdev, int i)
+{
+ wdev->hpens[i] = CreatePen(PS_SOLID, 1, PALETTEINDEX(i));
+ wdev->hbrushs[i] = CreateSolidBrush(PALETTEINDEX(i));
+}
+
+static void near
+win_prn_maketools(gx_device_win_prn * wdev, HDC hdc)
+{
+ int i;
+
+ wdev->hpensize = (1 << (wdev->color_info.depth)) * sizeof(HPEN);
+ wdev->hpens = (HPEN *) gs_malloc(wdev->memory, 1, wdev->hpensize,
+ "win_prn_maketools(pens)");
+ wdev->hbrushsize = (1 << (wdev->color_info.depth)) * sizeof(HBRUSH);
+ wdev->hbrushs = (HBRUSH *) gs_malloc(wdev->memory, 1, wdev->hbrushsize,
+ "win_prn_maketools(brushes)");
+ if (wdev->hpens && wdev->hbrushs) {
+ for (i = 0; i < wdev->nColors; i++)
+ win_prn_addtool(wdev, i);
+
+ wdev->hpen = wdev->hpens[0];
+ SelectObject(hdc, wdev->hpen);
+
+ wdev->hbrush = wdev->hbrushs[0];
+ SelectObject(hdc, wdev->hbrush);
+ }
+}
+
+static void near
+win_prn_destroytools(gx_device_win_prn * wdev)
+{
+ int i;
+
+ for (i = 0; i < wdev->nColors; i++) {
+ DeleteObject(wdev->hpens[i]);
+ DeleteObject(wdev->hbrushs[i]);
+ }
+ gs_free(wdev->memory, (char *)wdev->hbrushs, 1, wdev->hbrushsize,
+ "win_prn_destroytools(brushes)");
+ gs_free(wdev->memory, (char *)wdev->hpens, 1, wdev->hpensize,
+ "win_prn_destroytools(pens)");
+}
+
+BOOL CALLBACK _export
+AbortProc(HDC hdcPrn, int code)
+{
+ process_interrupts(NULL);
+ if (code == SP_OUTOFDISK)
+ return (FALSE); /* cancel job */
+ return (TRUE);
+}
diff --git a/devices/gdevx.c b/devices/gdevx.c
new file mode 100644
index 000000000..36fae6ca6
--- /dev/null
+++ b/devices/gdevx.c
@@ -0,0 +1,1298 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* X Windows driver for Ghostscript library */
+#include "gx.h" /* for gx_bitmap; includes std.h */
+#include "math_.h"
+#include "memory_.h"
+#include "x_.h"
+#include "gserrors.h"
+#include "gsmatrix.h" /* for gscoord.h */
+#include "gscoord.h" /* for gs_currentmatrix */
+#include "gsdevice.h" /* for gs_currentdevice */
+#include "gsparam.h"
+#include "gxdevice.h"
+#include "gxpath.h"
+#include "gxgetbit.h"
+#include "gxiparam.h"
+#include "gsiparm2.h"
+#include "gxdevmem.h"
+#include "gdevx.h"
+#include "gdevkrnlsclass.h" /* 'standard' built in subclasses, currently First/Last Page and obejct filter */
+
+/* Define whether to try to read back exposure events after XGetImage. */
+/****** THIS IS USELESS. XGetImage DOES NOT GENERATE EXPOSURE EVENTS. ******/
+#define GET_IMAGE_EXPOSURES 0
+
+/* GC descriptors */
+private_st_device_X();
+
+/* Forward references */
+static int x_copy_image(gx_device_X * xdev, const byte * base, int sourcex,
+ int raster, int x, int y, int w, int h);
+static int set_tile(gx_device *, const gx_strip_bitmap *);
+static void free_cp(gx_device *);
+
+/* Screen updating machinery */
+static void update_init(gx_device_X *);
+static void update_do_flush(gx_device_X *);
+
+#define flush_text(xdev)\
+ if (IN_TEXT(xdev)) do_flush_text(xdev)
+static void do_flush_text(gx_device_X *);
+
+/* Driver procedures */
+/* (External procedures are declared in gdevx.h.) */
+/*extern int gdev_x_open(gx_device_X *);*/
+static dev_proc_open_device(x_open);
+static dev_proc_get_initial_matrix(x_get_initial_matrix);
+static dev_proc_sync_output(x_sync);
+static dev_proc_output_page(x_output_page);
+/*extern int gdev_x_close(gx_device_X *);*/
+static dev_proc_close_device(x_close);
+/*extern dev_proc_map_rgb_color(gdev_x_map_rgb_color);*/
+/*extern dev_proc_map_color_rgb(gdev_x_map_color_rgb);*/
+static dev_proc_fill_rectangle(x_fill_rectangle);
+static dev_proc_copy_mono(x_copy_mono);
+static dev_proc_copy_color(x_copy_color);
+/*extern dev_proc_get_params(gdev_x_get_params);*/
+/*extern dev_proc_put_params(gdev_x_put_params);*/
+static dev_proc_get_page_device(x_get_page_device);
+static dev_proc_strip_tile_rectangle(x_strip_tile_rectangle);
+static dev_proc_begin_typed_image(x_begin_typed_image);
+static dev_proc_get_bits_rectangle(x_get_bits_rectangle);
+/*extern dev_proc_get_xfont_procs(gdev_x_finish_copydevice);*/
+
+/* The device descriptor */
+#define x_device(this_device, dev_body) \
+const gx_device_X this_device = { \
+ dev_body, \
+ { /* std_procs */ \
+ x_open, \
+ x_get_initial_matrix, \
+ x_sync, \
+ x_output_page, \
+ x_close, \
+ gdev_x_map_rgb_color, \
+ gdev_x_map_color_rgb, \
+ x_fill_rectangle, \
+ NULL, /* tile_rectangle */ \
+ x_copy_mono, \
+ x_copy_color, \
+ NULL, /* draw_line */ \
+ NULL, /* get_bits */ \
+ gdev_x_get_params, \
+ gdev_x_put_params, \
+ NULL, /* map_cmyk_color */ \
+ NULL, \
+ NULL, /* get_xfont_device */ \
+ NULL, /* map_rgb_alpha_color */ \
+ x_get_page_device, \
+ NULL, /* get_alpha_bits */ \
+ NULL, /* copy_alpha */ \
+ NULL, /* get_band */ \
+ NULL, /* copy_rop */ \
+ NULL, /* fill_path */ \
+ NULL, /* stroke_path */ \
+ NULL, /* fill_mask */ \
+ NULL, /* fill_trapezoid */ \
+ NULL, /* fill_parallelogram */ \
+ NULL, /* fill_triangle */ \
+ NULL, /* draw_thin_line */ \
+ NULL, /* begin_image */ \
+ NULL, /* image_data */ \
+ NULL, /* end_image */ \
+ x_strip_tile_rectangle, \
+ NULL, /* strip_copy_rop */ \
+ NULL, /* get_clipping_box */ \
+ x_begin_typed_image, \
+ x_get_bits_rectangle, \
+ NULL, /* map_color_rgb_alpha */ \
+ NULL, /* create_compositor */ \
+ NULL, /* get_hardware_params */ \
+ NULL, /* text_begin */ \
+ gdev_x_finish_copydevice \
+ }, \
+ gx_device_bbox_common_initial(0 /*false*/, 1 /*true*/, 1 /*true*/), \
+ 0 /*false*/, /* is_buffered */ \
+ 1 /*true*/, /* IsPageDevice */ \
+ NULL, /* buffer */ \
+ 0, /* buffer_size */ \
+ { /* image */ \
+ 0, 0, /* width, height */ \
+ 0, XYBitmap, NULL, /* xoffset, format, data */ \
+ MSBFirst, 8, /* byte-order, bitmap-unit */ \
+ MSBFirst, 8, 1, /* bitmap-bit-order, bitmap-pad, depth */ \
+ 0, 1, /* bytes_per_line, bits_per_pixel */ \
+ 0, 0, 0, /* red_mask, green_mask, blue_mask */ \
+ NULL, /* *obdata */ \
+ {NULL, /* *(*create_image)() */ \
+ NULL, /* (*destroy_image)() */ \
+ NULL, /* (*get_pixel)() */ \
+ NULL, /* (*put_pixel)() */ \
+ NULL, /* *(*sub_image)() */ \
+ NULL /* (*add_pixel)() */ \
+ }, \
+ }, \
+ NULL, NULL, /* dpy, scr */ \
+ /* (connection not initialized) */ \
+ NULL, /* vinfo */ \
+ (Colormap) None, /* cmap */ \
+ (Window) None, /* win */ \
+ NULL, /* gc */ \
+ (Window) None, /* pwin */ \
+ (Pixmap) 0, /* bpixmap */ \
+ 0, /* ghostview */ \
+ (Window) None, /* mwin */ \
+ {identity_matrix_body}, /* initial matrix (filled in) */ \
+ (Atom) 0, (Atom) 0, (Atom) 0, /* Atoms: NEXT, PAGE, DONE */ \
+ { /* update */ \
+ { /* box */ \
+ {max_int_in_fixed, max_int_in_fixed}, \
+ {min_int_in_fixed, min_int_in_fixed} \
+ }, \
+ 0, /* area */ \
+ 0, /* total */ \
+ 0 /* count */ \
+ }, \
+ (Pixmap) 0, /* dest */ \
+ 0L, (ulong)~0L, /* colors_or, colors_and */ \
+ { /* cp */ \
+ (Pixmap) 0, /* pixmap */ \
+ NULL, /* gc */ \
+ -1, -1 /* raster, height */ \
+ }, \
+ { /* ht */ \
+ (Pixmap) None, /* pixmap */ \
+ (Pixmap) None, /* no_pixmap */ \
+ gx_no_bitmap_id, /* id */ \
+ 0, 0, 0, /* width, height, raster */ \
+ 0, 0 /* fore_c, back_c */ \
+ }, \
+ GXcopy, /* function */ \
+ FillSolid, /* fill_style */ \
+ 0, /* font */ \
+ 0, 0, /* back_color, fore_color */ \
+ 0, 0, /* background, foreground */ \
+ { 0 }, /* cman */ \
+ 0, 0, /* borderColor, borderWidth */ \
+ NULL, /* geometry */ \
+ 128, 5, /* maxGrayRamp, maxRGBRamp */ \
+ NULL, /* palette */ \
+ 0.0, 0.0, /* xResolution, yResolution */ \
+ 1, /* useBackingPixmap */ \
+ 1, 1, /* useXPutImage, useXSetTile */ \
+ \
+ 0 /*false*/, /* AlwaysUpdate */ \
+ 20000, /* MaxTempPixmap */ \
+ 5000, /* MaxTempImage */ \
+ \
+ { /* text */ \
+ 0, /* item_count */ \
+ 0, /* char_count */ \
+ {0, 0}, /* origin */ \
+ 0, /* x */ \
+ { \
+ {0}}, /* items */ \
+ {0} /* chars */ \
+ } \
+};
+
+x_device(gs_x11_device,
+ std_device_color_stype_body(gx_device_X, 0, "x11", &st_device_X,
+ FAKE_RES * DEFAULT_WIDTH_10THS / 10,
+ FAKE_RES * DEFAULT_HEIGHT_10THS / 10, /* x and y extent (nominal) */
+ FAKE_RES, FAKE_RES, /* x and y density (nominal) */
+ 24, 255, 256 ))
+
+x_device(gs_x11alpha_device,
+ std_device_dci_alpha_type_body(gx_device_X, 0, "x11alpha", &st_device_X,
+ FAKE_RES * DEFAULT_WIDTH_10THS / 10,
+ FAKE_RES * DEFAULT_HEIGHT_10THS / 10, /* x and y extent (nominal) */
+ FAKE_RES, FAKE_RES, /* x and y density (nominal) */
+ 3, 24, 255, 255, 256, 256, 4, 4 ))
+
+/* If XPutImage doesn't work, do it ourselves. */
+static int alt_put_image(gx_device * dev, Display * dpy, Drawable win,
+GC gc, XImage * pi, int sx, int sy, int dx, int dy, unsigned w, unsigned h);
+
+#define put_image(dpy,win,gc,im,sx,sy,x,y,w,h)\
+ BEGIN\
+ if ( xdev->useXPutImage && !(XInitImage(im) == 0) ) {\
+ XPutImage(dpy,win,gc,im,sx,sy,x,y,w,h);\
+ } else {\
+ int code_ = alt_put_image(dev,dpy,win,gc,im,sx,sy,x,y,w,h);\
+ if ( code_ < 0 ) return code_;\
+ }\
+ END
+
+/* Open the device. Most of the code is in gdevxini.c. */
+static int
+x_open(gx_device * dev)
+{
+ gx_device_X *xdev = (gx_device_X *) dev;
+ int code;
+
+ if (xdev->color_info.anti_alias.text_bits > 1 ||
+ xdev->color_info.anti_alias.graphics_bits > 1)
+ xdev->space_params.MaxBitmap = 50000000; /* default MaxBitmap when using AA */
+ code = gdev_x_open(xdev);
+
+ if (code < 0)
+ return code;
+ update_init(xdev);
+ code = install_internal_subclass_devices((gx_device **)&xdev, NULL);
+ if (code < 0)
+ return code;
+
+ if (xdev->is_buffered)
+ xdev->box_proc_data = xdev;
+
+ return 0;
+}
+
+/* Close the device. */
+static int
+x_close(gx_device * dev)
+{
+ gx_device_X *xdev = (gx_device_X *) dev;
+
+ return gdev_x_close(xdev);
+}
+
+/* Get initial matrix for X device. */
+/* This conflicts seriously with the code for page devices; */
+/* we only do it if Ghostview is active. */
+static void
+x_get_initial_matrix(gx_device * dev, gs_matrix * pmat)
+{
+ gx_device_X *xdev = (gx_device_X *) dev;
+
+ if (!xdev->ghostview) {
+ gx_default_get_initial_matrix(dev, pmat);
+ return;
+ }
+ pmat->xx = xdev->initial_matrix.xx;
+ pmat->xy = xdev->initial_matrix.xy;
+ pmat->yx = xdev->initial_matrix.yx;
+ pmat->yy = xdev->initial_matrix.yy;
+ pmat->tx = xdev->initial_matrix.tx;
+ pmat->ty = xdev->initial_matrix.ty;
+}
+
+/* Synchronize the display with the commands already given. */
+static int
+x_sync(gx_device * dev)
+{
+ gx_device_X *xdev = (gx_device_X *) dev;
+
+ update_do_flush(xdev);
+ XSync(xdev->dpy, False);
+ return 0;
+}
+
+/* Send event to ghostview process */
+void
+gdev_x_send_event(gx_device_X *xdev, Atom msg)
+{
+ XEvent event;
+
+ event.xclient.type = ClientMessage;
+ event.xclient.display = xdev->dpy;
+ event.xclient.window = xdev->win;
+ event.xclient.message_type = msg;
+ event.xclient.format = 32;
+ event.xclient.data.l[0] = xdev->mwin;
+ event.xclient.data.l[1] = xdev->dest;
+ XSendEvent(xdev->dpy, xdev->win, False, 0, &event);
+}
+
+/* Output "page" */
+static int
+x_output_page(gx_device * dev, int num_copies, int flush)
+{
+ gx_device_X *xdev = (gx_device_X *) dev;
+
+ x_sync(dev);
+
+ /* Send ghostview a "page" client event */
+ /* Wait for a "next" client event */
+ if (xdev->ghostview) {
+ XEvent event;
+
+ gdev_x_send_event(xdev, xdev->PAGE);
+ XNextEvent(xdev->dpy, &event);
+ while (event.type != ClientMessage ||
+ event.xclient.message_type != xdev->NEXT) {
+ XNextEvent(xdev->dpy, &event);
+ }
+ }
+ return gx_finish_output_page(dev, num_copies, flush);
+}
+
+/* Fill a rectangle with a color. */
+static int
+x_fill_rectangle(gx_device * dev,
+ int x, int y, int w, int h, gx_color_index gscolor)
+{
+ gx_device_X *xdev = (gx_device_X *) dev;
+ unsigned long color = (unsigned long) gscolor;
+
+ fit_fill(dev, x, y, w, h);
+ flush_text(xdev);
+ X_SET_FILL_STYLE(xdev, FillSolid);
+ X_SET_FORE_COLOR(xdev, color);
+ X_SET_FUNCTION(xdev, GXcopy);
+ XFillRectangle(xdev->dpy, xdev->dest, xdev->gc, x, y, w, h);
+ /* If we are filling the entire screen, reset */
+ /* colors_or and colors_and. It's wasteful to test this */
+ /* on every operation, but there's no separate driver routine */
+ /* for erasepage (yet). */
+ if (x == 0 && y == 0 && w == xdev->width && h == xdev->height) {
+ if (color == xdev->foreground || color == xdev->background)
+ gdev_x_free_dynamic_colors(xdev);
+ xdev->colors_or = xdev->colors_and = color;
+ }
+ if (xdev->bpixmap != (Pixmap) 0) {
+ x_update_add(xdev, x, y, w, h);
+ }
+ if_debug5m('F', dev->memory, "[F] fill (%d,%d):(%d,%d) %ld\n",
+ x, y, w, h, (long)color);
+ return 0;
+}
+
+/* Copy a monochrome bitmap. */
+static int
+x_copy_mono(gx_device * dev,
+ const byte * base, int sourcex, int raster, gx_bitmap_id id,
+ int x, int y, int w, int h,
+ gx_color_index zero, gx_color_index one)
+/*
+ * X doesn't directly support the simple operation of writing a color
+ * through a mask specified by an image. The plot is the following:
+ * If neither color is gx_no_color_index ("transparent"),
+ * use XPutImage with the "copy" function as usual.
+ * If the color either bitwise-includes or is bitwise-included-in
+ * every color written to date
+ * (a special optimization for writing black/white on color displays),
+ * use XPutImage with an appropriate Boolean function.
+ * Otherwise, do the following complicated stuff:
+ * Create pixmap of depth 1 if necessary.
+ * If foreground color is "transparent" then
+ * invert the raster data.
+ * Use XPutImage to copy the raster image to the newly
+ * created Pixmap.
+ * Install the Pixmap as the clip_mask in the X GC and
+ * tweak the clip origin.
+ * Do an XFillRectangle, fill style=solid, specifying a
+ * rectangle the same size as the original raster data.
+ * De-install the clip_mask.
+ */
+{
+ gx_device_X *xdev = (gx_device_X *) dev;
+ int function = GXcopy;
+ unsigned long
+ lzero = zero,
+ lone = one;
+ x_pixel
+ bc = lzero,
+ fc = lone;
+
+ fit_copy(dev, base, sourcex, raster, id, x, y, w, h);
+ flush_text(xdev);
+
+ xdev->image.width = sourcex + w;
+ xdev->image.height = h;
+ xdev->image.data = (char *)base;
+ xdev->image.bytes_per_line = raster;
+ X_SET_FILL_STYLE(xdev, FillSolid);
+
+ /* Check for null, easy 1-color, hard 1-color, and 2-color cases. */
+ if (zero != gx_no_color_index) {
+ if (one != gx_no_color_index) {
+ /* 2-color case. */
+ /* Simply replace existing bits with what's in the image. */
+ } else if (!(~xdev->colors_and & bc)) {
+ function = GXand;
+ fc = ~(x_pixel) 0;
+ } else if (!(~bc & xdev->colors_or)) {
+ function = GXor;
+ fc = 0;
+ } else {
+ goto hard;
+ }
+ } else {
+ if (one == gx_no_color_index) { /* no-op */
+ return 0;
+ } else if (!(~xdev->colors_and & fc)) {
+ function = GXand;
+ bc = ~(x_pixel) 0;
+ } else if (!(~fc & xdev->colors_or)) {
+ function = GXor;
+ bc = 0;
+ } else {
+ goto hard;
+ }
+ }
+ xdev->image.format = XYBitmap;
+ X_SET_FUNCTION(xdev, function);
+ if (bc != xdev->back_color) {
+ XSetBackground(xdev->dpy, xdev->gc, (xdev->back_color = bc));
+ }
+ if (fc != xdev->fore_color) {
+ XSetForeground(xdev->dpy, xdev->gc, (xdev->fore_color = fc));
+ }
+ if (zero != gx_no_color_index)
+ NOTE_COLOR(xdev, lzero);
+ if (one != gx_no_color_index)
+ NOTE_COLOR(xdev, lone);
+ put_image(xdev->dpy, xdev->dest, xdev->gc, &xdev->image,
+ sourcex, 0, x, y, w, h);
+
+ goto out;
+
+ hard: /* Handle the hard 1-color case. */
+ if (raster > xdev->cp.raster || h > xdev->cp.height) {
+ /* Must allocate a new pixmap and GC. */
+ /* Release the old ones first. */
+ free_cp(dev);
+
+ /* Create the clipping pixmap, depth must be 1. */
+ xdev->cp.pixmap =
+ XCreatePixmap(xdev->dpy, xdev->win, raster << 3, h, 1);
+ if (xdev->cp.pixmap == (Pixmap) 0) {
+ lprintf("x_copy_mono: can't allocate pixmap\n");
+ return_error(gs_error_VMerror);
+ }
+ xdev->cp.gc = XCreateGC(xdev->dpy, xdev->cp.pixmap, 0, 0);
+ if (xdev->cp.gc == (GC) 0) {
+ lprintf("x_copy_mono: can't allocate GC\n");
+ return_error(gs_error_VMerror);
+ }
+ xdev->cp.raster = raster;
+ xdev->cp.height = h;
+ }
+ /* Initialize static mask image params */
+ xdev->image.format = XYBitmap;
+ X_SET_FUNCTION(xdev, GXcopy);
+
+ /* Select polarity based on fg/bg transparency. */
+ if (one == gx_no_color_index) { /* invert */
+ XSetBackground(xdev->dpy, xdev->cp.gc, (x_pixel) 1);
+ XSetForeground(xdev->dpy, xdev->cp.gc, (x_pixel) 0);
+ X_SET_FORE_COLOR(xdev, lzero);
+ } else {
+ XSetBackground(xdev->dpy, xdev->cp.gc, (x_pixel) 0);
+ XSetForeground(xdev->dpy, xdev->cp.gc, (x_pixel) 1);
+ X_SET_FORE_COLOR(xdev, lone);
+ }
+ put_image(xdev->dpy, xdev->cp.pixmap, xdev->cp.gc,
+ &xdev->image, sourcex, 0, 0, 0, w, h);
+
+ /* Install as clipmask. */
+ XSetClipMask(xdev->dpy, xdev->gc, xdev->cp.pixmap);
+ XSetClipOrigin(xdev->dpy, xdev->gc, x, y);
+
+ /*
+ * Draw a solid rectangle through the raster clip mask.
+ * Note fill style is guaranteed to be solid from above.
+ */
+ XFillRectangle(xdev->dpy, xdev->dest, xdev->gc, x, y, w, h);
+
+ /* Tidy up. Free the pixmap if it's big. */
+ XSetClipMask(xdev->dpy, xdev->gc, None);
+ if (raster * h > xdev->MaxTempPixmap)
+ free_cp(dev);
+
+ out:if (xdev->bpixmap != (Pixmap) 0) {
+ /* We wrote to the pixmap, so update the display now. */
+ x_update_add(xdev, x, y, w, h);
+ }
+ return 0;
+}
+
+/* Internal routine to free the GC and pixmap used for copying. */
+static void
+free_cp(gx_device * dev)
+{
+ gx_device_X *xdev = (gx_device_X *) dev;
+
+ if (xdev->cp.gc != NULL) {
+ XFreeGC(xdev->dpy, xdev->cp.gc);
+ xdev->cp.gc = NULL;
+ }
+ if (xdev->cp.pixmap != (Pixmap) 0) {
+ XFreePixmap(xdev->dpy, xdev->cp.pixmap);
+ xdev->cp.pixmap = (Pixmap) 0;
+ }
+ xdev->cp.raster = -1; /* mark as unallocated */
+}
+
+/* Copy a color bitmap. */
+static int
+x_copy_image(gx_device_X * xdev, const byte * base, int sourcex, int raster,
+ int x, int y, int w, int h)
+{
+ int depth = xdev->color_info.depth;
+
+ X_SET_FILL_STYLE(xdev, FillSolid);
+ X_SET_FUNCTION(xdev, GXcopy);
+
+ /* Filling with a colored halftone often gives rise to */
+ /* copy_color calls for a single pixel. Check for this now. */
+
+ if (h == 1 && w == 1) {
+ uint sbit = sourcex * depth;
+ const byte *ptr = base + (sbit >> 3);
+ x_pixel pixel;
+
+ if (depth < 8)
+ pixel = (byte) (*ptr << (sbit & 7)) >> (8 - depth);
+ else {
+ pixel = *ptr++;
+ while ((depth -= 8) > 0)
+ pixel = (pixel << 8) + *ptr++;
+ }
+ X_SET_FORE_COLOR(xdev, pixel);
+ XDrawPoint(xdev->dpy, xdev->dest, xdev->gc, x, y);
+ } else {
+ /* Reduce base, sourcex */
+ int width = sourcex + w;
+ int vdepth = xdev->vinfo->depth;
+ xdev->image.width = width;
+ xdev->image.height = h;
+ xdev->image.format = ZPixmap;
+ xdev->image.data = (char *)base;
+ xdev->image.depth = vdepth;
+ xdev->image.bitmap_pad = 8;
+ if (width * vdepth < raster * 8)
+ xdev->image.bytes_per_line = raster;
+ else
+ xdev->image.bytes_per_line = 0;
+ xdev->image.bits_per_pixel = depth;
+ if (XInitImage(&xdev->image) == 0){
+ errprintf(xdev->memory, "XInitImage failed in x_copy_image.\n");
+ return_error(gs_error_unknownerror);
+ }
+ XPutImage(xdev->dpy, xdev->dest, xdev->gc, &xdev->image,
+ sourcex, 0, x, y, w, h);
+ xdev->image.depth = xdev->image.bits_per_pixel = 1;
+
+ /* give up on optimization */
+ xdev->colors_or = (x_pixel)(-1);
+ xdev->colors_and = 0;
+ }
+ return 0;
+}
+static int
+x_copy_color(gx_device * dev,
+ const byte * base, int sourcex, int raster, gx_bitmap_id id,
+ int x, int y, int w, int h)
+{
+ gx_device_X *xdev = (gx_device_X *) dev;
+ int code;
+
+ fit_copy(dev, base, sourcex, raster, id, x, y, w, h);
+ flush_text(xdev);
+ code = x_copy_image(xdev, base, sourcex, raster, x, y, w, h);
+ if (xdev->bpixmap != (Pixmap) 0)
+ x_update_add(xdev, x, y, w, h);
+ if_debug4m('F', dev->memory, "[F] copy_color (%d,%d):(%d,%d)\n",
+ x, y, w, h);
+ return code;
+}
+
+/* Get the page device. We reimplement this so that we can make this */
+/* device be a page device conditionally. */
+static gx_device *
+x_get_page_device(gx_device * dev)
+{
+ return (((gx_device_X *) dev)->IsPageDevice ? dev : (gx_device *) 0);
+}
+
+/* Tile a rectangle. */
+static int
+x_strip_tile_rectangle(gx_device * dev, const gx_strip_bitmap * tiles,
+ int x, int y, int w, int h,
+ gx_color_index zero, gx_color_index one,
+ int px, int py)
+{
+ gx_device_X *xdev = (gx_device_X *) dev;
+ unsigned long lzero = (unsigned long) zero;
+ unsigned long lone = (unsigned long) one;
+
+ /* Give up if one color is transparent, or if the tile is colored. */
+ /* We should implement the latter someday, since X can handle it. */
+
+ if (one == gx_no_color_index || zero == gx_no_color_index)
+ return gx_default_strip_tile_rectangle(dev, tiles, x, y, w, h,
+ zero, one, px, py);
+
+ /* For the moment, give up if the phase or shift is non-zero. */
+ if (tiles->shift | px | py)
+ return gx_default_strip_tile_rectangle(dev, tiles, x, y, w, h,
+ zero, one, px, py);
+
+ fit_fill(dev, x, y, w, h);
+ flush_text(xdev);
+
+ /* Imaging with a halftone often gives rise to very small */
+ /* tile_rectangle calls. Check for this now. */
+
+ if (h <= 2 && w <= 2) {
+ int j;
+
+ X_SET_FILL_STYLE(xdev, FillSolid);
+ X_SET_FUNCTION(xdev, GXcopy);
+ for (j = y + h; --j >= y;) {
+ const byte *ptr =
+ tiles->data + (j % tiles->rep_height) * tiles->raster;
+ int i;
+
+ for (i = x + w; --i >= x;) {
+ uint tx = i % tiles->rep_width;
+ byte mask = 0x80 >> (tx & 7);
+ x_pixel pixel = (ptr[tx >> 3] & mask ? lone : lzero);
+
+ X_SET_FORE_COLOR(xdev, pixel);
+ XDrawPoint(xdev->dpy, xdev->dest, xdev->gc, i, j);
+ }
+ }
+ if (xdev->bpixmap != (Pixmap) 0) {
+ x_update_add(xdev, x, y, w, h);
+ }
+ return 0;
+ }
+ /*
+ * Remember, an X tile is already filled with particular
+ * pixel values (i.e., colors). Therefore if we are changing
+ * fore/background color, we must invalidate the tile (using
+ * the same technique as in set_tile). This problem only
+ * bites when using grayscale -- you may want to change
+ * fg/bg but use the same halftone screen.
+ */
+ if ((lzero != xdev->ht.back_c) || (lone != xdev->ht.fore_c))
+ xdev->ht.id = ~tiles->id; /* force reload */
+
+ X_SET_BACK_COLOR(xdev, lzero);
+ X_SET_FORE_COLOR(xdev, lone);
+ if (!set_tile(dev, tiles)) { /* Bad news. Fall back to the default algorithm. */
+ return gx_default_strip_tile_rectangle(dev, tiles, x, y, w, h,
+ zero, one, px, py);
+ }
+ /* Use the tile to fill the rectangle */
+ X_SET_FILL_STYLE(xdev, FillTiled);
+ X_SET_FUNCTION(xdev, GXcopy);
+ XFillRectangle(xdev->dpy, xdev->dest, xdev->gc, x, y, w, h);
+ if (xdev->bpixmap != (Pixmap) 0) {
+ x_update_add(xdev, x, y, w, h);
+ }
+ if_debug6m('F', dev->memory, "[F] tile (%d,%d):(%d,%d) %ld,%ld\n",
+ x, y, w, h, lzero, lone);
+ return 0;
+}
+
+/* Implement ImageType 2 using CopyArea if possible. */
+/* Note that since ImageType 2 images don't have any source data, */
+/* this procedure does all the work. */
+static int
+x_begin_typed_image(gx_device * dev,
+ const gs_imager_state * pis, const gs_matrix * pmat,
+ const gs_image_common_t * pic, const gs_int_rect * prect,
+ const gx_drawing_color * pdcolor, const gx_clip_path * pcpath,
+ gs_memory_t * mem, gx_image_enum_common_t ** pinfo)
+{
+ gx_device_X *xdev = (gx_device_X *) dev;
+ const gs_image2_t *pim;
+ gs_state *pgs;
+ gx_device *sdev;
+ gs_matrix smat, dmat;
+
+ if (pic->type->index != 2)
+ goto punt;
+ pim = (const gs_image2_t *)pic;
+ if (!pim->PixelCopy)
+ goto punt;
+ pgs = pim->DataSource;
+ sdev = gs_currentdevice(pgs);
+ if (dev->dname != sdev->dname ||
+ memcmp(&dev->color_info, &sdev->color_info,
+ sizeof(dev->color_info))
+ )
+ goto punt;
+ flush_text(xdev);
+ gs_currentmatrix(pgs, &smat);
+ /*
+ * Figure 7.2 of the Adobe 3010 Supplement says that we should
+ * compute CTM x ImageMatrix here, but I'm almost certain it
+ * should be the other way around. Also see gximage2.c.
+ */
+ gs_matrix_multiply(&pim->ImageMatrix, &smat, &smat);
+ if (pis == 0)
+ dmat = *pmat;
+ else
+ gs_currentmatrix((const gs_state *)pis, &dmat);
+ if (!((is_xxyy(&dmat) || is_xyyx(&dmat)) &&
+#define eqe(e) smat.e == dmat.e
+ eqe(xx) && eqe(xy) && eqe(yx) && eqe(yy))
+#undef eqe
+ )
+ goto punt;
+ {
+ gs_rect rect, src, dest;
+ gs_int_point size;
+ int srcx, srcy, destx, desty;
+
+ rect.p.x = rect.p.y = 0;
+ rect.q.x = pim->Width, rect.q.y = pim->Height;
+ gs_bbox_transform(&rect, &dmat, &dest);
+ if (pcpath != NULL &&
+ !gx_cpath_includes_rectangle(pcpath,
+ float2fixed(dest.p.x), float2fixed(dest.p.y),
+ float2fixed(dest.q.x), float2fixed(dest.q.y))
+ )
+ goto punt;
+ rect.q.x += (rect.p.x = pim->XOrigin);
+ rect.q.y += (rect.p.y = pim->YOrigin);
+ gs_bbox_transform(&rect, &smat, &src);
+ (*pic->type->source_size) (pis, pic, &size);
+ X_SET_FILL_STYLE(xdev, FillSolid);
+ X_SET_FUNCTION(xdev, GXcopy);
+ srcx = (int)(src.p.x + 0.5);
+ srcy = (int)(src.p.y + 0.5);
+ destx = (int)(dest.p.x + 0.5);
+ desty = (int)(dest.p.y + 0.5);
+ XCopyArea(xdev->dpy, xdev->bpixmap, xdev->bpixmap, xdev->gc,
+ srcx, srcy, size.x, size.y, destx, desty);
+ x_update_add(xdev, destx, desty, size.x, size.y);
+ }
+ return 0;
+ punt:return gx_default_begin_typed_image(dev, pis, pmat, pic, prect,
+ pdcolor, pcpath, mem, pinfo);
+}
+
+/* Read bits back from the screen. */
+static int
+x_get_bits_rectangle(gx_device * dev, const gs_int_rect * prect,
+ gs_get_bits_params_t * params, gs_int_rect ** unread)
+{
+ gx_device_X *xdev = (gx_device_X *) dev;
+ int depth = dev->color_info.depth;
+ int x0 = prect->p.x, y0 = prect->p.y, x1 = prect->q.x, y1 = prect->q.y;
+ uint width_bytes = ((x1 - x0) * depth + 7) >> 3;
+ uint band = xdev->MaxTempImage / width_bytes;
+ uint default_raster = bitmap_raster((x1 - x0) * depth);
+ gs_get_bits_options_t options = params->options;
+ uint raster =
+ (options & GB_RASTER_SPECIFIED ? params->raster :
+ (params->raster = default_raster));
+ long plane_mask = (1L << depth) - 1;
+ int y, h;
+ XImage *image;
+ int code = 0;
+#if GET_IMAGE_EXPOSURES
+ XWindowAttributes attributes;
+#endif /* GET_IMAGE_EXPOSURES */
+
+ if (x0 < 0 || y0 < 0 || x1 > dev->width || y1 > dev->height)
+ return_error(gs_error_rangecheck);
+ /* XGetImage can only handle x_offset = 0. */
+ if ((options & GB_OFFSET_SPECIFIED) && params->x_offset == 0)
+ options = (options & ~GB_OFFSET_SPECIFIED) | GB_OFFSET_0;
+ if (~options &
+ (GB_RETURN_COPY | GB_OFFSET_0 | GB_PACKING_CHUNKY |
+ GB_COLORS_NATIVE) ||
+ !(options & GB_ALIGN_ALL) ||
+ !(options & GB_RASTER_ALL)
+ )
+ return
+ gx_default_get_bits_rectangle(dev, prect, params, unread);
+ params->options =
+ GB_COLORS_NATIVE | GB_ALPHA_NONE | GB_PACKING_CHUNKY |
+ GB_RETURN_COPY | GB_OFFSET_0 |
+ (options & GB_ALIGN_ALL) |
+ (options & GB_RASTER_SPECIFIED ? GB_RASTER_SPECIFIED :
+ GB_RASTER_STANDARD);
+ if (x0 >= x1 || y0 >= y1)
+ return 0;
+ if (x1 <= xdev->update.box.p.x || x0 >= xdev->update.box.q.x ||
+ y1 <= xdev->update.box.p.y || y0 >= xdev->update.box.q.y
+ ) {
+ /*
+ * The area being read back doesn't overlap the pending update:
+ * just flush text.
+ */
+ flush_text(xdev);
+ } else
+ update_do_flush(xdev);
+ /*
+ * If we want a list of unread rectangles, turn on graphics
+ * exposures, and accept exposure events.
+ */
+ /******
+ ****** FOLLOWING IS WRONG. XGetImage DOES NOT GENERATE
+ ****** EXPOSURE EVENTS.
+ ******/
+#if GET_IMAGE_EXPOSURES
+ if (unread) {
+ XSetGraphicsExposures(xdev->dpy, xdev->gc, True);
+ XGetWindowAttributes(xdev->dpy, xdev->win, &attributes);
+ XSelectInput(xdev->dpy, xdev->win,
+ attributes.your_event_mask | ExposureMask);
+ }
+#endif /* GET_IMAGE_EXPOSURES */
+ /*
+ * The X library doesn't provide any way to specify the desired
+ * bit or byte ordering for the result, so we may have to swap the
+ * bit or byte order.
+ */
+ if (band == 0)
+ band = 1;
+ for (y = y0; y < y1; y += h) {
+ int cy;
+
+ h = min(band, y1 - y);
+ image = XGetImage(xdev->dpy, xdev->dest, x0, y, x1 - x0, h,
+ plane_mask, ZPixmap);
+ for (cy = y; cy < y + h; ++cy) {
+ const byte *source =
+ (const byte *)image->data + (cy - y) * image->bytes_per_line;
+ byte *dest = params->data[0] + (cy - y0) * raster;
+
+ /*
+ * XGetImage can return an image with any bit order, byte order,
+ * unit size (for bitmaps), and bits_per_pixel it wants: it's up
+ * to us to convert the results. (It's a major botch in the X
+ * design that even though the server has to have the ability to
+ * convert images from any format to any format, there's no way
+ * to specify a requested format for XGetImage.)
+ */
+ if (image->bits_per_pixel == image->depth &&
+ (image->depth > 1 || image->bitmap_bit_order == MSBFirst) &&
+ (image->byte_order == MSBFirst || image->depth <= 8)
+ ) {
+ /*
+ * The server has been nice enough to return an image in the
+ * format we use.
+ */
+ memcpy(dest, source, width_bytes);
+ } else {
+ /*
+ * We need to swap byte order and/or bit order. What a
+ * totally unnecessary nuisance! For the moment, the only
+ * cases we deal with are 15-, 16- and 24-bit images with padding
+ * and/or byte swapping.
+ * If we ever need to support other color depths, consider rewriting
+ * all these byte swaps with XGetPixel() .
+ */
+ if (image->depth == 24) {
+ int cx;
+ const byte *p = source;
+ byte *q = dest;
+ int step = image->bits_per_pixel >> 3;
+
+ if (image->byte_order == MSBFirst) {
+ p += step - 3;
+ for (cx = x0; cx < x1; p += step, q += 3, ++cx)
+ q[0] = p[0], q[1] = p[1], q[2] = p[2];
+ } else {
+ for (cx = x0; cx < x1; p += step, q += 3, ++cx)
+ q[0] = p[2], q[1] = p[1], q[2] = p[0];
+ }
+ } else if (image->depth == 16 || image->depth == 15) {
+ int cx;
+ const byte *p = source;
+ byte *q = dest;
+ int step = image->bits_per_pixel >> 3;
+
+ if (image->byte_order == MSBFirst) {
+ p += step - 2;
+ for (cx = x0; cx < x1; p += step, q += 2, ++cx)
+ q[0] = p[0], q[1] = p[1];
+ } else {
+ for (cx = x0; cx < x1; p += step, q += 2, ++cx)
+ q[0] = p[1], q[1] = p[0];
+ }
+ } else
+ code = gs_note_error(gs_error_rangecheck);
+ }
+ }
+ XDestroyImage(image);
+ }
+ if (unread) {
+#if GET_IMAGE_EXPOSURES
+ XEvent event;
+#endif /* GET_IMAGE_EXPOSURES */
+
+ *unread = 0;
+#if GET_IMAGE_EXPOSURES
+ /* Read any exposure events. */
+ XWindowEvent(xdev->dpy, xdev->win, ExposureMask, &event);
+ if (event.type == GraphicsExpose) {
+ gs_int_rect *rects = (gs_int_rect *)
+ gs_alloc_bytes(dev->memory, sizeof(gs_int_rect),
+ "x_get_bits_rectangle");
+ int num_rects = 0;
+
+ for (;;) {
+ if (rects == 0) {
+ code = gs_note_error(gs_error_VMerror);
+ break;
+ }
+#define xevent (*(XGraphicsExposeEvent *)&event)
+ rects[num_rects].q.x = xevent.width +
+ (rects[num_rects].p.x = xevent.x);
+ rects[num_rects].q.y = xevent.height +
+ (rects[num_rects].p.y = xevent.y);
+ ++num_rects;
+ if (!xevent.count)
+ break;
+#undef xevent
+ rects = gs_resize_object(dev->memory, rects,
+ (num_rects + 1) * sizeof(gs_int_rect),
+ "x_get_bits_rectangle");
+ }
+ if (code >= 0) {
+ *unread = rects;
+ code = num_rects;
+ }
+ }
+ /* Restore the window state. */
+ XSetGraphicsExposures(xdev->dpy, xdev->gc, False);
+ XSelectInput(xdev->dpy, xdev->win, attributes.your_event_mask);
+#endif /* GET_IMAGE_EXPOSURES */
+ }
+ return code;
+}
+
+/* Set up with a specified tile. */
+/* Return false if we can't do it for some reason. */
+static int
+set_tile(gx_device * dev, const gx_strip_bitmap * tile)
+{
+ gx_device_X *xdev = (gx_device_X *) dev;
+
+#ifdef DEBUG
+ if (gs_debug['T'])
+ return 0;
+#endif
+ if (tile->id == xdev->ht.id && tile->id != gx_no_bitmap_id)
+ return xdev->useXSetTile;
+ /* Set up the tile Pixmap */
+ if (tile->size.x != xdev->ht.width ||
+ tile->size.y != xdev->ht.height ||
+ xdev->ht.pixmap == (Pixmap) 0) {
+ if (xdev->ht.pixmap != (Pixmap) 0)
+ XFreePixmap(xdev->dpy, xdev->ht.pixmap);
+ xdev->ht.pixmap = XCreatePixmap(xdev->dpy, xdev->win,
+ tile->size.x, tile->size.y,
+ xdev->vinfo->depth);
+ if (xdev->ht.pixmap == (Pixmap) 0)
+ return 0;
+ xdev->ht.width = tile->size.x, xdev->ht.height = tile->size.y;
+ xdev->ht.raster = tile->raster;
+ }
+ xdev->ht.fore_c = xdev->fore_color;
+ xdev->ht.back_c = xdev->back_color;
+ /* Copy the tile into the Pixmap */
+ xdev->image.data = (char *)tile->data;
+ xdev->image.width = tile->size.x;
+ xdev->image.height = tile->size.y;
+ xdev->image.bytes_per_line = tile->raster;
+ xdev->image.format = XYBitmap;
+ X_SET_FILL_STYLE(xdev, FillSolid);
+#ifdef DEBUG
+ if (gs_debug['H']) {
+ int i;
+
+ dmlprintf4(xdev->memory, "[H] 0x%lx: width=%d height=%d raster=%d\n",
+ (ulong) tile->data, tile->size.x, tile->size.y, tile->raster);
+ dmlputs(xdev->memory, "");
+ for (i = 0; i < tile->raster * tile->size.y; i++)
+ dmprintf1(xdev->memory, " %02x", tile->data[i]);
+ dmputc(xdev->memory, '\n');
+ }
+#endif
+ XSetTile(xdev->dpy, xdev->gc, xdev->ht.no_pixmap); /* *** X bug *** */
+ X_SET_FUNCTION(xdev, GXcopy);
+ put_image(xdev->dpy, xdev->ht.pixmap, xdev->gc, &xdev->image,
+ 0, 0, 0, 0, tile->size.x, tile->size.y);
+ XSetTile(xdev->dpy, xdev->gc, xdev->ht.pixmap);
+ xdev->ht.id = tile->id;
+ return xdev->useXSetTile;
+}
+
+/* ------ Screen update procedures ------ */
+
+/* Initialize the update machinery. */
+static void
+update_init(gx_device_X *xdev)
+{
+ xdev->update.box.p.x = xdev->update.box.p.y = max_int_in_fixed;
+ xdev->update.box.q.x = xdev->update.box.q.y = min_int_in_fixed;
+ xdev->update.area = xdev->update.total = xdev->update.count = 0;
+}
+
+/* Flush updates to the screen if needed. */
+static void
+update_do_flush(gx_device_X * xdev)
+{
+ flush_text(xdev);
+ if (xdev->update.box.q.x == min_int_in_fixed || xdev->update.box.q.y == min_int_in_fixed)
+ return;
+ if (xdev->update.box.p.x == max_int_in_fixed || xdev->update.box.p.y == max_int_in_fixed)
+ return;
+ if (xdev->update.count != 0) {
+ int x = xdev->update.box.p.x, y = xdev->update.box.p.y;
+ int w = xdev->update.box.q.x - x, h = xdev->update.box.q.y - y;
+ const gx_device_memory *mdev = NULL;
+
+ /*
+ * Copy from memory image to X server if any.
+ */
+ if (xdev->is_buffered) {
+ /*
+ * The bbox device may have set the target to NULL
+ * temporarily. If this is the case, defer the screen
+ * update.
+ */
+ if (!(mdev = (const gx_device_memory *)xdev->target))
+ return;
+ }
+
+ /*
+ * mdev->width and mdev->height arn't the same as
+ * xdev->width and xdev->height ... at least for gv
+ */
+ if (mdev)
+ fit_fill_xywh(mdev, x, y, w, h);
+ else
+ fit_fill_xywh(xdev, x, y, w, h);
+
+ if (w > 0 && h > 0) {
+ if (mdev)
+ x_copy_image(xdev, mdev->line_ptrs[y], x, mdev->raster,
+ x, y, w, h);
+ if (xdev->bpixmap) {
+ /* Copy from X backing pixmap to screen. */
+
+ X_SET_FUNCTION(xdev, GXcopy);
+ XCopyArea(xdev->dpy, xdev->bpixmap, xdev->win, xdev->gc,
+ x, y, w, h, x, y);
+ }
+ }
+ update_init(xdev);
+ }
+}
+
+/* Add a region to be updated, after writing to that region. */
+void
+x_update_add(gx_device_X * xdev, int xo, int yo, int w, int h)
+{
+ int xe = xo + w, ye = yo + h;
+ long added = (long)w * h;
+ long old_area = xdev->update.area;
+ gs_int_rect u;
+ int nw, nh;
+ long new_up_area;
+
+ u.p.x = min(xo, xdev->update.box.p.x);
+ u.p.y = min(yo, xdev->update.box.p.y);
+ u.q.x = max(xe, xdev->update.box.q.x);
+ u.q.y = max(ye, xdev->update.box.q.y);
+ nw = u.q.x - u.p.x;
+ nh = u.q.y - u.p.y;
+ new_up_area = (long)nw * nh;
+ xdev->update.count++;
+ xdev->update.area = new_up_area;
+ xdev->update.total += added;
+ if (!xdev->AlwaysUpdate) {
+ /*
+ * Test whether adding this rectangle would result in too much being
+ * copied unnecessarily. The fraction of new_up_area used in the
+ * following test is not particularly critical; using a denominator
+ * that is a power of 2 eliminates a divide.
+ */
+ if (nw + nh >= 70 && (nw | nh) >= 16 &&
+ old_area + added < new_up_area - (new_up_area >> 2)
+ )
+ DO_NOTHING;
+ else {
+ xdev->update.box = u;
+ return;
+ }
+ }
+ if (xdev->is_buffered && (xdev->target == NULL))
+ xdev->update.box = u; /* update deferred since bbox has target disabled */
+ else {
+ update_do_flush(xdev);
+ xdev->update.box.p.x = xo, xdev->update.box.p.y = yo;
+ xdev->update.box.q.x = xe, xdev->update.box.q.y = ye;
+ xdev->update.count = 1;
+ xdev->update.area = xdev->update.total = added;
+ }
+}
+
+/* Flush buffered text to the screen. */
+static void
+do_flush_text(gx_device_X * xdev)
+{
+ if (!IN_TEXT(xdev))
+ return;
+ DRAW_TEXT(xdev);
+ xdev->text.item_count = xdev->text.char_count = 0;
+}
+
+/* Bounding box device procedures (only used when buffering) */
+static bool
+x_bbox_init_box(void *pdata)
+{
+ gx_device_X *const xdev = pdata;
+
+ update_init(xdev);
+ return true;
+}
+static void
+x_bbox_get_box(const void *pdata, gs_fixed_rect *pbox)
+{
+ const gx_device_X *const xdev = pdata;
+
+ pbox->p.x = int2fixed(xdev->update.box.p.x);
+ pbox->p.y = int2fixed(xdev->update.box.p.y);
+ pbox->q.x = int2fixed(xdev->update.box.q.x);
+ pbox->q.y = int2fixed(xdev->update.box.q.y);
+}
+static void
+x_bbox_add_rect(void *pdata, fixed x0, fixed y0, fixed x1, fixed y1)
+{
+ gx_device_X *const xdev = pdata;
+ int x = fixed2int(x0), y = fixed2int(y0);
+
+ x_update_add(xdev, x, y, fixed2int_ceiling(x1) - x,
+ fixed2int_ceiling(y1) - y);
+}
+static bool
+x_bbox_in_rect(const void *pdata, const gs_fixed_rect *pbox)
+{
+ gs_fixed_rect box;
+
+ x_bbox_get_box(pdata, &box);
+ return rect_within(*pbox, box);
+}
+const gx_device_bbox_procs_t gdev_x_box_procs = {
+ x_bbox_init_box, x_bbox_get_box, x_bbox_add_rect, x_bbox_in_rect
+};
+
+/* ------ Internal procedures ------ */
+
+/*
+ * Substitute for XPutImage using XFillRectangle. This is a hack to get
+ * around an apparent bug in some X servers. It only works with the
+ * specific parameters (bit/byte order, padding) used above.
+ */
+static int
+alt_put_image(gx_device *dev, Display *dpy, Drawable win, GC gc, XImage *pi,
+ int sx, int sy, int dx, int dy, unsigned w, unsigned h)
+{
+ int raster = pi->bytes_per_line;
+ byte *data = (byte *) pi->data + sy * raster + (sx >> 3);
+ int init_mask = 0x80 >> (sx & 7);
+ int invert = 0;
+ int yi;
+#define NUM_RECTS 40
+ XRectangle rects[NUM_RECTS];
+ XRectangle *rp = rects;
+ XGCValues gcv;
+
+#ifdef DEBUG
+ if (pi->format != XYBitmap || pi->byte_order != MSBFirst ||
+ pi->bitmap_bit_order != MSBFirst || pi->depth != 1
+ ) {
+ lprintf("alt_put_image: unimplemented parameter values!\n");
+ return_error(gs_error_rangecheck);
+ }
+#endif
+
+ XGetGCValues(dpy, gc, (GCFunction | GCForeground | GCBackground), &gcv);
+
+ if (gcv.function == GXcopy) {
+ XSetForeground(dpy, gc, gcv.background);
+ XFillRectangle(dpy, win, gc, dx, dy, w, h);
+ XSetForeground(dpy, gc, gcv.foreground);
+ } else if (gcv.function == GXand) {
+ /* The only cases used above are fc = ~0 or bc = ~0. */
+#ifdef DEBUG
+ if (gcv.foreground != ~(x_pixel)0 && gcv.background != ~(x_pixel)0) {
+ lprintf("alt_put_image: unimplemented GXand case!\n");
+ return_error(gs_error_rangecheck);
+ }
+#endif
+ if (gcv.background != ~(x_pixel) 0) {
+ XSetForeground(dpy, gc, gcv.background);
+ invert = 0xff;
+ }
+ } else if (gcv.function == GXor) {
+ /* The only cases used above are fc = 0 or bc = 0. */
+#ifdef DEBUG
+ if (gcv.foreground != 0 && gcv.background != 0) {
+ lprintf("alt_put_image: unimplemented GXor case!\n");
+ return_error(gs_error_rangecheck);
+ }
+#endif
+ if (gcv.background != 0) {
+ XSetForeground(dpy, gc, gcv.background);
+ invert = 0xff;
+ }
+ } else {
+ lprintf("alt_put_image: unimplemented function.\n");
+ return_error(gs_error_rangecheck);
+ }
+
+ for (yi = 0; yi < h; yi++, data += raster) {
+ int mask = init_mask;
+ byte *dp = data;
+ int xi = 0;
+
+ while (xi < w) {
+ if ((*dp ^ invert) & mask) {
+ int xleft = xi;
+
+ if (rp == &rects[NUM_RECTS]) {
+ XFillRectangles(dpy, win, gc, rects, NUM_RECTS);
+ rp = rects;
+ }
+ /* Scan over a run of 1-bits */
+ rp->x = dx + xi, rp->y = dy + yi;
+ do {
+ if (!(mask >>= 1))
+ mask = 0x80, dp++;
+ xi++;
+ } while (xi < w && ((*dp ^ invert) & mask));
+ rp->width = xi - xleft, rp->height = 1;
+ rp++;
+ } else {
+ if (!(mask >>= 1))
+ mask = 0x80, dp++;
+ xi++;
+ }
+ }
+ }
+ XFillRectangles(dpy, win, gc, rects, rp - rects);
+ if (invert)
+ XSetForeground(dpy, gc, gcv.foreground);
+ return 0;
+#undef NUM_RECTS
+}
diff --git a/devices/gdevx.h b/devices/gdevx.h
new file mode 100644
index 000000000..e2313a2e4
--- /dev/null
+++ b/devices/gdevx.h
@@ -0,0 +1,238 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Definitions for X Windows drivers */
+/* Requires gxdevice.h and x_.h */
+
+#ifndef gdevx_INCLUDED
+# define gdevx_INCLUDED
+
+/* Define the type of an X pixel. */
+typedef unsigned long x_pixel;
+
+#include "gdevbbox.h"
+#include "gdevxcmp.h"
+
+/* Declare the X resource tables compiled separately in gdevxres.c. */
+extern XtResource gdev_x_resources[];
+extern const int gdev_x_resource_count;
+extern String gdev_x_fallback_resources[];
+
+/* Define the X Windows device */
+typedef struct gx_device_X_s {
+ gx_device_bbox_common; /* if target != 0, is image buffer */
+ /*
+ * Normally, an X device has an image buffer iff target != 0. However,
+ * the bbox device sometimes sets target to NULL temporarily, so we need
+ * a separate flag to record whether this device is buffered.
+ */
+ bool is_buffered;
+ bool IsPageDevice;
+ byte *buffer; /* full-window image */
+ long buffer_size;
+
+ /* An XImage object for writing bitmap images to the screen */
+ XImage image;
+
+ /* Global X state */
+ Display *dpy;
+ Screen *scr;
+ XVisualInfo *vinfo;
+ Colormap cmap;
+ Window win;
+ GC gc;
+
+ /* An optional Window ID supplied as a device parameter */
+ Window pwin;
+
+ /* A backing pixmap so X will handle exposure automatically */
+ Pixmap bpixmap; /* 0 if useBackingPixmap is false, */
+ /* or if it can't be allocated */
+ int ghostview; /* flag to tell if ghostview is in control */
+ Window mwin; /* window to receive ghostview messages */
+ gs_matrix initial_matrix; /* the initial transformation */
+ Atom NEXT, PAGE, DONE; /* Atoms used to talk to ghostview */
+ struct {
+ gs_int_rect box; /* region needing updating */
+ long area; /* total area of update */
+ long total; /* total of individual area updates */
+ int count; /* # of updates since flush */
+ } update;
+ Pixmap dest; /* bpixmap if non-0, else use win */
+ x_pixel colors_or; /* 'or' of all device colors used so far */
+ x_pixel colors_and; /* 'and' ditto */
+
+ /* An intermediate pixmap for the stencil case of copy_mono */
+ struct {
+ Pixmap pixmap;
+ GC gc;
+ int raster, height;
+ } cp;
+
+ /* Structure for dealing with the halftone tile. */
+ /* Later this might become a multi-element cache. */
+ struct {
+ Pixmap pixmap;
+ Pixmap no_pixmap; /* kludge to get around X bug */
+ gx_bitmap_id id;
+ int width, height, raster;
+ x_pixel fore_c, back_c;
+ } ht;
+
+ /* Cache the function and fill style from the GC */
+ int function;
+ int fill_style;
+ Font fid;
+
+#define X_SET_FILL_STYLE(xdev, style)\
+ BEGIN\
+ if (xdev->fill_style != (style))\
+ XSetFillStyle(xdev->dpy, xdev->gc, (xdev->fill_style = (style)));\
+ END
+#define X_SET_FUNCTION(xdev, func)\
+ BEGIN\
+ if (xdev->function != (func))\
+ XSetFunction(xdev->dpy, xdev->gc, (xdev->function = (func)));\
+ END
+#define X_SET_FONT(xdev, font)\
+ BEGIN\
+ if (xdev->fid != (font))\
+ XSetFont(xdev->dpy, xdev->gc, (xdev->fid = (font)));\
+ END
+
+ x_pixel back_color, fore_color;
+
+ Pixel background, foreground;
+
+ /*
+ * The color management structure is defined in gdevxcmp.h and is
+ * managed by the code in gdevxcmp.c.
+ */
+ x11_cman_t cman;
+
+#define NOTE_COLOR(xdev, pixel)\
+ (xdev->colors_or |= (pixel),\
+ xdev->colors_and &= (pixel))
+#define X_SET_BACK_COLOR(xdev, pixel)\
+ BEGIN\
+ if (xdev->back_color != (pixel)) {\
+ xdev->back_color = (pixel);\
+ NOTE_COLOR(xdev, pixel);\
+ XSetBackground(xdev->dpy, xdev->gc, (pixel));\
+ }\
+ END
+#define X_SET_FORE_COLOR(xdev, pixel)\
+ BEGIN\
+ if (xdev->fore_color != (pixel)) {\
+ xdev->fore_color = (pixel);\
+ NOTE_COLOR(xdev, pixel);\
+ XSetForeground(xdev->dpy, xdev->gc, (pixel));\
+ }\
+ END
+
+ /* Defaults set by resources */
+ Pixel borderColor;
+ Dimension borderWidth;
+ String geometry;
+ int maxGrayRamp, maxRGBRamp;
+ String palette;
+ float xResolution, yResolution;
+
+ /* Flags work around various X server problems. */
+ Boolean useBackingPixmap;
+ Boolean useXPutImage;
+ Boolean useXSetTile;
+
+ /*
+ * Parameters for the screen update algorithms.
+ */
+
+ /*
+ * Define whether to update after every write, for debugging.
+ * Note that one can obtain the same effect by setting any of
+ */
+ bool AlwaysUpdate;
+ /*
+ * Define the maximum size of the temporary pixmap for copy_mono
+ * that we are willing to leave lying around in the server
+ * between uses.
+ */
+ int MaxTempPixmap;
+ /*
+ * Define the maximum size of the temporary image created in memory
+ * for get_bits_rectangle.
+ */
+ int MaxTempImage;
+
+ /*
+ * Buffered text awaiting display.
+ */
+ struct {
+ int item_count;
+#define IN_TEXT(xdev) ((xdev)->text.item_count != 0)
+ int char_count;
+ gs_int_point origin;
+ int x; /* after last buffered char */
+#define MAX_TEXT_ITEMS 12
+ XTextItem items[MAX_TEXT_ITEMS];
+#define MAX_TEXT_CHARS 25
+ char chars[MAX_TEXT_CHARS];
+ } text;
+/*
+ * All the GC parameters are set correctly when we buffer the first
+ * character: we must call DRAW_TEXT before resetting any of them.
+ * DRAW_TEXT assumes xdev->text.{item,char}_count > 0.
+ */
+#define DRAW_TEXT(xdev)\
+ XDrawText(xdev->dpy, xdev->dest, xdev->gc, xdev->text.origin.x,\
+ xdev->text.origin.y, xdev->text.items, xdev->text.item_count)
+
+} gx_device_X;
+#define private_st_device_X() /* in gdevx.c */\
+ gs_public_st_suffix_add1_final(st_device_X, gx_device_X,\
+ "gx_device_X", device_x_enum_ptrs, device_x_reloc_ptrs,\
+ gx_device_finalize, st_device_bbox, buffer)
+
+/* Send an event to the Ghostview process */
+void gdev_x_send_event(gx_device_X *xdev, Atom msg);
+
+/* function to keep track of screen updates */
+void x_update_add(gx_device_X *, int, int, int, int);
+void gdev_x_clear_window(gx_device_X *);
+int x_catch_free_colors(Display *, XErrorEvent *);
+
+/* Number used to distinguish when resolution was set from the command line */
+#define FAKE_RES (16*72)
+
+/* ------ Inter-module procedures ------ */
+
+/* Exported by gdevxcmp.c for gdevxini.c */
+int gdev_x_setup_colors(gx_device_X *);
+void gdev_x_free_colors(gx_device_X *);
+void gdev_x_free_dynamic_colors(gx_device_X *);
+
+/* Exported by gdevxini.c for gdevx.c */
+int gdev_x_open(gx_device_X *);
+int gdev_x_close(gx_device_X *);
+
+/* Driver procedures exported for gdevx.c */
+dev_proc_map_rgb_color(gdev_x_map_rgb_color); /* gdevxcmp.c */
+dev_proc_map_color_rgb(gdev_x_map_color_rgb); /* gdevxcmp.c */
+dev_proc_get_params(gdev_x_get_params); /* gdevxini.c */
+dev_proc_put_params(gdev_x_put_params); /* gdevxini.c */
+dev_proc_finish_copydevice(gdev_x_finish_copydevice); /* gdevxini.c */
+
+#endif /* gdevx_INCLUDED */
diff --git a/devices/gdevxalt.c b/devices/gdevxalt.c
new file mode 100644
index 000000000..9e3f5b664
--- /dev/null
+++ b/devices/gdevxalt.c
@@ -0,0 +1,864 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Alternative X Windows drivers for help in driver debugging */
+#include "gx.h" /* for gx_bitmap; includes std.h */
+#include "math_.h"
+#include "memory_.h"
+#include "x_.h"
+#include "gserrors.h"
+#include "gsparam.h"
+#include "gsstruct.h"
+#include "gxdevice.h"
+#include "gsdevice.h" /* for gs_copydevice */
+#include "gdevx.h"
+
+extern const gx_device_X gs_x11_device;
+extern const gx_device_X gs_x11alpha_device;
+
+/*
+ * Define a forwarding device with a cache for the first 16 colors,
+ * which avoids all of the time-consuming color mapping calls for
+ * the black-and-white, 2-bit gray, and 1-bit CMYK devices defined here.
+ */
+typedef struct {
+ gx_device_forward_common;
+ gx_color_index color_cache[16];
+ /*
+ * alt_map_color returns a value >= 0 if it maps directly to the final
+ * gx_color_index, or < 0 if it only sets RGB values.
+ */
+ dev_proc_map_color_rgb((*alt_map_color));
+} gx_device_X_wrapper;
+#define X_WRAPPER_DATA(amc_proc)\
+ /* gx_device_forward_common */\
+ {0}, /* std_procs */\
+ 0, /* target */\
+ /* gx_device_X_wrapper */\
+ {0}, /* cache */\
+ amc_proc
+gs_private_st_suffix_add0_final(st_device_X_wrapper, gx_device_X_wrapper,
+ "gx_device_X_wrapper", gdevx_wrapper_enum_ptrs, gdevx_wrapper_reloc_ptrs,
+ gx_device_finalize, st_device_forward);
+
+/* ---------------- Generic procedures ---------------- */
+
+/* Forward declarations */
+static int get_dev_target(gx_device **, gx_device *);
+
+static int get_target_info(gx_device *);
+static gx_color_index x_alt_map_color(gx_device *, gx_color_index);
+
+/* Clear the color mapping cache. */
+static void
+x_clear_color_cache(gx_device /*gx_device_X_wrapper */ * dev)
+{
+ gx_device_X_wrapper *xdev = (gx_device_X_wrapper *) dev;
+ int i;
+
+ for (i = 0; i < countof(xdev->color_cache); ++i)
+ xdev->color_cache[i] = gx_no_color_index;
+ gx_device_decache_colors(dev);
+}
+
+/* "Wrappers" for driver procedures */
+
+static int
+x_wrap_open(gx_device * dev)
+{
+ gx_device *tdev;
+ int rcode, code;
+
+ if ((code = get_dev_target(&tdev, dev)) < 0)
+ return code;
+ rcode = (*dev_proc(tdev, open_device)) (tdev);
+ if (rcode < 0)
+ return rcode;
+ tdev->is_open = true;
+ code = get_target_info(dev);
+ return (code < 0 ? code : rcode);
+}
+
+static int
+x_forward_sync_output(gx_device * dev)
+{
+ gx_device *tdev;
+ int code;
+
+ if ((code = get_dev_target(&tdev, dev)) < 0)
+ return code;
+ return (*dev_proc(tdev, sync_output)) (tdev);
+}
+
+static int
+x_forward_output_page(gx_device * dev, int num_copies, int flush)
+{
+ gx_device *tdev;
+ int code;
+
+ if ((code = get_dev_target(&tdev, dev)) < 0)
+ return code;
+ return (*dev_proc(tdev, output_page)) (tdev, num_copies, flush);
+}
+
+static int
+x_wrap_close(gx_device * dev)
+{
+ /*
+ * The underlying x11 device will be closed and freed as soon as there
+ * are no more pointers to it, which normally occurs in the next
+ * statement.
+ */
+ gx_device_set_target((gx_device_forward *)dev, NULL);
+ x_clear_color_cache(dev);
+ return 0;
+}
+
+static int
+x_wrap_map_color_rgb(gx_device * dev, gx_color_index color,
+ gx_color_value prgb[3])
+{
+ gx_device *tdev;
+ int code;
+
+ if ((code = get_dev_target(&tdev, dev)) < 0)
+ return code;
+ return (*dev_proc(tdev, map_color_rgb)) (tdev,
+ x_alt_map_color(dev, color),
+ prgb);
+}
+
+static int
+x_wrap_fill_rectangle(gx_device * dev, int x, int y, int w, int h,
+ gx_color_index color)
+{
+ gx_device *tdev;
+ int code;
+
+ if ((code = get_dev_target(&tdev, dev)) < 0)
+ return code;
+ return (*dev_proc(tdev, fill_rectangle)) (tdev, x, y, w, h,
+ x_alt_map_color(dev, color));
+}
+
+static int
+x_wrap_copy_mono(gx_device * dev,
+ const byte * base, int sourcex, int raster, gx_bitmap_id id,
+ int x, int y, int w, int h,
+ gx_color_index zero, gx_color_index one)
+{
+ gx_device *tdev;
+ int code;
+
+ if ((code = get_dev_target(&tdev, dev)) < 0)
+ return code;
+ return (*dev_proc(tdev, copy_mono)) (tdev, base, sourcex, raster, id,
+ x, y, w, h,
+ x_alt_map_color(dev, zero),
+ x_alt_map_color(dev, one));
+
+}
+
+static int
+x_wrap_copy_color(gx_device * dev, const byte * base, int sourcex,
+ int raster, gx_bitmap_id id, int x, int y, int w, int h)
+{
+ gx_device *tdev;
+
+#define mapped_bytes 480 /* must be a multiple of 3 & 4 */
+ int depth_bytes, source_bits;
+ int block_w, block_h;
+ int xblock, yblock;
+ byte mapped[mapped_bytes];
+ int code;
+
+ fit_copy(dev, base, sourcex, raster, id, x, y, w, h);
+ if ((code = get_dev_target(&tdev, dev)) < 0)
+ return code;
+ /* Device pixels must be an integral number of bytes. */
+ if (tdev->color_info.depth & 7)
+ return gx_default_copy_color(dev, base, sourcex, raster, id,
+ x, y, w, h);
+ depth_bytes = tdev->color_info.depth >> 3;
+ source_bits = dev->color_info.depth;
+ {
+ int mapped_pixels = mapped_bytes / depth_bytes;
+
+ if (w > mapped_pixels >> 1)
+ block_w = min(w, mapped_pixels), block_h = 1;
+ else
+ block_w = w, block_h = mapped_pixels / w;
+ }
+ for (yblock = y; yblock < y + h; yblock += block_h)
+ for (xblock = x; xblock < x + w; xblock += block_w) {
+ byte *p = mapped;
+ int xend = min(xblock + block_w, x + w);
+ int yend = min(yblock + block_h, y + h);
+ int xcur, ycur;
+ int code;
+
+ for (ycur = yblock; ycur < yend; ++ycur)
+ for (xcur = xblock; xcur < xend; ++xcur) {
+ int sbit = (xcur - x + sourcex) * source_bits;
+ uint sbyte =
+ base[(ycur - y) * raster + (sbit >> 3)];
+ uint spixel =
+ ((sbyte << (sbit & 7)) & 0xff) >> (8 - source_bits);
+ gx_color_index cindex =
+ ((gx_device_X_wrapper *) dev)->color_cache[spixel];
+
+ if (cindex == gx_no_color_index)
+ cindex = x_alt_map_color(dev, spixel);
+ switch (depth_bytes) {
+ case 4:
+ *p++ = (byte) (cindex >> 24);
+ case 3:
+ *p++ = (byte) (cindex >> 16);
+ case 2:
+ *p++ = (byte) (cindex >> 8);
+ default /*case 1 */ :
+ *p++ = (byte) cindex;
+ }
+ }
+ code = (*dev_proc(tdev, copy_color))
+ (tdev, mapped, 0, (xend - xblock) * depth_bytes, gx_no_bitmap_id,
+ xblock, yblock, xend - xblock, yend - yblock);
+ if (code < 0)
+ return code;
+ }
+ return 0;
+}
+
+static int
+x_forward_copy_color(gx_device * dev, const byte * base, int sourcex,
+ int raster, gx_bitmap_id id, int x, int y, int w, int h)
+{
+ gx_device *tdev;
+ int code;
+
+ if ((code = get_dev_target(&tdev, dev)) < 0)
+ return code;
+ return (*dev_proc(tdev, copy_color)) (tdev, base, sourcex, raster, id,
+ x, y, w, h);
+}
+
+static int
+x_forward_get_bits(gx_device * dev, int y, byte * str, byte ** actual_data)
+{
+ gx_device *tdev;
+ int code;
+
+ if ((code = get_dev_target(&tdev, dev)) < 0)
+ return code;
+ return (*dev_proc(tdev, get_bits)) (tdev, y, str, actual_data);
+}
+
+static int
+x_wrap_get_bits(gx_device * dev, int y, byte * str, byte ** actual_data)
+{
+ int depth = dev->color_info.depth;
+ gx_device *tdev;
+ int width;
+ int sdepth;
+ byte smask;
+ uint dsize;
+ gs_memory_t *mem = dev->memory;
+ byte *row;
+ byte *base;
+ int code;
+ gx_color_index pixel_in = gx_no_color_index;
+ /*
+ * The following initialization is unnecessary: since no pixel has a
+ * value of gx_no_color_index, the test pixel != pixel_in will always
+ * succeed the first time through the loop below, so pixel_out will
+ * always be set before it is used. We initialize pixel_out solely to
+ * suppress bogus warning messages from certain compilers.
+ */
+ gx_color_index pixel_out = 0;
+ int xi;
+ int sbit;
+
+ DECLARE_LINE_ACCUM(str, depth, 0);
+
+ if ((code = get_dev_target(&tdev, dev)) < 0)
+ return code;
+ width = tdev->width;
+ sdepth = tdev->color_info.depth;
+ smask = (sdepth <= 8 ? (1 << sdepth) - 1 : 0xff);
+ dsize = (width * sdepth + 7) / 8;
+ row = gs_alloc_bytes(mem, dsize, "x_wrap_get_bits");
+ if (row == 0)
+ return_error(gs_error_VMerror);
+ code = (*dev_proc(tdev, get_bits)) (tdev, y, row, &base);
+ if (code < 0)
+ goto gx;
+ for (sbit = 0, xi = 0; xi < width; sbit += sdepth, ++xi) {
+ const byte *sptr = base + (sbit >> 3);
+ gx_color_index pixel;
+ gx_color_value rgb[3];
+ int i;
+
+ if (sdepth <= 8)
+ pixel = (*sptr >> (8 - sdepth - (sbit & 7))) & smask;
+ else {
+ pixel = 0;
+ for (i = 0; i < sdepth; i += 8, ++sptr)
+ pixel = (pixel << 8) + *sptr;
+ }
+ if (pixel != pixel_in) {
+ (*dev_proc(tdev, map_color_rgb))(tdev, pixel, rgb);
+ pixel_in = pixel;
+ if (dev->color_info.num_components <= 3)
+ pixel_out = (*dev_proc(dev, map_rgb_color))(dev, rgb);
+ else {
+ /* Convert RGB to CMYK. */
+ gx_color_value c = gx_max_color_value - rgb[0];
+ gx_color_value m = gx_max_color_value - rgb[1];
+ gx_color_value y = gx_max_color_value - rgb[2];
+ gx_color_value k = (c < m ? min(c, y) : min(m, y));
+
+ gx_color_value cmyk[4];
+ cmyk[0] = c - k; cmyk[1] = m - k; cmyk[2] = y - k; cmyk[3] = k;
+ pixel_out = (*dev_proc(dev, map_cmyk_color))(dev, cmyk);
+ }
+ }
+ LINE_ACCUM(pixel_out, depth);
+ }
+ LINE_ACCUM_STORE(depth);
+ gx:gs_free_object(mem, row, "x_wrap_get_bits");
+ if (actual_data)
+ *actual_data = str;
+ return code;
+}
+
+static int
+x_wrap_get_params(gx_device * dev, gs_param_list * plist)
+{
+ gx_device *tdev;
+ /* We assume that a get_params call has no side effects.... */
+ gx_device_X save_dev;
+ int ecode;
+ int code;
+
+ if ((code = get_dev_target(&tdev, dev)) < 0)
+ return code;
+ save_dev = *(gx_device_X *) tdev;
+ if (tdev->is_open)
+ tdev->color_info = dev->color_info;
+ tdev->dname = dev->dname;
+ ecode = (*dev_proc(tdev, get_params)) (tdev, plist);
+ *(gx_device_X *) tdev = save_dev;
+ return ecode;
+}
+
+static int
+x_wrap_put_params(gx_device * dev, gs_param_list * plist)
+{
+ gx_device *tdev;
+ gx_device_color_info cinfo;
+ const char *dname;
+ int rcode, code;
+
+ if ((code = get_dev_target(&tdev, dev)) < 0)
+ return code;
+ /*
+ * put_params will choke if we simply feed it the output of
+ * get_params; we have to substitute color_info the same way.
+ */
+ cinfo = tdev->color_info;
+ dname = tdev->dname;
+ tdev->color_info = dev->color_info;
+ tdev->dname = dev->dname;
+ rcode = (*dev_proc(tdev, put_params)) (tdev, plist);
+ tdev->color_info = cinfo;
+ tdev->dname = dname;
+ if (rcode < 0)
+ return rcode;
+ code = get_target_info(dev);
+ return (code < 0 ? code : rcode);
+}
+
+/* Internal procedures */
+
+/* Get the target, creating it if necessary. */
+static int
+get_dev_target(gx_device ** ptdev, gx_device * dev)
+{
+ gx_device *tdev = ((gx_device_forward *) dev)->target;
+
+ if (tdev == 0) {
+ /* Create an X device instance. */
+ int code = gs_copydevice(&tdev, (const gx_device *)&gs_x11_device,
+ dev->memory);
+
+ if (code < 0)
+ return 0;
+ check_device_separable(tdev);
+ gx_device_fill_in_procs(tdev);
+ gx_device_set_target((gx_device_forward *)dev, tdev);
+ x_clear_color_cache(dev);
+ }
+ *ptdev = tdev;
+ return 0;
+}
+
+/* Copy parameters back from the target. */
+static int
+get_target_info(gx_device * dev)
+{
+ gx_device *tdev;
+ int code;
+
+ if ((code = get_dev_target(&tdev, dev)) < 0)
+ return code;
+
+#define copy(m) dev->m = tdev->m;
+#define copy2(m) copy(m[0]); copy(m[1])
+#define copy4(m) copy2(m); copy(m[2]); copy(m[3])
+
+ copy(width);
+ copy(height);
+ copy2(MediaSize);
+ copy4(ImagingBBox);
+ copy(ImagingBBox_set);
+ copy2(HWResolution);
+ copy2(MarginsHWResolution);
+ copy2(Margins);
+ copy4(HWMargins);
+ if (dev->color_info.num_components == 3) {
+ /* Leave the anti-aliasing information alone. */
+ gx_device_anti_alias_info aa;
+
+ aa = dev->color_info.anti_alias;
+ copy(color_info);
+ dev->color_info.anti_alias = aa;
+ }
+
+#undef copy4
+#undef copy2
+#undef copy
+
+ x_clear_color_cache(dev);
+ return 0;
+}
+
+/* Map a fake CMYK or black/white color to a real X color if necessary. */
+static gx_color_index
+x_alt_map_color(gx_device * dev, gx_color_index color)
+{
+ gx_device_X_wrapper *xdev = (gx_device_X_wrapper *) dev;
+ gx_device *tdev;
+ gx_color_value rgb[3];
+ gx_color_index cindex;
+ int result;
+ int code;
+
+ if (color == gx_no_color_index)
+ return color;
+ if (color < 16) {
+ cindex = ((gx_device_X_wrapper *) dev)->color_cache[color];
+ if (cindex != gx_no_color_index)
+ return cindex;
+ }
+ if ((code = get_dev_target(&tdev, dev)) < 0)
+ return code;
+ result = xdev->alt_map_color(dev, color, rgb);
+ if (result >= 0)
+ cindex = result;
+ else
+ cindex = dev_proc(tdev, map_rgb_color)(tdev, rgb);
+ if (color < 16)
+ ((gx_device_X_wrapper *) dev)->color_cache[color] = cindex;
+ return cindex;
+}
+
+/* ---------------- CMYK procedures ---------------- */
+
+/* Device procedures */
+static dev_proc_open_device(x_cmyk_open);
+static dev_proc_put_params(x_cmyk_put_params);
+static dev_proc_map_cmyk_color(x_cmyk_map_cmyk_color);
+/* Extended device procedures */
+static dev_proc_map_color_rgb(x_cmyk_alt_map_color);
+
+/* The device descriptor */
+static const gx_device_procs x_cmyk_procs = {
+ x_cmyk_open,
+ gx_forward_get_initial_matrix,
+ x_forward_sync_output,
+ x_forward_output_page,
+ x_wrap_close,
+ NULL, /* map_rgb_color */
+ x_wrap_map_color_rgb,
+ x_wrap_fill_rectangle,
+ gx_default_tile_rectangle,
+ x_wrap_copy_mono,
+ x_wrap_copy_color,
+ gx_default_draw_line,
+ x_wrap_get_bits,
+ x_wrap_get_params,
+ x_cmyk_put_params,
+ x_cmyk_map_cmyk_color,
+ gx_forward_get_xfont_procs,
+ gx_forward_get_xfont_device,
+ NULL, /* map_rgb_alpha_color */
+ gx_forward_get_page_device,
+ gx_forward_get_alpha_bits,
+ NULL /* copy_alpha */
+};
+
+/* The instances are public. */
+const gx_device_X_wrapper gs_x11cmyk_device = {
+ std_device_dci_type_body(gx_device_X_wrapper, &x_cmyk_procs, "x11cmyk",
+ &st_device_X_wrapper,
+ FAKE_RES * 85 / 10, FAKE_RES * 11, /* x and y extent (nominal) */
+ FAKE_RES, FAKE_RES, /* x and y density (nominal) */
+ 4, 4, 1, 1, 2, 2),
+ X_WRAPPER_DATA(x_cmyk_alt_map_color)
+};
+const gx_device_X_wrapper gs_x11cmyk2_device = {
+ std_device_dci_type_body(gx_device_X_wrapper, &x_cmyk_procs, "x11cmyk2",
+ &st_device_X_wrapper,
+ FAKE_RES * 85 / 10, FAKE_RES * 11, /* x and y extent (nominal) */
+ FAKE_RES, FAKE_RES, /* x and y density (nominal) */
+ 4, 8, 3, 3, 4, 4),
+ X_WRAPPER_DATA(x_cmyk_alt_map_color)
+};
+const gx_device_X_wrapper gs_x11cmyk4_device = {
+ std_device_dci_type_body(gx_device_X_wrapper, &x_cmyk_procs, "x11cmyk4",
+ &st_device_X_wrapper,
+ FAKE_RES * 85 / 10, FAKE_RES * 11, /* x and y extent (nominal) */
+ FAKE_RES, FAKE_RES, /* x and y density (nominal) */
+ 4, 16, 15, 15, 16, 16),
+ X_WRAPPER_DATA(x_cmyk_alt_map_color)
+};
+const gx_device_X_wrapper gs_x11cmyk8_device = {
+ std_device_dci_type_body(gx_device_X_wrapper, &x_cmyk_procs, "x11cmyk8",
+ &st_device_X_wrapper,
+ FAKE_RES * 85 / 10, FAKE_RES * 11, /* x and y extent (nominal) */
+ FAKE_RES, FAKE_RES, /* x and y density (nominal) */
+ 4, 32, 255, 255, 256, 256),
+ X_WRAPPER_DATA(x_cmyk_alt_map_color)
+};
+
+/* Map a fake color to RGB. */
+static int
+x_cmyk_alt_map_color(gx_device * dev, gx_color_index color,
+ gx_color_value rgb[3])
+{
+ int shift = dev->color_info.depth >> 2;
+ int mask = (1 << shift) - 1;
+ /* The following division is guaranteed exact. */
+ gx_color_value scale = gx_max_color_value / mask;
+ int cw = ~color & mask;
+ int cb = cw - ((color >> shift) & mask);
+ int cg = cw - ((color >> (shift * 2)) & mask);
+ int cr = cw - ((color >> (shift * 3)) & mask);
+
+ rgb[0] = max(cr, 0) * scale;
+ rgb[1] = max(cg, 0) * scale;
+ rgb[2] = max(cb, 0) * scale;
+ return -1;
+}
+
+/* Set color mapping procedures */
+static void
+x_cmyk_set_procs(gx_device *dev)
+{
+ if (dev->color_info.depth == 4) {
+ set_dev_proc(dev, map_cmyk_color, cmyk_1bit_map_cmyk_color);
+ } else {
+ set_dev_proc(dev, map_cmyk_color, x_cmyk_map_cmyk_color);
+ }
+}
+
+/* Device procedures */
+
+static int
+x_cmyk_open(gx_device *dev)
+{
+ int code = x_wrap_open(dev);
+
+ if (code >= 0)
+ x_cmyk_set_procs(dev);
+ return code;
+}
+
+static int
+x_cmyk_put_params(gx_device * dev, gs_param_list * plist)
+{
+ int code = x_wrap_put_params(dev, plist);
+
+ if (code >= 0)
+ x_cmyk_set_procs(dev);
+ return code;
+}
+
+static gx_color_index
+x_cmyk_map_cmyk_color(gx_device * dev, const gx_color_value cv[])
+{
+ int shift = dev->color_info.depth >> 2;
+ gx_color_index pixel = cv[0] >> (gx_color_value_bits - shift);
+ gx_color_value c, m, y, k;
+ c = cv[0]; m = cv[1]; y = cv[2]; k = cv[3];
+ pixel = (pixel << shift) | (m >> (gx_color_value_bits - shift));
+ pixel = (pixel << shift) | (y >> (gx_color_value_bits - shift));
+ return (pixel << shift) | (k >> (gx_color_value_bits - shift));
+}
+
+/* ---------------- Black-and-white procedures ---------------- */
+
+/* Extended device procedures */
+static dev_proc_map_color_rgb(x_mono_alt_map_color);
+
+/* The device descriptor */
+static const gx_device_procs x_mono_procs = {
+ x_wrap_open,
+ gx_forward_get_initial_matrix,
+ x_forward_sync_output,
+ x_forward_output_page,
+ x_wrap_close,
+ gx_default_b_w_map_rgb_color,
+ x_wrap_map_color_rgb,
+ x_wrap_fill_rectangle,
+ gx_default_tile_rectangle,
+ x_wrap_copy_mono,
+ gx_default_copy_color, /* this is fast for the 1-bit case */
+ gx_default_draw_line,
+ x_wrap_get_bits,
+ x_wrap_get_params,
+ x_wrap_put_params,
+ gx_default_map_cmyk_color,
+ gx_forward_get_xfont_procs,
+ gx_forward_get_xfont_device,
+ NULL, /* map_rgb_alpha_color */
+ gx_forward_get_page_device,
+ gx_forward_get_alpha_bits,
+ NULL /* copy_alpha */
+};
+
+/* The instance is public. */
+const gx_device_X_wrapper gs_x11mono_device = {
+ std_device_dci_type_body(gx_device_X_wrapper, &x_mono_procs, "x11mono",
+ &st_device_X_wrapper,
+ FAKE_RES * 85 / 10, FAKE_RES * 11, /* x and y extent (nominal) */
+ FAKE_RES, FAKE_RES, /* x and y density (nominal) */
+ 1, 1, 1, 0, 2, 0),
+ X_WRAPPER_DATA(x_mono_alt_map_color)
+};
+
+/* Map a fake color to RGB. */
+static int
+x_mono_alt_map_color(gx_device * dev, gx_color_index color,
+ gx_color_value rgb[3])
+{
+ rgb[0] = rgb[1] = rgb[2] = (color ? 0 : gx_max_color_value);
+ return -1;
+}
+
+/* ---------------- 2- and 4-bit gray-scale procedures ---------------- */
+
+/* Extended device procedures */
+static dev_proc_map_color_rgb(x_gray_alt_map_color);
+
+/* The device descriptor */
+static const gx_device_procs x_gray_procs = {
+ x_wrap_open,
+ gx_forward_get_initial_matrix,
+ x_forward_sync_output,
+ x_forward_output_page,
+ x_wrap_close,
+ gx_default_gray_map_rgb_color,
+ x_wrap_map_color_rgb,
+ x_wrap_fill_rectangle,
+ gx_default_tile_rectangle,
+ x_wrap_copy_mono,
+ x_wrap_copy_color,
+ gx_default_draw_line,
+ x_wrap_get_bits,
+ x_wrap_get_params,
+ x_wrap_put_params,
+ gx_default_map_cmyk_color,
+ gx_forward_get_xfont_procs,
+ gx_forward_get_xfont_device,
+ NULL, /* map_rgb_alpha_color */
+ gx_forward_get_page_device,
+ gx_forward_get_alpha_bits,
+ NULL /* copy_alpha */
+};
+
+/* The instances are public. */
+const gx_device_X_wrapper gs_x11gray2_device = {
+ std_device_dci_type_body(gx_device_X_wrapper, &x_gray_procs, "x11gray2",
+ &st_device_X_wrapper,
+ FAKE_RES * 85 / 10, FAKE_RES * 11, /* x and y extent (nominal) */
+ FAKE_RES, FAKE_RES, /* x and y density (nominal) */
+ 1, 2, 3, 0, 4, 0),
+ X_WRAPPER_DATA(x_gray_alt_map_color)
+};
+
+const gx_device_X_wrapper gs_x11gray4_device = {
+ std_device_dci_type_body(gx_device_X_wrapper, &x_gray_procs, "x11gray4",
+ &st_device_X_wrapper,
+ FAKE_RES * 85 / 10, FAKE_RES * 11, /* x and y extent (nominal) */
+ FAKE_RES, FAKE_RES, /* x and y density (nominal) */
+ 1, 4, 15, 0, 16, 0),
+ X_WRAPPER_DATA(x_gray_alt_map_color)
+};
+
+/* Map a fake color to RGB. */
+static int
+x_gray_alt_map_color(gx_device * dev, gx_color_index color,
+ gx_color_value rgb[3])
+{
+ rgb[0] = rgb[1] = rgb[2] =
+ color * gx_max_color_value / dev->color_info.max_gray;
+ return -1;
+}
+
+/* Device procedures */
+
+/* We encode a complemented alpha value in the top 8 bits of the */
+/* device color. */
+static gx_color_index
+x_alpha_map_rgb_alpha_color(gx_device * dev,
+ gx_color_value r, gx_color_value g, gx_color_value b, gx_color_value alpha)
+{
+ gx_color_index color;
+ gx_color_value cv[3];
+ byte abyte = alpha >> (gx_color_value_bits - 8);
+ cv[0] = r; cv[1] = g; cv[2] = b;
+ color = gx_forward_map_rgb_color(dev, cv);
+ return (abyte == 0 ? (gx_color_index)0xff << 24 :
+ ((gx_color_index) (abyte ^ 0xff) << 24) + color);
+}
+
+/* ---------------- Permuted RGB16/32 procedures ---------------- */
+
+/* Device procedures */
+static dev_proc_map_rgb_color(x_rg16x_map_rgb_color);
+static dev_proc_map_rgb_color(x_rg32x_map_rgb_color);
+/* Extended device procedures */
+static dev_proc_map_color_rgb(x_rg16x_alt_map_color);
+static dev_proc_map_color_rgb(x_rg32x_alt_map_color);
+
+/* The device descriptor */
+#define RGBX_PROCS(map_rgb_proc)\
+ x_wrap_open,\
+ gx_forward_get_initial_matrix,\
+ x_forward_sync_output,\
+ x_forward_output_page,\
+ x_wrap_close,\
+ map_rgb_proc, /* differs */\
+ x_wrap_map_color_rgb,\
+ x_wrap_fill_rectangle,\
+ gx_default_tile_rectangle,\
+ x_wrap_copy_mono,\
+ x_forward_copy_color,\
+ gx_default_draw_line,\
+ x_forward_get_bits,\
+ x_wrap_get_params,\
+ x_wrap_put_params,\
+ gx_forward_map_cmyk_color,\
+ gx_forward_get_xfont_procs,\
+ gx_forward_get_xfont_device,\
+ x_alpha_map_rgb_alpha_color,\
+ gx_forward_get_page_device,\
+ gx_default_get_alpha_bits,\
+ gx_default_copy_alpha
+
+static const gx_device_procs x_rg16x_procs = {
+ RGBX_PROCS(x_rg16x_map_rgb_color)
+};
+const gx_device_X_wrapper gs_x11rg16x_device = {
+ std_device_dci_type_body(gx_device_X_wrapper, &x_rg16x_procs, "x11rg16x",
+ &st_device_X_wrapper,
+ FAKE_RES * 85 / 10, FAKE_RES * 11, /* x and y extent (nominal) */
+ FAKE_RES, FAKE_RES, /* x and y density (nominal) */
+ 3, 16, 31, 31, 32, 32),
+ X_WRAPPER_DATA(x_rg16x_alt_map_color)
+};
+
+static const gx_device_procs x_rg32x_procs = {
+ RGBX_PROCS(x_rg32x_map_rgb_color)
+};
+const gx_device_X_wrapper gs_x11rg32x_device = {
+ std_device_dci_type_body(gx_device_X_wrapper, &x_rg32x_procs, "x11rg32x",
+ &st_device_X_wrapper,
+ FAKE_RES * 85 / 10, FAKE_RES * 11, /* x and y extent (nominal) */
+ FAKE_RES, FAKE_RES, /* x and y density (nominal) */
+ 3, 32, 1023, 1023, 1024, 1024),
+ X_WRAPPER_DATA(x_rg32x_alt_map_color)
+};
+
+/* Map RGB to a fake color. */
+static gx_color_index
+x_rg16x_map_rgb_color(gx_device * dev, const gx_color_value cv[])
+{
+ /* Permute the colors to G5/B5/R6. */
+ gx_color_value r, g, b;
+ r = cv[0]; g = cv[1]; b = cv[2];
+ return (r >> (gx_color_value_bits - 6)) +
+ ((g >> (gx_color_value_bits - 5)) << 11) +
+ ((b >> (gx_color_value_bits - 5)) << 6);
+}
+static gx_color_index
+x_rg32x_map_rgb_color(gx_device * dev, const gx_color_value cv[])
+{
+ /* Permute the colors to G11/B10/R11. */
+ gx_color_value r, g, b;
+ r = cv[0]; g = cv[1]; b = cv[2];
+ return (r >> (gx_color_value_bits - 11)) +
+ ((gx_color_index)(g >> (gx_color_value_bits - 11)) << 21) +
+ ((gx_color_index)(b >> (gx_color_value_bits - 10)) << 11);
+}
+
+/* Map a fake color to RGB. */
+static int
+x_rg16x_alt_map_color(gx_device * dev, gx_color_index color,
+ gx_color_value rgb[3])
+{
+ rgb[0] = (color & 0x3f) * gx_max_color_value / 0x3f;
+ rgb[1] = ((color >> 11) & 0x1f) * gx_max_color_value / 0x1f;
+ rgb[2] = ((color >> 6) & 0x1f) * gx_max_color_value / 0x1f;
+ return -1;
+}
+static int
+x_rg32x_alt_map_color(gx_device * dev, gx_color_index color,
+ gx_color_value rgb[3])
+{
+ rgb[0] = (color & 0x7ff) * gx_max_color_value / 0x7ff;
+ rgb[1] = ((color >> 21) & 0x7ff) * gx_max_color_value / 0x7ff;
+ rgb[2] = ((color >> 11) & 0x3ff) * gx_max_color_value / 0x3ff;
+ return -1;
+}
+
+#ifdef GS_DEVS_SHARED
+extern void gs_lib_register_device(const gx_device *dev);
+void
+gs_shared_init(void)
+{
+ gs_lib_register_device(&gs_x11_device);
+ gs_lib_register_device(&gs_x11alpha_device);
+ gs_lib_register_device(&gs_x11cmyk_device);
+ gs_lib_register_device(&gs_x11cmyk2_device);
+ gs_lib_register_device(&gs_x11cmyk4_device);
+ gs_lib_register_device(&gs_x11cmyk8_device);
+ gs_lib_register_device(&gs_x11gray2_device);
+ gs_lib_register_device(&gs_x11gray4_device);
+ gs_lib_register_device(&gs_x11mono_device);
+}
+#endif
diff --git a/devices/gdevxcf.c b/devices/gdevxcf.c
new file mode 100644
index 000000000..2c91ef6df
--- /dev/null
+++ b/devices/gdevxcf.c
@@ -0,0 +1,1470 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Gimp (XCF) export device, supporting DeviceN color models. */
+
+#include "math_.h"
+#include "gdevprn.h"
+#include "gsparam.h"
+#include "gscrd.h"
+#include "gscrdp.h"
+#include "gxlum.h"
+#include "gdevdcrd.h"
+#include "gstypes.h"
+#include "gxdcconv.h"
+#include "gscms.h"
+#include "gsicc_cache.h"
+#include "gsicc_manage.h"
+
+#ifndef cmm_gcmmhlink_DEFINED
+ #define cmm_gcmmhlink_DEFINED
+ typedef void* gcmmhlink_t;
+#endif
+
+#ifndef cmm_gcmmhprofile_DEFINED
+ #define cmm_gcmmhprofile_DEFINED
+ typedef void* gcmmhprofile_t;
+#endif
+
+#ifndef MAX_CHAN
+# define MAX_CHAN 8
+#endif
+
+/* Define the device parameters. */
+#ifndef X_DPI
+# define X_DPI 72
+#endif
+#ifndef Y_DPI
+# define Y_DPI 72
+#endif
+
+/* The device descriptor */
+static dev_proc_get_params(xcf_get_params);
+static dev_proc_close_device(xcf_prn_close);
+static dev_proc_put_params(xcf_put_params);
+static dev_proc_print_page(xcf_print_page);
+static dev_proc_map_color_rgb(xcf_map_color_rgb);
+static dev_proc_get_color_mapping_procs(get_spotrgb_color_mapping_procs);
+#if 0
+static dev_proc_get_color_mapping_procs(get_spotcmyk_color_mapping_procs);
+#endif
+static dev_proc_get_color_mapping_procs(get_xcf_color_mapping_procs);
+static dev_proc_get_color_comp_index(xcf_get_color_comp_index);
+static dev_proc_encode_color(xcf_encode_color);
+static dev_proc_decode_color(xcf_decode_color);
+
+/*
+ * Type definitions associated with the fixed color model names.
+ */
+typedef const char * fixed_colorant_name;
+typedef fixed_colorant_name fixed_colorant_names_list[];
+
+/*
+ * Structure for holding SeparationNames and SeparationOrder elements.
+ */
+typedef struct gs_separation_names_s {
+ int num_names;
+ const gs_param_string * names[GX_DEVICE_COLOR_MAX_COMPONENTS];
+} gs_separation_names;
+
+/* This is redundant with color_info.cm_name. We may eliminate this
+ typedef and use the latter string for everything. */
+typedef enum {
+ XCF_DEVICE_GRAY,
+ XCF_DEVICE_RGB,
+ XCF_DEVICE_CMYK,
+ XCF_DEVICE_N
+} xcf_color_model;
+
+/*
+ * A structure definition for a DeviceN type device
+ */
+typedef struct xcf_device_s {
+ gx_device_common;
+ gx_prn_device_common;
+
+ /* ... device-specific parameters ... */
+
+ xcf_color_model color_model;
+
+ /*
+ * Bits per component (device colorant). Currently only 1 and 8 are
+ * supported.
+ */
+ int bitspercomponent;
+
+ /*
+ * Pointer to the colorant names for the color model. This will be
+ * null if we have DeviceN type device. The actual possible colorant
+ * names are those in this list plus those in the separation_names
+ * list (below).
+ */
+ const fixed_colorant_names_list * std_colorant_names;
+ int num_std_colorant_names; /* Number of names in list */
+
+ /*
+ * Separation names (if any).
+ */
+ gs_separation_names separation_names;
+
+ /*
+ * Separation Order (if specified).
+ */
+ gs_separation_names separation_order;
+
+ /* ICC color profile objects, for color conversion.
+ These are all device link profiles. At least that
+ is how it appears looking at how this code
+ was written to work with the old icclib. Just
+ doing minimal updates here so that it works
+ with the new CMM API. I would be interested
+ to hear how people are using this. */
+
+ char profile_rgb_fn[256];
+ cmm_profile_t *rgb_profile;
+ gcmmhlink_t rgb_icc_link;
+
+ char profile_cmyk_fn[256];
+ cmm_profile_t *cmyk_profile;
+ gcmmhlink_t cmyk_icc_link;
+
+ char profile_out_fn[256];
+ cmm_profile_t *output_profile;
+ gcmmhlink_t output_icc_link;
+
+} xcf_device;
+
+/*
+ * Macro definition for DeviceN procedures
+ */
+#define device_procs(get_color_mapping_procs)\
+{ gdev_prn_open,\
+ gx_default_get_initial_matrix,\
+ NULL, /* sync_output */\
+ /* Since the print_page doesn't alter the device, this device can print in the background */\
+ gdev_prn_bg_output_page, /* output_page */\
+ xcf_prn_close, /* close */\
+ NULL, /* map_rgb_color - not used */\
+ xcf_map_color_rgb, /* map_color_rgb */\
+ NULL, /* fill_rectangle */\
+ NULL, /* tile_rectangle */\
+ NULL, /* copy_mono */\
+ NULL, /* copy_color */\
+ NULL, /* draw_line */\
+ NULL, /* get_bits */\
+ xcf_get_params, /* get_params */\
+ xcf_put_params, /* put_params */\
+ NULL, /* map_cmyk_color - not used */\
+ NULL, /* get_xfont_procs */\
+ NULL, /* get_xfont_device */\
+ NULL, /* map_rgb_alpha_color */\
+ gx_page_device_get_page_device, /* get_page_device */\
+ NULL, /* get_alpha_bits */\
+ NULL, /* copy_alpha */\
+ NULL, /* get_band */\
+ NULL, /* copy_rop */\
+ NULL, /* fill_path */\
+ NULL, /* stroke_path */\
+ NULL, /* fill_mask */\
+ NULL, /* fill_trapezoid */\
+ NULL, /* fill_parallelogram */\
+ NULL, /* fill_triangle */\
+ NULL, /* draw_thin_line */\
+ NULL, /* begin_image */\
+ NULL, /* image_data */\
+ NULL, /* end_image */\
+ NULL, /* strip_tile_rectangle */\
+ NULL, /* strip_copy_rop */\
+ NULL, /* get_clipping_box */\
+ NULL, /* begin_typed_image */\
+ NULL, /* get_bits_rectangle */\
+ NULL, /* map_color_rgb_alpha */\
+ NULL, /* create_compositor */\
+ NULL, /* get_hardware_params */\
+ NULL, /* text_begin */\
+ NULL, /* finish_copydevice */\
+ NULL, /* begin_transparency_group */\
+ NULL, /* end_transparency_group */\
+ NULL, /* begin_transparency_mask */\
+ NULL, /* end_transparency_mask */\
+ NULL, /* discard_transparency_layer */\
+ get_color_mapping_procs, /* get_color_mapping_procs */\
+ xcf_get_color_comp_index, /* get_color_comp_index */\
+ xcf_encode_color, /* encode_color */\
+ xcf_decode_color /* decode_color */\
+}
+
+static const fixed_colorant_names_list DeviceGrayComponents = {
+ "Gray",
+ 0 /* List terminator */
+};
+
+static const fixed_colorant_names_list DeviceRGBComponents = {
+ "Red",
+ "Green",
+ "Blue",
+ 0 /* List terminator */
+};
+
+static const fixed_colorant_names_list DeviceCMYKComponents = {
+ "Cyan",
+ "Magenta",
+ "Yellow",
+ "Black",
+ 0 /* List terminator */
+};
+
+/*
+ * Example device with RGB and spot color support
+ */
+static const gx_device_procs spot_rgb_procs = device_procs(get_spotrgb_color_mapping_procs);
+
+const xcf_device gs_xcf_device =
+{
+ prn_device_body_extended(xcf_device, spot_rgb_procs, "xcf",
+ DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
+ X_DPI, Y_DPI, /* X and Y hardware resolution */
+ 0, 0, 0, 0, /* margins */
+ GX_DEVICE_COLOR_MAX_COMPONENTS, 3, /* MaxComponents, NumComp */
+ GX_CINFO_POLARITY_ADDITIVE, /* Polarity */
+ 24, 0, /* Depth, Gray_index, */
+ 255, 255, 256, 256, /* MaxGray, MaxColor, DitherGray, DitherColor */
+ GX_CINFO_UNKNOWN_SEP_LIN, /* Let check_device_separable set up values */
+ "DeviceN", /* Process color model name */
+ xcf_print_page), /* Printer page print routine */
+ /* DeviceN device specific parameters */
+ XCF_DEVICE_RGB, /* Color model */
+ 8, /* Bits per color - must match ncomp, depth, etc. above */
+ (&DeviceRGBComponents), /* Names of color model colorants */
+ 3, /* Number colorants for RGB */
+ {0}, /* SeparationNames */
+ {0} /* SeparationOrder names */
+};
+
+static const gx_device_procs spot_cmyk_procs = device_procs(get_xcf_color_mapping_procs);
+
+const xcf_device gs_xcfcmyk_device =
+{
+ prn_device_body_extended(xcf_device, spot_cmyk_procs, "xcfcmyk",
+ DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
+ X_DPI, Y_DPI, /* X and Y hardware resolution */
+ 0, 0, 0, 0, /* margins */
+ GX_DEVICE_COLOR_MAX_COMPONENTS, 4, /* MaxComponents, NumComp */
+ GX_CINFO_POLARITY_SUBTRACTIVE, /* Polarity */
+ 32, 0, /* Depth, Gray_index, */
+ 255, 255, 256, 256, /* MaxGray, MaxColor, DitherGray, DitherColor */
+ GX_CINFO_UNKNOWN_SEP_LIN, /* Let check_device_separable set up values */
+ "DeviceN", /* Process color model name */
+ xcf_print_page), /* Printer page print routine */
+ /* DeviceN device specific parameters */
+ XCF_DEVICE_CMYK, /* Color model */
+ 8, /* Bits per color - must match ncomp, depth, etc. above */
+ (&DeviceCMYKComponents), /* Names of color model colorants */
+ 4, /* Number colorants for RGB */
+ {0}, /* SeparationNames */
+ {0} /* SeparationOrder names */
+};
+
+/*
+ * The following procedures are used to map the standard color spaces into
+ * the color components for the spotrgb device.
+ */
+static void
+gray_cs_to_spotrgb_cm(gx_device * dev, frac gray, frac out[])
+{
+/* TO_DO_DEVICEN This routine needs to include the effects of the SeparationOrder array */
+ int i = ((xcf_device *)dev)->separation_names.num_names;
+
+ out[0] = out[1] = out[2] = gray;
+ for(; i>0; i--) /* Clear spot colors */
+ out[2 + i] = 0;
+}
+
+static void
+rgb_cs_to_spotrgb_cm(gx_device * dev, const gs_imager_state *pis,
+ frac r, frac g, frac b, frac out[])
+{
+/* TO_DO_DEVICEN This routine needs to include the effects of the SeparationOrder array */
+ int i = ((xcf_device *)dev)->separation_names.num_names;
+
+ out[0] = r;
+ out[1] = g;
+ out[2] = b;
+ for(; i>0; i--) /* Clear spot colors */
+ out[2 + i] = 0;
+}
+
+static void
+cmyk_cs_to_spotrgb_cm(gx_device * dev, frac c, frac m, frac y, frac k, frac out[])
+{
+/* TO_DO_DEVICEN This routine needs to include the effects of the SeparationOrder array */
+ int i = ((xcf_device *)dev)->separation_names.num_names;
+
+ color_cmyk_to_rgb(c, m, y, k, NULL, out, dev->memory);
+ for(; i>0; i--) /* Clear spot colors */
+ out[2 + i] = 0;
+}
+
+static void
+gray_cs_to_spotcmyk_cm(gx_device * dev, frac gray, frac out[])
+{
+/* TO_DO_DEVICEN This routine needs to include the effects of the SeparationOrder array */
+ int i = ((xcf_device *)dev)->separation_names.num_names;
+
+ out[0] = out[1] = out[2] = 0;
+ out[3] = frac_1 - gray;
+ for(; i>0; i--) /* Clear spot colors */
+ out[3 + i] = 0;
+}
+
+static void
+rgb_cs_to_spotcmyk_cm(gx_device * dev, const gs_imager_state *pis,
+ frac r, frac g, frac b, frac out[])
+{
+/* TO_DO_DEVICEN This routine needs to include the effects of the SeparationOrder array */
+ xcf_device *xdev = (xcf_device *)dev;
+ int n = xdev->separation_names.num_names;
+ int i;
+
+ color_rgb_to_cmyk(r, g, b, pis, out, dev->memory);
+ for(i = 0; i < n; i++) /* Clear spot colors */
+ out[4 + i] = 0;
+}
+
+static void
+cmyk_cs_to_spotcmyk_cm(gx_device * dev, frac c, frac m, frac y, frac k, frac out[])
+{
+/* TO_DO_DEVICEN This routine needs to include the effects of the SeparationOrder array */
+ xcf_device *xdev = (xcf_device *)dev;
+ int n = xdev->separation_names.num_names;
+ int i;
+
+ out[0] = c;
+ out[1] = m;
+ out[2] = y;
+ out[3] = k;
+ for(i = 0; i < n; i++) /* Clear spot colors */
+ out[4 + i] = 0;
+}
+
+static void
+cmyk_cs_to_spotn_cm(gx_device * dev, frac c, frac m, frac y, frac k, frac out[])
+{
+/* TO_DO_DEVICEN This routine needs to include the effects of the SeparationOrder array */
+ xcf_device *xdev = (xcf_device *)dev;
+ int n = xdev->separation_names.num_names;
+
+ gcmmhlink_t link = xdev->cmyk_icc_link;
+ int i;
+
+ if (link != NULL) {
+ unsigned short in[4];
+ unsigned short tmp[MAX_CHAN];
+ int outn = xdev->cmyk_profile->num_comps_out;
+
+ in[0] = frac2ushort(c);
+ in[1] = frac2ushort(m);
+ in[2] = frac2ushort(y);
+ in[3] = frac2ushort(k);
+
+ gscms_transform_color(dev, link, &(in[0]), &(tmp[0]), 2);
+ for (i = 0; i < outn; i++)
+ out[i] = ushort2frac(tmp[i]);
+ for (; i < n + 4; i++)
+ out[i] = 0;
+
+ } else {
+ /* If no profile given, assume CMYK */
+ out[0] = c;
+ out[1] = m;
+ out[2] = y;
+ out[3] = k;
+ for(i = 0; i < n; i++) /* Clear spot colors */
+ out[4 + i] = 0;
+ }
+}
+
+static void
+gray_cs_to_spotn_cm(gx_device * dev, frac gray, frac out[])
+{
+/* TO_DO_DEVICEN This routine needs to include the effects of the SeparationOrder array */
+
+ cmyk_cs_to_spotn_cm(dev, 0, 0, 0, frac_1 - gray, out);
+}
+
+static void
+rgb_cs_to_spotn_cm(gx_device * dev, const gs_imager_state *pis,
+ frac r, frac g, frac b, frac out[])
+{
+/* TO_DO_DEVICEN This routine needs to include the effects of the SeparationOrder array */
+ xcf_device *xdev = (xcf_device *)dev;
+ int n = xdev->separation_names.num_names;
+ gcmmhlink_t link = xdev->rgb_icc_link;
+ int i;
+
+ if (link != NULL) {
+ unsigned short in[3];
+ unsigned short tmp[MAX_CHAN];
+ int outn = xdev->rgb_profile->num_comps_out;
+
+ in[0] = frac2ushort(r);
+ in[1] = frac2ushort(g);
+ in[2] = frac2ushort(b);
+
+ gscms_transform_color(dev, link, &(in[0]), &(tmp[0]), 2);
+
+ for (i = 0; i < outn; i++)
+ out[i] = ushort2frac(tmp[i]);
+ for (; i < n + 4; i++)
+ out[i] = 0;
+ } else {
+ frac cmyk[4];
+
+ color_rgb_to_cmyk(r, g, b, pis, cmyk, dev->memory);
+ cmyk_cs_to_spotn_cm(dev, cmyk[0], cmyk[1], cmyk[2], cmyk[3],
+ out);
+ }
+}
+
+static const gx_cm_color_map_procs spotRGB_procs = {
+ gray_cs_to_spotrgb_cm, rgb_cs_to_spotrgb_cm, cmyk_cs_to_spotrgb_cm
+};
+
+static const gx_cm_color_map_procs spotCMYK_procs = {
+ gray_cs_to_spotcmyk_cm, rgb_cs_to_spotcmyk_cm, cmyk_cs_to_spotcmyk_cm
+};
+
+static const gx_cm_color_map_procs spotN_procs = {
+ gray_cs_to_spotn_cm, rgb_cs_to_spotn_cm, cmyk_cs_to_spotn_cm
+};
+
+/*
+ * These are the handlers for returning the list of color space
+ * to color model conversion routines.
+ */
+static const gx_cm_color_map_procs *
+get_spotrgb_color_mapping_procs(const gx_device * dev)
+{
+ return &spotRGB_procs;
+}
+
+#if 0
+static const gx_cm_color_map_procs *
+get_spotcmyk_color_mapping_procs(const gx_device * dev)
+{
+ return &spotCMYK_procs;
+}
+#endif
+
+static const gx_cm_color_map_procs *
+get_xcf_color_mapping_procs(const gx_device * dev)
+{
+ const xcf_device *xdev = (const xcf_device *)dev;
+
+ if (xdev->color_model == XCF_DEVICE_RGB)
+ return &spotRGB_procs;
+ else if (xdev->color_model == XCF_DEVICE_CMYK)
+ return &spotCMYK_procs;
+ else if (xdev->color_model == XCF_DEVICE_N)
+ return &spotN_procs;
+ else
+ return NULL;
+}
+
+/*
+ * Encode a list of colorant values into a gx_color_index_value.
+ */
+static gx_color_index
+xcf_encode_color(gx_device *dev, const gx_color_value colors[])
+{
+ int bpc = ((xcf_device *)dev)->bitspercomponent;
+ gx_color_index color = 0;
+ int i = 0;
+ int ncomp = dev->color_info.num_components;
+ COLROUND_VARS;
+
+ COLROUND_SETUP(bpc);
+ for (; i<ncomp; i++) {
+ color <<= bpc;
+ color |= COLROUND_ROUND(colors[i]);
+ }
+ return (color == gx_no_color_index ? color ^ 1 : color);
+}
+
+/*
+ * Decode a gx_color_index value back to a list of colorant values.
+ */
+static int
+xcf_decode_color(gx_device * dev, gx_color_index color, gx_color_value * out)
+{
+ int bpc = ((xcf_device *)dev)->bitspercomponent;
+ int mask = (1 << bpc) - 1;
+ int i = 0;
+ int ncomp = dev->color_info.num_components;
+ COLDUP_VARS;
+
+ COLDUP_SETUP(bpc);
+ for (; i<ncomp; i++) {
+ out[ncomp - i - 1] = COLDUP_DUP(color & mask);
+ color >>= bpc;
+ }
+ return 0;
+}
+
+/*
+ * Convert a gx_color_index to RGB.
+ */
+static int
+xcf_map_color_rgb(gx_device *dev, gx_color_index color, gx_color_value rgb[3])
+{
+ xcf_device *xdev = (xcf_device *)dev;
+
+ if (xdev->color_model == XCF_DEVICE_RGB)
+ return xcf_decode_color(dev, color, rgb);
+ /* TODO: return reasonable values. */
+ rgb[0] = 0;
+ rgb[1] = 0;
+ rgb[2] = 0;
+ return 0;
+}
+
+/*
+ * This routine will extract a specified set of bits from a buffer and pack
+ * them into a given buffer.
+ *
+ * Parameters:
+ * source - The source of the data
+ * dest - The destination for the data
+ * depth - The size of the bits per pixel - must be a multiple of 8
+ * first_bit - The location of the first data bit (LSB).
+ * bit_width - The number of bits to be extracted.
+ * npixel - The number of pixels.
+ *
+ * Returns:
+ * Length of the output line (in bytes)
+ * Data in dest.
+ */
+#if 0
+static int
+repack_data(byte * source, byte * dest, int depth, int first_bit,
+ int bit_width, int npixel)
+{
+ int in_nbyte = depth >> 3; /* Number of bytes per input pixel */
+ int out_nbyte = bit_width >> 3; /* Number of bytes per output pixel */
+ gx_color_index mask = 1;
+ gx_color_index data;
+ int i, j, length = 0;
+ int in_byte_loc = 0, out_byte_loc = 0;
+ byte temp;
+ byte * out = dest;
+ int max_bit_byte = 8 - bit_width;
+
+ mask = (mask << bit_width) - 1;
+ for (i=0; i<npixel; i++) {
+ /* Get the pixel data */
+ if (!in_nbyte) { /* Multiple pixels per byte */
+ data = *source;
+ data >>= in_byte_loc;
+ in_byte_loc += depth;
+ if (in_byte_loc >= 8) { /* If finished with byte */
+ in_byte_loc = 0;
+ source++;
+ }
+ }
+ else { /* One or more bytes per pixel */
+ data = *source++;
+ for (j=1; j<in_nbyte; j++)
+ data = (data << 8) + *source++;
+ }
+ data >>= first_bit;
+ data &= mask;
+
+ /* Put the output data */
+ if (!out_nbyte) { /* Multiple pixels per byte */
+ temp = *out & ~(mask << out_byte_loc);
+ *out = temp | (data << out_byte_loc);
+ out_byte_loc += bit_width;
+ if (out_byte_loc > max_bit_byte) { /* If finished with byte */
+ out_byte_loc = 0;
+ out++;
+ }
+ }
+ else { /* One or more bytes per pixel */
+ *out++ = data >> ((out_nbyte - 1) * 8);
+ for (j=1; j<out_nbyte; j++) {
+ *out++ = data >> ((out_nbyte - 1 - j) * 8);
+ }
+ }
+ }
+ /* Return the number of bytes in the destination buffer. */
+ length = out - dest;
+ if (out_byte_loc) /* If partially filled last byte */
+ length++;
+ return length;
+}
+#endif /* 0 */
+
+static int
+xcf_open_profile(const char *profile_out_fn, cmm_profile_t *icc_profile, gcmmhlink_t icc_link, gs_memory_t *memory)
+{
+
+ gsicc_rendering_param_t rendering_params;
+
+ icc_profile = gsicc_get_profile_handle_file(profile_out_fn,
+ strlen(profile_out_fn), memory);
+
+ if (icc_profile == NULL)
+ return gs_throw(-1, "Could not create profile for xcf device");
+
+ /* Set up the rendering parameters */
+
+ rendering_params.black_point_comp = gsBPNOTSPECIFIED;
+ rendering_params.graphics_type_tag = GS_UNKNOWN_TAG; /* Already rendered */
+ rendering_params.rendering_intent = gsPERCEPTUAL;
+
+ /* Call with a NULL destination profile since we are using a device link profile here */
+ icc_link = gscms_get_link(icc_profile,
+ NULL, &rendering_params, 0, memory);
+
+ if (icc_link == NULL)
+ return gs_throw(-1, "Could not create link handle for xdev device");
+
+ return(0);
+
+}
+
+static int
+xcf_open_profiles(xcf_device *xdev)
+{
+ int code = 0;
+
+ if (xdev->output_icc_link == NULL && xdev->profile_out_fn[0]) {
+
+ code = xcf_open_profile(xdev->profile_out_fn, xdev->output_profile,
+ xdev->output_icc_link, xdev->memory);
+
+ }
+
+ if (code >= 0 && xdev->rgb_icc_link == NULL && xdev->profile_rgb_fn[0]) {
+
+ code = xcf_open_profile(xdev->profile_rgb_fn, xdev->rgb_profile,
+ xdev->rgb_icc_link, xdev->memory);
+
+ }
+
+ if (code >= 0 && xdev->cmyk_icc_link == NULL && xdev->profile_cmyk_fn[0]) {
+
+ code = xcf_open_profile(xdev->profile_cmyk_fn, xdev->cmyk_profile,
+ xdev->cmyk_icc_link, xdev->memory);
+
+ }
+
+ return code;
+}
+
+#define set_param_array(a, d, s)\
+ (a.data = d, a.size = s, a.persistent = false);
+
+/* Get parameters. We provide a default CRD. */
+static int
+xcf_get_params(gx_device * pdev, gs_param_list * plist)
+{
+ xcf_device *xdev = (xcf_device *)pdev;
+ int code;
+ bool seprs = false;
+ gs_param_string_array scna;
+ gs_param_string pos;
+ gs_param_string prgbs;
+ gs_param_string pcmyks;
+
+ set_param_array(scna, NULL, 0);
+
+ if ( (code = gdev_prn_get_params(pdev, plist)) < 0 ||
+ (code = sample_device_crd_get_params(pdev, plist, "CRDDefault")) < 0 ||
+ (code = param_write_name_array(plist, "SeparationColorNames", &scna)) < 0 ||
+ (code = param_write_bool(plist, "Separations", &seprs)) < 0)
+ return code;
+
+ pos.data = (const byte *)xdev->profile_out_fn,
+ pos.size = strlen(xdev->profile_out_fn),
+ pos.persistent = false;
+ code = param_write_string(plist, "ProfileOut", &pos);
+ if (code < 0)
+ return code;
+
+ prgbs.data = (const byte *)xdev->profile_rgb_fn,
+ prgbs.size = strlen(xdev->profile_rgb_fn),
+ prgbs.persistent = false;
+ code = param_write_string(plist, "ProfileRgb", &prgbs);
+
+ pcmyks.data = (const byte *)xdev->profile_cmyk_fn,
+ pcmyks.size = strlen(xdev->profile_cmyk_fn),
+ pcmyks.persistent = false;
+ code = param_write_string(plist, "ProfileCmyk", &prgbs);
+
+ return code;
+}
+#undef set_param_array
+
+#define compare_color_names(name, name_size, str, str_size) \
+ (name_size == str_size && \
+ (strncmp((const char *)name, (const char *)str, name_size) == 0))
+
+/*
+ * This routine will check if a name matches any item in a list of process model
+ * color component names.
+ */
+static bool
+check_process_color_names(const fixed_colorant_names_list * pcomp_list,
+ const gs_param_string * pstring)
+{
+ if (pcomp_list) {
+ const fixed_colorant_name * plist = *pcomp_list;
+ uint size = pstring->size;
+
+ while( *plist) {
+ if (compare_color_names(*plist, strlen(*plist), pstring->data, size)) {
+ return true;
+ }
+ plist++;
+ }
+ }
+ return false;
+}
+
+/*
+ * This utility routine calculates the number of bits required to store
+ * color information. In general the values are rounded up to an even
+ * byte boundary except those cases in which mulitple pixels can evenly
+ * into a single byte.
+ *
+ * The parameter are:
+ * ncomp - The number of components (colorants) for the device. Valid
+ * values are 1 to GX_DEVICE_COLOR_MAX_COMPONENTS
+ * bpc - The number of bits per component. Valid values are 1, 2, 4, 5,
+ * and 8.
+ * Input values are not tested for validity.
+ */
+static int
+bpc_to_depth(int ncomp, int bpc)
+{
+ static const byte depths[4][8] = {
+ {1, 2, 0, 4, 8, 0, 0, 8},
+ {2, 4, 0, 8, 16, 0, 0, 16},
+ {4, 8, 0, 16, 16, 0, 0, 24},
+ {4, 8, 0, 16, 32, 0, 0, 32}
+ };
+
+ if (ncomp <=4 && bpc <= 8)
+ return depths[ncomp -1][bpc-1];
+ else
+ return (ncomp * bpc + 7) & ~7;
+}
+
+#define BEGIN_ARRAY_PARAM(pread, pname, pa, psize, e)\
+ BEGIN\
+ switch (code = pread(plist, (param_name = pname), &(pa))) {\
+ case 0:\
+ if ((pa).size != psize) {\
+ ecode = gs_note_error(gs_error_rangecheck);\
+ (pa).data = 0; /* mark as not filled */\
+ } else
+#define END_ARRAY_PARAM(pa, e)\
+ goto e;\
+ default:\
+ ecode = code;\
+e: param_signal_error(plist, param_name, ecode);\
+ case 1:\
+ (pa).data = 0; /* mark as not filled */\
+ }\
+ END
+
+static int
+xcf_param_read_fn(gs_param_list *plist, const char *name,
+ gs_param_string *pstr, int max_len)
+{
+ int code = param_read_string(plist, name, pstr);
+
+ if (code == 0) {
+ if (pstr->size >= max_len)
+ param_signal_error(plist, name, code = gs_error_rangecheck);
+ } else {
+ pstr->data = 0;
+ }
+ return code;
+}
+
+/* Compare a C string and a gs_param_string. */
+static bool
+param_string_eq(const gs_param_string *pcs, const char *str)
+{
+ return (strlen(str) == pcs->size &&
+ !strncmp(str, (const char *)pcs->data, pcs->size));
+}
+
+static int
+xcf_set_color_model(xcf_device *xdev, xcf_color_model color_model)
+{
+ xdev->color_model = color_model;
+ if (color_model == XCF_DEVICE_GRAY) {
+ xdev->std_colorant_names = &DeviceGrayComponents;
+ xdev->num_std_colorant_names = 1;
+ xdev->color_info.cm_name = "DeviceGray";
+ xdev->color_info.polarity = GX_CINFO_POLARITY_ADDITIVE;
+ } else if (color_model == XCF_DEVICE_RGB) {
+ xdev->std_colorant_names = &DeviceRGBComponents;
+ xdev->num_std_colorant_names = 3;
+ xdev->color_info.cm_name = "DeviceRGB";
+ xdev->color_info.polarity = GX_CINFO_POLARITY_ADDITIVE;
+ } else if (color_model == XCF_DEVICE_CMYK) {
+ xdev->std_colorant_names = &DeviceCMYKComponents;
+ xdev->num_std_colorant_names = 4;
+ xdev->color_info.cm_name = "DeviceCMYK";
+ xdev->color_info.polarity = GX_CINFO_POLARITY_SUBTRACTIVE;
+ } else if (color_model == XCF_DEVICE_N) {
+ xdev->std_colorant_names = &DeviceCMYKComponents;
+ xdev->num_std_colorant_names = 4;
+ xdev->color_info.cm_name = "DeviceN";
+ xdev->color_info.polarity = GX_CINFO_POLARITY_SUBTRACTIVE;
+ } else {
+ return -1;
+ }
+
+ return 0;
+}
+
+/*
+ * Close device and clean up ICC structures.
+ */
+
+static int
+xcf_prn_close(gx_device *dev)
+{
+ xcf_device * const xdev = (xcf_device *) dev;
+
+ if (xdev->cmyk_icc_link != NULL) {
+ gscms_release_link(xdev->cmyk_icc_link);
+ rc_decrement(xdev->cmyk_profile, "xcf_prn_close");
+ }
+
+ if (xdev->rgb_icc_link != NULL) {
+ gscms_release_link(xdev->rgb_icc_link);
+ rc_decrement(xdev->rgb_profile, "xcf_prn_close");
+ }
+
+ if (xdev->output_icc_link != NULL) {
+ gscms_release_link(xdev->output_icc_link);
+ rc_decrement(xdev->output_profile, "xcf_prn_close");
+ }
+
+ return gdev_prn_close(dev);
+}
+
+/* Set parameters. We allow setting the number of bits per component. */
+static int
+xcf_put_params(gx_device * pdev, gs_param_list * plist)
+{
+ xcf_device * const pdevn = (xcf_device *) pdev;
+ gx_device_color_info save_info;
+ gs_param_name param_name;
+ int npcmcolors;
+ int num_spot = pdevn->separation_names.num_names;
+ int ecode = 0;
+ int code;
+ gs_param_string_array scna;
+ gs_param_string po;
+ gs_param_string prgb;
+ gs_param_string pcmyk;
+ gs_param_string pcm;
+ xcf_color_model color_model = pdevn->color_model;
+
+ BEGIN_ARRAY_PARAM(param_read_name_array, "SeparationColorNames", scna, scna.size, scne) {
+ break;
+ } END_ARRAY_PARAM(scna, scne);
+
+ if (code >= 0)
+ code = xcf_param_read_fn(plist, "ProfileOut", &po,
+ sizeof(pdevn->profile_out_fn));
+ if (code >= 0)
+ code = xcf_param_read_fn(plist, "ProfileRgb", &prgb,
+ sizeof(pdevn->profile_rgb_fn));
+ if (code >= 0)
+ code = xcf_param_read_fn(plist, "ProfileCmyk", &pcmyk,
+ sizeof(pdevn->profile_cmyk_fn));
+
+ if (code >= 0)
+ code = param_read_name(plist, "ProcessColorModel", &pcm);
+ if (code == 0) {
+ if (param_string_eq (&pcm, "DeviceGray"))
+ color_model = XCF_DEVICE_GRAY;
+ else if (param_string_eq (&pcm, "DeviceRGB"))
+ color_model = XCF_DEVICE_RGB;
+ else if (param_string_eq (&pcm, "DeviceCMYK"))
+ color_model = XCF_DEVICE_CMYK;
+ else if (param_string_eq (&pcm, "DeviceN"))
+ color_model = XCF_DEVICE_N;
+ else {
+ param_signal_error(plist, "ProcessColorModel",
+ code = gs_error_rangecheck);
+ }
+ }
+ if (code < 0)
+ ecode = code;
+
+ /*
+ * Save the color_info in case gdev_prn_put_params fails, and for
+ * comparison.
+ */
+ save_info = pdevn->color_info;
+ ecode = xcf_set_color_model(pdevn, color_model);
+ if (ecode == 0)
+ ecode = gdev_prn_put_params(pdev, plist);
+ if (ecode < 0) {
+ pdevn->color_info = save_info;
+ return ecode;
+ }
+
+ /* Separations are only valid with a subrtractive color model */
+ if (pdev->color_info.polarity == GX_CINFO_POLARITY_SUBTRACTIVE) {
+ /*
+ * Process the separation color names. Remove any names that already
+ * match the process color model colorant names for the device.
+ */
+ if (scna.data != 0) {
+ int i;
+ int num_names = scna.size;
+ const fixed_colorant_names_list * pcomp_names =
+ ((xcf_device *)pdev)->std_colorant_names;
+
+ for (i = num_spot = 0; i < num_names; i++) {
+ if (!check_process_color_names(pcomp_names, &scna.data[i]))
+ pdevn->separation_names.names[num_spot++] = &scna.data[i];
+ }
+ pdevn->separation_names.num_names = num_spot;
+ if (pdevn->is_open)
+ gs_closedevice(pdev);
+ }
+ npcmcolors = pdevn->num_std_colorant_names;
+ pdevn->color_info.num_components = npcmcolors + num_spot;
+ /*
+ * The DeviceN device can have zero components if nothing has been
+ * specified. This causes some problems so force at least one
+ * component until something is specified.
+ */
+ if (!pdevn->color_info.num_components)
+ pdevn->color_info.num_components = 1;
+ pdevn->color_info.depth = bpc_to_depth(pdevn->color_info.num_components,
+ pdevn->bitspercomponent);
+ if (pdevn->color_info.depth != save_info.depth) {
+ gs_closedevice(pdev);
+ }
+ }
+
+ if (po.data != 0) {
+ memcpy(pdevn->profile_out_fn, po.data, po.size);
+ pdevn->profile_out_fn[po.size] = 0;
+ }
+ if (prgb.data != 0) {
+ memcpy(pdevn->profile_rgb_fn, prgb.data, prgb.size);
+ pdevn->profile_rgb_fn[prgb.size] = 0;
+ }
+ if (pcmyk.data != 0) {
+ memcpy(pdevn->profile_cmyk_fn, pcmyk.data, pcmyk.size);
+ pdevn->profile_cmyk_fn[pcmyk.size] = 0;
+ }
+ code = xcf_open_profiles(pdevn);
+
+ return code;
+}
+
+/*
+ * This routine will check to see if the color component name match those
+ * that are available amoung the current device's color components.
+ *
+ * Parameters:
+ * dev - pointer to device data structure.
+ * pname - pointer to name (zero termination not required)
+ * nlength - length of the name
+ *
+ * This routine returns a positive value (0 to n) which is the device colorant
+ * number if the name is found. It returns a negative value if not found.
+ */
+static int
+xcf_get_color_comp_index(gx_device * dev, const char * pname, int name_size,
+ int component_type)
+{
+/* TO_DO_DEVICEN This routine needs to include the effects of the SeparationOrder array */
+ const fixed_colorant_names_list * list = ((const xcf_device *)dev)->std_colorant_names;
+ const fixed_colorant_name * pcolor = *list;
+ int color_component_number = 0;
+ int i;
+
+ /* Check if the component is in the implied list. */
+ if (pcolor) {
+ while( *pcolor) {
+ if (compare_color_names(pname, name_size, *pcolor, strlen(*pcolor)))
+ return color_component_number;
+ pcolor++;
+ color_component_number++;
+ }
+ }
+
+ /* Check if the component is in the separation names list. */
+ {
+ const gs_separation_names * separations = &((const xcf_device *)dev)->separation_names;
+ int num_spot = separations->num_names;
+
+ for (i=0; i<num_spot; i++) {
+ if (compare_color_names((const char *)separations->names[i]->data,
+ separations->names[i]->size, pname, name_size)) {
+ return color_component_number;
+ }
+ color_component_number++;
+ }
+ }
+
+ return -1;
+}
+
+/* ------ Private definitions ------ */
+
+/* All two-byte quantities are stored MSB-first! */
+#if arch_is_big_endian
+# define assign_u16(a,v) a = (v)
+# define assign_u32(a,v) a = (v)
+#else
+# define assign_u16(a,v) a = ((v) >> 8) + ((v) << 8)
+# define assign_u32(a,v) a = (((v) >> 24) & 0xff) + (((v) >> 8) & 0xff00) + (((v) & 0xff00) << 8) + (((v) & 0xff) << 24)
+#endif
+
+typedef struct {
+ FILE *f;
+ int offset;
+
+ int width;
+ int height;
+ int base_bytes_pp; /* almost always 3 (rgb) */
+ int n_extra_channels;
+
+ int n_tiles_x;
+ int n_tiles_y;
+ int n_tiles;
+ int n_levels;
+
+ /* byte offset of image data */
+ int image_data_off;
+} xcf_write_ctx;
+
+#define TILE_WIDTH 64
+#define TILE_HEIGHT 64
+
+static int
+xcf_calc_levels(int size, int tile_size)
+{
+ int levels = 1;
+ while (size > tile_size) {
+ size >>= 1;
+ levels++;
+ }
+ return levels;
+}
+
+static int
+xcf_setup_tiles(xcf_write_ctx *xc, xcf_device *dev)
+{
+ xc->base_bytes_pp = 3;
+ xc->n_extra_channels = dev->separation_names.num_names;
+ xc->width = dev->width;
+ xc->height = dev->height;
+ xc->n_tiles_x = (dev->width + TILE_WIDTH - 1) / TILE_WIDTH;
+ xc->n_tiles_y = (dev->height + TILE_HEIGHT - 1) / TILE_HEIGHT;
+ xc->n_tiles = xc->n_tiles_x * xc->n_tiles_y;
+ xc->n_levels = max(xcf_calc_levels(dev->width, TILE_WIDTH),
+ xcf_calc_levels(dev->height, TILE_HEIGHT));
+
+ return 0;
+}
+
+/* Return value: Size of tile in pixels. */
+static int
+xcf_tile_sizeof(xcf_write_ctx *xc, int tile_idx)
+{
+ int tile_i = tile_idx % xc->n_tiles_x;
+ int tile_j = tile_idx / xc->n_tiles_x;
+ int tile_size_x = min(TILE_WIDTH, xc->width - tile_i * TILE_WIDTH);
+ int tile_size_y = min(TILE_HEIGHT, xc->height - tile_j * TILE_HEIGHT);
+ return tile_size_x * tile_size_y;
+}
+
+static int
+xcf_write(xcf_write_ctx *xc, const byte *buf, int size) {
+ int code;
+
+ code = fwrite(buf, 1, size, xc->f);
+ if (code < 0)
+ return code;
+ xc->offset += code;
+ return 0;
+}
+
+static int
+xcf_write_32(xcf_write_ctx *xc, bits32 v)
+{
+ bits32 buf;
+
+ assign_u32(buf, v);
+ return xcf_write(xc, (byte *)&buf, 4);
+}
+
+static int
+xcf_write_image_props(xcf_write_ctx *xc)
+{
+ int code = 0;
+
+ xcf_write_32(xc, 0);
+ xcf_write_32(xc, 0);
+
+ return code;
+}
+
+/**
+ * Return value: Number of bytes needed to write layer.
+ **/
+static int
+xcf_base_size(xcf_write_ctx *xc, const char *layer_name)
+{
+ int bytes_pp = xc->base_bytes_pp + xc->n_extra_channels;
+
+ return 17 + strlen (layer_name) + /* header and name */
+ 8 + /* layer props */
+ 12 + xc->n_levels * 16 + /* layer tile hierarchy */
+ 12 + xc->n_tiles * 4 + /* tile offsets */
+ xc->width * xc->height * bytes_pp; /* image data */
+}
+
+static int
+xcf_channel_size(xcf_write_ctx *xc, int name_size)
+{
+ return 17 + name_size + /* header and name */
+ 8 + /* channel props */
+ 4 + xc->n_levels * 16 + /* channel tile hiearchy */
+ 12 + xc->n_tiles * 4; /* tile offsets */
+}
+
+static int
+xcf_write_header(xcf_write_ctx *xc, xcf_device *pdev)
+{
+ int code = 0;
+ const char *layer_name = "Background";
+ int level;
+ int tile_offset;
+ int tile_idx;
+ int n_extra_channels = xc->n_extra_channels;
+ int bytes_pp = xc->base_bytes_pp + n_extra_channels;
+ int channel_idx;
+
+ xcf_write(xc, (const byte *)"gimp xcf file", 14);
+ xcf_write_32(xc, xc->width);
+ xcf_write_32(xc, xc->height);
+ xcf_write_32(xc, 0);
+
+ xcf_write_image_props(xc);
+
+ /* layer offsets */
+ xcf_write_32(xc, xc->offset + 12 + 4 * n_extra_channels);
+ xcf_write_32(xc, 0);
+
+ /* channel offsets */
+ tile_offset = xc->offset + 4 + 4 * n_extra_channels +
+ xcf_base_size(xc, layer_name);
+ for (channel_idx = 0; channel_idx < n_extra_channels; channel_idx++) {
+ const gs_param_string *separation_name =
+ pdev->separation_names.names[channel_idx];
+ dmlprintf1(pdev->memory, "tile offset: %d\n", tile_offset);
+ xcf_write_32(xc, tile_offset);
+ tile_offset += xcf_channel_size(xc, separation_name->size);
+ }
+ xcf_write_32(xc, 0);
+
+ /* layer */
+ xcf_write_32(xc, xc->width);
+ xcf_write_32(xc, xc->height);
+ xcf_write_32(xc, 0);
+ xcf_write_32(xc, strlen(layer_name) + 1);
+ xcf_write(xc, (const byte *)layer_name, strlen(layer_name) + 1);
+
+ /* layer props */
+ xcf_write_32(xc, 0);
+ xcf_write_32(xc, 0);
+
+ /* layer tile hierarchy */
+ xcf_write_32(xc, xc->offset + 8);
+ xcf_write_32(xc, 0);
+
+ xcf_write_32(xc, xc->width);
+ xcf_write_32(xc, xc->height);
+ xcf_write_32(xc, xc->base_bytes_pp);
+ xcf_write_32(xc, xc->offset + (1 + xc->n_levels) * 4);
+ tile_offset = xc->offset + xc->width * xc->height * bytes_pp +
+ xc->n_tiles * 4 + 12;
+ for (level = 1; level < xc->n_levels; level++) {
+ xcf_write_32(xc, tile_offset);
+ tile_offset += 12;
+ }
+ xcf_write_32(xc, 0);
+
+ /* layer tile offsets */
+ xcf_write_32(xc, xc->width);
+ xcf_write_32(xc, xc->height);
+ tile_offset = xc->offset + (xc->n_tiles + 1) * 4;
+ for (tile_idx = 0; tile_idx < xc->n_tiles; tile_idx++) {
+ xcf_write_32(xc, tile_offset);
+ tile_offset += xcf_tile_sizeof(xc, tile_idx) * bytes_pp;
+ }
+ xcf_write_32(xc, 0);
+
+ xc->image_data_off = xc->offset;
+
+ return code;
+}
+
+static void
+xcf_shuffle_to_tile(xcf_write_ctx *xc, byte **tile_data, const byte *row,
+ int y)
+{
+ int tile_j = y / TILE_HEIGHT;
+ int yrem = y % TILE_HEIGHT;
+ int tile_i;
+ int base_bytes_pp = xc->base_bytes_pp;
+ int n_extra_channels = xc->n_extra_channels;
+ int row_idx = 0;
+
+ for (tile_i = 0; tile_i < xc->n_tiles_x; tile_i++) {
+ int x;
+ int tile_width = min(TILE_WIDTH, xc->width - tile_i * TILE_WIDTH);
+ int tile_height = min(TILE_HEIGHT, xc->height - tile_j * TILE_HEIGHT);
+ byte *base_ptr = tile_data[tile_i] +
+ yrem * tile_width * base_bytes_pp;
+ int extra_stride = tile_width * tile_height;
+ byte *extra_ptr = tile_data[tile_i] + extra_stride * base_bytes_pp +
+ yrem * tile_width;
+
+ int base_idx = 0;
+
+ for (x = 0; x < tile_width; x++) {
+ int plane_idx;
+ for (plane_idx = 0; plane_idx < base_bytes_pp; plane_idx++)
+ base_ptr[base_idx++] = row[row_idx++];
+ for (plane_idx = 0; plane_idx < n_extra_channels; plane_idx++)
+ extra_ptr[plane_idx * extra_stride + x] = 255 ^ row[row_idx++];
+ }
+ }
+}
+
+static void
+xcf_icc_to_tile(gx_device_printer *pdev, xcf_write_ctx *xc, byte **tile_data, const byte *row,
+ int y, gcmmhlink_t link)
+{
+ int tile_j = y / TILE_HEIGHT;
+ int yrem = y % TILE_HEIGHT;
+ int tile_i;
+ int base_bytes_pp = xc->base_bytes_pp;
+ int n_extra_channels = xc->n_extra_channels;
+ int row_idx = 0;
+
+ for (tile_i = 0; tile_i < xc->n_tiles_x; tile_i++) {
+ int x;
+ int tile_width = min(TILE_WIDTH, xc->width - tile_i * TILE_WIDTH);
+ int tile_height = min(TILE_HEIGHT, xc->height - tile_j * TILE_HEIGHT);
+ byte *base_ptr = tile_data[tile_i] +
+ yrem * tile_width * base_bytes_pp;
+ int extra_stride = tile_width * tile_height;
+ byte *extra_ptr = tile_data[tile_i] + extra_stride * base_bytes_pp +
+ yrem * tile_width;
+
+ int base_idx = 0;
+
+ for (x = 0; x < tile_width; x++) {
+
+ int plane_idx;
+
+ /* This loop could be optimized. I don't quite
+ understand what is going on in the loop
+ with the 255^row[row_idx++] operation */
+
+ gscms_transform_color((gx_device*) pdev, link,
+ (void *) (&(row[row_idx])),
+ &(base_ptr[base_idx]), 1);
+
+ for (plane_idx = 0; plane_idx < n_extra_channels; plane_idx++)
+ extra_ptr[plane_idx * extra_stride + x] = 255 ^ row[row_idx++];
+ }
+ }
+}
+
+static int
+xcf_write_image_data(xcf_write_ctx *xc, gx_device_printer *pdev)
+{
+ int code = 0;
+ int raster = gdev_prn_raster(pdev);
+ int tile_i, tile_j;
+ byte **tile_data;
+ byte *line;
+ int base_bytes_pp = xc->base_bytes_pp;
+ int n_extra_channels = xc->n_extra_channels;
+ int bytes_pp = base_bytes_pp + n_extra_channels;
+ int chan_idx;
+ xcf_device *xdev = (xcf_device *)pdev;
+ gcmmhlink_t link = xdev->output_icc_link;
+
+ line = gs_alloc_bytes(pdev->memory, raster, "xcf_write_image_data");
+ tile_data = (byte **)gs_alloc_bytes(pdev->memory,
+ xc->n_tiles_x * sizeof(byte *),
+ "xcf_write_image_data");
+ for (tile_i = 0; tile_i < xc->n_tiles_x; tile_i++) {
+ int tile_bytes = xcf_tile_sizeof(xc, tile_i) * bytes_pp;
+
+ tile_data[tile_i] = gs_alloc_bytes(pdev->memory, tile_bytes,
+ "xcf_write_image_data");
+ }
+ for (tile_j = 0; tile_j < xc->n_tiles_y; tile_j++) {
+ int y0, y1;
+ int y;
+ byte *row;
+
+ y0 = tile_j * TILE_HEIGHT;
+ y1 = min(xc->height, y0 + TILE_HEIGHT);
+ for (y = y0; y < y1; y++) {
+ code = gdev_prn_get_bits(pdev, y, line, &row);
+ if (link == NULL)
+ xcf_shuffle_to_tile(xc, tile_data, row, y);
+ else
+ xcf_icc_to_tile(pdev, xc, tile_data, row, y, link);
+ }
+ for (tile_i = 0; tile_i < xc->n_tiles_x; tile_i++) {
+ int tile_idx = tile_j * xc->n_tiles_x + tile_i;
+ int tile_size = xcf_tile_sizeof(xc, tile_idx);
+ int base_size = tile_size * base_bytes_pp;
+
+ xcf_write(xc, tile_data[tile_i], base_size);
+ for (chan_idx = 0; chan_idx < n_extra_channels; chan_idx++) {
+ xcf_write(xc, tile_data[tile_i] + base_size +
+ tile_size * chan_idx, tile_size);
+ }
+ }
+ }
+
+ for (tile_i = 0; tile_i < xc->n_tiles_x; tile_i++) {
+ gs_free_object(pdev->memory, tile_data[tile_i],
+ "xcf_write_image_data");
+ }
+ gs_free_object(pdev->memory, tile_data, "xcf_write_image_data");
+ gs_free_object(pdev->memory, line, "xcf_write_image_data");
+ return code;
+}
+
+static int
+xcf_write_fake_hierarchy(xcf_write_ctx *xc)
+{
+ int widthf = xc->width, heightf = xc->height;
+ int i;
+
+ for (i = 1; i < xc->n_levels; i++) {
+ widthf >>= 1;
+ heightf >>= 1;
+ xcf_write_32(xc, widthf);
+ xcf_write_32(xc, heightf);
+ xcf_write_32(xc, 0);
+ }
+ return 0;
+}
+
+static int
+xcf_write_footer(xcf_write_ctx *xc, xcf_device *pdev)
+{
+ int code = 0;
+ int base_bytes_pp = xc->base_bytes_pp;
+ int n_extra_channels = xc->n_extra_channels;
+ int bytes_pp = base_bytes_pp + n_extra_channels;
+ int chan_idx;
+
+ xcf_write_fake_hierarchy(xc);
+
+ for (chan_idx = 0; chan_idx < xc->n_extra_channels; chan_idx++) {
+ const gs_param_string *separation_name =
+ pdev->separation_names.names[chan_idx];
+ byte nullbyte[] = { 0 };
+ int level;
+ int offset;
+ int tile_idx;
+
+ dmlprintf2(pdev->memory, "actual tile offset: %d %d\n", xc->offset, (int)arch_sizeof_color_index);
+ xcf_write_32(xc, xc->width);
+ xcf_write_32(xc, xc->height);
+ xcf_write_32(xc, separation_name->size + 1);
+ xcf_write(xc, separation_name->data, separation_name->size);
+ xcf_write(xc, nullbyte, 1);
+
+ /* channel props */
+ xcf_write_32(xc, 0);
+ xcf_write_32(xc, 0);
+
+ /* channel tile hierarchy */
+ xcf_write_32(xc, xc->offset + 4);
+
+ xcf_write_32(xc, xc->width);
+ xcf_write_32(xc, xc->height);
+ xcf_write_32(xc, 1);
+ xcf_write_32(xc, xc->offset + xc->n_levels * 16 - 8);
+ offset = xc->offset + xc->n_levels * 4;
+ for (level = 1; level < xc->n_levels; level++) {
+ xcf_write_32(xc, offset);
+ offset += 12;
+ }
+ xcf_write_32(xc, 0);
+ xcf_write_fake_hierarchy(xc);
+
+ /* channel tile offsets */
+ xcf_write_32(xc, xc->width);
+ xcf_write_32(xc, xc->height);
+ offset = xc->image_data_off;
+ for (tile_idx = 0; tile_idx < xc->n_tiles; tile_idx++) {
+ int tile_size = xcf_tile_sizeof(xc, tile_idx);
+
+ xcf_write_32(xc, offset + (base_bytes_pp + chan_idx) * tile_size);
+ offset += bytes_pp * tile_size;
+ }
+ xcf_write_32(xc, 0);
+
+ }
+ return code;
+}
+
+static int
+xcf_print_page(gx_device_printer *pdev, FILE *file)
+{
+ xcf_write_ctx xc;
+
+ xc.f = file;
+ xc.offset = 0;
+
+ xcf_setup_tiles(&xc, (xcf_device *)pdev);
+ xcf_write_header(&xc, (xcf_device *)pdev);
+ xcf_write_image_data(&xc, pdev);
+ xcf_write_footer(&xc, (xcf_device *)pdev);
+
+ return 0;
+}
diff --git a/devices/gdevxcmp.c b/devices/gdevxcmp.c
new file mode 100644
index 000000000..d91e78dd1
--- /dev/null
+++ b/devices/gdevxcmp.c
@@ -0,0 +1,897 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* X Windows color mapping */
+#include "math_.h"
+#include "x_.h"
+#include "gx.h" /* for gx_bitmap; includes std.h */
+#include "gserrors.h"
+#include "gxdevice.h"
+#include "gdevx.h"
+
+/* ---------------- Utilities ---------------- */
+
+static void
+gs_x_free(gs_memory_t *mem, void *obj, client_name_t cname)
+{
+ gs_free(mem, obj, 0 /*ignored*/, 0 /*ignored*/, cname);
+}
+
+/* ---------------- Color mapping setup / cleanup ---------------- */
+
+#if HaveStdCMap
+
+/* Install a standard color map in the device. */
+/* Sets std_cmap.* except for free_map. */
+static bool
+set_cmap_values(x11_cmap_values_t *values, int maxv, int mult)
+{
+ int i;
+
+ if (maxv < 1 || maxv > 63 || (maxv & (maxv + 1)) ||
+ (mult & (mult - 1))
+ )
+ return false;
+ values->cv_shift = 16 - small_exact_log2(maxv + 1);
+ for (i = 0; i <= maxv; ++i)
+ values->nearest[i] = X_max_color_value * i / maxv;
+ for (i = 0; mult != (1 << i); ++i)
+ DO_NOTHING;
+ values->pixel_shift = i;
+ return true;
+}
+static void
+set_std_cmap(gx_device_X *xdev, XStandardColormap *map)
+{
+ xdev->cman.std_cmap.map = map;
+ xdev->cman.std_cmap.fast =
+ set_cmap_values(&xdev->cman.std_cmap.red, map->red_max, map->red_mult) &&
+ set_cmap_values(&xdev->cman.std_cmap.green, map->green_max, map->green_mult) &&
+ set_cmap_values(&xdev->cman.std_cmap.blue, map->blue_max, map->blue_mult);
+}
+
+/* Get the Standard colormap if available. */
+/* Uses: dpy, scr, cmap. */
+static XStandardColormap *
+x_get_std_cmap(gx_device_X * xdev, Atom prop)
+{
+ int i;
+ XStandardColormap *scmap, *sp;
+ int nitems;
+
+ if (XGetRGBColormaps(xdev->dpy, RootWindowOfScreen(xdev->scr),
+ &scmap, &nitems, prop))
+ for (i = 0, sp = scmap; i < nitems; i++, sp++)
+ if (xdev->cmap == sp->colormap)
+ return sp;
+
+ return NULL;
+}
+
+/* Create a Standard colormap for a TrueColor or StaticGray display. */
+/* Return true if the allocation was successful. */
+/* Uses: vinfo. Sets: std_cmap.*. */
+static bool
+alloc_std_cmap(gx_device_X *xdev, bool colored)
+{
+ XStandardColormap *cmap = XAllocStandardColormap();
+
+ if (cmap == 0)
+ return false; /* can't allocate */
+ /*
+ * Some buggy X servers (including XFree86) don't set any of the
+ * _mask values for StaticGray visuals. Compensate for that here.
+ */
+ if ((cmap->red_max = xdev->vinfo->red_mask) == 0) {
+ cmap->red_max = (1 << xdev->vinfo->depth) - 1;
+ cmap->red_mult = 1;
+ } else {
+ for (cmap->red_mult = 1; (cmap->red_max & 1) == 0;) {
+ cmap->red_max >>= 1;
+ cmap->red_mult <<= 1;
+ }
+ }
+ if (colored) {
+ for (cmap->green_max = xdev->vinfo->green_mask, cmap->green_mult = 1;
+ (cmap->green_max & 1) == 0;
+ ) {
+ cmap->green_max >>= 1;
+ cmap->green_mult <<= 1;
+ }
+ for (cmap->blue_max = xdev->vinfo->blue_mask, cmap->blue_mult = 1;
+ (cmap->blue_max & 1) == 0;
+ ) {
+ cmap->blue_max >>= 1;
+ cmap->blue_mult <<= 1;
+ }
+ } else {
+ cmap->green_max = cmap->blue_max = cmap->red_max;
+ cmap->green_mult = cmap->blue_mult = cmap->red_mult;
+ }
+ set_std_cmap(xdev, cmap);
+ xdev->cman.std_cmap.free_map = true;
+ return true;
+}
+
+#endif
+
+/* Allocate the dynamic color table, if needed and possible. */
+/* Uses: vinfo, cman.num_rgb. Sets: cman.dynamic.*. */
+static void
+alloc_dynamic_colors(gx_device_X * xdev, int num_colors)
+{
+ if (num_colors > 0) {
+ xdev->cman.dynamic.colors = (x11_color_t **)
+ gs_malloc(xdev->memory, sizeof(x11_color_t *), xdev->cman.num_rgb,
+ "x11 cman.dynamic.colors");
+ if (xdev->cman.dynamic.colors) {
+ int i;
+
+ xdev->cman.dynamic.size = xdev->cman.num_rgb;
+ xdev->cman.dynamic.shift = 16 - xdev->vinfo->bits_per_rgb;
+ for (i = 0; i < xdev->cman.num_rgb; i++)
+ xdev->cman.dynamic.colors[i] = NULL;
+ xdev->cman.dynamic.max_used = min(256, num_colors);
+ xdev->cman.dynamic.used = 0;
+ }
+ }
+}
+
+/* Allocate an X color, updating the reverse map. */
+/* Return true if the allocation was successful. */
+static bool
+x_alloc_color(gx_device_X *xdev, XColor *xcolor)
+{
+ x11_rgb_t rgb;
+
+ rgb.rgb[0] = xcolor->red;
+ rgb.rgb[1] = xcolor->green;
+ rgb.rgb[2] = xcolor->blue;
+ if (!XAllocColor(xdev->dpy, xdev->cmap, xcolor))
+ return false;
+ if (xcolor->pixel < xdev->cman.color_to_rgb.size) {
+ x11_rgb_t *pxrgb = &xdev->cman.color_to_rgb.values[xcolor->pixel];
+
+ memcpy(pxrgb->rgb, rgb.rgb, sizeof(rgb.rgb));
+ pxrgb->defined = true;
+ }
+ return true;
+}
+
+/* Free X colors, updating the reverse map. */
+static void
+x_free_colors(gx_device_X *xdev, x_pixel *pixels /*[count]*/, int count)
+{
+ int i;
+ x_pixel pixel;
+
+ XFreeColors(xdev->dpy, xdev->cmap, pixels, count, 0);
+ for (i = 0; i < count; ++i)
+ if ((pixel = pixels[i]) < xdev->cman.color_to_rgb.size)
+ xdev->cman.color_to_rgb.values[pixel].defined = false;
+}
+
+/* Free a partially filled color cube or ramp. */
+/* Uses: dpy, cmap. Uses and sets: cman.dither_ramp. */
+static void
+free_ramp(gx_device_X * xdev, int num_used, int size)
+{
+ if (num_used - 1 > 0)
+ x_free_colors(xdev, xdev->cman.dither_ramp + 1, num_used - 1);
+ gs_x_free(xdev->memory, xdev->cman.dither_ramp, "x11_setup_colors");
+ xdev->cman.dither_ramp = NULL;
+}
+
+/* Allocate and fill in a color cube or ramp. */
+/* Return true if the operation succeeded. */
+/* Uses: dpy, cmap, foreground, background, cman.color_mask. */
+/* Sets: cman.dither_ramp. */
+static bool
+setup_cube(gx_device_X * xdev, int ramp_size, bool colors)
+{
+ int step, num_entries;
+ int max_rgb = ramp_size - 1;
+ int index;
+
+ if (colors) {
+ num_entries = ramp_size * ramp_size * ramp_size;
+ step = 1; /* all colors */
+ } else {
+ num_entries = ramp_size;
+ step = (ramp_size + 1) * ramp_size + 1; /* gray only */
+ }
+
+ xdev->cman.dither_ramp =
+ (x_pixel *) gs_malloc(xdev->memory, sizeof(x_pixel), num_entries,
+ "gdevx setup_cube");
+ if (xdev->cman.dither_ramp == NULL)
+ return false;
+
+ xdev->cman.dither_ramp[0] = xdev->foreground;
+ xdev->cman.dither_ramp[num_entries - 1] = xdev->background;
+ for (index = 1; index < num_entries - 1; index++) {
+ int rgb_index = index * step;
+ int q = rgb_index / ramp_size,
+ r = q / ramp_size,
+ g = q % ramp_size,
+ b = rgb_index % ramp_size;
+ XColor xc;
+
+ xc.red = (X_max_color_value * r / max_rgb) & xdev->cman.color_mask.red;
+ xc.green = (X_max_color_value * g / max_rgb) & xdev->cman.color_mask.green;
+ xc.blue = (X_max_color_value * b / max_rgb) & xdev->cman.color_mask.blue;
+ if (!x_alloc_color(xdev, &xc)) {
+ free_ramp(xdev, index, num_entries);
+ return false;
+ }
+ xdev->cman.dither_ramp[index] = xc.pixel;
+ }
+
+ return true;
+}
+
+/* Setup color mapping. */
+int
+gdev_x_setup_colors(gx_device_X * xdev)
+{
+ char palette =
+ ((xdev->vinfo->class != StaticGray) &&
+ (xdev->vinfo->class != GrayScale) ? 'C' : /* Color */
+ (xdev->vinfo->colormap_size > 2) ? 'G' : /* GrayScale */
+ 'M'); /* MonoChrome */
+
+ if (xdev->ghostview) {
+ Atom gv_colors = XInternAtom(xdev->dpy, "GHOSTVIEW_COLORS", False);
+ Atom type;
+ int format;
+ unsigned long nitems, bytes_after;
+ char *buf;
+
+ /* Delete property if explicit dest is given */
+ if (XGetWindowProperty(xdev->dpy, xdev->win, gv_colors, 0,
+ 256, (xdev->dest != 0), XA_STRING,
+ &type, &format, &nitems, &bytes_after,
+ (unsigned char **)&buf) == 0 &&
+ type == XA_STRING) {
+ nitems = sscanf(buf, "%*s %ld %ld", &(xdev->foreground),
+ &(xdev->background));
+ if (nitems != 2 || (*buf != 'M' && *buf != 'G' && *buf != 'C')) {
+ emprintf(xdev->memory, "Malformed GHOSTVIEW_COLOR property.\n");
+ return_error(gs_error_rangecheck);
+ }
+ palette = max(palette, *buf);
+ }
+ } else {
+ if (xdev->palette[0] == 'c')
+ xdev->palette[0] = 'C';
+ else if (xdev->palette[0] == 'g')
+ xdev->palette[0] = 'G';
+ else if (xdev->palette[0] == 'm')
+ xdev->palette[0] = 'M';
+ palette = max(palette, xdev->palette[0]);
+ }
+
+ /* set up color mappings here */
+ xdev->cman.color_mask.red = xdev->cman.color_mask.green =
+ xdev->cman.color_mask.blue = X_max_color_value -
+ (X_max_color_value >> xdev->vinfo->bits_per_rgb);
+ xdev->cman.match_mask = xdev->cman.color_mask; /* default */
+ xdev->cman.num_rgb = 1 << xdev->vinfo->bits_per_rgb;
+
+#if HaveStdCMap
+ xdev->cman.std_cmap.map = NULL;
+ xdev->cman.std_cmap.free_map = false;
+#endif
+ xdev->cman.dither_ramp = NULL;
+ xdev->cman.dynamic.colors = NULL;
+ xdev->cman.dynamic.size = 0;
+ xdev->cman.dynamic.used = 0;
+ switch (xdev->vinfo->depth) {
+ case 1: case 2: case 4: case 8: case 16: case 24: case 32:
+ xdev->color_info.depth = xdev->vinfo->depth;
+ break;
+ case 15:
+ xdev->color_info.depth = 16;
+ break;
+ default:
+ emprintf1(xdev->memory,
+ "Unsupported X visual depth: %d\n",
+ xdev->vinfo->depth);
+ return_error(gs_error_rangecheck);
+ }
+ { /* Set up the reverse map from pixel values to RGB. */
+ int count = 1 << min(xdev->color_info.depth, 8);
+
+ xdev->cman.color_to_rgb.values =
+ (x11_rgb_t *)gs_malloc(xdev->memory, sizeof(x11_rgb_t), count,
+ "gdevx color_to_rgb");
+ if (xdev->cman.color_to_rgb.values) {
+ int i;
+
+ for (i = 0; i < count; ++i)
+ xdev->cman.color_to_rgb.values[i].defined = false;
+ xdev->cman.color_to_rgb.size = count;
+ } else
+ xdev->cman.color_to_rgb.size = 0;
+ }
+ switch ((int)palette) {
+ case 'C':
+ xdev->color_info.num_components = 3;
+ xdev->color_info.max_gray =
+ xdev->color_info.max_color = xdev->cman.num_rgb - 1;
+#if HaveStdCMap
+ /* Get a standard color map if available */
+ if (xdev->vinfo->visual == DefaultVisualOfScreen(xdev->scr)) {
+ xdev->cman.std_cmap.map = x_get_std_cmap(xdev, XA_RGB_DEFAULT_MAP);
+ } else {
+ xdev->cman.std_cmap.map = x_get_std_cmap(xdev, XA_RGB_BEST_MAP);
+ }
+ if (xdev->cman.std_cmap.map ||
+ (xdev->vinfo->class == TrueColor && alloc_std_cmap(xdev, true))
+ ) {
+ xdev->color_info.dither_grays = xdev->color_info.dither_colors =
+ min(xdev->cman.std_cmap.map->red_max,
+ min(xdev->cman.std_cmap.map->green_max,
+ xdev->cman.std_cmap.map->blue_max)) + 1;
+ if (xdev->cman.std_cmap.map)
+ set_std_cmap(xdev, xdev->cman.std_cmap.map);
+ } else
+#endif
+ /* Otherwise set up a rgb cube of our own */
+ /* The color cube is limited to about 1/2 of the available */
+ /* colormap, the user specified maxRGBRamp (usually 5), */
+ /* or the number of representable colors */
+#define CUBE(r) (r*r*r)
+#define CBRT(r) pow(r, 1.0/3.0)
+ {
+ int ramp_size =
+ min((int)CBRT(xdev->vinfo->colormap_size / 2.0),
+ min(xdev->maxRGBRamp, xdev->cman.num_rgb));
+
+ while (!xdev->cman.dither_ramp && ramp_size >= 2) {
+ xdev->color_info.dither_grays =
+ xdev->color_info.dither_colors = ramp_size;
+ if (!setup_cube(xdev, ramp_size, true)) {
+#ifdef DEBUG
+ emprintf3(xdev->memory,
+ "Warning: failed to allocate %dx%dx%d RGB cube.\n",
+ ramp_size,
+ ramp_size,
+ ramp_size);
+#endif
+ ramp_size--;
+ continue;
+ }
+ }
+
+ if (!xdev->cman.dither_ramp) {
+ goto grayscale;
+ }
+ }
+
+ /* Allocate the dynamic color table. */
+ alloc_dynamic_colors(xdev, CUBE(xdev->cman.num_rgb) -
+ CUBE(xdev->color_info.dither_colors));
+#undef CUBE
+#undef CBRT
+ break;
+ case 'G':
+grayscale:
+ xdev->color_info.num_components = 1;
+ xdev->color_info.gray_index = 0;
+ xdev->color_info.max_gray = xdev->cman.num_rgb - 1;
+#if HaveStdCMap
+ /* Get a standard color map if available */
+ xdev->cman.std_cmap.map = x_get_std_cmap(xdev, XA_RGB_GRAY_MAP);
+ if (xdev->cman.std_cmap.map ||
+ (xdev->vinfo->class == StaticGray && alloc_std_cmap(xdev, false))
+ ) {
+ xdev->color_info.dither_grays =
+ xdev->cman.std_cmap.map->red_max + 1;
+ if (xdev->cman.std_cmap.map)
+ set_std_cmap(xdev, xdev->cman.std_cmap.map);
+ } else
+#endif
+ /* Otherwise set up a gray ramp of our own */
+ /* The gray ramp is limited to about 1/2 of the available */
+ /* colormap, the user specified maxGrayRamp (usually 128), */
+ /* or the number of representable grays */
+ {
+ int ramp_size = min(xdev->vinfo->colormap_size / 2,
+ min(xdev->maxGrayRamp, xdev->cman.num_rgb));
+
+ while (!xdev->cman.dither_ramp && ramp_size >= 3) {
+ xdev->color_info.dither_grays = ramp_size;
+ if (!setup_cube(xdev, ramp_size, false)) {
+#ifdef DEBUG
+ emprintf1(xdev->memory,
+ "Warning: failed to allocate %d level gray ramp.\n",
+ ramp_size);
+#endif
+ ramp_size /= 2;
+ continue;
+ }
+ }
+ if (!xdev->cman.dither_ramp) {
+ goto monochrome;
+ }
+ }
+
+ /* Allocate the dynamic color table. */
+ alloc_dynamic_colors(xdev, xdev->cman.num_rgb -
+ xdev->color_info.dither_grays);
+ break;
+ case 'M':
+monochrome:
+ xdev->color_info.num_components = 1;
+ xdev->color_info.gray_index = 0;
+ xdev->color_info.max_gray = 1;
+ xdev->color_info.dither_grays = 2;
+ break;
+ default:
+ emprintf1(xdev->memory, "Unknown palette: %s\n", xdev->palette);
+ if (xdev->cman.color_to_rgb.values) {
+ gs_x_free(xdev->memory, xdev->cman.color_to_rgb.values, "gdevx color_to_rgb");
+ xdev->cman.color_to_rgb.values = 0;
+ }
+ return_error(gs_error_rangecheck);
+ }
+
+#if HaveStdCMap
+ /*
+ * When comparing colors, if not halftoning, we must only compare as
+ * many bits as actually fit in a pixel, even if the hardware has more.
+ */
+ if (!gx_device_must_halftone(xdev)) {
+ if (xdev->cman.std_cmap.map) {
+ xdev->cman.match_mask.red &=
+ X_max_color_value << xdev->cman.std_cmap.red.cv_shift;
+ xdev->cman.match_mask.green &=
+ X_max_color_value << xdev->cman.std_cmap.green.cv_shift;
+ xdev->cman.match_mask.blue &=
+ X_max_color_value << xdev->cman.std_cmap.blue.cv_shift;
+ }
+ }
+#endif
+
+ return 0;
+}
+
+/* Free the dynamic colors when doing an erasepage. */
+/* Uses: cman.dynamic.*. Sets: cman.dynamic.used. */
+void
+gdev_x_free_dynamic_colors(gx_device_X *xdev)
+{
+ if (xdev->cman.dynamic.colors) {
+ int i;
+ x11_color_t *xcp;
+ x11_color_t *next;
+
+ for (i = 0; i < xdev->cman.dynamic.size; i++) {
+ for (xcp = xdev->cman.dynamic.colors[i]; xcp; xcp = next) {
+ next = xcp->next;
+ if (xcp->color.pad)
+ x_free_colors(xdev, &xcp->color.pixel, 1);
+ gs_x_free(xdev->memory, xcp, "x11_dynamic_color");
+ }
+ xdev->cman.dynamic.colors[i] = NULL;
+ }
+ xdev->cman.dynamic.used = 0;
+ }
+}
+
+/*
+ * Free storage and color map entries when closing the device.
+ * Uses and sets: cman.{std_cmap.map, dither_ramp, dynamic.colors,
+ * color_to_rgb}. Uses: cman.std_cmap.free_map.
+ */
+void
+gdev_x_free_colors(gx_device_X *xdev)
+{
+ if (xdev->cman.std_cmap.free_map) {
+ /* XFree is declared as taking a char *, not a void *! */
+ XFree((void *)xdev->cman.std_cmap.map);
+ xdev->cman.std_cmap.free_map = false;
+ }
+ xdev->cman.std_cmap.map = 0;
+ if (xdev->cman.dither_ramp)
+ gs_x_free(xdev->memory, xdev->cman.dither_ramp, "x11 dither_colors");
+ if (xdev->cman.dynamic.colors) {
+ gdev_x_free_dynamic_colors(xdev);
+ gs_x_free(xdev->memory, xdev->cman.dynamic.colors, "x11 cman.dynamic.colors");
+ xdev->cman.dynamic.colors = NULL;
+ }
+ if (xdev->cman.color_to_rgb.values) {
+ gs_x_free(xdev->memory, xdev->cman.color_to_rgb.values, "x11 color_to_rgb");
+ xdev->cman.color_to_rgb.values = NULL;
+ xdev->cman.color_to_rgb.size = 0;
+ }
+}
+
+/* ---------------- Driver color mapping calls ---------------- */
+
+/* Define a table for computing N * X_max_color_value / D for 0 <= N <= D, */
+/* 1 <= D <= 7. */
+/* This requires a multiply and a divide otherwise; */
+/* integer multiply and divide are slow on all platforms. */
+#define CV_FRACTION(n, d) ((X_color_value)(X_max_color_value * (n) / (d)))
+#define ND(n, d) CV_FRACTION(n, d)
+static const X_color_value cv_tab1[] = {
+ ND(0,1), ND(1,1)
+};
+static const X_color_value cv_tab2[] = {
+ ND(0,2), ND(1,2), ND(2,2)
+};
+static const X_color_value cv_tab3[] = {
+ ND(0,3), ND(1,3), ND(2,3), ND(3,3)
+};
+static const X_color_value cv_tab4[] = {
+ ND(0,4), ND(1,4), ND(2,4), ND(3,4), ND(4,4)
+};
+static const X_color_value cv_tab5[] = {
+ ND(0,5), ND(1,5), ND(2,5), ND(3,5), ND(4,5), ND(5,5)
+};
+static const X_color_value cv_tab6[] = {
+ ND(0,6), ND(1,6), ND(2,6), ND(3,6), ND(4,6), ND(5,6), ND(6,6)
+};
+static const X_color_value cv_tab7[] = {
+ ND(0,7), ND(1,7), ND(2,7), ND(3,7), ND(4,7), ND(5,7), ND(6,7), ND(7,7)
+};
+#undef ND
+static const X_color_value *const cv_tables[] =
+{
+ 0, cv_tab1, cv_tab2, cv_tab3, cv_tab4, cv_tab5, cv_tab6, cv_tab7
+};
+
+/* Some C compilers don't declare the abs function in math.h. */
+/* Provide one of our own. */
+static inline int
+iabs(int x)
+{
+ return (x < 0 ? -x : x);
+}
+
+/* Map RGB values to a pixel value. */
+gx_color_index
+gdev_x_map_rgb_color(gx_device * dev, const gx_color_value cv[])
+{
+ gx_device_X *const xdev = (gx_device_X *) dev;
+ gx_color_value r = cv[0];
+ gx_color_value g = cv[1];
+ gx_color_value b = cv[2];
+
+ /* X and ghostscript both use shorts for color values. */
+ /* Set drgb to the nearest color that the device can represent. */
+ X_color_value dr = r & xdev->cman.color_mask.red;
+ X_color_value dg = g & xdev->cman.color_mask.green;
+ X_color_value db = b & xdev->cman.color_mask.blue;
+
+ {
+ /* Foreground and background get special treatment: */
+ /* They may be mapped to other colors. */
+ /* Set mrgb to the color to be used for match testing. */
+ X_color_value mr = r & xdev->cman.match_mask.red;
+ X_color_value mg = g & xdev->cman.match_mask.green;
+ X_color_value mb = b & xdev->cman.match_mask.blue;
+
+ if ((mr | mg | mb) == 0) { /* i.e., all 0 */
+ if_debug4m('C', dev->memory, "[cX]%u,%u,%u => foreground = %lu\n",
+ r, g, b, (ulong) xdev->foreground);
+ return xdev->foreground;
+ }
+ if (mr == xdev->cman.match_mask.red &&
+ mg == xdev->cman.match_mask.green &&
+ mb == xdev->cman.match_mask.blue
+ ) {
+ if_debug4m('C', dev->memory, "[cX]%u,%u,%u => background = %lu\n",
+ r, g, b, (ulong) xdev->background);
+ return xdev->background;
+ }
+ }
+
+#define CV_DENOM (gx_max_color_value + 1)
+
+#if HaveStdCMap
+ /* check the standard colormap first */
+ if (xdev->cman.std_cmap.map) {
+ const XStandardColormap *cmap = xdev->cman.std_cmap.map;
+
+ if (gx_device_has_color(xdev)) {
+ uint cr, cg, cb; /* rgb cube indices */
+ X_color_value cvr, cvg, cvb; /* color value on cube */
+
+ if (xdev->cman.std_cmap.fast) {
+ cr = r >> xdev->cman.std_cmap.red.cv_shift;
+ cvr = xdev->cman.std_cmap.red.nearest[cr];
+ cg = g >> xdev->cman.std_cmap.green.cv_shift;
+ cvg = xdev->cman.std_cmap.green.nearest[cg];
+ cb = b >> xdev->cman.std_cmap.blue.cv_shift;
+ cvb = xdev->cman.std_cmap.blue.nearest[cb];
+ } else {
+ cr = r * (cmap->red_max + 1) / CV_DENOM;
+ cg = g * (cmap->green_max + 1) / CV_DENOM;
+ cb = b * (cmap->blue_max + 1) / CV_DENOM;
+ cvr = X_max_color_value * cr / cmap->red_max;
+ cvg = X_max_color_value * cg / cmap->green_max;
+ cvb = X_max_color_value * cb / cmap->blue_max;
+ }
+ if ((iabs((int)r - (int)cvr) & xdev->cman.match_mask.red) == 0 &&
+ (iabs((int)g - (int)cvg) & xdev->cman.match_mask.green) == 0 &&
+ (iabs((int)b - (int)cvb) & xdev->cman.match_mask.blue) == 0) {
+ gx_color_index pixel =
+ (xdev->cman.std_cmap.fast ?
+ (cr << xdev->cman.std_cmap.red.pixel_shift) +
+ (cg << xdev->cman.std_cmap.green.pixel_shift) +
+ (cb << xdev->cman.std_cmap.blue.pixel_shift) :
+ cr * cmap->red_mult + cg * cmap->green_mult +
+ cb * cmap->blue_mult) + cmap->base_pixel;
+
+ if_debug4m('C', dev->memory, "[cX]%u,%u,%u (std cmap) => %lu\n",
+ r, g, b, pixel); /* NB: gx_color_index size is 4 or 8 */
+ return pixel;
+ }
+ if_debug3m('C', dev->memory, "[cX]%u,%u,%u (std cmap fails)\n", r, g, b);
+ } else {
+ uint cr;
+ X_color_value cvr;
+
+ cr = r * (cmap->red_max + 1) / CV_DENOM;
+ cvr = X_max_color_value * cr / cmap->red_max;
+ if ((iabs((int)r - (int)cvr) & xdev->cman.match_mask.red) == 0) {
+ gx_color_index pixel = cr * cmap->red_mult + cmap->base_pixel;
+
+ if_debug2m('C', dev->memory, "[cX]%u (std cmap) => %lu\n", r, pixel);
+ return pixel;
+ }
+ if_debug1m('C', dev->memory, "[cX]%u (std cmap fails)\n", r);
+ }
+ } else
+#endif
+
+ /* If there is no standard colormap, check the dither cube/ramp */
+ if (xdev->cman.dither_ramp) {
+ if (gx_device_has_color(xdev)) {
+ uint cr, cg, cb; /* rgb cube indices */
+ X_color_value cvr, cvg, cvb; /* color value on cube */
+ int dither_rgb = xdev->color_info.dither_colors;
+ uint max_rgb = dither_rgb - 1;
+
+ cr = r * dither_rgb / CV_DENOM;
+ cg = g * dither_rgb / CV_DENOM;
+ cb = b * dither_rgb / CV_DENOM;
+ if (max_rgb < countof(cv_tables)) {
+ const ushort *cv_tab = cv_tables[max_rgb];
+
+ cvr = cv_tab[cr];
+ cvg = cv_tab[cg];
+ cvb = cv_tab[cb];
+ } else {
+ cvr = CV_FRACTION(cr, max_rgb);
+ cvg = CV_FRACTION(cg, max_rgb);
+ cvb = CV_FRACTION(cb, max_rgb);
+ }
+ if ((iabs((int)r - (int)cvr) & xdev->cman.match_mask.red) == 0 &&
+ (iabs((int)g - (int)cvg) & xdev->cman.match_mask.green) == 0 &&
+ (iabs((int)b - (int)cvb) & xdev->cman.match_mask.blue) == 0) {
+ gx_color_index pixel =
+ xdev->cman.dither_ramp[CUBE_INDEX(cr, cg, cb)];
+
+ if_debug4m('C', dev->memory, "[cX]%u,%u,%u (dither cube) => %lu\n",
+ r, g, b, pixel);
+ return pixel;
+ }
+ if_debug3m('C', dev->memory, "[cX]%u,%u,%u (dither cube fails)\n", r, g, b);
+ } else {
+ uint cr;
+ X_color_value cvr;
+ int dither_grays = xdev->color_info.dither_grays;
+ uint max_gray = dither_grays - 1;
+
+ cr = r * dither_grays / CV_DENOM;
+ cvr = (X_max_color_value * cr / max_gray);
+ if ((iabs((int)r - (int)cvr) & xdev->cman.match_mask.red) == 0) {
+ gx_color_index pixel = xdev->cman.dither_ramp[cr];
+
+ if_debug2m('C', dev->memory, "[cX]%u (dither ramp) => %lu\n", r, pixel);
+ return pixel;
+ }
+ if_debug1m('C', dev->memory, "[cX]%u (dither ramp fails)\n", r);
+ }
+ }
+
+ /* Finally look through the list of dynamic colors */
+ if (xdev->cman.dynamic.colors) {
+ int i = (dr ^ dg ^ db) >> xdev->cman.dynamic.shift;
+ x11_color_t *xcp = xdev->cman.dynamic.colors[i];
+ x11_color_t *prev = NULL;
+ XColor xc;
+
+ for (; xcp; prev = xcp, xcp = xcp->next)
+ if (xcp->color.red == dr && xcp->color.green == dg &&
+ xcp->color.blue == db) {
+ /* Promote the found entry to the front of the list. */
+ if (prev) {
+ prev->next = xcp->next;
+ xcp->next = xdev->cman.dynamic.colors[i];
+ xdev->cman.dynamic.colors[i] = xcp;
+ }
+ if (xcp->color.pad) {
+ if_debug4m('C', dev->memory, "[cX]%u,%u,%u (dynamic) => %lu\n",
+ r, g, b, (ulong) xcp->color.pixel);
+ return xcp->color.pixel;
+ } else {
+ if_debug3m('C', dev->memory, "[cX]%u,%u,%u (dynamic) => missing\n",
+ r, g, b);
+ return gx_no_color_index;
+ }
+ }
+
+ /* If not in our list of dynamic colors, */
+ /* ask the X server and add an entry. */
+ /* First check if dynamic table is exhausted */
+ if (xdev->cman.dynamic.used > xdev->cman.dynamic.max_used) {
+ if_debug3m('C', dev->memory, "[cX]%u,%u,%u (dynamic) => full\n", r, g, b);
+ return gx_no_color_index;
+ }
+ xcp = (x11_color_t *)
+ gs_malloc(xdev->memory, sizeof(x11_color_t), 1, "x11_dynamic_color");
+ if (!xcp)
+ return gx_no_color_index;
+ xc.red = xcp->color.red = dr;
+ xc.green = xcp->color.green = dg;
+ xc.blue = xcp->color.blue = db;
+ xcp->next = xdev->cman.dynamic.colors[i];
+ xdev->cman.dynamic.colors[i] = xcp;
+ xdev->cman.dynamic.used++;
+ if (x_alloc_color(xdev, &xc)) {
+ xcp->color.pixel = xc.pixel;
+ xcp->color.pad = true;
+ if_debug5m('c', dev->memory, "[cX]0x%x,0x%x,0x%x (dynamic) => added [%d]%lu\n",
+ dr, dg, db, xdev->cman.dynamic.used - 1,
+ (ulong)xc.pixel);
+ return xc.pixel;
+ } else {
+ xcp->color.pad = false;
+ if_debug3m('c', dev->memory, "[cX]0x%x,0x%x,0x%x (dynamic) => can't alloc\n",
+ dr, dg, db);
+ return gx_no_color_index;
+ }
+ }
+ if_debug3m('C', dev->memory, "[cX]%u,%u,%u fails\n", r, g, b);
+ return gx_no_color_index;
+#undef CV_DENOM
+}
+
+/* Map a pixel value back to r-g-b. */
+int
+gdev_x_map_color_rgb(gx_device * dev, gx_color_index color,
+ gx_color_value prgb[3])
+{
+ const gx_device_X *const xdev = (const gx_device_X *) dev;
+#if HaveStdCMap
+ const XStandardColormap *cmap = xdev->cman.std_cmap.map;
+#endif
+
+ if (color == xdev->foreground) {
+ prgb[0] = prgb[1] = prgb[2] = 0;
+ return 0;
+ }
+ if (color == xdev->background) {
+ prgb[0] = prgb[1] = prgb[2] = gx_max_color_value;
+ return 0;
+ }
+ if (color < xdev->cman.color_to_rgb.size) {
+ const x11_rgb_t *pxrgb = &xdev->cman.color_to_rgb.values[color];
+
+ if (pxrgb->defined) {
+ prgb[0] = pxrgb->rgb[0];
+ prgb[1] = pxrgb->rgb[1];
+ prgb[2] = pxrgb->rgb[2];
+ return 0;
+ }
+#if HaveStdCMap
+ }
+
+ /* Check the standard colormap. */
+ if (cmap) {
+ if (color >= cmap->base_pixel) {
+ x_pixel value = color - cmap->base_pixel;
+ uint r = (value / cmap->red_mult) % (cmap->red_max + 1);
+ uint g = (value / cmap->green_mult) % (cmap->green_max + 1);
+ uint b = (value / cmap->blue_mult) % (cmap->blue_max + 1);
+
+ if (value == r * cmap->red_mult + g * cmap->green_mult +
+ b * cmap->blue_mult) {
+ /* When mapping color buckets back to specific colors,
+ * we can choose to map them to the darkest shades
+ * (e.g., 0, 1/3, 2/3), to the lightest shades (e.g.,
+ * 1/3-epsilon, 2/3-epsilon, 1-epsilon), to the middle
+ * shades (e.g., 1/6, 1/2, 5/6), or for maximum range
+ * (e.g., 0, 1/2, 1). The last of these matches the
+ * assumptions of the halftoning code, so that is what
+ * we choose.
+ */
+ prgb[0] = r * gx_max_color_value / cmap->red_max;
+ prgb[1] = g * gx_max_color_value / cmap->green_max;
+ prgb[2] = b * gx_max_color_value / cmap->blue_max;
+ return 0;
+ }
+ }
+ }
+ if (color < xdev->cman.color_to_rgb.size) {
+#endif
+ /* Error -- undefined pixel value. */
+ return_error(gs_error_unknownerror);
+ }
+ /*
+ * Check the dither cube/ramp. This is hardly ever used, since if
+ * there are few enough colors to require dithering, the pixel values
+ * are likely to be small enough to index color_to_rgb.
+ */
+ if (xdev->cman.dither_ramp) {
+ if (gx_device_has_color(xdev)) {
+ int size = xdev->color_info.dither_colors;
+ int size3 = size * size * size;
+ int i;
+
+ for (i = 0; i < size3; ++i)
+ if (xdev->cman.dither_ramp[i] == color) {
+ uint max_rgb = size - 1;
+ uint q = i / size,
+ r = q / size,
+ g = q % size,
+ b = i % size;
+
+ /*
+ * See above regarding the choice of color mapping
+ * algorithm.
+ */
+ prgb[0] = r * gx_max_color_value / max_rgb;
+ prgb[1] = g * gx_max_color_value / max_rgb;
+ prgb[2] = b * gx_max_color_value / max_rgb;
+ return 0;
+ }
+ } else {
+ int size = xdev->color_info.dither_grays;
+ int i;
+
+ for (i = 0; i < size; ++i)
+ if (xdev->cman.dither_ramp[i] == color) {
+ prgb[0] = prgb[1] = prgb[2] =
+ i * gx_max_color_value / (size - 1);
+ return 0;
+ }
+ }
+ }
+
+ /* Finally, search the list of dynamic colors. */
+ if (xdev->cman.dynamic.colors) {
+ int i;
+ const x11_color_t *xcp;
+
+ for (i = xdev->cman.dynamic.size; --i >= 0;)
+ for (xcp = xdev->cman.dynamic.colors[i]; xcp; xcp = xcp->next)
+ if (xcp->color.pixel == color && xcp->color.pad) {
+ prgb[0] = xcp->color.red;
+ prgb[1] = xcp->color.green;
+ prgb[2] = xcp->color.blue;
+ return 0;
+ }
+ }
+
+ /* Not found -- not possible! */
+ return_error(gs_error_unknownerror);
+}
diff --git a/devices/gdevxcmp.h b/devices/gdevxcmp.h
new file mode 100644
index 000000000..a4411adbd
--- /dev/null
+++ b/devices/gdevxcmp.h
@@ -0,0 +1,145 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* X driver color mapping structure */
+
+#ifndef gdevxcmp_INCLUDED
+# define gdevxcmp_INCLUDED
+
+/*
+ * The structure defined in this file is used in only one place, in the
+ * gx_device_X structure defined in gdevx.h. We define it as a separate
+ * structure because its function is logically separate from the rest of the
+ * X driver, and because this function (color mapping / management) accounts
+ * for the single largest piece of the driver.
+ */
+
+/* Define pixel value to RGB mapping */
+typedef struct x11_rgb_s {
+ gx_color_value rgb[3];
+ bool defined;
+} x11_rgb_t;
+
+/* Define dynamic color hash table structure */
+typedef struct x11_color_s x11_color_t;
+struct x11_color_s {
+ XColor color;
+ x11_color_t *next;
+};
+
+/*
+ * Define X color values. Fortuitously, these are the same as Ghostscript
+ * color values; in gdevxcmp.c, we are pretty sloppy about aliasing the
+ * two.
+ */
+typedef ushort X_color_value;
+#define X_max_color_value 0xffff
+
+#if HaveStdCMap /* Standard colormap stuff is only in X11R4 and later. */
+
+/* Define the structure for values computed from a standard cmap component. */
+typedef struct x11_cmap_values_s {
+ int cv_shift; /* 16 - log2(max_value + 1) */
+ X_color_value nearest[64]; /* [i] = i * 0xffff / max_value */
+ int pixel_shift; /* log2(mult) */
+} x11_cmap_values_t;
+
+#endif
+
+typedef struct x11_cman_s {
+
+ /*
+ * num_rgb is the number of possible R/G/B values, i.e.,
+ * 1 << the bits_per_rgb of the X visual.
+ */
+ int num_rgb;
+
+ /*
+ * color_mask is a mask that selects the high-order N bits of an
+ * X color value, where N may be the mask width for TrueColor or
+ * StaticGray and is bits_per_rgb for the other visual classes.
+ *
+ * match_mask is the mask used for comparing colors. It may have
+ * fewer bits than color_mask if the device is not using halftones.
+ */
+ struct cmm_ {
+ X_color_value red, green, blue;
+ } color_mask, match_mask;
+
+#if HaveStdCMap /* Standard colormap stuff is only in X11R4 and later. */
+
+ struct {
+
+ /*
+ * map is the X standard colormap for the display and screen,
+ * if one is available.
+ */
+ XStandardColormap *map;
+
+ /*
+ * When possible, we precompute shift values and tables that replace
+ * some multiplies and divides.
+ */
+ bool fast;
+ x11_cmap_values_t red, green, blue;
+
+ /*
+ * If free_map is true, we allocated the map ourselves (to
+ * represent a TrueColor or Static Gray visual), and must free it
+ * when closing the device.
+ */
+ bool free_map;
+
+ } std_cmap;
+
+#endif /* HaveStdCmap */
+
+ /*
+ * color_to_rgb is a reverse map from pixel values to RGB values. It
+ * only maps pixels values up to 255: pixel values above this must go
+ * through the standard colormap or query the server.
+ */
+ struct cmc_ {
+ int size; /* min(1 << depth, 256) */
+ x11_rgb_t *values; /* [color_to_rgb.size] */
+ } color_to_rgb;
+
+ /*
+ * For systems with writable colormaps and no suitable standard
+ * colormap, dither_ramp is a preallocated ramp or cube used for
+ * dithering.
+ */
+#define CUBE_INDEX(r,g,b) (((r) * xdev->color_info.dither_colors + (g)) * \
+ xdev->color_info.dither_colors + (b))
+ x_pixel *dither_ramp; /* [color_info.dither_colors^3] if color,
+ [color_info.dither_grays] if gray */
+
+ /*
+ * For systems with writable colormaps, dynamic.colors is a chained
+ * hash table that maps RGB values (masked with color_mask) to
+ * pixel values. Entries are added dynamically.
+ */
+ struct cmd_ {
+ int size;
+ x11_color_t **colors; /* [size] */
+ int shift; /* 16 - log2(size) */
+ int used;
+ int max_used;
+ } dynamic;
+
+} x11_cman_t;
+
+#endif /* gdevxcmp_INCLUDED */
diff --git a/devices/gdevxini.c b/devices/gdevxini.c
new file mode 100644
index 000000000..d48b17862
--- /dev/null
+++ b/devices/gdevxini.c
@@ -0,0 +1,951 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* X Windows driver initialization/finalization */
+#include "memory_.h"
+#include "x_.h"
+#include "gx.h"
+#include "gserrors.h"
+#include "gxdevice.h"
+#include "gxdevmem.h"
+#include "gsparamx.h"
+#include "gdevbbox.h"
+#include "gdevx.h"
+
+extern char *getenv(const char *);
+
+extern const gx_device_bbox gs_bbox_device;
+extern const gx_device_X gs_x11_device;
+
+extern const gx_device_bbox_procs_t gdev_x_box_procs;
+
+/* Define constants for orientation from ghostview */
+/* Number represents clockwise rotation of the paper in degrees */
+typedef enum {
+ Portrait = 0, /* Normal portrait orientation */
+ Landscape = 90, /* Normal landscape orientation */
+ Upsidedown = 180, /* Don't think this will be used much */
+ Seascape = 270 /* Landscape rotated the wrong way */
+} orientation;
+
+/* ---------------- Opening/initialization ---------------- */
+
+/* Forward references */
+static void x_get_work_area(gx_device_X *xdev, int *pwidth, int *pheight);
+static long *x_get_win_property(gx_device_X *xdev, const char *atom_name);
+
+/* Catch the alloc error when there is not enough resources for the
+ * backing pixmap. Automatically shut off backing pixmap and let the
+ * user know when this happens.
+ *
+ * Because the X API was designed without adequate thought to reentrancy,
+ * these variables must be allocated statically. We do not see how this
+ * code can work reliably in the presence of multi-threading.
+ */
+static struct xv_ {
+ Boolean alloc_error;
+ XErrorHandler orighandler;
+ XErrorHandler oldhandler;
+} x_error_handler;
+
+static int
+x_catch_alloc(Display * dpy, XErrorEvent * err)
+{
+ if (err->error_code == BadAlloc)
+ x_error_handler.alloc_error = True;
+ if (x_error_handler.alloc_error)
+ return 0;
+ return x_error_handler.oldhandler(dpy, err);
+}
+
+int
+x_catch_free_colors(Display * dpy, XErrorEvent * err)
+{
+ if (err->request_code == X_FreeColors)
+ return 0;
+ return x_error_handler.orighandler(dpy, err);
+}
+
+/* Open the X device */
+int
+gdev_x_open(gx_device_X * xdev)
+{
+ XSizeHints sizehints;
+ char *window_id;
+ XEvent event;
+ XVisualInfo xvinfo;
+ int nitems;
+ XtAppContext app_con;
+ Widget toplevel;
+ Display *dpy;
+ XColor xc;
+ int zero = 0;
+ int xid_height = 0, xid_width = 0;
+ int code;
+
+#ifdef DEBUG
+# ifdef have_Xdebug
+ if (gs_debug['X']) {
+ extern int _Xdebug;
+
+ _Xdebug = 1;
+ }
+# endif
+#endif
+ if (!(xdev->dpy = XOpenDisplay((char *)NULL))) {
+ char *dispname = getenv("DISPLAY");
+
+ emprintf1(xdev->memory,
+ "Cannot open X display `%s'.\n",
+ (dispname == NULL ? "(null)" : dispname));
+ return_error(gs_error_ioerror);
+ }
+ xdev->dest = 0;
+ if ((window_id = getenv("GHOSTVIEW"))) {
+ if (!(xdev->ghostview = sscanf(window_id, "%ld %ld",
+ &(xdev->win), &(xdev->dest)))) {
+ emprintf(xdev->memory, "Cannot get Window ID from ghostview.\n");
+ return_error(gs_error_ioerror);
+ }
+ }
+ if (xdev->pwin != (Window) None) { /* pick up the destination window parameters if specified */
+ XWindowAttributes attrib;
+
+ xdev->win = xdev->pwin;
+ if (XGetWindowAttributes(xdev->dpy, xdev->win, &attrib)) {
+ xdev->scr = attrib.screen;
+ xvinfo.visual = attrib.visual;
+ xdev->cmap = attrib.colormap;
+ xid_width = attrib.width;
+ xid_height = attrib.height;
+ } else {
+ /* No idea why we can't get the attributes, but */
+ /* we shouldn't let it lead to a failure below. */
+ xid_width = xid_height = 0;
+ }
+ } else if (xdev->ghostview) {
+ XWindowAttributes attrib;
+ Atom type;
+ int format;
+ unsigned long nitems, bytes_after;
+ char *buf;
+ Atom ghostview_atom = XInternAtom(xdev->dpy, "GHOSTVIEW", False);
+
+ if (XGetWindowAttributes(xdev->dpy, xdev->win, &attrib)) {
+ xdev->scr = attrib.screen;
+ xvinfo.visual = attrib.visual;
+ xdev->cmap = attrib.colormap;
+ xdev->width = attrib.width;
+ xdev->height = attrib.height;
+ }
+ /* Delete property if explicit dest is given */
+ if (XGetWindowProperty(xdev->dpy, xdev->win, ghostview_atom, 0,
+ 256, (xdev->dest != 0), XA_STRING,
+ &type, &format, &nitems, &bytes_after,
+ (unsigned char **)&buf) == 0 &&
+ type == XA_STRING) {
+ int llx, lly, urx, ury;
+ int left_margin = 0, bottom_margin = 0;
+ int right_margin = 0, top_margin = 0;
+
+ /* We declare page_orientation as an int so that we can */
+ /* use an int * to reference it for sscanf; compilers */
+ /* might be tempted to use less space to hold it if */
+ /* it were declared as an orientation. */
+ int /*orientation */ page_orientation;
+ float xppp, yppp; /* pixels per point */
+
+ nitems = sscanf(buf,
+ "%ld %d %d %d %d %d %f %f %d %d %d %d",
+ &(xdev->bpixmap), &page_orientation,
+ &llx, &lly, &urx, &ury,
+ &(xdev->x_pixels_per_inch),
+ &(xdev->y_pixels_per_inch),
+ &left_margin, &bottom_margin,
+ &right_margin, &top_margin);
+ if (!(nitems == 8 || nitems == 12)) {
+ emprintf(xdev->memory, "Cannot get ghostview property.\n");
+ return_error(gs_error_ioerror);
+ }
+ if (xdev->dest && xdev->bpixmap) {
+ emprintf(xdev->memory,
+ "Both destination and backing pixmap specified.\n");
+ return_error(gs_error_rangecheck);
+ }
+ if (xdev->dest) {
+ Window root;
+ int x, y;
+ unsigned int width, height;
+ unsigned int border_width, depth;
+
+ if (XGetGeometry(xdev->dpy, xdev->dest, &root, &x, &y,
+ &width, &height, &border_width, &depth)) {
+ xdev->width = width;
+ xdev->height = height;
+ }
+ }
+ xppp = xdev->x_pixels_per_inch / 72.0;
+ yppp = xdev->y_pixels_per_inch / 72.0;
+ switch (page_orientation) {
+ case Portrait:
+ xdev->initial_matrix.xx = xppp;
+ xdev->initial_matrix.xy = 0.0;
+ xdev->initial_matrix.yx = 0.0;
+ xdev->initial_matrix.yy = -yppp;
+ xdev->initial_matrix.tx = -llx * xppp;
+ xdev->initial_matrix.ty = ury * yppp;
+ break;
+ case Landscape:
+ xdev->initial_matrix.xx = 0.0;
+ xdev->initial_matrix.xy = yppp;
+ xdev->initial_matrix.yx = xppp;
+ xdev->initial_matrix.yy = 0.0;
+ xdev->initial_matrix.tx = -lly * xppp;
+ xdev->initial_matrix.ty = -llx * yppp;
+ break;
+ case Upsidedown:
+ xdev->initial_matrix.xx = -xppp;
+ xdev->initial_matrix.xy = 0.0;
+ xdev->initial_matrix.yx = 0.0;
+ xdev->initial_matrix.yy = yppp;
+ xdev->initial_matrix.tx = urx * xppp;
+ xdev->initial_matrix.ty = -lly * yppp;
+ break;
+ case Seascape:
+ xdev->initial_matrix.xx = 0.0;
+ xdev->initial_matrix.xy = -yppp;
+ xdev->initial_matrix.yx = -xppp;
+ xdev->initial_matrix.yy = 0.0;
+ xdev->initial_matrix.tx = ury * xppp;
+ xdev->initial_matrix.ty = urx * yppp;
+ break;
+ }
+
+ /* The following sets the imageable area according to the */
+ /* bounding box and margins sent by ghostview. */
+ /* This code has been patched many times; its current state */
+ /* is per a recommendation by Tim Theisen on 4/28/95. */
+
+ xdev->ImagingBBox[0] = llx - left_margin;
+ xdev->ImagingBBox[1] = lly - bottom_margin;
+ xdev->ImagingBBox[2] = urx + right_margin;
+ xdev->ImagingBBox[3] = ury + top_margin;
+ xdev->ImagingBBox_set = true;
+
+ } else if (xdev->pwin == (Window) None) {
+ emprintf(xdev->memory, "Cannot get ghostview property.\n");
+ return_error(gs_error_ioerror);
+ }
+ } else {
+ Screen *scr = DefaultScreenOfDisplay(xdev->dpy);
+
+ xdev->scr = scr;
+ xvinfo.visual = DefaultVisualOfScreen(scr);
+ xdev->cmap = DefaultColormapOfScreen(scr);
+ if (xvinfo.visual->class != TrueColor) {
+ int scrno = DefaultScreen(xdev->dpy);
+ if ( XMatchVisualInfo(xdev->dpy, scrno, 24, TrueColor, &xvinfo) ||
+ XMatchVisualInfo(xdev->dpy, scrno, 32, TrueColor, &xvinfo) ||
+ XMatchVisualInfo(xdev->dpy, scrno, 16, TrueColor, &xvinfo) ||
+ XMatchVisualInfo(xdev->dpy, scrno, 15, TrueColor, &xvinfo) ) {
+ xdev->cmap = XCreateColormap (xdev->dpy,
+ DefaultRootWindow(xdev->dpy),
+ xvinfo.visual, AllocNone );
+ }
+ }
+ }
+ xvinfo.visualid = XVisualIDFromVisual(xvinfo.visual);
+ xdev->vinfo = XGetVisualInfo(xdev->dpy, VisualIDMask, &xvinfo, &nitems);
+ if (xdev->vinfo == NULL) {
+ emprintf(xdev->memory, "Cannot get XVisualInfo.\n");
+ return_error(gs_error_ioerror);
+ }
+ /* Buggy X servers may cause a Bad Access on XFreeColors. */
+ x_error_handler.orighandler = XSetErrorHandler(x_catch_free_colors);
+
+ /* Get X Resources. Use the toolkit for this. */
+ XtToolkitInitialize();
+ app_con = XtCreateApplicationContext();
+ XtAppSetFallbackResources(app_con, gdev_x_fallback_resources);
+ dpy = XtOpenDisplay(app_con, NULL, "ghostscript", "Ghostscript",
+ NULL, 0, &zero, NULL);
+ toplevel = XtAppCreateShell(NULL, "Ghostscript",
+ applicationShellWidgetClass, dpy, NULL, 0);
+ XtGetApplicationResources(toplevel, (XtPointer) xdev,
+ gdev_x_resources, gdev_x_resource_count,
+ NULL, 0);
+
+ /* Reserve foreground and background colors under the regular connection. */
+ xc.pixel = xdev->foreground;
+ XQueryColor(xdev->dpy, DefaultColormap(xdev->dpy,DefaultScreen(xdev->dpy)), &xc);
+ XAllocColor(xdev->dpy, xdev->cmap, &xc);
+ xdev->foreground = xc.pixel;
+ xc.pixel = xdev->background;
+ XQueryColor(xdev->dpy, DefaultColormap(xdev->dpy,DefaultScreen(xdev->dpy)), &xc);
+ XAllocColor(xdev->dpy, xdev->cmap, &xc);
+ xdev->background = xc.pixel;
+
+ code = gdev_x_setup_colors(xdev);
+ if (code < 0) {
+ XCloseDisplay(xdev->dpy);
+ return code;
+ }
+ /* Now that the color map is setup check if the device is separable. */
+ check_device_separable((gx_device *)xdev);
+
+ if (!xdev->ghostview) {
+ XWMHints wm_hints;
+ XClassHint class_hint;
+ XSetWindowAttributes xswa;
+ gx_device *dev = (gx_device *) xdev;
+
+ /* Take care of resolution and paper size. */
+ if (xdev->x_pixels_per_inch == FAKE_RES ||
+ xdev->y_pixels_per_inch == FAKE_RES) {
+ float xsize = (float)xdev->width / xdev->x_pixels_per_inch;
+ float ysize = (float)xdev->height / xdev->y_pixels_per_inch;
+ int workarea_width = WidthOfScreen(xdev->scr), workarea_height = HeightOfScreen(xdev->scr);
+
+ /* Get area available for windows placement */
+ x_get_work_area(xdev, &workarea_width, &workarea_height);
+
+ if (xdev->xResolution == 0.0 && xdev->yResolution == 0.0) {
+ float dpi, xdpi, ydpi;
+
+ xdpi = 25.4 * WidthOfScreen(xdev->scr) /
+ WidthMMOfScreen(xdev->scr);
+ ydpi = 25.4 * HeightOfScreen(xdev->scr) /
+ HeightMMOfScreen(xdev->scr);
+ dpi = min(xdpi, ydpi);
+ /*
+ * Some X servers provide very large "virtual screens", and
+ * return the virtual screen size for Width/HeightMM but the
+ * physical size for Width/Height. Attempt to detect and
+ * correct for this now. This is a KLUDGE required because
+ * the X server provides no way to read the screen
+ * resolution directly.
+ */
+ if (dpi < 30)
+ dpi = 75; /* arbitrary */
+ else {
+ while (xsize * dpi > WidthOfScreen(xdev->scr) - 32 ||
+ ysize * dpi > HeightOfScreen(xdev->scr) - 32)
+ dpi *= 0.95;
+ }
+ xdev->x_pixels_per_inch = dpi;
+ xdev->y_pixels_per_inch = dpi;
+ } else {
+ xdev->x_pixels_per_inch = xdev->xResolution;
+ xdev->y_pixels_per_inch = xdev->yResolution;
+ }
+ /* Restrict maximum window size to available work area */
+ if (xdev->width > workarea_width) {
+ xdev->width = min(xsize * xdev->x_pixels_per_inch, workarea_width);
+ }
+ if (xdev->height > workarea_height) {
+ xdev->height = min(ysize * xdev->y_pixels_per_inch, workarea_height);
+ }
+ xdev->MediaSize[0] =
+ (float)xdev->width / xdev->x_pixels_per_inch * 72;
+ xdev->MediaSize[1] =
+ (float)xdev->height / xdev->y_pixels_per_inch * 72;
+ }
+
+ /*
+ * If the margins' resolution values are not initialized
+ * default to the device resolution, most devices initialize
+ * this at compile time because they don't "detect" the
+ * resolution as we do here.
+ */
+ if (xdev->MarginsHWResolution[0] == FAKE_RES ||
+ xdev->MarginsHWResolution[1] == FAKE_RES) {
+ xdev->MarginsHWResolution[0] = xdev->x_pixels_per_inch;
+ xdev->MarginsHWResolution[1] = xdev->y_pixels_per_inch;
+ }
+
+ sizehints.x = 0;
+ sizehints.y = 0;
+ sizehints.width = xdev->width;
+ sizehints.height = xdev->height;
+ sizehints.flags = 0;
+
+ if (xdev->geometry != NULL) {
+ /*
+ * Note that border_width must be set first. We can't use
+ * scr, because that is a Screen*, and XWMGeometry wants
+ * the screen number.
+ */
+ char gstr[40];
+ int bitmask;
+
+ gs_sprintf(gstr, "%dx%d+%d+%d", sizehints.width,
+ sizehints.height, sizehints.x, sizehints.y);
+ bitmask = XWMGeometry(xdev->dpy, DefaultScreen(xdev->dpy),
+ xdev->geometry, gstr, xdev->borderWidth,
+ &sizehints,
+ &sizehints.x, &sizehints.y,
+ &sizehints.width, &sizehints.height,
+ &sizehints.win_gravity);
+
+ if (bitmask & (XValue | YValue))
+ sizehints.flags |= USPosition;
+ }
+ gx_default_get_initial_matrix(dev, &(xdev->initial_matrix));
+
+ if (xdev->pwin != (Window) None && xid_width != 0 && xid_height != 0) {
+#if 0 /*************** */
+
+ /*
+ * The user who originally implemented the WindowID feature
+ * provided the following code to scale the displayed output
+ * to fit in the window. We think this is a bad idea,
+ * since it doesn't track window resizing and is generally
+ * completely at odds with the way Ghostscript treats
+ * window or paper size in all other contexts. We are
+ * leaving the code here in case someone decides that
+ * this really is the behavior they want.
+ */
+
+ /* Scale to fit in the window. */
+ xdev->initial_matrix.xx
+ = xdev->initial_matrix.xx *
+ (float)xid_width / (float)xdev->width;
+ xdev->initial_matrix.yy
+ = xdev->initial_matrix.yy *
+ (float)xid_height / (float)xdev->height;
+
+#endif /*************** */
+ xdev->width = xid_width;
+ xdev->height = xid_height;
+ xdev->initial_matrix.ty = xdev->height;
+ } else { /* !xdev->pwin */
+ xswa.event_mask = ExposureMask;
+ xswa.background_pixel = xdev->background;
+ xswa.border_pixel = xdev->borderColor;
+ xswa.colormap = xdev->cmap;
+ xdev->win = XCreateWindow(xdev->dpy, RootWindowOfScreen(xdev->scr),
+ sizehints.x, sizehints.y, /* upper left */
+ xdev->width, xdev->height,
+ xdev->borderWidth,
+ xdev->vinfo->depth,
+ InputOutput, /* class */
+ xdev->vinfo->visual, /* visual */
+ CWEventMask | CWBackPixel |
+ CWBorderPixel | CWColormap,
+ &xswa);
+ XStoreName(xdev->dpy, xdev->win, "ghostscript");
+ XSetWMNormalHints(xdev->dpy, xdev->win, &sizehints);
+ wm_hints.flags = InputHint;
+ wm_hints.input = False;
+ XSetWMHints(xdev->dpy, xdev->win, &wm_hints); /* avoid input focus */
+ class_hint.res_name = "ghostscript";
+ class_hint.res_class = "Ghostscript";
+ XSetClassHint(xdev->dpy, xdev->win, &class_hint);
+ }
+ }
+ /***
+ *** According to Ricard Torres (torres@upf.es), we have to wait until here
+ *** to close the toolkit connection, which we formerly did
+ *** just after the calls on XAllocColor above. I suspect that
+ *** this will cause things to be left dangling if an error occurs
+ *** anywhere in the above code, but I'm willing to let users
+ *** fight over fixing it, since I have no idea what's right.
+ ***/
+
+ /* And close the toolkit connection. */
+ XtDestroyWidget(toplevel);
+ XtCloseDisplay(dpy);
+ XtDestroyApplicationContext(app_con);
+
+ xdev->ht.pixmap = (Pixmap) 0;
+ xdev->ht.id = gx_no_bitmap_id;;
+ xdev->fill_style = FillSolid;
+ xdev->function = GXcopy;
+ xdev->fid = (Font) 0;
+
+ /* Set up a graphics context */
+ xdev->gc = XCreateGC(xdev->dpy, xdev->win, 0, (XGCValues *) NULL);
+ XSetFunction(xdev->dpy, xdev->gc, GXcopy);
+ XSetLineAttributes(xdev->dpy, xdev->gc, 0,
+ LineSolid, CapButt, JoinMiter);
+
+ gdev_x_clear_window(xdev);
+
+ if (!xdev->ghostview) { /* Make the window appear. */
+ XMapWindow(xdev->dpy, xdev->win);
+
+ /* Before anything else, do a flush and wait for */
+ /* an exposure event. */
+ XSync(xdev->dpy, False);
+ if (xdev->pwin == (Window) None) { /* there isn't a next event for existing windows */
+ XNextEvent(xdev->dpy, &event);
+ }
+ /* Now turn off graphics exposure events so they don't queue up */
+ /* indefinitely. Also, since we can't do anything about real */
+ /* Expose events, mask them out. */
+ XSetGraphicsExposures(xdev->dpy, xdev->gc, False);
+ XSelectInput(xdev->dpy, xdev->win, NoEventMask);
+ } else {
+ /* Create an unmapped window, that the window manager will ignore.
+ * This invisible window will be used to receive "next page"
+ * events from ghostview */
+ XSetWindowAttributes attributes;
+
+ attributes.override_redirect = True;
+ xdev->mwin = XCreateWindow(xdev->dpy, RootWindowOfScreen(xdev->scr),
+ 0, 0, 1, 1, 0, CopyFromParent,
+ CopyFromParent, CopyFromParent,
+ CWOverrideRedirect, &attributes);
+ xdev->NEXT = XInternAtom(xdev->dpy, "NEXT", False);
+ xdev->PAGE = XInternAtom(xdev->dpy, "PAGE", False);
+ xdev->DONE = XInternAtom(xdev->dpy, "DONE", False);
+ }
+
+ xdev->ht.no_pixmap = XCreatePixmap(xdev->dpy, xdev->win, 1, 1,
+ xdev->vinfo->depth);
+
+ return 0;
+}
+
+/* Get area available for windows placement */
+static void
+x_get_work_area(gx_device_X *xdev, int *pwidth, int *pheight)
+{
+ /* First get work area from window manager if available */
+ long *area;
+
+ /* Try to get NET_WORKAREA then WIN_WORKAREA */
+ if ((area = x_get_win_property(xdev, "_NET_WORKAREA")) != NULL ||
+ (area = x_get_win_property(xdev, "_WIN_WORKAREA")) != NULL) {
+ /* Update maximum screen size with usable screen size */
+ *pwidth = area[2];
+ *pheight = area[3];
+ XFree(area);
+ }
+}
+
+/* Get window property with specified name (should be CARDINAL, four 32-bit words) */
+static long *
+x_get_win_property(gx_device_X *xdev, const char *atom_name)
+{
+ Atom r_type = (Atom)0;
+ int r_format = 0;
+ unsigned long count = 0;
+ unsigned long bytes_remain;
+ unsigned char *prop;
+
+ if (XGetWindowProperty(xdev->dpy, RootWindowOfScreen(xdev->scr),
+ XInternAtom(xdev->dpy, atom_name, False),
+ 0, 4, False, XA_CARDINAL,
+ &r_type, &r_format,
+ &count, &bytes_remain, &prop) == Success &&
+ prop &&
+ r_type == XA_CARDINAL &&
+ r_format == 32 &&
+ count == 4 &&
+ bytes_remain == 0)
+ return (long *)prop; /* should free at the caller */
+ /* property does not exists or something went wrong */
+ XFree(prop);
+ return NULL;
+}
+
+/* Set up or take down buffering in a RAM image. */
+static int
+x_set_buffer(gx_device_X * xdev)
+{
+ /*
+ * We must use the stable memory here, since the existence of the
+ * buffer is independent of save/restore.
+ */
+ gs_memory_t *mem = gs_memory_stable(xdev->memory);
+ bool buffered = xdev->space_params.MaxBitmap > 0;
+ const gx_device_procs *procs;
+
+ setup:
+ if (buffered) {
+ /* We want to buffer. */
+ /* Check that we can set up a memory device. */
+ gx_device_memory *mdev = (gx_device_memory *)xdev->target;
+
+ if (mdev == 0 || mdev->color_info.depth != xdev->color_info.depth) {
+ const gx_device_memory *mdproto =
+ gdev_mem_device_for_bits(xdev->color_info.depth);
+
+ if (!mdproto) {
+ buffered = false;
+ goto setup;
+ }
+ if (mdev) {
+ /* Update the pointer we're about to overwrite. */
+ gx_device_set_target((gx_device_forward *)mdev, NULL);
+ } else {
+ mdev = gs_alloc_struct(mem, gx_device_memory,
+ &st_device_memory, "memory device");
+ if (mdev == 0) {
+ buffered = false;
+ goto setup;
+ }
+ }
+ /*
+ * We want to forward image drawing to the memory device.
+ * That requires making the memory device forward its color
+ * mapping operations back to the X device, creating a circular
+ * pointer structure. This is not a disaster, we just need to
+ * be aware that this is going on.
+ */
+ gs_make_mem_device(mdev, mdproto, mem, 0, (gx_device *)xdev);
+ gx_device_set_target((gx_device_forward *)xdev, (gx_device *)mdev);
+ xdev->is_buffered = true;
+ }
+ if (mdev->width != xdev->width || mdev->height != xdev->height) {
+ byte *buffer;
+ ulong space;
+
+ if (gdev_mem_data_size(mdev, xdev->width, xdev->height, &space) < 0 ||
+ space > xdev->space_params.MaxBitmap) {
+ buffered = false;
+ goto setup;
+ }
+ buffer =
+ (xdev->buffer ?
+ (byte *)gs_resize_object(mem, xdev->buffer, space, "buffer") :
+ gs_alloc_bytes(mem, space, "buffer"));
+ if (!buffer) {
+ buffered = false;
+ goto setup;
+ }
+ xdev->buffer_size = space;
+ xdev->buffer = buffer;
+ mdev->width = xdev->width;
+ mdev->height = xdev->height;
+ mdev->icc_struct = xdev->icc_struct;
+ rc_increment(xdev->icc_struct);
+ mdev->color_info = xdev->color_info;
+ mdev->base = xdev->buffer;
+ gdev_mem_open_scan_lines(mdev, xdev->height);
+ }
+ xdev->white = gx_device_white((gx_device *)xdev);
+ xdev->black = gx_device_black((gx_device *)xdev);
+ procs = &gs_bbox_device.procs;
+ } else {
+ /* Not buffering. Release the buffer and memory device. */
+ gs_free_object(mem, xdev->buffer, "buffer");
+ xdev->buffer = 0;
+ xdev->buffer_size = 0;
+ if (!xdev->is_buffered)
+ return 0;
+ gx_device_set_target((gx_device_forward *)xdev->target, NULL);
+ gx_device_set_target((gx_device_forward *)xdev, NULL);
+ xdev->is_buffered = false;
+ procs = &gs_x11_device.procs;
+ }
+ if (dev_proc(xdev, fill_rectangle) != procs->fill_rectangle) {
+#define COPY_PROC(p) set_dev_proc(xdev, p, procs->p)
+ COPY_PROC(fill_rectangle);
+ COPY_PROC(copy_mono);
+ COPY_PROC(copy_color);
+ COPY_PROC(copy_alpha);
+ COPY_PROC(fill_path);
+ COPY_PROC(stroke_path);
+ COPY_PROC(fill_mask);
+ COPY_PROC(fill_trapezoid);
+ COPY_PROC(fill_parallelogram);
+ COPY_PROC(fill_triangle);
+ COPY_PROC(draw_thin_line);
+ COPY_PROC(strip_tile_rectangle);
+ COPY_PROC(strip_copy_rop);
+ COPY_PROC(begin_typed_image);
+ COPY_PROC(text_begin);
+#undef COPY_PROC
+ if (xdev->is_buffered) {
+ check_device_separable((gx_device *)xdev);
+ gx_device_forward_fill_in_procs((gx_device_forward *)xdev);
+ xdev->box_procs = gdev_x_box_procs;
+ xdev->box_proc_data = xdev;
+ } else {
+ check_device_separable((gx_device *)xdev);
+ gx_device_fill_in_procs((gx_device *)xdev);
+ }
+ }
+ return 0;
+}
+
+/* Allocate the backing pixmap, if any, and clear the window. */
+void
+gdev_x_clear_window(gx_device_X * xdev)
+{
+ if (!xdev->ghostview) {
+ if (xdev->useBackingPixmap) {
+ if (xdev->bpixmap == 0) {
+ x_error_handler.oldhandler = XSetErrorHandler(x_catch_alloc);
+ x_error_handler.alloc_error = False;
+ xdev->bpixmap =
+ XCreatePixmap(xdev->dpy, xdev->win,
+ xdev->width, xdev->height,
+ xdev->vinfo->depth);
+ XSync(xdev->dpy, False); /* Force the error */
+ if (x_error_handler.alloc_error) {
+ xdev->useBackingPixmap = False;
+#ifdef DEBUG
+ emprintf(xdev->memory,
+ "Warning: Failed to allocated backing pixmap.\n");
+#endif
+ if (xdev->bpixmap) {
+ XFreePixmap(xdev->dpy, xdev->bpixmap);
+ xdev->bpixmap = None;
+ XSync(xdev->dpy, False); /* Force the error */
+ }
+ }
+ x_error_handler.oldhandler =
+ XSetErrorHandler(x_error_handler.oldhandler);
+ }
+ } else {
+ if (xdev->bpixmap != 0) {
+ XFreePixmap(xdev->dpy, xdev->bpixmap);
+ xdev->bpixmap = (Pixmap) 0;
+ }
+ }
+ }
+ x_set_buffer(xdev);
+ /* Clear the destination pixmap to avoid initializing with garbage. */
+ if (xdev->dest == (Pixmap) 0) {
+ xdev->dest = (xdev->bpixmap != (Pixmap) 0 ?
+ xdev->bpixmap : (Pixmap) xdev->win);
+ }
+ if (xdev->dest != (Pixmap) 0) {
+ XSetForeground(xdev->dpy, xdev->gc, xdev->background);
+ XFillRectangle(xdev->dpy, xdev->dest, xdev->gc,
+ 0, 0, xdev->width, xdev->height);
+ }
+
+ /* Clear the background pixmap to avoid initializing with garbage. */
+ if (xdev->bpixmap != (Pixmap) 0) {
+ if (!xdev->ghostview)
+ XSetWindowBackgroundPixmap(xdev->dpy, xdev->win, xdev->bpixmap);
+ XSetForeground(xdev->dpy, xdev->gc, xdev->background);
+ XFillRectangle(xdev->dpy, xdev->bpixmap, xdev->gc,
+ 0, 0, xdev->width, xdev->height);
+ }
+ /* Initialize foreground and background colors */
+ xdev->back_color = xdev->background;
+ XSetBackground(xdev->dpy, xdev->gc, xdev->background);
+ xdev->fore_color = xdev->background;
+ XSetForeground(xdev->dpy, xdev->gc, xdev->background);
+ xdev->colors_or = xdev->colors_and = xdev->background;
+}
+
+
+/* Clean up the instance after making a copy. */
+int
+gdev_x_finish_copydevice(gx_device *dev, const gx_device *from_dev)
+{
+ gx_device_X *xdev = (gx_device_X *) dev;
+
+ /* Mark the new instance as closed. */
+ xdev->is_open = false;
+
+ /* Clear all other pointers. */
+ xdev->target = 0;
+ xdev->buffer = 0;
+ xdev->dpy = 0;
+ xdev->scr = 0;
+ xdev->vinfo = 0;
+
+ /* Clear pointer-like parameters. */
+ xdev->win = (Window)None;
+ xdev->bpixmap = (Pixmap)0;
+ xdev->dest = (Pixmap)0;
+ xdev->cp.pixmap = (Pixmap)0;
+ xdev->ht.pixmap = (Pixmap)0;
+
+ /* Reset pointer-related parameters. */
+ xdev->is_buffered = false;
+ /* See x_set_buffer for why we do this: */
+ set_dev_proc(xdev, fill_rectangle,
+ dev_proc(&gs_x11_device, fill_rectangle));
+
+ return 0;
+}
+
+/* ---------------- Get/put parameters ---------------- */
+
+/* Get the device parameters. See below. */
+int
+gdev_x_get_params(gx_device * dev, gs_param_list * plist)
+{
+ gx_device_X *xdev = (gx_device_X *) dev;
+ int code = gx_default_get_params(dev, plist);
+ long id = (long)xdev->pwin;
+
+ if (code < 0 ||
+ (code = param_write_long(plist, "WindowID", &id)) < 0 ||
+ (code = param_write_bool(plist, ".IsPageDevice", &xdev->IsPageDevice)) < 0 ||
+ (code = param_write_int(plist, "MaxTempPixmap", &xdev->MaxTempPixmap)) < 0 ||
+ (code = param_write_int(plist, "MaxTempImage", &xdev->MaxTempImage)) < 0
+ )
+ DO_NOTHING;
+ return code;
+}
+
+/* Set the device parameters. We reimplement this so we can resize */
+/* the window and avoid closing and reopening the device, and to add */
+/* .IsPageDevice. */
+int
+gdev_x_put_params(gx_device * dev, gs_param_list * plist)
+{
+ gx_device_X *xdev = (gx_device_X *) dev;
+ /*
+ * Provide copies of values of parameters being set:
+ * is_open, width, height, HWResolution, IsPageDevice, Max*.
+ */
+ gx_device_X values;
+ int orig_MaxBitmap = xdev->space_params.MaxBitmap;
+
+ long pwin = (long)xdev->pwin;
+ bool save_is_page = xdev->IsPageDevice;
+ int ecode = 0, code;
+ bool clear_window = false;
+
+ values = *xdev;
+
+ /* Handle extra parameters */
+
+ ecode = param_put_long(plist, "WindowID", &pwin, ecode);
+ ecode = param_put_bool(plist, ".IsPageDevice", &values.IsPageDevice, ecode);
+ ecode = param_put_int(plist, "MaxTempPixmap", &values.MaxTempPixmap, ecode);
+ ecode = param_put_int(plist, "MaxTempImage", &values.MaxTempImage, ecode);
+
+ if (ecode < 0)
+ return ecode;
+
+ /* Unless we specified a new window ID, */
+ /* prevent gx_default_put_params from closing the device. */
+ if (pwin == (long)xdev->pwin)
+ dev->is_open = false;
+ xdev->IsPageDevice = values.IsPageDevice;
+ code = gx_default_put_params(dev, plist);
+ dev->is_open = values.is_open; /* saved value */
+ if (code < 0) { /* Undo setting of .IsPageDevice */
+ xdev->IsPageDevice = save_is_page;
+ return code;
+ }
+ if (pwin != (long)xdev->pwin) {
+ if (xdev->is_open)
+ gs_closedevice(dev);
+ xdev->pwin = (Window) pwin;
+ }
+ /* Restore the original page size if it was set by Ghostview */
+ /* This gives the Ghostview user control over the /setpage entry */
+ if (xdev->is_open && xdev->ghostview) {
+ dev->width = values.width;
+ dev->height = values.height;
+ dev->x_pixels_per_inch = values.x_pixels_per_inch;
+ dev->y_pixels_per_inch = values.y_pixels_per_inch;
+ dev->HWResolution[0] = values.HWResolution[0];
+ dev->HWResolution[1] = values.HWResolution[1];
+ dev->MediaSize[0] = values.MediaSize[0];
+ dev->MediaSize[1] = values.MediaSize[1];
+ }
+ /* If the device is open, resize the window. */
+ /* Don't do this if Ghostview is active. */
+ if (xdev->is_open && !xdev->ghostview &&
+ (dev->width != values.width || dev->height != values.height ||
+ dev->HWResolution[0] != values.HWResolution[0] ||
+ dev->HWResolution[1] != values.HWResolution[1])
+ ) {
+ int area_width = WidthOfScreen(xdev->scr), area_height = HeightOfScreen(xdev->scr);
+ int dw, dh;
+
+ /* Get work area */
+ x_get_work_area(xdev, &area_width, &area_height);
+
+ /* Preserve screen resolution */
+ dev->x_pixels_per_inch = values.x_pixels_per_inch;
+ dev->y_pixels_per_inch = values.y_pixels_per_inch;
+ dev->HWResolution[0] = values.HWResolution[0];
+ dev->HWResolution[1] = values.HWResolution[1];
+
+ /* Recompute window size using screen resolution and available work area size*/
+ /* pixels */
+ dev->width = min(dev->width, area_width);
+ dev->height = min(dev->height, area_height);
+
+ if (dev->width <= 0 || dev->height <= 0) {
+ emprintf3(dev->memory, "Requested pagesize %d x %d not supported by %s device\n",
+ dev->width, dev->height, dev->dname);
+ return_error(gs_error_rangecheck);
+ }
+
+ /* points */
+ dev->MediaSize[0] = (float)dev->width / xdev->x_pixels_per_inch * 72;
+ dev->MediaSize[1] = (float)dev->height / xdev->y_pixels_per_inch * 72;
+
+ dw = dev->width - values.width;
+ dh = dev->height - values.height;
+ if (dw || dh) {
+ XResizeWindow(xdev->dpy, xdev->win,
+ dev->width, dev->height);
+ if (xdev->bpixmap != (Pixmap) 0) {
+ XFreePixmap(xdev->dpy, xdev->bpixmap);
+ xdev->bpixmap = (Pixmap) 0;
+ }
+ xdev->dest = 0;
+ clear_window = true;
+ }
+ /* Attempt to update the initial matrix in a sensible way. */
+ /* The whole handling of the initial matrix is a hack! */
+ if (xdev->initial_matrix.xy == 0) {
+ if (xdev->initial_matrix.xx < 0) { /* 180 degree rotation */
+ xdev->initial_matrix.tx += dw;
+ } else { /* no rotation */
+ xdev->initial_matrix.ty += dh;
+ }
+ } else {
+ if (xdev->initial_matrix.xy < 0) { /* 90 degree rotation */
+ xdev->initial_matrix.tx += dh;
+ xdev->initial_matrix.ty += dw;
+ } else { /* 270 degree rotation */
+ }
+ }
+ }
+ xdev->MaxTempPixmap = values.MaxTempPixmap;
+ xdev->MaxTempImage = values.MaxTempImage;
+
+ if (clear_window || xdev->space_params.MaxBitmap != orig_MaxBitmap) {
+ if (xdev->is_open)
+ gdev_x_clear_window(xdev);
+ }
+ return 0;
+}
+
+/* ---------------- Closing/finalization ---------------- */
+/* Close the device. */
+int
+gdev_x_close(gx_device_X *xdev)
+{
+ if (xdev->ghostview)
+ gdev_x_send_event(xdev, xdev->DONE);
+ if (xdev->vinfo) {
+ XFree((char *)xdev->vinfo);
+ xdev->vinfo = NULL;
+ }
+ gdev_x_free_colors(xdev);
+ if (xdev->cmap != DefaultColormapOfScreen(xdev->scr))
+ XFreeColormap(xdev->dpy, xdev->cmap);
+ XCloseDisplay(xdev->dpy);
+ return 0;
+}
diff --git a/devices/gdevxres.c b/devices/gdevxres.c
new file mode 100644
index 000000000..6c4412102
--- /dev/null
+++ b/devices/gdevxres.c
@@ -0,0 +1,84 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* X Windows driver resource tables */
+#include "std.h" /* must precede any file that includes <sys/types.h> */
+#include "x_.h"
+#include "gstypes.h"
+#include "gsmemory.h"
+#include "gxdevice.h"
+#include "gdevx.h"
+
+/*
+ * We segregate these tables into their own file because the definition of
+ * the XtResource structure is botched -- it declares the strings as char *
+ * rather than const char * -- and so compiling the statically initialized
+ * tables with gcc -Wcast-qual produces dozens of bogus warnings.
+ *
+ * Astoundingly, not only does the X API specify these structures as not
+ * being const, the Xt implementation actually writes into them.
+ */
+
+XtResource gdev_x_resources[] = {
+
+/* (String) casts are here to suppress warnings about discarding `const' */
+#define RINIT(a,b,t,s,o,it,n)\
+ {(String)(a), (String)(b), (String)t, sizeof(s),\
+ XtOffsetOf(gx_device_X, o), (String)it, (n)}
+#define rpix(a,b,o,n)\
+ RINIT(a,b,XtRPixel,Pixel,o,XtRString,(XtPointer)(n))
+#define rdim(a,b,o,n)\
+ RINIT(a,b,XtRDimension,Dimension,o,XtRImmediate,(XtPointer)(n))
+#define rstr(a,b,o,n)\
+ RINIT(a,b,XtRString,String,o,XtRString,(char*)(n))
+#define rint(a,b,o,n)\
+ RINIT(a,b,XtRInt,int,o,XtRImmediate,(XtPointer)(n))
+#define rbool(a,b,o,n)\
+ RINIT(a,b,XtRBoolean,Boolean,o,XtRImmediate,(XtPointer)(n))
+#define rfloat(a,b,o,n)\
+ RINIT(a,b,XtRFloat,float,o,XtRString,(XtPointer)(n))
+
+ rpix(XtNbackground, XtCBackground, background, "XtDefaultBackground"),
+ rpix(XtNborderColor, XtCBorderColor, borderColor, "XtDefaultForeground"),
+ rdim(XtNborderWidth, XtCBorderWidth, borderWidth, 1),
+ rpix(XtNforeground, XtCForeground, foreground, "XtDefaultForeground"),
+ rstr(XtNgeometry, XtCGeometry, geometry, NULL),
+ rint("maxGrayRamp", "MaxGrayRamp", maxGrayRamp, 128),
+ rint("maxRGBRamp", "MaxRGBRamp", maxRGBRamp, 5),
+ rstr("palette", "Palette", palette, "Color"),
+
+ rbool("useBackingPixmap", "UseBackingPixmap", useBackingPixmap, True),
+ rbool("useXPutImage", "UseXPutImage", useXPutImage, True),
+ rbool("useXSetTile", "UseXSetTile", useXSetTile, True),
+ rfloat("xResolution", "Resolution", xResolution, "0.0"),
+ rfloat("yResolution", "Resolution", yResolution, "0.0"),
+
+#undef RINIT
+#undef rpix
+#undef rdim
+#undef rstr
+#undef rint
+#undef rbool
+#undef rfloat
+};
+
+const int gdev_x_resource_count = XtNumber(gdev_x_resources);
+
+String gdev_x_fallback_resources[] = {
+ (String) "Ghostscript*Background: white",
+ (String) "Ghostscript*Foreground: black",
+ NULL
+};
diff --git a/devices/gxfcopy.c b/devices/gxfcopy.c
new file mode 100644
index 000000000..334bcfee8
--- /dev/null
+++ b/devices/gxfcopy.c
@@ -0,0 +1,2602 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Font copying for high-level output */
+#include "memory_.h"
+#include "gx.h"
+#include <stdlib.h> /* for qsort */
+#include "gscencs.h"
+#include "gserrors.h"
+#include "gsline.h" /* for BuildChar */
+#include "gspaint.h" /* for BuildChar */
+#include "gspath.h" /* for gs_moveto in BuildChar */
+#include "gsstruct.h"
+#include "gsutil.h"
+#include "gschar.h"
+#include "stream.h"
+#include "gxfont.h"
+#include "gxfont1.h"
+#include "gxfont42.h"
+#include "gxchar.h"
+#include "gxfcid.h"
+#include "gxfcopy.h"
+#include "gxfcache.h" /* for gs_font_dir_s */
+#include "gxistate.h" /* for Type 1 glyph_outline */
+#include "gxtext.h" /* for BuildChar */
+#include "gxtype1.h" /* for Type 1 glyph_outline */
+#include "gzstate.h" /* for path for BuildChar */
+#include "gdevpsf.h"
+#include "smd5.h" /* Create MD5 hash of Subrs when comparing fonts */
+
+#define GLYPHS_SIZE_IS_PRIME 1 /* Old code = 0, new code = 1. */
+
+/* ================ Types and structures ================ */
+
+typedef struct gs_copied_glyph_s gs_copied_glyph_t;
+typedef struct gs_copied_font_data_s gs_copied_font_data_t;
+
+typedef struct gs_copied_font_procs_s {
+ int (*finish_copy_font)(gs_font *font, gs_font *copied);
+ int (*copy_glyph)(gs_font *font, gs_glyph glyph, gs_font *copied,
+ int options);
+ int (*add_encoding)(gs_font *copied, gs_char chr, gs_glyph glyph);
+ int (*named_glyph_slot)(gs_copied_font_data_t *cfdata, gs_glyph glyph,
+ gs_copied_glyph_t **pslot);
+ /* Font procedures */
+ font_proc_encode_char((*encode_char));
+ font_proc_glyph_info((*glyph_info));
+ font_proc_glyph_outline((*glyph_outline));
+} gs_copied_font_procs_t;
+
+/*
+ * We need to store the Subrs data for Type 1/2 and CIDFontType 0 fonts,
+ * and the GlobalSubrs data for all but Type 1.
+ */
+typedef struct gs_subr_info_s {
+ byte *data; /* Subr data */
+ int count;
+ uint *starts; /* [count+1] Subr[i] = data[starts[i]..starts[i+1]] */
+} gs_subr_info_t;
+
+/*
+ * The glyphs for copied fonts are stored explicitly in a table indexed by
+ * glyph number.
+ * For Type 1 fonts, the glyph numbers are parallel to the hashed name table.
+ * For TrueType fonts, the glyph number is the TrueType GID.
+ * For CIDFontType 0 fonts, the glyph number is the CID.
+ * For CIDFontType 2 fonts, the glyph number is the TrueType GID;
+ * a separate CIDMap array maps CIDs to GIDs.
+ */
+struct gs_copied_glyph_s {
+ gs_const_string gdata; /* vector data */
+#define HAS_DATA 1 /* entry is in use */
+ /* HAS_SBW* are only used for TT-based fonts */
+#define HAS_SBW0 2 /* has hmtx */
+#define HAS_SBW1 4 /* has vmtx */
+ byte used; /* non-zero iff this entry is in use */
+ /* (if not, gdata.{data,size} = 0) */
+ int order_index; /* Index for the ordered glyph set. */
+};
+/*
+ * We use a special GC descriptor to avoid large GC overhead.
+ */
+gs_private_st_composite(st_gs_copied_glyph_element, gs_copied_glyph_t,
+ "gs_copied_glyph_t[]", copied_glyph_element_enum_ptrs,
+ copied_glyph_element_reloc_ptrs);
+static ENUM_PTRS_WITH(copied_glyph_element_enum_ptrs, gs_copied_glyph_t *pcg)
+ if (index < size / (uint)sizeof(gs_copied_glyph_t))
+ return ENUM_CONST_STRING(&pcg[index].gdata);
+ return 0;
+ENUM_PTRS_END
+static RELOC_PTRS_WITH(copied_glyph_element_reloc_ptrs, gs_copied_glyph_t *pcg)
+{
+ uint count = size / (uint)sizeof(gs_copied_glyph_t);
+ gs_copied_glyph_t *p = pcg;
+
+ for (; count > 0; --count, ++p)
+ if (p->gdata.size > 0)
+ RELOC_CONST_STRING_VAR(p->gdata);
+}
+RELOC_PTRS_END
+
+/*
+ * Type 1 and TrueType fonts also have a 'names' table, parallel to the
+ * 'glyphs' table.
+ * For Type 1 fonts, this is a hash table; glyph numbers are assigned
+ * arbitrarily, according to the hashed placement of the names.
+ * For TrueType fonts, this is indexed by GID.
+ * The strings in this table are either those returned by the font's
+ * glyph_name procedure, which we assume are garbage-collected, or those
+ * associated with the known encodings, which we assume are immutable.
+ */
+typedef struct gs_copied_glyph_name_s {
+ gs_glyph glyph; /* key (for comparison and glyph_name only) */
+ gs_const_string str; /* glyph name */
+} gs_copied_glyph_name_t;
+/*
+ * We use the same special GC descriptor as above for 'names'.
+ */
+gs_private_st_composite(st_gs_copied_glyph_name_element,
+ gs_copied_glyph_name_t,
+ "gs_copied_glyph_name_t[]",
+ copied_glyph_name_enum_ptrs,
+ copied_glyph_name_reloc_ptrs);
+static ENUM_PTRS_WITH(copied_glyph_name_enum_ptrs, gs_copied_glyph_name_t *pcgn)
+ if (index < size / (uint)sizeof(gs_copied_glyph_name_t)) {
+ const gs_copied_glyph_name_t *const p = &pcgn[index];
+
+ return (p->str.size == 0 ||
+ gs_is_c_glyph_name(p->str.data, p->str.size) ?
+ ENUM_CONST_STRING2(0, 0) :
+ ENUM_CONST_STRING(&p->str));
+ }
+ return 0;
+ /* We should mark glyph name here, but we have no access to
+ the gs_font_dir instance. Will mark in gs_copied_font_data_enum_ptrs.
+ */
+ENUM_PTRS_END
+static RELOC_PTRS_WITH(copied_glyph_name_reloc_ptrs, gs_copied_glyph_name_t *pcgn)
+{
+ uint count = size / (uint)sizeof(gs_copied_glyph_name_t);
+ gs_copied_glyph_name_t *p = pcgn;
+
+ for (; count > 0; --count, ++p)
+ if (p->str.size > 0 && !gs_is_c_glyph_name(p->str.data, p->str.size))
+ RELOC_CONST_STRING_VAR(p->str);
+}
+RELOC_PTRS_END
+
+/*
+ * To accommodate glyphs with multiple names, there is an additional
+ * 'extra_names' table. Since this is rare, this table uses linear search.
+ */
+typedef struct gs_copied_glyph_extra_name_s gs_copied_glyph_extra_name_t;
+struct gs_copied_glyph_extra_name_s {
+ gs_copied_glyph_name_t name;
+ uint gid; /* index in glyphs table */
+ gs_copied_glyph_extra_name_t *next;
+};
+BASIC_PTRS(gs_copied_glyph_extra_name_ptrs) {
+ GC_STRING_ELT(gs_copied_glyph_extra_name_t, name.str),
+ GC_OBJ_ELT(gs_copied_glyph_extra_name_t, next)
+};
+gs_private_st_basic(st_gs_copied_glyph_extra_name,
+ gs_copied_glyph_extra_name_t,
+ "gs_copied_glyph_extra_name_t",
+ gs_copied_glyph_extra_name_ptrs,
+ gs_copied_glyph_extra_name_data);
+
+/*
+ * The client_data of copied fonts points to an instance of
+ * gs_copied_font_data_t.
+ */
+struct gs_copied_font_data_s {
+ gs_font_info_t info; /* from the original font, must be first */
+ const gs_copied_font_procs_t *procs;
+ gs_copied_glyph_t *glyphs; /* [glyphs_size] */
+ uint glyphs_size; /* (a power of 2 or a prime number for Type 1/2) */
+ uint num_glyphs; /* The number of glyphs copied. */
+ gs_glyph notdef; /* CID 0 or .notdef glyph */
+ /*
+ * We don't use a union for the rest of the data, because some of the
+ * cases overlap and it's just not worth the trouble.
+ */
+ gs_copied_glyph_name_t *names; /* (Type 1/2, TrueType) [glyphs_size] */
+ gs_copied_glyph_extra_name_t *extra_names; /* (TrueType) */
+ byte *data; /* (TrueType and CID fonts) copied data */
+ uint data_size; /* (TrueType and CID fonts) */
+ gs_glyph *Encoding; /* (Type 1/2 and Type 42) [256] */
+ ushort *CIDMap; /* (CIDFontType 2) [CIDCount] */
+ gs_subr_info_t subrs; /* (Type 1/2 and CIDFontType 0) */
+ gs_subr_info_t global_subrs; /* (Type 2 and CIDFontType 0) */
+ gs_font_cid0 *parent; /* (Type 1 subfont) => parent CIDFontType 0 */
+ gs_font_dir *dir;
+ bool ordered;
+};
+extern_st(st_gs_font_info);
+static
+ENUM_PTRS_WITH(gs_copied_font_data_enum_ptrs, gs_copied_font_data_t *cfdata)
+ if (index == 12) {
+ gs_copied_glyph_name_t *names = cfdata->names;
+ gs_copied_glyph_extra_name_t *en = cfdata->extra_names;
+ int i;
+
+ if (names != NULL)
+ for (i = 0; i < cfdata->glyphs_size; ++i)
+ if (names[i].glyph < gs_c_min_std_encoding_glyph)
+ cfdata->dir->ccache.mark_glyph(mem, names[i].glyph, NULL);
+ for (; en != NULL; en = en->next)
+ if (en->name.glyph < gs_c_min_std_encoding_glyph)
+ cfdata->dir->ccache.mark_glyph(mem, en->name.glyph, NULL);
+ }
+ return ENUM_USING(st_gs_font_info, &cfdata->info, sizeof(gs_font_info_t), index - 12);
+ ENUM_PTR3(0, gs_copied_font_data_t, glyphs, names, extra_names);
+ ENUM_PTR3(3, gs_copied_font_data_t, data, Encoding, CIDMap);
+ ENUM_PTR3(6, gs_copied_font_data_t, subrs.data, subrs.starts, global_subrs.data);
+ ENUM_PTR3(9, gs_copied_font_data_t, global_subrs.starts, parent, dir);
+ENUM_PTRS_END
+
+static RELOC_PTRS_WITH(gs_copied_font_data_reloc_ptrs, gs_copied_font_data_t *cfdata)
+{
+ RELOC_PTR3(gs_copied_font_data_t, glyphs, names, extra_names);
+ RELOC_PTR3(gs_copied_font_data_t, data, Encoding, CIDMap);
+ RELOC_PTR3(gs_copied_font_data_t, subrs.data, subrs.starts, global_subrs.data);
+ RELOC_PTR3(gs_copied_font_data_t, global_subrs.starts, parent, dir);
+ RELOC_USING(st_gs_font_info, &cfdata->info, sizeof(gs_font_info_t));
+}
+RELOC_PTRS_END
+
+gs_private_st_composite(st_gs_copied_font_data, gs_copied_font_data_t, "gs_copied_font_data_t",\
+ gs_copied_font_data_enum_ptrs, gs_copied_font_data_reloc_ptrs);
+
+static inline gs_copied_font_data_t *
+cf_data(const gs_font *font)
+{
+ return (gs_copied_font_data_t *)font->client_data;
+}
+
+/* ================ Procedures ================ */
+
+/* ---------------- Private utilities ---------------- */
+
+/* Copy a string. Return 0 or gs_error_VMerror. */
+static int
+copy_string(gs_memory_t *mem, gs_const_string *pstr, client_name_t cname)
+{
+ const byte *data = pstr->data;
+ uint size = pstr->size;
+ byte *str;
+
+ if (data == 0)
+ return 0; /* empty string */
+ str = gs_alloc_string(mem, size, cname);
+ pstr->data = str;
+ if (str == 0)
+ return_error(gs_error_VMerror);
+ memcpy(str, data, size);
+ return 0;
+}
+
+/* Free a copied string. */
+static void
+uncopy_string(gs_memory_t *mem, gs_const_string *pstr, client_name_t cname)
+{
+ if (pstr->data)
+ gs_free_const_string(mem, pstr->data, pstr->size, cname);
+}
+
+/*
+ * Allocate an Encoding for a Type 1 or Type 42 font.
+ */
+static int
+copied_Encoding_alloc(gs_font *copied)
+{
+ gs_copied_font_data_t *const cfdata = cf_data(copied);
+ gs_glyph *Encoding = (gs_glyph *)
+ gs_alloc_byte_array(copied->memory, 256, sizeof(*cfdata->Encoding),
+ "copy_font_type1(Encoding)");
+ int i;
+
+ if (Encoding == 0)
+ return_error(gs_error_VMerror);
+ for (i = 0; i < 256; ++i)
+ Encoding[i] = GS_NO_GLYPH;
+ cfdata->Encoding = Encoding;
+ return 0;
+}
+
+/*
+ * Allocate and set up data copied from a TrueType or CID font.
+ * stell(*s) + extra is the length of the data.
+ */
+static int
+copied_data_alloc(gs_font *copied, stream *s, uint extra, int code)
+{
+ gs_copied_font_data_t *const cfdata = cf_data(copied);
+ uint len = stell(s);
+ byte *fdata;
+
+ if (code < 0)
+ return code;
+ fdata = gs_alloc_bytes(copied->memory, len + extra, "copied_data_alloc");
+ if (fdata == 0)
+ return_error(gs_error_VMerror);
+ s_init(s, copied->memory);
+ swrite_string(s, fdata, len);
+ cfdata->data = fdata;
+ cfdata->data_size = len + extra;
+ return 0;
+}
+
+/*
+ * Copy Subrs or GSubrs from a font.
+ */
+static int
+copy_subrs(gs_font_type1 *pfont, bool global, gs_subr_info_t *psi,
+ gs_memory_t *mem)
+{
+ int i, code;
+ uint size;
+ gs_glyph_data_t gdata;
+ byte *data;
+ uint *starts;
+
+ gdata.memory = pfont->memory;
+ /* Scan the font to determine the size of the subrs. */
+ for (i = 0, size = 0;
+ (code = pfont->data.procs.subr_data(pfont, i, global, &gdata)) !=
+ gs_error_rangecheck;
+ ++i) {
+ if (code >= 0) {
+ size += gdata.bits.size;
+ gs_glyph_data_free(&gdata, "copy_subrs");
+ }
+ }
+ if (size == 0)
+ data = 0, starts = 0, i = 0;
+ else {
+ /* Allocate the copy. */
+ data = gs_alloc_bytes(mem, size, "copy_subrs(data)");
+ starts = (uint *)gs_alloc_byte_array(mem, i + 1, sizeof(*starts),
+ "copy_subrs(starts)");
+ if (data == 0 || starts == 0) {
+ gs_free_object(mem, starts, "copy_subrs(starts)");
+ gs_free_object(mem, data, "copy_subrs(data)");
+ return_error(gs_error_VMerror);
+ }
+
+ /* Copy the data. */
+ for (i = 0, size = 0;
+ (code = pfont->data.procs.subr_data(pfont, i, global, &gdata)) !=
+ gs_error_rangecheck;
+ ++i) {
+ starts[i] = size;
+ if (code >= 0) {
+ memcpy(data + size, gdata.bits.data, gdata.bits.size);
+ size += gdata.bits.size;
+ gs_glyph_data_free(&gdata, "copy_subrs");
+ }
+ }
+ starts[i] = size;
+ }
+
+ psi->data = data;
+ psi->starts = starts;
+ psi->count = i;
+ return 0;
+}
+
+/*
+ * Return a pointer to the definition of a copied glyph, accessed either by
+ * name or by glyph number. If the glyph is out of range, return
+ * gs_error_rangecheck; if the glyph is in range but undefined, store a
+ * pointer to the slot where it would be added, which will have gdata.data
+ * == 0, and return gs_error_undefined; if the glyph is defined, store the
+ * pointer and return 0.
+ */
+static int
+copied_glyph_slot(gs_copied_font_data_t *cfdata, gs_glyph glyph,
+ gs_copied_glyph_t **pslot)
+{
+ uint gsize = cfdata->glyphs_size;
+
+ *pslot = 0;
+ if (glyph >= GS_MIN_GLYPH_INDEX) {
+ /* CIDFontType 2 uses glyph indices for slots. */
+ if (glyph - GS_MIN_GLYPH_INDEX >= gsize)
+ return_error(gs_error_rangecheck);
+ *pslot = &cfdata->glyphs[glyph - GS_MIN_GLYPH_INDEX];
+ } else if (glyph >= GS_MIN_CID_GLYPH) {
+ /* CIDFontType 0 uses CIDS for slots. */
+ if (glyph - GS_MIN_CID_GLYPH >= gsize)
+ return_error(gs_error_rangecheck);
+ *pslot = &cfdata->glyphs[glyph - GS_MIN_CID_GLYPH];
+ } else if (cfdata->names == 0)
+ return_error(gs_error_rangecheck);
+ else {
+ int code = cfdata->procs->named_glyph_slot(cfdata, glyph, pslot);
+
+ if (code < 0)
+ return code;
+ }
+ if (!(*pslot)->used)
+ return_error(gs_error_undefined);
+ return 0;
+}
+static int
+named_glyph_slot_none(gs_copied_font_data_t *cfdata, gs_glyph glyph,
+ gs_copied_glyph_t **pslot)
+{
+ return_error(gs_error_rangecheck);
+}
+static int
+named_glyph_slot_hashed(gs_copied_font_data_t *cfdata, gs_glyph glyph,
+ gs_copied_glyph_t **pslot)
+{
+ uint gsize = cfdata->glyphs_size;
+ gs_copied_glyph_name_t *names = cfdata->names;
+ uint hash = (uint)glyph % gsize;
+ /*
+ * gsize is either a prime number or a power of 2.
+ * If it is prime, any positive reprobe below gsize guarantees that we
+ * will touch every slot.
+ * If it is a power of 2, any odd reprobe guarantees that we
+ * will touch every slot.
+ */
+ uint hash2 = ((uint)glyph / gsize * 2 + 1) % gsize;
+ uint tries = gsize;
+
+ while (names[hash].str.data != 0 && names[hash].glyph != glyph) {
+ hash = (hash + hash2) % gsize;
+ if (!tries)
+ return gs_error_undefined;
+ tries--;
+ }
+ *pslot = &cfdata->glyphs[hash];
+ return 0;
+}
+static int
+named_glyph_slot_linear(gs_copied_font_data_t *cfdata, gs_glyph glyph,
+ gs_copied_glyph_t **pslot)
+{
+ {
+ gs_copied_glyph_name_t *names = cfdata->names;
+ int i;
+
+ for (i = 0; i < cfdata->glyphs_size; ++i)
+ if (names[i].glyph == glyph) {
+ *pslot = &cfdata->glyphs[i];
+ return 0;
+ }
+ }
+ /* This might be a glyph with multiple names. Search extra_names. */
+ {
+ gs_copied_glyph_extra_name_t *extra_name = cfdata->extra_names;
+
+ for (; extra_name != 0; extra_name = extra_name->next)
+ if (extra_name->name.glyph == glyph) {
+ *pslot = &cfdata->glyphs[extra_name->gid];
+ return 0;
+ }
+ }
+ return_error(gs_error_rangecheck);
+}
+
+/*
+ * Add glyph data to the glyph table. This handles copying the vector
+ * data, detecting attempted redefinitions, and freeing temporary glyph
+ * data. The glyph must be an integer, an index in the glyph table.
+ * Return 1 if the glyph was already defined, 0 if newly added (or an
+ * error per options).
+ */
+static int
+copy_glyph_data(gs_font *font, gs_glyph glyph, gs_font *copied, int options,
+ gs_glyph_data_t *pgdata, const byte *prefix, int prefix_bytes)
+{
+ gs_copied_font_data_t *const cfdata = cf_data(copied);
+ uint size = pgdata->bits.size;
+ gs_copied_glyph_t *pcg = 0;
+ int code = copied_glyph_slot(cfdata, glyph, &pcg);
+
+ if (cfdata->ordered)
+ return_error(gs_error_unregistered); /* Must not happen. */
+ switch (code) {
+ case 0: /* already defined */
+ if ((options & COPY_GLYPH_NO_OLD) ||
+ pcg->gdata.size != prefix_bytes + size ||
+ memcmp(pcg->gdata.data, prefix, prefix_bytes) ||
+ memcmp(pcg->gdata.data + prefix_bytes,
+ pgdata->bits.data, size)
+ )
+ code = gs_note_error(gs_error_invalidaccess);
+ else
+ code = 1;
+ break;
+ case gs_error_undefined:
+ if (options & COPY_GLYPH_NO_NEW)
+ code = gs_note_error(gs_error_undefined);
+ else if (pcg == NULL)
+ code = gs_note_error(gs_error_undefined);
+ else {
+ uint str_size = prefix_bytes + size;
+ byte *str = gs_alloc_string(copied->memory, str_size,
+ "copy_glyph_data(data)");
+
+ if (str == 0)
+ code = gs_note_error(gs_error_VMerror);
+ else {
+ if (prefix_bytes)
+ memcpy(str, prefix, prefix_bytes);
+ memcpy(str + prefix_bytes, pgdata->bits.data, size);
+ pcg->gdata.data = str;
+ pcg->gdata.size = str_size;
+ pcg->used = HAS_DATA;
+ pcg->order_index = -1;
+ code = 0;
+ cfdata->num_glyphs++;
+ }
+ }
+ default:
+ break;
+ }
+ gs_glyph_data_free(pgdata, "copy_glyph_data");
+ return code;
+}
+
+/*
+ * Copy a glyph name into the names table.
+ */
+static int
+copy_glyph_name(gs_font *font, gs_glyph glyph, gs_font *copied,
+ gs_glyph copied_glyph)
+{
+ gs_glyph known_glyph;
+ gs_copied_font_data_t *const cfdata = cf_data(copied);
+ gs_copied_glyph_t *pcg;
+ int code = copied_glyph_slot(cfdata, copied_glyph, &pcg);
+ gs_copied_glyph_name_t *pcgn;
+ gs_const_string str;
+
+ if (cfdata->ordered)
+ return_error(gs_error_unregistered); /* Must not happen. */
+ if (code < 0 ||
+ (code = font->procs.glyph_name(font, glyph, &str)) < 0
+ )
+ return code;
+ /* Try to share a permanently allocated known glyph name. */
+ if ((known_glyph = gs_c_name_glyph(str.data, str.size)) != GS_NO_GLYPH)
+ gs_c_glyph_name(known_glyph, &str);
+ else if ((code = copy_string(copied->memory, &str, "copy_glyph_name")) < 0)
+ return code;
+ pcgn = cfdata->names + (pcg - cfdata->glyphs);
+ if (pcgn->glyph != GS_NO_GLYPH &&
+ (pcgn->str.size != str.size ||
+ memcmp(pcgn->str.data, str.data, str.size))
+ ) {
+ /* This is a glyph with multiple names. Add an extra_name entry. */
+ gs_copied_glyph_extra_name_t *extra_name =
+ gs_alloc_struct(copied->memory, gs_copied_glyph_extra_name_t,
+ &st_gs_copied_glyph_extra_name,
+ "copy_glyph_name(extra_name)");
+
+ if (extra_name == 0)
+ return_error(gs_error_VMerror);
+ extra_name->next = cfdata->extra_names;
+ extra_name->gid = pcg - cfdata->glyphs;
+ cfdata->extra_names = extra_name;
+ pcgn = &extra_name->name;
+ }
+ pcgn->glyph = glyph;
+ pcgn->str = str;
+ return 0;
+}
+
+/*
+ * Find the .notdef glyph in a font.
+ */
+static gs_glyph
+find_notdef(gs_font_base *font)
+{
+ int index = 0;
+ gs_glyph glyph;
+
+ while (font->procs.enumerate_glyph((gs_font *)font, &index,
+ GLYPH_SPACE_NAME, &glyph),
+ index != 0)
+ if (gs_font_glyph_is_notdef(font, glyph))
+ return glyph;
+ return GS_NO_GLYPH; /* best we can do */
+}
+
+/*
+ * Add an Encoding entry to a character-indexed font (Type 1/2/42).
+ */
+static int
+copied_char_add_encoding(gs_font *copied, gs_char chr, gs_glyph glyph)
+{
+ gs_copied_font_data_t *const cfdata = cf_data(copied);
+ gs_glyph *Encoding = cfdata->Encoding;
+ gs_copied_glyph_t *pslot;
+ int code;
+
+ if (cfdata->ordered)
+ return_error(gs_error_unregistered); /* Must not happen. */
+ if (Encoding == 0)
+ return_error(gs_error_invalidaccess);
+ if (chr >= 256 || glyph >= GS_MIN_CID_GLYPH)
+ return_error(gs_error_rangecheck);
+ code = copied_glyph_slot(cfdata, glyph, &pslot);
+ if (code < 0)
+ return code;
+ if (Encoding[chr] != glyph && Encoding[chr] != GS_NO_GLYPH)
+ return_error(gs_error_invalidaccess);
+ Encoding[chr] = glyph;
+ return 0;
+}
+
+/* Don't allow adding an Encoding entry. */
+static int
+copied_no_add_encoding(gs_font *copied, gs_char chr, gs_glyph glyph)
+{
+ return_error(gs_error_invalidaccess);
+}
+
+/* ---------------- Font procedures ---------------- */
+
+static int
+copied_font_info(gs_font *font, const gs_point *pscale, int members,
+ gs_font_info_t *info)
+{
+ if (pscale != 0)
+ return_error(gs_error_rangecheck);
+ *info = cf_data(font)->info;
+ return 0;
+}
+
+static gs_glyph
+copied_encode_char(gs_font *copied, gs_char chr, gs_glyph_space_t glyph_space)
+{
+ gs_copied_font_data_t *const cfdata = cf_data(copied);
+ const gs_glyph *Encoding = cfdata->Encoding;
+
+ if (chr >= 256 || Encoding == 0)
+ return GS_NO_GLYPH;
+ return Encoding[chr];
+}
+
+static int
+copied_enumerate_glyph(gs_font *font, int *pindex,
+ gs_glyph_space_t glyph_space, gs_glyph *pglyph)
+{
+ gs_copied_font_data_t *const cfdata = cf_data(font);
+
+ if (cfdata->ordered) {
+ if (*pindex >= cfdata->num_glyphs)
+ *pindex = 0;
+ else {
+ int i = cfdata->glyphs[*pindex].order_index;
+
+ *pglyph = cfdata->names[i].glyph;
+ ++(*pindex);
+ }
+ return 0;
+ }
+ for (; *pindex < cfdata->glyphs_size; ++*pindex)
+ if (cfdata->glyphs[*pindex].used) {
+ *pglyph =
+ (glyph_space == GLYPH_SPACE_NAME && cfdata->names != 0 ?
+ cfdata->names[*pindex].glyph :
+ /* CIDFontType 0 uses CIDS as slot indices; CIDFontType 2 uses GIDs. */
+ *pindex + (glyph_space == GLYPH_SPACE_NAME
+ ? GS_MIN_CID_GLYPH : GS_MIN_GLYPH_INDEX));
+ ++(*pindex);
+ return 0;
+ }
+ *pindex = 0;
+ return 0;
+}
+
+static int
+copied_glyph_name(gs_font *font, gs_glyph glyph, gs_const_string *pstr)
+{
+ gs_copied_font_data_t *const cfdata = cf_data(font);
+ gs_copied_glyph_t *pcg;
+
+ if (glyph >= GS_MIN_CID_GLYPH)
+ return_error(gs_error_rangecheck);
+ if (copied_glyph_slot(cfdata, glyph, &pcg) < 0)
+ return_error(gs_error_undefined);
+ *pstr = cfdata->names[pcg - cfdata->glyphs].str;
+ return 0;
+}
+
+static int
+copied_build_char(gs_show_enum *pte, gs_state *pgs, gs_font *font,
+ gs_char chr, gs_glyph glyph)
+{
+ int wmode = font->WMode;
+ int code;
+ gs_glyph_info_t info;
+ double wxy[6];
+ double sbw_stub[4]; /* Currently glyph_outline retrieves sbw only with type 1,2,9 fonts. */
+
+ if (glyph == GS_NO_GLYPH) {
+ glyph = font->procs.encode_char(font, chr, GLYPH_SPACE_INDEX);
+ if (glyph == GS_NO_GLYPH)
+ glyph = cf_data(font)->notdef;
+ }
+ /*
+ * Type 1/2 outlines don't require a current point, but TrueType
+ * outlines do. We might want to fix this someday....
+ */
+ if ((code = gs_moveto(pgs, 0.0, 0.0)) < 0 ||
+ (code = font->procs.glyph_info(font, glyph, NULL,
+ (GLYPH_INFO_WIDTH << wmode) |
+ GLYPH_INFO_BBOX |
+ GLYPH_INFO_OUTLINE_WIDTHS,
+ &info)) < 0
+ )
+ return code;
+ wxy[0] = info.width[wmode].x;
+ wxy[1] = info.width[wmode].y;
+ wxy[2] = info.bbox.p.x;
+ wxy[3] = info.bbox.p.y;
+ wxy[4] = info.bbox.q.x;
+ wxy[5] = info.bbox.q.y;
+ if ((code = gs_setcachedevice_double(pte, pte->pgs, wxy)) < 0 ||
+ (code = font->procs.glyph_outline(font, wmode, glyph, &ctm_only(pgs),
+ pgs->path, sbw_stub)) < 0
+ )
+ return code;
+ if (font->PaintType != 0) {
+ gs_setlinewidth(pgs, font->StrokeWidth);
+ return gs_stroke(pgs);
+ } else {
+ return gs_fill(pgs);
+ }
+}
+
+static inline bool
+compare_arrays(const float *v0, int l0, const float *v1, int l1)
+{
+ if (l0 != l1)
+ return false;
+ if (memcmp(v0, v1, l0 * sizeof(v0[0])))
+ return false;
+ return true;
+}
+
+#define compare_tables(a, b) compare_arrays(a.values, a.count, b.values, b.count)
+
+static int
+compare_glyphs(const gs_font *cfont, const gs_font *ofont, gs_glyph *glyphs,
+ int num_glyphs, int glyphs_step, int level)
+{
+ /*
+ * Checking widths because we can synthesize fonts from random fonts
+ * having same FontName and FontType.
+ * We must request width explicitely because Type 42 stores widths
+ * separately from outline data. We could skip it for Type 1, which doesn't.
+ * We don't care of Metrics, Metrics2 because copied font never has them.
+ */
+ int i, WMode = ofont->WMode;
+ int members = (GLYPH_INFO_WIDTH0 << WMode) | GLYPH_INFO_OUTLINE_WIDTHS | GLYPH_INFO_NUM_PIECES;
+ gs_matrix mat;
+ gs_copied_font_data_t *const cfdata = cf_data(cfont);
+ int num_new_glyphs = 0;
+
+ gs_make_identity(&mat);
+ for (i = 0; i < num_glyphs; i++) {
+ gs_glyph glyph = *(gs_glyph *)((byte *)glyphs + i * glyphs_step);
+ gs_glyph pieces0[40], *pieces = pieces0;
+ gs_glyph_info_t info0, info1;
+ int code0 = ofont->procs.glyph_info((gs_font *)ofont, glyph, &mat, members, &info0);
+ int code1 = cfont->procs.glyph_info((gs_font *)cfont, glyph, &mat, members, &info1);
+ int code2, code;
+
+ if (code0 == gs_error_undefined)
+ continue;
+ if (code1 == gs_error_undefined) {
+ num_new_glyphs++;
+ if (num_new_glyphs > cfdata->glyphs_size - cfdata->num_glyphs)
+ return 0;
+ continue;
+ }
+ if (code0 < 0)
+ return code0;
+ if (code1 < 0)
+ return code1;
+ if (info0.num_pieces != info1.num_pieces)
+ return 0;
+ if (info0.width[WMode].x != info1.width[WMode].x ||
+ info0.width[WMode].y != info1.width[WMode].y)
+ return 0;
+ if (WMode && (info0.v.x != info1.v.x || info0.v.y != info1.v.y))
+ return 0;
+ if (info0.num_pieces > 0) {
+ if(level > 5)
+ return_error(gs_error_rangecheck); /* abnormal glyph recursion */
+ if (info0.num_pieces > countof(pieces0) / 2) {
+ pieces = (gs_glyph *)gs_alloc_bytes(cfont->memory,
+ sizeof(gs_glyph) * info0.num_pieces * 2, "compare_glyphs");
+ if (pieces == 0)
+ return_error(gs_error_VMerror);
+ }
+ info0.pieces = pieces;
+ info1.pieces = pieces + info0.num_pieces;
+ code0 = ofont->procs.glyph_info((gs_font *)ofont, glyph, &mat,
+ GLYPH_INFO_PIECES, &info0);
+ code1 = cfont->procs.glyph_info((gs_font *)cfont, glyph, &mat,
+ GLYPH_INFO_PIECES, &info1);
+ if (code0 >= 0 && code1 >= 0) {
+ code2 = memcmp(info0.pieces, info1.pieces, info0.num_pieces * sizeof(*pieces));
+ if (!code2)
+ code = compare_glyphs(cfont, ofont, pieces, info0.num_pieces, glyphs_step, level + 1);
+ else
+ code = 0; /* Quiet compiler. */
+ } else
+ code2 = code = 0;
+ if (pieces != pieces0)
+ gs_free_object(cfont->memory, pieces, "compare_glyphs");
+ if (code0 == gs_error_undefined)
+ continue;
+ if (code1 == gs_error_undefined) {
+ num_new_glyphs++;
+ if (num_new_glyphs > cfdata->glyphs_size - cfdata->num_glyphs)
+ return 0;
+ continue;
+ }
+ if (code0 < 0)
+ return code0;
+ if (code1 < 0)
+ return code1;
+ if (code2 || code == 0) {
+ return 0;
+ }
+ } else {
+ gs_glyph_data_t gdata0, gdata1;
+
+ switch(cfont->FontType) {
+ case ft_encrypted:
+ case ft_encrypted2: {
+ gs_font_type1 *font0 = (gs_font_type1 *)cfont;
+ gs_font_type1 *font1 = (gs_font_type1 *)ofont;
+
+ gdata0.memory = font0->memory;
+ gdata1.memory = font1->memory;
+ code0 = font0->data.procs.glyph_data(font0, glyph, &gdata0);
+ code1 = font1->data.procs.glyph_data(font1, glyph, &gdata1);
+ break;
+ }
+ case ft_TrueType:
+ case ft_CID_TrueType: {
+ gs_font_type42 *font0 = (gs_font_type42 *)cfont;
+ gs_font_type42 *font1 = (gs_font_type42 *)ofont;
+ uint glyph_index0 = font0->data.get_glyph_index(font0, glyph);
+ uint glyph_index1 = font1->data.get_glyph_index(font1, glyph);
+
+ gdata0.memory = font0->memory;
+ gdata1.memory = font1->memory;
+ code0 = font0->data.get_outline(font0, glyph_index0, &gdata0);
+ code1 = font1->data.get_outline(font1, glyph_index1, &gdata1);
+ break;
+ }
+ case ft_CID_encrypted: {
+ gs_font_cid0 *font0 = (gs_font_cid0 *)cfont;
+ gs_font_cid0 *font1 = (gs_font_cid0 *)ofont;
+ int fidx0, fidx1;
+
+ gdata0.memory = font0->memory;
+ gdata1.memory = font1->memory;
+ code0 = font0->cidata.glyph_data((gs_font_base *)font0, glyph, &gdata0, &fidx0);
+ code1 = font1->cidata.glyph_data((gs_font_base *)font1, glyph, &gdata1, &fidx1);
+ break;
+ }
+ default:
+ return_error(gs_error_unregistered); /* unimplemented */
+ }
+ if (code0 < 0) {
+ if (code1 >= 0)
+ gs_glyph_data_free(&gdata1, "compare_glyphs");
+ return code0;
+ }
+ if (code1 < 0) {
+ if (code0 >= 0)
+ gs_glyph_data_free(&gdata0, "compare_glyphs");
+ return code1;
+ }
+ if (gdata0.bits.size != gdata1.bits.size)
+ return 0;
+ if (memcmp(gdata0.bits.data, gdata0.bits.data, gdata0.bits.size))
+ return 0;
+ gs_glyph_data_free(&gdata0, "compare_glyphs");
+ gs_glyph_data_free(&gdata1, "compare_glyphs");
+ }
+ }
+ return 1;
+}
+
+/* ---------------- Individual FontTypes ---------------- */
+
+/* ------ Type 1 ------ */
+
+static int
+copied_type1_glyph_data(gs_font_type1 * pfont, gs_glyph glyph,
+ gs_glyph_data_t *pgd)
+{
+ gs_copied_font_data_t *const cfdata = cf_data((gs_font *)pfont);
+ gs_copied_glyph_t *pslot;
+ int code = copied_glyph_slot(cfdata, glyph, &pslot);
+
+ if (code < 0)
+ return code;
+ gs_glyph_data_from_string(pgd, pslot->gdata.data, pslot->gdata.size,
+ NULL);
+ return 0;
+}
+
+static int
+copied_type1_subr_data(gs_font_type1 * pfont, int subr_num, bool global,
+ gs_glyph_data_t *pgd)
+{
+ gs_copied_font_data_t *const cfdata = cf_data((gs_font *)pfont);
+ const gs_subr_info_t *psi =
+ (global ? &cfdata->global_subrs : &cfdata->subrs);
+
+ if (subr_num < 0 || subr_num >= psi->count)
+ return_error(gs_error_rangecheck);
+ gs_glyph_data_from_string(pgd, psi->data + psi->starts[subr_num],
+ psi->starts[subr_num + 1] -
+ psi->starts[subr_num],
+ NULL);
+ return 0;
+}
+
+static int
+copied_type1_seac_data(gs_font_type1 * pfont, int ccode,
+ gs_glyph * pglyph, gs_const_string *gstr, gs_glyph_data_t *pgd)
+{
+ /*
+ * This can only be invoked if the components have already been
+ * copied to their proper positions, so it is simple.
+ */
+ gs_glyph glyph = gs_c_known_encode((gs_char)ccode, ENCODING_INDEX_STANDARD);
+ gs_glyph glyph1;
+ int code;
+
+ if (glyph == GS_NO_GLYPH)
+ return_error(gs_error_rangecheck);
+ code = gs_c_glyph_name(glyph, gstr);
+ if (code < 0)
+ return code;
+ code = pfont->dir->global_glyph_code(pfont->memory, gstr, &glyph1);
+ if (code < 0)
+ return code;
+ if (pglyph)
+ *pglyph = glyph1;
+ if (pgd)
+ return copied_type1_glyph_data(pfont, glyph1, pgd);
+ else
+ return 0;
+}
+
+static int
+copied_type1_push_values(void *callback_data, const fixed *values, int count)
+{
+ return_error(gs_error_unregistered);
+}
+
+static int
+copied_type1_pop_value(void *callback_data, fixed *value)
+{
+ return_error(gs_error_unregistered);
+}
+
+static int
+copy_font_type1(gs_font *font, gs_font *copied)
+{
+ gs_font_type1 *font1 = (gs_font_type1 *)font;
+ gs_font_type1 *copied1 = (gs_font_type1 *)copied;
+ gs_copied_font_data_t *const cfdata = cf_data(copied);
+ int code;
+
+ cfdata->notdef = find_notdef((gs_font_base *)font);
+ code = copied_Encoding_alloc(copied);
+ if (code < 0)
+ return code;
+ if ((code = copy_subrs(font1, false, &cfdata->subrs, copied->memory)) < 0 ||
+ (code = copy_subrs(font1, true, &cfdata->global_subrs, copied->memory)) < 0
+ ) {
+ gs_free_object(copied->memory, cfdata->Encoding,
+ "copy_font_type1(Encoding)");
+ return code;
+ }
+ /*
+ * We don't need real push/pop procedures, because we can't do anything
+ * useful with fonts that have non-standard OtherSubrs anyway.
+ */
+ copied1->data.procs.glyph_data = copied_type1_glyph_data;
+ copied1->data.procs.subr_data = copied_type1_subr_data;
+ copied1->data.procs.seac_data = copied_type1_seac_data;
+ copied1->data.procs.push_values = copied_type1_push_values;
+ copied1->data.procs.pop_value = copied_type1_pop_value;
+ copied1->data.proc_data = 0;
+ return 0;
+}
+
+static int
+copy_glyph_type1(gs_font *font, gs_glyph glyph, gs_font *copied, int options)
+{
+ gs_glyph_data_t gdata;
+ gs_font_type1 *font1 = (gs_font_type1 *)font;
+ int code;
+ int rcode;
+
+ gdata.memory = font->memory;
+ code = font1->data.procs.glyph_data(font1, glyph, &gdata);
+ if (code < 0)
+ return code;
+ code = copy_glyph_data(font, glyph, copied, options, &gdata, NULL, 0);
+ if (code < 0)
+ return code;
+ rcode = code;
+ if (code == 0)
+ code = copy_glyph_name(font, glyph, copied, glyph);
+ return (code < 0 ? code : rcode);
+}
+
+static int
+copied_type1_glyph_outline(gs_font *font, int WMode, gs_glyph glyph,
+ const gs_matrix *pmat, gx_path *ppath, double sbw[4])
+{ /*
+ * 'WMode' may be inherited from an upper font.
+ * We ignore in because Type 1,2 charstrings do not depend on it.
+ */
+
+ /*
+ * This code duplicates much of zcharstring_outline in zchar1.c.
+ * This is unfortunate, but we don't see a simple way to refactor the
+ * code to avoid it.
+ */
+ gs_glyph_data_t gdata;
+ gs_font_type1 *const pfont1 = (gs_font_type1 *)font;
+ int code;
+ const gs_glyph_data_t *pgd = &gdata;
+ gs_type1_state cis;
+ gs_imager_state gis;
+
+ memset(&cis, 0x00, sizeof(cis));
+
+ gdata.memory = pfont1->memory;
+ code = pfont1->data.procs.glyph_data(pfont1, glyph, &gdata);
+ if (code < 0)
+ return code;
+ if (pgd->bits.size <= max(pfont1->data.lenIV, 0))
+ return_error(gs_error_invalidfont);
+ /* Initialize just enough of the imager state. */
+ if (pmat)
+ gs_matrix_fixed_from_matrix(&gis.ctm, pmat);
+ else {
+ gs_matrix imat;
+
+ gs_make_identity(&imat);
+ gs_matrix_fixed_from_matrix(&gis.ctm, &imat);
+ }
+ gis.flatness = 0;
+ code = gs_type1_interp_init(&cis, &gis, ppath, NULL, NULL, true, 0,
+ pfont1);
+ if (code < 0)
+ return code;
+ cis.no_grid_fitting = true;
+ /* Continue interpreting. */
+ for (;;) {
+ int value;
+
+ code = pfont1->data.interpret(&cis, pgd, &value);
+ switch (code) {
+ case 0: /* all done */
+ /* falls through */
+ default: /* code < 0, error */
+ return code;
+ case type1_result_callothersubr: /* unknown OtherSubr */
+ return_error(gs_error_rangecheck); /* can't handle it */
+ case type1_result_sbw: /* [h]sbw, just continue */
+ pgd = 0;
+ type1_cis_get_metrics(&cis, sbw);
+ }
+ }
+}
+
+static const gs_copied_font_procs_t copied_procs_type1 = {
+ copy_font_type1, copy_glyph_type1, copied_char_add_encoding,
+ named_glyph_slot_hashed,
+ copied_encode_char, gs_type1_glyph_info, copied_type1_glyph_outline
+};
+
+static void hash_subrs(gs_font_type1 *pfont)
+{
+ gs_type1_data *d0 = &pfont->data;
+ gs_glyph_data_t gdata0;
+ gs_md5_state_t md5;
+ int i, exit = 0;
+
+ gs_md5_init(&md5);
+ gdata0.memory = pfont->memory;
+ /* Scan the font to hash the global subrs. */
+ for (i = 0; !exit; i++) {
+ int code0 = pfont->data.procs.subr_data((gs_font_type1 *)pfont,
+ i, true, &gdata0);
+ if (code0 == gs_error_rangecheck)
+ /* rangecheck means we ran out of /Subrs */
+ exit = true;
+ if (code0 == gs_error_typecheck)
+ /* typecheck means that we may have encoutnered a null object
+ * for a Subr, we ignore this subr, but carry on hashing, as there
+ * may be more Subrs.
+ */
+ continue;
+ if (code0 < 0)
+ break;
+ else {
+ gs_md5_append(&md5, gdata0.bits.data, gdata0.bits.size);
+ gs_glyph_data_free(&gdata0, "hash_type1_subrs");
+ }
+ }
+ /* For a 'belt and braces' approach, we also record the number of local
+ * and global /Subrs, and compare these below as well. Shifting the global
+ * subrs up means that we can avoid an accidental co-incidence where simply
+ * summing the two sets together might give the same result for different fonts.
+ */
+ d0->num_subrs = i << 16;
+ exit = 0;
+ /* Scan the font to hash the local subrs. */
+ for (i = 0; !exit; i++) {
+ int code0 = pfont->data.procs.subr_data((gs_font_type1 *)pfont,
+ i, false, &gdata0);
+ if (code0 == gs_error_rangecheck)
+ /* rangecheck means we ran out of /Subrs */
+ exit = true;
+ if (code0 == gs_error_typecheck)
+ /* typecheck means that we may have encoutnered a null object
+ * for a Subr, we ignore this subr, but carry on hashing, as there
+ * may be more Subrs.
+ */
+ continue;
+ if (code0 < 0)
+ break;
+ else {
+ gs_md5_append(&md5, gdata0.bits.data, gdata0.bits.size);
+ gs_glyph_data_free(&gdata0, "hash_type1_subrs");
+ }
+ }
+ gs_md5_finish(&md5, d0->hash_subrs);
+ d0->num_subrs += i;
+}
+
+static bool
+same_type1_hinting(const gs_font_type1 *cfont, const gs_font_type1 *ofont)
+{
+ const gs_type1_data *d0 = &cfont->data, *d1 = &ofont->data;
+ int *hash0 = (int *)&d0->hash_subrs;
+ int *hash1 = (int *)&d1->hash_subrs;
+
+ if (d0->lenIV != d1->lenIV)
+ return false;
+ /*
+ if (d0->defaultWidthX != d1->defaultWidthX)
+ return false;
+ if (d0->nominalWidthX != d1->nominalWidthX)
+ return false;
+ */
+ if (d0->BlueFuzz != d1->BlueFuzz)
+ return false;
+ if (d0->BlueScale != d1->BlueScale)
+ return false;
+ if (d0->BlueShift != d1->BlueShift)
+ return false;
+ if (d0->ExpansionFactor != d1->ExpansionFactor)
+ return false;
+ if (d0->ForceBold != d1->ForceBold)
+ return false;
+ if (!compare_tables(d0->FamilyBlues, d1->FamilyBlues))
+ return false;
+ if (!compare_tables(d0->FamilyOtherBlues, d1->FamilyOtherBlues))
+ return false;
+ if (d0->LanguageGroup != d1->LanguageGroup)
+ return false;
+ if (!compare_tables(d0->OtherBlues, d1->OtherBlues))
+ return false;
+ if (d0->RndStemUp != d1->RndStemUp)
+ return false;
+ if (!compare_tables(d0->StdHW, d1->StdHW))
+ return false;
+ if (!compare_tables(d0->StemSnapH, d1->StemSnapH))
+ return false;
+ if (!compare_tables(d0->StemSnapV, d1->StemSnapV))
+ return false;
+ if (!compare_tables(d0->WeightVector, d1->WeightVector))
+ return false;
+ if (hash0[0] == 0x00 && hash0[1] == 0x00 && hash0[2] == 0x00 && hash0[3] == 0x00)
+ hash_subrs((gs_font_type1 *)cfont);
+ if (hash1[0] == 0x00 && hash1[1] == 0x00 && hash1[2] == 0x00 && hash1[3] == 0x00)
+ hash_subrs((gs_font_type1 *)ofont);
+ if (memcmp(d0->hash_subrs, d1->hash_subrs, 16) != 0 || d0->num_subrs != d1->num_subrs)
+ return false;
+
+ /*
+ * We ignore differences in OtherSubrs because pdfwrite
+ * must build without PS interpreter and therefore copied font
+ * have no storage for them.
+ */
+ return true;
+}
+
+/* ------ Type 42 ------ */
+
+static int
+copied_type42_string_proc(gs_font_type42 *font, ulong offset, uint len,
+ const byte **pstr)
+{
+ gs_copied_font_data_t *const cfdata = font->data.proc_data;
+
+ if (offset + len > cfdata->data_size)
+ return_error(gs_error_rangecheck);
+ *pstr = cfdata->data + offset;
+ return 0;
+}
+
+static int
+copied_type42_get_outline(gs_font_type42 *font, uint glyph_index,
+ gs_glyph_data_t *pgd)
+{
+ gs_copied_font_data_t *const cfdata = font->data.proc_data;
+ gs_copied_glyph_t *pcg;
+
+ if (glyph_index >= cfdata->glyphs_size)
+ return_error(gs_error_rangecheck);
+ pcg = &cfdata->glyphs[glyph_index];
+ if (!pcg->used)
+ gs_glyph_data_from_null(pgd);
+ else
+ gs_glyph_data_from_string(pgd, pcg->gdata.data, pcg->gdata.size, NULL);
+ return 0;
+}
+
+static int
+copied_type42_get_metrics(gs_font_type42 * pfont, uint glyph_index,
+ gs_type42_metrics_options_t options, float sbw[4])
+{
+ /* Check whether we have metrics for this (glyph,wmode) pair. */
+ gs_copied_font_data_t *const cfdata = pfont->data.proc_data;
+ gs_copied_glyph_t *pcg;
+ int wmode = gs_type42_metrics_options_wmode(options);
+
+ if (glyph_index >= cfdata->glyphs_size)
+ return_error(gs_error_rangecheck);
+ pcg = &cfdata->glyphs[glyph_index];
+ if (!(pcg->used & (HAS_SBW0 << wmode)))
+ return_error(gs_error_undefined);
+ return gs_type42_default_get_metrics(pfont, glyph_index, options, sbw);
+}
+
+static uint
+copied_type42_get_glyph_index(gs_font_type42 *font, gs_glyph glyph)
+{
+ gs_copied_font_data_t *const cfdata = cf_data((gs_font *)font);
+ gs_copied_glyph_t *pcg;
+ int code = copied_glyph_slot(cfdata, glyph, &pcg);
+
+ if (code < 0)
+ return GS_NO_GLYPH;
+ return pcg - cfdata->glyphs;
+}
+
+static int
+copy_font_type42(gs_font *font, gs_font *copied)
+{
+ gs_font_type42 *const font42 = (gs_font_type42 *)font;
+ gs_font_type42 *const copied42 = (gs_font_type42 *)copied;
+ gs_copied_font_data_t *const cfdata = cf_data(copied);
+ /*
+ * We "write" the font, aside from the glyphs, into an in-memory
+ * structure, and access it from there.
+ * We allocate room at the end of the copied data for fake hmtx/vmtx.
+ */
+ uint extra = font42->data.trueNumGlyphs * 8;
+ stream fs;
+ int code;
+
+ cfdata->notdef = find_notdef((gs_font_base *)font);
+ code = copied_Encoding_alloc(copied);
+ if (code < 0)
+ return code;
+ s_init(&fs, font->memory);
+ swrite_position_only(&fs);
+ code = (font->FontType == ft_TrueType ? psf_write_truetype_stripped(&fs, font42)
+ : psf_write_cid2_stripped(&fs, (gs_font_cid2 *)font42));
+ code = copied_data_alloc(copied, &fs, extra, code);
+ if (code < 0)
+ goto fail;
+ if (font->FontType == ft_TrueType)
+ psf_write_truetype_stripped(&fs, font42);
+ else
+ psf_write_cid2_stripped(&fs, (gs_font_cid2 *)font42);
+ copied42->data.string_proc = copied_type42_string_proc;
+ copied42->data.proc_data = cfdata;
+ code = gs_type42_font_init(copied42, 0);
+ if (code < 0)
+ goto fail2;
+ /* gs_type42_font_init overwrites font_info. */
+ copied->procs.font_info = copied_font_info;
+ /* gs_type42_font_init overwrites enumerate_glyph. */
+ copied42->procs.enumerate_glyph = copied_enumerate_glyph;
+ copied42->data.get_glyph_index = copied_type42_get_glyph_index;
+ copied42->data.get_outline = copied_type42_get_outline;
+ copied42->data.get_metrics = copied_type42_get_metrics;
+ copied42->data.metrics[0].numMetrics =
+ copied42->data.metrics[1].numMetrics =
+ extra / 8;
+ copied42->data.metrics[0].offset = cfdata->data_size - extra;
+ copied42->data.metrics[1].offset = cfdata->data_size - extra / 2;
+ copied42->data.metrics[0].length =
+ copied42->data.metrics[1].length =
+ extra / 2;
+ memset(cfdata->data + cfdata->data_size - extra, 0, extra);
+ copied42->data.numGlyphs = font42->data.numGlyphs;
+ copied42->data.trueNumGlyphs = font42->data.trueNumGlyphs;
+ return 0;
+ fail2:
+ gs_free_object(copied->memory, cfdata->data,
+ "copy_font_type42(data)");
+ fail:
+ gs_free_object(copied->memory, cfdata->Encoding,
+ "copy_font_type42(Encoding)");
+ return code;
+}
+
+static int
+copy_glyph_type42(gs_font *font, gs_glyph glyph, gs_font *copied, int options)
+{
+ gs_glyph_data_t gdata;
+ gs_font_type42 *font42 = (gs_font_type42 *)font;
+ gs_font_cid2 *fontCID2 = (gs_font_cid2 *)font;
+ gs_font_type42 *const copied42 = (gs_font_type42 *)copied;
+ uint gid = (options & COPY_GLYPH_BY_INDEX ? glyph - GS_MIN_GLYPH_INDEX :
+ font->FontType == ft_CID_TrueType
+ ? fontCID2->cidata.CIDMap_proc(fontCID2, glyph)
+ : font42->data.get_glyph_index(font42, glyph));
+ int code;
+ int rcode;
+ gs_copied_font_data_t *const cfdata = cf_data(copied);
+ gs_copied_glyph_t *pcg;
+ float sbw[4];
+ double factor = font42->data.unitsPerEm;
+ int i;
+
+ gdata.memory = font42->memory;
+ code = font42->data.get_outline(font42, gid, &gdata);
+ /* If the glyph is a /.notdef, and the GID is not 0, and we failed to find
+ * the /.notdef, try again with GID 0. We have seen fonts from GraphPad
+ * Prism which end up putting the /.notdef in the CharStrings dictionary
+ * with the wrong GID value (Bug #691573)
+ */
+ if (code < 0 && gid != 0) {
+ gs_const_string gnstr;
+ if (font->procs.glyph_name(font, glyph, &gnstr) >= 0 && gnstr.size == 7
+ && !memcmp(gnstr.data, ".notdef", 7)) {
+ gid = 0;
+ code = font42->data.get_outline(font42, gid, &gdata);
+ }
+ }
+ if (code < 0)
+ return code;
+
+ code = copy_glyph_data(font, gid + GS_MIN_GLYPH_INDEX, copied, options,
+ &gdata, NULL, 0);
+ if (code < 0)
+ return code;
+ rcode = code;
+ if (glyph < GS_MIN_CID_GLYPH)
+ code = copy_glyph_name(font, glyph, copied,
+ gid + GS_MIN_GLYPH_INDEX);
+ DISCARD(copied_glyph_slot(cfdata, gid + GS_MIN_GLYPH_INDEX, &pcg)); /* can't fail */
+ for (i = 0; i < 2; ++i) {
+ if (font42->data.get_metrics(font42, gid, i, sbw) >= 0) {
+ int sb = (int)(sbw[i] * factor + 0.5);
+ uint width = (uint)(sbw[2 + i] * factor + 0.5);
+ byte *pmetrics =
+ cfdata->data + copied42->data.metrics[i].offset + gid * 4;
+
+ pmetrics[0] = (byte)(width >> 8);
+ pmetrics[1] = (byte)width;
+ pmetrics[2] = (byte)(sb >> 8);
+ pmetrics[3] = (byte)sb;
+ pcg->used |= HAS_SBW0 << i;
+ }
+ factor = -factor; /* values are negated for WMode = 1 */
+ }
+ return (code < 0 ? code : rcode);
+}
+
+static gs_glyph
+copied_type42_encode_char(gs_font *copied, gs_char chr,
+ gs_glyph_space_t glyph_space)
+{
+ gs_copied_font_data_t *const cfdata = cf_data(copied);
+ const gs_glyph *Encoding = cfdata->Encoding;
+ gs_glyph glyph;
+
+ if (chr >= 256 || Encoding == 0)
+ return GS_NO_GLYPH;
+ glyph = Encoding[chr];
+ if (glyph_space == GLYPH_SPACE_INDEX) {
+ /* Search linearly for the glyph by name. */
+ gs_copied_glyph_t *pcg;
+ int code = named_glyph_slot_linear(cfdata, glyph, &pcg);
+
+ if (code < 0 || !pcg->used)
+ return GS_NO_GLYPH;
+ return GS_MIN_GLYPH_INDEX + (pcg - cfdata->glyphs);
+ }
+ return glyph;
+}
+
+static const gs_copied_font_procs_t copied_procs_type42 = {
+ copy_font_type42, copy_glyph_type42, copied_char_add_encoding,
+ named_glyph_slot_linear,
+ copied_type42_encode_char, gs_type42_glyph_info, gs_type42_glyph_outline
+};
+
+static inline int
+access_type42_data(gs_font_type42 *pfont, ulong base, ulong length,
+ const byte **vptr)
+{
+ return pfont->data.string_proc(pfont, base, length, vptr);
+}
+
+static inline uint
+U16(const byte *p)
+{
+ return ((uint)p[0] << 8) + p[1];
+}
+
+static int
+same_maxp_values(gs_font_type42 *font0, gs_font_type42 *font1)
+{
+ gs_type42_data *d0 = &font0->data, *d1 = &font1->data;
+
+ if (d0->maxPoints < d1->maxPoints)
+ return 0;
+ if (d0->maxContours < d1->maxContours)
+ return 0;
+ if (d0->maxCPoints < d1->maxCPoints)
+ return 0;
+ if (d0->maxCContours < d1->maxCContours)
+ return 0;
+ return 1;
+}
+
+static int
+same_type42_hinting(gs_font_type42 *font0, gs_font_type42 *font1)
+{
+ gs_type42_data *d0 = &font0->data, *d1 = &font1->data;
+ gs_font_type42 *font[2];
+ uint pos[2][3];
+ uint len[2][3] = {{0,0,0}, {0,0,0}};
+ int i, j, code;
+
+ if (d0->unitsPerEm != d1->unitsPerEm)
+ return 0;
+ font[0] = font0;
+ font[1] = font1;
+ memset(pos, 0, sizeof(pos));
+ for (j = 0; j < 2; j++) {
+ const byte *OffsetTable;
+ uint numTables;
+
+ code = access_type42_data(font[j], 0, 12, &OffsetTable);
+ if (code < 0)
+ return code;
+ numTables = U16(OffsetTable + 4);
+ for (i = 0; i < numTables; ++i) {
+ const byte *tab;
+ ulong start;
+ uint length;
+
+ code = access_type42_data(font[j], 12 + i * 16, 16, &tab);
+ if (code < 0)
+ return code;
+ start = get_u32_msb(tab + 8);
+ length = get_u32_msb(tab + 12);
+ if (!memcmp("prep", tab, 4))
+ pos[j][0] = start, len[j][0] = length;
+ else if (!memcmp("cvt ", tab, 4))
+ pos[j][1] = start, len[j][1] = length;
+ else if (!memcmp("fpgm", tab, 4))
+ pos[j][2] = start, len[j][2] = length;
+ }
+ }
+ for (i = 0; i < 3; i++) {
+ if (len[0][i] != len[1][i])
+ return 0;
+ }
+ for (i = 0; i < 3; i++) {
+ if (len[0][i] != 0) {
+ const byte *data0, *data1;
+ ulong length = len[0][i], size0, size1, size;
+ ulong pos0 = pos[0][i], pos1 = pos[1][i];
+
+ while (length > 0) {
+ code = access_type42_data(font0, pos0, length, &data0);
+ if (code < 0)
+ return code;
+ size0 = (code == 0 ? length : code);
+ code = access_type42_data(font1, pos1, length, &data1);
+ if (code < 0)
+ return code;
+ size1 = (code == 0 ? length : code);
+ size = min(size0, size1);
+ if (memcmp(data0, data1, size))
+ return 0;
+ pos0 += size;
+ pos1 += size;
+ length -= size;
+ }
+ }
+ }
+ return 1;
+}
+
+/* ------ CIDFont shared ------ */
+
+static int
+copy_font_cid_common(gs_font *font, gs_font *copied, gs_font_cid_data *pcdata)
+{
+ return (copy_string(copied->memory, &pcdata->CIDSystemInfo.Registry,
+ "Registry") |
+ copy_string(copied->memory, &pcdata->CIDSystemInfo.Ordering,
+ "Ordering"));
+}
+
+/* ------ CIDFontType 0 ------ */
+
+static int
+copied_cid0_glyph_data(gs_font_base *font, gs_glyph glyph,
+ gs_glyph_data_t *pgd, int *pfidx)
+{
+ gs_font_cid0 *fcid0 = (gs_font_cid0 *)font;
+ gs_copied_font_data_t *const cfdata = cf_data((gs_font *)font);
+ gs_copied_glyph_t *pcg;
+ int code = copied_glyph_slot(cfdata, glyph, &pcg);
+ int fdbytes = fcid0->cidata.FDBytes;
+ int i;
+
+ if (pfidx)
+ *pfidx = 0;
+ if (code < 0) {
+ if (pgd)
+ gs_glyph_data_from_null(pgd);
+ return_error(gs_error_undefined);
+ }
+ if (pfidx)
+ for (i = 0; i < fdbytes; ++i)
+ *pfidx = (*pfidx << 8) + pcg->gdata.data[i];
+ if (pgd)
+ gs_glyph_data_from_string(pgd, pcg->gdata.data + fdbytes,
+ pcg->gdata.size - fdbytes, NULL);
+ return 0;
+}
+static int
+copied_sub_type1_glyph_data(gs_font_type1 * pfont, gs_glyph glyph,
+ gs_glyph_data_t *pgd)
+{
+ return
+ copied_cid0_glyph_data((gs_font_base *)cf_data((gs_font *)pfont)->parent,
+ glyph, pgd, NULL);
+}
+
+static int
+cid0_subfont(gs_font *copied, gs_glyph glyph, gs_font_type1 **pfont1)
+{
+ int fidx;
+ int code = copied_cid0_glyph_data((gs_font_base *)copied, glyph, NULL,
+ &fidx);
+
+ if (code >= 0) {
+ gs_font_cid0 *font0 = (gs_font_cid0 *)copied;
+
+ if (fidx >= font0->cidata.FDArray_size)
+ return_error(gs_error_unregistered); /* Must not happen. */
+ *pfont1 = font0->cidata.FDArray[fidx];
+ }
+ return code;
+}
+
+static int
+copied_cid0_glyph_info(gs_font *font, gs_glyph glyph, const gs_matrix *pmat,
+ int members, gs_glyph_info_t *info)
+{
+ gs_font_type1 *subfont1;
+ int code = cid0_subfont(font, glyph, &subfont1);
+
+ if (code < 0)
+ return code;
+ if (members & GLYPH_INFO_WIDTH1) {
+ /* Hack : There is no way to pass WMode from font to glyph_info,
+ * and usually CID font has no metrics for WMode 1.
+ * Therefore we use FontBBox as default size.
+ * Warning : this incompletely implements the request :
+ * other requested members are not retrieved.
+ */
+ gs_font_info_t finfo;
+ int code = subfont1->procs.font_info(font, NULL, FONT_INFO_BBOX, &finfo);
+
+ if (code < 0)
+ return code;
+ info->width[0].x = 0;
+ info->width[0].y = 0;
+ info->width[1].x = 0;
+ info->width[1].y = -finfo.BBox.q.x; /* Sic! */
+ info->v.x = finfo.BBox.q.x / 2;
+ info->v.y = finfo.BBox.q.y;
+ info->members = GLYPH_INFO_WIDTH1;
+ return 0;
+ }
+ return subfont1->procs.glyph_info((gs_font *)subfont1, glyph, pmat,
+ members, info);
+}
+
+static int
+copied_cid0_glyph_outline(gs_font *font, int WMode, gs_glyph glyph,
+ const gs_matrix *pmat, gx_path *ppath, double sbw[4])
+{
+ gs_font_type1 *subfont1;
+ int code = cid0_subfont(font, glyph, &subfont1);
+
+ if (code < 0)
+ return code;
+ return subfont1->procs.glyph_outline((gs_font *)subfont1, WMode, glyph, pmat,
+ ppath, sbw);
+}
+
+static int
+copy_font_cid0(gs_font *font, gs_font *copied)
+{
+ gs_font_cid0 *copied0 = (gs_font_cid0 *)copied;
+ gs_copied_font_data_t *const cfdata = cf_data(copied);
+ gs_font_type1 **FDArray =
+ gs_alloc_struct_array(copied->memory, copied0->cidata.FDArray_size,
+ gs_font_type1 *,
+ &st_gs_font_type1_ptr_element, "FDArray");
+ int i = 0, code;
+
+ if (FDArray == 0)
+ return_error(gs_error_VMerror);
+ code = copy_font_cid_common(font, copied, &copied0->cidata.common);
+ if (code < 0)
+ goto fail;
+ for (; i < copied0->cidata.FDArray_size; ++i) {
+ gs_font *subfont = (gs_font *)copied0->cidata.FDArray[i];
+ gs_font_type1 *subfont1 = (gs_font_type1 *)subfont;
+ gs_font *subcopy;
+ gs_font_type1 *subcopy1;
+ gs_copied_font_data_t *subdata;
+
+ if (i == 0) {
+ /* copy_subrs requires a Type 1 font, even for GSubrs. */
+ code = copy_subrs(subfont1, true, &cfdata->global_subrs,
+ copied->memory);
+ if (code < 0)
+ goto fail;
+ }
+ code = gs_copy_font(subfont, &subfont->FontMatrix, copied->memory, &subcopy, -1);
+ if (code < 0)
+ goto fail;
+ subcopy1 = (gs_font_type1 *)subcopy;
+ subcopy1->data.parent = NULL;
+ subdata = cf_data(subcopy);
+ subdata->parent = copied0;
+ gs_free_object(copied->memory, subdata->Encoding,
+ "copy_font_cid0(Encoding)");
+ subdata->Encoding = 0;
+ /*
+ * Share the glyph data and global_subrs with the parent. This
+ * allows copied_type1_glyph_data in the subfont to do the right
+ * thing.
+ */
+ gs_free_object(copied->memory, subdata->names,
+ "copy_font_cid0(subfont names)");
+ gs_free_object(copied->memory, subdata->glyphs,
+ "copy_font_cid0(subfont glyphs)");
+ subcopy1->data.procs.glyph_data = copied_sub_type1_glyph_data;
+ subdata->glyphs = cfdata->glyphs;
+ subdata->glyphs_size = cfdata->glyphs_size;
+ subdata->names = 0;
+ subdata->global_subrs = cfdata->global_subrs;
+ FDArray[i] = subcopy1;
+ }
+ cfdata->notdef = GS_MIN_CID_GLYPH;
+ copied0->cidata.FDArray = FDArray;
+ copied0->cidata.FDBytes =
+ (copied0->cidata.FDArray_size <= 1 ? 0 :
+ copied0->cidata.FDArray_size <= 256 ? 1 : 2);
+ copied0->cidata.glyph_data = copied_cid0_glyph_data;
+ return 0;
+ fail:
+ while (--i >= 0)
+ gs_free_object(copied->memory, FDArray[i], "copy_font_cid0(subfont)");
+ gs_free_object(copied->memory, FDArray, "FDArray");
+ return code;
+}
+
+static int
+copy_glyph_cid0(gs_font *font, gs_glyph glyph, gs_font *copied, int options)
+{
+ gs_glyph_data_t gdata;
+ gs_font_cid0 *fcid0 = (gs_font_cid0 *)font;
+ gs_font_cid0 *copied0 = (gs_font_cid0 *)copied;
+ int fdbytes = copied0->cidata.FDBytes;
+ int fidx;
+ int code;
+ byte prefix[MAX_FDBytes];
+ int i;
+
+ gdata.memory = font->memory;
+ code = fcid0->cidata.glyph_data((gs_font_base *)font, glyph,
+ &gdata, &fidx);
+ if (code < 0)
+ return code;
+ for (i = fdbytes - 1; i >= 0; --i, fidx >>= 8)
+ prefix[i] = (byte)fidx;
+ if (fidx != 0)
+ return_error(gs_error_rangecheck);
+ return copy_glyph_data(font, glyph, copied, options, &gdata, prefix, fdbytes);
+}
+
+static const gs_copied_font_procs_t copied_procs_cid0 = {
+ copy_font_cid0, copy_glyph_cid0, copied_no_add_encoding,
+ named_glyph_slot_none,
+ gs_no_encode_char, copied_cid0_glyph_info, copied_cid0_glyph_outline
+};
+
+static int
+same_cid0_hinting(const gs_font_cid0 *cfont, const gs_font_cid0 *ofont)
+{
+ int i;
+
+ if (cfont->cidata.FDArray_size != ofont->cidata.FDArray_size)
+ return 0;
+
+ for (i = 0; i < cfont->cidata.FDArray_size; i++) {
+ gs_font_type1 *subfont0 = cfont->cidata.FDArray[i];
+ gs_font_type1 *subfont1 = ofont->cidata.FDArray[i];
+ if (!same_type1_hinting(subfont0, subfont1))
+ return 0;
+ }
+ return 1;
+}
+
+/* ------ CIDFontType 2 ------ */
+
+static int
+copied_cid2_CIDMap_proc(gs_font_cid2 *fcid2, gs_glyph glyph)
+{
+ uint cid = glyph - GS_MIN_CID_GLYPH;
+ gs_copied_font_data_t *const cfdata = cf_data((gs_font *)fcid2);
+ const ushort *CIDMap = cfdata->CIDMap;
+
+ if (glyph < GS_MIN_CID_GLYPH || cid >= fcid2->cidata.common.CIDCount)
+ return_error(gs_error_rangecheck);
+ if (CIDMap[cid] == 0xffff)
+ return -1;
+ return CIDMap[cid];
+}
+
+static uint
+copied_cid2_get_glyph_index(gs_font_type42 *font, gs_glyph glyph)
+{
+ int glyph_index = copied_cid2_CIDMap_proc((gs_font_cid2 *)font, glyph);
+
+ if (glyph_index < 0)
+ return GS_NO_GLYPH;
+ return glyph_index;
+}
+
+extern_st(st_subst_CID_on_WMode);
+
+static int
+copy_font_cid2(gs_font *font, gs_font *copied)
+{
+ gs_font_cid2 *copied2 = (gs_font_cid2 *)copied;
+ gs_copied_font_data_t *const cfdata = cf_data(copied);
+ int code;
+ int CIDCount = copied2->cidata.common.CIDCount;
+ ushort *CIDMap = (ushort *)
+ gs_alloc_byte_array(copied->memory, CIDCount, sizeof(ushort),
+ "copy_font_cid2(CIDMap");
+
+ if (CIDMap == 0)
+ return_error(gs_error_VMerror);
+ code = copy_font_cid_common(font, copied, &copied2->cidata.common);
+ if (code < 0 ||
+ (code = copy_font_type42(font, copied)) < 0
+ ) {
+ gs_free_object(copied->memory, CIDMap, "copy_font_cid2(CIDMap");
+ return code;
+ }
+ cfdata->notdef = GS_MIN_CID_GLYPH;
+ memset(CIDMap, 0xff, CIDCount * sizeof(*CIDMap));
+ cfdata->CIDMap = CIDMap;
+ copied2->cidata.MetricsCount = 0;
+ copied2->cidata.CIDMap_proc = copied_cid2_CIDMap_proc;
+ {
+ gs_font_type42 *const copied42 = (gs_font_type42 *)copied;
+
+ copied42->data.get_glyph_index = copied_cid2_get_glyph_index;
+ }
+ if (copied2->subst_CID_on_WMode) {
+ gs_subst_CID_on_WMode_t *subst = NULL;
+
+ rc_alloc_struct_1(subst, gs_subst_CID_on_WMode_t, &st_subst_CID_on_WMode,
+ copied2->memory, return_error(gs_error_VMerror), "copy_font_cid2");
+ subst->data[0] = subst->data[1] = 0;
+ copied2->subst_CID_on_WMode = subst;
+ rc_increment(subst);
+ }
+
+ return 0;
+}
+
+static int expand_CIDMap(gs_font_cid2 *copied2, uint CIDCount)
+{
+ ushort *CIDMap;
+ gs_copied_font_data_t *const cfdata = cf_data((gs_font *)copied2);
+
+ if (CIDCount <= copied2->cidata.common.CIDCount)
+ return 0;
+ CIDMap = (ushort *)
+ gs_alloc_byte_array(copied2->memory, CIDCount, sizeof(ushort),
+ "copy_font_cid2(CIDMap");
+ if (CIDMap == 0)
+ return_error(gs_error_VMerror);
+ memcpy(CIDMap, cfdata->CIDMap, copied2->cidata.common.CIDCount * sizeof(*CIDMap));
+ memset(CIDMap + copied2->cidata.common.CIDCount, 0xFF,
+ (CIDCount - copied2->cidata.common.CIDCount) * sizeof(*CIDMap));
+ cfdata->CIDMap = CIDMap;
+ copied2->cidata.common.CIDCount = CIDCount;
+ return 0;
+}
+
+static int
+copy_glyph_cid2(gs_font *font, gs_glyph glyph, gs_font *copied, int options)
+{
+ gs_font_cid2 *fcid2 = (gs_font_cid2 *)font;
+ gs_copied_font_data_t *const cfdata = cf_data(copied);
+ gs_font_cid2 *copied2 = (gs_font_cid2 *)copied;
+ int gid;
+ int code;
+
+ if (!(options & COPY_GLYPH_BY_INDEX)) {
+ uint cid = glyph - GS_MIN_CID_GLYPH;
+ int CIDCount;
+
+ code = expand_CIDMap(copied2, cid + 1);
+ if (code < 0)
+ return code;
+ CIDCount = copied2->cidata.common.CIDCount;
+ gid = fcid2->cidata.CIDMap_proc(fcid2, glyph);
+ if (gid < 0 || gid >= cfdata->glyphs_size)
+ return_error(gs_error_rangecheck);
+ if (cid > CIDCount)
+ return_error(gs_error_invalidaccess);
+ if (cfdata->CIDMap[cid] != 0xffff && cfdata->CIDMap[cid] != gid)
+ return_error(gs_error_invalidaccess);
+ code = copy_glyph_type42(font, glyph, copied, options);
+ if (code < 0)
+ return code;
+ cfdata->CIDMap[cid] = gid;
+ } else {
+ gid = glyph - GS_MIN_GLYPH_INDEX;
+ if (gid < 0 || gid >= cfdata->glyphs_size)
+ return_error(gs_error_rangecheck);
+ code = copy_glyph_type42(font, glyph, copied, options);
+ if (code < 0)
+ return code;
+ }
+ return code;
+}
+
+static const gs_copied_font_procs_t copied_procs_cid2 = {
+ copy_font_cid2, copy_glyph_cid2, copied_no_add_encoding,
+ named_glyph_slot_none,
+ gs_no_encode_char, gs_type42_glyph_info, gs_type42_glyph_outline
+};
+
+static int
+same_cid2_hinting(const gs_font_cid2 *cfont, const gs_font_cid2 *ofont)
+{
+ return same_type42_hinting((gs_font_type42 *)cfont, (gs_font_type42 *)ofont);
+}
+
+/* ---------------- Public ---------------- */
+
+/*
+ * Procedure vector for copied fonts.
+ */
+static font_proc_font_info(copied_font_info);
+static font_proc_enumerate_glyph(copied_enumerate_glyph);
+static const gs_font_procs copied_font_procs = {
+ 0, /* define_font, not supported */
+ 0, /* make_font, not supported */
+ copied_font_info,
+ gs_default_same_font,
+ 0, /* encode_char, varies by FontType */
+ 0, /* decode_char, not supported */
+ copied_enumerate_glyph,
+ 0, /* glyph_info, varies by FontType */
+ 0, /* glyph_outline, varies by FontType */
+ copied_glyph_name,
+ gs_default_init_fstack,
+ gs_default_next_char_glyph,
+ copied_build_char
+};
+
+#if GLYPHS_SIZE_IS_PRIME
+static const int some_primes[] = {
+ /* Arbitrary choosen prime numbers, being reasonable for a Type 1|2 font size.
+ We start with 257 to fit 256 glyphs and .notdef .
+ Smaller numbers aren't useful, because we don't know whether a font
+ will add more glyphs incrementally when we allocate its stable copy.
+ */
+ 257, 359, 521, 769, 1031, 2053,
+ 3079, 4099, 5101, 6101, 7109, 8209, 10007, 12007, 14009,
+ 16411, 20107, 26501, 32771, 48857, 65537, 85229, 127837};
+#endif
+
+/*
+ * Copy a font, aside from its glyphs.
+ */
+int
+gs_copy_font(gs_font *font, const gs_matrix *orig_matrix, gs_memory_t *mem, gs_font **pfont_new, int max_reserved_glyphs)
+{
+ gs_memory_type_ptr_t fstype = gs_object_type(font->memory, font);
+ uint fssize = gs_struct_type_size(fstype);
+ gs_font *copied = 0;
+ gs_copied_font_data_t *cfdata = 0;
+ gs_font_info_t info;
+ gs_copied_glyph_t *glyphs = 0;
+ uint glyphs_size;
+ gs_copied_glyph_name_t *names = 0;
+ bool have_names = false;
+ const gs_copied_font_procs_t *procs;
+ int code;
+
+ /*
+ * Check for a supported FontType, and compute the size of its
+ * copied glyph table.
+ */
+ switch (font->FontType) {
+ case ft_TrueType:
+ procs = &copied_procs_type42;
+ glyphs_size = ((gs_font_type42 *)font)->data.trueNumGlyphs;
+ have_names = true;
+ break;
+ case ft_encrypted:
+ case ft_encrypted2:
+ procs = &copied_procs_type1;
+ /* Count the glyphs. */
+ glyphs_size = 0;
+ {
+ int index = 0;
+ gs_glyph glyph;
+
+ while (font->procs.enumerate_glyph(font, &index, GLYPH_SPACE_NAME,
+ &glyph), index != 0)
+ ++glyphs_size;
+ }
+ if(glyphs_size > max_reserved_glyphs && max_reserved_glyphs != -1)
+ glyphs_size = max_reserved_glyphs;
+
+#if GLYPHS_SIZE_IS_PRIME
+ if (glyphs_size < 257)
+ glyphs_size = 257;
+ /*
+ * Make glyphs_size a prime number to ensure termination of the loop in
+ * named_glyphs_slot_hashed, q.v.
+ * Also reserve additional slots for the case of font merging and
+ * for possible font increments.
+ */
+ glyphs_size = glyphs_size * 3 / 2;
+
+ { int i;
+ for (i = 0; i < count_of(some_primes); i++)
+ if (glyphs_size <= some_primes[i])
+ break;
+ if (i >= count_of(some_primes))
+ return_error(gs_error_rangecheck);
+ glyphs_size = some_primes[i];
+ }
+#else
+ /*
+ * Make names_size a power of 2 to ensure termination of the loop in
+ * named_glyphs_slot_hashed, q.v.
+ */
+ glyphs_size = glyphs_size * 3 / 2;
+ while (glyphs_size & (glyphs_size - 1))
+ glyphs_size = (glyphs_size | (glyphs_size - 1)) + 1;
+ if (glyphs_size < 256) /* probably incremental font */
+ glyphs_size = 256;
+#endif
+ have_names = true;
+ break;
+ case ft_CID_encrypted:
+ procs = &copied_procs_cid0;
+ /* We used to use the CIDCount here, but for CIDFonts with a GlyphDirectory
+ * (dictionary form) the number of CIDs is not the same as the highest CID.
+ * Because we use the CID as the slot, we need to assign the highest possible
+ * CID, not the number of CIDs. Don't forget to add one because CIDs
+ * count from 0.
+ */
+ glyphs_size = ((gs_font_cid0 *)font)->cidata.common.MaxCID + 1;
+ break;
+ case ft_CID_TrueType:
+ procs = &copied_procs_cid2;
+ /* Glyphs are indexed by GID, not by CID. */
+ glyphs_size = ((gs_font_cid2 *)font)->data.trueNumGlyphs;
+ break;
+ default:
+ return_error(gs_error_rangecheck);
+ }
+
+ /* Get the font_info for copying. */
+
+ memset(&info, 0, sizeof(info));
+ info.Flags_requested = ~0;
+ code = font->procs.font_info(font, NULL, ~0, &info);
+
+ /* We can ignore a lack of FontInfo for TrueType fonts which
+ * are descendants of CID fonts
+ */
+ if (code < 0 && !(font->FontType == ft_CID_TrueType))
+ return code;
+
+ /* Allocate the generic copied information. */
+
+ glyphs = gs_alloc_struct_array(mem, glyphs_size, gs_copied_glyph_t,
+ &st_gs_copied_glyph_element,
+ "gs_copy_font(glyphs)");
+ if (have_names != 0)
+ names = gs_alloc_struct_array(mem, glyphs_size, gs_copied_glyph_name_t,
+ &st_gs_copied_glyph_name_element,
+ "gs_copy_font(names)");
+ copied = gs_alloc_struct(mem, gs_font, fstype,
+ "gs_copy_font(copied font)");
+ cfdata = gs_alloc_struct(mem, gs_copied_font_data_t,
+ &st_gs_copied_font_data,
+ "gs_copy_font(wrapper data)");
+ if (cfdata)
+ memset(cfdata, 0, sizeof(*cfdata));
+ if (glyphs == 0 || (names == 0 && have_names) || copied == 0 ||
+ cfdata == 0
+ ) {
+ code = gs_note_error(gs_error_VMerror);
+ goto fail;
+ }
+ cfdata->info = info;
+ cfdata->dir = font->dir;
+ if ((code = (copy_string(mem, &cfdata->info.Copyright,
+ "gs_copy_font(Copyright)") |
+ copy_string(mem, &cfdata->info.Notice,
+ "gs_copy_font(Notice)") |
+ copy_string(mem, &cfdata->info.FamilyName,
+ "gs_copy_font(FamilyName)") |
+ copy_string(mem, &cfdata->info.FullName,
+ "gs_copy_font(FullName)"))) < 0
+ )
+ goto fail;
+
+ /* Initialize the copied font. */
+
+ memcpy(copied, font, fssize);
+ copied->next = copied->prev = 0;
+ copied->memory = mem;
+ copied->is_resource = false;
+ gs_notify_init(&copied->notify_list, mem);
+ copied->base = copied;
+ copied->FontMatrix = *orig_matrix;
+ copied->client_data = cfdata;
+ copied->procs = copied_font_procs;
+ copied->procs.encode_char = procs->encode_char;
+ copied->procs.glyph_info = procs->glyph_info;
+ copied->procs.glyph_outline = procs->glyph_outline;
+ {
+ gs_font_base *bfont = (gs_font_base *)copied;
+
+ bfont->FAPI = 0;
+ bfont->FAPI_font_data = 0;
+ bfont->encoding_index = ENCODING_INDEX_UNKNOWN;
+ code = uid_copy(&bfont->UID, mem, "gs_copy_font(UID)");
+ if (code < 0)
+ goto fail;
+ }
+
+ cfdata->procs = procs;
+ memset(glyphs, 0, glyphs_size * sizeof(*glyphs));
+ cfdata->glyphs = glyphs;
+ cfdata->glyphs_size = glyphs_size;
+ cfdata->num_glyphs = 0;
+ cfdata->ordered = false;
+ if (names)
+ memset(names, 0, glyphs_size * sizeof(*names));
+ cfdata->names = names;
+ if (names != 0) {
+ uint i;
+
+ for (i = 0; i < glyphs_size; ++i)
+ names[i].glyph = GS_NO_GLYPH;
+ }
+
+ /* Do FontType-specific initialization. */
+
+ code = procs->finish_copy_font(font, copied);
+ if (code < 0)
+ goto fail;
+
+ *pfont_new = copied;
+ if (cfdata->notdef != GS_NO_GLYPH)
+ code = gs_copy_glyph(font, cfdata->notdef, copied);
+ return code;
+
+ fail:
+ /* Free storage and exit. */
+ if (cfdata) {
+ uncopy_string(mem, &cfdata->info.FullName,
+ "gs_copy_font(FullName)");
+ uncopy_string(mem, &cfdata->info.FamilyName,
+ "gs_copy_font(FamilyName)");
+ uncopy_string(mem, &cfdata->info.Notice,
+ "gs_copy_font(Notice)");
+ uncopy_string(mem, &cfdata->info.Copyright,
+ "gs_copy_font(Copyright)");
+ gs_free_object(mem, cfdata, "gs_copy_font(wrapper data)");
+ }
+ gs_free_object(mem, copied, "gs_copy_font(copied font)");
+ gs_free_object(mem, names, "gs_copy_font(names)");
+ gs_free_object(mem, glyphs, "gs_copy_font(glyphs)");
+ return code;
+}
+
+/* We only need this because the ddescndant(s) share the parent
+ * CIDFont glyph space, so we can't free that if we are a descendant.
+ */
+static int gs_free_copied_descendant_font(gs_font *font)
+{
+ gs_copied_font_data_t *cfdata = font->client_data;
+ gs_memory_t *mem = font->memory;
+
+ if (cfdata) {
+ uncopy_string(mem, &cfdata->info.FullName,
+ "gs_free_copied_font(FullName)");
+ uncopy_string(mem, &cfdata->info.FamilyName,
+ "gs_free_copied_font(FamilyName)");
+ uncopy_string(mem, &cfdata->info.Notice,
+ "gs_free_copied_font(Notice)");
+ uncopy_string(mem, &cfdata->info.Copyright,
+ "gs_free_copied_font(Copyright)");
+ if (cfdata->Encoding)
+ gs_free_object(mem, cfdata->Encoding, "gs_free_copied_font(Encoding)");
+ gs_free_object(mem, cfdata->names, "gs_free_copied_font(names)");
+ gs_free_object(mem, cfdata->data, "gs_free_copied_font(data)");
+ gs_free_object(mem, cfdata, "gs_free_copied_font(wrapper data)");
+ }
+ gs_free_object(mem, font, "gs_free_copied_font(copied font)");
+ return 0;
+}
+
+int gs_free_copied_font(gs_font *font)
+{
+ gs_copied_font_data_t *cfdata = font->client_data;
+ gs_memory_t *mem = font->memory;
+ int i, code;
+ gs_copied_glyph_t *pcg = 0;
+
+ /* For CID fonts, we must also free the descendants, which we copied
+ * at the time we copied the actual CIDFont itself
+ */
+ if (font->FontType == ft_CID_encrypted) {
+ gs_font_cid0 *copied0 = (gs_font_cid0 *)font;
+
+ for (i = 0; i < copied0->cidata.FDArray_size; ++i) {
+ code = gs_free_copied_descendant_font((gs_font *)copied0->cidata.FDArray[i]);
+ if (code < 0)
+ return code;
+ }
+ gs_free_object(mem, copied0->cidata.FDArray, "free copied CIDFont FDArray");
+ copied0->cidata.FDArray = 0;
+ }
+ /* free copied glyph data */
+ for (i=0;i < cfdata->glyphs_size;i++) {
+ pcg = &cfdata->glyphs[i];
+ if(pcg->gdata.size) {
+ gs_free_string(font->memory, (byte *)pcg->gdata.data, pcg->gdata.size, "Free copied glyph");
+ }
+ }
+
+ if (cfdata) {
+ uncopy_string(mem, &cfdata->info.FullName,
+ "gs_free_copied_font(FullName)");
+ uncopy_string(mem, &cfdata->info.FamilyName,
+ "gs_free_copied_font(FamilyName)");
+ uncopy_string(mem, &cfdata->info.Notice,
+ "gs_free_copied_font(Notice)");
+ uncopy_string(mem, &cfdata->info.Copyright,
+ "gs_free_copied_font(Copyright)");
+ if (cfdata->Encoding)
+ gs_free_object(mem, cfdata->Encoding, "gs_free_copied_font(Encoding)");
+ gs_free_object(mem, cfdata->glyphs, "gs_free_copied_font(glyphs)");
+ gs_free_object(mem, cfdata->names, "gs_free_copied_font(names)");
+ gs_free_object(mem, cfdata->data, "gs_free_copied_font(data)");
+ gs_free_object(mem, cfdata, "gs_free_copied_font(wrapper data)");
+ }
+ gs_free_object(mem, font, "gs_free_copied_font(copied font)");
+ return 0;
+}
+
+/*
+ * Copy a glyph, including any sub-glyphs.
+ */
+int
+gs_copy_glyph(gs_font *font, gs_glyph glyph, gs_font *copied)
+{
+ return gs_copy_glyph_options(font, glyph, copied, 0);
+}
+int
+gs_copy_glyph_options(gs_font *font, gs_glyph glyph, gs_font *copied,
+ int options)
+{
+ int code;
+#define MAX_GLYPH_PIECES 64 /* arbitrary, but 32 is too small - bug 687698. */
+ gs_glyph glyphs[MAX_GLYPH_PIECES];
+ uint count = 1, i;
+
+ if (copied->procs.font_info != copied_font_info)
+ return_error(gs_error_rangecheck);
+ code = cf_data(copied)->procs->copy_glyph(font, glyph, copied, options);
+ if (code != 0)
+ return code;
+ /* Copy any sub-glyphs. */
+ glyphs[0] = glyph;
+ code = psf_add_subset_pieces(glyphs, &count, MAX_GLYPH_PIECES, MAX_GLYPH_PIECES,
+ font);
+ if (code < 0)
+ return code;
+ if (count > MAX_GLYPH_PIECES)
+ return_error(gs_error_limitcheck);
+ for (i = 1; i < count; ++i) {
+ code = gs_copy_glyph_options(font, glyphs[i], copied,
+ (options & ~COPY_GLYPH_NO_OLD) | COPY_GLYPH_BY_INDEX);
+ if (code < 0)
+ return code;
+ /* if code > 0 then we already have the glyph, so no need to process further.
+ * If the original glyph was not a CID then we are copying by name, not by index.
+ * But the copy above copies by index which means we don't have an entry for
+ * the glyp-h component in the name table. If we are using names then we
+ * absolutely *must* have an entry in the name table, so go ahead and add
+ * one here. Note that the array returned by psf_add_subset_pieces has the
+ * GIDs with an offset of GS_MIN_GLYPH_INDEX added. Previously we removed this
+ * offset, but if the resulting GID referenced a name already in use (or later used)
+ * then the generated CMAP was incorrect. By leaving the offset in place we get
+ * a name generated (numeric name based on GID) which gurantees no name collisions.
+ * (Bug #693444).
+ */
+ if (code == 0 && glyph < GS_MIN_CID_GLYPH && glyphs[i] > GS_MIN_GLYPH_INDEX) {
+ code = copy_glyph_name(font, glyphs[i], copied,
+ glyphs[i]);
+ if (code < 0)
+ return code;
+ }
+ }
+ /*
+ * Because 'seac' accesses the Encoding of the font as well as the
+ * glyphs, we have to copy the Encoding entries as well.
+ */
+ if (count == 1)
+ return 0;
+ switch (font->FontType) {
+ case ft_encrypted:
+ case ft_encrypted2:
+ break;
+ default:
+ return 0;
+ }
+#if 0 /* No need to add subglyphs to the Encoding because they always are
+ taken from StandardEncoding (See the Type 1 spec about 'seac').
+ Attempt to add them to the encoding can cause a conflict,
+ if the encoding specifies different glyphs for these char codes
+ (See the bug #687172). */
+ {
+ gs_copied_glyph_t *pcg;
+ gs_glyph_data_t gdata;
+ gs_char chars[2];
+
+ gdata.memory = font->memory;
+ /* Since we just copied the glyph, copied_glyph_slot can't fail. */
+ DISCARD(copied_glyph_slot(cf_data(copied), glyph, &pcg));
+ gs_glyph_data_from_string(&gdata, pcg->gdata.data, pcg->gdata.size,
+ NULL);
+ code = gs_type1_piece_codes((gs_font_type1 *)font, &gdata, chars);
+ if (code <= 0 || /* 0 is not possible here */
+ (code = gs_copied_font_add_encoding(copied, chars[0], glyphs[1])) < 0 ||
+ (code = gs_copied_font_add_encoding(copied, chars[1], glyphs[2])) < 0
+ )
+ return code;
+ }
+#endif
+ return 0;
+#undef MAX_GLYPH_PIECES
+}
+
+/*
+ * Add an Encoding entry to a copied font. The glyph need not already have
+ * been copied.
+ */
+int
+gs_copied_font_add_encoding(gs_font *copied, gs_char chr, gs_glyph glyph)
+{
+ gs_copied_font_data_t *const cfdata = cf_data(copied);
+
+ if (copied->procs.font_info != copied_font_info)
+ return_error(gs_error_rangecheck);
+ return cfdata->procs->add_encoding(copied, chr, glyph);
+}
+
+/*
+ * Copy all the glyphs and, if relevant, Encoding entries from a font. This
+ * is equivalent to copying the glyphs and Encoding entries individually,
+ * and returns errors under the same conditions.
+ */
+int
+gs_copy_font_complete(gs_font *font, gs_font *copied)
+{
+ int index, code = 0;
+ gs_glyph_space_t space = GLYPH_SPACE_NAME;
+ gs_glyph glyph;
+
+ /*
+ * For Type 1 fonts and CIDFonts, enumerating the glyphs using
+ * GLYPH_SPACE_NAME will cover all the glyphs. (The "names" of glyphs
+ * in CIDFonts are CIDs, but that is not a problem.) For Type 42 fonts,
+ * however, we have to copy by name once, so that we also copy the
+ * name-to-GID mapping (the CharStrings dictionary in PostScript), and
+ * then copy again by GID, to cover glyphs that don't have names.
+ */
+ for (;;) {
+ for (index = 0;
+ code >= 0 &&
+ (font->procs.enumerate_glyph(font, &index, space, &glyph),
+ index != 0);
+ ) {
+ if (font->FontType == ft_TrueType &&
+ ((glyph >= GS_MIN_CID_GLYPH && glyph < GS_MIN_GLYPH_INDEX) || glyph == GS_NO_GLYPH ||
+ (space == GLYPH_SPACE_INDEX && glyph < GS_MIN_GLYPH_INDEX)))
+ return_error(gs_error_invalidfont); /* bug 688370. */
+ code = gs_copy_glyph(font, glyph, copied);
+ }
+ /* For Type 42 fonts, if we copied by name, now copy again by index. */
+ if (space == GLYPH_SPACE_NAME && font->FontType == ft_TrueType)
+ space = GLYPH_SPACE_INDEX;
+ else
+ break;
+ }
+ if (cf_data(copied)->Encoding != 0)
+ for (index = 0; code >= 0 && index < 256; ++index) {
+ glyph = font->procs.encode_char(font, (gs_char)index,
+ GLYPH_SPACE_NAME);
+ if (glyph != GS_NO_GLYPH) {
+ code = gs_copied_font_add_encoding(copied, (gs_char)index,
+ glyph);
+ if (code == gs_error_undefined) {
+ /* Skip Encoding entries, which point to undefiuned glyphs -
+ happens with 033-52-5873.pdf. */
+ code = 0;
+ }
+ if (code == gs_error_rangecheck) {
+ /* Skip Encoding entries, which point to undefiuned glyphs -
+ happens with 159.pdf. */
+ code = 0;
+ }
+ }
+ }
+ if (copied->FontType != ft_composite) {
+ gs_font_base *bfont = (gs_font_base *)font;
+ gs_font_base *bcopied = (gs_font_base *)copied;
+
+ bcopied->encoding_index = bfont->encoding_index;
+ bcopied->nearest_encoding_index = bfont->nearest_encoding_index;
+ }
+ return code;
+}
+
+/*
+ * Check whether specified glyphs can be copied from another font.
+ * It means that (1) fonts have same hinting parameters and
+ * (2) font subsets for the specified glyph set don't include different
+ * outlines or metrics. Possible returned values :
+ * 0 (incompatible), 1 (compatible), < 0 (error)
+ */
+int
+gs_copied_can_copy_glyphs(const gs_font *cfont, const gs_font *ofont,
+ gs_glyph *glyphs, int num_glyphs, int glyphs_step,
+ bool check_hinting)
+{
+ int code = 0;
+
+ if (cfont == ofont)
+ return 1;
+ if (cfont->FontType != ofont->FontType)
+ return 0;
+ if (cfont->WMode != ofont->WMode)
+ return 0;
+ if (cfont->font_name.size == 0 || ofont->font_name.size == 0) {
+ if (cfont->key_name.size != ofont->key_name.size ||
+ memcmp(cfont->key_name.chars, ofont->key_name.chars,
+ cfont->font_name.size))
+ return 0; /* Don't allow to merge random fonts. */
+ } else {
+ if (cfont->font_name.size != ofont->font_name.size ||
+ memcmp(cfont->font_name.chars, ofont->font_name.chars,
+ cfont->font_name.size))
+ return 0; /* Don't allow to merge random fonts. */
+ }
+ if (check_hinting) {
+ switch(cfont->FontType) {
+ case ft_encrypted:
+ case ft_encrypted2:
+ if (!same_type1_hinting((const gs_font_type1 *)cfont,
+ (const gs_font_type1 *)ofont))
+ return 0;
+ code = 1;
+ break;
+ case ft_TrueType:
+ code = same_type42_hinting((gs_font_type42 *)cfont,
+ (gs_font_type42 *)ofont);
+ if (code > 0)
+ code = same_maxp_values((gs_font_type42 *)cfont,
+ (gs_font_type42 *)ofont);
+ break;
+ case ft_CID_encrypted:
+ if (!gs_is_CIDSystemInfo_compatible(
+ gs_font_cid_system_info(cfont),
+ gs_font_cid_system_info(ofont)))
+ return 0;
+ code = same_cid0_hinting((const gs_font_cid0 *)cfont,
+ (const gs_font_cid0 *)ofont);
+ break;
+ case ft_CID_TrueType:
+ if (!gs_is_CIDSystemInfo_compatible(
+ gs_font_cid_system_info(cfont),
+ gs_font_cid_system_info(ofont)))
+ return 0;
+ code = same_cid2_hinting((const gs_font_cid2 *)cfont,
+ (const gs_font_cid2 *)ofont);
+ if (code > 0)
+ code = same_maxp_values((gs_font_type42 *)cfont,
+ (gs_font_type42 *)ofont);
+ break;
+ default:
+ return_error(gs_error_unregistered); /* Must not happen. */
+ }
+ if (code <= 0) /* an error or false */
+ return code;
+ }
+ return compare_glyphs(cfont, ofont, glyphs, num_glyphs, glyphs_step, 0);
+}
+
+/* Extension glyphs may be added to a font to resolve
+ glyph name conflicts while conwerting a PDF Widths into Metrics.
+ This function drops them before writing out an embedded font. */
+int
+copied_drop_extension_glyphs(gs_font *copied)
+{
+ /* Note : This function drops 'used' flags for some glyphs
+ and truncates glyph names. Can't use the font
+ for outlining|rasterization|width after applying it.
+ */
+ gs_copied_font_data_t *const cfdata = cf_data(copied);
+ uint gsize = cfdata->glyphs_size, i;
+ const int sl = strlen(gx_extendeg_glyph_name_separator);
+
+ for (i = 0; i < gsize; i++) {
+ gs_copied_glyph_t *pslot = &cfdata->glyphs[i];
+ gs_copied_glyph_name_t *name;
+ int l, j, k, i0;
+
+ if (!pslot->used)
+ continue;
+ name = &cfdata->names[i];
+ l = name->str.size - sl;
+
+ for (j = 0; j < l; j ++)
+ if (!memcmp(gx_extendeg_glyph_name_separator, name->str.data + j, sl))
+ break;
+ if (j >= l)
+ continue;
+ /* Found an extension name.
+ Find the corresponding non-extended one. */
+ i0 = i;
+ for (k = 0; k < gsize; k++)
+ if (cfdata->glyphs[k].used &&
+ cfdata->names[k].str.size == j &&
+ !memcmp(cfdata->names[k].str.data, name->str.data, j) &&
+ !bytes_compare(pslot->gdata.data, pslot->gdata.size,
+ cfdata->glyphs[k].gdata.data, cfdata->glyphs[k].gdata.size)) {
+ i0 = k;
+ break;
+ }
+ /* Truncate the extended glyph name. */
+ cfdata->names[i0].str.size = j;
+ /* Drop others with same prefix. */
+ for (k = 0; k < gsize; k++)
+ if (k != i0 && cfdata->glyphs[k].used &&
+ cfdata->names[k].str.size >= j + sl &&
+ !memcmp(cfdata->names[k].str.data, name->str.data, j) &&
+ !memcmp(gx_extendeg_glyph_name_separator, name + j, sl) &&
+ !bytes_compare(pslot->gdata.data, pslot->gdata.size,
+ cfdata->glyphs[k].gdata.data, cfdata->glyphs[k].gdata.size))
+ cfdata->glyphs[k].used = false;
+ }
+ return 0;
+}
+
+static int
+compare_glyph_names(const void *pg1, const void *pg2)
+{
+ const gs_copied_glyph_name_t * gn1 = *(const gs_copied_glyph_name_t **)pg1;
+ const gs_copied_glyph_name_t * gn2 = *(const gs_copied_glyph_name_t **)pg2;
+
+ return bytes_compare(gn1->str.data, gn1->str.size, gn2->str.data, gn2->str.size);
+}
+
+/* Order font data to avoid a serialization indeterminism. */
+static int
+order_font_data(gs_copied_font_data_t *cfdata, gs_memory_t *memory)
+{
+ int i, j = 0;
+
+ gs_copied_glyph_name_t **a = (gs_copied_glyph_name_t **)gs_alloc_byte_array(memory, cfdata->num_glyphs,
+ sizeof(gs_copied_glyph_name_t *), "order_font_data");
+ if (a == NULL)
+ return_error(gs_error_VMerror);
+ j = 0;
+ for (i = 0; i < cfdata->glyphs_size; i++) {
+ if (cfdata->glyphs[i].used) {
+ if (j >= cfdata->num_glyphs)
+ return_error(gs_error_unregistered); /* Must not happen */
+ a[j++] = &cfdata->names[i];
+ }
+ }
+ qsort(a, cfdata->num_glyphs, sizeof(*a), compare_glyph_names);
+ for (j--; j >= 0; j--)
+ cfdata->glyphs[j].order_index = a[j] - cfdata->names;
+ gs_free_object(memory, a, "order_font_data");
+ return 0;
+}
+
+/* Order font to avoid a serialization indeterminism. */
+int
+copied_order_font(gs_font *font)
+{
+
+ if (font->procs.enumerate_glyph != copied_enumerate_glyph)
+ return_error(gs_error_unregistered); /* Must not happen */
+ if (font->FontType != ft_encrypted && font->FontType != ft_encrypted2) {
+ /* Don't need to order, because it is ordered by CIDs or glyph indices. */
+ return 0;
+ }
+ { gs_copied_font_data_t * cfdata = cf_data(font);
+ cfdata->ordered = true;
+ return order_font_data(cfdata, font->memory);
+ }
+}
+
+/* Get .nmotdef glyph. */
+gs_glyph
+copied_get_notdef(const gs_font *font)
+{
+ gs_copied_font_data_t * cfdata = cf_data(font);
+
+ return cfdata->notdef;
+}
diff --git a/devices/gxfcopy.h b/devices/gxfcopy.h
new file mode 100644
index 000000000..c5353b6dc
--- /dev/null
+++ b/devices/gxfcopy.h
@@ -0,0 +1,192 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Font copying for high-level output */
+
+#ifndef gxfcopy_INCLUDED
+# define gxfcopy_INCLUDED
+
+#include "gsccode.h"
+
+#ifndef gs_font_DEFINED
+# define gs_font_DEFINED
+typedef struct gs_font_s gs_font;
+#endif
+
+#ifndef gs_matrix_DEFINED
+# define gs_matrix_DEFINED
+typedef struct gs_matrix_s gs_matrix;
+#endif
+
+/*
+ * Copy a font, aside from its glyphs. Note that PostScript-specific data
+ * -- that is, data that do not appear in the C structure that is the
+ * relevant subclass of gs_font -- are NOT copied. In particular,
+ * Metrics[2], CDevProc, and FontInfo are not copied, except for the
+ * information returned by font->procs.font_info (see the definition of
+ * gs_font_info_t in gxfont.h).
+ *
+ * Note that in some cases the font must have a definition for the "not
+ * defined" glyph, as noted below, or a rangecheck error occurs.
+ *
+ * The following FontTypes are supported:
+ *
+ * Type 1, Type 2 - Encoding and CharStrings are not copied. Subrs and
+ * GlobalSubrs (for CFFs) are copied; OtherSubrs are not copied. The
+ * font must have a glyph named .notdef; its definition is copied.
+ *
+ * Type 42 (TrueType) - Encoding and CharStrings are not copied. The
+ * TrueType glyf and loca tables are not copied, nor are the bogus
+ * Adobe gdir, glyx, and locx "tables". If the font has a definition
+ * for a glyph named .notdef (in the CharStrings dictionary), the
+ * definition is copied.
+ *
+ * CIDFontType 0 (Type 1/2-based CIDFonts) - the glyph data are not
+ * copied. The Type 1/2 subfonts *are* copied, as are the Subrs (both
+ * local and global).
+ *
+ * CIDFontType 2 (TrueType-based CIDFonts) - the glyph data and CIDMap
+ * are not copied.
+ *
+ * The resulting font supports querying (font_info, glyph_info, etc.) and
+ * rendering (glyph_outline, etc.), but it does not support make_font.
+ *
+ * max_reserved_glyphs limits the maount of space allocated for the glyph names
+ * and glyph outlines of type 1 or 2 fonts. -1 reserves as many as required for
+ * a full copy of the original font. This is used by pdfwrite to limit the size
+ * of a copy when creating a subset font.
+ */
+int gs_copy_font(gs_font *font, const gs_matrix *orig_matrix,
+ gs_memory_t *mem, gs_font **pfont_new,
+ int max_reserved_glyphs);
+
+int gs_free_copied_font(gs_font *font);
+
+/*
+ * Copy a glyph, including any sub-glyphs. The destination font ("copied"
+ * argument) must be a font created by gs_copy_font. The source font
+ * ("font" argument) must have the same FontType as the destination, and in
+ * addition must be "compatible" with it as described under the individual
+ * FontTypes just below; however, gs_copy_glyph does not check
+ * compatibility.
+ *
+ * If any glyph has already been copied but does not have the same
+ * definition as the one being copied now, gs_copy_glyph returns an
+ * invalidaccess error. If the top-level glyph has already been copied
+ * (with the same definition), gs_copy_glyph returns 1. Otherwise,
+ * gs_copy_glyph returns 0.
+ *
+ * Type 1, Type 2 - the destination and source must have the same
+ * Subrs. glyph must be a name (not an integer). Copies the
+ * CharString entry. Note that the Type 1/2 'seac' operator requires
+ * copying not only the sub-glyphs but their Encoding entries as well.
+ *
+ * Type 42 - the destination and source must have compatible tables
+ * other than glyf and loca. In practice this means that the source
+ * must be the same font that was passed to gs_copy_font. If glyph is
+ * an integer, it is interpreted as a GID; if glyph is a name, both
+ * the CharString entry and the glyph data are copied.
+ *
+ * CIDFontType 0 - the destination and source must have the same Subrs,
+ * and the Type 1/2 subfont(s) referenced by the glyph(s) being copied
+ * must be compatible. glyph must be a CID. Copies the CharString.
+ *
+ * CIDFontType 2 - compatibility is as for Type 42. glyph must be a
+ * CID (integer), not a GID. Copies the glyph data and the CIDMap
+ * entry.
+ *
+ * Metrics[2] and CDevProc in the source font are ignored. Currently,
+ * for CIDFontType 2 fonts with MetricsCount != 0, the metrics attached to
+ * the individual glyph outlines are also ignored (not copied).
+ */
+int gs_copy_glyph(gs_font *font, gs_glyph glyph, gs_font *copied);
+
+/*
+ * Copy a glyph with additional checking options. If options includes
+ * COPY_GLYPH_NO_OLD, then if the top-level glyph has already been copied,
+ * return an invalidaccess error rather than 1. If options includes
+ * COPY_GLYPH_NO_NEW, then if the top-level glyph has *not* already been
+ * copied, return an undefined error rather than 0.
+ *
+ * Returns an error if a glyph is added after calling copied_order_font.
+ */
+#define COPY_GLYPH_NO_OLD 1
+#define COPY_GLYPH_NO_NEW 2
+#define COPY_GLYPH_BY_INDEX 4
+int gs_copy_glyph_options(gs_font *font, gs_glyph glyph, gs_font *copied,
+ int options);
+
+int gs_copied_font_free_glyphs(gs_font *font);
+int gs_copied_font_free_data(gs_font *font);
+
+/*
+ * Add an encoding entry to a copied font. If the given encoding entry is
+ * not empty and is not the same as the new value, gs_copied_font_encode
+ * returns an invalidaccess error.
+ *
+ * The action depends on FontType as follows:
+ *
+ * Type 1, Type 2 - glyph must be a name, not a CID (integer). Makes
+ * an entry in the font's Encoding. A glyph by that name must exist
+ * in the copied font.
+ *
+ * Type 42 - same as Type 1.
+ *
+ * CIDFontType 0 - gives an error.
+ *
+ * CIDFontType 2 - gives an error.
+ */
+int gs_copied_font_add_encoding(gs_font *copied, gs_char chr, gs_glyph glyph);
+
+/*
+ * Copy all the glyphs and, if relevant, Encoding entries from a font. This
+ * is equivalent to copying the glyphs and Encoding entries individually,
+ * and returns errors under the same conditions.
+ */
+int gs_copy_font_complete(gs_font *font, gs_font *copied);
+
+/*
+ * Check whether specified glyphs can be copied from another font.
+ * It means that (1) fonts have same hinting parameters and
+ * (2) font subsets for the specified glyph set don't include different
+ * outlines or metrics. Possible returned values :
+ * 0 (incompatible), 1 (compatible), < 0 (error)
+ */
+int gs_copied_can_copy_glyphs(const gs_font *cfont, const gs_font *ofont,
+ gs_glyph *glyphs, int num_glyphs, int glyphs_step,
+ bool check_hinting);
+
+/* Extension glyphs may be added to a font to resolve
+ glyph name conflicts while conwerting a PDF Widths into Metrics.
+ This function drops them before writing out an embedded font. */
+int copied_drop_extension_glyphs(gs_font *cfont);
+
+/* Order font to avoid a serialization indeterminism.
+ An indeterminizm can happen due to PS name indices
+ depend on memory allocation.
+ Must not add glyphs after calling this function.
+ After calling this function, enumerate_glyph
+ enumerates glyphs in the alphabetic order of glyph names.
+ Currently works for Type 1,2 only,
+ because other fonts type don't use PS name indices
+ when enumerate glyphs.
+*/
+int copied_order_font(gs_font *font);
+
+/* Get .notdef glyph. */
+gs_glyph copied_get_notdef(const gs_font *font);
+
+#endif /* gxfcopy_INCLUDED */
diff --git a/devices/minftrsz.c b/devices/minftrsz.c
new file mode 100644
index 000000000..aaaf0ab53
--- /dev/null
+++ b/devices/minftrsz.c
@@ -0,0 +1,357 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+/* $Id */
+/* Support functions for 1bpp Fax/Tiff devices */
+#include "minftrsz.h"
+#include "gserrors.h"
+#include "gsmalloc.h"
+#include "memory_.h"
+
+/************************************************************************
+ *
+ * These functions implement raster data processing to ensure that the
+ * dot size and line width are at least some minimum number of pixels.
+ * This is needed for some printing technologies that cannot image pixels
+ * (or small groups of pixels) but that rasterize at full resolution to
+ * give better quality. The effect is to 'thicken' lines and expand
+ * independent pixels (which sets a minimum on the lightest halftoned
+ * gray value).
+ *
+ * While some of this can be done by setting a minimum on linewidth, this
+ * would have no effect on features painted with narrow fills or with
+ * monochrome images (sometimes pre-halftoned).
+ *
+ ************************************************************************/
+
+typedef struct min_feature_data_s {
+ gs_memory_t *memory;
+ int min_size;
+ int width; /* Pixels per line */
+ int height;
+ int cur_line;
+ int bytes_per_line;
+ byte *lines; /* vertical history lines */
+ byte *lines_prev[8]; /* pointers to previous lines */
+ /* lines_prev[0] is most recent */
+ byte remap_mid8[65536]; /* Horizontal pixel expansion map */
+ /* The h_remap array is indexed by the data byte with the 'extra' */
+ /* min_size bits to the left and right of the byte. Thus 16 bits */
+ /* needed for 8 bits plus the preceding and following 4 bits needed */
+ /* for min_size of 4. This is probably adequate for current users */
+ /* the beginning and end of a line are special since they need */
+ /* to darken extra pixels within the line bounds */
+ byte remap_first4[256];
+ byte remap_last4[256];
+} min_feature_data_t;
+
+static int
+next_zero(int startbit, int val)
+{
+ int i;
+
+ for (i=startbit-1; i>=0; i--)
+ if ((val & 1<<i) == 0)
+ return i;
+ return -1;
+}
+
+static int
+next_one(int startbit, int val)
+{
+ int i;
+
+ for (i=startbit-1; i>=0; i--)
+ if ((val & 1<<i) != 0)
+ return i;
+ return -1;
+}
+
+/* Note linewidth is number of pixels -- needed for the end of line */
+int
+min_feature_size_init(gs_memory_t *mem, int min_feature_size,
+ int width, int height, void **min_feature_data)
+{
+ min_feature_data_t *data;
+ int i, bytes_per_line;
+
+ if (min_feature_size > 4)
+ return_error(gs_error_limitcheck);
+ /* allocate our data structure and vertical tracking buffer */
+ if ((data = (min_feature_data_t *)gs_malloc(mem, 1, sizeof(min_feature_data_t),
+ "mem_feature_size(data)")) == NULL)
+ return_error(gs_error_VMerror);
+ bytes_per_line = (width+7)/8;
+ if ((data->lines = (byte *)gs_malloc(mem, bytes_per_line, 2 * min_feature_size,
+ "mem_feature_size(lines)")) == NULL) {
+ gs_free(mem, data, 1, sizeof(min_feature_data_t),
+ "mem_feature_size(data)");
+ return_error(gs_error_VMerror);
+ }
+ data->memory = mem;
+ data->width = width;
+ data->height = height;
+ data->cur_line = -1;
+ data->min_size = min_feature_size;
+ data->bytes_per_line = bytes_per_line;
+ memset(data->lines, 0, bytes_per_line * 2 * min_feature_size);
+ for(i=0; i<2*min_feature_size; i++)
+ data->lines_prev[i] = data->lines + (bytes_per_line * i);
+
+#define bm(b) /* bitmask(b) 0 is lsb */\
+ (1<<(b))
+
+ /* build the 'remap' data for the min_feature_size */
+ for (i=0; i<256; i++) {
+ int f = i, l = i, fd = 8, fw;
+
+ do {
+ fd = next_one(fd, f); /* value == -1 if past bit_0 */
+ if (fd < 0)
+ break;
+ fw = next_zero(fd, f); /* value == -1 if past bit_0 */
+ if ((fd - fw) < min_feature_size) {
+ /* darken (set to 0) bits needed depending on min_feature_size */
+ /* 'first' and 'last' are different because we need to expand */
+ /* within the bits, i.e., right of bit 7 for first and left of */
+ /* bit 0 for last */
+ /* Note that we will only be using the left most bits of first */
+ /* and the right most bits of last. */
+ switch (min_feature_size) {
+ case 2:
+ /* current feature size is 1, darken bit to the right */
+ /* unless it is past the end of the byte in 'last' */
+ if (fd > 0) {
+ f |= bm(fw);
+ l |= bm(fw);
+ } else /* fd == 0 */
+ l |= 0x03; /* darken to the left of lsb */
+ break;
+ case 3:
+ /* This case is more complicated -- we want to darken about */
+ /* the center if the current size is 1, else darken a bit */
+ /* to the right, with the constraints of staying within the */
+ /* byte (bits 7::0). (fd-1) is to the right. */
+ if ((fd < 7) && (fd > 0)) {
+ /* within byte, left and right */
+ /* darkening is referenced from 'fw' since that is the */
+ /* white bit to the right of the 1 or 2 pixel wide area */
+ f |= bm(fw+2) | bm(fd-2);
+ l |= bm(fw+2) | bm(fd-2);
+ } else if (fd == 7) {
+ f |= 0xe0; /* darken top three pixels */
+ } else { /* fd == 0 */
+ f |= 0x07; /* darken bottom three pixels */
+ l |= 0x07;
+ }
+ break;
+ case 4:
+ /* like case 3, except we prefer to darken one to the left */
+ /* and two to the right. */
+ if ((fd < 7) && (fd > 1)) {
+ /* within byte, left and right */
+ f |= bm(fw+2) | bm(fd-1) | bm(fd-2);
+ l |= bm(fw+2) | bm(fd-1) | bm(fd-2);
+ } else if (fd == 7) {
+ /* darken high 4 bits */
+ f |= 0xfd;
+ } else { /* fd <= 1 */
+ /* darken final 4 bits */
+ f |= 0x0f;
+ l |= 0x0f;
+ }
+ break;
+ default: /* don't change the data (shouldn't be here) */
+ break;
+ }
+ }
+ fd = next_zero(fd, f); /* advance past this group of '1' bits */
+ /* we 'seek' again because data may */
+ /* have changed due to darkening right */
+ } while (fd >= 0);
+ /* finished with the whole byte. Save the results */
+ data->remap_first4[i] = f;
+ data->remap_last4[i] = l;
+ }
+ for (i=0; i<65536; i++) {
+ int d = i, fd = 16, fw;
+
+ do {
+ fd = next_one(fd, d); /* value == -1 if past bit_0 */
+ if (fd < 0)
+ break;
+ fw = next_zero(fd, d); /* value == -1 if past bit_0 */
+ if ((fd - fw) < min_feature_size) {
+ /* darken (set to 0) bits needed depending on min_feature_size */
+ /* 'first' and 'last' are different because we need to expand */
+ /* within the bits, i.e., right of bit 7 for first and left of */
+ /* bit 0 for last */
+ /* Note that we will only be using the left most bits of first */
+ /* and the right most bits of last. */
+ switch (min_feature_size) {
+ case 2:
+ /* current feature size is 1, darken bit to the right */
+ if (fd > 0) {
+ d |= bm(fw);
+ } else /* fd == 0 */
+ d |= 0x0003; /* two lsb's darkened */
+ break;
+ case 3:
+ /* This case is more complicated -- we want to darken about */
+ /* the center but bias darkening to the right */
+ if ((fd < 15) && (fd > 0)) {
+ /* within value, left and right */
+ d |= bm(fw+2) | bm(fd-1);
+ } else if (fd == 15) {
+ d |= 0xe000; /* darken top three */
+ } else { /* fd == 0 */
+ d |= 0x0007; /* darken three lsb's */
+ }
+ break;
+ case 4:
+ /* like case 3, except we prefer to darken one to the left */
+ /* and two to the right. */
+ if ((fd < 15) && (fd > 1)) {
+ /* within byte, left and right */
+ d |= bm(fw+2) | bm(fd-1) | bm(fd-2);
+ } else if (fd == 15) {
+ d &= 0xf000; /* darken top 4 pixels */
+ } else { /* fd <= 1 */
+ d &= 0x000f; /* darken last 4 pixels */
+ }
+ break;
+ default: /* don't change the data (shouldn't be here) */
+ break;
+ }
+ }
+ fd = next_zero(fd, d); /* advance past this group of '1' bits */
+ /* we 'seek' again because data may */
+ /* have changed due to darkening right */
+ } while (fd >= 0);
+ /* finished with the whole short. Save the byte in the middle */
+ data->remap_mid8[i] = (d >> 4) & 0xff;
+ }
+ *min_feature_data = data; /* give the caller our pointer */
+ return 0;
+}
+
+int
+min_feature_size_dnit(void *min_feature_data)
+{
+ min_feature_data_t *data = min_feature_data;
+
+ if (data != NULL) {
+ if (data->lines != NULL)
+ gs_free(data->memory, data->lines, data->bytes_per_line,
+ 2*data->min_size, "mem_feature_size(lines)");
+ gs_free(data->memory, data, 1, sizeof(min_feature_data_t),
+ "mem_feature_size(data)");
+ }
+ return 0;
+}
+
+/* Return number of byte available */
+int
+min_feature_size_process(byte *line, void *min_feature_data)
+{
+ min_feature_data_t *data = min_feature_data;
+ ushort d;
+ byte n, m, *btmp;
+ int i, pad_bits = (8 - (data->width & 7)) & 7; /* range 0 to 7 */
+ int bytes_per_line = (data->width+7)/8, end = bytes_per_line - 2;
+ int count_out = 0; /* bytes_per_line if line is output */
+
+ data->cur_line++;
+ d = data->remap_first4[line[0]]; /* darken bits toward the right */
+ d <<= 4; /* shift up to enter loop */
+
+ for (i=0; i<=end; i++) { /* stop short of 'end' */
+ n = line[i+1];
+ d |= n >> 4;
+ m = data->remap_mid8[d]; /* middle byte with bits darkened */
+ line[i] = m;
+ d |= m << 4;
+ d = ((d<<4) | n) << 4;
+ }
+ /* Final bits require alignment for the last 4 bits */
+ d = (((line[i-1] << 8) | line[i]) >> pad_bits) & 0xff;
+ m = data->remap_last4[d];
+ line[i-1] |= m >> (8 - pad_bits);
+ line[i] |= m << pad_bits;
+
+ /* Now handle the vertical darkening */
+ /* shift lines up by adjusting pointers */
+ btmp = data->lines_prev[2*(data->min_size)-1]; /* oldest line */
+ for (i=2*(data->min_size)-1; i>0; i--)
+ data->lines_prev[i] = data->lines_prev[i-1];
+ data->lines_prev[0] = btmp;
+
+ /* Copy this input line into the most recent lines_prev: lines_prev[0] */
+ memcpy(data->lines_prev[0], line, bytes_per_line);
+
+ switch (data->min_size) {
+ case 4:
+ /**** TBI ****/
+ case 3:
+ /**** TBI ****/
+
+ case 2:
+ if (data->cur_line >= data->height - 1) {
+ if (data->cur_line == data->height - 1) {
+ /* end of page must darken next to last line*/
+ for (i=0; i<bytes_per_line; i++)
+ line[i] = data->lines_prev[1][i] |= data->lines_prev[0][i];
+ } else {
+ /* very last line is unchanged, ignore input line in lines_prev[0] */
+ for (i=0; i<bytes_per_line; i++)
+ line[i] = data->lines_prev[1][i];
+ }
+ } else {
+ /* darken bits in lines_prev[1] */
+ /* Since we are darkening in following lines, we only really need 3 lines */
+ for (i=0; i<bytes_per_line; i++) {
+ data->lines_prev[0][i] |= data->lines_prev[1][i] & ~(data->lines_prev[2][i]);
+ line[i] = data->lines_prev[1][i];
+ }
+ }
+ if (data->cur_line >= 1)
+ count_out = bytes_per_line;
+ break;
+ default:
+ break;
+ }
+ return count_out;
+}
+
+int
+fax_adjusted_width(int width, int adjust_width)
+{
+ if (adjust_width <= 0)
+ return width;
+ if (adjust_width == 1) {
+ /* Adjust the page width to a legal value for fax systems. */
+ if (width >= 1680 && width <= 1736)
+ /* Adjust width for A4 paper. */
+ return 1728;
+ else if (width >= 2000 && width <= 2056)
+ /* Adjust width for B4 paper. */
+ return 2048;
+ else
+ return width;
+ } else {
+ /* Adjust for the user-defined width. */
+ return adjust_width;
+ }
+}
diff --git a/devices/minftrsz.h b/devices/minftrsz.h
new file mode 100644
index 000000000..e5e020e90
--- /dev/null
+++ b/devices/minftrsz.h
@@ -0,0 +1,32 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+/* Minimum feature size support function definitions for fax/tiff devices */
+
+#ifndef minftrsz_INCLUDED
+# define minftrsz_INCLUDED
+
+#include "std.h"
+
+int min_feature_size_init(gs_memory_t *mem, int min_feature_size,
+ int width, int height, void **min_feature_data);
+
+int min_feature_size_dnit(void *min_feature_data);
+
+int min_feature_size_process(byte *line, void *min_feature_data);
+
+int fax_adjusted_width(int width, int adjust_width);
+
+#endif /* minftrsz_INCLUDED */
diff --git a/devices/rinkj/evenbetter-rll.c b/devices/rinkj/evenbetter-rll.c
new file mode 100644
index 000000000..692c0d8ad
--- /dev/null
+++ b/devices/rinkj/evenbetter-rll.c
@@ -0,0 +1,1802 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Testbed implementation of Even Better Screening. */
+
+/*
+ * Code in this module is covered by US Patents 5,055,942 and
+ * 5,917,614, and corresponding international patents.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include "evenbetter-rll.h"
+
+/* Set this define if compiling with AltiVec optimizations. */
+#define noUSE_AVEC
+
+/* Set this define if compiling with SSE optimizations. */
+#define noUSE_SSE2
+
+#define EVENBETTER_VERSION 133
+
+#define EVEN_SHIFT 16
+#define IMO_SHIFT 14
+#define EVEN_RB_CAP (1 << (EVEN_SHIFT - 2))
+
+#define FANCY_COUPLING
+
+#if defined(USE_AVEC) || defined(USE_SSE2)
+#define USE_VECTOR
+#endif
+
+#ifdef USE_AVEC
+#include "eb_avec.h"
+
+#endif
+
+#ifdef USE_SSE2
+typedef struct _eb_ctx_sse2 eb_ctx_sse2;
+typedef struct _eb_srcbuf eb_srcbuf;
+
+int eb_test_sse2(void);
+int eb_sse2_core(eb_ctx_sse2 *ctx, unsigned char **out, eb_srcbuf *in,
+ int offset);
+int eb_sse2_rev_rs(eb_ctx_sse2 *ctx, int offset);
+int eb_sse2_set_daz(void);
+void eb_sse2_restore_daz(int save_mxcsr);
+
+struct _eb_ctx_sse2 {
+ int xs;
+ int *iir_line;
+ int *r_line;
+ int *a_line;
+ int *b_line;
+ char *skip_line;
+ int dummy[2];
+ float *luts[4];
+ float e[4];
+ float e_i_1[4];
+ int r[4];
+ int a[4];
+ int b[4];
+ int ones[4];
+ int twos[4];
+ int aspect2[4];
+ float ehi[4];
+ float elo[4];
+ float ohi[4];
+ float r_mul[4];
+ float kernel[4];
+ unsigned int seed1[4];
+ unsigned int seed2[4];
+};
+
+struct _eb_srcbuf {
+ float im[64];
+ float rb[64];
+ float rs[64];
+ int dummy[3];
+};
+
+#endif
+
+typedef struct _EBPlaneCtx EBPlaneCtx;
+typedef unsigned int uint32;
+
+struct _EvenBetterCtx {
+ int source_width;
+ int dest_width;
+ int n_planes;
+ int levels; /* Number of levels on output, <= 256 */
+ EBPlaneCtx **plane_ctx;
+ int aspect;
+ int *strengths;
+ int even_elo;
+ int even_ehi;
+ int *c_line;
+
+ int even_c1;
+ int do_shadows;
+
+ uint32 seed1;
+ uint32 seed2;
+
+ FILE *dump_file;
+ EbDumpLevel dump_level;
+
+#ifdef USE_SSE2
+ eb_ctx_sse2 **sse2_ctx;
+ int using_vectors;
+#endif
+#ifdef USE_AVEC
+ eb_ctx_avec **avec_ctx;
+ int using_vectors;
+#endif
+};
+
+struct _EBPlaneCtx {
+ int source_width;
+ int dest_width;
+ int *rb_line;
+ int *iir_line;
+ int *r_line;
+ int *a_line;
+ int *b_line;
+ int *r_line_sh;
+ int *a_line_sh;
+ int *b_line_sh;
+ int *lut;
+ int *rb_lut;
+ char *rs_lut;
+ int *white_count_line;
+};
+
+void *
+eb_malloc_aligned(int size, int align)
+{
+ void *result;
+ void *alloced = malloc(size + align);
+ int pad;
+
+ if (alloced == 0)
+ return 0;
+ pad = (((int)(size_t)alloced + 12) & 15) + 4;
+ result = (void *)(pad + (char *)alloced);
+ ((int *)result)[-1] = pad;
+ return result;
+}
+
+void
+eb_free_aligned(void *p)
+{
+ int pad = ((int *)p)[-1];
+ free((char*)p - pad);
+}
+
+static double
+eb_compute_rbscale(const EvenBetterParams *params)
+{
+ double rbscale = params->rbscale;
+
+ if (rbscale == 0.0)
+ {
+ rbscale = params->aspect == 1 ? 0.95 :
+ params->aspect == 2 ? 1.8 :
+ params->aspect == 4 ? 3.6 : 1;
+ }
+ return rbscale;
+}
+
+static int
+eb_compute_randshift(int nl, int rs_base, int do_shadows, int levels)
+{
+ int rs = rs_base;
+ if ((nl > (90 << (EVEN_SHIFT - 10)) &&
+ nl < (129 << (EVEN_SHIFT - 10))) ||
+ (nl > (162 << (EVEN_SHIFT - 10)) &&
+ nl < (180 << (EVEN_SHIFT - 10))))
+ rs--;
+ else if (nl > (321 << (EVEN_SHIFT - 10)) &&
+ nl < (361 << (EVEN_SHIFT - 10)))
+ {
+ rs--;
+ if (nl > (331 << (EVEN_SHIFT - 10)) &&
+ nl < (351 << (EVEN_SHIFT - 10)))
+ rs--;
+ }
+ else if ((do_shadows ||
+ nl == (levels - 1) << EVEN_SHIFT) &&
+ nl > ((levels - 1) << EVEN_SHIFT) -
+ (1 << (EVEN_SHIFT - 2)))
+ {
+ /* don't add randomness in extreme shadows */
+ }
+ else if ((nl > (3 << (EVEN_SHIFT - 2))))
+ {
+ nl -= (nl + (1 << (EVEN_SHIFT - 2))) & -(1 << (EVEN_SHIFT - 1));
+ if (nl < 0) nl = -nl;
+ if (nl < (1 << (EVEN_SHIFT - 4))) rs--;
+ if (nl < (1 << (EVEN_SHIFT - 5))) rs--;
+ if (nl < (1 << (EVEN_SHIFT - 6))) rs--;
+ }
+ else
+ {
+ if (nl < (3 << (EVEN_SHIFT - 3))) nl += 1 << (EVEN_SHIFT - 2);
+ nl = nl - (1 << (EVEN_SHIFT - 1));
+ if (nl < 0) nl = -nl;
+ if (nl < (1 << (EVEN_SHIFT - 4))) rs--;
+ if (nl < (1 << (EVEN_SHIFT - 5))) rs--;
+ if (nl < (1 << (EVEN_SHIFT - 6))) rs--;
+ }
+ return rs;
+}
+
+#ifdef USE_SSE2
+static eb_ctx_sse2 *
+eb_ctx_sse2_new(const EvenBetterParams *params, int start_plane, int end_plane)
+{
+ int xs = params->source_width;
+ int aspect2 = params->aspect * params->aspect;
+ eb_ctx_sse2 *ctx;
+ int i;
+ double im_scale;
+ float r_mul = 1.0 / (params->aspect * (1 << (6 - params->even_c1_scale)));
+ double rbscale = eb_compute_rbscale(params);
+ int rs_base;
+
+ ctx = (eb_ctx_sse2 *)eb_malloc_aligned(sizeof(eb_ctx_sse2), 16);
+ ctx->xs = xs;
+ for (i = 0; i < 4; i++)
+ {
+ ctx->e[i] = 0.0;
+ ctx->e_i_1[i] = 0.0;
+ ctx->r[i] = 0;
+ ctx->a[i] = 1;
+ ctx->b[i] = aspect2;
+ ctx->ones[i] = 1;
+ ctx->twos[i] = 2;
+ ctx->aspect2[i] = aspect2;
+ ctx->ohi[i] = params->levels - 1;
+ ctx->ehi[i] = 1.1;
+ ctx->elo[i] = -0.1;
+ ctx->r_mul[i] = r_mul;
+ ctx->seed1[i] = (i << 8) + 0x7000;
+ ctx->seed2[i] = (i << 16) + 0x9000;
+ }
+ ctx->kernel[0] = 1.0 / 16;
+ ctx->kernel[1] = 3.0 / 16;
+ ctx->kernel[2] = 5.0 / 16;
+ ctx->kernel[3] = 7.0 / 16;
+
+ im_scale = (params->levels - 1) * 1.0 / (1 << 24);
+ rs_base = 35 - EVEN_SHIFT - params->rand_scale;
+
+ for (i = start_plane; i < end_plane; i++)
+ {
+ float *lut = (float *)malloc((ET_SRC_MAX + 1) * sizeof(float) * 3);
+ int j;
+ ctx->luts[i - start_plane] = lut;
+
+ for (j = 0; j < ET_SRC_MAX + 1; j++)
+ {
+ double g = ((1 << 24) - params->luts[i][j]) * im_scale;
+ int nl, rs;
+
+ lut[j * 3] = g;
+ if (g == 0.0)
+ lut[j * 3 + 1] = 0.5;
+ else
+ lut[j * 3 + 1] = 0.5 - r_mul * rbscale / g;
+
+ nl = (params->levels - 1 - g) * (1 << EVEN_SHIFT);
+ rs = eb_compute_randshift(nl, rs_base,
+ params->do_shadows, params->levels);
+
+ lut[j * 3 + 2] = 1.0 / (1 << EVEN_SHIFT) / (1 << rs);
+ }
+ }
+ for (i = i - start_plane; i < 4; i++)
+ ctx->luts[i] = NULL;
+
+ ctx->iir_line = (int *)eb_malloc_aligned(16 * (xs + 32), 16);
+ ctx->a_line = (int *)eb_malloc_aligned(16 * (xs + 32), 16);
+ ctx->b_line = (int *)eb_malloc_aligned(16 * (xs + 32), 16);
+ ctx->r_line = (int *)eb_malloc_aligned(16 * (xs + 32), 16);
+ for (i = 0; i < (xs + 32) * 4; i++)
+ {
+ ((float *)ctx->iir_line)[i] = 0;
+ ctx->a_line[i] = 1;
+ ctx->b_line[i] = aspect2;
+ ctx->r_line[i] = 0;
+ }
+
+ ctx->skip_line = (char *)malloc((xs + 15) & -16);
+
+ return ctx;
+}
+
+static void
+eb_ctx_sse2_free(eb_ctx_sse2 *ctx)
+{
+ int i;
+
+ for (i = 0; i < 4; i++)
+ free(ctx->luts[i]);
+ eb_free_aligned(ctx->iir_line);
+ eb_free_aligned(ctx->a_line);
+ eb_free_aligned(ctx->b_line);
+ eb_free_aligned(ctx->r_line);
+ free(ctx->skip_line);
+ eb_free_aligned(ctx);
+}
+#endif
+
+#ifdef USE_AVEC
+static eb_ctx_avec *
+eb_ctx_avec_new(const EvenBetterParams *params, int start_plane, int end_plane)
+{
+ int xs = params->source_width;
+ int aspect2 = params->aspect * params->aspect;
+ eb_ctx_avec *ctx;
+ int i;
+ double im_scale;
+ double k;
+ float imscale1, imscale2, rbmul, rsbase;
+ float r_mul = 1.0 / (params->aspect * (1 << (6 - params->even_c1_scale)));
+ double rbscale = eb_compute_rbscale(params);
+ vector unsigned int zero = vec_splat_u32(0);
+ const vector float kernel = { 1.0 / 16, 3.0 / 16, 5.0 / 16, 7.0 / 16 };
+ vector float almostone = { 255.0/256, 255.0/256, 255.0/256, 255.0/256 };
+ int rs_base;
+
+ ctx = (eb_ctx_avec *)eb_malloc_aligned(sizeof(eb_ctx_avec), 16);
+ ctx->xs = xs;
+
+ ctx->e = (vector float) zero;
+ ctx->e_i_1 = (vector float) zero;
+ ctx->r = zero;
+ ctx->a = zero;
+ im_scale = (params->levels - 1) * (1.0 / (1 << 24));
+ rs_base = 35 - EVEN_SHIFT - params->rand_scale;
+
+ if (params->gamma == 1.0)
+ k = 0;
+ else if (params->gamma == 1.8)
+ k = 0.835;
+ else if (params->gamma == 2.0)
+ k = 1.0;
+ else
+ /* this shouldn't happen! */
+ k = 0;
+
+ for (;;)
+ {
+ vector float foff, f0, f1;
+
+ imscale1 = (1 - k) * (params->levels - 1) * (256.0 / 255.0);
+ imscale2 = k * (params->levels - 1) * sqrt(256.0 / 255.0);
+ for (i = 0; i < 4; i++)
+ {
+ ((float *)&ctx->imscale1)[i] = imscale1;
+ ((float *)&ctx->imscale2)[i] = imscale2;
+ }
+ f0 = vec_rsqrte(almostone);
+ f0 = vec_madd(f0, almostone, (vector float)zero);
+ f1 = vec_madd(f0, ctx->imscale2, (vector float)zero);
+ foff = vec_madd(almostone, ctx->imscale1, f1);
+ f1 = vec_nmsub(f0, ctx->imscale2, foff);
+ f1 = vec_nmsub(almostone, ctx->imscale1, f1);
+ if (vec_all_eq(f1, (vector float)zero))
+ {
+ ctx->foff = foff;
+ break;
+ }
+ k += 1e-5;
+ }
+ rbmul = -r_mul * rbscale;
+ rsbase = 1.0 / (1 << EVEN_SHIFT) / (1 << rs_base);
+ for (i = 0; i < 4; i++)
+ {
+ ((int *)&ctx->b)[i] = aspect2;
+ ((int *)&ctx->aspect2)[i] = aspect2;
+ ((int *)&ctx->seed1)[i] = (i << 8) + 0x7000;
+ ((int *)&ctx->seed2)[i] = (i << 16) + 0x9000;
+ ((float *)&ctx->ohi)[i] = params->levels - 1;
+ ((float *)&ctx->ehi)[i] = 1.1;
+ ((float *)&ctx->elo)[i] = -0.1;
+ ((float *)&ctx->r_mul)[i] = r_mul;
+ ((float *)&ctx->rsbase)[i] = rsbase;
+ ((float *)&ctx->rbmul)[i] = rbmul;
+ }
+ ctx->kernel = kernel;
+
+ rs_base = 35 - EVEN_SHIFT - params->rand_scale;
+
+ for (i = start_plane; i < end_plane; i++)
+ {
+ float *lut = (float *)malloc((ET_SRC_MAX + 1) * sizeof(float) * 3);
+ int j;
+ ctx->luts[i - start_plane] = lut;
+
+ for (j = 0; j < ET_SRC_MAX + 1; j++)
+ {
+ double g = ((1 << 24) - params->luts[i][j]) * im_scale;
+ int nl, rs;
+
+ lut[j * 3] = g;
+ if (g == 0.0)
+ lut[j * 3 + 1] = 0.5;
+ else
+ lut[j * 3 + 1] = 0.5 - r_mul * rbscale / g;
+ nl = (params->levels - 1 - g) * (1 << EVEN_SHIFT);
+ rs = eb_compute_randshift(nl, rs_base,
+ params->do_shadows, params->levels);
+
+ lut[j * 3 + 2] = 1.0 / (1 << EVEN_SHIFT) / (1 << rs);
+ }
+ }
+ for (i = i - start_plane; i < 4; i++)
+ ctx->luts[i] = NULL;
+
+ ctx->iir_line = (vector float *)eb_malloc_aligned(16 * (xs + 32), 16);
+ ctx->a_line = (vector unsigned int *)eb_malloc_aligned(16 * (xs + 32), 16);
+ ctx->b_line = (vector unsigned int *)eb_malloc_aligned(16 * (xs + 32), 16);
+ ctx->r_line = (vector unsigned int *)eb_malloc_aligned(16 * (xs + 32), 16);
+ for (i = 0; i < (xs + 32) * 4; i++)
+ {
+ ((float *)ctx->iir_line)[i] = 0;
+ ((int *)ctx->a_line)[i] = 1;
+ ((int *)ctx->b_line)[i] = aspect2;
+ ((int *)ctx->r_line)[i] = 0;
+ }
+
+ ctx->skip_line = (char *)malloc((xs + 15) & -16);
+
+ return ctx;
+}
+
+static void
+eb_ctx_avec_free(eb_ctx_avec *ctx)
+{
+ int i;
+
+ for (i = 0; i < 4; i++)
+ free(ctx->luts[i]);
+ eb_free_aligned(ctx->iir_line);
+ eb_free_aligned(ctx->a_line);
+ eb_free_aligned(ctx->b_line);
+ eb_free_aligned(ctx->r_line);
+ free(ctx->skip_line);
+ eb_free_aligned(ctx);
+}
+
+#endif
+
+#ifdef USE_VECTOR
+static int
+even_better_line_vector(EvenBetterCtx *ebc, uchar **dest,
+ const ET_Rll *const *src)
+{
+ int n_planes = ebc->n_planes;
+ int xd = ebc->dest_width;
+ int strip;
+ eb_srcbuf sb_alloc;
+ eb_srcbuf *srcbuf;
+ uchar dummy_a[32];
+ uchar *dummy_dst = (uchar *)(((int)dummy_a + 15) & -16);
+#ifdef USE_SSE2
+ int save_mxcsr = eb_sse2_set_daz();
+#endif
+
+ srcbuf = (eb_srcbuf *)(((int)&sb_alloc + 12) & -16);
+
+ for (strip = 0; strip < n_planes; strip += 4)
+ {
+#ifdef USE_AVEC
+ eb_ctx_avec *ctx = ebc->avec_ctx[strip >> 2];
+#endif
+#ifdef USE_SSE2
+ eb_ctx_sse2 *ctx = ebc->sse2_ctx[strip >> 2];
+#endif
+ uchar *destbufs[4];
+ const ET_Rll *const *sbuf = src + strip;
+ int count[4];
+ int src_idx[4];
+ int plane_idx, last_plane;
+ float im[4], rb[4], rs[4];
+ int i;
+
+ last_plane = n_planes - strip < 4 ? n_planes - strip : 4;
+ for (plane_idx = 0; plane_idx < last_plane; plane_idx++)
+ {
+ count[plane_idx] = 0;
+ src_idx[plane_idx] = 0;
+ destbufs[plane_idx] = dest[plane_idx + strip];
+ }
+ for (; plane_idx < 4; plane_idx++)
+ {
+ int j;
+
+ for (j = 0; j < 16; j++)
+ {
+ ((float *)&srcbuf->im)[j * 4 + plane_idx] = 0.0;
+ ((float *)&srcbuf->rb)[j * 4 + plane_idx] = 0.0;
+ ((float *)&srcbuf->rs)[j * 4 + plane_idx] = 0.0;
+ }
+ }
+ for (i = 0; i < xd; i += 16)
+ {
+ int jmax = (xd - i) > 16 ? 16 : xd - i;
+ int skip = 1;
+ int j;
+
+ for (plane_idx = 0; plane_idx < last_plane; plane_idx++)
+ {
+ if (count[plane_idx] < 16 || im[plane_idx] != 0.0)
+ {
+ skip = 0;
+ break;
+ }
+ }
+ ctx->skip_line[i >> 4] = skip;
+
+ if (skip)
+ {
+ /* all white */
+
+ for (plane_idx = 0; plane_idx < last_plane; plane_idx++)
+ {
+ uchar *dst_ptr = destbufs[plane_idx];
+ if (jmax == 16)
+ {
+ ((uint32 *)dst_ptr)[(i >> 2) + 0] = 0;
+ ((uint32 *)dst_ptr)[(i >> 2) + 1] = 0;
+ ((uint32 *)dst_ptr)[(i >> 2) + 2] = 0;
+ ((uint32 *)dst_ptr)[(i >> 2) + 3] = 0;
+ }
+ else
+ {
+ for (j = 0; j < jmax; j++)
+ dst_ptr[i + j] = 0;
+ }
+ count[plane_idx] -= jmax;
+ }
+ }
+ else
+ {
+ for (plane_idx = 0; plane_idx < last_plane; plane_idx++)
+ {
+ const float *lut = ctx->luts[plane_idx];
+ float imp = im[plane_idx];
+ float rbp = rb[plane_idx];
+ float rsp = rs[plane_idx];
+ for (j = 0; j < jmax; j++)
+ {
+ if (count[plane_idx] == 0)
+ {
+ const ET_Rll *src_p = sbuf[plane_idx] +
+ src_idx[plane_idx]++;
+ ET_SrcPixel src_pixel = src_p->value;
+ count[plane_idx] = src_p->length;
+ imp = lut[src_pixel * 3];
+ rbp = lut[src_pixel * 3 + 1];
+ rsp = lut[src_pixel * 3 + 2];
+ }
+ ((float *)&srcbuf->im)[j * 4 + plane_idx] = imp;
+ ((float *)&srcbuf->rb)[j * 4 + plane_idx] = rbp;
+ ((float *)&srcbuf->rs)[j * 4 + plane_idx] = rsp;
+ count[plane_idx]--;
+ }
+ im[plane_idx] = imp;
+ rb[plane_idx] = rbp;
+ rs[plane_idx] = rsp;
+ }
+ for (; plane_idx < 4; plane_idx++)
+ {
+ destbufs[plane_idx] = dummy_dst - i;
+ }
+#ifdef USE_AVEC
+ eb_avec_core(ctx, (vector unsigned char **)destbufs, srcbuf, i);
+#endif
+#ifdef USE_SSE2
+ eb_sse2_core(ctx, destbufs, srcbuf, i);
+#endif
+ }
+ }
+
+ for (i = xd & -16; i >= 0; i -= 16)
+ {
+ if (!ctx->skip_line[i >> 4])
+ {
+#ifdef USE_AVEC
+ eb_avec_rev_rs(ctx, i + 15);
+#endif
+#ifdef USE_SSE2
+ eb_sse2_rev_rs(ctx, i + 15);
+#endif
+ }
+ }
+ }
+#ifdef USE_SSE2
+ eb_sse2_restore_daz(save_mxcsr);
+#endif
+ return 0;
+}
+#endif
+
+#ifdef USE_AVEC
+static int
+even_better_line_fastprep(EvenBetterCtx *ebc, uchar **dest,
+ const ET_SrcPixel *const *src)
+{
+ int n_planes = ebc->n_planes;
+ int xd = ebc->dest_width;
+ int strip;
+ eb_srcbuf sb_alloc;
+ eb_srcbuf *srcbuf;
+ uchar dummy_a[32];
+ uchar *dummy_dst = (uchar *)(((int)dummy_a + 15) & -16);
+
+ srcbuf = (eb_srcbuf *)(((int)&sb_alloc + 12) & -16);
+
+ for (strip = 0; strip < n_planes; strip += 4)
+ {
+#ifdef USE_AVEC
+ eb_ctx_avec *ctx = ebc->avec_ctx[strip >> 2];
+#endif
+#ifdef USE_SSE2
+ eb_ctx_sse2 *ctx = ebc->sse2_ctx[strip >> 2];
+#endif
+ uchar *destbufs[4];
+ const ET_SrcPixel *const *sbuf = src + strip;
+ int plane_idx, last_plane;
+ int i;
+
+ last_plane = n_planes - strip < 4 ? n_planes - strip : 4;
+ for (plane_idx = 0; plane_idx < last_plane; plane_idx++)
+ {
+ destbufs[plane_idx] = dest[plane_idx + strip];
+ }
+ for (i = 0; i < xd; i += 16)
+ {
+ int noskip;
+ noskip = eb_avec_prep_srcbuf(ctx, last_plane, srcbuf, sbuf, i);
+ ctx->skip_line[i >> 4] = noskip;
+ if (noskip)
+ {
+ for (plane_idx = last_plane; plane_idx < 4; plane_idx++)
+ destbufs[plane_idx] = dummy_dst - i;
+ eb_avec_core(ctx, (vector unsigned char **)destbufs, srcbuf, i);
+ }
+ else
+ {
+ /* all white */
+
+ for (plane_idx = 0; plane_idx < last_plane; plane_idx++)
+ {
+ uchar *dst_ptr = destbufs[plane_idx];
+ ((uint32 *)dst_ptr)[(i >> 2) + 0] = 0;
+ ((uint32 *)dst_ptr)[(i >> 2) + 1] = 0;
+ ((uint32 *)dst_ptr)[(i >> 2) + 2] = 0;
+ ((uint32 *)dst_ptr)[(i >> 2) + 3] = 0;
+ }
+ }
+ }
+
+ for (i = xd & -16; i >= 0; i -= 16)
+ {
+ if (ctx->skip_line[i >> 4])
+ {
+#ifdef USE_AVEC
+ eb_avec_rev_rs(ctx, i + 15);
+#endif
+#ifdef USE_SSE2
+ eb_sse2_rev_rs(ctx, i + 15);
+#endif
+ }
+ }
+ }
+ return 0;
+}
+#endif
+
+/* Maximum number of planes, but actually we want to dynamically
+ allocate all scratch buffers that depend on this. */
+#define M 16
+
+static void
+even_better_line_hi (EvenBetterCtx *ebc, uchar **dest,
+ const ET_Rll *const *src)
+{
+ int a[M], b[M];
+ int e_1_0[M], e_m1_1[M], e_0_1[M], e_1_1[M];
+ int iml[M], rbl[M];
+ int i, j;
+ int im;
+ int *pa, *pb, *piir, *pr;
+ int r[M], rg;
+ int xd, xs;
+ uint32 seed1 = ebc->seed1;
+ uint32 seed2 = ebc->seed2;
+ uint32 sum;
+ int plane_idx;
+ int r_scratch[M];
+ int n_planes = ebc->n_planes;
+ int levels = ebc->levels;
+#ifdef OLD_QUANT
+ int dith_mul = levels << 8;
+#else
+ int dith_mul = (levels - 1) << 8;
+#endif
+ int imo_mul = (1 << (EVEN_SHIFT + IMO_SHIFT)) / (levels - 1);
+ int aspect2 = ebc->aspect * ebc->aspect;
+ int *strengths = ebc->strengths;
+ int even_elo = ebc->even_elo;
+ int even_ehi = ebc->even_ehi;
+ int coupling;
+ int *c_line = ebc->c_line;
+ int even_c1 = ebc->even_c1;
+ int rand_shift;
+ int even_rlimit = 1 << (30 - EVEN_SHIFT + even_c1);
+ int count[M], src_idx[M];
+ int rs[M];
+
+ xs = ebc->source_width;
+ xd = ebc->dest_width;
+
+ for (plane_idx = 0; plane_idx < n_planes; plane_idx++)
+ {
+ a[plane_idx] = 1;
+ b[plane_idx] = aspect2;
+ r[plane_idx] = 0;
+ e_0_1[plane_idx] = 0;
+ e_1_0[plane_idx] = 0;
+ e_1_1[plane_idx] = 0;
+ count[plane_idx] = 0;
+ src_idx[plane_idx] = 0;
+ }
+
+ coupling = 0;
+
+ for (i = 0; i < xd;)
+ {
+ int work_planes[M];
+ int n_work = 0;
+ int work_idx;
+ int jmax;
+
+ jmax = (xd - i) > 16 ? 16 : xd - i;
+
+ for (plane_idx = 0; plane_idx < n_planes; plane_idx++)
+ {
+ EBPlaneCtx *ctx = ebc->plane_ctx[plane_idx];
+ int *wcl = ctx->white_count_line;
+ if (count[plane_idx] >= 16 && iml[plane_idx] == 0)
+ wcl[i >> 4]++;
+ else
+ wcl[i >> 4] = 0;
+ if (wcl[i >> 4] > 15)
+ {
+ uchar *dst_ptr = dest[plane_idx];
+ if (jmax == 16)
+ {
+ ((uint32 *)dst_ptr)[(i >> 2) + 0] = 0;
+ ((uint32 *)dst_ptr)[(i >> 2) + 1] = 0;
+ ((uint32 *)dst_ptr)[(i >> 2) + 2] = 0;
+ ((uint32 *)dst_ptr)[(i >> 2) + 3] = 0;
+ }
+ else
+ {
+ for (j = 0; j < jmax; j++)
+ dst_ptr[i + j] = 0;
+ }
+ count[plane_idx] -= jmax;
+ }
+ else
+ {
+ work_planes[n_work++] = plane_idx;
+ }
+ }
+
+ if (n_work == 0)
+ {
+ /* all planes were white */
+ i += jmax;
+ continue;
+ }
+
+ for (j = 0; j < jmax; j++)
+ {
+#ifdef FANCY_COUPLING
+ coupling += c_line[i];
+#else
+ coupling = 0;
+#endif
+ /* Lookup image data and compute R for all planes. */
+ for (work_idx = 0; work_idx < n_work; work_idx++)
+ {
+ int plane_idx = work_planes[work_idx];
+ EBPlaneCtx *ctx = ebc->plane_ctx[plane_idx];
+ ET_SrcPixel src_pixel;
+ int new_r;
+
+ pr = ctx->r_line;
+ pa = ctx->a_line;
+ pb = ctx->b_line;
+ if (count[plane_idx] == 0)
+ {
+ const ET_Rll *src_p = src[plane_idx] + src_idx[plane_idx]++;
+ int *lut = ctx->lut;
+ int *rblut = ctx->rb_lut;
+ char *rslut = ctx->rs_lut;
+
+ count[plane_idx] = src_p->length;
+ src_pixel = src_p->value;
+ iml[plane_idx] = lut[src_pixel];
+ rbl[plane_idx] = rblut[src_pixel];
+ rs[plane_idx] = rslut[src_pixel];
+ }
+ count[plane_idx]--;
+
+ if (r[plane_idx] + a[plane_idx] < pr[i])
+ {
+ r[plane_idx] += a[plane_idx];
+ a[plane_idx] += 2;
+ }
+ else
+ {
+ a[plane_idx] = pa[i];
+ b[plane_idx] = pb[i];
+ r[plane_idx] = pr[i];
+ }
+ if (iml[plane_idx] == 0)
+ {
+ r_scratch[plane_idx] = 0;
+ }
+ else
+ {
+ int r_tmp;
+ const int r_max = 0;
+ new_r = r[plane_idx];
+ if (new_r > even_rlimit)
+ new_r = even_rlimit;
+ /* Should we store back with the limit? */
+
+ rg = new_r << (EVEN_SHIFT - even_c1);
+ r_tmp = rg - rbl[plane_idx];
+ if (r_tmp > r_max) r_tmp >>= 3;
+ r_scratch[plane_idx] = r_tmp;
+ }
+ }
+
+ /* Dither each plane. */
+ for (work_idx = 0; work_idx < n_work; work_idx++)
+ {
+ int plane_idx = work_planes[work_idx];
+ EBPlaneCtx *ctx = ebc->plane_ctx[plane_idx];
+ uchar *dst_ptr = dest[plane_idx];
+ int new_e_1_0;
+ int coupling_contribution;
+
+ pr = ctx->r_line;
+ pa = ctx->a_line;
+ pb = ctx->b_line;
+ piir = ctx->iir_line;
+
+ im = iml[plane_idx];
+ e_m1_1[plane_idx] = e_0_1[plane_idx];
+ e_0_1[plane_idx] = e_1_1[plane_idx];
+ e_1_1[plane_idx] = i == xd - 1 ? 0 : piir[i + 1];
+ new_e_1_0 = ((e_1_0[plane_idx] * 7 + e_m1_1[plane_idx] * 3 +
+ e_0_1[plane_idx] * 5 + e_1_1[plane_idx] * 1) >> 4);
+ if (im == 0)
+ {
+ dst_ptr[i] = 0;
+ }
+ else
+ {
+ int err;
+ int imo;
+
+ err = new_e_1_0;
+
+ err += r_scratch[plane_idx];
+
+ /* Add the two seeds together */
+ sum = seed1 + seed2;
+
+ /* If the add generated a carry, increment
+ * the result of the addition.
+ */
+ if (sum < seed1 || sum < seed2) sum++;
+
+ /* Seed2 becomes old seed1, seed1 becomes result */
+ seed2 = seed1;
+ seed1 = sum;
+
+ rand_shift = rs[plane_idx];
+ err -= (sum >> rand_shift) - (0x80000000 >> rand_shift);
+
+ if (err < even_elo)
+ err = even_elo;
+
+ else if (err > even_ehi)
+ err = even_ehi;
+
+#if 1
+ err += coupling;
+#endif
+
+#ifdef OLD_QUANT
+ imo = ((err + im) * dith_mul) >> (EVEN_SHIFT + 8);
+#else
+ imo = ((err + im) * dith_mul + (1 << (EVEN_SHIFT + 7))) >> (EVEN_SHIFT + 8);
+#endif
+ if (imo < 0) imo = 0;
+ else if (imo > levels - 1) imo = levels - 1;
+ dst_ptr[i] = imo;
+ coupling_contribution = im - ((imo * imo_mul) >> IMO_SHIFT);
+ new_e_1_0 += coupling_contribution;
+ coupling += (coupling_contribution * strengths[plane_idx]) >> 8;
+ }
+ if (dst_ptr[i] != 0)
+ {
+ a[plane_idx] = 1;
+ b[plane_idx] = aspect2;
+ r[plane_idx] = 0;
+ }
+ pa[i] = a[plane_idx];
+ pb[i] = b[plane_idx];
+ pr[i] = r[plane_idx];
+ piir[i] = new_e_1_0;
+ e_1_0[plane_idx] = new_e_1_0;
+ }
+#ifdef FANCY_COUPLING
+ coupling = coupling >> 1;
+ c_line[i] = coupling;
+#endif
+ i++;
+ }
+ }
+
+ /* Note: this isn't white optimized, but the payoff is probably not
+ that important. */
+#ifdef FANCY_COUPLING
+ coupling = 0;
+ for (i = xd - 1; i >= 0; i--)
+ {
+ coupling = (coupling + c_line[i]) >> 1;
+ c_line[i] = (coupling - (coupling >> 4));
+ }
+#endif
+
+ /* Update distances. */
+ for (plane_idx = 0; plane_idx < n_planes; plane_idx++)
+ {
+ EBPlaneCtx *ctx = ebc->plane_ctx[plane_idx];
+ int *wcl = ctx->white_count_line;
+ int av, bv, rv;
+ int jmax;
+
+ pr = ctx->r_line;
+ pa = ctx->a_line;
+ pb = ctx->b_line;
+
+ av = 1;
+ bv = 1;
+ rv = 0;
+ jmax = ((xd - 1) & 15) + 1;
+ for (i = xd - 1; i >= 0;)
+ {
+ if (wcl[i >> 4] < 16)
+ {
+ for (j = 0; j < jmax; j++)
+ {
+ if (rv + bv + av < pr[i] + pb[i])
+ {
+ rv += av;
+ av += 2;
+ }
+ else
+ {
+ rv = pr[i];
+ av = pa[i];
+ bv = pb[i];
+ }
+ if (rv > even_rlimit) rv = even_rlimit;
+ pa[i] = av;
+ pb[i] = bv + (aspect2 << 1);
+ pr[i] = rv + bv;
+ i--;
+ }
+ }
+ else
+ i -= jmax;
+ jmax = 16;
+ }
+ }
+
+ ebc->seed1 = seed1;
+ ebc->seed2 = seed2;
+}
+
+static void
+even_better_line_both (EvenBetterCtx *ebc, uchar **dest,
+ const ET_Rll *const *src)
+{
+#if 0
+ int a[M], b[M];
+ int a_sh[M], b_sh[M];
+ int e_1_0[M], e_m1_1[M], e_0_1[M], e_1_1[M];
+ int imraw[M];
+ int iml[M];
+ int i;
+ int im;
+ int *lut;
+ const ET_SrcPixel *ps;
+ int *pa, *pb, *piir, *pr;
+ int *pa_sh, *pb_sh, *pr_sh;
+ int r[M], rb, rg;
+ int r_sh[M];
+ int *rblut;
+ int xd, xrem, xs;
+ uint32 seed1 = ebc->seed1;
+ uint32 seed2 = ebc->seed2;
+ uint32 sum;
+ int plane_idx;
+ int r_scratch[M];
+ int src_idx;
+ int n_planes = ebc->n_planes;
+ int levels = ebc->levels;
+#ifdef OLD_QUANT
+ int dith_mul = levels << 8;
+#else
+ int dith_mul = (levels - 1) << 8;
+#endif
+ int imo_mul = (1 << (EVEN_SHIFT + IMO_SHIFT)) / (levels - 1);
+ int aspect2 = ebc->aspect * ebc->aspect;
+ int *strengths = ebc->strengths;
+ int even_elo= ebc->even_elo;
+ int even_ehi= ebc->even_ehi;
+ int coupling;
+ int *c_line = ebc->c_line;
+ int even_c1 = ebc->even_c1;
+ int rand_shift = ebc->rand_shift;
+ int even_rlimit = 1 << (30 - EVEN_SHIFT + even_c1);
+
+ xs = ebc->source_width;
+ xd = ebc->dest_width;
+ xrem = xd - xs;
+
+ for (plane_idx = 0; plane_idx < n_planes; plane_idx++)
+ {
+ a[plane_idx] = 1;
+ b[plane_idx] = aspect2;
+ a_sh[plane_idx] = 1;
+ b_sh[plane_idx] = aspect2;
+ r[plane_idx] = 0;
+ r_sh[plane_idx] = 0;
+ e_0_1[plane_idx] = 0;
+ e_1_0[plane_idx] = 0;
+ e_1_1[plane_idx] = 0;
+ }
+
+ coupling = 0;
+
+ src_idx = 0;
+ for (i = 0; i < xd; i++)
+ {
+#ifdef FANCY_COUPLING
+ coupling += c_line[i];
+#else
+ coupling = 0;
+#endif
+
+ xrem += xs;
+ if (xrem >= xd)
+ {
+ for (plane_idx = 0; plane_idx < n_planes; plane_idx++)
+ {
+ ps = src[plane_idx];
+ imraw[plane_idx] = ps[src_idx];
+ }
+ src_idx++;
+ xrem -= xd;
+ }
+
+ /* Lookup image data and compute R for all planes. */
+ for (plane_idx = 0; plane_idx < n_planes; plane_idx++)
+ {
+ EBPlaneCtx *ctx = ebc->plane_ctx[plane_idx];
+ ET_SrcPixel src_pixel;
+ int new_r;
+
+ pr = ctx->r_line;
+ pa = ctx->a_line;
+ pb = ctx->b_line;
+ pr_sh = ctx->r_line_sh;
+ pa_sh = ctx->a_line_sh;
+ pb_sh = ctx->b_line_sh;
+ lut = ctx->lut;
+ rblut = ctx->rb_lut;
+ src_pixel = imraw[plane_idx];
+
+ im = lut[src_pixel];
+ iml[plane_idx] = im;
+ rb = rblut[src_pixel];
+ if (r[plane_idx] + a[plane_idx] < pr[i])
+ {
+ r[plane_idx] += a[plane_idx];
+ a[plane_idx] += 2;
+ }
+ else
+ {
+ a[plane_idx] = pa[i];
+ b[plane_idx] = pb[i];
+ r[plane_idx] = pr[i];
+ }
+ if (r_sh[plane_idx] + a_sh[plane_idx] < pr_sh[i])
+ {
+ r_sh[plane_idx] += a_sh[plane_idx];
+ a_sh[plane_idx] += 2;
+ }
+ else
+ {
+ a_sh[plane_idx] = pa_sh[i];
+ b_sh[plane_idx] = pb_sh[i];
+ r_sh[plane_idx] = pr_sh[i];
+ }
+ if (im == 0 || im == (1 << EVEN_SHIFT))
+ {
+ r_scratch[plane_idx] = 0;
+ }
+ else
+ {
+ new_r = r[plane_idx];
+ if (new_r > even_rlimit)
+ new_r = even_rlimit;
+ /* Should we store back with the limit? */
+ rg = new_r << (EVEN_SHIFT - even_c1);
+
+ new_r = r_sh[plane_idx];
+ if (new_r > even_rlimit)
+ new_r = even_rlimit;
+ rg -= new_r << (EVEN_SHIFT - even_c1);
+ r_scratch[plane_idx] = rg - rb;
+ }
+ }
+
+ /* Dither each plane. */
+ for (plane_idx = 0; plane_idx < n_planes; plane_idx++)
+ {
+ EBPlaneCtx *ctx = ebc->plane_ctx[plane_idx];
+ uchar *dst_ptr = dest[plane_idx];
+ int new_e_1_0;
+ int coupling_contribution;
+
+ pr = ctx->r_line;
+ pa = ctx->a_line;
+ pb = ctx->b_line;
+ pr_sh = ctx->r_line_sh;
+ pa_sh = ctx->a_line_sh;
+ pb_sh = ctx->b_line_sh;
+ piir = ctx->iir_line;
+
+ im = iml[plane_idx];
+ e_m1_1[plane_idx] = e_0_1[plane_idx];
+ e_0_1[plane_idx] = e_1_1[plane_idx];
+ e_1_1[plane_idx] = i == xd - 1 ? 0 : piir[i + 1];
+ new_e_1_0 = ((e_1_0[plane_idx] * 7 + e_m1_1[plane_idx] * 3 +
+ e_0_1[plane_idx] * 5 + e_1_1[plane_idx] * 1) >> 4);
+ if (im == 0)
+ {
+ dst_ptr[i] = 0;
+ }
+ else
+ {
+ int err;
+ int imo;
+
+ err = new_e_1_0;
+
+ err += r_scratch[plane_idx];
+
+ /* Add the two seeds together */
+ sum = seed1 + seed2;
+
+ /* If the add generated a carry, increment
+ * the result of the addition.
+ */
+ if (sum < seed1 || sum < seed2) sum++;
+
+ /* Seed2 becomes old seed1, seed1 becomes result */
+ seed2 = seed1;
+ seed1 = sum;
+
+ err -= (sum >> rand_shift) - (0x80000000 >> rand_shift);
+
+ if (err < even_elo)
+ err = even_elo;
+
+ else if (err > even_ehi)
+ err = even_ehi;
+
+#if 1
+ err += coupling;
+#endif
+
+#ifdef OLD_QUANT
+ imo = ((err + im) * dith_mul) >> (EVEN_SHIFT + 8);
+#else
+ imo = ((err + im) * dith_mul + (1 << (EVEN_SHIFT + 7))) >> (EVEN_SHIFT + 8);
+#endif
+ if (imo < 0) imo = 0;
+ else if (imo > levels - 1) imo = levels - 1;
+ dst_ptr[i] = imo;
+ coupling_contribution = im - ((imo * imo_mul) >> IMO_SHIFT);
+ new_e_1_0 += coupling_contribution;
+ coupling += (coupling_contribution * strengths[plane_idx]) >> 8;
+ }
+ if (dst_ptr[i] != 0)
+ {
+ a[plane_idx] = 1;
+ b[plane_idx] = aspect2;
+ r[plane_idx] = 0;
+ }
+ if (dst_ptr[i] != levels - 1)
+ {
+ a_sh[plane_idx] = 1;
+ b_sh[plane_idx] = aspect2;
+ r_sh[plane_idx] = 0;
+ }
+ pa[i] = a[plane_idx];
+ pb[i] = b[plane_idx];
+ pr[i] = r[plane_idx];
+ pa_sh[i] = a_sh[plane_idx];
+ pb_sh[i] = b_sh[plane_idx];
+ pr_sh[i] = r_sh[plane_idx];
+ piir[i] = new_e_1_0;
+ e_1_0[plane_idx] = new_e_1_0;
+ }
+#ifdef FANCY_COUPLING
+ coupling = coupling >> 1;
+ c_line[i] = coupling;
+#endif
+ }
+
+#ifdef FANCY_COUPLING
+ coupling = 0;
+ for (i = xd - 1; i >= 0; i--)
+ {
+ if (plane_idx == 0)
+ {
+ coupling = (coupling + c_line[i]) >> 1;
+ c_line[i] = (coupling - (coupling >> 4));
+ }
+ }
+#endif
+
+ /* Update distances. */
+ for (plane_idx = 0; plane_idx < n_planes; plane_idx++)
+ {
+ EBPlaneCtx *ctx = ebc->plane_ctx[plane_idx];
+ int av, bv, rv;
+ int av_sh, bv_sh, rv_sh;
+
+ pr = ctx->r_line;
+ pa = ctx->a_line;
+ pb = ctx->b_line;
+ pr_sh = ctx->r_line_sh;
+ pa_sh = ctx->a_line_sh;
+ pb_sh = ctx->b_line_sh;
+
+ av = 1;
+ bv = 1;
+ rv = 0;
+ av_sh = 1;
+ bv_sh = 1;
+ rv_sh = 0;
+ for (i = xd - 1; i >= 0; i--)
+ {
+ if (rv + bv + av < pr[i] + pb[i])
+ {
+ rv += av;
+ av += 2;
+ }
+ else
+ {
+ rv = pr[i];
+ av = pa[i];
+ bv = pb[i];
+ }
+ if (rv > even_rlimit) rv = even_rlimit;
+ pa[i] = av;
+ pb[i] = bv + (aspect2 << 1);
+ pr[i] = rv + bv;
+
+ if (rv_sh + bv_sh + av_sh < pr_sh[i] + pb_sh[i])
+ {
+ rv_sh += av_sh;
+ av_sh += 2;
+ }
+ else
+ {
+ rv_sh = pr_sh[i];
+ av_sh = pa_sh[i];
+ bv_sh = pb_sh[i];
+ }
+ if (rv_sh > even_rlimit) rv_sh = even_rlimit;
+ pa_sh[i] = av_sh;
+ pb_sh[i] = bv_sh + (aspect2 << 1);
+ pr_sh[i] = rv_sh + bv_sh;
+ }
+ }
+
+ ebc->seed1 = seed1;
+ ebc->seed2 = seed2;
+#endif
+}
+
+/**
+ * even_better_line_rll: Screen a line using Even ToneFS screeing.
+ * @ctx: An #EBPlaneCtx context.
+ * @dest: Array of destination buffers, 8 bpp pixels each.
+ * @src: Array of source buffers, runlength encoded.
+ *
+ * Screens a single line using Even ToneFS screening.
+ **/
+void
+even_better_line_rll (EvenBetterCtx *ebc, uchar **dest,
+ const ET_Rll *const *src)
+{
+
+ if (ebc->dump_file && ebc->dump_level >= EB_DUMP_INPUT)
+ {
+ int i;
+
+ /* Note: we should calculate the actual number of runlength
+ codes here. As it is, it will just waste storage a bit. */
+ for (i = 0; i < ebc->n_planes; i++)
+ fwrite (src[i], sizeof(ET_Rll), ebc->source_width,
+ ebc->dump_file);
+ }
+#ifdef USE_VECTOR
+ if (ebc->using_vectors)
+ even_better_line_vector(ebc, dest, src);
+ else
+#endif
+ if (ebc->do_shadows)
+ even_better_line_both (ebc, dest, src);
+ else
+ even_better_line_hi (ebc, dest, src);
+ if (ebc->dump_file && ebc->dump_level >= EB_DUMP_INPUT)
+ {
+ int i;
+
+ for (i = 0; i < ebc->n_planes; i++)
+ fwrite (dest[i], 1, ebc->dest_width,
+ ebc->dump_file);
+ }
+}
+
+/**
+ * even_better_compress_rll: Compress a single scan line to RLL format.
+ * @dst: Destination buffer.
+ * @src: Source buffer.
+ * @width: Number of source pixels.
+ *
+ * Return value: number of runlength codes.
+ **/
+static int
+even_better_compress_rll (ET_Rll *dst, const ET_SrcPixel *src,
+ int src_width, int dst_width)
+{
+ int rll_idx;
+ int i;
+ int count;
+ ET_SrcPixel last_val;
+ int whole = dst_width / src_width;
+ int frac = dst_width % src_width;
+ int rem;
+
+ rll_idx = 0;
+ last_val = src[0];
+ count = whole;
+ if (frac == 0)
+ {
+ for (i = 1; i < src_width; i++)
+ {
+ ET_SrcPixel val = src[i];
+
+ if (count > 0xffff - whole || val != last_val)
+ {
+ dst[rll_idx].length = count;
+ dst[rll_idx].value = last_val;
+ rll_idx++;
+ last_val = val;
+ count = 0;
+ }
+ count += whole;
+ }
+ }
+ else
+ {
+ rem = frac;
+ for (i = 1; i < src_width; i++)
+ {
+ ET_SrcPixel val = src[i];
+
+ if (count >= 0xffff - whole || val != last_val)
+ {
+ dst[rll_idx].length = count;
+ dst[rll_idx].value = last_val;
+ rll_idx++;
+ last_val = val;
+ count = 0;
+ }
+ count += whole;
+ rem += frac;
+ if (rem >= src_width)
+ {
+ count++;
+ rem -= src_width;
+ }
+ }
+ }
+ dst[rll_idx].length = count;
+ dst[rll_idx].value = last_val;
+ rll_idx++;
+ return rll_idx;
+}
+
+/**
+ * even_better_line: Screen a line using Even TonenFS screeing.
+ * @ctx: An #EBPlaneCtx context.
+ * @dest: Array of destination buffers, 8 bpp pixels each.
+ * @src: Array of source buffer, ET_SrcPixel pixels each.
+ *
+ * Screens a single line using Even ToneFS screening.
+ **/
+void
+even_better_line (EvenBetterCtx *ebc, uchar **dest,
+ const ET_SrcPixel *const *src)
+{
+ ET_Rll *rll_buf[M];
+ int i;
+ int source_width = ebc->source_width;
+ int dest_width = ebc->dest_width;
+
+#ifdef USE_AVEC
+ if (ebc->using_vectors == 2)
+ {
+ even_better_line_fastprep (ebc, dest, src);
+ }
+ else
+#endif
+ {
+ for (i = 0; i < ebc->n_planes; i++)
+ {
+ rll_buf[i] = (ET_Rll *)malloc (source_width * sizeof(ET_Rll));
+ even_better_compress_rll (rll_buf[i], src[i], source_width, dest_width);
+ }
+ even_better_line_rll (ebc, dest, (const ET_Rll * const *)rll_buf);
+ for (i = 0; i < ebc->n_planes; i++)
+ free (rll_buf[i]);
+ }
+}
+
+/**
+ * even_better_plane_free: Free an #EBPlaneCtx context.
+ * @ctx: The #EBPlaneCtx context to free.
+ *
+ * Frees @ctx.
+ **/
+static void
+even_better_plane_free (EBPlaneCtx *ctx)
+{
+ free (ctx->rb_line);
+ free (ctx->iir_line);
+ free (ctx->r_line);
+ free (ctx->a_line);
+ free (ctx->b_line);
+ free (ctx->lut);
+ free (ctx->rb_lut);
+ free (ctx->rs_lut);
+ free (ctx->white_count_line);
+ free (ctx);
+}
+
+static int
+even_log2 (int x)
+{
+ int y = 0;
+ int z;
+
+ for (z = x; z > 1; z = z >> 1)
+ y++;
+ return y;
+}
+
+/**
+ * even_better_new: Create new Even ToneFS screening context.
+ * @source_width: Width of source buffer.
+ * @dest_width: Width of destination buffer, in pixels.
+ * @lut: Lookup table for gray values.
+ *
+ * Creates a new context for Even ToneFS screening.
+ *
+ * If @dest_width is larger than @source_width, then input lines will
+ * be expanded using nearest-neighbor sampling.
+ *
+ * @lut should be an array of 256 values, one for each possible input
+ * gray value. @lut is a lookup table for gray values. Each value
+ * ranges from 0 (black) to 2^24 (white).
+ *
+ * Return value: The new #EBPlaneCtx context.
+ **/
+static EBPlaneCtx *
+even_better_plane_new (const EvenBetterParams *params, EvenBetterCtx *ebc,
+ int plane_idx)
+{
+ int source_width = params->source_width;
+ int dest_width = params->dest_width;
+ int *lut = params->luts[plane_idx];
+ EBPlaneCtx *result;
+ int i;
+ int *new_lut;
+ int *rb_lut;
+ char *rs_lut;
+ double rbscale = eb_compute_rbscale(params);
+ int even_c1 = ebc->even_c1;
+ int even_rlimit = 1 << (30 - EVEN_SHIFT + even_c1);
+ int do_shadows = params->do_shadows;
+ int log2_levels;
+ int rs_base;
+
+ result = (EBPlaneCtx *)malloc (sizeof(EBPlaneCtx));
+
+ result->source_width = source_width;
+ result->dest_width = dest_width;
+
+ new_lut = (int *)malloc ((ET_SRC_MAX + 1) * sizeof(int));
+ for (i = 0; i < ET_SRC_MAX + 1; i++)
+ {
+ int nli;
+
+ if (lut == NULL)
+ {
+#if ET_SRC_MAX == 255
+ nli = (i * 65793 + (i >> 7)) >> (24 - EVEN_SHIFT);
+#else
+ nli = (i * ((double) (1 << EVEN_SHIFT)) / ET_SRC_MAX) + 0.5;
+#endif
+ }
+ else
+ nli = lut[i] >> (24 - EVEN_SHIFT);
+ new_lut[i] = (1 << EVEN_SHIFT) - nli;
+ }
+
+ rb_lut = (int *)malloc ((ET_SRC_MAX + 1) * sizeof(int));
+ rs_lut = (char *)malloc ((ET_SRC_MAX + 1) * sizeof(int));
+
+ log2_levels = even_log2 (params->levels);
+ rs_base = 35 - EVEN_SHIFT + log2_levels - params->rand_scale;
+
+ for (i = 0; i <= ET_SRC_MAX; i++)
+ {
+ double rb;
+ int nl = new_lut[i] * (params->levels - 1);
+ int rs;
+
+ if (nl == 0)
+ rb = 0;
+ else
+ {
+ rb = (rbscale * (1 << (2 * EVEN_SHIFT - even_c1))) / nl;
+ if (rb > even_rlimit << (EVEN_SHIFT - even_c1))
+ rb = even_rlimit << (EVEN_SHIFT - even_c1);
+ }
+
+ rs = eb_compute_randshift(nl, rs_base, do_shadows, params->levels);
+ rs_lut[i] = rs;
+
+ if (params->do_shadows)
+ {
+ nl = ((1 << EVEN_SHIFT) - new_lut[i]) * (params->levels - 1);
+
+ if (nl == 0)
+ rb = 0;
+ else
+ {
+ int rb_sh;
+ rb_sh = (rbscale * (1 << (2 * EVEN_SHIFT - even_c1))) / nl;
+ if (rb_sh > even_rlimit << (EVEN_SHIFT - even_c1))
+ rb_sh = even_rlimit << (EVEN_SHIFT - even_c1);
+ rb -= rb_sh;
+ }
+ }
+ rb_lut[i] = rb;
+
+ }
+
+ result->lut = new_lut;
+ result->rb_lut = rb_lut;
+ result->rs_lut = rs_lut;
+
+ result->rb_line = (int *)calloc (dest_width, sizeof(int));
+ result->iir_line = (int *)calloc (dest_width, sizeof(int));
+ result->r_line = (int *)calloc (dest_width, sizeof(int));
+ result->a_line = (int *)calloc (dest_width, sizeof(int));
+ result->b_line = (int *)calloc (dest_width, sizeof(int));
+ result->white_count_line = (int *)calloc ((dest_width + 15) >> 4, sizeof(int));
+ if (do_shadows)
+ {
+ result->r_line_sh = (int *)calloc (dest_width, sizeof(int));
+ result->a_line_sh = (int *)calloc (dest_width, sizeof(int));
+ result->b_line_sh = (int *)calloc (dest_width, sizeof(int));
+ }
+ else
+ {
+ result->r_line_sh = NULL;
+ result->a_line_sh = NULL;
+ result->b_line_sh = NULL;
+ }
+ for (i = 0; i < dest_width; i++)
+ {
+ result->a_line[i] = 1;
+ result->b_line[i] = 1;
+ result->iir_line[i] = -((rand () & 0x7fff) << 6) >> (24 - EVEN_SHIFT);
+ if (do_shadows)
+ {
+ result->a_line_sh[i] = 1;
+ result->b_line_sh[i] = 1;
+ }
+ }
+
+ return result;
+}
+
+EvenBetterCtx *
+even_better_new (const EvenBetterParams *params)
+{
+ EvenBetterCtx *result = (EvenBetterCtx *)malloc (sizeof(EvenBetterCtx));
+ int n_planes = params->n_planes;
+ int i;
+ int log2_levels, log2_aspect;
+ int using_vectors = 0;
+
+ if (params->dump_file)
+ {
+ int header[5];
+
+ header[0] = 0x70644245;
+ header[1] = 'M' * 0x1010000 + 'I' * 0x101;
+ header[2] = EVENBETTER_VERSION;
+ header[3] = ET_SRC_MAX;
+ header[4] = sizeof(ET_SrcPixel);
+ fwrite (header, sizeof(int), sizeof(header) / sizeof(header[0]),
+ params->dump_file);
+ if (params->dump_level >= EB_DUMP_PARAMS)
+ {
+
+ fwrite (params, 1, sizeof(EvenBetterParams), params->dump_file);
+ }
+ if (params->dump_level >= EB_DUMP_LUTS)
+ {
+ int i;
+ for (i = 0; i < params->n_planes; i++)
+ fwrite (params->luts[i], sizeof(int), ET_SRC_MAX + 1,
+ params->dump_file);
+ }
+ }
+
+ result->source_width = params->source_width;
+ result->dest_width = params->dest_width;
+ result->n_planes = n_planes;
+ result->levels = params->levels;
+
+ result->aspect = params->aspect;
+
+ result->even_ehi = 0.6 * (1 << EVEN_SHIFT) / (params->levels - 1);
+ result->even_elo = -result->even_ehi;
+
+ result->strengths = (int *)malloc (sizeof(int) * n_planes);
+ memcpy (result->strengths, params->strengths,
+ sizeof(int) * n_planes);
+
+ log2_levels = even_log2 (params->levels);
+ log2_aspect = even_log2 (params->aspect);
+ result->even_c1 = 6 + log2_aspect + log2_levels - params->even_c1_scale;
+ result->do_shadows = params->do_shadows;
+
+ result->c_line = (int *)calloc (params->dest_width, sizeof(int));
+
+ result->seed1 = 0x5324879f;
+ result->seed2 = 0xb78d0945;
+
+ result->dump_file = params->dump_file;
+ result->dump_level = params->dump_level;
+
+#ifdef USE_SSE2
+ using_vectors = eb_test_sse2();
+#endif
+#ifdef USE_AVEC
+ using_vectors = 1; /* todo: Altivec sensing */
+
+ /* select fastprep */
+ if (sizeof(ET_SrcPixel) == 1 && using_vectors && params->gamma != 0)
+ using_vectors = 2;
+
+#endif
+
+#ifdef USE_VECTOR
+ result->using_vectors = using_vectors;
+#endif
+ if (using_vectors)
+ {
+#ifdef USE_SSE2
+ result->sse2_ctx = (eb_ctx_sse2 **)malloc(sizeof(eb_ctx_sse2 *) *
+ ((n_planes + 3) >> 2));
+ for (i = 0; i < n_planes; i += 4)
+ {
+ int end_plane = i + 4 < n_planes ? i + 4 : n_planes;
+ result->sse2_ctx[i >> 2] = eb_ctx_sse2_new(params, i, end_plane);
+ }
+#endif
+#ifdef USE_AVEC
+ result->avec_ctx = (eb_ctx_avec **)malloc(sizeof(eb_ctx_avec *) *
+ ((n_planes + 3) >> 2));
+ for (i = 0; i < n_planes; i += 4)
+ {
+ int end_plane = i + 4 < n_planes ? i + 4 : n_planes;
+ result->avec_ctx[i >> 2] = eb_ctx_avec_new(params, i, end_plane);
+ }
+#endif
+ result->plane_ctx = NULL;
+ }
+ else
+ {
+ result->plane_ctx = (EBPlaneCtx **)malloc(sizeof(EBPlaneCtx *) * n_planes);
+ for (i = 0; i < n_planes; i++)
+ result->plane_ctx[i] = even_better_plane_new (params, result, i);
+ }
+ return result;
+}
+
+/**
+ * even_better_free: Free an #EvenBetterCtx context.
+ * @ctx: The #EvenBetterCtx context to free.
+ *
+ * Frees @ctx.
+ **/
+void
+even_better_free (EvenBetterCtx *ctx)
+{
+ int i;
+ int n_planes = ctx->n_planes;
+
+ if (ctx->dump_file)
+ fclose (ctx->dump_file);
+
+#ifdef USE_VECTOR
+ if (ctx->using_vectors)
+ {
+#ifdef USE_SSE2
+ for (i = 0; i < n_planes; i += 4)
+ eb_ctx_sse2_free(ctx->sse2_ctx[i >> 2]);
+ free(ctx->sse2_ctx);
+#endif
+#ifdef USE_AVEC
+ for (i = 0; i < n_planes; i += 4)
+ eb_ctx_avec_free(ctx->avec_ctx[i >> 2]);
+ free(ctx->avec_ctx);
+#endif
+ }
+ else
+#endif
+ {
+ for (i = 0; i < n_planes; i++)
+ even_better_plane_free (ctx->plane_ctx[i]);
+ free(ctx->plane_ctx);
+ }
+ free (ctx->strengths);
+ free (ctx->c_line);
+
+ free (ctx);
+}
diff --git a/devices/rinkj/evenbetter-rll.h b/devices/rinkj/evenbetter-rll.h
new file mode 100644
index 000000000..2f9524863
--- /dev/null
+++ b/devices/rinkj/evenbetter-rll.h
@@ -0,0 +1,103 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Header file for testbed implementation of Even Better Screening. */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Definitions for source pixel format.
+ * Reasonable settings are as follows:
+ * 8-bit: unsigned char ET_SrcPixel; ET_SRC_MAX = 255
+ * 12-bit 0..4095: unsigned short (or int) ET_SrcPixel; ET_SRC_MAX = 4095
+ * 12-bit 0..4096: unsigned short (or int) ET_SrcPixel; ET_SRC_MAX = 4096
+ * 16-bit 0..65535: unsigned short (or int) ET_SrcPixel; ET_SRC_MAX = 65535
+ * 16-bit 0..65536: unsigned int ET_SrcPixel; ET_SRC_MAX = 65536
+ */
+typedef unsigned char ET_SrcPixel;
+#define ET_SRC_MAX 255
+
+/* Simple runlength code */
+typedef struct {
+ unsigned short length;
+ ET_SrcPixel value;
+} ET_Rll;
+
+typedef unsigned char uchar;
+
+/* To use the file dump capability:
+
+ Open a file as with: fopen ("dumpfile", "wb");
+ Put the resulting FILE * pointer in params->dump_file.
+ Set params->dump_level to the desired level. EB_DUMP_ALL dumps all
+ inputs and outputs. Other values will lead to much smaller files,
+ but may not be as insightful.
+
+ If no dump file is desired, set params->dump_file to NULL.
+*/
+
+typedef enum {
+ EB_DUMP_MINIMAL,
+ EB_DUMP_PARAMS,
+ EB_DUMP_LUTS,
+ EB_DUMP_INPUT,
+ EB_DUMP_ALL
+} EbDumpLevel;
+
+typedef struct {
+ int source_width;
+ int dest_width;
+ int n_planes;
+ int levels; /* Number of levels on output, <= 256 */
+ int **luts;
+ double rbscale; /* Should be 0.95 for 1:1, 1.8 for 2:1 */
+ int aspect;
+ int *strengths;
+ int rand_scale; /* 0 is default */
+ int even_c1_scale; /* 0 is default */
+ int do_shadows;
+ int dotsize;
+ FILE *dump_file;
+ EbDumpLevel dump_level;
+ double gamma; /* 0 to enable luts, other values are 1.0, 1.8 and 2.0 */
+} EvenBetterParams;
+
+typedef struct _EvenBetterCtx EvenBetterCtx;
+
+EvenBetterCtx *
+even_better_new (const EvenBetterParams *params);
+
+void
+even_better_line_rll (EvenBetterCtx *ctx, uchar **dest,
+ const ET_Rll *const *src);
+
+void
+even_better_line (EvenBetterCtx *ctx, uchar **dest,
+ const ET_SrcPixel *const *src);
+
+void
+even_better_free (EvenBetterCtx *ctx);
+
+void *
+eb_malloc_aligned(int size, int align);
+
+void
+eb_free_aligned(void *p);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/devices/rinkj/rinkj-byte-stream.c b/devices/rinkj/rinkj-byte-stream.c
new file mode 100644
index 000000000..56286c3a0
--- /dev/null
+++ b/devices/rinkj/rinkj-byte-stream.c
@@ -0,0 +1,108 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Bytestream abstraction for Rinkj driver. */
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include "rinkj-byte-stream.h"
+
+int
+rinkj_byte_stream_write (RinkjByteStream *bs, const char *buf, int size)
+{
+ return bs->write (bs, buf, size);
+}
+
+int
+rinkj_byte_stream_puts (RinkjByteStream *bs, const char *str)
+{
+ return bs->write (bs, str, strlen (str));
+}
+
+#define MAX_STRING 8192
+
+/* Careful using this function! */
+int
+rinkj_byte_stream_printf (RinkjByteStream *bs, const char *fmt, ...)
+{
+ char str[MAX_STRING];
+ int len;
+ va_list ap;
+
+ va_start (ap, fmt);
+ len = vsnprintf (str, sizeof(str), fmt, ap);
+ va_end (ap);
+ return rinkj_byte_stream_write (bs, str, len);
+}
+
+int
+rinkj_byte_stream_close (RinkjByteStream *bs)
+{
+ return bs->write (bs, NULL, 0);
+}
+
+/* This module just writes a byte stream to a file. */
+
+typedef struct _RinkjByteStreamFile RinkjByteStreamFile;
+
+struct _RinkjByteStreamFile {
+ RinkjByteStream super;
+ FILE *f;
+};
+
+static int
+rinkj_byte_stream_file_write (RinkjByteStream *self, const char *buf, int size)
+{
+ RinkjByteStreamFile *z = (RinkjByteStreamFile *)self;
+ int status;
+
+ if (size == 0)
+ {
+#if 1
+ status = 0; /* Ghostscript wants to close the file itself. */
+#else
+ status = fclose (z->f);
+#endif
+ free (self);
+ return status;
+ }
+ else
+ {
+#ifdef DEBUG_OUT
+ return 0;
+#endif
+ status = fwrite (buf, 1, size, z->f);
+ if (status == size)
+ return 0;
+ else
+ return -1;
+ }
+}
+
+RinkjByteStream *
+rinkj_byte_stream_file_new (FILE *f)
+{
+ RinkjByteStreamFile *result;
+
+ result = (RinkjByteStreamFile *)malloc (sizeof (RinkjByteStreamFile));
+
+ result->super.write = rinkj_byte_stream_file_write;
+ result->f = f;
+
+ return &result->super;
+}
diff --git a/devices/rinkj/rinkj-byte-stream.h b/devices/rinkj/rinkj-byte-stream.h
new file mode 100644
index 000000000..23b7ede6f
--- /dev/null
+++ b/devices/rinkj/rinkj-byte-stream.h
@@ -0,0 +1,38 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Bytestream abstraction for Rinkj driver. */
+
+typedef struct _RinkjByteStream RinkjByteStream;
+
+struct _RinkjByteStream {
+ int (*write) (RinkjByteStream *self, const char *buf, int size);
+};
+
+int
+rinkj_byte_stream_write (RinkjByteStream *bs, const char *buf, int size);
+
+int
+rinkj_byte_stream_puts (RinkjByteStream *bs, const char *str);
+
+int
+rinkj_byte_stream_printf (RinkjByteStream *bs, const char *fmt, ...);
+
+int
+rinkj_byte_stream_close (RinkjByteStream *bs);
+
+RinkjByteStream *
+rinkj_byte_stream_file_new (FILE *f);
diff --git a/devices/rinkj/rinkj-config.c b/devices/rinkj/rinkj-config.c
new file mode 100644
index 000000000..a77bbb57e
--- /dev/null
+++ b/devices/rinkj/rinkj-config.c
@@ -0,0 +1,153 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Support for reading Rinkj config files. */
+
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+
+#include "rinkj-config.h"
+
+/* A rinkj config string is a series of \n terminated lines, each with
+ the format "Key: Value". */
+
+/**
+ * rinkj_strdup_size: Duplicate a string with given size.
+ * @src: Source string to duplicate.
+ * @size: Size of @src.
+ *
+ * Return value: newly allocated string.
+ **/
+char *
+rinkj_strdup_size (const char *src, int size)
+{
+ char *result;
+
+ result = malloc (size + 1);
+ memcpy (result, src, size);
+ result[size] = 0;
+ return result;
+}
+
+/**
+ * rinkj_config_get: Get a config value.
+ * @config: A config string.
+ * @key: A key.
+ *
+ * Looks up @key in @config. Returns a newly allocated string, if found.
+ *
+ * Return value: value of the key, or NULL if not found.
+ **/
+char *
+rinkj_config_get (const char *config, const char *key)
+{
+ int ix;
+ int ix_eol, ix_next;
+
+ if (config == NULL)
+ return NULL;
+
+ for (ix = 0; config[ix]; ix = ix_next)
+ {
+ char *p_nl;
+ int key_ix;
+
+ p_nl = strchr (config + ix, '\n');
+ if (p_nl == NULL)
+ {
+ /* last line not \n terminated */
+ ix_eol = strlen (config + ix);
+ ix_next = ix_eol;
+ }
+ else
+ {
+ ix_eol = ix + p_nl - config;
+ ix_next = ix_eol + 1;
+ }
+
+ for (key_ix = 0; ix + key_ix < ix_eol; key_ix++)
+ {
+ if (key[key_ix] == 0 && config[ix + key_ix] == ':')
+ {
+ ix += key_ix + 1;
+ while (ix < ix_eol && isspace (config[ix]))
+ ix++;
+ return rinkj_strdup_size (config + ix, ix_eol - ix);
+ }
+ else if (key[key_ix] != config[ix + key_ix])
+ break;
+ }
+ }
+ return NULL;
+}
+
+/**
+ * rinkj_config_keyval: Get key, value pair from config string.
+ * @config: A config string.
+ * @p_val: Where to store newly allocated val.
+ * @p_next: Where to store pointer to next config line,
+ *
+ * Gets a key, value pair from the config string.
+ *
+ * Return value: Newly allocated key, or NULL if empty config.
+ **/
+char *
+rinkj_config_keyval (const char *config, char **p_val, const char **p_next)
+{
+ char *key;
+ int ix;
+ int ix_eol, ix_next;
+
+ if (config == NULL)
+ return NULL;
+
+ for (ix = 0; config[ix]; ix = ix_next)
+ {
+ char *p_nl;
+ int key_ix;
+
+ p_nl = strchr (config + ix, '\n');
+ if (p_nl == NULL)
+ {
+ /* last line not \n terminated */
+ ix_eol = strlen (config + ix);
+ ix_next = ix_eol;
+ }
+ else
+ {
+ ix_eol = ix + p_nl - config;
+ ix_next = ix_eol + 1;
+ }
+
+ for (key_ix = 0; ix + key_ix < ix_eol; key_ix++)
+ {
+ if (config[ix + key_ix] == ':')
+ {
+ key = rinkj_strdup_size (config + ix, key_ix);
+ ix += key_ix + 1;
+ while (ix < ix_eol && isspace (config[ix]))
+ ix++;
+ if (p_val != NULL)
+ *p_val = rinkj_strdup_size (config + ix, ix_eol - ix);
+ if (p_next != NULL)
+ *p_next = config + ix_next;
+ return key;
+ }
+ }
+ }
+ return NULL;
+}
diff --git a/devices/rinkj/rinkj-config.h b/devices/rinkj/rinkj-config.h
new file mode 100644
index 000000000..23bff8f9e
--- /dev/null
+++ b/devices/rinkj/rinkj-config.h
@@ -0,0 +1,26 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Support for reading Rinkj config files. */
+
+char *
+rinkj_strdup_size (const char *src, int size);
+
+char *
+rinkj_config_get (const char *config, const char *key);
+
+char *
+rinkj_config_keyval (const char *config, char **p_val, const char **p_next);
diff --git a/devices/rinkj/rinkj-device.c b/devices/rinkj/rinkj-device.c
new file mode 100644
index 000000000..0fde3aa12
--- /dev/null
+++ b/devices/rinkj/rinkj-device.c
@@ -0,0 +1,87 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* The device abstraction within the Rinkj driver. */
+
+#include "rinkj-device.h"
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+/* Deprecated */
+int
+rinkj_device_set (RinkjDevice *self, const char *config)
+{
+ if (self->init_happened != 0)
+ return -1;
+ return self->set (self, config);
+}
+
+/* Preferred, as it matches IJS */
+int
+rinkj_device_set_param (RinkjDevice *self, const char *key,
+ const char *value, int value_size)
+{
+ int keylen = strlen (key);
+ int bufsize = keylen + value_size + 3;
+ char *buf = malloc (bufsize);
+ int status;
+
+ /* This implementation is in terms of device_set, but we're going to
+ change the prototype of the device so this is native. */
+ memcpy (buf, key, keylen);
+ memcpy (buf + keylen, ": ", 2);
+ memcpy (buf + keylen + 2, value, value_size);
+ buf[keylen + 2 + value_size] = 0;
+ status = rinkj_device_set (self, buf);
+ free (buf);
+ return status;
+}
+
+int
+rinkj_device_set_param_string (RinkjDevice *self, const char *key,
+ const char *value)
+{
+ return rinkj_device_set_param (self, key, value, strlen (value));
+}
+
+int
+rinkj_device_set_param_int (RinkjDevice *self, const char *key, int value)
+{
+ char buf[32];
+ int value_size = sprintf (buf, "%d", value);
+ return rinkj_device_set_param (self, key, buf, value_size);
+}
+
+int
+rinkj_device_init (RinkjDevice *self, const RinkjDeviceParams *params)
+{
+ int status;
+
+ if (self->init_happened != 0)
+ return -1;
+ status = self->init (self, params);
+ self->init_happened = 42;
+ return status;
+}
+
+int
+rinkj_device_write (RinkjDevice *self, const char **data)
+{
+ if (self->init_happened != 42)
+ return -1;
+ return self->write (self, data);
+}
diff --git a/devices/rinkj/rinkj-device.h b/devices/rinkj/rinkj-device.h
new file mode 100644
index 000000000..8f4e2f567
--- /dev/null
+++ b/devices/rinkj/rinkj-device.h
@@ -0,0 +1,55 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* The device abstraction within the Rinkj driver. */
+
+typedef struct _RinkjDevice RinkjDevice;
+typedef struct _RinkjDeviceParams RinkjDeviceParams;
+
+struct _RinkjDeviceParams {
+ int width;
+ int height;
+ int n_planes;
+ char *plane_names;
+};
+
+struct _RinkjDevice {
+ int (*set) (RinkjDevice *self, const char *config);
+ int (*init) (RinkjDevice *self, const RinkjDeviceParams *params);
+ int (*write) (RinkjDevice *self, const char **data);
+ int init_happened;
+};
+
+/* Deprecated */
+int
+rinkj_device_set (RinkjDevice *self, const char *config);
+
+int
+rinkj_device_set_param (RinkjDevice *self, const char *key,
+ const char *value, int value_size);
+
+/* Convenience functions */
+int
+rinkj_device_set_param_string (RinkjDevice *self, const char *key,
+ const char *value);
+int
+rinkj_device_set_param_int (RinkjDevice *self, const char *key, int value);
+
+int
+rinkj_device_init (RinkjDevice *self, const RinkjDeviceParams *params);
+
+int
+rinkj_device_write (RinkjDevice *self, const char **data);
diff --git a/devices/rinkj/rinkj-dither.c b/devices/rinkj/rinkj-dither.c
new file mode 100644
index 000000000..37721018b
--- /dev/null
+++ b/devices/rinkj/rinkj-dither.c
@@ -0,0 +1,31 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* The dither object abstraction within the Rinkj driver. */
+
+#include "rinkj-dither.h"
+
+void
+rinkj_dither_line (RinkjDither *self, unsigned char *dst, const unsigned char *src)
+{
+ self->dither_line (self, dst, src);
+}
+
+void
+rinkj_dither_close (RinkjDither *self)
+{
+ self->close (self);
+}
diff --git a/devices/rinkj/rinkj-dither.h b/devices/rinkj/rinkj-dither.h
new file mode 100644
index 000000000..db3cb1654
--- /dev/null
+++ b/devices/rinkj/rinkj-dither.h
@@ -0,0 +1,30 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* The dither object abstraction within the Rinkj driver. */
+
+typedef struct _RinkjDither RinkjDither;
+
+struct _RinkjDither {
+ void (*dither_line) (RinkjDither *self, unsigned char *dst, const unsigned char *src);
+ void (*close) (RinkjDither *self);
+};
+
+void
+rinkj_dither_line (RinkjDither *self, unsigned char *dst, const unsigned char *src);
+
+void
+rinkj_dither_close (RinkjDither *self);
diff --git a/devices/rinkj/rinkj-epson870.c b/devices/rinkj/rinkj-epson870.c
new file mode 100644
index 000000000..3130b7100
--- /dev/null
+++ b/devices/rinkj/rinkj-epson870.c
@@ -0,0 +1,1093 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* A Rinkj driver for a number of variable-dot Epson devices. */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "rinkj-byte-stream.h"
+#include "rinkj-device.h"
+#include "rinkj-config.h"
+#include "rinkj-epson870.h"
+
+typedef struct _RinkjEscp RinkjEscp;
+
+struct _RinkjEscp {
+ RinkjDevice super;
+ RinkjByteStream *out;
+ int width;
+ int height;
+ int y;
+
+ char *manufacturer;
+ char *model;
+
+ int num_chan;
+ int bps; /* bits per sample */
+
+ int xres; /* resolution of input image */
+ int yres;
+
+ int head_bps;
+ int head_xres; /* x resolution of printhead in dpi */
+ int head_yres; /* y resolution of printhead in dpi */
+
+ /* Number of passes for a single scanline */
+ int passes_per_scan;
+
+ /* parameters for controlling microweaving */
+ int spacing;
+ int n_pins;
+
+ int plane_offsets[6];
+ int max_offset;
+
+ char *buf;
+ unsigned char *buf_linevalid;
+ int bufheight;
+ int rowstride;
+ int planestride;
+ int pass;
+
+ int vertpos; /* for ESC ( v */
+
+ /* parameters passed into ESCP2 */
+ int autocut;
+ int blankskip;
+ int microdot;
+ int unidir;
+ int printer_weave;
+};
+
+static int
+rinkj_escp_set (RinkjDevice *self, const char *config)
+{
+ RinkjEscp *z = (RinkjEscp *)self;
+ const char *p, *next;
+ char *key, *val;
+
+ for (p = config; (key = rinkj_config_keyval (p, &val, &next)); p = next)
+ {
+ if (!strcmp (key, "Resolution"))
+ {
+ char *p;
+
+ z->xres = atoi (val);
+ p = strchr (val, 'x');
+ if (p != NULL)
+ z->yres = atoi (p + 1);
+ else
+ z->yres = z->xres;
+#ifdef VERBOSE
+ fprintf (stderr, "Resolution = %d x %d\n", z->xres, z->yres);
+#endif
+ }
+ else if (!strcmp (key, "Manufacturer"))
+ {
+ if (z->manufacturer)
+ free (z->manufacturer);
+ z->manufacturer = val;
+ val = NULL;
+ }
+ else if (!strcmp (key, "Model"))
+ {
+ if (z->model)
+ free (z->model);
+ z->model = val;
+ val = NULL;
+ }
+ else if (!strcmp (key, "BitsPerSample"))
+ {
+ z->bps = atoi (val);
+#ifdef VERBOSE
+ fprintf (stderr, "BitsPerSample = %d\n", z->bps);
+#endif
+ }
+ else if (!strcmp (key, "NumChan"))
+ {
+ /* This is in params, but should become a setting. */
+ z->num_chan = atoi (val);
+#ifdef VERBOSE
+ fprintf (stderr, "NumChan = %d\n", z->num_chan);
+#endif
+ }
+ else if (!strcmp (key, "PrinterWeave"))
+ {
+ z->printer_weave = atoi (val);
+ }
+ else if (!strcmp (key, "Microdot"))
+ {
+ z->microdot = atoi (val);
+ }
+ else if (!strcmp (key, "Unidirectional"))
+ {
+ z->unidir = atoi (val);
+ }
+ else if (!strcmp (key, "AutoCut"))
+ {
+ z->autocut = atoi (val);
+ }
+ else if (!strcmp (key, "BlankSkip"))
+ {
+ z->blankskip = atoi (val);
+ }
+ free (key);
+ if (val)
+ free (val);
+ }
+ return 0;
+}
+
+static int
+rinkj_escp_ytop (RinkjEscp *z, int pass, int *p_x_pass)
+{
+ int ytop;
+ int x_pass, y_pass;
+ int passes_per_scan = z->passes_per_scan;
+ int spacing = z->spacing;
+ int n_cycle = spacing * passes_per_scan;
+ int mod_pass = pass % n_cycle;
+ int y_modulo;
+ const int four[4] = { 0, 3, 1, 2 };
+ const int six[6] = { 0, 3, 2, 5, 1, 4 };
+
+ ytop = mod_pass * z->n_pins * spacing / n_cycle;
+#if 1
+ x_pass = mod_pass / spacing;
+ y_pass = mod_pass % spacing;
+ if (passes_per_scan == 4)
+ x_pass = four[x_pass];
+#else
+ x_pass = mod_pass % passes_per_scan;
+ y_pass = (mod_pass / passes_per_scan + x_pass * (spacing - 1)) % spacing;
+#endif
+
+ switch (spacing)
+ {
+ case 4:
+ y_modulo = four[y_pass];
+ break;
+ case 6:
+ y_modulo = six[y_pass];
+ break;
+ case 8:
+ y_modulo = y_pass * 3;
+ break;
+ default:
+ y_modulo = y_pass;
+ break;
+ }
+
+ ytop += (spacing + y_modulo - ytop % spacing) % spacing;
+ ytop += (pass / n_cycle) * spacing * z->n_pins;
+
+ if (spacing == 4 && passes_per_scan == 4 && z->n_pins == 96)
+ {
+ const int sixteen[] = { 0, 3, 1, 0, 3, 1, 2, 3, 1, 2, 0, 1, 2, 0, 3, 2 };
+ x_pass = sixteen[mod_pass & 15];
+ ytop = pass * 23;
+ }
+
+#ifdef VERBOSE
+ fprintf (stderr, "pass %d: ytop = %d, x_pass = %d\n", pass, ytop, x_pass);
+#endif
+
+ if (p_x_pass)
+ *p_x_pass = x_pass;
+ return ytop;
+}
+
+static int
+rinkj_epson_headres (RinkjEscp *z, int baseres)
+{
+ return rinkj_byte_stream_printf (z->out, "\033(D\004%c%c%c%c%c", 0,
+ baseres & 255, baseres >> 8,
+ baseres / z->head_yres,
+ baseres / z->head_xres);
+}
+
+static int
+rinkj_epson_units (RinkjEscp *z, int xres, int yres, int baseres)
+{
+ return rinkj_byte_stream_printf (z->out, "\033(U\005%c%c%c%c%c%c", 0,
+ baseres / yres,
+ baseres / yres,
+ baseres / xres,
+ baseres & 255, baseres >> 8);
+}
+
+static int
+rinkj_epson_set_common (RinkjEscp *z)
+{
+ int status = 0;
+
+ if (z->printer_weave >= 0)
+ /* set microweave */
+ status = rinkj_byte_stream_printf (z->out, "\033(i\001%c%c", 0,
+ z->printer_weave);
+
+ if (status == 0 && z->unidir >= 0)
+ /* set unidirectional */
+ status = rinkj_byte_stream_printf (z->out, "\033U%c", z->unidir);
+
+ if (status == 0 && z->microdot >= 0)
+ /* set dot size */
+ status = rinkj_byte_stream_printf (z->out, "\033(e\002%c%c%c", 0, 0,
+ z->microdot);
+
+ return status;
+}
+
+/**
+ * Spit out a command string to resemble the gimp-print output as much
+ * as possible.
+ **/
+static int
+rinkj_epson870_init (RinkjDevice *self, const RinkjDeviceParams *params)
+{
+ RinkjEscp *z = (RinkjEscp *)self;
+
+ rinkj_byte_stream_printf (z->out,
+ "%c%c%c\033\001@EJL 1284.4\n", 0, 0, 0);
+ rinkj_byte_stream_printf (z->out, "@EJL \n\033@\033@");
+
+ /* remote string goes here, but is probably optional */
+
+ /* set graphics mode */
+ rinkj_byte_stream_printf (z->out, "\033(G\001%c\001", 0);
+
+ /* set units to 1/720" */
+ rinkj_byte_stream_printf (z->out, "\033(U\005%c\002\002\002\240\005", 0);
+
+ rinkj_epson_set_common(z);
+
+ /* set page length to about 22.377 inches */
+ rinkj_byte_stream_printf (z->out, "\033(C\002%cx\037", 0);
+
+ /* ESC ( c */
+
+ /* ESC ( S */
+
+ /* set resolution to 360 x 120 dpi - should probably generate this
+ string from head_xres and head_yres */
+ rinkj_byte_stream_printf (z->out, "\033(D\004%c@8x(", 0);
+
+ /* ESC ( v */
+
+ /* ESC \ */
+ return 0;
+}
+
+/**
+ * Spit out a command string to resemble the gimp-print output as much
+ * as possible.
+ **/
+static int
+rinkj_epson2200_init (RinkjDevice *self, const RinkjDeviceParams *params)
+{
+ RinkjEscp *z = (RinkjEscp *)self;
+
+ rinkj_byte_stream_printf (z->out,
+ "%c%c%c\033\001@EJL 1284.4\n", 0, 0, 0);
+ rinkj_byte_stream_printf (z->out, "@EJL \n\033@\033@");
+
+ /* remote string */
+ rinkj_byte_stream_printf( z->out,
+ "\033(R%c%c%cREMOTE1", 8, 0, 0);
+ rinkj_byte_stream_printf( z->out,
+ "PP\003%c%c\002%cPH\002%c%c\001SN\003%c%c\004k",
+ 0, 0, 0, 0, 0, 0, 0);
+ rinkj_byte_stream_printf( z->out,
+ "\033%c%c%c", 0, 0, 0);
+
+ /* set graphics mode */
+ rinkj_byte_stream_printf (z->out, "\033(G\001%c\001", 0);
+
+ rinkj_epson_units(z, z->xres, z->yres, 2880);
+
+ rinkj_epson_set_common(z);
+
+ /* set page length to 40 inches */
+ rinkj_byte_stream_printf (z->out, "\033(C\002%c\200p", 0);
+
+ /* ESC ( c */
+
+ /* ESC ( S */
+
+ rinkj_epson_headres (z, 2880);
+
+ /* ESC ( v */
+
+ /* ESC \ */
+ return 0;
+}
+
+/**
+ * Spit out a command string to resemble the gimp-print output as much
+ * as possible.
+ **/
+static int
+rinkj_epson7600_init (RinkjDevice *self, const RinkjDeviceParams *params)
+{
+ RinkjEscp *z = (RinkjEscp *)self;
+ int pl;
+ int pw = 720 * 24; /* hardcode to 24 inch */
+
+#if 0
+ rinkj_byte_stream_printf (z->out,
+ "%c%c%c\033\001@EJL 1284.4\n", 0, 0, 0);
+ rinkj_byte_stream_printf (z->out, "@EJL \n");
+#endif
+ rinkj_byte_stream_printf (z->out, "\033@\033@");
+
+#if 1
+ /* remote string */
+ rinkj_byte_stream_printf( z->out,
+ "\033(R%c%c%cREMOTE1", 8, 0, 0);
+ rinkj_byte_stream_printf( z->out, "SN%c%c%c%c%c", 3, 0, 0, 1, 0);
+ rinkj_byte_stream_printf( z->out, "SN%c%c%c%c%c", 3, 0, 0, 2, 6);
+ rinkj_byte_stream_printf( z->out, "SN%c%c%c%c%c", 3, 0, 0, 3, 0);
+ rinkj_byte_stream_printf( z->out, "SN%c%c%c%c%c", 3, 0, 0, 4, 129);
+ rinkj_byte_stream_printf( z->out, "SN%c%c%c%c%c", 3, 0, 0, 5, 51);
+ rinkj_byte_stream_printf( z->out, "SN%c%c%c%c%c", 3, 0, 0, 8, 0);
+ rinkj_byte_stream_printf( z->out, "SN%c%c%c%c%c", 3, 0, 0, 9, 2);
+ rinkj_byte_stream_printf( z->out, "SN%c%c%c%c%c", 3, 0, 0, 10, 0);
+ rinkj_byte_stream_printf( z->out, "SN%c%c%c%c%c", 3, 0, 0, 128, 1);
+ rinkj_byte_stream_printf( z->out, "SN%c%c%c%c%c", 3, 0, 0, 129, 0);
+ if (z->autocut >= 0)
+ rinkj_byte_stream_printf( z->out, "AC%c%c%c%c", 2, 0, 0, z->autocut);
+ if (z->blankskip >= 0)
+ rinkj_byte_stream_printf( z->out, "AC%c%c%c%c", 2, 0, 0, 64 + z->blankskip);
+ rinkj_byte_stream_printf( z->out, "DR%c%c%c%c%c%c", 4, 0, 0, 1, 0, 0);
+ rinkj_byte_stream_printf( z->out, "DR%c%c%c%c%c%c", 4, 0, 0, 0, 0, 0);
+ rinkj_byte_stream_printf( z->out, "PH%c%c%c%c", 2, 0, 0, 0);
+ rinkj_byte_stream_printf( z->out, "FP%c%c%c%c%c", 3, 0, 0, 0, 0);
+ rinkj_byte_stream_printf( z->out, "AC%c%c%c%c", 2, 0, 0, 64);
+ rinkj_byte_stream_printf( z->out, "SN%c%c%c%c%c", 3, 0, 0, 132, 1);
+ rinkj_byte_stream_printf( z->out, "PP%c%c%c%c%c", 3, 0, 0, 3, 0);
+ rinkj_byte_stream_printf( z->out, "IK%c%c%c%c", 2, 0, 0, 1);
+ rinkj_byte_stream_printf( z->out, "EX%c%c%c%c%c%c%c%c", 6, 0, 0, 0, 0, 0, 20, 0);
+ rinkj_byte_stream_printf( z->out,
+ "\033%c%c%c", 0, 0, 0);
+#endif
+
+ /* set graphics mode */
+ rinkj_byte_stream_printf (z->out, "\033(G\001%c\001", 0);
+
+ /* set units to 1/720" */
+ rinkj_byte_stream_printf (z->out, "\033(U\005%c\002\002\002\240\005", 0);
+
+ rinkj_epson_set_common(z);
+
+ pl = z->height * 720 / z->yres + 180;
+ /* set page length to page height + 1/4 inch */
+ rinkj_byte_stream_printf (z->out, "\033(S\010%c%c%c%c%c%c%c%c%c", 0,
+ pw & 255, (pw >> 8) & 255, (pw >> 16) & 255, pw >> 24,
+ pl & 255, (pl >> 8) & 255, (pl >> 16) & 255, pl >> 24);
+
+ rinkj_byte_stream_printf (z->out, "\033(c\010%c%c%c%c%c%c%c%c%c", 0,
+ 0, 0, 0, 0,
+ pl & 255, (pl >> 8) & 255, (pl >> 16) & 255, pl >> 24);
+
+ rinkj_epson_headres (z, 2880);
+
+ /* ESC ( v */
+
+ /* ESC \ */
+ return 0;
+}
+
+/**
+ * Spit out a command string to resemble the gimp-print output as much
+ * as possible.
+ **/
+static int
+rinkj_epsonc80_init (RinkjDevice *self, const RinkjDeviceParams *params)
+{
+ RinkjEscp *z = (RinkjEscp *)self;
+
+ rinkj_byte_stream_printf (z->out,
+ "%c%c%c\033\001@EJL 1284.4\n", 0, 0, 0);
+ rinkj_byte_stream_printf (z->out, "@EJL \n\033@\033@");
+
+ /* remote string goes here, but is probably optional */
+
+ /* set graphics mode */
+ rinkj_byte_stream_printf (z->out, "\033(G\001%c\001", 0);
+
+ /* set units to 1/720" */
+ rinkj_byte_stream_printf (z->out, "\033(U\005%c\002\002\002\240\005", 0);
+
+ rinkj_epson_set_common(z);
+
+ /* set page length to about 22.377 inches */
+ rinkj_byte_stream_printf (z->out, "\033(C\002%cx\037", 0);
+
+ /* set margins (magic) */
+ rinkj_byte_stream_printf (z->out, "\033(c\010%c\040\376\377\377\376\036%c%c",
+ 0, 0, 0);
+
+ /* ESC ( c */
+
+ /* ESC ( S */
+
+ /* set resolution to 360 x 180 dpi - should probably generate this
+ string from head_xres and head_yres */
+ rinkj_byte_stream_printf (z->out, "\033(D\004%c@8P(", 0);
+
+ /* ESC ( v */
+
+ /* ESC \ */
+ return 0;
+}
+
+static int
+rinkj_escp_init (RinkjDevice *self, const RinkjDeviceParams *params)
+{
+ RinkjEscp *z = (RinkjEscp *)self;
+ int resolution = 720; /* todo: make settable */
+ int uweave;
+ int height, top, bottom;
+ int i;
+
+ z->width = params->width;
+ z->height = params->height;
+ z->num_chan = params->n_planes;
+
+ /* weaving stuff */
+ z->pass = 0;
+
+ /* 0 inch margins on top, 0.5 on bottom */
+ top = 0 * resolution;
+ bottom = params->height + 0.5 * resolution;
+ height = params->height * resolution + resolution;
+
+ /* some defaults */
+ for (i = 0; i < sizeof(z->plane_offsets) / sizeof(z->plane_offsets[0]); i++)
+ z->plane_offsets[i] = 0;
+
+#ifdef VERBOSE
+ fprintf (stderr, "Manufacturer: %s; Model; %s\n", z->manufacturer,
+ z->model);
+#endif
+
+ if (z->model && !strcmp (z->model, "Stylus Photo 870"))
+ {
+ z->head_xres = 360;
+ z->head_yres = 120;
+ z->head_bps = 2;
+ z->n_pins = 48;
+ z->printer_weave = 0;
+ }
+ else if (z->model && !strcmp (z->model, "Stylus Photo 2200"))
+ {
+ z->head_xres = 360;
+ z->head_yres = 180;
+ z->head_bps = 2;
+ if (z->xres == 2880)
+ {
+ z->head_xres = 720;
+ z->head_bps = 1;
+ }
+ z->n_pins = 96;
+ z->printer_weave = 0;
+ z->plane_offsets[3] = z->yres / 360;
+ z->plane_offsets[4] = z->yres / 360;
+ z->plane_offsets[5] = z->yres / 360;
+ }
+ else if (z->model && !strcmp (z->model, "Stylus Photo 7600"))
+ {
+ z->head_xres = z->xres;
+ z->head_yres = z->yres;
+ z->head_bps = z->bps;
+ z->n_pins = 1;
+ }
+ else if (z->model && !strcmp (z->model, "Stylus C80"))
+ {
+ z->head_xres = 360;
+ z->head_yres = 180;
+ z->head_bps = 2;
+ z->n_pins = 60;
+ z->printer_weave = 0;
+ z->plane_offsets[0] = 480;
+ z->plane_offsets[1] = 240;
+ z->plane_offsets[2] = 480;
+ }
+ else
+ {
+ z->spacing = 1;
+ z->n_pins = 1;
+ }
+
+ z->spacing = z->yres / z->head_yres;
+ z->passes_per_scan = z->xres / z->head_xres;
+
+ /* microweave */
+ uweave = (z->n_pins == 1);
+
+ z->max_offset = 0;
+ for (i = 0; i < sizeof(z->plane_offsets) / sizeof(z->plane_offsets[0]); i++)
+ if (z->plane_offsets[i] > z->max_offset)
+ z->max_offset = z->plane_offsets[i];
+
+ z->y = rinkj_escp_ytop (z, z->spacing * z->passes_per_scan - 1, NULL) -
+ (z->spacing - 1) + z->max_offset;
+
+ z->planestride = (z->width * z->bps + 7) >> 3;
+ z->rowstride = z->planestride * z->num_chan;
+ z->bufheight = 2048; /* todo: compute */
+ z->buf = (char *)calloc (z->rowstride, z->bufheight);
+ z->buf_linevalid = (unsigned char *)calloc (z->num_chan, z->bufheight);
+ z->vertpos = -1;
+
+ if (z->model && !strcmp (z->model, "Stylus Photo 870"))
+ rinkj_epson870_init (self, params);
+ else if (z->model && !strcmp (z->model, "Stylus Photo 2200"))
+ rinkj_epson2200_init (self, params);
+ else if (z->model && !strcmp (z->model, "Stylus Photo 7600"))
+ rinkj_epson7600_init (self, params);
+ else if (z->model && !strcmp (z->model, "Stylus C80"))
+ rinkj_epsonc80_init (self, params);
+
+ /* todo: error checking */
+ return 0;
+
+}
+
+/**
+ * rinkj_escp_shuffle_dblx: Shuffle bits for doubled X resolution.
+ * @dst: Where to store shuffled bits.
+ * @src: Source of bits.
+ * @pass: Pass number.
+ * @n_bytes: Number of bytes in @src.
+ *
+ * Samples half of the bits in @src, finely interleaved for Epson Stylus
+ * Color 1440 x 720 modes. If @pass is 0, then it selects bits 7, 5, 3, 1.
+ * If @pass is 1, then 6, 4, 2, 0.
+ *
+ * The number of bytes in @dst should be (@n_bytes + 1) >> 1.
+ **/
+static void
+rinkj_escp_shuffle_dblx (char *dst, const char *src, int pass, int n_bytes)
+{
+ int i;
+ int n_dst;
+ unsigned char s0, s1;
+
+ n_dst = n_bytes >> 1;
+ for (i = 0; i < n_dst; i++)
+ {
+ s0 = src[i * 2] << pass;
+ s1 = src[i * 2 + 1] << pass;
+ dst[i] = (s0 & 0x80) | ((s0 & 0x20) << 1) | ((s0 & 8) << 2) | ((s0 & 2) << 3) |
+ ((s1 & 0x80) >> 4) | ((s1 & 0x20) >> 3) | ((s1 & 8) >> 2) | ((s1 & 2) >> 1);
+ }
+ if (n_bytes & 1)
+ {
+ s0 = src[n_bytes - 1] << pass;
+ dst[n_dst] = (s0 & 0x80) | ((s0 & 0x20) << 1) | ((s0 & 8) << 2) | ((s0 & 2) << 3);
+ }
+}
+
+/**
+ * rinkj_escp_shuffle_4pass_2bit: Shuffle bits for quadrupled X resolution.
+ * @dst: Where to store shuffled bits.
+ * @src: Source of bits.
+ * @pass: Pass number.
+ * @n_bytes: Number of bytes in @src.
+ *
+ * Samples the bits in @src for 4-pass, 1-bit operation.
+ *
+ * The number of bytes in @dst should be (@n_bytes + 1) >> 1.
+ **/
+static void
+rinkj_escp_shuffle_4pass_1bit (char *dst, const char *src, int pass, int n_bytes)
+{
+ int i;
+ int n_dst;
+ unsigned char s0, s1, s2, s3;
+ int shift = pass;
+
+ n_dst = n_bytes >> 2;
+ for (i = 0; i < n_dst; i++)
+ {
+ s0 = src[i * 4] << shift;
+ s1 = src[i * 4 + 1] << shift;
+ s2 = src[i * 4 + 2] << shift;
+ s3 = src[i * 4 + 3] << shift;
+ dst[i] = (s0 & 0x80) | ((s0 & 8) << 3) |
+ ((s1 & 0x80) >> 2) | ((s1 & 8) << 1) |
+ ((s2 & 0x80) >> 4) | ((s2 & 8) >> 1) |
+ ((s3 & 0x80) >> 6) | ((s3 & 8) >> 3);
+ }
+ if (n_bytes & 3)
+ {
+ char d = 0;
+
+ for (i = 0; i < (n_bytes & 3); i++)
+ {
+ s0 = src[n_dst * 4 + i] << shift;
+ d |= ((s0 & 0x80) | ((s0 & 8) << 3)) >> (i << 1);
+ }
+ dst[n_dst] = d;
+ }
+}
+
+/**
+ * rinkj_escp_shuffle_2pass_2bit: Shuffle bits for doubled X resolution.
+ * @dst: Where to store shuffled bits.
+ * @src: Source of bits.
+ * @pass: Pass number.
+ * @n_bytes: Number of bytes in @src.
+ *
+ * Samples the bits in @src for 2-pass, 2-bit operation.
+ *
+ * The number of bytes in @dst should be (@n_bytes + 1) >> 1.
+ **/
+static void
+rinkj_escp_shuffle_2pass_2bit (char *dst, const char *src, int pass, int n_bytes)
+{
+ int i;
+ int n_dst;
+ unsigned char s0, s1;
+ int shift = pass << 1;
+
+ n_dst = n_bytes >> 1;
+ for (i = 0; i < n_dst; i++)
+ {
+ s0 = src[i * 2] << shift;
+ s1 = src[i * 2 + 1] << shift;
+ dst[i] = (s0 & 0xc0) | ((s0 & 0x0c) << 2) |
+ ((s1 & 0xc0) >> 4) | ((s1 & 0x0c) >> 2);
+ }
+ if (n_bytes & 1)
+ {
+ s0 = src[n_bytes - 1] << shift;
+ dst[i] = (s0 & 0xc0) | ((s0 & 0x0c) << 2);
+ }
+}
+
+/**
+ * rinkj_escp_shuffle_4pass_2bit: Shuffle bits for quadrupled X resolution.
+ * @dst: Where to store shuffled bits.
+ * @src: Source of bits.
+ * @pass: Pass number.
+ * @n_bytes: Number of bytes in @src.
+ *
+ * Samples the bits in @src for 4-pass, 2-bit operation.
+ *
+ * The number of bytes in @dst should be (@n_bytes + 1) >> 1.
+ **/
+static void
+rinkj_escp_shuffle_4pass_2bit (char *dst, const char *src, int pass, int n_bytes)
+{
+ int i;
+ int n_dst;
+ unsigned char s0, s1, s2, s3;
+ int shift = pass << 1;
+
+ n_dst = n_bytes >> 2;
+ for (i = 0; i < n_dst; i++)
+ {
+ s0 = src[i * 4] << shift;
+ s1 = src[i * 4 + 1] << shift;
+ s2 = src[i * 4 + 2] << shift;
+ s3 = src[i * 4 + 3] << shift;
+ dst[i] = (s0 & 0xc0) | ((s1 & 0xc0) >> 2) |
+ ((s2 & 0xc0) >> 4) | ((s3 & 0xc0) >> 6);
+ }
+ if (n_bytes & 3)
+ {
+ char d = 0;
+
+ for (i = 0; i < (n_bytes & 3); i++)
+ d |= ((src[n_dst * 4 + i] << shift) & 0xc0) >> (i << 1);
+ dst[n_dst] = d;
+ }
+}
+
+#define DOT 3
+
+static void
+rinkj_escp_1pass_dblx (char *dst, const char *src, int n_bytes)
+{
+ int i;
+
+ /* todo: may need half-byte fixup */
+ for (i = 0; i < n_bytes; i ++)
+ {
+ unsigned char s = src[i];
+ dst[i * 2] = (((s & 0x80) >> 1) | ((s & 0x40) >> 2) |
+ ((s & 0x20) >> 3) | ((s & 0x10) >> 4)) * DOT;
+ dst[i * 2 + 1] = (((s & 8) << 3) | ((s & 4) << 2) |
+ ((s & 2) << 1) | (s & 1)) * DOT;
+ }
+}
+
+static void
+rinkj_escp_select_dblx (char *dst, const char *src, int x_pass, int n_bytes)
+{
+ int i;
+
+ for (i = 0; i < n_bytes; i++)
+ dst[i] = (src[i] >> (1 - x_pass) & 0x55) * DOT;
+}
+
+static void
+rinkj_escp_sel_shuffle_dblx (char *dst, const char *src, int pass, int n_bytes)
+{
+ int i;
+ int n_dst;
+ unsigned char s0, s1;
+
+ n_dst = n_bytes >> 1;
+ for (i = 0; i < n_dst; i++)
+ {
+ s0 = src[i * 2] << pass;
+ s1 = src[i * 2 + 1] << pass;
+ dst[i] = (((s0 & 0x80) >> 1) | ((s0 & 8) << 1) |
+ ((s1 & 0x80) >> 5) | ((s1 & 0x8) >> 3)) * DOT;
+ }
+ if (n_bytes & 1)
+ {
+ s0 = src[n_bytes - 1] << pass;
+ dst[n_dst] = (((s0 & 0x80) >> 1) | ((s0 & 8) << 1)) * DOT;
+ }
+}
+
+static void
+rinkj_escp_shuffle (char *dst, const char *src, int pass, int n_bytes,
+ int passes_per_scan, int bps, int head_bps)
+{
+ if (bps == 2 && head_bps == 2)
+ {
+ if (passes_per_scan == 1)
+ memcpy (dst, src, n_bytes);
+ else if (passes_per_scan == 2)
+ rinkj_escp_shuffle_2pass_2bit (dst, src, pass, n_bytes);
+ else if (passes_per_scan == 4)
+ rinkj_escp_shuffle_4pass_2bit (dst, src, pass, n_bytes);
+ }
+ else if (bps == 1 && head_bps == 2)
+ {
+ if (passes_per_scan == 1)
+ rinkj_escp_1pass_dblx (dst, src, n_bytes);
+ if (passes_per_scan == 2)
+ rinkj_escp_select_dblx (dst, src, pass, n_bytes);
+ else if (passes_per_scan == 4)
+ rinkj_escp_sel_shuffle_dblx (dst, src, pass, n_bytes);
+ }
+ else if (bps == 1 && head_bps == 1)
+ {
+ if (passes_per_scan == 1)
+ memcpy (dst, src, n_bytes);
+ else if (passes_per_scan == 2)
+ rinkj_escp_shuffle_dblx (dst, src, pass, n_bytes);
+ else if (passes_per_scan == 4)
+ rinkj_escp_shuffle_4pass_1bit (dst, src, pass, n_bytes);
+ }
+}
+
+/**
+ * rinkj_escp_compress_rle: Compress a chunk of data using runlengths.
+ * @dst: Where to store compressed data.
+ * @src: Source of data.
+ * @n: Size of @src in bytes.
+ *
+ * Compresses a chunk of data using ESCP/2 runlength encoding. @dst must be
+ * at least @n + ((@n + 127) >> 7) bytes long.
+ *
+ * Return value: size of @dst in bytes.
+ **/
+static int
+rinkj_escp_compress_rle (char *dst, const char *src, int n)
+{
+ int i, j;
+ int b;
+ int run;
+
+ j = 0;
+ for (i = 0; i < n; i += run)
+ {
+ b = src[i];
+ for (run = 1; run < 129 && i + run < n; run++)
+ if (b != src[i + run])
+ break;
+ if (run > 2)
+ {
+ dst[j++] = 257 - run;
+ dst[j++] = b;
+ }
+ else
+ {
+ for (run = 1; run < 128 && i + run < n; run++)
+ {
+ b = src[i + run];
+ if (i + run + 2 < n &&
+ b == src[i + run + 1] && b == src[i + run + 2])
+ break;
+ }
+ dst[j++] = run - 1;
+ memcpy (dst + j, src + i, run);
+ j += run;
+ }
+ }
+
+ return j;
+}
+
+static int
+rinkj_escp_flush (RinkjEscp *z)
+{
+ int xsb, xsb_out;
+ int xs_out;
+ int status;
+ const int plane[7] = {3, 1, 0, 2, 5, 4, 6};
+ const int color[7] = {0, 1, 2, 4, 17, 18, 16};
+ int i, j;
+ int ytop, ysc;
+ int bufy;
+ char *thisbuf;
+ char *compress_buf = NULL;
+ int rle = 1;
+ int x_pass;
+ char pass_mask;
+ int m;
+
+ ytop = rinkj_escp_ytop (z, z->pass, &x_pass);
+ pass_mask = 1 << x_pass;
+
+#ifdef VERBOSE
+ fprintf (stderr, "flush pass %d: ytop = %d (= %d mod %d), x_pass = %d\n",
+ z->pass, ytop, ytop % z->spacing, z->spacing, x_pass);
+#endif
+
+ xsb = (z->width * z->bps + 7) >> 3;
+
+ xs_out = (z->width + z->passes_per_scan - 1) / (z->passes_per_scan);
+ xsb_out = (((z->width * z->head_bps + 7) >> 3) + z->passes_per_scan - 1) /
+ (z->passes_per_scan);
+
+ thisbuf = malloc (xsb_out);
+ if (rle)
+ compress_buf = malloc (xsb_out + ((xsb_out + 127) >> 7));
+ ysc = ytop;
+ if (z->vertpos == -1)
+ status = rinkj_byte_stream_printf (z->out, "\033(V%c%c%c%c",
+ 2, 0, ysc & 0xff, (ysc >> 8) & 0xff);
+ else
+ {
+ int yrel = ysc - z->vertpos;
+ status = rinkj_byte_stream_printf (z->out, "\033(v%c%c%c%c%c%c",
+ 4, 0, yrel & 0xff, (yrel >> 8) & 0xff, (yrel >> 16) & 0xff, (yrel >> 24) & 0xff);
+ }
+
+ z->vertpos = ysc;
+
+ if (status < 0)
+ {
+ free (thisbuf);
+ if (rle)
+ free (compress_buf);
+ return status;
+ }
+
+ for (i = 0; i < z->num_chan; i++)
+ {
+ int plane_off = z->plane_offsets[i];
+
+ m = (z->y - ytop + z->spacing - 1 - plane_off) / z->spacing;
+ if (m > z->n_pins)
+ m = z->n_pins;
+
+ if (m <= 0)
+ continue;
+
+ if (1)
+ {
+ /* todo: make this calculation fully agile */
+ int x = (x_pass & 3);
+ status = rinkj_byte_stream_printf (z->out, "\033($\4%c%c%c%c%c",
+ 0, x & 0xff,
+ (x >> 8) & 0xff,
+ (x >> 16) & 0xff,
+ (x >> 24) & 0xff);
+
+ if (status < 0)
+ {
+ free(thisbuf);
+ if (rle)
+ free(compress_buf);
+ return status;
+ }
+ }
+
+ status = rinkj_byte_stream_printf (z->out, "\033i%c%c%c%c%c%c%c",
+ color[i],
+ rle,
+ z->head_bps,
+ xsb_out & 0xff,
+ (xsb_out >> 8) & 0xff,
+ m & 0xff, m >> 8);
+ if (status < 0)
+ {
+ free(thisbuf);
+ if (rle)
+ free(compress_buf);
+ return status;
+ }
+ for (j = 0; j < m; j++)
+ {
+ const char *line;
+
+ bufy = (ytop + j * z->spacing + plane_off) % z->bufheight;
+ line = z->buf + bufy * z->rowstride + plane[i] * z->planestride;
+ if (z->buf_linevalid[bufy * z->num_chan + i] & pass_mask)
+ rinkj_escp_shuffle (thisbuf, line, x_pass, xsb,
+ z->passes_per_scan, z->bps, z->head_bps);
+ else
+ memset (thisbuf, 0, xsb_out);
+ z->buf_linevalid[bufy * z->num_chan + i] &= ~pass_mask;
+#ifdef VERBOSE
+ if (i == 0 && j == 0)
+ {
+ fprintf (stderr, "flush line[0] = %d shuffled[0] = %d\n",
+ line[0], thisbuf[0]);
+ }
+#endif
+ if (rle)
+ {
+ int nbytes;
+
+ nbytes = rinkj_escp_compress_rle (compress_buf, thisbuf, xsb_out);
+ status = rinkj_byte_stream_write (z->out, compress_buf, nbytes);
+ }
+ else
+ status = rinkj_byte_stream_write (z->out, thisbuf, xsb_out);
+ }
+ if (status < 0) return status;
+#if 0
+ status = rinkj_byte_stream_puts (z->out, "\r");
+ if (status < 0) return status;
+#endif
+ }
+
+ z->pass++;
+ if (rle)
+ free (compress_buf);
+ free (thisbuf);
+ return status;
+}
+
+static int
+rinkj_escp_flush_bottom (RinkjEscp *z)
+{
+ int ytop;
+ int status;
+
+ for (;;)
+ {
+ ytop = rinkj_escp_ytop (z, z->pass, NULL);
+ if (ytop >= z->y)
+ break;
+ status = rinkj_escp_flush (z);
+ if (status != 0)
+ return status;
+ }
+ return 0;
+}
+
+static void
+rinkj_escp_free (RinkjEscp *z)
+{
+ if (z->manufacturer)
+ free (z->manufacturer);
+ if (z->model)
+ free (z->model);
+ free (z->buf);
+ free (z->buf_linevalid);
+ free (z);
+}
+
+static int
+rinkj_escp_write (RinkjDevice *self, const char **data)
+{
+ RinkjEscp *z = (RinkjEscp *)self;
+ int xsb;
+ int status;
+ int i;
+ int ytop;
+ int bufy;
+ int dblx_pass;
+
+ if (data == NULL)
+ {
+ status = rinkj_escp_flush_bottom (z);
+ /* todo: check error */
+ status = rinkj_byte_stream_puts (z->out, "\f\033@");
+ /* todo: check error */
+ status = rinkj_byte_stream_close (z->out);
+ rinkj_escp_free (z);
+ return status;
+ }
+
+ xsb = (z->width * z->bps + 7) >> 3;
+ bufy = z->y % z->bufheight;
+ for (i = 0; i < z->num_chan; i++)
+ {
+ memcpy (z->buf + bufy * z->rowstride + i * z->planestride, data[i], xsb);
+ z->buf_linevalid[bufy * z->num_chan + i] = 0xff;
+ }
+
+ z->y++;
+
+ ytop = rinkj_escp_ytop (z, z->pass, &dblx_pass);
+
+ if (z->y < ytop + (z->n_pins - 1) * z->spacing + 1 + z->max_offset)
+ return 0;
+
+ return rinkj_escp_flush (z);
+}
+
+RinkjDevice *
+rinkj_epson870_new (RinkjByteStream *out)
+{
+ RinkjEscp *result;
+
+ result = (RinkjEscp *)malloc (sizeof (RinkjEscp));
+
+ result->super.set = rinkj_escp_set;
+ result->super.write = rinkj_escp_write;
+ result->super.init = rinkj_escp_init;
+ result->super.init_happened = 0;
+ result->width = 0;
+ result->out = out;
+
+ result->num_chan = 4;
+ result->bps = 1;
+
+ result->manufacturer = NULL;
+ result->model = NULL;
+
+ result->autocut = -1;
+ result->microdot = -1;
+ result->unidir = -1;
+ result->printer_weave = -1;
+
+ return &result->super;
+}
diff --git a/devices/rinkj/rinkj-epson870.h b/devices/rinkj/rinkj-epson870.h
new file mode 100644
index 000000000..fd7b048be
--- /dev/null
+++ b/devices/rinkj/rinkj-epson870.h
@@ -0,0 +1,20 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* A Rinkj driver for a number of variable-dot Epson devices. */
+
+RinkjDevice *
+rinkj_epson870_new (RinkjByteStream *out);
diff --git a/devices/rinkj/rinkj-screen-eb.c b/devices/rinkj/rinkj-screen-eb.c
new file mode 100644
index 000000000..129f85549
--- /dev/null
+++ b/devices/rinkj/rinkj-screen-eb.c
@@ -0,0 +1,329 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* A Rinkj module encapsulating ETS screening. */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <math.h>
+
+#include "rinkj-device.h"
+#include "rinkj-config.h"
+#include "rinkj-dither.h"
+#include "evenbetter-rll.h"
+#include "rinkj-screen-eb.h"
+
+#define MAX_CHAN 16
+
+typedef struct _RinkjScreenEb RinkjScreenEb;
+
+struct _RinkjScreenEb {
+ RinkjDevice super;
+ int width_out;
+ int height_in, height_out;
+ int n_planes;
+ RinkjDevice *dev_out;
+ EvenBetterCtx *dither;
+ int **lut;
+ double xscale, yscale;
+ int yrem;
+ int aspect;
+ int bps;
+};
+
+static int
+rinkj_screen_eb_set (RinkjDevice *self, const char *config)
+{
+ RinkjScreenEb *z = (RinkjScreenEb *)self;
+ const char *p, *next;
+ char *key, *val;
+
+ for (p = config; (key = rinkj_config_keyval (p, &val, &next)); p = next)
+ {
+ if (!strcmp (key, "Dither"))
+ {
+ if (!strcmp (val, "1"))
+ z->bps = 1;
+ else if (!strcmp (val, "2"))
+ {
+ z->bps = 2;
+ rinkj_device_set_param_int (z->dev_out, "BitsPerSample", 2);
+ }
+ }
+ else if (!strcmp (key, "Aspect"))
+ {
+ z->aspect = atoi (val);
+ }
+ free (key);
+ free (val);
+ }
+ return 0;
+}
+
+static int
+rinkj_screen_eb_init (RinkjDevice *self, const RinkjDeviceParams *params)
+{
+ RinkjScreenEb *z = (RinkjScreenEb *)self;
+ int n_planes = params->n_planes;
+ EvenBetterParams ebp;
+ int strengths4[] = { 128, 50, 50, 10 };
+ int strengths6[] = { 128, 50, 50, 25, 25, 10 };
+ int strengths7[] = { 128, 80, 50, 50, 25, 26, 10 };
+ int *strengths;
+
+ RinkjDeviceParams out_params;
+
+ out_params.width = floor (z->xscale * params->width + 0.5);
+ out_params.height = floor (z->yscale * params->height + 0.5);
+ out_params.n_planes = n_planes;
+ out_params.plane_names = params->plane_names;
+
+ z->yrem = params->height >> 1;
+
+ if (n_planes == 4)
+ strengths = strengths4;
+ else if (n_planes == 6)
+ strengths = strengths6;
+ else if (n_planes == 7)
+ strengths = strengths7;
+
+ ebp.source_width = params->width;
+ ebp.dest_width = out_params.width;
+ ebp.levels = 1 << z->bps;
+ ebp.luts = z->lut;
+ ebp.n_planes = n_planes;
+ ebp.aspect = z->aspect;
+ ebp.rbscale = 0.0;
+ ebp.strengths = strengths;
+ ebp.rand_scale = 0;
+ ebp.even_c1_scale = 1;
+ ebp.do_shadows = 0;
+ ebp.dump_file = NULL;
+ ebp.gamma = 0;
+ z->dither = even_better_new (&ebp);
+
+ z->width_out = out_params.width;
+ z->height_in = params->height;
+ z->height_out = out_params.height;
+ z->n_planes = n_planes;
+#ifdef DEBUG_OUT
+ fprintf (stdout, "P5\n%d %d\n255\n", params->width, params->height);
+#endif
+ return rinkj_device_init (z->dev_out, &out_params);
+}
+
+static int
+rinkj_screen_eb_write (RinkjDevice *self, const char **data)
+{
+ RinkjScreenEb *z = (RinkjScreenEb *)self;
+ uchar **out_data;
+ uchar **out_buf;
+ int i;
+ int n_planes = z->n_planes;
+ int xs = z->width_out;
+ int xsb;
+ int status;
+ uchar **data_permuted;
+ int cmyk_permutation[] = { 3, 0, 1, 2 };
+ int ccmmyk_permutation[] = { 3, 0, 1, 4, 5, 2 };
+ int ccmmykk_permutation[] = { 3, 6, 0, 1, 4, 5, 2 };
+ int *permutation;
+
+ if (data == NULL)
+ {
+ status = rinkj_device_write (z->dev_out, NULL);
+ even_better_free (z->dither);
+ free (self);
+ return status;
+ }
+
+ if (n_planes == 4)
+ permutation = cmyk_permutation;
+ else if (n_planes == 6)
+ permutation = ccmmyk_permutation;
+ else if (n_planes == 7)
+ permutation = ccmmykk_permutation;
+ else
+ /* NYI */
+ return -1;
+
+#ifdef DEBUG_OUT
+ fwrite (data[0], 1, z->width, stdout);
+#endif
+
+ xsb = (xs * z->bps + 7) >> 3;
+
+ out_data = (uchar **)malloc (n_planes * sizeof(char *));
+ out_buf = (uchar **)malloc (n_planes * sizeof(char *));
+ data_permuted = (uchar **)malloc (n_planes * sizeof(char *));
+
+ for (i = 0; i < n_planes; i++)
+ {
+ out_data[i] = malloc (xsb);
+ out_buf[i] = malloc (xs);
+ data_permuted[i] = data[permutation[i]];
+ }
+
+ status = 0;
+ for (; status >= 0 && z->yrem < z->height_out; z->yrem += z->height_in)
+ {
+ even_better_line (z->dither,
+ (unsigned char *)out_buf,
+ (unsigned char *)data_permuted);
+
+ for (i = 0; i < n_planes; i++)
+ {
+ uchar *pd = out_data[permutation[i]];
+ uchar *pb = out_buf[i];
+ int x;
+
+ if (z->bps == 2)
+ {
+ for (x = 0; x < xs - 3; x += 4)
+ {
+ pd[x >> 2] = (pb[x] << 6) | (pb[x + 1] << 4) |
+ (pb[x + 2] << 2) | pb[x + 3];
+ }
+ if (x < xs)
+ {
+ int j;
+ uchar b = 0;
+
+ for (j = 0; j < xs - x; j++)
+ b |= pb[x + j] << ((3 - j) << 1);
+ pd[x >> 2] = b;
+ }
+ }
+ else if (z->bps == 1)
+ {
+ for (x = 0; x < xs - 7; x += 8)
+ {
+ pd[x >> 3] = (pb[x] << 7) | (pb[x + 1] << 6) |
+ (pb[x + 2] << 5) | (pb[x + 3] << 4) |
+ (pb[x + 4] << 3) | (pb[x + 5] << 2) |
+ (pb[x + 6] << 1) | (pb[x + 7] << 0);
+ }
+ if (x < xs)
+ {
+ int j;
+ uchar b = 0;
+
+ for (j = 0; j < xs - x; j++)
+ b |= pb[x + j] << (7 - j);
+ pd[x >> 3] = b;
+ }
+ }
+ }
+
+ status = rinkj_device_write (z->dev_out, (const char **)out_data);
+ }
+ z->yrem -= z->height_out;
+
+ for (i = 0; i < n_planes; i++)
+ {
+ free (out_data[i]);
+ free (out_buf[i]);
+ }
+ free (out_data);
+ free (out_buf);
+ free (data_permuted);
+ return status;
+}
+
+/**
+ * rinkj_screen_eb_new: Create a new Screen object.
+ * @dev_out: Output device to take screened image.
+ *
+ * Creates a new Screen object. This implementation currently just
+ * iterates a Dither for all planes in the image, although other
+ * implementations (such as a dithering algorithm that works on
+ * multiple planes concurrently) are also possible.
+ *
+ * Return value: a #RinkjDevice to take separated contone image.
+ **/
+RinkjDevice *
+rinkj_screen_eb_new (RinkjDevice *dev_out)
+{
+ RinkjScreenEb *result;
+
+ result = (RinkjScreenEb *)malloc (sizeof(RinkjScreenEb));
+
+ result->super.set = rinkj_screen_eb_set;
+ result->super.write = rinkj_screen_eb_write;
+ result->super.init = rinkj_screen_eb_init;
+ result->super.init_happened = 0;
+ result->dev_out = dev_out;
+ result->lut = NULL;
+ result->xscale = 1.0;
+ result->yscale = 1.0;
+ result->aspect = 1;
+ result->bps = 1;
+
+ return &result->super;
+}
+
+/* todo: move to _set */
+void
+rinkj_screen_eb_set_scale (RinkjDevice *self, double xscale, double yscale)
+{
+ RinkjScreenEb *z = (RinkjScreenEb *)self;
+ z->xscale = xscale;
+ z->yscale = yscale;
+}
+
+void
+rinkj_screen_eb_set_gamma (RinkjDevice *self, int plane, double gamma, double max)
+{
+ RinkjScreenEb *z = (RinkjScreenEb *)self;
+ int i;
+
+ if (plane >= MAX_CHAN)
+ return;
+
+ if (z->lut == NULL)
+ z->lut = (int **)malloc (MAX_CHAN * sizeof (int *));
+
+ z->lut[plane] = (int *)malloc (256 * sizeof (int));
+ for (i = 0; i < 256; i++)
+ {
+ double v;
+ v = pow (i * (1.0 / 255.0), gamma);
+ v += (1 - v) * (1 - max);
+ z->lut[plane][i] = floor (0.5 + 0x1000000 * v);
+ }
+}
+
+void
+rinkj_screen_eb_set_lut (RinkjDevice *self, int plane, const double *lut)
+{
+ RinkjScreenEb *z = (RinkjScreenEb *)self;
+ int i;
+
+ if (plane >= MAX_CHAN)
+ return;
+
+ if (z->lut == NULL)
+ z->lut = (int **)malloc (MAX_CHAN * sizeof (int *));
+
+ z->lut[plane] = (int *)malloc (256 * sizeof (int));
+ for (i = 0; i < 256; i++)
+ {
+ double v;
+ v = 1.0 - lut[i];
+ z->lut[plane][i] = floor (0.5 + 0x1000000 * v);
+ }
+}
diff --git a/devices/rinkj/rinkj-screen-eb.h b/devices/rinkj/rinkj-screen-eb.h
new file mode 100644
index 000000000..6d3cbfb5c
--- /dev/null
+++ b/devices/rinkj/rinkj-screen-eb.h
@@ -0,0 +1,29 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* A Rinkj module encapsulating ETS screening. */
+
+RinkjDevice *
+rinkj_screen_eb_new (RinkjDevice *dev_out);
+
+void
+rinkj_screen_eb_set_scale (RinkjDevice *self, double xscale, double yscale);
+
+void
+rinkj_screen_eb_set_gamma (RinkjDevice *self, int plane, double gamma, double max);
+
+void
+rinkj_screen_eb_set_lut (RinkjDevice *self, int plane, const double *lut);
diff --git a/devices/vector/gdevagl.c b/devices/vector/gdevagl.c
new file mode 100644
index 000000000..76b0836f7
--- /dev/null
+++ b/devices/vector/gdevagl.c
@@ -0,0 +1,4312 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+#include "gdevagl.h"
+
+single_glyph_list_t SingleGlyphList[] = {
+{"A", 0x0041},
+{"AE", 0x00C6},
+{"AEacute", 0x01FC},
+{"AEmacron", 0x01E2},
+{"AEsmall", 0xF7E6},
+{"Aacute", 0x00C1},
+{"Aacutesmall", 0xF7E1},
+{"Abreve", 0x0102},
+{"Abreveacute", 0x1EAE},
+{"Abrevecyrillic", 0x04D0},
+{"Abrevedotbelow", 0x1EB6},
+{"Abrevegrave", 0x1EB0},
+{"Abrevehookabove", 0x1EB2},
+{"Abrevetilde", 0x1EB4},
+{"Acaron", 0x01CD},
+{"Acircle", 0x24B6},
+{"Acircumflex", 0x00C2},
+{"Acircumflexacute", 0x1EA4},
+{"Acircumflexdotbelow", 0x1EAC},
+{"Acircumflexgrave", 0x1EA6},
+{"Acircumflexhookabove", 0x1EA8},
+{"Acircumflexsmall", 0xF7E2},
+{"Acircumflextilde", 0x1EAA},
+{"Acute", 0xF6C9},
+{"Acutesmall", 0xF7B4},
+{"Acyrillic", 0x0410},
+{"Adblgrave", 0x0200},
+{"Adieresis", 0x00C4},
+{"Adieresiscyrillic", 0x04D2},
+{"Adieresismacron", 0x01DE},
+{"Adieresissmall", 0xF7E4},
+{"Adotbelow", 0x1EA0},
+{"Adotmacron", 0x01E0},
+{"Agrave", 0x00C0},
+{"Agravesmall", 0xF7E0},
+{"Ahookabove", 0x1EA2},
+{"Aiecyrillic", 0x04D4},
+{"Ainvertedbreve", 0x0202},
+{"Alpha", 0x0391},
+{"Alphatonos", 0x0386},
+{"Amacron", 0x0100},
+{"Amonospace", 0xFF21},
+{"Aogonek", 0x0104},
+{"Aring", 0x00C5},
+{"Aringacute", 0x01FA},
+{"Aringbelow", 0x1E00},
+{"Aringsmall", 0xF7E5},
+{"Asmall", 0xF761},
+{"Atilde", 0x00C3},
+{"Atildesmall", 0xF7E3},
+{"Aybarmenian", 0x0531},
+{"B", 0x0042},
+{"Bcircle", 0x24B7},
+{"Bdotaccent", 0x1E02},
+{"Bdotbelow", 0x1E04},
+{"Becyrillic", 0x0411},
+{"Benarmenian", 0x0532},
+{"Beta", 0x0392},
+{"Bhook", 0x0181},
+{"Blinebelow", 0x1E06},
+{"Bmonospace", 0xFF22},
+{"Brevesmall", 0xF6F4},
+{"Bsmall", 0xF762},
+{"Btopbar", 0x0182},
+{"C", 0x0043},
+{"Caarmenian", 0x053E},
+{"Cacute", 0x0106},
+{"Caron", 0xF6CA},
+{"Caronsmall", 0xF6F5},
+{"Ccaron", 0x010C},
+{"Ccedilla", 0x00C7},
+{"Ccedillaacute", 0x1E08},
+{"Ccedillasmall", 0xF7E7},
+{"Ccircle", 0x24B8},
+{"Ccircumflex", 0x0108},
+{"Cdot", 0x010A},
+{"Cdotaccent", 0x010A},
+{"Cedillasmall", 0xF7B8},
+{"Chaarmenian", 0x0549},
+{"Cheabkhasiancyrillic", 0x04BC},
+{"Checyrillic", 0x0427},
+{"Chedescenderabkhasiancyrillic", 0x04BE},
+{"Chedescendercyrillic", 0x04B6},
+{"Chedieresiscyrillic", 0x04F4},
+{"Cheharmenian", 0x0543},
+{"Chekhakassiancyrillic", 0x04CB},
+{"Cheverticalstrokecyrillic", 0x04B8},
+{"Chi", 0x03A7},
+{"Chook", 0x0187},
+{"Circumflexsmall", 0xF6F6},
+{"Cmonospace", 0xFF23},
+{"Coarmenian", 0x0551},
+{"Csmall", 0xF763},
+{"D", 0x0044},
+{"DZ", 0x01F1},
+{"DZcaron", 0x01C4},
+{"Daarmenian", 0x0534},
+{"Dafrican", 0x0189},
+{"Dcaron", 0x010E},
+{"Dcedilla", 0x1E10},
+{"Dcircle", 0x24B9},
+{"Dcircumflexbelow", 0x1E12},
+{"Dcroat", 0x0110},
+{"Ddotaccent", 0x1E0A},
+{"Ddotbelow", 0x1E0C},
+{"Decyrillic", 0x0414},
+{"Deicoptic", 0x03EE},
+{"Delta", 0x2206},
+{"Deltagreek", 0x0394},
+{"Dhook", 0x018A},
+{"Dieresis", 0xF6CB},
+{"DieresisAcute", 0xF6CC},
+{"DieresisGrave", 0xF6CD},
+{"Dieresissmall", 0xF7A8},
+{"Digammagreek", 0x03DC},
+{"Djecyrillic", 0x0402},
+{"Dlinebelow", 0x1E0E},
+{"Dmonospace", 0xFF24},
+{"Dotaccentsmall", 0xF6F7},
+{"Dslash", 0x0110},
+{"Dsmall", 0xF764},
+{"Dtopbar", 0x018B},
+{"Dz", 0x01F2},
+{"Dzcaron", 0x01C5},
+{"Dzeabkhasiancyrillic", 0x04E0},
+{"Dzecyrillic", 0x0405},
+{"Dzhecyrillic", 0x040F},
+{"E", 0x0045},
+{"Eacute", 0x00C9},
+{"Eacutesmall", 0xF7E9},
+{"Ebreve", 0x0114},
+{"Ecaron", 0x011A},
+{"Ecedillabreve", 0x1E1C},
+{"Echarmenian", 0x0535},
+{"Ecircle", 0x24BA},
+{"Ecircumflex", 0x00CA},
+{"Ecircumflexacute", 0x1EBE},
+{"Ecircumflexbelow", 0x1E18},
+{"Ecircumflexdotbelow", 0x1EC6},
+{"Ecircumflexgrave", 0x1EC0},
+{"Ecircumflexhookabove", 0x1EC2},
+{"Ecircumflexsmall", 0xF7EA},
+{"Ecircumflextilde", 0x1EC4},
+{"Ecyrillic", 0x0404},
+{"Edblgrave", 0x0204},
+{"Edieresis", 0x00CB},
+{"Edieresissmall", 0xF7EB},
+{"Edot", 0x0116},
+{"Edotaccent", 0x0116},
+{"Edotbelow", 0x1EB8},
+{"Efcyrillic", 0x0424},
+{"Egrave", 0x00C8},
+{"Egravesmall", 0xF7E8},
+{"Eharmenian", 0x0537},
+{"Ehookabove", 0x1EBA},
+{"Eightroman", 0x2167},
+{"Einvertedbreve", 0x0206},
+{"Eiotifiedcyrillic", 0x0464},
+{"Elcyrillic", 0x041B},
+{"Elevenroman", 0x216A},
+{"Emacron", 0x0112},
+{"Emacronacute", 0x1E16},
+{"Emacrongrave", 0x1E14},
+{"Emcyrillic", 0x041C},
+{"Emonospace", 0xFF25},
+{"Encyrillic", 0x041D},
+{"Endescendercyrillic", 0x04A2},
+{"Eng", 0x014A},
+{"Enghecyrillic", 0x04A4},
+{"Enhookcyrillic", 0x04C7},
+{"Eogonek", 0x0118},
+{"Eopen", 0x0190},
+{"Epsilon", 0x0395},
+{"Epsilontonos", 0x0388},
+{"Ercyrillic", 0x0420},
+{"Ereversed", 0x018E},
+{"Ereversedcyrillic", 0x042D},
+{"Escyrillic", 0x0421},
+{"Esdescendercyrillic", 0x04AA},
+{"Esh", 0x01A9},
+{"Esmall", 0xF765},
+{"Eta", 0x0397},
+{"Etarmenian", 0x0538},
+{"Etatonos", 0x0389},
+{"Eth", 0x00D0},
+{"Ethsmall", 0xF7F0},
+{"Etilde", 0x1EBC},
+{"Etildebelow", 0x1E1A},
+{"Euro", 0x20AC},
+{"Ezh", 0x01B7},
+{"Ezhcaron", 0x01EE},
+{"Ezhreversed", 0x01B8},
+{"F", 0x0046},
+{"Fcircle", 0x24BB},
+{"Fdotaccent", 0x1E1E},
+{"Feharmenian", 0x0556},
+{"Feicoptic", 0x03E4},
+{"Fhook", 0x0191},
+{"Fitacyrillic", 0x0472},
+{"Fiveroman", 0x2164},
+{"Fmonospace", 0xFF26},
+{"Fourroman", 0x2163},
+{"Fsmall", 0xF766},
+{"G", 0x0047},
+{"GBsquare", 0x3387},
+{"Gacute", 0x01F4},
+{"Gamma", 0x0393},
+{"Gammaafrican", 0x0194},
+{"Gangiacoptic", 0x03EA},
+{"Gbreve", 0x011E},
+{"Gcaron", 0x01E6},
+{"Gcedilla", 0x0122},
+{"Gcircle", 0x24BC},
+{"Gcircumflex", 0x011C},
+{"Gcommaaccent", 0x0122},
+{"Gdot", 0x0120},
+{"Gdotaccent", 0x0120},
+{"Gecyrillic", 0x0413},
+{"Ghadarmenian", 0x0542},
+{"Ghemiddlehookcyrillic", 0x0494},
+{"Ghestrokecyrillic", 0x0492},
+{"Gheupturncyrillic", 0x0490},
+{"Ghook", 0x0193},
+{"Gimarmenian", 0x0533},
+{"Gjecyrillic", 0x0403},
+{"Gmacron", 0x1E20},
+{"Gmonospace", 0xFF27},
+{"Grave", 0xF6CE},
+{"Gravesmall", 0xF760},
+{"Gsmall", 0xF767},
+{"Gsmallhook", 0x029B},
+{"Gstroke", 0x01E4},
+{"H", 0x0048},
+{"H18533", 0x25CF},
+{"H18543", 0x25AA},
+{"H18551", 0x25AB},
+{"H22073", 0x25A1},
+{"HPsquare", 0x33CB},
+{"Haabkhasiancyrillic", 0x04A8},
+{"Hadescendercyrillic", 0x04B2},
+{"Hardsigncyrillic", 0x042A},
+{"Hbar", 0x0126},
+{"Hbrevebelow", 0x1E2A},
+{"Hcedilla", 0x1E28},
+{"Hcircle", 0x24BD},
+{"Hcircumflex", 0x0124},
+{"Hdieresis", 0x1E26},
+{"Hdotaccent", 0x1E22},
+{"Hdotbelow", 0x1E24},
+{"Hmonospace", 0xFF28},
+{"Hoarmenian", 0x0540},
+{"Horicoptic", 0x03E8},
+{"Hsmall", 0xF768},
+{"Hungarumlaut", 0xF6CF},
+{"Hungarumlautsmall", 0xF6F8},
+{"Hzsquare", 0x3390},
+{"I", 0x0049},
+{"IAcyrillic", 0x042F},
+{"IJ", 0x0132},
+{"IUcyrillic", 0x042E},
+{"Iacute", 0x00CD},
+{"Iacutesmall", 0xF7ED},
+{"Ibreve", 0x012C},
+{"Icaron", 0x01CF},
+{"Icircle", 0x24BE},
+{"Icircumflex", 0x00CE},
+{"Icircumflexsmall", 0xF7EE},
+{"Icyrillic", 0x0406},
+{"Idblgrave", 0x0208},
+{"Idieresis", 0x00CF},
+{"Idieresisacute", 0x1E2E},
+{"Idieresiscyrillic", 0x04E4},
+{"Idieresissmall", 0xF7EF},
+{"Idot", 0x0130},
+{"Idotaccent", 0x0130},
+{"Idotbelow", 0x1ECA},
+{"Iebrevecyrillic", 0x04D6},
+{"Iecyrillic", 0x0415},
+{"Ifraktur", 0x2111},
+{"Igrave", 0x00CC},
+{"Igravesmall", 0xF7EC},
+{"Ihookabove", 0x1EC8},
+{"Iicyrillic", 0x0418},
+{"Iinvertedbreve", 0x020A},
+{"Iishortcyrillic", 0x0419},
+{"Imacron", 0x012A},
+{"Imacroncyrillic", 0x04E2},
+{"Imonospace", 0xFF29},
+{"Iniarmenian", 0x053B},
+{"Iocyrillic", 0x0401},
+{"Iogonek", 0x012E},
+{"Iota", 0x0399},
+{"Iotaafrican", 0x0196},
+{"Iotadieresis", 0x03AA},
+{"Iotatonos", 0x038A},
+{"Ismall", 0xF769},
+{"Istroke", 0x0197},
+{"Itilde", 0x0128},
+{"Itildebelow", 0x1E2C},
+{"Izhitsacyrillic", 0x0474},
+{"Izhitsadblgravecyrillic", 0x0476},
+{"J", 0x004A},
+{"Jaarmenian", 0x0541},
+{"Jcircle", 0x24BF},
+{"Jcircumflex", 0x0134},
+{"Jecyrillic", 0x0408},
+{"Jheharmenian", 0x054B},
+{"Jmonospace", 0xFF2A},
+{"Jsmall", 0xF76A},
+{"K", 0x004B},
+{"KBsquare", 0x3385},
+{"KKsquare", 0x33CD},
+{"Kabashkircyrillic", 0x04A0},
+{"Kacute", 0x1E30},
+{"Kacyrillic", 0x041A},
+{"Kadescendercyrillic", 0x049A},
+{"Kahookcyrillic", 0x04C3},
+{"Kappa", 0x039A},
+{"Kastrokecyrillic", 0x049E},
+{"Kaverticalstrokecyrillic", 0x049C},
+{"Kcaron", 0x01E8},
+{"Kcedilla", 0x0136},
+{"Kcircle", 0x24C0},
+{"Kcommaaccent", 0x0136},
+{"Kdotbelow", 0x1E32},
+{"Keharmenian", 0x0554},
+{"Kenarmenian", 0x053F},
+{"Khacyrillic", 0x0425},
+{"Kheicoptic", 0x03E6},
+{"Khook", 0x0198},
+{"Kjecyrillic", 0x040C},
+{"Klinebelow", 0x1E34},
+{"Kmonospace", 0xFF2B},
+{"Koppacyrillic", 0x0480},
+{"Koppagreek", 0x03DE},
+{"Ksicyrillic", 0x046E},
+{"Ksmall", 0xF76B},
+{"L", 0x004C},
+{"LJ", 0x01C7},
+{"LL", 0xF6BF},
+{"Lacute", 0x0139},
+{"Lambda", 0x039B},
+{"Lcaron", 0x013D},
+{"Lcedilla", 0x013B},
+{"Lcircle", 0x24C1},
+{"Lcircumflexbelow", 0x1E3C},
+{"Lcommaaccent", 0x013B},
+{"Ldot", 0x013F},
+{"Ldotaccent", 0x013F},
+{"Ldotbelow", 0x1E36},
+{"Ldotbelowmacron", 0x1E38},
+{"Liwnarmenian", 0x053C},
+{"Lj", 0x01C8},
+{"Ljecyrillic", 0x0409},
+{"Llinebelow", 0x1E3A},
+{"Lmonospace", 0xFF2C},
+{"Lslash", 0x0141},
+{"Lslashsmall", 0xF6F9},
+{"Lsmall", 0xF76C},
+{"M", 0x004D},
+{"MBsquare", 0x3386},
+{"Macron", 0xF6D0},
+{"Macronsmall", 0xF7AF},
+{"Macute", 0x1E3E},
+{"Mcircle", 0x24C2},
+{"Mdotaccent", 0x1E40},
+{"Mdotbelow", 0x1E42},
+{"Menarmenian", 0x0544},
+{"Mmonospace", 0xFF2D},
+{"Msmall", 0xF76D},
+{"Mturned", 0x019C},
+{"Mu", 0x039C},
+{"N", 0x004E},
+{"NJ", 0x01CA},
+{"Nacute", 0x0143},
+{"Ncaron", 0x0147},
+{"Ncedilla", 0x0145},
+{"Ncircle", 0x24C3},
+{"Ncircumflexbelow", 0x1E4A},
+{"Ncommaaccent", 0x0145},
+{"Ndotaccent", 0x1E44},
+{"Ndotbelow", 0x1E46},
+{"Nhookleft", 0x019D},
+{"Nineroman", 0x2168},
+{"Nj", 0x01CB},
+{"Njecyrillic", 0x040A},
+{"Nlinebelow", 0x1E48},
+{"Nmonospace", 0xFF2E},
+{"Nowarmenian", 0x0546},
+{"Nsmall", 0xF76E},
+{"Ntilde", 0x00D1},
+{"Ntildesmall", 0xF7F1},
+{"Nu", 0x039D},
+{"O", 0x004F},
+{"OE", 0x0152},
+{"OEsmall", 0xF6FA},
+{"Oacute", 0x00D3},
+{"Oacutesmall", 0xF7F3},
+{"Obarredcyrillic", 0x04E8},
+{"Obarreddieresiscyrillic", 0x04EA},
+{"Obreve", 0x014E},
+{"Ocaron", 0x01D1},
+{"Ocenteredtilde", 0x019F},
+{"Ocircle", 0x24C4},
+{"Ocircumflex", 0x00D4},
+{"Ocircumflexacute", 0x1ED0},
+{"Ocircumflexdotbelow", 0x1ED8},
+{"Ocircumflexgrave", 0x1ED2},
+{"Ocircumflexhookabove", 0x1ED4},
+{"Ocircumflexsmall", 0xF7F4},
+{"Ocircumflextilde", 0x1ED6},
+{"Ocyrillic", 0x041E},
+{"Odblacute", 0x0150},
+{"Odblgrave", 0x020C},
+{"Odieresis", 0x00D6},
+{"Odieresiscyrillic", 0x04E6},
+{"Odieresissmall", 0xF7F6},
+{"Odotbelow", 0x1ECC},
+{"Ogoneksmall", 0xF6FB},
+{"Ograve", 0x00D2},
+{"Ogravesmall", 0xF7F2},
+{"Oharmenian", 0x0555},
+{"Ohm", 0x2126},
+{"Ohookabove", 0x1ECE},
+{"Ohorn", 0x01A0},
+{"Ohornacute", 0x1EDA},
+{"Ohorndotbelow", 0x1EE2},
+{"Ohorngrave", 0x1EDC},
+{"Ohornhookabove", 0x1EDE},
+{"Ohorntilde", 0x1EE0},
+{"Ohungarumlaut", 0x0150},
+{"Oi", 0x01A2},
+{"Oinvertedbreve", 0x020E},
+{"Omacron", 0x014C},
+{"Omacronacute", 0x1E52},
+{"Omacrongrave", 0x1E50},
+{"Omega", 0x2126},
+{"Omegacyrillic", 0x0460},
+{"Omegagreek", 0x03A9},
+{"Omegaroundcyrillic", 0x047A},
+{"Omegatitlocyrillic", 0x047C},
+{"Omegatonos", 0x038F},
+{"Omicron", 0x039F},
+{"Omicrontonos", 0x038C},
+{"Omonospace", 0xFF2F},
+{"Oneroman", 0x2160},
+{"Oogonek", 0x01EA},
+{"Oogonekmacron", 0x01EC},
+{"Oopen", 0x0186},
+{"Oslash", 0x00D8},
+{"Oslashacute", 0x01FE},
+{"Oslashsmall", 0xF7F8},
+{"Osmall", 0xF76F},
+{"Ostrokeacute", 0x01FE},
+{"Otcyrillic", 0x047E},
+{"Otilde", 0x00D5},
+{"Otildeacute", 0x1E4C},
+{"Otildedieresis", 0x1E4E},
+{"Otildesmall", 0xF7F5},
+{"P", 0x0050},
+{"Pacute", 0x1E54},
+{"Pcircle", 0x24C5},
+{"Pdotaccent", 0x1E56},
+{"Pecyrillic", 0x041F},
+{"Peharmenian", 0x054A},
+{"Pemiddlehookcyrillic", 0x04A6},
+{"Phi", 0x03A6},
+{"Phook", 0x01A4},
+{"Pi", 0x03A0},
+{"Piwrarmenian", 0x0553},
+{"Pmonospace", 0xFF30},
+{"Psi", 0x03A8},
+{"Psicyrillic", 0x0470},
+{"Psmall", 0xF770},
+{"Q", 0x0051},
+{"Qcircle", 0x24C6},
+{"Qmonospace", 0xFF31},
+{"Qsmall", 0xF771},
+{"R", 0x0052},
+{"Raarmenian", 0x054C},
+{"Racute", 0x0154},
+{"Rcaron", 0x0158},
+{"Rcedilla", 0x0156},
+{"Rcircle", 0x24C7},
+{"Rcommaaccent", 0x0156},
+{"Rdblgrave", 0x0210},
+{"Rdotaccent", 0x1E58},
+{"Rdotbelow", 0x1E5A},
+{"Rdotbelowmacron", 0x1E5C},
+{"Reharmenian", 0x0550},
+{"Rfraktur", 0x211C},
+{"Rho", 0x03A1},
+{"Ringsmall", 0xF6FC},
+{"Rinvertedbreve", 0x0212},
+{"Rlinebelow", 0x1E5E},
+{"Rmonospace", 0xFF32},
+{"Rsmall", 0xF772},
+{"Rsmallinverted", 0x0281},
+{"Rsmallinvertedsuperior", 0x02B6},
+{"S", 0x0053},
+{"SF010000", 0x250C},
+{"SF020000", 0x2514},
+{"SF030000", 0x2510},
+{"SF040000", 0x2518},
+{"SF050000", 0x253C},
+{"SF060000", 0x252C},
+{"SF070000", 0x2534},
+{"SF080000", 0x251C},
+{"SF090000", 0x2524},
+{"SF100000", 0x2500},
+{"SF110000", 0x2502},
+{"SF190000", 0x2561},
+{"SF200000", 0x2562},
+{"SF210000", 0x2556},
+{"SF220000", 0x2555},
+{"SF230000", 0x2563},
+{"SF240000", 0x2551},
+{"SF250000", 0x2557},
+{"SF260000", 0x255D},
+{"SF270000", 0x255C},
+{"SF280000", 0x255B},
+{"SF360000", 0x255E},
+{"SF370000", 0x255F},
+{"SF380000", 0x255A},
+{"SF390000", 0x2554},
+{"SF400000", 0x2569},
+{"SF410000", 0x2566},
+{"SF420000", 0x2560},
+{"SF430000", 0x2550},
+{"SF440000", 0x256C},
+{"SF450000", 0x2567},
+{"SF460000", 0x2568},
+{"SF470000", 0x2564},
+{"SF480000", 0x2565},
+{"SF490000", 0x2559},
+{"SF500000", 0x2558},
+{"SF510000", 0x2552},
+{"SF520000", 0x2553},
+{"SF530000", 0x256B},
+{"SF540000", 0x256A},
+{"Sacute", 0x015A},
+{"Sacutedotaccent", 0x1E64},
+{"Sampigreek", 0x03E0},
+{"Scaron", 0x0160},
+{"Scarondotaccent", 0x1E66},
+{"Scaronsmall", 0xF6FD},
+{"Scedilla", 0x015E},
+{"Schwa", 0x018F},
+{"Schwacyrillic", 0x04D8},
+{"Schwadieresiscyrillic", 0x04DA},
+{"Scircle", 0x24C8},
+{"Scircumflex", 0x015C},
+{"Scommaaccent", 0x0218},
+{"Sdotaccent", 0x1E60},
+{"Sdotbelow", 0x1E62},
+{"Sdotbelowdotaccent", 0x1E68},
+{"Seharmenian", 0x054D},
+{"Sevenroman", 0x2166},
+{"Shaarmenian", 0x0547},
+{"Shacyrillic", 0x0428},
+{"Shchacyrillic", 0x0429},
+{"Sheicoptic", 0x03E2},
+{"Shhacyrillic", 0x04BA},
+{"Shimacoptic", 0x03EC},
+{"Sigma", 0x03A3},
+{"Sixroman", 0x2165},
+{"Smonospace", 0xFF33},
+{"Softsigncyrillic", 0x042C},
+{"Ssmall", 0xF773},
+{"Stigmagreek", 0x03DA},
+{"T", 0x0054},
+{"Tau", 0x03A4},
+{"Tbar", 0x0166},
+{"Tcaron", 0x0164},
+{"Tcedilla", 0x0162},
+{"Tcircle", 0x24C9},
+{"Tcircumflexbelow", 0x1E70},
+{"Tcommaaccent", 0x0162},
+{"Tdotaccent", 0x1E6A},
+{"Tdotbelow", 0x1E6C},
+{"Tecyrillic", 0x0422},
+{"Tedescendercyrillic", 0x04AC},
+{"Tenroman", 0x2169},
+{"Tetsecyrillic", 0x04B4},
+{"Theta", 0x0398},
+{"Thook", 0x01AC},
+{"Thorn", 0x00DE},
+{"Thornsmall", 0xF7FE},
+{"Threeroman", 0x2162},
+{"Tildesmall", 0xF6FE},
+{"Tiwnarmenian", 0x054F},
+{"Tlinebelow", 0x1E6E},
+{"Tmonospace", 0xFF34},
+{"Toarmenian", 0x0539},
+{"Tonefive", 0x01BC},
+{"Tonesix", 0x0184},
+{"Tonetwo", 0x01A7},
+{"Tretroflexhook", 0x01AE},
+{"Tsecyrillic", 0x0426},
+{"Tshecyrillic", 0x040B},
+{"Tsmall", 0xF774},
+{"Twelveroman", 0x216B},
+{"Tworoman", 0x2161},
+{"U", 0x0055},
+{"Uacute", 0x00DA},
+{"Uacutesmall", 0xF7FA},
+{"Ubreve", 0x016C},
+{"Ucaron", 0x01D3},
+{"Ucircle", 0x24CA},
+{"Ucircumflex", 0x00DB},
+{"Ucircumflexbelow", 0x1E76},
+{"Ucircumflexsmall", 0xF7FB},
+{"Ucyrillic", 0x0423},
+{"Udblacute", 0x0170},
+{"Udblgrave", 0x0214},
+{"Udieresis", 0x00DC},
+{"Udieresisacute", 0x01D7},
+{"Udieresisbelow", 0x1E72},
+{"Udieresiscaron", 0x01D9},
+{"Udieresiscyrillic", 0x04F0},
+{"Udieresisgrave", 0x01DB},
+{"Udieresismacron", 0x01D5},
+{"Udieresissmall", 0xF7FC},
+{"Udotbelow", 0x1EE4},
+{"Ugrave", 0x00D9},
+{"Ugravesmall", 0xF7F9},
+{"Uhookabove", 0x1EE6},
+{"Uhorn", 0x01AF},
+{"Uhornacute", 0x1EE8},
+{"Uhorndotbelow", 0x1EF0},
+{"Uhorngrave", 0x1EEA},
+{"Uhornhookabove", 0x1EEC},
+{"Uhorntilde", 0x1EEE},
+{"Uhungarumlaut", 0x0170},
+{"Uhungarumlautcyrillic", 0x04F2},
+{"Uinvertedbreve", 0x0216},
+{"Ukcyrillic", 0x0478},
+{"Umacron", 0x016A},
+{"Umacroncyrillic", 0x04EE},
+{"Umacrondieresis", 0x1E7A},
+{"Umonospace", 0xFF35},
+{"Uogonek", 0x0172},
+{"Upsilon", 0x03A5},
+{"Upsilon1", 0x03D2},
+{"Upsilonacutehooksymbolgreek", 0x03D3},
+{"Upsilonafrican", 0x01B1},
+{"Upsilondieresis", 0x03AB},
+{"Upsilondieresishooksymbolgreek", 0x03D4},
+{"Upsilonhooksymbol", 0x03D2},
+{"Upsilontonos", 0x038E},
+{"Uring", 0x016E},
+{"Ushortcyrillic", 0x040E},
+{"Usmall", 0xF775},
+{"Ustraightcyrillic", 0x04AE},
+{"Ustraightstrokecyrillic", 0x04B0},
+{"Utilde", 0x0168},
+{"Utildeacute", 0x1E78},
+{"Utildebelow", 0x1E74},
+{"V", 0x0056},
+{"Vcircle", 0x24CB},
+{"Vdotbelow", 0x1E7E},
+{"Vecyrillic", 0x0412},
+{"Vewarmenian", 0x054E},
+{"Vhook", 0x01B2},
+{"Vmonospace", 0xFF36},
+{"Voarmenian", 0x0548},
+{"Vsmall", 0xF776},
+{"Vtilde", 0x1E7C},
+{"W", 0x0057},
+{"Wacute", 0x1E82},
+{"Wcircle", 0x24CC},
+{"Wcircumflex", 0x0174},
+{"Wdieresis", 0x1E84},
+{"Wdotaccent", 0x1E86},
+{"Wdotbelow", 0x1E88},
+{"Wgrave", 0x1E80},
+{"Wmonospace", 0xFF37},
+{"Wsmall", 0xF777},
+{"X", 0x0058},
+{"Xcircle", 0x24CD},
+{"Xdieresis", 0x1E8C},
+{"Xdotaccent", 0x1E8A},
+{"Xeharmenian", 0x053D},
+{"Xi", 0x039E},
+{"Xmonospace", 0xFF38},
+{"Xsmall", 0xF778},
+{"Y", 0x0059},
+{"Yacute", 0x00DD},
+{"Yacutesmall", 0xF7FD},
+{"Yatcyrillic", 0x0462},
+{"Ycircle", 0x24CE},
+{"Ycircumflex", 0x0176},
+{"Ydieresis", 0x0178},
+{"Ydieresissmall", 0xF7FF},
+{"Ydotaccent", 0x1E8E},
+{"Ydotbelow", 0x1EF4},
+{"Yericyrillic", 0x042B},
+{"Yerudieresiscyrillic", 0x04F8},
+{"Ygrave", 0x1EF2},
+{"Yhook", 0x01B3},
+{"Yhookabove", 0x1EF6},
+{"Yiarmenian", 0x0545},
+{"Yicyrillic", 0x0407},
+{"Yiwnarmenian", 0x0552},
+{"Ymonospace", 0xFF39},
+{"Ysmall", 0xF779},
+{"Ytilde", 0x1EF8},
+{"Yusbigcyrillic", 0x046A},
+{"Yusbigiotifiedcyrillic", 0x046C},
+{"Yuslittlecyrillic", 0x0466},
+{"Yuslittleiotifiedcyrillic", 0x0468},
+{"Z", 0x005A},
+{"Zaarmenian", 0x0536},
+{"Zacute", 0x0179},
+{"Zcaron", 0x017D},
+{"Zcaronsmall", 0xF6FF},
+{"Zcircle", 0x24CF},
+{"Zcircumflex", 0x1E90},
+{"Zdot", 0x017B},
+{"Zdotaccent", 0x017B},
+{"Zdotbelow", 0x1E92},
+{"Zecyrillic", 0x0417},
+{"Zedescendercyrillic", 0x0498},
+{"Zedieresiscyrillic", 0x04DE},
+{"Zeta", 0x0396},
+{"Zhearmenian", 0x053A},
+{"Zhebrevecyrillic", 0x04C1},
+{"Zhecyrillic", 0x0416},
+{"Zhedescendercyrillic", 0x0496},
+{"Zhedieresiscyrillic", 0x04DC},
+{"Zlinebelow", 0x1E94},
+{"Zmonospace", 0xFF3A},
+{"Zsmall", 0xF77A},
+{"Zstroke", 0x01B5},
+{"a", 0x0061},
+{"aabengali", 0x0986},
+{"aacute", 0x00E1},
+{"aadeva", 0x0906},
+{"aagujarati", 0x0A86},
+{"aagurmukhi", 0x0A06},
+{"aamatragurmukhi", 0x0A3E},
+{"aarusquare", 0x3303},
+{"aavowelsignbengali", 0x09BE},
+{"aavowelsigndeva", 0x093E},
+{"aavowelsigngujarati", 0x0ABE},
+{"abbreviationmarkarmenian", 0x055F},
+{"abbreviationsigndeva", 0x0970},
+{"abengali", 0x0985},
+{"abopomofo", 0x311A},
+{"abreve", 0x0103},
+{"abreveacute", 0x1EAF},
+{"abrevecyrillic", 0x04D1},
+{"abrevedotbelow", 0x1EB7},
+{"abrevegrave", 0x1EB1},
+{"abrevehookabove", 0x1EB3},
+{"abrevetilde", 0x1EB5},
+{"acaron", 0x01CE},
+{"acircle", 0x24D0},
+{"acircumflex", 0x00E2},
+{"acircumflexacute", 0x1EA5},
+{"acircumflexdotbelow", 0x1EAD},
+{"acircumflexgrave", 0x1EA7},
+{"acircumflexhookabove", 0x1EA9},
+{"acircumflextilde", 0x1EAB},
+{"acute", 0x00B4},
+{"acutebelowcmb", 0x0317},
+{"acutecmb", 0x0301},
+{"acutecomb", 0x0301},
+{"acutedeva", 0x0954},
+{"acutelowmod", 0x02CF},
+{"acutetonecmb", 0x0341},
+{"acyrillic", 0x0430},
+{"adblgrave", 0x0201},
+{"addakgurmukhi", 0x0A71},
+{"adeva", 0x0905},
+{"adieresis", 0x00E4},
+{"adieresiscyrillic", 0x04D3},
+{"adieresismacron", 0x01DF},
+{"adotbelow", 0x1EA1},
+{"adotmacron", 0x01E1},
+{"ae", 0x00E6},
+{"aeacute", 0x01FD},
+{"aekorean", 0x3150},
+{"aemacron", 0x01E3},
+{"afii00208", 0x2015},
+{"afii08941", 0x20A4},
+{"afii10017", 0x0410},
+{"afii10018", 0x0411},
+{"afii10019", 0x0412},
+{"afii10020", 0x0413},
+{"afii10021", 0x0414},
+{"afii10022", 0x0415},
+{"afii10023", 0x0401},
+{"afii10024", 0x0416},
+{"afii10025", 0x0417},
+{"afii10026", 0x0418},
+{"afii10027", 0x0419},
+{"afii10028", 0x041A},
+{"afii10029", 0x041B},
+{"afii10030", 0x041C},
+{"afii10031", 0x041D},
+{"afii10032", 0x041E},
+{"afii10033", 0x041F},
+{"afii10034", 0x0420},
+{"afii10035", 0x0421},
+{"afii10036", 0x0422},
+{"afii10037", 0x0423},
+{"afii10038", 0x0424},
+{"afii10039", 0x0425},
+{"afii10040", 0x0426},
+{"afii10041", 0x0427},
+{"afii10042", 0x0428},
+{"afii10043", 0x0429},
+{"afii10044", 0x042A},
+{"afii10045", 0x042B},
+{"afii10046", 0x042C},
+{"afii10047", 0x042D},
+{"afii10048", 0x042E},
+{"afii10049", 0x042F},
+{"afii10050", 0x0490},
+{"afii10051", 0x0402},
+{"afii10052", 0x0403},
+{"afii10053", 0x0404},
+{"afii10054", 0x0405},
+{"afii10055", 0x0406},
+{"afii10056", 0x0407},
+{"afii10057", 0x0408},
+{"afii10058", 0x0409},
+{"afii10059", 0x040A},
+{"afii10060", 0x040B},
+{"afii10061", 0x040C},
+{"afii10062", 0x040E},
+{"afii10063", 0xF6C4},
+{"afii10064", 0xF6C5},
+{"afii10065", 0x0430},
+{"afii10066", 0x0431},
+{"afii10067", 0x0432},
+{"afii10068", 0x0433},
+{"afii10069", 0x0434},
+{"afii10070", 0x0435},
+{"afii10071", 0x0451},
+{"afii10072", 0x0436},
+{"afii10073", 0x0437},
+{"afii10074", 0x0438},
+{"afii10075", 0x0439},
+{"afii10076", 0x043A},
+{"afii10077", 0x043B},
+{"afii10078", 0x043C},
+{"afii10079", 0x043D},
+{"afii10080", 0x043E},
+{"afii10081", 0x043F},
+{"afii10082", 0x0440},
+{"afii10083", 0x0441},
+{"afii10084", 0x0442},
+{"afii10085", 0x0443},
+{"afii10086", 0x0444},
+{"afii10087", 0x0445},
+{"afii10088", 0x0446},
+{"afii10089", 0x0447},
+{"afii10090", 0x0448},
+{"afii10091", 0x0449},
+{"afii10092", 0x044A},
+{"afii10093", 0x044B},
+{"afii10094", 0x044C},
+{"afii10095", 0x044D},
+{"afii10096", 0x044E},
+{"afii10097", 0x044F},
+{"afii10098", 0x0491},
+{"afii10099", 0x0452},
+{"afii10100", 0x0453},
+{"afii10101", 0x0454},
+{"afii10102", 0x0455},
+{"afii10103", 0x0456},
+{"afii10104", 0x0457},
+{"afii10105", 0x0458},
+{"afii10106", 0x0459},
+{"afii10107", 0x045A},
+{"afii10108", 0x045B},
+{"afii10109", 0x045C},
+{"afii10110", 0x045E},
+{"afii10145", 0x040F},
+{"afii10146", 0x0462},
+{"afii10147", 0x0472},
+{"afii10148", 0x0474},
+{"afii10192", 0xF6C6},
+{"afii10193", 0x045F},
+{"afii10194", 0x0463},
+{"afii10195", 0x0473},
+{"afii10196", 0x0475},
+{"afii10831", 0xF6C7},
+{"afii10832", 0xF6C8},
+{"afii10846", 0x04D9},
+{"afii299", 0x200E},
+{"afii300", 0x200F},
+{"afii301", 0x200D},
+{"afii57381", 0x066A},
+{"afii57388", 0x060C},
+{"afii57392", 0x0660},
+{"afii57393", 0x0661},
+{"afii57394", 0x0662},
+{"afii57395", 0x0663},
+{"afii57396", 0x0664},
+{"afii57397", 0x0665},
+{"afii57398", 0x0666},
+{"afii57399", 0x0667},
+{"afii57400", 0x0668},
+{"afii57401", 0x0669},
+{"afii57403", 0x061B},
+{"afii57407", 0x061F},
+{"afii57409", 0x0621},
+{"afii57410", 0x0622},
+{"afii57411", 0x0623},
+{"afii57412", 0x0624},
+{"afii57413", 0x0625},
+{"afii57414", 0x0626},
+{"afii57415", 0x0627},
+{"afii57416", 0x0628},
+{"afii57417", 0x0629},
+{"afii57418", 0x062A},
+{"afii57419", 0x062B},
+{"afii57420", 0x062C},
+{"afii57421", 0x062D},
+{"afii57422", 0x062E},
+{"afii57423", 0x062F},
+{"afii57424", 0x0630},
+{"afii57425", 0x0631},
+{"afii57426", 0x0632},
+{"afii57427", 0x0633},
+{"afii57428", 0x0634},
+{"afii57429", 0x0635},
+{"afii57430", 0x0636},
+{"afii57431", 0x0637},
+{"afii57432", 0x0638},
+{"afii57433", 0x0639},
+{"afii57434", 0x063A},
+{"afii57440", 0x0640},
+{"afii57441", 0x0641},
+{"afii57442", 0x0642},
+{"afii57443", 0x0643},
+{"afii57444", 0x0644},
+{"afii57445", 0x0645},
+{"afii57446", 0x0646},
+{"afii57448", 0x0648},
+{"afii57449", 0x0649},
+{"afii57450", 0x064A},
+{"afii57451", 0x064B},
+{"afii57452", 0x064C},
+{"afii57453", 0x064D},
+{"afii57454", 0x064E},
+{"afii57455", 0x064F},
+{"afii57456", 0x0650},
+{"afii57457", 0x0651},
+{"afii57458", 0x0652},
+{"afii57470", 0x0647},
+{"afii57505", 0x06A4},
+{"afii57506", 0x067E},
+{"afii57507", 0x0686},
+{"afii57508", 0x0698},
+{"afii57509", 0x06AF},
+{"afii57511", 0x0679},
+{"afii57512", 0x0688},
+{"afii57513", 0x0691},
+{"afii57514", 0x06BA},
+{"afii57519", 0x06D2},
+{"afii57534", 0x06D5},
+{"afii57636", 0x20AA},
+{"afii57645", 0x05BE},
+{"afii57658", 0x05C3},
+{"afii57664", 0x05D0},
+{"afii57665", 0x05D1},
+{"afii57666", 0x05D2},
+{"afii57667", 0x05D3},
+{"afii57668", 0x05D4},
+{"afii57669", 0x05D5},
+{"afii57670", 0x05D6},
+{"afii57671", 0x05D7},
+{"afii57672", 0x05D8},
+{"afii57673", 0x05D9},
+{"afii57674", 0x05DA},
+{"afii57675", 0x05DB},
+{"afii57676", 0x05DC},
+{"afii57677", 0x05DD},
+{"afii57678", 0x05DE},
+{"afii57679", 0x05DF},
+{"afii57680", 0x05E0},
+{"afii57681", 0x05E1},
+{"afii57682", 0x05E2},
+{"afii57683", 0x05E3},
+{"afii57684", 0x05E4},
+{"afii57685", 0x05E5},
+{"afii57686", 0x05E6},
+{"afii57687", 0x05E7},
+{"afii57688", 0x05E8},
+{"afii57689", 0x05E9},
+{"afii57690", 0x05EA},
+{"afii57694", 0xFB2A},
+{"afii57695", 0xFB2B},
+{"afii57700", 0xFB4B},
+{"afii57705", 0xFB1F},
+{"afii57716", 0x05F0},
+{"afii57717", 0x05F1},
+{"afii57718", 0x05F2},
+{"afii57723", 0xFB35},
+{"afii57793", 0x05B4},
+{"afii57794", 0x05B5},
+{"afii57795", 0x05B6},
+{"afii57796", 0x05BB},
+{"afii57797", 0x05B8},
+{"afii57798", 0x05B7},
+{"afii57799", 0x05B0},
+{"afii57800", 0x05B2},
+{"afii57801", 0x05B1},
+{"afii57802", 0x05B3},
+{"afii57803", 0x05C2},
+{"afii57804", 0x05C1},
+{"afii57806", 0x05B9},
+{"afii57807", 0x05BC},
+{"afii57839", 0x05BD},
+{"afii57841", 0x05BF},
+{"afii57842", 0x05C0},
+{"afii57929", 0x02BC},
+{"afii61248", 0x2105},
+{"afii61289", 0x2113},
+{"afii61352", 0x2116},
+{"afii61573", 0x202C},
+{"afii61574", 0x202D},
+{"afii61575", 0x202E},
+{"afii61664", 0x200C},
+{"afii63167", 0x066D},
+{"afii64937", 0x02BD},
+{"agrave", 0x00E0},
+{"agujarati", 0x0A85},
+{"agurmukhi", 0x0A05},
+{"ahiragana", 0x3042},
+{"ahookabove", 0x1EA3},
+{"aibengali", 0x0990},
+{"aibopomofo", 0x311E},
+{"aideva", 0x0910},
+{"aiecyrillic", 0x04D5},
+{"aigujarati", 0x0A90},
+{"aigurmukhi", 0x0A10},
+{"aimatragurmukhi", 0x0A48},
+{"ainarabic", 0x0639},
+{"ainfinalarabic", 0xFECA},
+{"aininitialarabic", 0xFECB},
+{"ainmedialarabic", 0xFECC},
+{"ainvertedbreve", 0x0203},
+{"aivowelsignbengali", 0x09C8},
+{"aivowelsigndeva", 0x0948},
+{"aivowelsigngujarati", 0x0AC8},
+{"akatakana", 0x30A2},
+{"akatakanahalfwidth", 0xFF71},
+{"akorean", 0x314F},
+{"alef", 0x05D0},
+{"alefarabic", 0x0627},
+{"alefdageshhebrew", 0xFB30},
+{"aleffinalarabic", 0xFE8E},
+{"alefhamzaabovearabic", 0x0623},
+{"alefhamzaabovefinalarabic", 0xFE84},
+{"alefhamzabelowarabic", 0x0625},
+{"alefhamzabelowfinalarabic", 0xFE88},
+{"alefhebrew", 0x05D0},
+{"aleflamedhebrew", 0xFB4F},
+{"alefmaddaabovearabic", 0x0622},
+{"alefmaddaabovefinalarabic", 0xFE82},
+{"alefmaksuraarabic", 0x0649},
+{"alefmaksurafinalarabic", 0xFEF0},
+{"alefmaksurainitialarabic", 0xFEF3},
+{"alefmaksuramedialarabic", 0xFEF4},
+{"alefpatahhebrew", 0xFB2E},
+{"alefqamatshebrew", 0xFB2F},
+{"aleph", 0x2135},
+{"allequal", 0x224C},
+{"alpha", 0x03B1},
+{"alphatonos", 0x03AC},
+{"amacron", 0x0101},
+{"amonospace", 0xFF41},
+{"ampersand", 0x0026},
+{"ampersandmonospace", 0xFF06},
+{"ampersandsmall", 0xF726},
+{"amsquare", 0x33C2},
+{"anbopomofo", 0x3122},
+{"angbopomofo", 0x3124},
+{"angkhankhuthai", 0x0E5A},
+{"angle", 0x2220},
+{"anglebracketleft", 0x3008},
+{"anglebracketleftvertical", 0xFE3F},
+{"anglebracketright", 0x3009},
+{"anglebracketrightvertical", 0xFE40},
+{"angleleft", 0x2329},
+{"angleright", 0x232A},
+{"angstrom", 0x212B},
+{"anoteleia", 0x0387},
+{"anudattadeva", 0x0952},
+{"anusvarabengali", 0x0982},
+{"anusvaradeva", 0x0902},
+{"anusvaragujarati", 0x0A82},
+{"aogonek", 0x0105},
+{"apaatosquare", 0x3300},
+{"aparen", 0x249C},
+{"apostrophearmenian", 0x055A},
+{"apostrophemod", 0x02BC},
+{"apple", 0xF8FF},
+{"approaches", 0x2250},
+{"approxequal", 0x2248},
+{"approxequalorimage", 0x2252},
+{"approximatelyequal", 0x2245},
+{"araeaekorean", 0x318E},
+{"araeakorean", 0x318D},
+{"arc", 0x2312},
+{"arighthalfring", 0x1E9A},
+{"aring", 0x00E5},
+{"aringacute", 0x01FB},
+{"aringbelow", 0x1E01},
+{"arrowboth", 0x2194},
+{"arrowdashdown", 0x21E3},
+{"arrowdashleft", 0x21E0},
+{"arrowdashright", 0x21E2},
+{"arrowdashup", 0x21E1},
+{"arrowdblboth", 0x21D4},
+{"arrowdbldown", 0x21D3},
+{"arrowdblleft", 0x21D0},
+{"arrowdblright", 0x21D2},
+{"arrowdblup", 0x21D1},
+{"arrowdown", 0x2193},
+{"arrowdownleft", 0x2199},
+{"arrowdownright", 0x2198},
+{"arrowdownwhite", 0x21E9},
+{"arrowheaddownmod", 0x02C5},
+{"arrowheadleftmod", 0x02C2},
+{"arrowheadrightmod", 0x02C3},
+{"arrowheadupmod", 0x02C4},
+{"arrowhorizex", 0xF8E7},
+{"arrowleft", 0x2190},
+{"arrowleftdbl", 0x21D0},
+{"arrowleftdblstroke", 0x21CD},
+{"arrowleftoverright", 0x21C6},
+{"arrowleftwhite", 0x21E6},
+{"arrowright", 0x2192},
+{"arrowrightdblstroke", 0x21CF},
+{"arrowrightheavy", 0x279E},
+{"arrowrightoverleft", 0x21C4},
+{"arrowrightwhite", 0x21E8},
+{"arrowtableft", 0x21E4},
+{"arrowtabright", 0x21E5},
+{"arrowup", 0x2191},
+{"arrowupdn", 0x2195},
+{"arrowupdnbse", 0x21A8},
+{"arrowupdownbase", 0x21A8},
+{"arrowupleft", 0x2196},
+{"arrowupleftofdown", 0x21C5},
+{"arrowupright", 0x2197},
+{"arrowupwhite", 0x21E7},
+{"arrowvertex", 0xF8E6},
+{"asciicircum", 0x005E},
+{"asciicircummonospace", 0xFF3E},
+{"asciitilde", 0x007E},
+{"asciitildemonospace", 0xFF5E},
+{"ascript", 0x0251},
+{"ascriptturned", 0x0252},
+{"asmallhiragana", 0x3041},
+{"asmallkatakana", 0x30A1},
+{"asmallkatakanahalfwidth", 0xFF67},
+{"asterisk", 0x002A},
+{"asteriskaltonearabic", 0x066D},
+{"asteriskarabic", 0x066D},
+{"asteriskmath", 0x2217},
+{"asteriskmonospace", 0xFF0A},
+{"asterisksmall", 0xFE61},
+{"asterism", 0x2042},
+{"asuperior", 0xF6E9},
+{"asymptoticallyequal", 0x2243},
+{"at", 0x0040},
+{"atilde", 0x00E3},
+{"atmonospace", 0xFF20},
+{"atsmall", 0xFE6B},
+{"aturned", 0x0250},
+{"aubengali", 0x0994},
+{"aubopomofo", 0x3120},
+{"audeva", 0x0914},
+{"augujarati", 0x0A94},
+{"augurmukhi", 0x0A14},
+{"aulengthmarkbengali", 0x09D7},
+{"aumatragurmukhi", 0x0A4C},
+{"auvowelsignbengali", 0x09CC},
+{"auvowelsigndeva", 0x094C},
+{"auvowelsigngujarati", 0x0ACC},
+{"avagrahadeva", 0x093D},
+{"aybarmenian", 0x0561},
+{"ayin", 0x05E2},
+{"ayinaltonehebrew", 0xFB20},
+{"ayinhebrew", 0x05E2},
+{"b", 0x0062},
+{"babengali", 0x09AC},
+{"backslash", 0x005C},
+{"backslashmonospace", 0xFF3C},
+{"badeva", 0x092C},
+{"bagujarati", 0x0AAC},
+{"bagurmukhi", 0x0A2C},
+{"bahiragana", 0x3070},
+{"bahtthai", 0x0E3F},
+{"bakatakana", 0x30D0},
+{"bar", 0x007C},
+{"barmonospace", 0xFF5C},
+{"bbopomofo", 0x3105},
+{"bcircle", 0x24D1},
+{"bdotaccent", 0x1E03},
+{"bdotbelow", 0x1E05},
+{"beamedsixteenthnotes", 0x266C},
+{"because", 0x2235},
+{"becyrillic", 0x0431},
+{"beharabic", 0x0628},
+{"behfinalarabic", 0xFE90},
+{"behinitialarabic", 0xFE91},
+{"behiragana", 0x3079},
+{"behmedialarabic", 0xFE92},
+{"behmeeminitialarabic", 0xFC9F},
+{"behmeemisolatedarabic", 0xFC08},
+{"behnoonfinalarabic", 0xFC6D},
+{"bekatakana", 0x30D9},
+{"benarmenian", 0x0562},
+{"bet", 0x05D1},
+{"beta", 0x03B2},
+{"betasymbolgreek", 0x03D0},
+{"betdagesh", 0xFB31},
+{"betdageshhebrew", 0xFB31},
+{"bethebrew", 0x05D1},
+{"betrafehebrew", 0xFB4C},
+{"bhabengali", 0x09AD},
+{"bhadeva", 0x092D},
+{"bhagujarati", 0x0AAD},
+{"bhagurmukhi", 0x0A2D},
+{"bhook", 0x0253},
+{"bihiragana", 0x3073},
+{"bikatakana", 0x30D3},
+{"bilabialclick", 0x0298},
+{"bindigurmukhi", 0x0A02},
+{"birusquare", 0x3331},
+{"blackcircle", 0x25CF},
+{"blackdiamond", 0x25C6},
+{"blackdownpointingtriangle", 0x25BC},
+{"blackleftpointingpointer", 0x25C4},
+{"blackleftpointingtriangle", 0x25C0},
+{"blacklenticularbracketleft", 0x3010},
+{"blacklenticularbracketleftvertical", 0xFE3B},
+{"blacklenticularbracketright", 0x3011},
+{"blacklenticularbracketrightvertical", 0xFE3C},
+{"blacklowerlefttriangle", 0x25E3},
+{"blacklowerrighttriangle", 0x25E2},
+{"blackrectangle", 0x25AC},
+{"blackrightpointingpointer", 0x25BA},
+{"blackrightpointingtriangle", 0x25B6},
+{"blacksmallsquare", 0x25AA},
+{"blacksmilingface", 0x263B},
+{"blacksquare", 0x25A0},
+{"blackstar", 0x2605},
+{"blackupperlefttriangle", 0x25E4},
+{"blackupperrighttriangle", 0x25E5},
+{"blackuppointingsmalltriangle", 0x25B4},
+{"blackuppointingtriangle", 0x25B2},
+{"blank", 0x2423},
+{"blinebelow", 0x1E07},
+{"block", 0x2588},
+{"bmonospace", 0xFF42},
+{"bobaimaithai", 0x0E1A},
+{"bohiragana", 0x307C},
+{"bokatakana", 0x30DC},
+{"bparen", 0x249D},
+{"bqsquare", 0x33C3},
+{"braceex", 0xF8F4},
+{"braceleft", 0x007B},
+{"braceleftbt", 0xF8F3},
+{"braceleftmid", 0xF8F2},
+{"braceleftmonospace", 0xFF5B},
+{"braceleftsmall", 0xFE5B},
+{"bracelefttp", 0xF8F1},
+{"braceleftvertical", 0xFE37},
+{"braceright", 0x007D},
+{"bracerightbt", 0xF8FE},
+{"bracerightmid", 0xF8FD},
+{"bracerightmonospace", 0xFF5D},
+{"bracerightsmall", 0xFE5C},
+{"bracerighttp", 0xF8FC},
+{"bracerightvertical", 0xFE38},
+{"bracketleft", 0x005B},
+{"bracketleftbt", 0xF8F0},
+{"bracketleftex", 0xF8EF},
+{"bracketleftmonospace", 0xFF3B},
+{"bracketlefttp", 0xF8EE},
+{"bracketright", 0x005D},
+{"bracketrightbt", 0xF8FB},
+{"bracketrightex", 0xF8FA},
+{"bracketrightmonospace", 0xFF3D},
+{"bracketrighttp", 0xF8F9},
+{"breve", 0x02D8},
+{"brevebelowcmb", 0x032E},
+{"brevecmb", 0x0306},
+{"breveinvertedbelowcmb", 0x032F},
+{"breveinvertedcmb", 0x0311},
+{"breveinverteddoublecmb", 0x0361},
+{"bridgebelowcmb", 0x032A},
+{"bridgeinvertedbelowcmb", 0x033A},
+{"brokenbar", 0x00A6},
+{"bstroke", 0x0180},
+{"bsuperior", 0xF6EA},
+{"btopbar", 0x0183},
+{"buhiragana", 0x3076},
+{"bukatakana", 0x30D6},
+{"bullet", 0x2022},
+{"bulletinverse", 0x25D8},
+{"bulletoperator", 0x2219},
+{"bullseye", 0x25CE},
+{"c", 0x0063},
+{"caarmenian", 0x056E},
+{"cabengali", 0x099A},
+{"cacute", 0x0107},
+{"cadeva", 0x091A},
+{"cagujarati", 0x0A9A},
+{"cagurmukhi", 0x0A1A},
+{"calsquare", 0x3388},
+{"candrabindubengali", 0x0981},
+{"candrabinducmb", 0x0310},
+{"candrabindudeva", 0x0901},
+{"candrabindugujarati", 0x0A81},
+{"capslock", 0x21EA},
+{"careof", 0x2105},
+{"caron", 0x02C7},
+{"caronbelowcmb", 0x032C},
+{"caroncmb", 0x030C},
+{"carriagereturn", 0x21B5},
+{"cbopomofo", 0x3118},
+{"ccaron", 0x010D},
+{"ccedilla", 0x00E7},
+{"ccedillaacute", 0x1E09},
+{"ccircle", 0x24D2},
+{"ccircumflex", 0x0109},
+{"ccurl", 0x0255},
+{"cdot", 0x010B},
+{"cdotaccent", 0x010B},
+{"cdsquare", 0x33C5},
+{"cedilla", 0x00B8},
+{"cedillacmb", 0x0327},
+{"cent", 0x00A2},
+{"centigrade", 0x2103},
+{"centinferior", 0xF6DF},
+{"centmonospace", 0xFFE0},
+{"centoldstyle", 0xF7A2},
+{"centsuperior", 0xF6E0},
+{"chaarmenian", 0x0579},
+{"chabengali", 0x099B},
+{"chadeva", 0x091B},
+{"chagujarati", 0x0A9B},
+{"chagurmukhi", 0x0A1B},
+{"chbopomofo", 0x3114},
+{"cheabkhasiancyrillic", 0x04BD},
+{"checkmark", 0x2713},
+{"checyrillic", 0x0447},
+{"chedescenderabkhasiancyrillic", 0x04BF},
+{"chedescendercyrillic", 0x04B7},
+{"chedieresiscyrillic", 0x04F5},
+{"cheharmenian", 0x0573},
+{"chekhakassiancyrillic", 0x04CC},
+{"cheverticalstrokecyrillic", 0x04B9},
+{"chi", 0x03C7},
+{"chieuchacirclekorean", 0x3277},
+{"chieuchaparenkorean", 0x3217},
+{"chieuchcirclekorean", 0x3269},
+{"chieuchkorean", 0x314A},
+{"chieuchparenkorean", 0x3209},
+{"chochangthai", 0x0E0A},
+{"chochanthai", 0x0E08},
+{"chochingthai", 0x0E09},
+{"chochoethai", 0x0E0C},
+{"chook", 0x0188},
+{"cieucacirclekorean", 0x3276},
+{"cieucaparenkorean", 0x3216},
+{"cieuccirclekorean", 0x3268},
+{"cieuckorean", 0x3148},
+{"cieucparenkorean", 0x3208},
+{"cieucuparenkorean", 0x321C},
+{"circle", 0x25CB},
+{"circlemultiply", 0x2297},
+{"circleot", 0x2299},
+{"circleplus", 0x2295},
+{"circlepostalmark", 0x3036},
+{"circlewithlefthalfblack", 0x25D0},
+{"circlewithrighthalfblack", 0x25D1},
+{"circumflex", 0x02C6},
+{"circumflexbelowcmb", 0x032D},
+{"circumflexcmb", 0x0302},
+{"clear", 0x2327},
+{"clickalveolar", 0x01C2},
+{"clickdental", 0x01C0},
+{"clicklateral", 0x01C1},
+{"clickretroflex", 0x01C3},
+{"club", 0x2663},
+{"clubsuitblack", 0x2663},
+{"clubsuitwhite", 0x2667},
+{"cmcubedsquare", 0x33A4},
+{"cmonospace", 0xFF43},
+{"cmsquaredsquare", 0x33A0},
+{"coarmenian", 0x0581},
+{"colon", 0x003A},
+{"colonmonetary", 0x20A1},
+{"colonmonospace", 0xFF1A},
+{"colonsign", 0x20A1},
+{"colonsmall", 0xFE55},
+{"colontriangularhalfmod", 0x02D1},
+{"colontriangularmod", 0x02D0},
+{"comma", 0x002C},
+{"commaabovecmb", 0x0313},
+{"commaaboverightcmb", 0x0315},
+{"commaaccent", 0xF6C3},
+{"commaarabic", 0x060C},
+{"commaarmenian", 0x055D},
+{"commainferior", 0xF6E1},
+{"commamonospace", 0xFF0C},
+{"commareversedabovecmb", 0x0314},
+{"commareversedmod", 0x02BD},
+{"commasmall", 0xFE50},
+{"commasuperior", 0xF6E2},
+{"commaturnedabovecmb", 0x0312},
+{"commaturnedmod", 0x02BB},
+{"compass", 0x263C},
+{"congruent", 0x2245},
+{"contourintegral", 0x222E},
+{"control", 0x2303},
+{"controlACK", 0x0006},
+{"controlBEL", 0x0007},
+{"controlBS", 0x0008},
+{"controlCAN", 0x0018},
+{"controlCR", 0x000D},
+{"controlDC1", 0x0011},
+{"controlDC2", 0x0012},
+{"controlDC3", 0x0013},
+{"controlDC4", 0x0014},
+{"controlDEL", 0x007F},
+{"controlDLE", 0x0010},
+{"controlEM", 0x0019},
+{"controlENQ", 0x0005},
+{"controlEOT", 0x0004},
+{"controlESC", 0x001B},
+{"controlETB", 0x0017},
+{"controlETX", 0x0003},
+{"controlFF", 0x000C},
+{"controlFS", 0x001C},
+{"controlGS", 0x001D},
+{"controlHT", 0x0009},
+{"controlLF", 0x000A},
+{"controlNAK", 0x0015},
+{"controlRS", 0x001E},
+{"controlSI", 0x000F},
+{"controlSO", 0x000E},
+{"controlSOT", 0x0002},
+{"controlSTX", 0x0001},
+{"controlSUB", 0x001A},
+{"controlSYN", 0x0016},
+{"controlUS", 0x001F},
+{"controlVT", 0x000B},
+{"copyright", 0x00A9},
+{"copyrightsans", 0xF8E9},
+{"copyrightserif", 0xF6D9},
+{"cornerbracketleft", 0x300C},
+{"cornerbracketlefthalfwidth", 0xFF62},
+{"cornerbracketleftvertical", 0xFE41},
+{"cornerbracketright", 0x300D},
+{"cornerbracketrighthalfwidth", 0xFF63},
+{"cornerbracketrightvertical", 0xFE42},
+{"corporationsquare", 0x337F},
+{"cosquare", 0x33C7},
+{"coverkgsquare", 0x33C6},
+{"cparen", 0x249E},
+{"cruzeiro", 0x20A2},
+{"cstretched", 0x0297},
+{"curlyand", 0x22CF},
+{"curlyor", 0x22CE},
+{"currency", 0x00A4},
+{"cyrBreve", 0xF6D1},
+{"cyrFlex", 0xF6D2},
+{"cyrbreve", 0xF6D4},
+{"cyrflex", 0xF6D5},
+{"d", 0x0064},
+{"daarmenian", 0x0564},
+{"dabengali", 0x09A6},
+{"dadarabic", 0x0636},
+{"dadeva", 0x0926},
+{"dadfinalarabic", 0xFEBE},
+{"dadinitialarabic", 0xFEBF},
+{"dadmedialarabic", 0xFEC0},
+{"dagesh", 0x05BC},
+{"dageshhebrew", 0x05BC},
+{"dagger", 0x2020},
+{"daggerdbl", 0x2021},
+{"dagujarati", 0x0AA6},
+{"dagurmukhi", 0x0A26},
+{"dahiragana", 0x3060},
+{"dakatakana", 0x30C0},
+{"dalarabic", 0x062F},
+{"dalet", 0x05D3},
+{"daletdagesh", 0xFB33},
+{"daletdageshhebrew", 0xFB33},
+{"dalethebrew", 0x05D3},
+{"dalfinalarabic", 0xFEAA},
+{"dammaarabic", 0x064F},
+{"dammalowarabic", 0x064F},
+{"dammatanaltonearabic", 0x064C},
+{"dammatanarabic", 0x064C},
+{"danda", 0x0964},
+{"dargahebrew", 0x05A7},
+{"dargalefthebrew", 0x05A7},
+{"dasiapneumatacyrilliccmb", 0x0485},
+{"dblGrave", 0xF6D3},
+{"dblanglebracketleft", 0x300A},
+{"dblanglebracketleftvertical", 0xFE3D},
+{"dblanglebracketright", 0x300B},
+{"dblanglebracketrightvertical", 0xFE3E},
+{"dblarchinvertedbelowcmb", 0x032B},
+{"dblarrowleft", 0x21D4},
+{"dblarrowright", 0x21D2},
+{"dbldanda", 0x0965},
+{"dblgrave", 0xF6D6},
+{"dblgravecmb", 0x030F},
+{"dblintegral", 0x222C},
+{"dbllowline", 0x2017},
+{"dbllowlinecmb", 0x0333},
+{"dbloverlinecmb", 0x033F},
+{"dblprimemod", 0x02BA},
+{"dblverticalbar", 0x2016},
+{"dblverticallineabovecmb", 0x030E},
+{"dbopomofo", 0x3109},
+{"dbsquare", 0x33C8},
+{"dcaron", 0x010F},
+{"dcedilla", 0x1E11},
+{"dcircle", 0x24D3},
+{"dcircumflexbelow", 0x1E13},
+{"dcroat", 0x0111},
+{"ddabengali", 0x09A1},
+{"ddadeva", 0x0921},
+{"ddagujarati", 0x0AA1},
+{"ddagurmukhi", 0x0A21},
+{"ddalarabic", 0x0688},
+{"ddalfinalarabic", 0xFB89},
+{"dddhadeva", 0x095C},
+{"ddhabengali", 0x09A2},
+{"ddhadeva", 0x0922},
+{"ddhagujarati", 0x0AA2},
+{"ddhagurmukhi", 0x0A22},
+{"ddotaccent", 0x1E0B},
+{"ddotbelow", 0x1E0D},
+{"decimalseparatorarabic", 0x066B},
+{"decimalseparatorpersian", 0x066B},
+{"decyrillic", 0x0434},
+{"degree", 0x00B0},
+{"dehihebrew", 0x05AD},
+{"dehiragana", 0x3067},
+{"deicoptic", 0x03EF},
+{"dekatakana", 0x30C7},
+{"deleteleft", 0x232B},
+{"deleteright", 0x2326},
+{"delta", 0x03B4},
+{"deltaturned", 0x018D},
+{"denominatorminusonenumeratorbengali", 0x09F8},
+{"dezh", 0x02A4},
+{"dhabengali", 0x09A7},
+{"dhadeva", 0x0927},
+{"dhagujarati", 0x0AA7},
+{"dhagurmukhi", 0x0A27},
+{"dhook", 0x0257},
+{"dialytikatonos", 0x0385},
+{"dialytikatonoscmb", 0x0344},
+{"diamond", 0x2666},
+{"diamondsuitwhite", 0x2662},
+{"dieresis", 0x00A8},
+{"dieresisacute", 0xF6D7},
+{"dieresisbelowcmb", 0x0324},
+{"dieresiscmb", 0x0308},
+{"dieresisgrave", 0xF6D8},
+{"dieresistonos", 0x0385},
+{"dihiragana", 0x3062},
+{"dikatakana", 0x30C2},
+{"dittomark", 0x3003},
+{"divide", 0x00F7},
+{"divides", 0x2223},
+{"divisionslash", 0x2215},
+{"djecyrillic", 0x0452},
+{"dkshade", 0x2593},
+{"dlinebelow", 0x1E0F},
+{"dlsquare", 0x3397},
+{"dmacron", 0x0111},
+{"dmonospace", 0xFF44},
+{"dnblock", 0x2584},
+{"dochadathai", 0x0E0E},
+{"dodekthai", 0x0E14},
+{"dohiragana", 0x3069},
+{"dokatakana", 0x30C9},
+{"dollar", 0x0024},
+{"dollarinferior", 0xF6E3},
+{"dollarmonospace", 0xFF04},
+{"dollaroldstyle", 0xF724},
+{"dollarsmall", 0xFE69},
+{"dollarsuperior", 0xF6E4},
+{"dong", 0x20AB},
+{"dorusquare", 0x3326},
+{"dotaccent", 0x02D9},
+{"dotaccentcmb", 0x0307},
+{"dotbelowcmb", 0x0323},
+{"dotbelowcomb", 0x0323},
+{"dotkatakana", 0x30FB},
+{"dotlessi", 0x0131},
+{"dotlessj", 0xF6BE},
+{"dotlessjstrokehook", 0x0284},
+{"dotmath", 0x22C5},
+{"dottedcircle", 0x25CC},
+{"doubleyodpatah", 0xFB1F},
+{"doubleyodpatahhebrew", 0xFB1F},
+{"downtackbelowcmb", 0x031E},
+{"downtackmod", 0x02D5},
+{"dparen", 0x249F},
+{"dsuperior", 0xF6EB},
+{"dtail", 0x0256},
+{"dtopbar", 0x018C},
+{"duhiragana", 0x3065},
+{"dukatakana", 0x30C5},
+{"dz", 0x01F3},
+{"dzaltone", 0x02A3},
+{"dzcaron", 0x01C6},
+{"dzcurl", 0x02A5},
+{"dzeabkhasiancyrillic", 0x04E1},
+{"dzecyrillic", 0x0455},
+{"dzhecyrillic", 0x045F},
+{"e", 0x0065},
+{"eacute", 0x00E9},
+{"earth", 0x2641},
+{"ebengali", 0x098F},
+{"ebopomofo", 0x311C},
+{"ebreve", 0x0115},
+{"ecandradeva", 0x090D},
+{"ecandragujarati", 0x0A8D},
+{"ecandravowelsigndeva", 0x0945},
+{"ecandravowelsigngujarati", 0x0AC5},
+{"ecaron", 0x011B},
+{"ecedillabreve", 0x1E1D},
+{"echarmenian", 0x0565},
+{"echyiwnarmenian", 0x0587},
+{"ecircle", 0x24D4},
+{"ecircumflex", 0x00EA},
+{"ecircumflexacute", 0x1EBF},
+{"ecircumflexbelow", 0x1E19},
+{"ecircumflexdotbelow", 0x1EC7},
+{"ecircumflexgrave", 0x1EC1},
+{"ecircumflexhookabove", 0x1EC3},
+{"ecircumflextilde", 0x1EC5},
+{"ecyrillic", 0x0454},
+{"edblgrave", 0x0205},
+{"edeva", 0x090F},
+{"edieresis", 0x00EB},
+{"edot", 0x0117},
+{"edotaccent", 0x0117},
+{"edotbelow", 0x1EB9},
+{"eegurmukhi", 0x0A0F},
+{"eematragurmukhi", 0x0A47},
+{"efcyrillic", 0x0444},
+{"egrave", 0x00E8},
+{"egujarati", 0x0A8F},
+{"eharmenian", 0x0567},
+{"ehbopomofo", 0x311D},
+{"ehiragana", 0x3048},
+{"ehookabove", 0x1EBB},
+{"eibopomofo", 0x311F},
+{"eight", 0x0038},
+{"eightarabic", 0x0668},
+{"eightbengali", 0x09EE},
+{"eightcircle", 0x2467},
+{"eightcircleinversesansserif", 0x2791},
+{"eightdeva", 0x096E},
+{"eighteencircle", 0x2471},
+{"eighteenparen", 0x2485},
+{"eighteenperiod", 0x2499},
+{"eightgujarati", 0x0AEE},
+{"eightgurmukhi", 0x0A6E},
+{"eighthackarabic", 0x0668},
+{"eighthangzhou", 0x3028},
+{"eighthnotebeamed", 0x266B},
+{"eightideographicparen", 0x3227},
+{"eightinferior", 0x2088},
+{"eightmonospace", 0xFF18},
+{"eightoldstyle", 0xF738},
+{"eightparen", 0x247B},
+{"eightperiod", 0x248F},
+{"eightpersian", 0x06F8},
+{"eightroman", 0x2177},
+{"eightsuperior", 0x2078},
+{"eightthai", 0x0E58},
+{"einvertedbreve", 0x0207},
+{"eiotifiedcyrillic", 0x0465},
+{"ekatakana", 0x30A8},
+{"ekatakanahalfwidth", 0xFF74},
+{"ekonkargurmukhi", 0x0A74},
+{"ekorean", 0x3154},
+{"elcyrillic", 0x043B},
+{"element", 0x2208},
+{"elevencircle", 0x246A},
+{"elevenparen", 0x247E},
+{"elevenperiod", 0x2492},
+{"elevenroman", 0x217A},
+{"ellipsis", 0x2026},
+{"ellipsisvertical", 0x22EE},
+{"emacron", 0x0113},
+{"emacronacute", 0x1E17},
+{"emacrongrave", 0x1E15},
+{"emcyrillic", 0x043C},
+{"emdash", 0x2014},
+{"emdashvertical", 0xFE31},
+{"emonospace", 0xFF45},
+{"emphasismarkarmenian", 0x055B},
+{"emptyset", 0x2205},
+{"enbopomofo", 0x3123},
+{"encyrillic", 0x043D},
+{"endash", 0x2013},
+{"endashvertical", 0xFE32},
+{"endescendercyrillic", 0x04A3},
+{"eng", 0x014B},
+{"engbopomofo", 0x3125},
+{"enghecyrillic", 0x04A5},
+{"enhookcyrillic", 0x04C8},
+{"enspace", 0x2002},
+{"eogonek", 0x0119},
+{"eokorean", 0x3153},
+{"eopen", 0x025B},
+{"eopenclosed", 0x029A},
+{"eopenreversed", 0x025C},
+{"eopenreversedclosed", 0x025E},
+{"eopenreversedhook", 0x025D},
+{"eparen", 0x24A0},
+{"epsilon", 0x03B5},
+{"epsilontonos", 0x03AD},
+{"equal", 0x003D},
+{"equalmonospace", 0xFF1D},
+{"equalsmall", 0xFE66},
+{"equalsuperior", 0x207C},
+{"equivalence", 0x2261},
+{"erbopomofo", 0x3126},
+{"ercyrillic", 0x0440},
+{"ereversed", 0x0258},
+{"ereversedcyrillic", 0x044D},
+{"escyrillic", 0x0441},
+{"esdescendercyrillic", 0x04AB},
+{"esh", 0x0283},
+{"eshcurl", 0x0286},
+{"eshortdeva", 0x090E},
+{"eshortvowelsigndeva", 0x0946},
+{"eshreversedloop", 0x01AA},
+{"eshsquatreversed", 0x0285},
+{"esmallhiragana", 0x3047},
+{"esmallkatakana", 0x30A7},
+{"esmallkatakanahalfwidth", 0xFF6A},
+{"estimated", 0x212E},
+{"esuperior", 0xF6EC},
+{"eta", 0x03B7},
+{"etarmenian", 0x0568},
+{"etatonos", 0x03AE},
+{"eth", 0x00F0},
+{"etilde", 0x1EBD},
+{"etildebelow", 0x1E1B},
+{"etnahtafoukhhebrew", 0x0591},
+{"etnahtafoukhlefthebrew", 0x0591},
+{"etnahtahebrew", 0x0591},
+{"etnahtalefthebrew", 0x0591},
+{"eturned", 0x01DD},
+{"eukorean", 0x3161},
+{"euro", 0x20AC},
+{"evowelsignbengali", 0x09C7},
+{"evowelsigndeva", 0x0947},
+{"evowelsigngujarati", 0x0AC7},
+{"exclam", 0x0021},
+{"exclamarmenian", 0x055C},
+{"exclamdbl", 0x203C},
+{"exclamdown", 0x00A1},
+{"exclamdownsmall", 0xF7A1},
+{"exclammonospace", 0xFF01},
+{"exclamsmall", 0xF721},
+{"existential", 0x2203},
+{"ezh", 0x0292},
+{"ezhcaron", 0x01EF},
+{"ezhcurl", 0x0293},
+{"ezhreversed", 0x01B9},
+{"ezhtail", 0x01BA},
+{"f", 0x0066},
+{"fadeva", 0x095E},
+{"fagurmukhi", 0x0A5E},
+{"fahrenheit", 0x2109},
+{"fathaarabic", 0x064E},
+{"fathalowarabic", 0x064E},
+{"fathatanarabic", 0x064B},
+{"fbopomofo", 0x3108},
+{"fcircle", 0x24D5},
+{"fdotaccent", 0x1E1F},
+{"feharabic", 0x0641},
+{"feharmenian", 0x0586},
+{"fehfinalarabic", 0xFED2},
+{"fehinitialarabic", 0xFED3},
+{"fehmedialarabic", 0xFED4},
+{"feicoptic", 0x03E5},
+{"female", 0x2640},
+{"ff", 0xFB00},
+{"ffi", 0xFB03},
+{"ffl", 0xFB04},
+{"fi", 0xFB01},
+{"fifteencircle", 0x246E},
+{"fifteenparen", 0x2482},
+{"fifteenperiod", 0x2496},
+{"figuredash", 0x2012},
+{"filledbox", 0x25A0},
+{"filledrect", 0x25AC},
+{"finalkaf", 0x05DA},
+{"finalkafdagesh", 0xFB3A},
+{"finalkafdageshhebrew", 0xFB3A},
+{"finalkafhebrew", 0x05DA},
+{"finalmem", 0x05DD},
+{"finalmemhebrew", 0x05DD},
+{"finalnun", 0x05DF},
+{"finalnunhebrew", 0x05DF},
+{"finalpe", 0x05E3},
+{"finalpehebrew", 0x05E3},
+{"finaltsadi", 0x05E5},
+{"finaltsadihebrew", 0x05E5},
+{"firsttonechinese", 0x02C9},
+{"fisheye", 0x25C9},
+{"fitacyrillic", 0x0473},
+{"five", 0x0035},
+{"fivearabic", 0x0665},
+{"fivebengali", 0x09EB},
+{"fivecircle", 0x2464},
+{"fivecircleinversesansserif", 0x278E},
+{"fivedeva", 0x096B},
+{"fiveeighths", 0x215D},
+{"fivegujarati", 0x0AEB},
+{"fivegurmukhi", 0x0A6B},
+{"fivehackarabic", 0x0665},
+{"fivehangzhou", 0x3025},
+{"fiveideographicparen", 0x3224},
+{"fiveinferior", 0x2085},
+{"fivemonospace", 0xFF15},
+{"fiveoldstyle", 0xF735},
+{"fiveparen", 0x2478},
+{"fiveperiod", 0x248C},
+{"fivepersian", 0x06F5},
+{"fiveroman", 0x2174},
+{"fivesuperior", 0x2075},
+{"fivethai", 0x0E55},
+{"fl", 0xFB02},
+{"florin", 0x0192},
+{"fmonospace", 0xFF46},
+{"fmsquare", 0x3399},
+{"fofanthai", 0x0E1F},
+{"fofathai", 0x0E1D},
+{"fongmanthai", 0x0E4F},
+{"forall", 0x2200},
+{"four", 0x0034},
+{"fourarabic", 0x0664},
+{"fourbengali", 0x09EA},
+{"fourcircle", 0x2463},
+{"fourcircleinversesansserif", 0x278D},
+{"fourdeva", 0x096A},
+{"fourgujarati", 0x0AEA},
+{"fourgurmukhi", 0x0A6A},
+{"fourhackarabic", 0x0664},
+{"fourhangzhou", 0x3024},
+{"fourideographicparen", 0x3223},
+{"fourinferior", 0x2084},
+{"fourmonospace", 0xFF14},
+{"fournumeratorbengali", 0x09F7},
+{"fouroldstyle", 0xF734},
+{"fourparen", 0x2477},
+{"fourperiod", 0x248B},
+{"fourpersian", 0x06F4},
+{"fourroman", 0x2173},
+{"foursuperior", 0x2074},
+{"fourteencircle", 0x246D},
+{"fourteenparen", 0x2481},
+{"fourteenperiod", 0x2495},
+{"fourthai", 0x0E54},
+{"fourthtonechinese", 0x02CB},
+{"fparen", 0x24A1},
+{"fraction", 0x2044},
+{"franc", 0x20A3},
+{"g", 0x0067},
+{"gabengali", 0x0997},
+{"gacute", 0x01F5},
+{"gadeva", 0x0917},
+{"gafarabic", 0x06AF},
+{"gaffinalarabic", 0xFB93},
+{"gafinitialarabic", 0xFB94},
+{"gafmedialarabic", 0xFB95},
+{"gagujarati", 0x0A97},
+{"gagurmukhi", 0x0A17},
+{"gahiragana", 0x304C},
+{"gakatakana", 0x30AC},
+{"gamma", 0x03B3},
+{"gammalatinsmall", 0x0263},
+{"gammasuperior", 0x02E0},
+{"gangiacoptic", 0x03EB},
+{"gbopomofo", 0x310D},
+{"gbreve", 0x011F},
+{"gcaron", 0x01E7},
+{"gcedilla", 0x0123},
+{"gcircle", 0x24D6},
+{"gcircumflex", 0x011D},
+{"gcommaaccent", 0x0123},
+{"gdot", 0x0121},
+{"gdotaccent", 0x0121},
+{"gecyrillic", 0x0433},
+{"gehiragana", 0x3052},
+{"gekatakana", 0x30B2},
+{"geometricallyequal", 0x2251},
+{"gereshaccenthebrew", 0x059C},
+{"gereshhebrew", 0x05F3},
+{"gereshmuqdamhebrew", 0x059D},
+{"germandbls", 0x00DF},
+{"gershayimaccenthebrew", 0x059E},
+{"gershayimhebrew", 0x05F4},
+{"getamark", 0x3013},
+{"ghabengali", 0x0998},
+{"ghadarmenian", 0x0572},
+{"ghadeva", 0x0918},
+{"ghagujarati", 0x0A98},
+{"ghagurmukhi", 0x0A18},
+{"ghainarabic", 0x063A},
+{"ghainfinalarabic", 0xFECE},
+{"ghaininitialarabic", 0xFECF},
+{"ghainmedialarabic", 0xFED0},
+{"ghemiddlehookcyrillic", 0x0495},
+{"ghestrokecyrillic", 0x0493},
+{"gheupturncyrillic", 0x0491},
+{"ghhadeva", 0x095A},
+{"ghhagurmukhi", 0x0A5A},
+{"ghook", 0x0260},
+{"ghzsquare", 0x3393},
+{"gihiragana", 0x304E},
+{"gikatakana", 0x30AE},
+{"gimarmenian", 0x0563},
+{"gimel", 0x05D2},
+{"gimeldagesh", 0xFB32},
+{"gimeldageshhebrew", 0xFB32},
+{"gimelhebrew", 0x05D2},
+{"gjecyrillic", 0x0453},
+{"glottalinvertedstroke", 0x01BE},
+{"glottalstop", 0x0294},
+{"glottalstopinverted", 0x0296},
+{"glottalstopmod", 0x02C0},
+{"glottalstopreversed", 0x0295},
+{"glottalstopreversedmod", 0x02C1},
+{"glottalstopreversedsuperior", 0x02E4},
+{"glottalstopstroke", 0x02A1},
+{"glottalstopstrokereversed", 0x02A2},
+{"gmacron", 0x1E21},
+{"gmonospace", 0xFF47},
+{"gohiragana", 0x3054},
+{"gokatakana", 0x30B4},
+{"gparen", 0x24A2},
+{"gpasquare", 0x33AC},
+{"gradient", 0x2207},
+{"grave", 0x0060},
+{"gravebelowcmb", 0x0316},
+{"gravecmb", 0x0300},
+{"gravecomb", 0x0300},
+{"gravedeva", 0x0953},
+{"gravelowmod", 0x02CE},
+{"gravemonospace", 0xFF40},
+{"gravetonecmb", 0x0340},
+{"greater", 0x003E},
+{"greaterequal", 0x2265},
+{"greaterequalorless", 0x22DB},
+{"greatermonospace", 0xFF1E},
+{"greaterorequivalent", 0x2273},
+{"greaterorless", 0x2277},
+{"greateroverequal", 0x2267},
+{"greatersmall", 0xFE65},
+{"gscript", 0x0261},
+{"gstroke", 0x01E5},
+{"guhiragana", 0x3050},
+{"guillemotleft", 0x00AB},
+{"guillemotright", 0x00BB},
+{"guilsinglleft", 0x2039},
+{"guilsinglright", 0x203A},
+{"gukatakana", 0x30B0},
+{"guramusquare", 0x3318},
+{"gysquare", 0x33C9},
+{"h", 0x0068},
+{"haabkhasiancyrillic", 0x04A9},
+{"haaltonearabic", 0x06C1},
+{"habengali", 0x09B9},
+{"hadescendercyrillic", 0x04B3},
+{"hadeva", 0x0939},
+{"hagujarati", 0x0AB9},
+{"hagurmukhi", 0x0A39},
+{"haharabic", 0x062D},
+{"hahfinalarabic", 0xFEA2},
+{"hahinitialarabic", 0xFEA3},
+{"hahiragana", 0x306F},
+{"hahmedialarabic", 0xFEA4},
+{"haitusquare", 0x332A},
+{"hakatakana", 0x30CF},
+{"hakatakanahalfwidth", 0xFF8A},
+{"halantgurmukhi", 0x0A4D},
+{"hamzaarabic", 0x0621},
+{"hamzalowarabic", 0x0621},
+{"hangulfiller", 0x3164},
+{"hardsigncyrillic", 0x044A},
+{"harpoonleftbarbup", 0x21BC},
+{"harpoonrightbarbup", 0x21C0},
+{"hasquare", 0x33CA},
+{"hatafpatah", 0x05B2},
+{"hatafpatah16", 0x05B2},
+{"hatafpatah23", 0x05B2},
+{"hatafpatah2f", 0x05B2},
+{"hatafpatahhebrew", 0x05B2},
+{"hatafpatahnarrowhebrew", 0x05B2},
+{"hatafpatahquarterhebrew", 0x05B2},
+{"hatafpatahwidehebrew", 0x05B2},
+{"hatafqamats", 0x05B3},
+{"hatafqamats1b", 0x05B3},
+{"hatafqamats28", 0x05B3},
+{"hatafqamats34", 0x05B3},
+{"hatafqamatshebrew", 0x05B3},
+{"hatafqamatsnarrowhebrew", 0x05B3},
+{"hatafqamatsquarterhebrew", 0x05B3},
+{"hatafqamatswidehebrew", 0x05B3},
+{"hatafsegol", 0x05B1},
+{"hatafsegol17", 0x05B1},
+{"hatafsegol24", 0x05B1},
+{"hatafsegol30", 0x05B1},
+{"hatafsegolhebrew", 0x05B1},
+{"hatafsegolnarrowhebrew", 0x05B1},
+{"hatafsegolquarterhebrew", 0x05B1},
+{"hatafsegolwidehebrew", 0x05B1},
+{"hbar", 0x0127},
+{"hbopomofo", 0x310F},
+{"hbrevebelow", 0x1E2B},
+{"hcedilla", 0x1E29},
+{"hcircle", 0x24D7},
+{"hcircumflex", 0x0125},
+{"hdieresis", 0x1E27},
+{"hdotaccent", 0x1E23},
+{"hdotbelow", 0x1E25},
+{"he", 0x05D4},
+{"heart", 0x2665},
+{"heartsuitblack", 0x2665},
+{"heartsuitwhite", 0x2661},
+{"hedagesh", 0xFB34},
+{"hedageshhebrew", 0xFB34},
+{"hehaltonearabic", 0x06C1},
+{"heharabic", 0x0647},
+{"hehebrew", 0x05D4},
+{"hehfinalaltonearabic", 0xFBA7},
+{"hehfinalalttwoarabic", 0xFEEA},
+{"hehfinalarabic", 0xFEEA},
+{"hehhamzaabovefinalarabic", 0xFBA5},
+{"hehhamzaaboveisolatedarabic", 0xFBA4},
+{"hehinitialaltonearabic", 0xFBA8},
+{"hehinitialarabic", 0xFEEB},
+{"hehiragana", 0x3078},
+{"hehmedialaltonearabic", 0xFBA9},
+{"hehmedialarabic", 0xFEEC},
+{"heiseierasquare", 0x337B},
+{"hekatakana", 0x30D8},
+{"hekatakanahalfwidth", 0xFF8D},
+{"hekutaarusquare", 0x3336},
+{"henghook", 0x0267},
+{"herutusquare", 0x3339},
+{"het", 0x05D7},
+{"hethebrew", 0x05D7},
+{"hhook", 0x0266},
+{"hhooksuperior", 0x02B1},
+{"hieuhacirclekorean", 0x327B},
+{"hieuhaparenkorean", 0x321B},
+{"hieuhcirclekorean", 0x326D},
+{"hieuhkorean", 0x314E},
+{"hieuhparenkorean", 0x320D},
+{"hihiragana", 0x3072},
+{"hikatakana", 0x30D2},
+{"hikatakanahalfwidth", 0xFF8B},
+{"hiriq", 0x05B4},
+{"hiriq14", 0x05B4},
+{"hiriq21", 0x05B4},
+{"hiriq2d", 0x05B4},
+{"hiriqhebrew", 0x05B4},
+{"hiriqnarrowhebrew", 0x05B4},
+{"hiriqquarterhebrew", 0x05B4},
+{"hiriqwidehebrew", 0x05B4},
+{"hlinebelow", 0x1E96},
+{"hmonospace", 0xFF48},
+{"hoarmenian", 0x0570},
+{"hohipthai", 0x0E2B},
+{"hohiragana", 0x307B},
+{"hokatakana", 0x30DB},
+{"hokatakanahalfwidth", 0xFF8E},
+{"holam", 0x05B9},
+{"holam19", 0x05B9},
+{"holam26", 0x05B9},
+{"holam32", 0x05B9},
+{"holamhebrew", 0x05B9},
+{"holamnarrowhebrew", 0x05B9},
+{"holamquarterhebrew", 0x05B9},
+{"holamwidehebrew", 0x05B9},
+{"honokhukthai", 0x0E2E},
+{"hookabovecomb", 0x0309},
+{"hookcmb", 0x0309},
+{"hookpalatalizedbelowcmb", 0x0321},
+{"hookretroflexbelowcmb", 0x0322},
+{"hoonsquare", 0x3342},
+{"horicoptic", 0x03E9},
+{"horizontalbar", 0x2015},
+{"horncmb", 0x031B},
+{"hotsprings", 0x2668},
+{"house", 0x2302},
+{"hparen", 0x24A3},
+{"hsuperior", 0x02B0},
+{"hturned", 0x0265},
+{"huhiragana", 0x3075},
+{"huiitosquare", 0x3333},
+{"hukatakana", 0x30D5},
+{"hukatakanahalfwidth", 0xFF8C},
+{"hungarumlaut", 0x02DD},
+{"hungarumlautcmb", 0x030B},
+{"hv", 0x0195},
+{"hyphen", 0x002D},
+{"hypheninferior", 0xF6E5},
+{"hyphenmonospace", 0xFF0D},
+{"hyphensmall", 0xFE63},
+{"hyphensuperior", 0xF6E6},
+{"hyphentwo", 0x2010},
+{"i", 0x0069},
+{"iacute", 0x00ED},
+{"iacyrillic", 0x044F},
+{"ibengali", 0x0987},
+{"ibopomofo", 0x3127},
+{"ibreve", 0x012D},
+{"icaron", 0x01D0},
+{"icircle", 0x24D8},
+{"icircumflex", 0x00EE},
+{"icyrillic", 0x0456},
+{"idblgrave", 0x0209},
+{"ideographearthcircle", 0x328F},
+{"ideographfirecircle", 0x328B},
+{"ideographicallianceparen", 0x323F},
+{"ideographiccallparen", 0x323A},
+{"ideographiccentrecircle", 0x32A5},
+{"ideographicclose", 0x3006},
+{"ideographiccomma", 0x3001},
+{"ideographiccommaleft", 0xFF64},
+{"ideographiccongratulationparen", 0x3237},
+{"ideographiccorrectcircle", 0x32A3},
+{"ideographicearthparen", 0x322F},
+{"ideographicenterpriseparen", 0x323D},
+{"ideographicexcellentcircle", 0x329D},
+{"ideographicfestivalparen", 0x3240},
+{"ideographicfinancialcircle", 0x3296},
+{"ideographicfinancialparen", 0x3236},
+{"ideographicfireparen", 0x322B},
+{"ideographichaveparen", 0x3232},
+{"ideographichighcircle", 0x32A4},
+{"ideographiciterationmark", 0x3005},
+{"ideographiclaborcircle", 0x3298},
+{"ideographiclaborparen", 0x3238},
+{"ideographicleftcircle", 0x32A7},
+{"ideographiclowcircle", 0x32A6},
+{"ideographicmedicinecircle", 0x32A9},
+{"ideographicmetalparen", 0x322E},
+{"ideographicmoonparen", 0x322A},
+{"ideographicnameparen", 0x3234},
+{"ideographicperiod", 0x3002},
+{"ideographicprintcircle", 0x329E},
+{"ideographicreachparen", 0x3243},
+{"ideographicrepresentparen", 0x3239},
+{"ideographicresourceparen", 0x323E},
+{"ideographicrightcircle", 0x32A8},
+{"ideographicsecretcircle", 0x3299},
+{"ideographicselfparen", 0x3242},
+{"ideographicsocietyparen", 0x3233},
+{"ideographicspace", 0x3000},
+{"ideographicspecialparen", 0x3235},
+{"ideographicstockparen", 0x3231},
+{"ideographicstudyparen", 0x323B},
+{"ideographicsunparen", 0x3230},
+{"ideographicsuperviseparen", 0x323C},
+{"ideographicwaterparen", 0x322C},
+{"ideographicwoodparen", 0x322D},
+{"ideographiczero", 0x3007},
+{"ideographmetalcircle", 0x328E},
+{"ideographmooncircle", 0x328A},
+{"ideographnamecircle", 0x3294},
+{"ideographsuncircle", 0x3290},
+{"ideographwatercircle", 0x328C},
+{"ideographwoodcircle", 0x328D},
+{"ideva", 0x0907},
+{"idieresis", 0x00EF},
+{"idieresisacute", 0x1E2F},
+{"idieresiscyrillic", 0x04E5},
+{"idotbelow", 0x1ECB},
+{"iebrevecyrillic", 0x04D7},
+{"iecyrillic", 0x0435},
+{"ieungacirclekorean", 0x3275},
+{"ieungaparenkorean", 0x3215},
+{"ieungcirclekorean", 0x3267},
+{"ieungkorean", 0x3147},
+{"ieungparenkorean", 0x3207},
+{"igrave", 0x00EC},
+{"igujarati", 0x0A87},
+{"igurmukhi", 0x0A07},
+{"ihiragana", 0x3044},
+{"ihookabove", 0x1EC9},
+{"iibengali", 0x0988},
+{"iicyrillic", 0x0438},
+{"iideva", 0x0908},
+{"iigujarati", 0x0A88},
+{"iigurmukhi", 0x0A08},
+{"iimatragurmukhi", 0x0A40},
+{"iinvertedbreve", 0x020B},
+{"iishortcyrillic", 0x0439},
+{"iivowelsignbengali", 0x09C0},
+{"iivowelsigndeva", 0x0940},
+{"iivowelsigngujarati", 0x0AC0},
+{"ij", 0x0133},
+{"ikatakana", 0x30A4},
+{"ikatakanahalfwidth", 0xFF72},
+{"ikorean", 0x3163},
+{"ilde", 0x02DC},
+{"iluyhebrew", 0x05AC},
+{"imacron", 0x012B},
+{"imacroncyrillic", 0x04E3},
+{"imageorapproximatelyequal", 0x2253},
+{"imatragurmukhi", 0x0A3F},
+{"imonospace", 0xFF49},
+{"increment", 0x2206},
+{"infinity", 0x221E},
+{"iniarmenian", 0x056B},
+{"integral", 0x222B},
+{"integralbottom", 0x2321},
+{"integralbt", 0x2321},
+{"integralex", 0xF8F5},
+{"integraltop", 0x2320},
+{"integraltp", 0x2320},
+{"intersection", 0x2229},
+{"intisquare", 0x3305},
+{"invbullet", 0x25D8},
+{"invcircle", 0x25D9},
+{"invsmileface", 0x263B},
+{"iocyrillic", 0x0451},
+{"iogonek", 0x012F},
+{"iota", 0x03B9},
+{"iotadieresis", 0x03CA},
+{"iotadieresistonos", 0x0390},
+{"iotalatin", 0x0269},
+{"iotatonos", 0x03AF},
+{"iparen", 0x24A4},
+{"irigurmukhi", 0x0A72},
+{"ismallhiragana", 0x3043},
+{"ismallkatakana", 0x30A3},
+{"ismallkatakanahalfwidth", 0xFF68},
+{"issharbengali", 0x09FA},
+{"istroke", 0x0268},
+{"isuperior", 0xF6ED},
+{"iterationhiragana", 0x309D},
+{"iterationkatakana", 0x30FD},
+{"itilde", 0x0129},
+{"itildebelow", 0x1E2D},
+{"iubopomofo", 0x3129},
+{"iucyrillic", 0x044E},
+{"ivowelsignbengali", 0x09BF},
+{"ivowelsigndeva", 0x093F},
+{"ivowelsigngujarati", 0x0ABF},
+{"izhitsacyrillic", 0x0475},
+{"izhitsadblgravecyrillic", 0x0477},
+{"j", 0x006A},
+{"jaarmenian", 0x0571},
+{"jabengali", 0x099C},
+{"jadeva", 0x091C},
+{"jagujarati", 0x0A9C},
+{"jagurmukhi", 0x0A1C},
+{"jbopomofo", 0x3110},
+{"jcaron", 0x01F0},
+{"jcircle", 0x24D9},
+{"jcircumflex", 0x0135},
+{"jcrossedtail", 0x029D},
+{"jdotlessstroke", 0x025F},
+{"jecyrillic", 0x0458},
+{"jeemarabic", 0x062C},
+{"jeemfinalarabic", 0xFE9E},
+{"jeeminitialarabic", 0xFE9F},
+{"jeemmedialarabic", 0xFEA0},
+{"jeharabic", 0x0698},
+{"jehfinalarabic", 0xFB8B},
+{"jhabengali", 0x099D},
+{"jhadeva", 0x091D},
+{"jhagujarati", 0x0A9D},
+{"jhagurmukhi", 0x0A1D},
+{"jheharmenian", 0x057B},
+{"jis", 0x3004},
+{"jmonospace", 0xFF4A},
+{"jparen", 0x24A5},
+{"jsuperior", 0x02B2},
+{"k", 0x006B},
+{"kabashkircyrillic", 0x04A1},
+{"kabengali", 0x0995},
+{"kacute", 0x1E31},
+{"kacyrillic", 0x043A},
+{"kadescendercyrillic", 0x049B},
+{"kadeva", 0x0915},
+{"kaf", 0x05DB},
+{"kafarabic", 0x0643},
+{"kafdagesh", 0xFB3B},
+{"kafdageshhebrew", 0xFB3B},
+{"kaffinalarabic", 0xFEDA},
+{"kafhebrew", 0x05DB},
+{"kafinitialarabic", 0xFEDB},
+{"kafmedialarabic", 0xFEDC},
+{"kafrafehebrew", 0xFB4D},
+{"kagujarati", 0x0A95},
+{"kagurmukhi", 0x0A15},
+{"kahiragana", 0x304B},
+{"kahookcyrillic", 0x04C4},
+{"kakatakana", 0x30AB},
+{"kakatakanahalfwidth", 0xFF76},
+{"kappa", 0x03BA},
+{"kappasymbolgreek", 0x03F0},
+{"kapyeounmieumkorean", 0x3171},
+{"kapyeounphieuphkorean", 0x3184},
+{"kapyeounpieupkorean", 0x3178},
+{"kapyeounssangpieupkorean", 0x3179},
+{"karoriisquare", 0x330D},
+{"kashidaautoarabic", 0x0640},
+{"kashidaautonosidebearingarabic", 0x0640},
+{"kasmallkatakana", 0x30F5},
+{"kasquare", 0x3384},
+{"kasraarabic", 0x0650},
+{"kasratanarabic", 0x064D},
+{"kastrokecyrillic", 0x049F},
+{"katahiraprolongmarkhalfwidth", 0xFF70},
+{"kaverticalstrokecyrillic", 0x049D},
+{"kbopomofo", 0x310E},
+{"kcalsquare", 0x3389},
+{"kcaron", 0x01E9},
+{"kcedilla", 0x0137},
+{"kcircle", 0x24DA},
+{"kcommaaccent", 0x0137},
+{"kdotbelow", 0x1E33},
+{"keharmenian", 0x0584},
+{"kehiragana", 0x3051},
+{"kekatakana", 0x30B1},
+{"kekatakanahalfwidth", 0xFF79},
+{"kenarmenian", 0x056F},
+{"kesmallkatakana", 0x30F6},
+{"kgreenlandic", 0x0138},
+{"khabengali", 0x0996},
+{"khacyrillic", 0x0445},
+{"khadeva", 0x0916},
+{"khagujarati", 0x0A96},
+{"khagurmukhi", 0x0A16},
+{"khaharabic", 0x062E},
+{"khahfinalarabic", 0xFEA6},
+{"khahinitialarabic", 0xFEA7},
+{"khahmedialarabic", 0xFEA8},
+{"kheicoptic", 0x03E7},
+{"khhadeva", 0x0959},
+{"khhagurmukhi", 0x0A59},
+{"khieukhacirclekorean", 0x3278},
+{"khieukhaparenkorean", 0x3218},
+{"khieukhcirclekorean", 0x326A},
+{"khieukhkorean", 0x314B},
+{"khieukhparenkorean", 0x320A},
+{"khokhaithai", 0x0E02},
+{"khokhonthai", 0x0E05},
+{"khokhuatthai", 0x0E03},
+{"khokhwaithai", 0x0E04},
+{"khomutthai", 0x0E5B},
+{"khook", 0x0199},
+{"khorakhangthai", 0x0E06},
+{"khzsquare", 0x3391},
+{"kihiragana", 0x304D},
+{"kikatakana", 0x30AD},
+{"kikatakanahalfwidth", 0xFF77},
+{"kiroguramusquare", 0x3315},
+{"kiromeetorusquare", 0x3316},
+{"kirosquare", 0x3314},
+{"kiyeokacirclekorean", 0x326E},
+{"kiyeokaparenkorean", 0x320E},
+{"kiyeokcirclekorean", 0x3260},
+{"kiyeokkorean", 0x3131},
+{"kiyeokparenkorean", 0x3200},
+{"kiyeoksioskorean", 0x3133},
+{"kjecyrillic", 0x045C},
+{"klinebelow", 0x1E35},
+{"klsquare", 0x3398},
+{"kmcubedsquare", 0x33A6},
+{"kmonospace", 0xFF4B},
+{"kmsquaredsquare", 0x33A2},
+{"kohiragana", 0x3053},
+{"kohmsquare", 0x33C0},
+{"kokaithai", 0x0E01},
+{"kokatakana", 0x30B3},
+{"kokatakanahalfwidth", 0xFF7A},
+{"kooposquare", 0x331E},
+{"koppacyrillic", 0x0481},
+{"koreanstandardsymbol", 0x327F},
+{"koroniscmb", 0x0343},
+{"kparen", 0x24A6},
+{"kpasquare", 0x33AA},
+{"ksicyrillic", 0x046F},
+{"ktsquare", 0x33CF},
+{"kturned", 0x029E},
+{"kuhiragana", 0x304F},
+{"kukatakana", 0x30AF},
+{"kukatakanahalfwidth", 0xFF78},
+{"kvsquare", 0x33B8},
+{"kwsquare", 0x33BE},
+{"l", 0x006C},
+{"labengali", 0x09B2},
+{"lacute", 0x013A},
+{"ladeva", 0x0932},
+{"lagujarati", 0x0AB2},
+{"lagurmukhi", 0x0A32},
+{"lakkhangyaothai", 0x0E45},
+{"lamaleffinalarabic", 0xFEFC},
+{"lamalefhamzaabovefinalarabic", 0xFEF8},
+{"lamalefhamzaaboveisolatedarabic", 0xFEF7},
+{"lamalefhamzabelowfinalarabic", 0xFEFA},
+{"lamalefhamzabelowisolatedarabic", 0xFEF9},
+{"lamalefisolatedarabic", 0xFEFB},
+{"lamalefmaddaabovefinalarabic", 0xFEF6},
+{"lamalefmaddaaboveisolatedarabic", 0xFEF5},
+{"lamarabic", 0x0644},
+{"lambda", 0x03BB},
+{"lambdastroke", 0x019B},
+{"lamed", 0x05DC},
+{"lameddagesh", 0xFB3C},
+{"lameddageshhebrew", 0xFB3C},
+{"lamedhebrew", 0x05DC},
+{"lamfinalarabic", 0xFEDE},
+{"lamhahinitialarabic", 0xFCCA},
+{"laminitialarabic", 0xFEDF},
+{"lamjeeminitialarabic", 0xFCC9},
+{"lamkhahinitialarabic", 0xFCCB},
+{"lamlamhehisolatedarabic", 0xFDF2},
+{"lammedialarabic", 0xFEE0},
+{"lammeemhahinitialarabic", 0xFD88},
+{"lammeeminitialarabic", 0xFCCC},
+{"largecircle", 0x25EF},
+{"lbar", 0x019A},
+{"lbelt", 0x026C},
+{"lbopomofo", 0x310C},
+{"lcaron", 0x013E},
+{"lcedilla", 0x013C},
+{"lcircle", 0x24DB},
+{"lcircumflexbelow", 0x1E3D},
+{"lcommaaccent", 0x013C},
+{"ldot", 0x0140},
+{"ldotaccent", 0x0140},
+{"ldotbelow", 0x1E37},
+{"ldotbelowmacron", 0x1E39},
+{"leftangleabovecmb", 0x031A},
+{"lefttackbelowcmb", 0x0318},
+{"less", 0x003C},
+{"lessequal", 0x2264},
+{"lessequalorgreater", 0x22DA},
+{"lessmonospace", 0xFF1C},
+{"lessorequivalent", 0x2272},
+{"lessorgreater", 0x2276},
+{"lessoverequal", 0x2266},
+{"lesssmall", 0xFE64},
+{"lezh", 0x026E},
+{"lfblock", 0x258C},
+{"lhookretroflex", 0x026D},
+{"lira", 0x20A4},
+{"liwnarmenian", 0x056C},
+{"lj", 0x01C9},
+{"ljecyrillic", 0x0459},
+{"ll", 0xF6C0},
+{"lladeva", 0x0933},
+{"llagujarati", 0x0AB3},
+{"llinebelow", 0x1E3B},
+{"llladeva", 0x0934},
+{"llvocalicbengali", 0x09E1},
+{"llvocalicdeva", 0x0961},
+{"llvocalicvowelsignbengali", 0x09E3},
+{"llvocalicvowelsigndeva", 0x0963},
+{"lmiddletilde", 0x026B},
+{"lmonospace", 0xFF4C},
+{"lmsquare", 0x33D0},
+{"lochulathai", 0x0E2C},
+{"logicaland", 0x2227},
+{"logicalnot", 0x00AC},
+{"logicalnotreversed", 0x2310},
+{"logicalor", 0x2228},
+{"lolingthai", 0x0E25},
+{"longs", 0x017F},
+{"lowlinecenterline", 0xFE4E},
+{"lowlinecmb", 0x0332},
+{"lowlinedashed", 0xFE4D},
+{"lozenge", 0x25CA},
+{"lparen", 0x24A7},
+{"lslash", 0x0142},
+{"lsquare", 0x2113},
+{"lsuperior", 0xF6EE},
+{"ltshade", 0x2591},
+{"luthai", 0x0E26},
+{"lvocalicbengali", 0x098C},
+{"lvocalicdeva", 0x090C},
+{"lvocalicvowelsignbengali", 0x09E2},
+{"lvocalicvowelsigndeva", 0x0962},
+{"lxsquare", 0x33D3},
+{"m", 0x006D},
+{"mabengali", 0x09AE},
+{"macron", 0x00AF},
+{"macronbelowcmb", 0x0331},
+{"macroncmb", 0x0304},
+{"macronlowmod", 0x02CD},
+{"macronmonospace", 0xFFE3},
+{"macute", 0x1E3F},
+{"madeva", 0x092E},
+{"magujarati", 0x0AAE},
+{"magurmukhi", 0x0A2E},
+{"mahapakhhebrew", 0x05A4},
+{"mahapakhlefthebrew", 0x05A4},
+{"mahiragana", 0x307E},
+{"maichattawalowleftthai", 0xF895},
+{"maichattawalowrightthai", 0xF894},
+{"maichattawathai", 0x0E4B},
+{"maichattawaupperleftthai", 0xF893},
+{"maieklowleftthai", 0xF88C},
+{"maieklowrightthai", 0xF88B},
+{"maiekthai", 0x0E48},
+{"maiekupperleftthai", 0xF88A},
+{"maihanakatleftthai", 0xF884},
+{"maihanakatthai", 0x0E31},
+{"maitaikhuleftthai", 0xF889},
+{"maitaikhuthai", 0x0E47},
+{"maitholowleftthai", 0xF88F},
+{"maitholowrightthai", 0xF88E},
+{"maithothai", 0x0E49},
+{"maithoupperleftthai", 0xF88D},
+{"maitrilowleftthai", 0xF892},
+{"maitrilowrightthai", 0xF891},
+{"maitrithai", 0x0E4A},
+{"maitriupperleftthai", 0xF890},
+{"maiyamokthai", 0x0E46},
+{"makatakana", 0x30DE},
+{"makatakanahalfwidth", 0xFF8F},
+{"male", 0x2642},
+{"mansyonsquare", 0x3347},
+{"maqafhebrew", 0x05BE},
+{"mars", 0x2642},
+{"masoracirclehebrew", 0x05AF},
+{"masquare", 0x3383},
+{"mbopomofo", 0x3107},
+{"mbsquare", 0x33D4},
+{"mcircle", 0x24DC},
+{"mcubedsquare", 0x33A5},
+{"mdotaccent", 0x1E41},
+{"mdotbelow", 0x1E43},
+{"meemarabic", 0x0645},
+{"meemfinalarabic", 0xFEE2},
+{"meeminitialarabic", 0xFEE3},
+{"meemmedialarabic", 0xFEE4},
+{"meemmeeminitialarabic", 0xFCD1},
+{"meemmeemisolatedarabic", 0xFC48},
+{"meetorusquare", 0x334D},
+{"mehiragana", 0x3081},
+{"meizierasquare", 0x337E},
+{"mekatakana", 0x30E1},
+{"mekatakanahalfwidth", 0xFF92},
+{"mem", 0x05DE},
+{"memdagesh", 0xFB3E},
+{"memdageshhebrew", 0xFB3E},
+{"memhebrew", 0x05DE},
+{"menarmenian", 0x0574},
+{"merkhahebrew", 0x05A5},
+{"merkhakefulahebrew", 0x05A6},
+{"merkhakefulalefthebrew", 0x05A6},
+{"merkhalefthebrew", 0x05A5},
+{"mhook", 0x0271},
+{"mhzsquare", 0x3392},
+{"middledotkatakanahalfwidth", 0xFF65},
+{"middot", 0x00B7},
+{"mieumacirclekorean", 0x3272},
+{"mieumaparenkorean", 0x3212},
+{"mieumcirclekorean", 0x3264},
+{"mieumkorean", 0x3141},
+{"mieumpansioskorean", 0x3170},
+{"mieumparenkorean", 0x3204},
+{"mieumpieupkorean", 0x316E},
+{"mieumsioskorean", 0x316F},
+{"mihiragana", 0x307F},
+{"mikatakana", 0x30DF},
+{"mikatakanahalfwidth", 0xFF90},
+{"minus", 0x2212},
+{"minusbelowcmb", 0x0320},
+{"minuscircle", 0x2296},
+{"minusmod", 0x02D7},
+{"minusplus", 0x2213},
+{"minute", 0x2032},
+{"miribaarusquare", 0x334A},
+{"mirisquare", 0x3349},
+{"mlonglegturned", 0x0270},
+{"mlsquare", 0x3396},
+{"mmcubedsquare", 0x33A3},
+{"mmonospace", 0xFF4D},
+{"mmsquaredsquare", 0x339F},
+{"mohiragana", 0x3082},
+{"mohmsquare", 0x33C1},
+{"mokatakana", 0x30E2},
+{"mokatakanahalfwidth", 0xFF93},
+{"molsquare", 0x33D6},
+{"momathai", 0x0E21},
+{"moverssquare", 0x33A7},
+{"moverssquaredsquare", 0x33A8},
+{"mparen", 0x24A8},
+{"mpasquare", 0x33AB},
+{"mssquare", 0x33B3},
+{"msuperior", 0xF6EF},
+{"mturned", 0x026F},
+{"mu", 0x00B5},
+{"mu1", 0x00B5},
+{"muasquare", 0x3382},
+{"muchgreater", 0x226B},
+{"muchless", 0x226A},
+{"mufsquare", 0x338C},
+{"mugreek", 0x03BC},
+{"mugsquare", 0x338D},
+{"muhiragana", 0x3080},
+{"mukatakana", 0x30E0},
+{"mukatakanahalfwidth", 0xFF91},
+{"mulsquare", 0x3395},
+{"multiply", 0x00D7},
+{"mumsquare", 0x339B},
+{"munahhebrew", 0x05A3},
+{"munahlefthebrew", 0x05A3},
+{"musicalnote", 0x266A},
+{"musicalnotedbl", 0x266B},
+{"musicflatsign", 0x266D},
+{"musicsharpsign", 0x266F},
+{"mussquare", 0x33B2},
+{"muvsquare", 0x33B6},
+{"muwsquare", 0x33BC},
+{"mvmegasquare", 0x33B9},
+{"mvsquare", 0x33B7},
+{"mwmegasquare", 0x33BF},
+{"mwsquare", 0x33BD},
+{"n", 0x006E},
+{"nabengali", 0x09A8},
+{"nabla", 0x2207},
+{"nacute", 0x0144},
+{"nadeva", 0x0928},
+{"nagujarati", 0x0AA8},
+{"nagurmukhi", 0x0A28},
+{"nahiragana", 0x306A},
+{"nakatakana", 0x30CA},
+{"nakatakanahalfwidth", 0xFF85},
+{"napostrophe", 0x0149},
+{"nasquare", 0x3381},
+{"nbopomofo", 0x310B},
+{"nbspace", 0x00A0},
+{"ncaron", 0x0148},
+{"ncedilla", 0x0146},
+{"ncircle", 0x24DD},
+{"ncircumflexbelow", 0x1E4B},
+{"ncommaaccent", 0x0146},
+{"ndotaccent", 0x1E45},
+{"ndotbelow", 0x1E47},
+{"nehiragana", 0x306D},
+{"nekatakana", 0x30CD},
+{"nekatakanahalfwidth", 0xFF88},
+{"newsheqelsign", 0x20AA},
+{"nfsquare", 0x338B},
+{"ngabengali", 0x0999},
+{"ngadeva", 0x0919},
+{"ngagujarati", 0x0A99},
+{"ngagurmukhi", 0x0A19},
+{"ngonguthai", 0x0E07},
+{"nhiragana", 0x3093},
+{"nhookleft", 0x0272},
+{"nhookretroflex", 0x0273},
+{"nieunacirclekorean", 0x326F},
+{"nieunaparenkorean", 0x320F},
+{"nieuncieuckorean", 0x3135},
+{"nieuncirclekorean", 0x3261},
+{"nieunhieuhkorean", 0x3136},
+{"nieunkorean", 0x3134},
+{"nieunpansioskorean", 0x3168},
+{"nieunparenkorean", 0x3201},
+{"nieunsioskorean", 0x3167},
+{"nieuntikeutkorean", 0x3166},
+{"nihiragana", 0x306B},
+{"nikatakana", 0x30CB},
+{"nikatakanahalfwidth", 0xFF86},
+{"nikhahitleftthai", 0xF899},
+{"nikhahitthai", 0x0E4D},
+{"nine", 0x0039},
+{"ninearabic", 0x0669},
+{"ninebengali", 0x09EF},
+{"ninecircle", 0x2468},
+{"ninecircleinversesansserif", 0x2792},
+{"ninedeva", 0x096F},
+{"ninegujarati", 0x0AEF},
+{"ninegurmukhi", 0x0A6F},
+{"ninehackarabic", 0x0669},
+{"ninehangzhou", 0x3029},
+{"nineideographicparen", 0x3228},
+{"nineinferior", 0x2089},
+{"ninemonospace", 0xFF19},
+{"nineoldstyle", 0xF739},
+{"nineparen", 0x247C},
+{"nineperiod", 0x2490},
+{"ninepersian", 0x06F9},
+{"nineroman", 0x2178},
+{"ninesuperior", 0x2079},
+{"nineteencircle", 0x2472},
+{"nineteenparen", 0x2486},
+{"nineteenperiod", 0x249A},
+{"ninethai", 0x0E59},
+{"nj", 0x01CC},
+{"njecyrillic", 0x045A},
+{"nkatakana", 0x30F3},
+{"nkatakanahalfwidth", 0xFF9D},
+{"nlegrightlong", 0x019E},
+{"nlinebelow", 0x1E49},
+{"nmonospace", 0xFF4E},
+{"nmsquare", 0x339A},
+{"nnabengali", 0x09A3},
+{"nnadeva", 0x0923},
+{"nnagujarati", 0x0AA3},
+{"nnagurmukhi", 0x0A23},
+{"nnnadeva", 0x0929},
+{"nohiragana", 0x306E},
+{"nokatakana", 0x30CE},
+{"nokatakanahalfwidth", 0xFF89},
+{"nonbreakingspace", 0x00A0},
+{"nonenthai", 0x0E13},
+{"nonuthai", 0x0E19},
+{"noonarabic", 0x0646},
+{"noonfinalarabic", 0xFEE6},
+{"noonghunnaarabic", 0x06BA},
+{"noonghunnafinalarabic", 0xFB9F},
+{"nooninitialarabic", 0xFEE7},
+{"noonjeeminitialarabic", 0xFCD2},
+{"noonjeemisolatedarabic", 0xFC4B},
+{"noonmedialarabic", 0xFEE8},
+{"noonmeeminitialarabic", 0xFCD5},
+{"noonmeemisolatedarabic", 0xFC4E},
+{"noonnoonfinalarabic", 0xFC8D},
+{"notcontains", 0x220C},
+{"notelement", 0x2209},
+{"notelementof", 0x2209},
+{"notequal", 0x2260},
+{"notgreater", 0x226F},
+{"notgreaternorequal", 0x2271},
+{"notgreaternorless", 0x2279},
+{"notidentical", 0x2262},
+{"notless", 0x226E},
+{"notlessnorequal", 0x2270},
+{"notparallel", 0x2226},
+{"notprecedes", 0x2280},
+{"notsubset", 0x2284},
+{"notsucceeds", 0x2281},
+{"notsuperset", 0x2285},
+{"nowarmenian", 0x0576},
+{"nparen", 0x24A9},
+{"nssquare", 0x33B1},
+{"nsuperior", 0x207F},
+{"ntilde", 0x00F1},
+{"nu", 0x03BD},
+{"nuhiragana", 0x306C},
+{"nukatakana", 0x30CC},
+{"nukatakanahalfwidth", 0xFF87},
+{"nuktabengali", 0x09BC},
+{"nuktadeva", 0x093C},
+{"nuktagujarati", 0x0ABC},
+{"nuktagurmukhi", 0x0A3C},
+{"numbersign", 0x0023},
+{"numbersignmonospace", 0xFF03},
+{"numbersignsmall", 0xFE5F},
+{"numeralsigngreek", 0x0374},
+{"numeralsignlowergreek", 0x0375},
+{"numero", 0x2116},
+{"nun", 0x05E0},
+{"nundagesh", 0xFB40},
+{"nundageshhebrew", 0xFB40},
+{"nunhebrew", 0x05E0},
+{"nvsquare", 0x33B5},
+{"nwsquare", 0x33BB},
+{"nyabengali", 0x099E},
+{"nyadeva", 0x091E},
+{"nyagujarati", 0x0A9E},
+{"nyagurmukhi", 0x0A1E},
+{"o", 0x006F},
+{"oacute", 0x00F3},
+{"oangthai", 0x0E2D},
+{"obarred", 0x0275},
+{"obarredcyrillic", 0x04E9},
+{"obarreddieresiscyrillic", 0x04EB},
+{"obengali", 0x0993},
+{"obopomofo", 0x311B},
+{"obreve", 0x014F},
+{"ocandradeva", 0x0911},
+{"ocandragujarati", 0x0A91},
+{"ocandravowelsigndeva", 0x0949},
+{"ocandravowelsigngujarati", 0x0AC9},
+{"ocaron", 0x01D2},
+{"ocircle", 0x24DE},
+{"ocircumflex", 0x00F4},
+{"ocircumflexacute", 0x1ED1},
+{"ocircumflexdotbelow", 0x1ED9},
+{"ocircumflexgrave", 0x1ED3},
+{"ocircumflexhookabove", 0x1ED5},
+{"ocircumflextilde", 0x1ED7},
+{"ocyrillic", 0x043E},
+{"odblacute", 0x0151},
+{"odblgrave", 0x020D},
+{"odeva", 0x0913},
+{"odieresis", 0x00F6},
+{"odieresiscyrillic", 0x04E7},
+{"odotbelow", 0x1ECD},
+{"oe", 0x0153},
+{"oekorean", 0x315A},
+{"ogonek", 0x02DB},
+{"ogonekcmb", 0x0328},
+{"ograve", 0x00F2},
+{"ogujarati", 0x0A93},
+{"oharmenian", 0x0585},
+{"ohiragana", 0x304A},
+{"ohookabove", 0x1ECF},
+{"ohorn", 0x01A1},
+{"ohornacute", 0x1EDB},
+{"ohorndotbelow", 0x1EE3},
+{"ohorngrave", 0x1EDD},
+{"ohornhookabove", 0x1EDF},
+{"ohorntilde", 0x1EE1},
+{"ohungarumlaut", 0x0151},
+{"oi", 0x01A3},
+{"oinvertedbreve", 0x020F},
+{"okatakana", 0x30AA},
+{"okatakanahalfwidth", 0xFF75},
+{"okorean", 0x3157},
+{"olehebrew", 0x05AB},
+{"omacron", 0x014D},
+{"omacronacute", 0x1E53},
+{"omacrongrave", 0x1E51},
+{"omdeva", 0x0950},
+{"omega", 0x03C9},
+{"omega1", 0x03D6},
+{"omegacyrillic", 0x0461},
+{"omegalatinclosed", 0x0277},
+{"omegaroundcyrillic", 0x047B},
+{"omegatitlocyrillic", 0x047D},
+{"omegatonos", 0x03CE},
+{"omgujarati", 0x0AD0},
+{"omicron", 0x03BF},
+{"omicrontonos", 0x03CC},
+{"omonospace", 0xFF4F},
+{"one", 0x0031},
+{"onearabic", 0x0661},
+{"onebengali", 0x09E7},
+{"onecircle", 0x2460},
+{"onecircleinversesansserif", 0x278A},
+{"onedeva", 0x0967},
+{"onedotenleader", 0x2024},
+{"oneeighth", 0x215B},
+{"onefitted", 0xF6DC},
+{"onegujarati", 0x0AE7},
+{"onegurmukhi", 0x0A67},
+{"onehackarabic", 0x0661},
+{"onehalf", 0x00BD},
+{"onehangzhou", 0x3021},
+{"oneideographicparen", 0x3220},
+{"oneinferior", 0x2081},
+{"onemonospace", 0xFF11},
+{"onenumeratorbengali", 0x09F4},
+{"oneoldstyle", 0xF731},
+{"oneparen", 0x2474},
+{"oneperiod", 0x2488},
+{"onepersian", 0x06F1},
+{"onequarter", 0x00BC},
+{"oneroman", 0x2170},
+{"onesuperior", 0x00B9},
+{"onethai", 0x0E51},
+{"onethird", 0x2153},
+{"oogonek", 0x01EB},
+{"oogonekmacron", 0x01ED},
+{"oogurmukhi", 0x0A13},
+{"oomatragurmukhi", 0x0A4B},
+{"oopen", 0x0254},
+{"oparen", 0x24AA},
+{"openbullet", 0x25E6},
+{"option", 0x2325},
+{"ordfeminine", 0x00AA},
+{"ordmasculine", 0x00BA},
+{"orthogonal", 0x221F},
+{"oshortdeva", 0x0912},
+{"oshortvowelsigndeva", 0x094A},
+{"oslash", 0x00F8},
+{"oslashacute", 0x01FF},
+{"osmallhiragana", 0x3049},
+{"osmallkatakana", 0x30A9},
+{"osmallkatakanahalfwidth", 0xFF6B},
+{"ostrokeacute", 0x01FF},
+{"osuperior", 0xF6F0},
+{"otcyrillic", 0x047F},
+{"otilde", 0x00F5},
+{"otildeacute", 0x1E4D},
+{"otildedieresis", 0x1E4F},
+{"oubopomofo", 0x3121},
+{"overline", 0x203E},
+{"overlinecenterline", 0xFE4A},
+{"overlinecmb", 0x0305},
+{"overlinedashed", 0xFE49},
+{"overlinedblwavy", 0xFE4C},
+{"overlinewavy", 0xFE4B},
+{"overscore", 0x00AF},
+{"ovowelsignbengali", 0x09CB},
+{"ovowelsigndeva", 0x094B},
+{"ovowelsigngujarati", 0x0ACB},
+{"p", 0x0070},
+{"paampssquare", 0x3380},
+{"paasentosquare", 0x332B},
+{"pabengali", 0x09AA},
+{"pacute", 0x1E55},
+{"padeva", 0x092A},
+{"pagedown", 0x21DF},
+{"pageup", 0x21DE},
+{"pagujarati", 0x0AAA},
+{"pagurmukhi", 0x0A2A},
+{"pahiragana", 0x3071},
+{"paiyannoithai", 0x0E2F},
+{"pakatakana", 0x30D1},
+{"palatalizationcyrilliccmb", 0x0484},
+{"palochkacyrillic", 0x04C0},
+{"pansioskorean", 0x317F},
+{"paragraph", 0x00B6},
+{"parallel", 0x2225},
+{"parenleft", 0x0028},
+{"parenleftaltonearabic", 0xFD3E},
+{"parenleftbt", 0xF8ED},
+{"parenleftex", 0xF8EC},
+{"parenleftinferior", 0x208D},
+{"parenleftmonospace", 0xFF08},
+{"parenleftsmall", 0xFE59},
+{"parenleftsuperior", 0x207D},
+{"parenlefttp", 0xF8EB},
+{"parenleftvertical", 0xFE35},
+{"parenright", 0x0029},
+{"parenrightaltonearabic", 0xFD3F},
+{"parenrightbt", 0xF8F8},
+{"parenrightex", 0xF8F7},
+{"parenrightinferior", 0x208E},
+{"parenrightmonospace", 0xFF09},
+{"parenrightsmall", 0xFE5A},
+{"parenrightsuperior", 0x207E},
+{"parenrighttp", 0xF8F6},
+{"parenrightvertical", 0xFE36},
+{"partialdiff", 0x2202},
+{"paseqhebrew", 0x05C0},
+{"pashtahebrew", 0x0599},
+{"pasquare", 0x33A9},
+{"patah", 0x05B7},
+{"patah11", 0x05B7},
+{"patah1d", 0x05B7},
+{"patah2a", 0x05B7},
+{"patahhebrew", 0x05B7},
+{"patahnarrowhebrew", 0x05B7},
+{"patahquarterhebrew", 0x05B7},
+{"patahwidehebrew", 0x05B7},
+{"pazerhebrew", 0x05A1},
+{"pbopomofo", 0x3106},
+{"pcircle", 0x24DF},
+{"pdotaccent", 0x1E57},
+{"pe", 0x05E4},
+{"pecyrillic", 0x043F},
+{"pedagesh", 0xFB44},
+{"pedageshhebrew", 0xFB44},
+{"peezisquare", 0x333B},
+{"pefinaldageshhebrew", 0xFB43},
+{"peharabic", 0x067E},
+{"peharmenian", 0x057A},
+{"pehebrew", 0x05E4},
+{"pehfinalarabic", 0xFB57},
+{"pehinitialarabic", 0xFB58},
+{"pehiragana", 0x307A},
+{"pehmedialarabic", 0xFB59},
+{"pekatakana", 0x30DA},
+{"pemiddlehookcyrillic", 0x04A7},
+{"perafehebrew", 0xFB4E},
+{"percent", 0x0025},
+{"percentarabic", 0x066A},
+{"percentmonospace", 0xFF05},
+{"percentsmall", 0xFE6A},
+{"period", 0x002E},
+{"periodarmenian", 0x0589},
+{"periodcentered", 0x00B7},
+{"periodhalfwidth", 0xFF61},
+{"periodinferior", 0xF6E7},
+{"periodmonospace", 0xFF0E},
+{"periodsmall", 0xFE52},
+{"periodsuperior", 0xF6E8},
+{"perispomenigreekcmb", 0x0342},
+{"perpendicular", 0x22A5},
+{"perthousand", 0x2030},
+{"peseta", 0x20A7},
+{"pfsquare", 0x338A},
+{"phabengali", 0x09AB},
+{"phadeva", 0x092B},
+{"phagujarati", 0x0AAB},
+{"phagurmukhi", 0x0A2B},
+{"phi", 0x03C6},
+{"phi1", 0x03D5},
+{"phieuphacirclekorean", 0x327A},
+{"phieuphaparenkorean", 0x321A},
+{"phieuphcirclekorean", 0x326C},
+{"phieuphkorean", 0x314D},
+{"phieuphparenkorean", 0x320C},
+{"philatin", 0x0278},
+{"phinthuthai", 0x0E3A},
+{"phisymbolgreek", 0x03D5},
+{"phook", 0x01A5},
+{"phophanthai", 0x0E1E},
+{"phophungthai", 0x0E1C},
+{"phosamphaothai", 0x0E20},
+{"pi", 0x03C0},
+{"pieupacirclekorean", 0x3273},
+{"pieupaparenkorean", 0x3213},
+{"pieupcieuckorean", 0x3176},
+{"pieupcirclekorean", 0x3265},
+{"pieupkiyeokkorean", 0x3172},
+{"pieupkorean", 0x3142},
+{"pieupparenkorean", 0x3205},
+{"pieupsioskiyeokkorean", 0x3174},
+{"pieupsioskorean", 0x3144},
+{"pieupsiostikeutkorean", 0x3175},
+{"pieupthieuthkorean", 0x3177},
+{"pieuptikeutkorean", 0x3173},
+{"pihiragana", 0x3074},
+{"pikatakana", 0x30D4},
+{"pisymbolgreek", 0x03D6},
+{"piwrarmenian", 0x0583},
+{"plus", 0x002B},
+{"plusbelowcmb", 0x031F},
+{"pluscircle", 0x2295},
+{"plusminus", 0x00B1},
+{"plusmod", 0x02D6},
+{"plusmonospace", 0xFF0B},
+{"plussmall", 0xFE62},
+{"plussuperior", 0x207A},
+{"pmonospace", 0xFF50},
+{"pmsquare", 0x33D8},
+{"pohiragana", 0x307D},
+{"pointingindexdownwhite", 0x261F},
+{"pointingindexleftwhite", 0x261C},
+{"pointingindexrightwhite", 0x261E},
+{"pointingindexupwhite", 0x261D},
+{"pokatakana", 0x30DD},
+{"poplathai", 0x0E1B},
+{"postalmark", 0x3012},
+{"postalmarkface", 0x3020},
+{"pparen", 0x24AB},
+{"precedes", 0x227A},
+{"prescription", 0x211E},
+{"primemod", 0x02B9},
+{"primereversed", 0x2035},
+{"product", 0x220F},
+{"projective", 0x2305},
+{"prolongedkana", 0x30FC},
+{"propellor", 0x2318},
+{"propersubset", 0x2282},
+{"propersuperset", 0x2283},
+{"proportion", 0x2237},
+{"proportional", 0x221D},
+{"psi", 0x03C8},
+{"psicyrillic", 0x0471},
+{"psilipneumatacyrilliccmb", 0x0486},
+{"pssquare", 0x33B0},
+{"puhiragana", 0x3077},
+{"pukatakana", 0x30D7},
+{"pvsquare", 0x33B4},
+{"pwsquare", 0x33BA},
+{"q", 0x0071},
+{"qadeva", 0x0958},
+{"qadmahebrew", 0x05A8},
+{"qafarabic", 0x0642},
+{"qaffinalarabic", 0xFED6},
+{"qafinitialarabic", 0xFED7},
+{"qafmedialarabic", 0xFED8},
+{"qamats", 0x05B8},
+{"qamats10", 0x05B8},
+{"qamats1a", 0x05B8},
+{"qamats1c", 0x05B8},
+{"qamats27", 0x05B8},
+{"qamats29", 0x05B8},
+{"qamats33", 0x05B8},
+{"qamatsde", 0x05B8},
+{"qamatshebrew", 0x05B8},
+{"qamatsnarrowhebrew", 0x05B8},
+{"qamatsqatanhebrew", 0x05B8},
+{"qamatsqatannarrowhebrew", 0x05B8},
+{"qamatsqatanquarterhebrew", 0x05B8},
+{"qamatsqatanwidehebrew", 0x05B8},
+{"qamatsquarterhebrew", 0x05B8},
+{"qamatswidehebrew", 0x05B8},
+{"qarneyparahebrew", 0x059F},
+{"qbopomofo", 0x3111},
+{"qcircle", 0x24E0},
+{"qhook", 0x02A0},
+{"qmonospace", 0xFF51},
+{"qof", 0x05E7},
+{"qofdagesh", 0xFB47},
+{"qofdageshhebrew", 0xFB47},
+{"qofhebrew", 0x05E7},
+{"qparen", 0x24AC},
+{"quarternote", 0x2669},
+{"qubuts", 0x05BB},
+{"qubuts18", 0x05BB},
+{"qubuts25", 0x05BB},
+{"qubuts31", 0x05BB},
+{"qubutshebrew", 0x05BB},
+{"qubutsnarrowhebrew", 0x05BB},
+{"qubutsquarterhebrew", 0x05BB},
+{"qubutswidehebrew", 0x05BB},
+{"question", 0x003F},
+{"questionarabic", 0x061F},
+{"questionarmenian", 0x055E},
+{"questiondown", 0x00BF},
+{"questiondownsmall", 0xF7BF},
+{"questiongreek", 0x037E},
+{"questionmonospace", 0xFF1F},
+{"questionsmall", 0xF73F},
+{"quotedbl", 0x0022},
+{"quotedblbase", 0x201E},
+{"quotedblleft", 0x201C},
+{"quotedblmonospace", 0xFF02},
+{"quotedblprime", 0x301E},
+{"quotedblprimereversed", 0x301D},
+{"quotedblright", 0x201D},
+{"quoteleft", 0x2018},
+{"quoteleftreversed", 0x201B},
+{"quotereversed", 0x201B},
+{"quoteright", 0x2019},
+{"quoterightn", 0x0149},
+{"quotesinglbase", 0x201A},
+{"quotesingle", 0x0027},
+{"quotesinglemonospace", 0xFF07},
+{"r", 0x0072},
+{"raarmenian", 0x057C},
+{"rabengali", 0x09B0},
+{"racute", 0x0155},
+{"radeva", 0x0930},
+{"radical", 0x221A},
+{"radicalex", 0xF8E5},
+{"radoverssquare", 0x33AE},
+{"radoverssquaredsquare", 0x33AF},
+{"radsquare", 0x33AD},
+{"rafe", 0x05BF},
+{"rafehebrew", 0x05BF},
+{"ragujarati", 0x0AB0},
+{"ragurmukhi", 0x0A30},
+{"rahiragana", 0x3089},
+{"rakatakana", 0x30E9},
+{"rakatakanahalfwidth", 0xFF97},
+{"ralowerdiagonalbengali", 0x09F1},
+{"ramiddlediagonalbengali", 0x09F0},
+{"ramshorn", 0x0264},
+{"ratio", 0x2236},
+{"rbopomofo", 0x3116},
+{"rcaron", 0x0159},
+{"rcedilla", 0x0157},
+{"rcircle", 0x24E1},
+{"rcommaaccent", 0x0157},
+{"rdblgrave", 0x0211},
+{"rdotaccent", 0x1E59},
+{"rdotbelow", 0x1E5B},
+{"rdotbelowmacron", 0x1E5D},
+{"referencemark", 0x203B},
+{"reflexsubset", 0x2286},
+{"reflexsuperset", 0x2287},
+{"registered", 0x00AE},
+{"registersans", 0xF8E8},
+{"registerserif", 0xF6DA},
+{"reharabic", 0x0631},
+{"reharmenian", 0x0580},
+{"rehfinalarabic", 0xFEAE},
+{"rehiragana", 0x308C},
+{"rekatakana", 0x30EC},
+{"rekatakanahalfwidth", 0xFF9A},
+{"resh", 0x05E8},
+{"reshdageshhebrew", 0xFB48},
+{"reshhebrew", 0x05E8},
+{"reversedtilde", 0x223D},
+{"reviahebrew", 0x0597},
+{"reviamugrashhebrew", 0x0597},
+{"revlogicalnot", 0x2310},
+{"rfishhook", 0x027E},
+{"rfishhookreversed", 0x027F},
+{"rhabengali", 0x09DD},
+{"rhadeva", 0x095D},
+{"rho", 0x03C1},
+{"rhook", 0x027D},
+{"rhookturned", 0x027B},
+{"rhookturnedsuperior", 0x02B5},
+{"rhosymbolgreek", 0x03F1},
+{"rhotichookmod", 0x02DE},
+{"rieulacirclekorean", 0x3271},
+{"rieulaparenkorean", 0x3211},
+{"rieulcirclekorean", 0x3263},
+{"rieulhieuhkorean", 0x3140},
+{"rieulkiyeokkorean", 0x313A},
+{"rieulkiyeoksioskorean", 0x3169},
+{"rieulkorean", 0x3139},
+{"rieulmieumkorean", 0x313B},
+{"rieulpansioskorean", 0x316C},
+{"rieulparenkorean", 0x3203},
+{"rieulphieuphkorean", 0x313F},
+{"rieulpieupkorean", 0x313C},
+{"rieulpieupsioskorean", 0x316B},
+{"rieulsioskorean", 0x313D},
+{"rieulthieuthkorean", 0x313E},
+{"rieultikeutkorean", 0x316A},
+{"rieulyeorinhieuhkorean", 0x316D},
+{"rightangle", 0x221F},
+{"righttackbelowcmb", 0x0319},
+{"righttriangle", 0x22BF},
+{"rihiragana", 0x308A},
+{"rikatakana", 0x30EA},
+{"rikatakanahalfwidth", 0xFF98},
+{"ring", 0x02DA},
+{"ringbelowcmb", 0x0325},
+{"ringcmb", 0x030A},
+{"ringhalfleft", 0x02BF},
+{"ringhalfleftarmenian", 0x0559},
+{"ringhalfleftbelowcmb", 0x031C},
+{"ringhalfleftcentered", 0x02D3},
+{"ringhalfright", 0x02BE},
+{"ringhalfrightbelowcmb", 0x0339},
+{"ringhalfrightcentered", 0x02D2},
+{"rinvertedbreve", 0x0213},
+{"rittorusquare", 0x3351},
+{"rlinebelow", 0x1E5F},
+{"rlongleg", 0x027C},
+{"rlonglegturned", 0x027A},
+{"rmonospace", 0xFF52},
+{"rohiragana", 0x308D},
+{"rokatakana", 0x30ED},
+{"rokatakanahalfwidth", 0xFF9B},
+{"roruathai", 0x0E23},
+{"rparen", 0x24AD},
+{"rrabengali", 0x09DC},
+{"rradeva", 0x0931},
+{"rragurmukhi", 0x0A5C},
+{"rreharabic", 0x0691},
+{"rrehfinalarabic", 0xFB8D},
+{"rrvocalicbengali", 0x09E0},
+{"rrvocalicdeva", 0x0960},
+{"rrvocalicgujarati", 0x0AE0},
+{"rrvocalicvowelsignbengali", 0x09C4},
+{"rrvocalicvowelsigndeva", 0x0944},
+{"rrvocalicvowelsigngujarati", 0x0AC4},
+{"rsuperior", 0xF6F1},
+{"rtblock", 0x2590},
+{"rturned", 0x0279},
+{"rturnedsuperior", 0x02B4},
+{"ruhiragana", 0x308B},
+{"rukatakana", 0x30EB},
+{"rukatakanahalfwidth", 0xFF99},
+{"rupeemarkbengali", 0x09F2},
+{"rupeesignbengali", 0x09F3},
+{"rupiah", 0xF6DD},
+{"ruthai", 0x0E24},
+{"rvocalicbengali", 0x098B},
+{"rvocalicdeva", 0x090B},
+{"rvocalicgujarati", 0x0A8B},
+{"rvocalicvowelsignbengali", 0x09C3},
+{"rvocalicvowelsigndeva", 0x0943},
+{"rvocalicvowelsigngujarati", 0x0AC3},
+{"s", 0x0073},
+{"sabengali", 0x09B8},
+{"sacute", 0x015B},
+{"sacutedotaccent", 0x1E65},
+{"sadarabic", 0x0635},
+{"sadeva", 0x0938},
+{"sadfinalarabic", 0xFEBA},
+{"sadinitialarabic", 0xFEBB},
+{"sadmedialarabic", 0xFEBC},
+{"sagujarati", 0x0AB8},
+{"sagurmukhi", 0x0A38},
+{"sahiragana", 0x3055},
+{"sakatakana", 0x30B5},
+{"sakatakanahalfwidth", 0xFF7B},
+{"sallallahoualayhewasallamarabic", 0xFDFA},
+{"samekh", 0x05E1},
+{"samekhdagesh", 0xFB41},
+{"samekhdageshhebrew", 0xFB41},
+{"samekhhebrew", 0x05E1},
+{"saraaathai", 0x0E32},
+{"saraaethai", 0x0E41},
+{"saraaimaimalaithai", 0x0E44},
+{"saraaimaimuanthai", 0x0E43},
+{"saraamthai", 0x0E33},
+{"saraathai", 0x0E30},
+{"saraethai", 0x0E40},
+{"saraiileftthai", 0xF886},
+{"saraiithai", 0x0E35},
+{"saraileftthai", 0xF885},
+{"saraithai", 0x0E34},
+{"saraothai", 0x0E42},
+{"saraueeleftthai", 0xF888},
+{"saraueethai", 0x0E37},
+{"saraueleftthai", 0xF887},
+{"sarauethai", 0x0E36},
+{"sarauthai", 0x0E38},
+{"sarauuthai", 0x0E39},
+{"sbopomofo", 0x3119},
+{"scaron", 0x0161},
+{"scarondotaccent", 0x1E67},
+{"scedilla", 0x015F},
+{"schwa", 0x0259},
+{"schwacyrillic", 0x04D9},
+{"schwadieresiscyrillic", 0x04DB},
+{"schwahook", 0x025A},
+{"scircle", 0x24E2},
+{"scircumflex", 0x015D},
+{"scommaaccent", 0x0219},
+{"sdotaccent", 0x1E61},
+{"sdotbelow", 0x1E63},
+{"sdotbelowdotaccent", 0x1E69},
+{"seagullbelowcmb", 0x033C},
+{"second", 0x2033},
+{"secondtonechinese", 0x02CA},
+{"section", 0x00A7},
+{"seenarabic", 0x0633},
+{"seenfinalarabic", 0xFEB2},
+{"seeninitialarabic", 0xFEB3},
+{"seenmedialarabic", 0xFEB4},
+{"segol", 0x05B6},
+{"segol13", 0x05B6},
+{"segol1f", 0x05B6},
+{"segol2c", 0x05B6},
+{"segolhebrew", 0x05B6},
+{"segolnarrowhebrew", 0x05B6},
+{"segolquarterhebrew", 0x05B6},
+{"segoltahebrew", 0x0592},
+{"segolwidehebrew", 0x05B6},
+{"seharmenian", 0x057D},
+{"sehiragana", 0x305B},
+{"sekatakana", 0x30BB},
+{"sekatakanahalfwidth", 0xFF7E},
+{"semicolon", 0x003B},
+{"semicolonarabic", 0x061B},
+{"semicolonmonospace", 0xFF1B},
+{"semicolonsmall", 0xFE54},
+{"semivoicedmarkkana", 0x309C},
+{"semivoicedmarkkanahalfwidth", 0xFF9F},
+{"sentisquare", 0x3322},
+{"sentosquare", 0x3323},
+{"seven", 0x0037},
+{"sevenarabic", 0x0667},
+{"sevenbengali", 0x09ED},
+{"sevencircle", 0x2466},
+{"sevencircleinversesansserif", 0x2790},
+{"sevendeva", 0x096D},
+{"seveneighths", 0x215E},
+{"sevengujarati", 0x0AED},
+{"sevengurmukhi", 0x0A6D},
+{"sevenhackarabic", 0x0667},
+{"sevenhangzhou", 0x3027},
+{"sevenideographicparen", 0x3226},
+{"seveninferior", 0x2087},
+{"sevenmonospace", 0xFF17},
+{"sevenoldstyle", 0xF737},
+{"sevenparen", 0x247A},
+{"sevenperiod", 0x248E},
+{"sevenpersian", 0x06F7},
+{"sevenroman", 0x2176},
+{"sevensuperior", 0x2077},
+{"seventeencircle", 0x2470},
+{"seventeenparen", 0x2484},
+{"seventeenperiod", 0x2498},
+{"seventhai", 0x0E57},
+{"sfthyphen", 0x00AD},
+{"shaarmenian", 0x0577},
+{"shabengali", 0x09B6},
+{"shacyrillic", 0x0448},
+{"shaddaarabic", 0x0651},
+{"shaddadammaarabic", 0xFC61},
+{"shaddadammatanarabic", 0xFC5E},
+{"shaddafathaarabic", 0xFC60},
+{"shaddakasraarabic", 0xFC62},
+{"shaddakasratanarabic", 0xFC5F},
+{"shade", 0x2592},
+{"shadedark", 0x2593},
+{"shadelight", 0x2591},
+{"shademedium", 0x2592},
+{"shadeva", 0x0936},
+{"shagujarati", 0x0AB6},
+{"shagurmukhi", 0x0A36},
+{"shalshelethebrew", 0x0593},
+{"shbopomofo", 0x3115},
+{"shchacyrillic", 0x0449},
+{"sheenarabic", 0x0634},
+{"sheenfinalarabic", 0xFEB6},
+{"sheeninitialarabic", 0xFEB7},
+{"sheenmedialarabic", 0xFEB8},
+{"sheicoptic", 0x03E3},
+{"sheqel", 0x20AA},
+{"sheqelhebrew", 0x20AA},
+{"sheva", 0x05B0},
+{"sheva115", 0x05B0},
+{"sheva15", 0x05B0},
+{"sheva22", 0x05B0},
+{"sheva2e", 0x05B0},
+{"shevahebrew", 0x05B0},
+{"shevanarrowhebrew", 0x05B0},
+{"shevaquarterhebrew", 0x05B0},
+{"shevawidehebrew", 0x05B0},
+{"shhacyrillic", 0x04BB},
+{"shimacoptic", 0x03ED},
+{"shin", 0x05E9},
+{"shindagesh", 0xFB49},
+{"shindageshhebrew", 0xFB49},
+{"shindageshshindot", 0xFB2C},
+{"shindageshshindothebrew", 0xFB2C},
+{"shindageshsindot", 0xFB2D},
+{"shindageshsindothebrew", 0xFB2D},
+{"shindothebrew", 0x05C1},
+{"shinhebrew", 0x05E9},
+{"shinshindot", 0xFB2A},
+{"shinshindothebrew", 0xFB2A},
+{"shinsindot", 0xFB2B},
+{"shinsindothebrew", 0xFB2B},
+{"shook", 0x0282},
+{"sigma", 0x03C3},
+{"sigma1", 0x03C2},
+{"sigmafinal", 0x03C2},
+{"sigmalunatesymbolgreek", 0x03F2},
+{"sihiragana", 0x3057},
+{"sikatakana", 0x30B7},
+{"sikatakanahalfwidth", 0xFF7C},
+{"siluqhebrew", 0x05BD},
+{"siluqlefthebrew", 0x05BD},
+{"similar", 0x223C},
+{"sindothebrew", 0x05C2},
+{"siosacirclekorean", 0x3274},
+{"siosaparenkorean", 0x3214},
+{"sioscieuckorean", 0x317E},
+{"sioscirclekorean", 0x3266},
+{"sioskiyeokkorean", 0x317A},
+{"sioskorean", 0x3145},
+{"siosnieunkorean", 0x317B},
+{"siosparenkorean", 0x3206},
+{"siospieupkorean", 0x317D},
+{"siostikeutkorean", 0x317C},
+{"six", 0x0036},
+{"sixarabic", 0x0666},
+{"sixbengali", 0x09EC},
+{"sixcircle", 0x2465},
+{"sixcircleinversesansserif", 0x278F},
+{"sixdeva", 0x096C},
+{"sixgujarati", 0x0AEC},
+{"sixgurmukhi", 0x0A6C},
+{"sixhackarabic", 0x0666},
+{"sixhangzhou", 0x3026},
+{"sixideographicparen", 0x3225},
+{"sixinferior", 0x2086},
+{"sixmonospace", 0xFF16},
+{"sixoldstyle", 0xF736},
+{"sixparen", 0x2479},
+{"sixperiod", 0x248D},
+{"sixpersian", 0x06F6},
+{"sixroman", 0x2175},
+{"sixsuperior", 0x2076},
+{"sixteencircle", 0x246F},
+{"sixteencurrencydenominatorbengali", 0x09F9},
+{"sixteenparen", 0x2483},
+{"sixteenperiod", 0x2497},
+{"sixthai", 0x0E56},
+{"slash", 0x002F},
+{"slashmonospace", 0xFF0F},
+{"slong", 0x017F},
+{"slongdotaccent", 0x1E9B},
+{"smileface", 0x263A},
+{"smonospace", 0xFF53},
+{"sofpasuqhebrew", 0x05C3},
+{"softhyphen", 0x00AD},
+{"softsigncyrillic", 0x044C},
+{"sohiragana", 0x305D},
+{"sokatakana", 0x30BD},
+{"sokatakanahalfwidth", 0xFF7F},
+{"soliduslongoverlaycmb", 0x0338},
+{"solidusshortoverlaycmb", 0x0337},
+{"sorusithai", 0x0E29},
+{"sosalathai", 0x0E28},
+{"sosothai", 0x0E0B},
+{"sosuathai", 0x0E2A},
+{"space", 0x0020},
+{"spacehackarabic", 0x0020},
+{"spade", 0x2660},
+{"spadesuitblack", 0x2660},
+{"spadesuitwhite", 0x2664},
+{"sparen", 0x24AE},
+{"squarebelowcmb", 0x033B},
+{"squarecc", 0x33C4},
+{"squarecm", 0x339D},
+{"squarediagonalcrosshatchfill", 0x25A9},
+{"squarehorizontalfill", 0x25A4},
+{"squarekg", 0x338F},
+{"squarekm", 0x339E},
+{"squarekmcapital", 0x33CE},
+{"squareln", 0x33D1},
+{"squarelog", 0x33D2},
+{"squaremg", 0x338E},
+{"squaremil", 0x33D5},
+{"squaremm", 0x339C},
+{"squaremsquared", 0x33A1},
+{"squareorthogonalcrosshatchfill", 0x25A6},
+{"squareupperlefttolowerrightfill", 0x25A7},
+{"squareupperrighttolowerleftfill", 0x25A8},
+{"squareverticalfill", 0x25A5},
+{"squarewhitewithsmallblack", 0x25A3},
+{"srsquare", 0x33DB},
+{"ssabengali", 0x09B7},
+{"ssadeva", 0x0937},
+{"ssagujarati", 0x0AB7},
+{"ssangcieuckorean", 0x3149},
+{"ssanghieuhkorean", 0x3185},
+{"ssangieungkorean", 0x3180},
+{"ssangkiyeokkorean", 0x3132},
+{"ssangnieunkorean", 0x3165},
+{"ssangpieupkorean", 0x3143},
+{"ssangsioskorean", 0x3146},
+{"ssangtikeutkorean", 0x3138},
+{"ssuperior", 0xF6F2},
+{"sterling", 0x00A3},
+{"sterlingmonospace", 0xFFE1},
+{"strokelongoverlaycmb", 0x0336},
+{"strokeshortoverlaycmb", 0x0335},
+{"subset", 0x2282},
+{"subsetnotequal", 0x228A},
+{"subsetorequal", 0x2286},
+{"succeeds", 0x227B},
+{"suchthat", 0x220B},
+{"suhiragana", 0x3059},
+{"sukatakana", 0x30B9},
+{"sukatakanahalfwidth", 0xFF7D},
+{"sukunarabic", 0x0652},
+{"summation", 0x2211},
+{"sun", 0x263C},
+{"superset", 0x2283},
+{"supersetnotequal", 0x228B},
+{"supersetorequal", 0x2287},
+{"svsquare", 0x33DC},
+{"syouwaerasquare", 0x337C},
+{"t", 0x0074},
+{"tabengali", 0x09A4},
+{"tackdown", 0x22A4},
+{"tackleft", 0x22A3},
+{"tadeva", 0x0924},
+{"tagujarati", 0x0AA4},
+{"tagurmukhi", 0x0A24},
+{"taharabic", 0x0637},
+{"tahfinalarabic", 0xFEC2},
+{"tahinitialarabic", 0xFEC3},
+{"tahiragana", 0x305F},
+{"tahmedialarabic", 0xFEC4},
+{"taisyouerasquare", 0x337D},
+{"takatakana", 0x30BF},
+{"takatakanahalfwidth", 0xFF80},
+{"tatweelarabic", 0x0640},
+{"tau", 0x03C4},
+{"tav", 0x05EA},
+{"tavdages", 0xFB4A},
+{"tavdagesh", 0xFB4A},
+{"tavdageshhebrew", 0xFB4A},
+{"tavhebrew", 0x05EA},
+{"tbar", 0x0167},
+{"tbopomofo", 0x310A},
+{"tcaron", 0x0165},
+{"tccurl", 0x02A8},
+{"tcedilla", 0x0163},
+{"tcheharabic", 0x0686},
+{"tchehfinalarabic", 0xFB7B},
+{"tchehinitialarabic", 0xFB7C},
+{"tchehmedialarabic", 0xFB7D},
+{"tcircle", 0x24E3},
+{"tcircumflexbelow", 0x1E71},
+{"tcommaaccent", 0x0163},
+{"tdieresis", 0x1E97},
+{"tdotaccent", 0x1E6B},
+{"tdotbelow", 0x1E6D},
+{"tecyrillic", 0x0442},
+{"tedescendercyrillic", 0x04AD},
+{"teharabic", 0x062A},
+{"tehfinalarabic", 0xFE96},
+{"tehhahinitialarabic", 0xFCA2},
+{"tehhahisolatedarabic", 0xFC0C},
+{"tehinitialarabic", 0xFE97},
+{"tehiragana", 0x3066},
+{"tehjeeminitialarabic", 0xFCA1},
+{"tehjeemisolatedarabic", 0xFC0B},
+{"tehmarbutaarabic", 0x0629},
+{"tehmarbutafinalarabic", 0xFE94},
+{"tehmedialarabic", 0xFE98},
+{"tehmeeminitialarabic", 0xFCA4},
+{"tehmeemisolatedarabic", 0xFC0E},
+{"tehnoonfinalarabic", 0xFC73},
+{"tekatakana", 0x30C6},
+{"tekatakanahalfwidth", 0xFF83},
+{"telephone", 0x2121},
+{"telephoneblack", 0x260E},
+{"telishagedolahebrew", 0x05A0},
+{"telishaqetanahebrew", 0x05A9},
+{"tencircle", 0x2469},
+{"tenideographicparen", 0x3229},
+{"tenparen", 0x247D},
+{"tenperiod", 0x2491},
+{"tenroman", 0x2179},
+{"tesh", 0x02A7},
+{"tet", 0x05D8},
+{"tetdagesh", 0xFB38},
+{"tetdageshhebrew", 0xFB38},
+{"tethebrew", 0x05D8},
+{"tetsecyrillic", 0x04B5},
+{"tevirhebrew", 0x059B},
+{"tevirlefthebrew", 0x059B},
+{"thabengali", 0x09A5},
+{"thadeva", 0x0925},
+{"thagujarati", 0x0AA5},
+{"thagurmukhi", 0x0A25},
+{"thalarabic", 0x0630},
+{"thalfinalarabic", 0xFEAC},
+{"thanthakhatlowleftthai", 0xF898},
+{"thanthakhatlowrightthai", 0xF897},
+{"thanthakhatthai", 0x0E4C},
+{"thanthakhatupperleftthai", 0xF896},
+{"theharabic", 0x062B},
+{"thehfinalarabic", 0xFE9A},
+{"thehinitialarabic", 0xFE9B},
+{"thehmedialarabic", 0xFE9C},
+{"thereexists", 0x2203},
+{"therefore", 0x2234},
+{"theta", 0x03B8},
+{"theta1", 0x03D1},
+{"thetasymbolgreek", 0x03D1},
+{"thieuthacirclekorean", 0x3279},
+{"thieuthaparenkorean", 0x3219},
+{"thieuthcirclekorean", 0x326B},
+{"thieuthkorean", 0x314C},
+{"thieuthparenkorean", 0x320B},
+{"thirteencircle", 0x246C},
+{"thirteenparen", 0x2480},
+{"thirteenperiod", 0x2494},
+{"thonangmonthothai", 0x0E11},
+{"thook", 0x01AD},
+{"thophuthaothai", 0x0E12},
+{"thorn", 0x00FE},
+{"thothahanthai", 0x0E17},
+{"thothanthai", 0x0E10},
+{"thothongthai", 0x0E18},
+{"thothungthai", 0x0E16},
+{"thousandcyrillic", 0x0482},
+{"thousandsseparatorarabic", 0x066C},
+{"thousandsseparatorpersian", 0x066C},
+{"three", 0x0033},
+{"threearabic", 0x0663},
+{"threebengali", 0x09E9},
+{"threecircle", 0x2462},
+{"threecircleinversesansserif", 0x278C},
+{"threedeva", 0x0969},
+{"threeeighths", 0x215C},
+{"threegujarati", 0x0AE9},
+{"threegurmukhi", 0x0A69},
+{"threehackarabic", 0x0663},
+{"threehangzhou", 0x3023},
+{"threeideographicparen", 0x3222},
+{"threeinferior", 0x2083},
+{"threemonospace", 0xFF13},
+{"threenumeratorbengali", 0x09F6},
+{"threeoldstyle", 0xF733},
+{"threeparen", 0x2476},
+{"threeperiod", 0x248A},
+{"threepersian", 0x06F3},
+{"threequarters", 0x00BE},
+{"threequartersemdash", 0xF6DE},
+{"threeroman", 0x2172},
+{"threesuperior", 0x00B3},
+{"threethai", 0x0E53},
+{"thzsquare", 0x3394},
+{"tihiragana", 0x3061},
+{"tikatakana", 0x30C1},
+{"tikatakanahalfwidth", 0xFF81},
+{"tikeutacirclekorean", 0x3270},
+{"tikeutaparenkorean", 0x3210},
+{"tikeutcirclekorean", 0x3262},
+{"tikeutkorean", 0x3137},
+{"tikeutparenkorean", 0x3202},
+{"tilde", 0x02DC},
+{"tildebelowcmb", 0x0330},
+{"tildecmb", 0x0303},
+{"tildecomb", 0x0303},
+{"tildedoublecmb", 0x0360},
+{"tildeoperator", 0x223C},
+{"tildeoverlaycmb", 0x0334},
+{"tildeverticalcmb", 0x033E},
+{"timescircle", 0x2297},
+{"tipehahebrew", 0x0596},
+{"tipehalefthebrew", 0x0596},
+{"tippigurmukhi", 0x0A70},
+{"titlocyrilliccmb", 0x0483},
+{"tiwnarmenian", 0x057F},
+{"tlinebelow", 0x1E6F},
+{"tmonospace", 0xFF54},
+{"toarmenian", 0x0569},
+{"tohiragana", 0x3068},
+{"tokatakana", 0x30C8},
+{"tokatakanahalfwidth", 0xFF84},
+{"tonebarextrahighmod", 0x02E5},
+{"tonebarextralowmod", 0x02E9},
+{"tonebarhighmod", 0x02E6},
+{"tonebarlowmod", 0x02E8},
+{"tonebarmidmod", 0x02E7},
+{"tonefive", 0x01BD},
+{"tonesix", 0x0185},
+{"tonetwo", 0x01A8},
+{"tonos", 0x0384},
+{"tonsquare", 0x3327},
+{"topatakthai", 0x0E0F},
+{"tortoiseshellbracketleft", 0x3014},
+{"tortoiseshellbracketleftsmall", 0xFE5D},
+{"tortoiseshellbracketleftvertical", 0xFE39},
+{"tortoiseshellbracketright", 0x3015},
+{"tortoiseshellbracketrightsmall", 0xFE5E},
+{"tortoiseshellbracketrightvertical", 0xFE3A},
+{"totaothai", 0x0E15},
+{"tpalatalhook", 0x01AB},
+{"tparen", 0x24AF},
+{"trademark", 0x2122},
+{"trademarksans", 0xF8EA},
+{"trademarkserif", 0xF6DB},
+{"tretroflexhook", 0x0288},
+{"triagdn", 0x25BC},
+{"triaglf", 0x25C4},
+{"triagrt", 0x25BA},
+{"triagup", 0x25B2},
+{"ts", 0x02A6},
+{"tsadi", 0x05E6},
+{"tsadidagesh", 0xFB46},
+{"tsadidageshhebrew", 0xFB46},
+{"tsadihebrew", 0x05E6},
+{"tsecyrillic", 0x0446},
+{"tsere", 0x05B5},
+{"tsere12", 0x05B5},
+{"tsere1e", 0x05B5},
+{"tsere2b", 0x05B5},
+{"tserehebrew", 0x05B5},
+{"tserenarrowhebrew", 0x05B5},
+{"tserequarterhebrew", 0x05B5},
+{"tserewidehebrew", 0x05B5},
+{"tshecyrillic", 0x045B},
+{"tsuperior", 0xF6F3},
+{"ttabengali", 0x099F},
+{"ttadeva", 0x091F},
+{"ttagujarati", 0x0A9F},
+{"ttagurmukhi", 0x0A1F},
+{"tteharabic", 0x0679},
+{"ttehfinalarabic", 0xFB67},
+{"ttehinitialarabic", 0xFB68},
+{"ttehmedialarabic", 0xFB69},
+{"tthabengali", 0x09A0},
+{"tthadeva", 0x0920},
+{"tthagujarati", 0x0AA0},
+{"tthagurmukhi", 0x0A20},
+{"tturned", 0x0287},
+{"tuhiragana", 0x3064},
+{"tukatakana", 0x30C4},
+{"tukatakanahalfwidth", 0xFF82},
+{"tusmallhiragana", 0x3063},
+{"tusmallkatakana", 0x30C3},
+{"tusmallkatakanahalfwidth", 0xFF6F},
+{"twelvecircle", 0x246B},
+{"twelveparen", 0x247F},
+{"twelveperiod", 0x2493},
+{"twelveroman", 0x217B},
+{"twentycircle", 0x2473},
+{"twentyhangzhou", 0x5344},
+{"twentyparen", 0x2487},
+{"twentyperiod", 0x249B},
+{"two", 0x0032},
+{"twoarabic", 0x0662},
+{"twobengali", 0x09E8},
+{"twocircle", 0x2461},
+{"twocircleinversesansserif", 0x278B},
+{"twodeva", 0x0968},
+{"twodotenleader", 0x2025},
+{"twodotleader", 0x2025},
+{"twodotleadervertical", 0xFE30},
+{"twogujarati", 0x0AE8},
+{"twogurmukhi", 0x0A68},
+{"twohackarabic", 0x0662},
+{"twohangzhou", 0x3022},
+{"twoideographicparen", 0x3221},
+{"twoinferior", 0x2082},
+{"twomonospace", 0xFF12},
+{"twonumeratorbengali", 0x09F5},
+{"twooldstyle", 0xF732},
+{"twoparen", 0x2475},
+{"twoperiod", 0x2489},
+{"twopersian", 0x06F2},
+{"tworoman", 0x2171},
+{"twostroke", 0x01BB},
+{"twosuperior", 0x00B2},
+{"twothai", 0x0E52},
+{"twothirds", 0x2154},
+{"u", 0x0075},
+{"uacute", 0x00FA},
+{"ubar", 0x0289},
+{"ubengali", 0x0989},
+{"ubopomofo", 0x3128},
+{"ubreve", 0x016D},
+{"ucaron", 0x01D4},
+{"ucircle", 0x24E4},
+{"ucircumflex", 0x00FB},
+{"ucircumflexbelow", 0x1E77},
+{"ucyrillic", 0x0443},
+{"udattadeva", 0x0951},
+{"udblacute", 0x0171},
+{"udblgrave", 0x0215},
+{"udeva", 0x0909},
+{"udieresis", 0x00FC},
+{"udieresisacute", 0x01D8},
+{"udieresisbelow", 0x1E73},
+{"udieresiscaron", 0x01DA},
+{"udieresiscyrillic", 0x04F1},
+{"udieresisgrave", 0x01DC},
+{"udieresismacron", 0x01D6},
+{"udotbelow", 0x1EE5},
+{"ugrave", 0x00F9},
+{"ugujarati", 0x0A89},
+{"ugurmukhi", 0x0A09},
+{"uhiragana", 0x3046},
+{"uhookabove", 0x1EE7},
+{"uhorn", 0x01B0},
+{"uhornacute", 0x1EE9},
+{"uhorndotbelow", 0x1EF1},
+{"uhorngrave", 0x1EEB},
+{"uhornhookabove", 0x1EED},
+{"uhorntilde", 0x1EEF},
+{"uhungarumlaut", 0x0171},
+{"uhungarumlautcyrillic", 0x04F3},
+{"uinvertedbreve", 0x0217},
+{"ukatakana", 0x30A6},
+{"ukatakanahalfwidth", 0xFF73},
+{"ukcyrillic", 0x0479},
+{"ukorean", 0x315C},
+{"umacron", 0x016B},
+{"umacroncyrillic", 0x04EF},
+{"umacrondieresis", 0x1E7B},
+{"umatragurmukhi", 0x0A41},
+{"umonospace", 0xFF55},
+{"underscore", 0x005F},
+{"underscoredbl", 0x2017},
+{"underscoremonospace", 0xFF3F},
+{"underscorevertical", 0xFE33},
+{"underscorewavy", 0xFE4F},
+{"union", 0x222A},
+{"universal", 0x2200},
+{"uogonek", 0x0173},
+{"uparen", 0x24B0},
+{"upblock", 0x2580},
+{"upperdothebrew", 0x05C4},
+{"upsilon", 0x03C5},
+{"upsilondieresis", 0x03CB},
+{"upsilondieresistonos", 0x03B0},
+{"upsilonlatin", 0x028A},
+{"upsilontonos", 0x03CD},
+{"uptackbelowcmb", 0x031D},
+{"uptackmod", 0x02D4},
+{"uragurmukhi", 0x0A73},
+{"uring", 0x016F},
+{"ushortcyrillic", 0x045E},
+{"usmallhiragana", 0x3045},
+{"usmallkatakana", 0x30A5},
+{"usmallkatakanahalfwidth", 0xFF69},
+{"ustraightcyrillic", 0x04AF},
+{"ustraightstrokecyrillic", 0x04B1},
+{"utilde", 0x0169},
+{"utildeacute", 0x1E79},
+{"utildebelow", 0x1E75},
+{"uubengali", 0x098A},
+{"uudeva", 0x090A},
+{"uugujarati", 0x0A8A},
+{"uugurmukhi", 0x0A0A},
+{"uumatragurmukhi", 0x0A42},
+{"uuvowelsignbengali", 0x09C2},
+{"uuvowelsigndeva", 0x0942},
+{"uuvowelsigngujarati", 0x0AC2},
+{"uvowelsignbengali", 0x09C1},
+{"uvowelsigndeva", 0x0941},
+{"uvowelsigngujarati", 0x0AC1},
+{"v", 0x0076},
+{"vadeva", 0x0935},
+{"vagujarati", 0x0AB5},
+{"vagurmukhi", 0x0A35},
+{"vakatakana", 0x30F7},
+{"vav", 0x05D5},
+{"vavdagesh", 0xFB35},
+{"vavdagesh65", 0xFB35},
+{"vavdageshhebrew", 0xFB35},
+{"vavhebrew", 0x05D5},
+{"vavholam", 0xFB4B},
+{"vavholamhebrew", 0xFB4B},
+{"vavvavhebrew", 0x05F0},
+{"vavyodhebrew", 0x05F1},
+{"vcircle", 0x24E5},
+{"vdotbelow", 0x1E7F},
+{"vecyrillic", 0x0432},
+{"veharabic", 0x06A4},
+{"vehfinalarabic", 0xFB6B},
+{"vehinitialarabic", 0xFB6C},
+{"vehmedialarabic", 0xFB6D},
+{"vekatakana", 0x30F9},
+{"venus", 0x2640},
+{"verticalbar", 0x007C},
+{"verticallineabovecmb", 0x030D},
+{"verticallinebelowcmb", 0x0329},
+{"verticallinelowmod", 0x02CC},
+{"verticallinemod", 0x02C8},
+{"vewarmenian", 0x057E},
+{"vhook", 0x028B},
+{"vikatakana", 0x30F8},
+{"viramabengali", 0x09CD},
+{"viramadeva", 0x094D},
+{"viramagujarati", 0x0ACD},
+{"visargabengali", 0x0983},
+{"visargadeva", 0x0903},
+{"visargagujarati", 0x0A83},
+{"vmonospace", 0xFF56},
+{"voarmenian", 0x0578},
+{"voicediterationhiragana", 0x309E},
+{"voicediterationkatakana", 0x30FE},
+{"voicedmarkkana", 0x309B},
+{"voicedmarkkanahalfwidth", 0xFF9E},
+{"vokatakana", 0x30FA},
+{"vparen", 0x24B1},
+{"vtilde", 0x1E7D},
+{"vturned", 0x028C},
+{"vuhiragana", 0x3094},
+{"vukatakana", 0x30F4},
+{"w", 0x0077},
+{"wacute", 0x1E83},
+{"waekorean", 0x3159},
+{"wahiragana", 0x308F},
+{"wakatakana", 0x30EF},
+{"wakatakanahalfwidth", 0xFF9C},
+{"wakorean", 0x3158},
+{"wasmallhiragana", 0x308E},
+{"wasmallkatakana", 0x30EE},
+{"wattosquare", 0x3357},
+{"wavedash", 0x301C},
+{"wavyunderscorevertical", 0xFE34},
+{"wawarabic", 0x0648},
+{"wawfinalarabic", 0xFEEE},
+{"wawhamzaabovearabic", 0x0624},
+{"wawhamzaabovefinalarabic", 0xFE86},
+{"wbsquare", 0x33DD},
+{"wcircle", 0x24E6},
+{"wcircumflex", 0x0175},
+{"wdieresis", 0x1E85},
+{"wdotaccent", 0x1E87},
+{"wdotbelow", 0x1E89},
+{"wehiragana", 0x3091},
+{"weierstrass", 0x2118},
+{"wekatakana", 0x30F1},
+{"wekorean", 0x315E},
+{"weokorean", 0x315D},
+{"wgrave", 0x1E81},
+{"whitebullet", 0x25E6},
+{"whitecircle", 0x25CB},
+{"whitecircleinverse", 0x25D9},
+{"whitecornerbracketleft", 0x300E},
+{"whitecornerbracketleftvertical", 0xFE43},
+{"whitecornerbracketright", 0x300F},
+{"whitecornerbracketrightvertical", 0xFE44},
+{"whitediamond", 0x25C7},
+{"whitediamondcontainingblacksmalldiamond", 0x25C8},
+{"whitedownpointingsmalltriangle", 0x25BF},
+{"whitedownpointingtriangle", 0x25BD},
+{"whiteleftpointingsmalltriangle", 0x25C3},
+{"whiteleftpointingtriangle", 0x25C1},
+{"whitelenticularbracketleft", 0x3016},
+{"whitelenticularbracketright", 0x3017},
+{"whiterightpointingsmalltriangle", 0x25B9},
+{"whiterightpointingtriangle", 0x25B7},
+{"whitesmallsquare", 0x25AB},
+{"whitesmilingface", 0x263A},
+{"whitesquare", 0x25A1},
+{"whitestar", 0x2606},
+{"whitetelephone", 0x260F},
+{"whitetortoiseshellbracketleft", 0x3018},
+{"whitetortoiseshellbracketright", 0x3019},
+{"whiteuppointingsmalltriangle", 0x25B5},
+{"whiteuppointingtriangle", 0x25B3},
+{"wihiragana", 0x3090},
+{"wikatakana", 0x30F0},
+{"wikorean", 0x315F},
+{"wmonospace", 0xFF57},
+{"wohiragana", 0x3092},
+{"wokatakana", 0x30F2},
+{"wokatakanahalfwidth", 0xFF66},
+{"won", 0x20A9},
+{"wonmonospace", 0xFFE6},
+{"wowaenthai", 0x0E27},
+{"wparen", 0x24B2},
+{"wring", 0x1E98},
+{"wsuperior", 0x02B7},
+{"wturned", 0x028D},
+{"wynn", 0x01BF},
+{"x", 0x0078},
+{"xabovecmb", 0x033D},
+{"xbopomofo", 0x3112},
+{"xcircle", 0x24E7},
+{"xdieresis", 0x1E8D},
+{"xdotaccent", 0x1E8B},
+{"xeharmenian", 0x056D},
+{"xi", 0x03BE},
+{"xmonospace", 0xFF58},
+{"xparen", 0x24B3},
+{"xsuperior", 0x02E3},
+{"y", 0x0079},
+{"yaadosquare", 0x334E},
+{"yabengali", 0x09AF},
+{"yacute", 0x00FD},
+{"yadeva", 0x092F},
+{"yaekorean", 0x3152},
+{"yagujarati", 0x0AAF},
+{"yagurmukhi", 0x0A2F},
+{"yahiragana", 0x3084},
+{"yakatakana", 0x30E4},
+{"yakatakanahalfwidth", 0xFF94},
+{"yakorean", 0x3151},
+{"yamakkanthai", 0x0E4E},
+{"yasmallhiragana", 0x3083},
+{"yasmallkatakana", 0x30E3},
+{"yasmallkatakanahalfwidth", 0xFF6C},
+{"yatcyrillic", 0x0463},
+{"ycircle", 0x24E8},
+{"ycircumflex", 0x0177},
+{"ydieresis", 0x00FF},
+{"ydotaccent", 0x1E8F},
+{"ydotbelow", 0x1EF5},
+{"yeharabic", 0x064A},
+{"yehbarreearabic", 0x06D2},
+{"yehbarreefinalarabic", 0xFBAF},
+{"yehfinalarabic", 0xFEF2},
+{"yehhamzaabovearabic", 0x0626},
+{"yehhamzaabovefinalarabic", 0xFE8A},
+{"yehhamzaaboveinitialarabic", 0xFE8B},
+{"yehhamzaabovemedialarabic", 0xFE8C},
+{"yehinitialarabic", 0xFEF3},
+{"yehmedialarabic", 0xFEF4},
+{"yehmeeminitialarabic", 0xFCDD},
+{"yehmeemisolatedarabic", 0xFC58},
+{"yehnoonfinalarabic", 0xFC94},
+{"yehthreedotsbelowarabic", 0x06D1},
+{"yekorean", 0x3156},
+{"yen", 0x00A5},
+{"yenmonospace", 0xFFE5},
+{"yeokorean", 0x3155},
+{"yeorinhieuhkorean", 0x3186},
+{"yerahbenyomohebrew", 0x05AA},
+{"yerahbenyomolefthebrew", 0x05AA},
+{"yericyrillic", 0x044B},
+{"yerudieresiscyrillic", 0x04F9},
+{"yesieungkorean", 0x3181},
+{"yesieungpansioskorean", 0x3183},
+{"yesieungsioskorean", 0x3182},
+{"yetivhebrew", 0x059A},
+{"ygrave", 0x1EF3},
+{"yhook", 0x01B4},
+{"yhookabove", 0x1EF7},
+{"yiarmenian", 0x0575},
+{"yicyrillic", 0x0457},
+{"yikorean", 0x3162},
+{"yinyang", 0x262F},
+{"yiwnarmenian", 0x0582},
+{"ymonospace", 0xFF59},
+{"yod", 0x05D9},
+{"yoddagesh", 0xFB39},
+{"yoddageshhebrew", 0xFB39},
+{"yodhebrew", 0x05D9},
+{"yodyodhebrew", 0x05F2},
+{"yodyodpatahhebrew", 0xFB1F},
+{"yohiragana", 0x3088},
+{"yoikorean", 0x3189},
+{"yokatakana", 0x30E8},
+{"yokatakanahalfwidth", 0xFF96},
+{"yokorean", 0x315B},
+{"yosmallhiragana", 0x3087},
+{"yosmallkatakana", 0x30E7},
+{"yosmallkatakanahalfwidth", 0xFF6E},
+{"yotgreek", 0x03F3},
+{"yoyaekorean", 0x3188},
+{"yoyakorean", 0x3187},
+{"yoyakthai", 0x0E22},
+{"yoyingthai", 0x0E0D},
+{"yparen", 0x24B4},
+{"ypogegrammeni", 0x037A},
+{"ypogegrammenigreekcmb", 0x0345},
+{"yr", 0x01A6},
+{"yring", 0x1E99},
+{"ysuperior", 0x02B8},
+{"ytilde", 0x1EF9},
+{"yturned", 0x028E},
+{"yuhiragana", 0x3086},
+{"yuikorean", 0x318C},
+{"yukatakana", 0x30E6},
+{"yukatakanahalfwidth", 0xFF95},
+{"yukorean", 0x3160},
+{"yusbigcyrillic", 0x046B},
+{"yusbigiotifiedcyrillic", 0x046D},
+{"yuslittlecyrillic", 0x0467},
+{"yuslittleiotifiedcyrillic", 0x0469},
+{"yusmallhiragana", 0x3085},
+{"yusmallkatakana", 0x30E5},
+{"yusmallkatakanahalfwidth", 0xFF6D},
+{"yuyekorean", 0x318B},
+{"yuyeokorean", 0x318A},
+{"yyabengali", 0x09DF},
+{"yyadeva", 0x095F},
+{"z", 0x007A},
+{"zaarmenian", 0x0566},
+{"zacute", 0x017A},
+{"zadeva", 0x095B},
+{"zagurmukhi", 0x0A5B},
+{"zaharabic", 0x0638},
+{"zahfinalarabic", 0xFEC6},
+{"zahinitialarabic", 0xFEC7},
+{"zahiragana", 0x3056},
+{"zahmedialarabic", 0xFEC8},
+{"zainarabic", 0x0632},
+{"zainfinalarabic", 0xFEB0},
+{"zakatakana", 0x30B6},
+{"zaqefgadolhebrew", 0x0595},
+{"zaqefqatanhebrew", 0x0594},
+{"zarqahebrew", 0x0598},
+{"zayin", 0x05D6},
+{"zayindagesh", 0xFB36},
+{"zayindageshhebrew", 0xFB36},
+{"zayinhebrew", 0x05D6},
+{"zbopomofo", 0x3117},
+{"zcaron", 0x017E},
+{"zcircle", 0x24E9},
+{"zcircumflex", 0x1E91},
+{"zcurl", 0x0291},
+{"zdot", 0x017C},
+{"zdotaccent", 0x017C},
+{"zdotbelow", 0x1E93},
+{"zecyrillic", 0x0437},
+{"zedescendercyrillic", 0x0499},
+{"zedieresiscyrillic", 0x04DF},
+{"zehiragana", 0x305C},
+{"zekatakana", 0x30BC},
+{"zero", 0x0030},
+{"zeroarabic", 0x0660},
+{"zerobengali", 0x09E6},
+{"zerodeva", 0x0966},
+{"zerogujarati", 0x0AE6},
+{"zerogurmukhi", 0x0A66},
+{"zerohackarabic", 0x0660},
+{"zeroinferior", 0x2080},
+{"zeromonospace", 0xFF10},
+{"zerooldstyle", 0xF730},
+{"zeropersian", 0x06F0},
+{"zerosuperior", 0x2070},
+{"zerothai", 0x0E50},
+{"zerowidthjoiner", 0xFEFF},
+{"zerowidthnonjoiner", 0x200C},
+{"zerowidthspace", 0x200B},
+{"zeta", 0x03B6},
+{"zhbopomofo", 0x3113},
+{"zhearmenian", 0x056A},
+{"zhebrevecyrillic", 0x04C2},
+{"zhecyrillic", 0x0436},
+{"zhedescendercyrillic", 0x0497},
+{"zhedieresiscyrillic", 0x04DD},
+{"zihiragana", 0x3058},
+{"zikatakana", 0x30B8},
+{"zinorhebrew", 0x05AE},
+{"zlinebelow", 0x1E95},
+{"zmonospace", 0xFF5A},
+{"zohiragana", 0x305E},
+{"zokatakana", 0x30BE},
+{"zparen", 0x24B5},
+{"zretroflexhook", 0x0290},
+{"zstroke", 0x01B6},
+{"zuhiragana", 0x305A},
+{"zukatakana", 0x30BA},
+ {0x00, 0}
+};
+
+double_glyph_list_t DoubleGlyphList[] = {
+{"dalethatafpatah", {0x05D3, 0x05B2}},
+{"dalethatafpatahhebrew", {0x05D3, 0x05B2}},
+{"dalethatafsegol", {0x05D3, 0x05B1}},
+{"dalethatafsegolhebrew", {0x05D3, 0x05B1}},
+{"dalethiriq", {0x05D3, 0x05B4}},
+{"dalethiriqhebrew", {0x05D3, 0x05B4}},
+{"daletholam", {0x05D3, 0x05B9}},
+{"daletholamhebrew", {0x05D3, 0x05B9}},
+{"daletpatah", {0x05D3, 0x05B7}},
+{"daletpatahhebrew", {0x05D3, 0x05B7}},
+{"daletqamats", {0x05D3, 0x05B8}},
+{"daletqamatshebrew", {0x05D3, 0x05B8}},
+{"daletqubuts", {0x05D3, 0x05BB}},
+{"daletqubutshebrew", {0x05D3, 0x05BB}},
+{"daletsegol", {0x05D3, 0x05B6}},
+{"daletsegolhebrew", {0x05D3, 0x05B6}},
+{"daletsheva", {0x05D3, 0x05B0}},
+{"daletshevahebrew", {0x05D3, 0x05B0}},
+{"dalettsere", {0x05D3, 0x05B5}},
+{"dalettserehebrew", {0x05D3, 0x05B5}},
+{"finalkafqamats", {0x05DA, 0x05B8}},
+{"finalkafqamatshebrew", {0x05DA, 0x05B8}},
+{"finalkafsheva", {0x05DA, 0x05B0}},
+{"finalkafshevahebrew", {0x05DA, 0x05B0}},
+{"hamzadammaarabic", {0x0621, 0x064F}},
+{"hamzadammatanarabic", {0x0621, 0x064C}},
+{"hamzafathaarabic", {0x0621, 0x064E}},
+{"hamzafathatanarabic", {0x0621, 0x064B}},
+{"hamzalowkasraarabic", {0x0621, 0x0650}},
+{"hamzalowkasratanarabic", {0x0621, 0x064D}},
+{"hamzasukunarabic", {0x0621, 0x0652}},
+{"lamedholam", {0x05DC, 0x05B9}},
+{"lamedholamhebrew", {0x05DC, 0x05B9}},
+{"noonhehinitialarabic", {0xFEE7, 0xFEEC}},
+{"qofhatafpatah", {0x05E7, 0x05B2}},
+{"qofhatafpatahhebrew", {0x05E7, 0x05B2}},
+{"qofhatafsegol", {0x05E7, 0x05B1}},
+{"qofhatafsegolhebrew", {0x05E7, 0x05B1}},
+{"qofhiriq", {0x05E7, 0x05B4}},
+{"qofhiriqhebrew", {0x05E7, 0x05B4}},
+{"qofholam", {0x05E7, 0x05B9}},
+{"qofholamhebrew", {0x05E7, 0x05B9}},
+{"qofpatah", {0x05E7, 0x05B7}},
+{"qofpatahhebrew", {0x05E7, 0x05B7}},
+{"qofqamats", {0x05E7, 0x05B8}},
+{"qofqamatshebrew", {0x05E7, 0x05B8}},
+{"qofqubuts", {0x05E7, 0x05BB}},
+{"qofqubutshebrew", {0x05E7, 0x05BB}},
+{"qofsegol", {0x05E7, 0x05B6}},
+{"qofsegolhebrew", {0x05E7, 0x05B6}},
+{"qofsheva", {0x05E7, 0x05B0}},
+{"qofshevahebrew", {0x05E7, 0x05B0}},
+{"qoftsere", {0x05E7, 0x05B5}},
+{"qoftserehebrew", {0x05E7, 0x05B5}},
+{"reshhatafpatah", {0x05E8, 0x05B2}},
+{"reshhatafpatahhebrew", {0x05E8, 0x05B2}},
+{"reshhatafsegol", {0x05E8, 0x05B1}},
+{"reshhatafsegolhebrew", {0x05E8, 0x05B1}},
+{"reshhiriq", {0x05E8, 0x05B4}},
+{"reshhiriqhebrew", {0x05E8, 0x05B4}},
+{"reshholam", {0x05E8, 0x05B9}},
+{"reshholamhebrew", {0x05E8, 0x05B9}},
+{"reshpatah", {0x05E8, 0x05B7}},
+{"reshpatahhebrew", {0x05E8, 0x05B7}},
+{"reshqamats", {0x05E8, 0x05B8}},
+{"reshqamatshebrew", {0x05E8, 0x05B8}},
+{"reshqubuts", {0x05E8, 0x05BB}},
+{"reshqubutshebrew", {0x05E8, 0x05BB}},
+{"reshsegol", {0x05E8, 0x05B6}},
+{"reshsegolhebrew", {0x05E8, 0x05B6}},
+{"reshsheva", {0x05E8, 0x05B0}},
+{"reshshevahebrew", {0x05E8, 0x05B0}},
+{"reshtsere", {0x05E8, 0x05B5}},
+{"reshtserehebrew", {0x05E8, 0x05B5}},
+{"shaddafathatanarabic", {0x0651, 0x064B}},
+{"tchehmeeminitialarabic", {0xFB7C, 0xFEE4}},
+ {0x00, {0,0}}
+};
+
+treble_glyph_list_t TrebleGlyphList[] = {
+ {"lamedholamdagesh", {0x05DC, 0x05B9, 0x05BC}},
+ {"lamedholamdageshhebrew", {0x05DC, 0x05B9, 0x05BC}},
+ {"lammeemjeeminitialarabic", {0xFEDF, 0xFEE4, 0xFEA0}},
+ {"lammeemkhahinitialarabic", {0xFEDF, 0xFEE4, 0xFEA8}},
+ {0x00, {0,0,0}}
+};
+
+quad_glyph_list_t QuadGlyphList[] = {
+ {"rehyehaleflamarabic", {0x0631, 0xFEF3, 0xFE8E, 0x0644}},
+ {0x00, {0,0,0}}
+};
diff --git a/devices/vector/gdevagl.h b/devices/vector/gdevagl.h
new file mode 100644
index 000000000..02c73f39d
--- /dev/null
+++ b/devices/vector/gdevagl.h
@@ -0,0 +1,37 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+/* We need several tables, to hold the glyph names and a variable number of
+ * Unicode code points.
+ */
+
+typedef struct single_glyph_list_s {
+ const char *Glyph;
+ unsigned short Unicode;
+} single_glyph_list_t;
+
+typedef struct double_glyph_list_s {
+ const char *Glyph;
+ unsigned short Unicode[2];
+} double_glyph_list_t;
+
+typedef struct treble_glyph_list_s {
+ const char *Glyph;
+ unsigned short Unicode[3];
+} treble_glyph_list_t;
+
+typedef struct quad_glyph_list_s {
+ const char *Glyph;
+ short Unicode[4];
+} quad_glyph_list_t;
diff --git a/devices/vector/gdevpdf.c b/devices/vector/gdevpdf.c
new file mode 100644
index 000000000..aa98061fa
--- /dev/null
+++ b/devices/vector/gdevpdf.c
@@ -0,0 +1,3258 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* PDF-writing driver */
+#include "fcntl_.h"
+#include "memory_.h"
+#include "time_.h"
+#include "unistd_.h"
+#include "gx.h"
+#include "gp.h" /* for gp_get_realtime */
+#include "gserrors.h"
+#include "gxdevice.h"
+#include "gdevpdfx.h"
+#include "gdevpdfg.h" /* only for pdf_reset_graphics */
+#include "gdevpdfo.h"
+#include "smd5.h"
+#include "sarc4.h"
+#include "gscms.h"
+#include "gdevpdtf.h"
+#include "gdevpdtx.h"
+#include "gdevpdtd.h"
+#include "gdevpdti.h"
+#include "gsfcmap.h" /* For gs_cmap_ToUnicode_free */
+
+#include "gxfcache.h"
+#include "gdevpdts.h" /* for sync_text_state */
+
+/* Define the default language level and PDF compatibility level. */
+/* Acrobat 6 (PDF 1.5) is the default. (1.5 for ICC V4 profile support) */
+#define PSDF_VERSION_INITIAL psdf_version_ll3
+#define PDF_COMPATIBILITY_LEVEL_INITIAL 1.5
+
+/* Define the size of internal stream buffers. */
+/* (This is not a limitation, it only affects performance.) */
+#define sbuf_size 512
+
+/* GC descriptors */
+private_st_pdf_page();
+gs_private_st_element(st_pdf_page_element, pdf_page_t, "pdf_page_t[]",
+ pdf_page_elt_enum_ptrs, pdf_page_elt_reloc_ptrs,
+ st_pdf_page);
+private_st_pdf_linearisation_record();
+gs_private_st_element(st_pdf_linearisation_record_element, pdf_linearisation_record_t, "pdf_linearstion_record_t[]",
+ pdf_linearisation_record_elt_enum_ptrs, pdf_linearisation_record_elt_reloc_ptrs,
+ st_pdf_linearisation_record);
+private_st_device_pdfwrite();
+private_st_pdf_substream_save();
+private_st_pdf_substream_save_element();
+
+/* GC procedures */
+static
+ENUM_PTRS_WITH(device_pdfwrite_enum_ptrs, gx_device_pdf *pdev)
+{
+ index -= gx_device_pdf_num_ptrs + gx_device_pdf_num_param_strings;
+ if (index < NUM_RESOURCE_TYPES * NUM_RESOURCE_CHAINS)
+ ENUM_RETURN(pdev->resources[index / NUM_RESOURCE_CHAINS].chains[index % NUM_RESOURCE_CHAINS]);
+ index -= NUM_RESOURCE_TYPES * NUM_RESOURCE_CHAINS;
+ if (index <= pdev->outline_depth && pdev->outline_levels)
+ ENUM_RETURN(pdev->outline_levels[index].first.action);
+ index -= pdev->outline_depth + 1;
+ if (index <= pdev->outline_depth && pdev->outline_levels)
+ ENUM_RETURN(pdev->outline_levels[index].last.action);
+ index -= pdev->outline_depth + 1;
+ ENUM_PREFIX(st_device_psdf, 0);
+}
+ ENUM_PTR(0, gx_device_pdf, asides.strm);
+ ENUM_PTR(1, gx_device_pdf, asides.strm_buf);
+ ENUM_PTR(2, gx_device_pdf, asides.save_strm);
+ ENUM_PTR(3, gx_device_pdf, streams.strm);
+ ENUM_PTR(4, gx_device_pdf, streams.strm_buf);
+ ENUM_PTR(5, gx_device_pdf, pictures.strm);
+ ENUM_PTR(6, gx_device_pdf, pictures.strm_buf);
+ ENUM_PTR(7, gx_device_pdf, pictures.save_strm);
+ ENUM_PTR(8, gx_device_pdf, Catalog);
+ ENUM_PTR(9, gx_device_pdf, Info);
+ ENUM_PTR(10, gx_device_pdf, Pages);
+ ENUM_PTR(11, gx_device_pdf, text);
+ ENUM_PTR(12, gx_device_pdf, pages);
+ ENUM_PTR(13, gx_device_pdf, cs_Patterns[0]);
+ ENUM_PTR(14, gx_device_pdf, cs_Patterns[1]);
+ ENUM_PTR(15, gx_device_pdf, cs_Patterns[3]);
+ ENUM_PTR(16, gx_device_pdf, cs_Patterns[4]);
+ ENUM_PTR(17, gx_device_pdf, last_resource);
+ ENUM_PTR(18, gx_device_pdf, articles);
+ ENUM_PTR(19, gx_device_pdf, Dests);
+ ENUM_PTR(20, gx_device_pdf, global_named_objects);
+ ENUM_PTR(21, gx_device_pdf, local_named_objects);
+ ENUM_PTR(22, gx_device_pdf, NI_stack);
+ ENUM_PTR(23, gx_device_pdf, Namespace_stack);
+ ENUM_PTR(24, gx_device_pdf, font_cache);
+ ENUM_PTR(25, gx_device_pdf, clip_path);
+ ENUM_PTR(26, gx_device_pdf, PageLabels);
+ ENUM_PTR(27, gx_device_pdf, PageLabels_current_label);
+ ENUM_PTR(28, gx_device_pdf, sbstack);
+ ENUM_PTR(29, gx_device_pdf, substream_Resources);
+ ENUM_PTR(30, gx_device_pdf, font3);
+ ENUM_PTR(31, gx_device_pdf, accumulating_substream_resource);
+ ENUM_PTR(32, gx_device_pdf, pres_soft_mask_dict);
+ ENUM_PTR(33, gx_device_pdf, PDFXTrimBoxToMediaBoxOffset.data);
+ ENUM_PTR(34, gx_device_pdf, PDFXBleedBoxToTrimBoxOffset.data);
+ ENUM_PTR(35, gx_device_pdf, DSCEncodingToUnicode.data);
+ ENUM_PTR(36, gx_device_pdf, Identity_ToUnicode_CMaps[0]);
+ ENUM_PTR(37, gx_device_pdf, Identity_ToUnicode_CMaps[1]);
+ ENUM_PTR(38, gx_device_pdf, ResourceUsage);
+ ENUM_PTR(39, gx_device_pdf, vgstack);
+ ENUM_PTR(40, gx_device_pdf, outline_levels);
+ ENUM_PTR(41, gx_device_pdf, EmbeddedFiles);
+ ENUM_PTR(42, gx_device_pdf, pdf_font_dir);
+#define e1(i,elt) ENUM_PARAM_STRING_PTR(i + gx_device_pdf_num_ptrs, gx_device_pdf, elt);
+gx_device_pdf_do_param_strings(e1)
+#undef e1
+#define e1(i,elt) ENUM_STRING_PTR(i + gx_device_pdf_num_ptrs + gx_device_pdf_num_param_strings,\
+ gx_device_pdf, elt);
+gx_device_pdf_do_const_strings(e1)
+#undef e1
+ENUM_PTRS_END
+static RELOC_PTRS_WITH(device_pdfwrite_reloc_ptrs, gx_device_pdf *pdev)
+{
+ RELOC_PREFIX(st_device_psdf);
+ RELOC_PTR(gx_device_pdf, asides.strm);
+ RELOC_PTR(gx_device_pdf, asides.strm_buf);
+ RELOC_PTR(gx_device_pdf, asides.save_strm);
+ RELOC_PTR(gx_device_pdf, streams.strm);
+ RELOC_PTR(gx_device_pdf, streams.strm_buf);
+ RELOC_PTR(gx_device_pdf, pictures.strm);
+ RELOC_PTR(gx_device_pdf, pictures.strm_buf);
+ RELOC_PTR(gx_device_pdf, pictures.save_strm);
+ RELOC_PTR(gx_device_pdf, Catalog);
+ RELOC_PTR(gx_device_pdf, Info);
+ RELOC_PTR(gx_device_pdf, Pages);
+ RELOC_PTR(gx_device_pdf, text);
+ RELOC_PTR(gx_device_pdf, pages);
+ RELOC_PTR(gx_device_pdf, cs_Patterns[0]);
+ RELOC_PTR(gx_device_pdf, cs_Patterns[1]);
+ RELOC_PTR(gx_device_pdf, cs_Patterns[3]);
+ RELOC_PTR(gx_device_pdf, cs_Patterns[4]);
+ RELOC_PTR(gx_device_pdf, last_resource);
+ RELOC_PTR(gx_device_pdf, articles);
+ RELOC_PTR(gx_device_pdf, Dests);
+ RELOC_PTR(gx_device_pdf, global_named_objects);
+ RELOC_PTR(gx_device_pdf, local_named_objects);
+ RELOC_PTR(gx_device_pdf, NI_stack);
+ RELOC_PTR(gx_device_pdf, Namespace_stack);
+ RELOC_PTR(gx_device_pdf, font_cache);
+ RELOC_PTR(gx_device_pdf, clip_path);
+ RELOC_PTR(gx_device_pdf, PageLabels);
+ RELOC_PTR(gx_device_pdf, PageLabels_current_label);
+ RELOC_PTR(gx_device_pdf, sbstack);
+ RELOC_PTR(gx_device_pdf, substream_Resources);
+ RELOC_PTR(gx_device_pdf, font3);
+ RELOC_PTR(gx_device_pdf, accumulating_substream_resource);
+ RELOC_PTR(gx_device_pdf, pres_soft_mask_dict);
+ RELOC_PTR(gx_device_pdf, PDFXTrimBoxToMediaBoxOffset.data);
+ RELOC_PTR(gx_device_pdf, PDFXBleedBoxToTrimBoxOffset.data);
+ RELOC_PTR(gx_device_pdf, DSCEncodingToUnicode.data);
+ RELOC_PTR(gx_device_pdf, Identity_ToUnicode_CMaps[0]);
+ RELOC_PTR(gx_device_pdf, Identity_ToUnicode_CMaps[1]);
+ RELOC_PTR(gx_device_pdf, ResourceUsage);
+ RELOC_PTR(gx_device_pdf, vgstack);
+ RELOC_PTR(gx_device_pdf, EmbeddedFiles);
+ RELOC_PTR(gx_device_pdf, pdf_font_dir);
+#define r1(i,elt) RELOC_PARAM_STRING_PTR(gx_device_pdf,elt);
+ gx_device_pdf_do_param_strings(r1)
+#undef r1
+#define r1(i,elt) RELOC_CONST_STRING_PTR(gx_device_pdf,elt);
+ gx_device_pdf_do_const_strings(r1)
+#undef r1
+ {
+ int i, j;
+
+ for (i = 0; i < NUM_RESOURCE_TYPES; ++i)
+ for (j = 0; j < NUM_RESOURCE_CHAINS; ++j)
+ RELOC_PTR(gx_device_pdf, resources[i].chains[j]);
+ if (pdev->outline_levels) {
+ for (i = 0; i <= pdev->outline_depth; ++i) {
+ RELOC_PTR(gx_device_pdf, outline_levels[i].first.action);
+ RELOC_PTR(gx_device_pdf, outline_levels[i].last.action);
+ }
+ }
+ }
+ RELOC_PTR(gx_device_pdf, outline_levels);
+}
+RELOC_PTRS_END
+/* Even though device_pdfwrite_finalize is the same as gx_device_finalize, */
+/* we need to implement it separately because st_composite_final */
+/* declares all 3 procedures as private. */
+static void
+device_pdfwrite_finalize(const gs_memory_t *cmem, void *vpdev)
+{
+ gx_device_finalize(cmem, vpdev);
+}
+
+/* Driver procedures */
+static dev_proc_open_device(pdf_open);
+static dev_proc_output_page(pdf_output_page);
+static dev_proc_close_device(pdf_close);
+/* Driver procedures defined in other files are declared in gdevpdfx.h. */
+
+#ifndef X_DPI
+# define X_DPI 720
+#endif
+#ifndef Y_DPI
+# define Y_DPI 720
+#endif
+
+/* ---------------- Device prototype ---------------- */
+
+#define PDF_DEVICE_NAME "pdfwrite"
+#define PDF_DEVICE_IDENT gs_pdfwrite_device
+#define PDF_DEVICE_MaxInlineImageSize 4000
+#define PDF_FOR_OPDFREAD 0
+#define PDF_FOR_EPS2WRITE 0
+
+#include "gdevpdfb.h"
+
+#undef PDF_DEVICE_NAME
+#undef PDF_DEVICE_IDENT
+#undef PDF_DEVICE_MaxInlineImageSize
+#undef PDF_FOR_OPDFREAD
+
+#define PDF_DEVICE_NAME "ps2write"
+#define PDF_DEVICE_IDENT gs_ps2write_device
+#define PDF_DEVICE_MaxInlineImageSize max_long
+#define PDF_FOR_OPDFREAD 1
+#define PDF_FOR_EPS2WRITE 0
+
+#include "gdevpdfb.h"
+
+#undef PDF_DEVICE_NAME
+#undef PDF_DEVICE_IDENT
+#undef PDF_DEVICE_MaxInlineImageSize
+#undef PDF_FOR_OPDFREAD
+#undef PDF_FOR_EPS2WRITE
+
+#define PDF_DEVICE_NAME "eps2write"
+#define PDF_DEVICE_IDENT gs_eps2write_device
+#define PDF_DEVICE_MaxInlineImageSize max_long
+#define PDF_FOR_OPDFREAD 1
+#define PDF_FOR_EPS2WRITE 1
+
+#include "gdevpdfb.h"
+
+#undef PDF_DEVICE_NAME
+#undef PDF_DEVICE_IDENT
+#undef PDF_DEVICE_MaxInlineImageSize
+#undef PDF_FOR_OPDFREAD
+#undef PDF_FOR_EPS2WRITE
+/* ---------------- Device open/close ---------------- */
+
+/* Close and remove temporary files. */
+static int
+pdf_close_temp_file(gx_device_pdf *pdev, pdf_temp_file_t *ptf, int code)
+{
+ int err = 0;
+ FILE *file = ptf->file;
+
+ /*
+ * ptf->strm == 0 or ptf->file == 0 is only possible if this procedure
+ * is called to clean up during initialization failure, but ptf->strm
+ * might not be open if it was finalized before the device was closed.
+ */
+ if (ptf->strm) {
+ if (s_is_valid(ptf->strm)) {
+ sflush(ptf->strm);
+ /* Prevent freeing the stream from closing the file. */
+ ptf->strm->file = 0;
+ } else
+ ptf->file = file = 0; /* file was closed by finalization */
+ gs_free_object(pdev->pdf_memory, ptf->strm_buf,
+ "pdf_close_temp_file(strm_buf)");
+ ptf->strm_buf = 0;
+ gs_free_object(pdev->pdf_memory, ptf->strm,
+ "pdf_close_temp_file(strm)");
+ ptf->strm = 0;
+ }
+ if (file) {
+ err = ferror(file) | fclose(file);
+ unlink(ptf->file_name);
+ ptf->file = 0;
+ }
+ ptf->save_strm = 0;
+ return
+ (code < 0 ? code : err != 0 ? gs_note_error(gs_error_ioerror) : code);
+}
+static int
+pdf_close_files(gx_device_pdf * pdev, int code)
+{
+ code = pdf_close_temp_file(pdev, &pdev->pictures, code);
+ code = pdf_close_temp_file(pdev, &pdev->streams, code);
+ code = pdf_close_temp_file(pdev, &pdev->asides, code);
+ return pdf_close_temp_file(pdev, &pdev->xref, code);
+}
+
+/* Reset the state of the current page. */
+static void
+pdf_reset_page(gx_device_pdf * pdev)
+{
+ pdev->page_dsc_info = gs_pdfwrite_device.page_dsc_info;
+ pdev->contents_id = 0;
+ pdf_reset_graphics(pdev);
+ pdev->procsets = NoMarks;
+ memset(pdev->cs_Patterns, 0, sizeof(pdev->cs_Patterns)); /* simplest to create for each page */
+ pdf_reset_text_page(pdev->text);
+ pdf_remember_clip_path(pdev, 0);
+ pdev->clip_path_id = pdev->no_clip_path_id;
+}
+
+/* Open a temporary file, with or without a stream. */
+static int
+pdf_open_temp_file(gx_device_pdf *pdev, pdf_temp_file_t *ptf)
+{
+ char fmode[4];
+
+ if (strlen(gp_fmode_binary_suffix) > 2)
+ return_error(gs_error_invalidfileaccess);
+
+ strcpy(fmode, "w+");
+ strcat(fmode, gp_fmode_binary_suffix);
+ ptf->file = gp_open_scratch_file_64(pdev->memory,
+ gp_scratch_file_name_prefix,
+ ptf->file_name,
+ fmode);
+ if (ptf->file == 0)
+ return_error(gs_error_invalidfileaccess);
+ return 0;
+}
+static int
+pdf_open_temp_stream(gx_device_pdf *pdev, pdf_temp_file_t *ptf)
+{
+ int code = pdf_open_temp_file(pdev, ptf);
+
+ if (code < 0)
+ return code;
+ ptf->strm = s_alloc(pdev->pdf_memory, "pdf_open_temp_stream(strm)");
+ if (ptf->strm == 0)
+ return_error(gs_error_VMerror);
+ ptf->strm_buf = gs_alloc_bytes(pdev->pdf_memory, sbuf_size,
+ "pdf_open_temp_stream(strm_buf)");
+ if (ptf->strm_buf == 0) {
+ gs_free_object(pdev->pdf_memory, ptf->strm,
+ "pdf_open_temp_stream(strm)");
+ ptf->strm = 0;
+ return_error(gs_error_VMerror);
+ }
+ swrite_file(ptf->strm, ptf->file, ptf->strm_buf, sbuf_size);
+ return 0;
+}
+
+/* Initialize the IDs allocated at startup. */
+void
+pdf_initialize_ids(gx_device_pdf * pdev)
+{
+ gs_param_string nstr;
+
+ pdev->next_id = pdev->FirstObjectNumber;
+
+ /* Initialize the Catalog. */
+
+ param_string_from_string(nstr, "{Catalog}");
+ pdf_create_named_dict(pdev, &nstr, &pdev->Catalog, 0L);
+
+ /* Initialize the Info dictionary. */
+
+ param_string_from_string(nstr, "{DocInfo}");
+ pdf_create_named_dict(pdev, &nstr, &pdev->Info, 0L);
+ {
+ char buf[PDF_MAX_PRODUCER];
+
+ pdf_store_default_Producer(buf);
+ cos_dict_put_c_key_string(pdev->Info, "/Producer", (byte *)buf,
+ strlen(buf));
+ }
+ /*
+ * Acrobat Distiller sets CreationDate and ModDate to the current
+ * date and time, rather than (for example) %%CreationDate from the
+ * PostScript file. We think this is wrong, but we do the same.
+ */
+ {
+ struct tm tms;
+ time_t t;
+ char buf[1+2+4+2+2+2+2+2+1+2+1+2+1+1+1]; /* (D:yyyymmddhhmmssZhh'mm')\0 */
+ int timeoffset;
+ char timesign;
+
+ time(&t);
+ tms = *gmtime(&t);
+ tms.tm_isdst = -1;
+ timeoffset = (int)difftime(t, mktime(&tms)); /* tz+dst in seconds */
+ timesign = (timeoffset == 0 ? 'Z' : timeoffset < 0 ? '-' : '+');
+ timeoffset = any_abs(timeoffset) / 60;
+ tms = *localtime(&t);
+
+ gs_sprintf(buf, "(D:%04d%02d%02d%02d%02d%02d%c%02d\'%02d\')",
+ tms.tm_year + 1900, tms.tm_mon + 1, tms.tm_mday,
+ tms.tm_hour, tms.tm_min, tms.tm_sec,
+ timesign, timeoffset / 60, timeoffset % 60);
+
+ cos_dict_put_c_key_string(pdev->Info, "/CreationDate", (byte *)buf,
+ strlen(buf));
+ cos_dict_put_c_key_string(pdev->Info, "/ModDate", (byte *)buf,
+ strlen(buf));
+ }
+
+ /* Allocate the root of the pages tree. */
+
+ pdf_create_named_dict(pdev, NULL, &pdev->Pages, 0L);
+}
+
+static int
+pdf_compute_fileID(gx_device_pdf * pdev)
+{
+ /* We compute a file identifier when beginning a document
+ to allow its usage with PDF encryption. Due to that,
+ in contradiction to the Adobe recommendation, our
+ ID doesn't depend on the document size.
+ */
+ gs_memory_t *mem = pdev->pdf_memory;
+ stream *strm = pdev->strm;
+ uint ignore;
+ int code;
+ stream *s = s_MD5E_make_stream(mem, pdev->fileID, sizeof(pdev->fileID));
+ long secs_ns[2];
+ uint KeyLength = pdev->KeyLength;
+
+ if (s == NULL)
+ return_error(gs_error_VMerror);
+ pdev->KeyLength = 0; /* Disable encryption. Not so important though. */
+ gp_get_usertime(secs_ns);
+ sputs(s, (byte *)secs_ns, sizeof(secs_ns), &ignore);
+ sputs(s, (const byte *)pdev->fname, strlen(pdev->fname), &ignore);
+ pdev->strm = s;
+ code = cos_dict_elements_write(pdev->Info, pdev);
+ pdev->strm = strm;
+ pdev->KeyLength = KeyLength;
+ if (code < 0)
+ return code;
+ sclose(s);
+ gs_free_object(mem, s, "pdf_compute_fileID");
+ return 0;
+}
+
+static const byte pad[32] = { 0x28, 0xBF, 0x4E, 0x5E, 0x4E, 0x75, 0x8A, 0x41,
+ 0x64, 0x00, 0x4E, 0x56, 0xFF, 0xFA, 0x01, 0x08,
+ 0x2E, 0x2E, 0x00, 0xB6, 0xD0, 0x68, 0x3E, 0x80,
+ 0x2F, 0x0C, 0xA9, 0xFE, 0x64, 0x53, 0x69, 0x7A};
+
+static inline void
+copy_padded(byte buf[32], gs_param_string *str)
+{
+ memcpy(buf, str->data, min(str->size, 32));
+ if (32 > str->size)
+ memcpy(buf + str->size, pad, 32 - str->size);
+}
+
+static void
+Adobe_magic_loop_50(byte digest[16], int key_length)
+{
+ gs_md5_state_t md5;
+ int i;
+
+ for (i = 0; i < 50; i++) {
+ gs_md5_init(&md5);
+ gs_md5_append(&md5, digest, key_length);
+ gs_md5_finish(&md5, digest);
+ }
+}
+
+static void
+Adobe_magic_loop_19(byte *data, int data_size, const byte *key, int key_size)
+{
+ stream_arcfour_state sarc4;
+ byte key_buf[16];
+ int i, j;
+
+ for (i = 1; i <= 19; i++) {
+ for (j = 0; j < key_size; j++)
+ key_buf[j] = key[j] ^ (byte)i;
+ s_arcfour_set_key(&sarc4, key_buf, key_size);
+ s_arcfour_process_buffer(&sarc4, data, data_size);
+ }
+}
+
+static int
+pdf_compute_encryption_data(gx_device_pdf * pdev)
+{
+ gs_md5_state_t md5;
+ byte digest[16], buf[32], t;
+ stream_arcfour_state sarc4;
+
+ if (pdev->PDFX && pdev->KeyLength != 0) {
+ emprintf(pdev->memory,
+ "Encryption is not allowed in a PDF/X doucment.\n");
+ return_error(gs_error_rangecheck);
+ }
+ if (pdev->KeyLength == 0)
+ pdev->KeyLength = 40;
+ if (pdev->EncryptionV == 0 && pdev->KeyLength == 40)
+ pdev->EncryptionV = 1;
+ if (pdev->EncryptionV == 0 && pdev->KeyLength > 40)
+ pdev->EncryptionV = 2;
+ if (pdev->EncryptionV > 1 && pdev->CompatibilityLevel < 1.4) {
+ emprintf(pdev->memory, "PDF 1.3 only supports 40 bits keys.\n");
+ return_error(gs_error_rangecheck);
+ }
+ if (pdev->EncryptionR == 0)
+ pdev->EncryptionR = 2;
+ if (pdev->EncryptionR < 2 || pdev->EncryptionR > 3) {
+ emprintf(pdev->memory,
+ "Encryption revisions 2 and 3 are only supported.\n");
+ return_error(gs_error_rangecheck);
+ }
+ if (pdev->EncryptionR > 2 && pdev->CompatibilityLevel < 1.4) {
+ emprintf(pdev->memory,
+ "PDF 1.3 only supports the encryption revision 2.\n");
+ return_error(gs_error_rangecheck);
+ }
+ if (pdev->KeyLength > 128) {
+ emprintf(pdev->memory,
+ "The maximal length of PDF encryption key is 128 bits.\n");
+ return_error(gs_error_rangecheck);
+ }
+ if (pdev->KeyLength % 8) {
+ emprintf(pdev->memory,
+ "PDF encryption key length must be a multiple of 8.\n");
+ return_error(gs_error_rangecheck);
+ }
+ if (pdev->EncryptionR == 2 &&
+ ((pdev->Permissions & (0xFFFFFFC3)) != 0xFFFFFFC0)) {
+ emprintf(pdev->memory,
+ "Some of Permissions are not allowed with R=2.\n");
+ return_error(gs_error_rangecheck);
+ }
+ if (pdev->EncryptionV == 2 && pdev->EncryptionR == 2 && pdev->KeyLength > 40) {
+ emprintf(pdev->memory, "Encryption version 2 revision 2 with "
+ "KeyLength > 40 appears incompatible to some viewers. With "
+ "long keys use revision 3.\n");
+ return_error(gs_error_rangecheck);
+ }
+ /* Compute O : */
+ gs_md5_init(&md5);
+ copy_padded(buf, &pdev->OwnerPassword);
+ gs_md5_append(&md5, buf, sizeof(buf));
+ gs_md5_finish(&md5, digest);
+ if (pdev->EncryptionR == 3)
+ Adobe_magic_loop_50(digest, pdev->KeyLength / 8);
+ copy_padded(buf, &pdev->UserPassword);
+ s_arcfour_set_key(&sarc4, digest, pdev->KeyLength / 8);
+ s_arcfour_process_buffer(&sarc4, buf, sizeof(buf));
+ if (pdev->EncryptionR == 3)
+ Adobe_magic_loop_19(buf, sizeof(buf), digest, pdev->KeyLength / 8);
+ memcpy(pdev->EncryptionO, buf, sizeof(pdev->EncryptionO));
+ /* Compute Key : */
+ gs_md5_init(&md5);
+ copy_padded(buf, &pdev->UserPassword);
+ gs_md5_append(&md5, buf, sizeof(buf));
+ gs_md5_append(&md5, pdev->EncryptionO, sizeof(pdev->EncryptionO));
+ t = (byte)(pdev->Permissions >> 0); gs_md5_append(&md5, &t, 1);
+ t = (byte)(pdev->Permissions >> 8); gs_md5_append(&md5, &t, 1);
+ t = (byte)(pdev->Permissions >> 16); gs_md5_append(&md5, &t, 1);
+ t = (byte)(pdev->Permissions >> 24); gs_md5_append(&md5, &t, 1);
+ gs_md5_append(&md5, pdev->fileID, sizeof(pdev->fileID));
+ if (pdev->EncryptionR == 3)
+ if (!pdev->EncryptMetadata) {
+ const byte v[4] = {0xFF, 0xFF, 0xFF, 0xFF};
+
+ gs_md5_append(&md5, v, 4);
+ }
+ gs_md5_finish(&md5, digest);
+ if (pdev->EncryptionR == 3)
+ Adobe_magic_loop_50(digest, pdev->KeyLength / 8);
+ memcpy(pdev->EncryptionKey, digest, pdev->KeyLength / 8);
+ /* Compute U : */
+ if (pdev->EncryptionR == 3) {
+ gs_md5_init(&md5);
+ gs_md5_append(&md5, pad, sizeof(pad));
+ gs_md5_append(&md5, pdev->fileID, sizeof(pdev->fileID));
+ gs_md5_finish(&md5, digest);
+ s_arcfour_set_key(&sarc4, pdev->EncryptionKey, pdev->KeyLength / 8);
+ s_arcfour_process_buffer(&sarc4, digest, sizeof(digest));
+ Adobe_magic_loop_19(digest, sizeof(digest), pdev->EncryptionKey, pdev->KeyLength / 8);
+ memcpy(pdev->EncryptionU, digest, sizeof(digest));
+ memcpy(pdev->EncryptionU + sizeof(digest), pad,
+ sizeof(pdev->EncryptionU) - sizeof(digest));
+ } else {
+ memcpy(pdev->EncryptionU, pad, sizeof(pdev->EncryptionU));
+ s_arcfour_set_key(&sarc4, pdev->EncryptionKey, pdev->KeyLength / 8);
+ s_arcfour_process_buffer(&sarc4, pdev->EncryptionU, sizeof(pdev->EncryptionU));
+ }
+ return 0;
+}
+
+#ifdef __DECC
+/* The ansi alias rules are violated in this next routine. Tell the compiler
+ to ignore this.
+ */
+#pragma optimize save
+#pragma optimize ansi_alias=off
+#endif
+/*
+ * Update the color mapping procedures after setting ProcessColorModel.
+ *
+ * The 'index' value indicates the ProcessColorModel.
+ * 0 = DeviceGray
+ * 1 = DeviceRGB
+ * 2 = DeviceCMYK
+ * 3 = DeviceN (treat like CMYK except for color model name)
+ */
+void
+pdf_set_process_color_model(gx_device_pdf * pdev, int index)
+{
+ const static gx_device_color_info pcm_color_info[] = {
+ dci_values(1, 8, 255, 0, 256, 0), /* Gray */
+ dci_values(3, 24, 255, 255, 256, 256), /* RGB */
+ dci_values(4, 32, 255, 255, 256, 256), /* CMYK */
+ dci_values(4, 32, 255, 255, 256, 256) /* Treat DeviceN like CMYK */
+ };
+ /* Don't blow away the ICC information */
+
+ pdev->pcm_color_info_index = index;
+ pdev->color_info = pcm_color_info[index];
+ /* Set the separable and linear shift, masks, bits. */
+ set_linear_color_bits_mask_shift((gx_device *)pdev);
+ pdev->color_info.separable_and_linear = GX_CINFO_SEP_LIN;
+ /*
+ * The conversion from PS to PDF should be transparent as possible.
+ * Particularly it should not change representation of colors.
+ * Perhaps due to historical reasons the source color information
+ * sometimes isn't accessible from device methods, and
+ * therefore they perform a mapping of colors to
+ * an output color model. Here we handle some color models,
+ * which were selected almost due to antique reasons.
+ */
+ switch (index) {
+ case 0: /* DeviceGray */
+ set_dev_proc(pdev, map_rgb_color, gx_default_gray_map_rgb_color);
+ set_dev_proc(pdev, map_color_rgb, gx_default_gray_map_color_rgb);
+ set_dev_proc(pdev, map_cmyk_color, NULL);
+ set_dev_proc(pdev, get_color_mapping_procs,
+ gx_default_DevGray_get_color_mapping_procs);
+ set_dev_proc(pdev, get_color_comp_index,
+ gx_default_DevGray_get_color_comp_index);
+ set_dev_proc(pdev, encode_color, gx_default_gray_encode);
+ set_dev_proc(pdev, decode_color, gx_default_decode_color);
+ break;
+ case 1: /* DeviceRGB */
+ set_dev_proc(pdev, map_rgb_color, gx_default_rgb_map_rgb_color);
+ set_dev_proc(pdev, map_color_rgb, gx_default_rgb_map_color_rgb);
+ set_dev_proc(pdev, map_cmyk_color, NULL);
+ set_dev_proc(pdev, get_color_mapping_procs,
+ gx_default_DevRGB_get_color_mapping_procs);
+ set_dev_proc(pdev, get_color_comp_index,
+ gx_default_DevRGB_get_color_comp_index);
+ set_dev_proc(pdev, encode_color, gx_default_rgb_map_rgb_color);
+ set_dev_proc(pdev, decode_color, gx_default_rgb_map_color_rgb);
+ break;
+ case 3: /* DeviceN - treat like DeviceCMYK except for cm_name */
+ pdev->color_info.cm_name = "DeviceN";
+ case 2: /* DeviceCMYK */
+ set_dev_proc(pdev, map_rgb_color, NULL);
+ set_dev_proc(pdev, map_color_rgb, cmyk_8bit_map_color_rgb);
+ /* possible problems with aliassing on next statement */
+ set_dev_proc(pdev, map_cmyk_color, cmyk_8bit_map_cmyk_color);
+ set_dev_proc(pdev, get_color_mapping_procs,
+ gx_default_DevCMYK_get_color_mapping_procs);
+ set_dev_proc(pdev, get_color_comp_index,
+ gx_default_DevCMYK_get_color_comp_index);
+ set_dev_proc(pdev, encode_color, cmyk_8bit_map_cmyk_color);
+ set_dev_proc(pdev, decode_color, cmyk_8bit_map_color_rgb);
+ break;
+ default: /* can't happen - see the call from gdev_pdf_put_params. */
+ DO_NOTHING;
+ }
+}
+#ifdef __DECC
+#pragma optimize restore
+#endif
+
+/*
+ * Reset the text state parameters to initial values.
+ */
+void
+pdf_reset_text(gx_device_pdf * pdev)
+{
+ /* we need to flush the text buffer, in case we have (eg) Tr set,
+ * but have reset it to 0 for the current (buffered) text. If we restore to a
+ * graphics state which also has Tr 0 then we won't ever write out the change.
+ * I suspect this can theoretically happen with other graphics state values too
+ * See PCL file enter.test.
+ */
+ sync_text_state(pdev);
+ pdf_reset_text_state(pdev->text);
+}
+
+/* Open the device. */
+static int
+pdf_open(gx_device * dev)
+{
+ gx_device_pdf *pdev = (gx_device_pdf *) dev;
+ gs_memory_t *mem = pdev->pdf_memory = gs_memory_stable(pdev->memory);
+ int code;
+
+ pdev->InOutputPage = false;
+
+ if ((code = pdf_open_temp_file(pdev, &pdev->xref)) < 0 ||
+ (code = pdf_open_temp_stream(pdev, &pdev->asides)) < 0 ||
+ (code = pdf_open_temp_stream(pdev, &pdev->streams)) < 0 ||
+ (code = pdf_open_temp_stream(pdev, &pdev->pictures)) < 0
+ )
+ goto fail;
+ code = gdev_vector_open_file((gx_device_vector *) pdev, sbuf_size);
+ if (code < 0)
+ goto fail;
+ while (pdev->child) {
+ /* we've been subclassed by gdev_vector_open_file. Ordinarily we would want to call
+ * open_file last, in order to make sure that we don't care if we are subclessed
+ * but we want to set up the stream, so we can't do that....
+ */
+ pdev = (gx_device_pdf *)pdev->child;
+ }
+ if (pdev->ComputeDocumentDigest) {
+ stream *s = s_MD5C_make_stream(pdev->pdf_memory, pdev->strm);
+
+ if (s == NULL)
+ return_error(gs_error_VMerror);
+ pdev->strm = s;
+ }
+ gdev_vector_init((gx_device_vector *) pdev);
+ gp_get_realtime(pdev->uuid_time);
+ pdev->vec_procs = &pdf_vector_procs;
+ pdev->fill_options = pdev->stroke_options = gx_path_type_optimize;
+ /* Set in_page so the vector routines won't try to call */
+ /* any vector implementation procedures. */
+ pdev->in_page = true;
+ /*
+ * pdf_initialize_ids allocates some (global) named objects, so we must
+ * initialize the named objects dictionary now.
+ */
+ pdev->local_named_objects =
+ pdev->global_named_objects =
+ cos_dict_alloc(pdev, "pdf_open(global_named_objects)");
+ /* Initialize internal structures that don't have IDs. */
+ pdev->NI_stack = cos_array_alloc(pdev, "pdf_open(NI stack)");
+ pdev->vgstack = (pdf_viewer_state *)gs_alloc_bytes(pdev->pdf_memory, 11 * sizeof(pdf_viewer_state), "pdf_open(graphics state stack)");
+ if (pdev->vgstack == 0) {
+ code = gs_error_VMerror;
+ goto fail;
+ }
+ memset(pdev->vgstack, 0x00, 11 * sizeof(pdf_viewer_state));
+ pdev->vgstack_size = 11;
+ pdev->Namespace_stack = cos_array_alloc(pdev, "pdf_open(Namespace stack)");
+ pdf_initialize_ids(pdev);
+ code = pdf_compute_fileID(pdev);
+ if (code < 0)
+ goto fail;
+ if (pdev->OwnerPassword.size > 0) {
+ code = pdf_compute_encryption_data(pdev);
+ if (code < 0)
+ goto fail;
+ } else if(pdev->UserPassword.size > 0) {
+ emprintf(pdev->memory,
+ "User password is specified. Need an Owner password or both.\n");
+ return_error(gs_error_rangecheck);
+ } else if (pdev->KeyLength) {
+ emprintf(pdev->memory,
+ "Can't accept encryption options without a password.\n");
+ return_error(gs_error_rangecheck);
+ }
+ /* Now create a new dictionary for the local named objects. */
+ pdev->local_named_objects =
+ cos_dict_alloc(pdev, "pdf_open(local_named_objects)");
+ pdev->outlines_id = 0;
+ pdev->next_page = 0;
+ pdev->text = pdf_text_data_alloc(mem);
+ pdev->sbstack_size = pdev->vgstack_size; /* Overestimated a few. */
+ pdev->sbstack = gs_alloc_struct_array(mem, pdev->sbstack_size, pdf_substream_save,
+ &st_pdf_substream_save_element, "pdf_open");
+ pdev->pages =
+ gs_alloc_struct_array(mem, initial_num_pages, pdf_page_t,
+ &st_pdf_page_element, "pdf_open(pages)");
+ if (pdev->text == 0 || pdev->pages == 0 || pdev->sbstack == 0) {
+ code = gs_error_VMerror;
+ goto fail;
+ }
+ memset(pdev->sbstack, 0, pdev->sbstack_size * sizeof(pdf_substream_save));
+ memset(pdev->pages, 0, initial_num_pages * sizeof(pdf_page_t));
+ pdev->num_pages = initial_num_pages;
+ {
+ int i, j;
+
+ for (i = 0; i < NUM_RESOURCE_TYPES; ++i)
+ for (j = 0; j < NUM_RESOURCE_CHAINS; ++j)
+ pdev->resources[i].chains[j] = 0;
+ }
+ pdev->outline_levels = (pdf_outline_level_t *)gs_alloc_bytes(mem, INITIAL_MAX_OUTLINE_DEPTH * sizeof(pdf_outline_level_t), "outline_levels array");
+ memset(pdev->outline_levels, 0x00, INITIAL_MAX_OUTLINE_DEPTH * sizeof(pdf_outline_level_t));
+ pdev->max_outline_depth = INITIAL_MAX_OUTLINE_DEPTH;
+ pdev->outline_levels[0].first.id = 0;
+ pdev->outline_levels[0].left = max_int;
+ pdev->outline_levels[0].first.action = 0;
+ pdev->outline_levels[0].last.action = 0;
+ pdev->outline_depth = 0;
+ pdev->closed_outline_depth = 0;
+ pdev->outlines_open = 0;
+ pdev->articles = 0;
+ pdev->Dests = 0;
+ pdev->EmbeddedFiles = 0;
+ /* {global,local}_named_objects was initialized above */
+ pdev->PageLabels = 0;
+ pdev->PageLabels_current_page = 0;
+ pdev->PageLabels_current_label = 0;
+ pdev->pte = NULL;
+ pdf_reset_page(pdev);
+ pdev->BBox.p.x = pdev->width;
+ pdev->BBox.p.y = pdev->height;
+ pdev->BBox.q.x = 0;
+ pdev->BBox.q.y = 0;
+
+ if(pdev->UseCIEColor) {
+ emprintf(pdev->memory, "\n\nUse of -dUseCIEColor detected!\nSince the release of version 9.11 of Ghostscript we recommend you do not set\n-dUseCIEColor with the pdfwrite/ps2write device family.\n\n");
+ }
+
+ /* Build a font cache for pdfwrite, see 'pdf_free_pdf_font_cache' for why we need this. */
+ pdev->pdf_font_dir = gs_font_dir_alloc2(pdev->memory->stable_memory, pdev->memory->non_gc_memory);
+ if (pdev->pdf_font_dir == 0) {
+ code = gs_error_VMerror;
+ goto fail;
+ }
+ /* If we have a gs_lib_ctx, then we need to copy these function pointers from it (we are in PostScript).
+ * We can't fill them in otherwise, as the functions are declared static in gsfont.c.
+ * If we don't have one then we are in PCL/PXL/XPS, and cannot copy these function pointers. Fortunately
+ * we don't need them for fonts in these languages.
+ */
+ if (pdev->memory->gs_lib_ctx->font_dir) {
+ pdev->pdf_font_dir->ccache.mark_glyph = pdev->memory->gs_lib_ctx->font_dir->ccache.mark_glyph;
+ pdev->pdf_font_dir->global_glyph_code = pdev->memory->gs_lib_ctx->font_dir->global_glyph_code;
+ }
+
+ /* gs_opendevice() sets the device 'is_open' flag which is now of course the parent. We
+ * still need to set the child's flag, we'll do it here to avoid setting it if we get any
+ * failures, as those will also leave the parent not open.
+ */
+ if (pdev->parent)
+ pdev->is_open = true;
+
+ return 0;
+ fail:
+ gdev_vector_close_file((gx_device_vector *) pdev);
+ return pdf_close_files(pdev, code);
+}
+
+/* Detect I/O errors. */
+static int
+pdf_ferror(gx_device_pdf *pdev)
+{
+ fflush(pdev->file);
+ fflush(pdev->xref.file);
+ sflush(pdev->strm);
+ sflush(pdev->asides.strm);
+ sflush(pdev->streams.strm);
+ sflush(pdev->pictures.strm);
+ return ferror(pdev->file) || ferror(pdev->xref.file) ||
+ ferror(pdev->asides.file) || ferror(pdev->streams.file) ||
+ ferror(pdev->pictures.file);
+}
+
+/* Compute the dominant text orientation of a page. */
+static int
+pdf_dominant_rotation(const pdf_text_rotation_t *ptr)
+{
+ int i, imax = -1;
+ long max_count = 0;
+ static const int angles[] = { pdf_text_rotation_angle_values };
+
+ for (i = 0; i < countof(ptr->counts); ++i) {
+ long count = ptr->counts[i];
+
+ if (count > max_count)
+ imax = i, max_count = count;
+ }
+ return (imax < 0 ? imax : angles[imax]);
+}
+
+/* Print a Rotate command, if requested and possible. */
+static void
+pdf_print_orientation(gx_device_pdf * pdev, pdf_page_t *page)
+{
+ stream *s = pdev->strm;
+ int dsc_orientation = -1;
+ const pdf_page_dsc_info_t *ppdi;
+
+ if (pdev->params.AutoRotatePages == arp_None)
+ return; /* Not requested. */
+
+ ppdi = (page != NULL ? &page->dsc_info : &pdev->doc_dsc_info);
+
+ /* Determine DSC orientation : */
+ if (ppdi->viewing_orientation >= 0)
+ dsc_orientation = ppdi->viewing_orientation;
+ else if (ppdi->orientation >= 0)
+ dsc_orientation = ppdi->orientation;
+ if ((page == NULL && pdev->params.AutoRotatePages == arp_All) || /* document */
+ (page != NULL && page->text_rotation.Rotate >= 0) || /* page */
+ dsc_orientation >= 0 /* have DSC */) {
+ const pdf_text_rotation_t *ptr =
+ (page != NULL ? &page->text_rotation : &pdev->text_rotation);
+ int angle = -1;
+
+ /* Combine DSC rotation with text rotation : */
+ if (dsc_orientation == 0) {
+ if (ptr->Rotate == 0 || ptr->Rotate == 180)
+ angle = ptr->Rotate;
+ } else if (dsc_orientation == 1) {
+ if (ptr->Rotate == 90 || ptr->Rotate == 270)
+ angle = ptr->Rotate;
+ else
+ angle = 90;
+ }
+
+ if (angle < 0) {
+ /* Incompatible Auto-rotation and DSC specification.
+ * Previously we used to defer to the DSC, but if the user has
+ * specified auto-rotation, I htink we should respect that instead.
+ */
+ angle = ptr->Rotate;
+ }
+
+ /* If got some, write it out : */
+ if (angle >= 0)
+ pprintd1(s, "/Rotate %d", angle);
+ }
+}
+
+/* Close the current page. */
+static int
+pdf_close_page(gx_device_pdf * pdev, int num_copies)
+{
+ int page_num;
+ pdf_page_t *page;
+ int code, i;
+
+ /*
+ * If the very first page is blank, we need to open the document
+ * before doing anything else.
+ */
+
+ code = pdfwrite_pdf_open_document(pdev);
+ if (code < 0)
+ return code;
+ if (pdev->ForOPDFRead && pdev->context == PDF_IN_NONE) {
+ /* Must create a context stream for empty pages. */
+ code = pdf_open_contents(pdev, PDF_IN_STREAM);
+ if (code < 0)
+ return code;
+ }
+ pdf_close_contents(pdev, true);
+
+ if (!pdev->DoNumCopies)
+ num_copies = 1;
+
+ for(i=0;i<num_copies;i++) {
+ bool clear_resource_use = i < num_copies - 1 ? 0 : 1;
+
+ page_num = ++(pdev->next_page);
+ /*
+ * We can't write the page object or the annotations array yet, because
+ * later pdfmarks might add elements to them. Write the other objects
+ * that the page references, and record what we'll need later.
+ *
+ * Start by making sure the pages array element exists.
+ */
+
+ pdf_page_id(pdev, page_num);
+ page = &pdev->pages[page_num - 1];
+ page->MediaBox.x = pdev->MediaSize[0];
+ page->MediaBox.y = pdev->MediaSize[1];
+ page->contents_id = pdev->contents_id;
+ page->NumCopies_set = pdev->NumCopies_set;
+ page->NumCopies = pdev->NumCopies;
+ pdf_record_usage(pdev, pdev->contents_id, pdev->next_page);
+ pdf_record_usage(pdev, pdev->contents_length_id, pdev->next_page);
+ pdf_record_usage(pdev, page->Page->id, pdev->next_page);
+
+ /* pdf_store_page_resources sets procsets, resource_ids[]. */
+ code = pdf_store_page_resources(pdev, page, clear_resource_use);
+ if (code < 0)
+ return code;
+
+ /* Write the Functions. */
+
+ code = pdf_write_resource_objects(pdev, resourceFunction);
+ if (code < 0)
+ return code;
+
+ /* Close use of text on the page. */
+
+ pdf_close_text_page(pdev);
+
+ /* Accumulate text rotation. */
+
+ page->text_rotation.Rotate =
+ (pdev->params.AutoRotatePages == arp_PageByPage ?
+ pdf_dominant_rotation(&page->text_rotation) : -1);
+ {
+ int i;
+
+ for (i = 0; i < countof(page->text_rotation.counts); ++i)
+ pdev->text_rotation.counts[i] += page->text_rotation.counts[i];
+ }
+
+ /* Record information from DSC comments. */
+
+ page->dsc_info = pdev->page_dsc_info;
+ if (page->dsc_info.orientation < 0)
+ page->dsc_info.orientation = pdev->doc_dsc_info.orientation;
+ /* Bug 688793 */
+ if (page->dsc_info.viewing_orientation < 0)
+ page->dsc_info.viewing_orientation =
+ pdev->doc_dsc_info.viewing_orientation;
+ if (page->dsc_info.bounding_box.p.x >= page->dsc_info.bounding_box.q.x ||
+ page->dsc_info.bounding_box.p.y >= page->dsc_info.bounding_box.q.y
+ )
+ page->dsc_info.bounding_box = pdev->doc_dsc_info.bounding_box;
+
+ /* Finish up. */
+
+ if(pdf_ferror(pdev))
+ return(gs_note_error(gs_error_ioerror));
+ }
+ pdf_reset_page(pdev);
+ return (pdf_ferror(pdev) ? gs_note_error(gs_error_ioerror) : 0);
+}
+
+/* Write the page object. */
+static double
+round_box_coord(double xy)
+{
+ return (int)(xy * 100 + 0.5) / 100.0;
+}
+static int
+pdf_write_page(gx_device_pdf *pdev, int page_num)
+{
+ long page_id = pdf_page_id(pdev, page_num);
+ pdf_page_t *page = &pdev->pages[page_num - 1];
+ double mediabox[4] = {0, 0};
+ stream *s;
+ const cos_value_t *v_mediabox = cos_dict_find_c_key(page->Page, "/MediaBox");
+
+ /* If we have not been given a MediaBox overriding pdfmark, use the current media size. */
+ s = pdev->strm;
+ pdf_open_obj(pdev, page_id, resourcePage);
+
+ if (v_mediabox == NULL ) {
+ mediabox[2] = round_box_coord(page->MediaBox.x);
+ mediabox[3] = round_box_coord(page->MediaBox.y);
+ pprintg2(s, "<</Type/Page/MediaBox [0 0 %g %g]\n",
+ mediabox[2], mediabox[3]);
+ } else {
+ const byte *p = v_mediabox->contents.chars.data;
+ char buf[100];
+ int l = min (v_mediabox->contents.chars.size, sizeof(buf) - 1);
+ float temp[4]; /* the type is float for sscanf. */
+
+ temp[0] = temp[1] = 0;
+ temp[2] = round_box_coord(page->MediaBox.x);
+ temp[3] = round_box_coord(page->MediaBox.y);
+ memcpy(buf, p, l);
+ buf[l] = 0;
+ if (sscanf(buf, "[ %g %g %g %g ]",
+ &temp[0], &temp[1], &temp[2], &temp[3]) == 4) {
+ cos_dict_delete_c_key(page->Page, "/MediaBox");
+ }
+ pprintg4(s, "<</Type/Page/MediaBox [%g %g %g %g]\n",
+ temp[0], temp[1], temp[2], temp[3]);
+ }
+ if (pdev->PDFX) {
+ const cos_value_t *v_trimbox = cos_dict_find_c_key(page->Page, "/TrimBox");
+ const cos_value_t *v_artbox = cos_dict_find_c_key(page->Page, "/ArtBox");
+ const cos_value_t *v_cropbox = cos_dict_find_c_key(page->Page, "/CropBox");
+ const cos_value_t *v_bleedbox = cos_dict_find_c_key(page->Page, "/BleedBox");
+ double trimbox[4] = {0, 0}, bleedbox[4] = {0, 0};
+ bool print_bleedbox = false;
+
+ trimbox[2] = bleedbox[2] = mediabox[2];
+ trimbox[3] = bleedbox[3] = mediabox[3];
+ /* Offsets are [left right top bottom] according to the Acrobat 7.0
+ distiller parameters manual, 12/7/2004, pp. 102-103. */
+ if (v_trimbox != NULL && v_trimbox->value_type == COS_VALUE_SCALAR) {
+ const byte *p = v_trimbox->contents.chars.data;
+ char buf[100];
+ int l = min (v_trimbox->contents.chars.size, sizeof(buf) - 1);
+ float temp[4]; /* the type is float for sscanf. */
+
+ memcpy(buf, p, l);
+ buf[l] = 0;
+ if (sscanf(buf, "[ %g %g %g %g ]",
+ &temp[0], &temp[1], &temp[2], &temp[3]) == 4) {
+ trimbox[0] = temp[0];
+ trimbox[1] = temp[1];
+ trimbox[2] = temp[2];
+ trimbox[3] = temp[3];
+ cos_dict_delete_c_key(page->Page, "/TrimBox");
+ }
+ if (v_artbox != NULL && v_artbox->value_type == COS_VALUE_SCALAR)
+ cos_dict_delete_c_key(page->Page, "/ArtBox");
+
+ } else if (v_artbox != NULL && v_artbox->value_type == COS_VALUE_SCALAR) {
+ /* We have no TrimBox, but we ahve an ArtBox, set the TrimBox to be
+ * the supplied ArtBox (TrimBox is preferred for PDF/X)
+ */
+ const byte *p = v_artbox->contents.chars.data;
+ char buf[100];
+ int l = min (v_artbox->contents.chars.size, sizeof(buf) - 1);
+ float temp[4]; /* the type is float for sscanf. */
+
+ memcpy(buf, p, l);
+ buf[l] = 0;
+ if (sscanf(buf, "[ %g %g %g %g ]",
+ &temp[0], &temp[1], &temp[2], &temp[3]) == 4) {
+ trimbox[0] = temp[0];
+ trimbox[1] = temp[1];
+ trimbox[2] = temp[2];
+ trimbox[3] = temp[3];
+ cos_dict_delete_c_key(page->Page, "/ArtBox");
+ }
+ } else {
+ if (pdev->PDFXTrimBoxToMediaBoxOffset.size >= 4 &&
+ pdev->PDFXTrimBoxToMediaBoxOffset.data[0] >= 0 &&
+ pdev->PDFXTrimBoxToMediaBoxOffset.data[1] >= 0 &&
+ pdev->PDFXTrimBoxToMediaBoxOffset.data[2] >= 0 &&
+ pdev->PDFXTrimBoxToMediaBoxOffset.data[3] >= 0) {
+ trimbox[0] = mediabox[0] + pdev->PDFXTrimBoxToMediaBoxOffset.data[0];
+ trimbox[1] = mediabox[1] + pdev->PDFXTrimBoxToMediaBoxOffset.data[3];
+ trimbox[2] = mediabox[2] - pdev->PDFXTrimBoxToMediaBoxOffset.data[1];
+ trimbox[3] = mediabox[3] - pdev->PDFXTrimBoxToMediaBoxOffset.data[2];
+ }
+ }
+
+ if (v_bleedbox != NULL && v_bleedbox->value_type == COS_VALUE_SCALAR) {
+ const byte *p = v_bleedbox->contents.chars.data;
+ char buf[100];
+ int l = min (v_bleedbox->contents.chars.size, sizeof(buf) - 1);
+ float temp[4]; /* the type is float for sscanf. */
+
+ memcpy(buf, p, l);
+ buf[l] = 0;
+ if (sscanf(buf, "[ %g %g %g %g ]",
+ &temp[0], &temp[1], &temp[2], &temp[3]) == 4) {
+ if (temp[0] < mediabox[0])
+ bleedbox[0] = mediabox[0];
+ else
+ bleedbox[0] = temp[0];
+ if (temp[1] < mediabox[1])
+ bleedbox[1] = mediabox[1];
+ else
+ bleedbox[1] = temp[1];
+ if (temp[2] > mediabox[2])
+ bleedbox[2] = mediabox[2];
+ else
+ bleedbox[2] = temp[2];
+ if (temp[3] > mediabox[3])
+ bleedbox[3] = mediabox[3];
+ else
+ bleedbox[3] = temp[3];
+ print_bleedbox = true;
+ cos_dict_delete_c_key(page->Page, "/BleedBox");
+ }
+ } else if (pdev->PDFXSetBleedBoxToMediaBox)
+ print_bleedbox = true;
+ else if (pdev->PDFXBleedBoxToTrimBoxOffset.size >= 4 &&
+ pdev->PDFXBleedBoxToTrimBoxOffset.data[0] >= 0 &&
+ pdev->PDFXBleedBoxToTrimBoxOffset.data[1] >= 0 &&
+ pdev->PDFXBleedBoxToTrimBoxOffset.data[2] >= 0 &&
+ pdev->PDFXBleedBoxToTrimBoxOffset.data[3] >= 0) {
+ bleedbox[0] = trimbox[0] - pdev->PDFXBleedBoxToTrimBoxOffset.data[0];
+ bleedbox[1] = trimbox[1] - pdev->PDFXBleedBoxToTrimBoxOffset.data[3];
+ bleedbox[2] = trimbox[2] + pdev->PDFXBleedBoxToTrimBoxOffset.data[1];
+ bleedbox[3] = trimbox[3] + pdev->PDFXBleedBoxToTrimBoxOffset.data[2];
+ print_bleedbox = true;
+ }
+
+ if (print_bleedbox == true) {
+ if (trimbox[0] < bleedbox[0] || trimbox[1] < bleedbox[1] ||
+ trimbox[2] > bleedbox[2] || trimbox[3] > bleedbox[3]) {
+ switch (pdev->PDFACompatibilityPolicy) {
+ case 0:
+ emprintf(pdev->memory,
+ "TrimBox does not fit inside BleedBox, not permitted in PDF/X-3, reverting to normal PDF output\n");
+ pdev->AbortPDFAX = true;
+ pdev->PDFX = 0;
+ break;
+ case 1:
+ emprintf(pdev->memory,
+ "TrimBox does not fit inside BleedBox, not permitted in PDF/X-3, reducing TrimBox\n");
+ if (trimbox[0] < bleedbox[0])
+ trimbox[0] = bleedbox[0];
+ if (trimbox[1] < bleedbox[1])
+ trimbox[1] = bleedbox[1];
+ if (trimbox[2] > bleedbox[2])
+ trimbox[2] = bleedbox[2];
+ if (trimbox[3] > bleedbox[3])
+ trimbox[3] = bleedbox[3];
+ break;
+ case 2:
+ emprintf(pdev->memory,
+ "TrimBox does not fit inside BleedBox, not permitted in PDF/X-3, aborting conversion\n");
+ return gs_error_unknownerror;
+ break;
+ default:
+ emprintf(pdev->memory,
+ "TrimBox does not fit inside BleedBox, not permitted in PDF/X-3\nunrecognised PDFACompatibilityLevel,\nreverting to normal PDF output\n");
+ pdev->AbortPDFAX = true;
+ pdev->PDFX = 0;
+ break;
+ }
+ }
+ }
+
+ if (v_cropbox != NULL && v_cropbox->value_type == COS_VALUE_SCALAR) {
+ const byte *p = v_cropbox->contents.chars.data;
+ char buf[100];
+ int l = min (v_cropbox->contents.chars.size, sizeof(buf) - 1);
+ float temp[4]; /* the type is float for sscanf. */
+
+ memcpy(buf, p, l);
+ buf[l] = 0;
+ if (sscanf(buf, "[ %g %g %g %g ]",
+ &temp[0], &temp[1], &temp[2], &temp[3]) == 4) {
+ cos_dict_delete_c_key(page->Page, "/CropBox");
+ /* Ensure that CropBox is no larger than MediaBox. The spec says *nothing* about
+ * this, but Acrobat Preflight complains if it is larger. This can happen because
+ * we apply 'round_box_coord' to the mediabox at the start of this rouinte.
+ */
+ if (temp[0] < mediabox[0])
+ temp[0] = mediabox[0];
+ if (temp[1] < mediabox[1])
+ temp[1] = mediabox[1];
+ if (temp[2] > mediabox[2])
+ temp[2] = mediabox[2];
+ if (temp[3] > mediabox[3])
+ temp[3] = mediabox[3];
+ pprintg4(s, "/CropBox [%g %g %g %g]\n",
+ temp[0], temp[1], temp[2], temp[3]);
+ /* Make sure TrimBox fits inside CropBox. Spec says 'must not extend
+ * beyond the boundaries' but Acrobat Preflight complains if they
+ * are the same value.
+ */
+ if (trimbox[0] < temp[0] || trimbox[1] < temp[1] ||
+ trimbox[2] > temp[2] || trimbox[3] > temp[3]) {
+ switch (pdev->PDFACompatibilityPolicy) {
+ case 0:
+ emprintf(pdev->memory,
+ "TrimBox does not fit inside CropBox, not permitted in PDF/X-3, reverting to normal PDF output\n");
+ pdev->AbortPDFAX = true;
+ pdev->PDFX = 0;
+ break;
+ case 1:
+ emprintf(pdev->memory,
+ "TrimBox does not fit inside CropBox, not permitted in PDF/X-3, reducing TrimBox\n");
+ if (trimbox[0] < temp[0])
+ trimbox[0] = temp[0];
+ if (trimbox[1] < temp[1])
+ trimbox[1] = temp[1];
+ if (trimbox[2] > temp[2])
+ trimbox[2] = temp[2];
+ if (trimbox[3] > temp[3])
+ trimbox[3] = temp[3];
+ break;
+ case 2:
+ emprintf(pdev->memory,
+ "TrimBox does not fit inside CropBox, not permitted in PDF/X-3, aborting conversion\n");
+ return gs_error_unknownerror;
+ break;
+ default:
+ emprintf(pdev->memory,
+ "TrimBox does not fit inside CropBox, not permitted in PDF/X-3\nunrecognised PDFACompatibilityLevel,\nreverting to normal PDF output\n");
+ pdev->AbortPDFAX = true;
+ pdev->PDFX = 0;
+ break;
+ }
+ }
+ }
+ }
+
+ if (cos_dict_find_c_key(page->Page, "/TrimBox") == NULL &&
+ cos_dict_find_c_key(page->Page, "/ArtBox") == NULL)
+ pprintg4(s, "/TrimBox [%g %g %g %g]\n",
+ trimbox[0], trimbox[1], trimbox[2], trimbox[3]);
+ if (print_bleedbox &&
+ cos_dict_find_c_key(page->Page, "/BleedBox") == NULL)
+ pprintg4(s, "/BleedBox [%g %g %g %g]\n",
+ bleedbox[0], bleedbox[1], bleedbox[2], bleedbox[3]);
+ }
+ pdf_print_orientation(pdev, page);
+ pprintld1(s, "/Parent %ld 0 R\n", pdev->Pages->id);
+ if (pdev->ForOPDFRead && pdev->DoNumCopies && !pdev->ProduceDSC) {
+ if (page->NumCopies_set)
+ pprintld1(s, "/NumCopies %ld\n", page->NumCopies);
+ }
+ if (page->group_id > 0) {
+ pprintld1(s, "/Group %ld 0 R\n", page->group_id);
+ }
+ stream_puts(s, "/Resources<</ProcSet[/PDF");
+ if (page->procsets & ImageB)
+ stream_puts(s, " /ImageB");
+ if (page->procsets & ImageC)
+ stream_puts(s, " /ImageC");
+ if (page->procsets & ImageI)
+ stream_puts(s, " /ImageI");
+ if (page->procsets & Text)
+ stream_puts(s, " /Text");
+ stream_puts(s, "]\n");
+ {
+ int i;
+
+ for (i = 0; i < countof(page->resource_ids); ++i)
+ if (page->resource_ids[i] && pdf_resource_type_names[i]) {
+ stream_puts(s, pdf_resource_type_names[i]);
+ pprintld1(s, " %ld 0 R\n", page->resource_ids[i]);
+ }
+ }
+ stream_puts(s, ">>\n");
+
+ /* Write the annotations array if any. */
+
+ if (page->Annots) {
+ stream_puts(s, "/Annots");
+ COS_WRITE(page->Annots, pdev);
+ COS_FREE(page->Annots, "pdf_write_page(Annots)");
+ page->Annots = 0;
+ }
+ /*
+ * The PDF documentation allows, and this code formerly emitted,
+ * a Contents entry whose value was an empty array. Acrobat Reader
+ * 3 and 4 accept this, but Acrobat Reader 5.0 rejects it.
+ * Fortunately, the Contents entry is optional.
+ */
+ if (page->contents_id != 0)
+ pprintld1(s, "/Contents %ld 0 R\n", page->contents_id);
+
+ /* Write any elements stored by pdfmarks. */
+
+ cos_dict_elements_write(page->Page, pdev);
+
+ stream_puts(s, ">>\n");
+ pdf_end_obj(pdev, resourcePage);
+ return 0;
+}
+
+/* Wrap up ("output") a page. */
+/* if we are doing separate pages, call pdf_close to emit the file, then */
+/* pdf_open to open the next page as a new file */
+/* NB: converting an input PDF with offpage links will generate warnings */
+static int
+pdf_output_page(gx_device * dev, int num_copies, int flush)
+{
+ gx_device_pdf *const pdev = (gx_device_pdf *) dev;
+ int code;
+
+ if (pdev->ForOPDFRead) {
+ code = pdf_close_page(pdev, num_copies);
+ if (code < 0)
+ return code;
+
+ while (pdev->sbstack_depth) {
+ code = pdf_exit_substream(pdev);
+ if (code < 0)
+ return code;
+ }
+ } else {
+ while (pdev->sbstack_depth) {
+ code = pdf_exit_substream(pdev);
+ if (code < 0)
+ return code;
+ }
+ code = pdf_close_page(pdev, num_copies);
+ if (code < 0)
+ return code;
+ }
+
+ if(pdev->UseCIEColor) {
+ emprintf(pdev->memory, "\n\nUse of -dUseCIEColor detected!\nSince the release of version 9.11 of Ghostscript we recommend you do not set\n-dUseCIEColor with the pdfwrite/ps2write device family.\n\n");
+ }
+ if (pdf_ferror(pdev))
+ gs_note_error(gs_error_ioerror);
+
+ if ((code = gx_finish_output_page(dev, num_copies, flush)) < 0)
+ return code;
+
+ if (gx_outputfile_is_separate_pages(((gx_device_vector *)dev)->fname, dev->memory)) {
+ pdev->InOutputPage = true;
+ if ((code = pdf_close(dev)) < 0)
+ return code;
+ code = pdf_open(dev);
+ dev->is_open = true;
+ }
+ return code;
+}
+
+static int find_end_xref_section (gx_device_pdf *pdev, FILE *tfile, int64_t start, gs_offset_t resource_pos)
+{
+ int64_t start_offset = (start - pdev->FirstObjectNumber) * sizeof(gs_offset_t);
+
+ if (gp_fseek_64(tfile, start_offset, SEEK_SET) == 0)
+ {
+ long i, r;
+
+ for (i = start; i < pdev->next_id; ++i) {
+ gs_offset_t pos;
+
+ r = fread(&pos, sizeof(pos), 1, tfile);
+ if (r != 1)
+ return(gs_note_error(gs_error_ioerror));
+ if (pos & ASIDES_BASE_POSITION)
+ pos += resource_pos - ASIDES_BASE_POSITION;
+ pos -= pdev->OPDFRead_procset_length;
+ if (pos == 0) {
+ return i;
+ }
+ }
+ }
+ return pdev->next_id;
+}
+
+static int write_xref_section(gx_device_pdf *pdev, FILE *tfile, int64_t start, int end, gs_offset_t resource_pos, gs_offset_t *Offsets)
+{
+ int64_t start_offset = (start - pdev->FirstObjectNumber) * sizeof(gs_offset_t);
+
+ if (gp_fseek_64(tfile, start_offset, SEEK_SET) == 0)
+ {
+ long i, r;
+
+ for (i = start; i < end; ++i) {
+ gs_offset_t pos;
+ char str[21];
+
+ r = fread(&pos, sizeof(pos), 1, tfile);
+ if (r != 1)
+ return(gs_note_error(gs_error_ioerror));
+ if (pos & ASIDES_BASE_POSITION)
+ pos += resource_pos - ASIDES_BASE_POSITION;
+ pos -= pdev->OPDFRead_procset_length;
+ /* If we are linearising there's no point in writing an xref we will
+ * later replace. Also makes the file slightly smaller reducing the
+ * chances of needing to write white space to pad the file out.
+ */
+ if (!pdev->Linearise) {
+ gs_sprintf(str, "%010"PRId64" 00000 n \n", pos);
+ stream_puts(pdev->strm, str);
+ }
+ if (Offsets)
+ Offsets[i] = pos;
+ }
+ }
+ return 0;
+}
+
+static int
+rewrite_object(gx_device_pdf *const pdev, pdf_linearisation_t *linear_params, int object)
+{
+ ulong read, Size;
+ char c, *Scratch, *source, *target, Buf[280], *next;
+ int code, ID, ScratchSize=16384;
+
+ Size = pdev->ResourceUsage[object].Length;
+
+ Scratch = (char *)gs_alloc_bytes(pdev->pdf_memory, ScratchSize, "Working memory for object rewriting");
+ if (Scratch == 0L)
+ return (gs_note_error(gs_error_VMerror));
+
+ pdev->ResourceUsage[object].LinearisedOffset = gp_ftell_64(linear_params->Lin_File.file);
+ code = gp_fseek_64(linear_params->sfile, pdev->ResourceUsage[object].OriginalOffset, SEEK_SET);
+ if (code < 0)
+ return code;
+
+ read = 0;
+ do {
+ code = fread(&c, 1, 1, linear_params->sfile);
+ read++;
+ } while (c != '\n' && code > 0);
+ gs_sprintf(Scratch, "%d 0 obj\n", pdev->ResourceUsage[object].NewObjectNumber);
+ fwrite(Scratch, strlen(Scratch), 1, linear_params->Lin_File.file);
+
+ code = fread(&c, 1, 1, linear_params->sfile);
+ read++;
+ if (c == '<' || c == '[') {
+ int index = 0;
+ Scratch[index++] = c;
+ do {
+ do {
+ code = fread(&c, 1, 1, linear_params->sfile);
+ Scratch[index++] = c;
+ read++;
+ if (index == ScratchSize - 2) {
+ char *Temp;
+
+ Temp = (char *)gs_alloc_bytes(pdev->pdf_memory, ScratchSize * 2, "Working memory for object rewriting");
+ if (Temp == 0L) {
+ gs_free_object(pdev->pdf_memory, Scratch, "Free working memory for object rewriting");
+ return (gs_note_error(gs_error_VMerror));
+ }
+ memcpy(Temp, Scratch, ScratchSize);
+ gs_free_object(pdev->pdf_memory, Scratch, "Increase working memory for object rewriting");
+ Scratch = Temp;
+ ScratchSize *= 2;
+ }
+ }while (c != '\r' && c != '\n');
+ Scratch[index] = 0;
+ if (strncmp(&Scratch[index - 7], "endobj", 6) == 0 || strncmp(&Scratch[index - 7], "stream", 6) == 0)
+ break;
+ } while (code);
+ } else {
+ Scratch[0] = 0;
+ fwrite(&c, 1, 1, linear_params->Lin_File.file);
+ }
+
+ Size -= read;
+
+ source = Scratch;
+ do {
+ target = strstr(source, " 0 R");
+ if (target) {
+ next = target + 4;
+ do {
+ target--;
+ }while (*target >= '0' && *target <= '9');
+ target++;
+ sscanf(target, "%d 0 R", &ID);
+ fwrite(source, target - source, 1, linear_params->Lin_File.file);
+ gs_sprintf(Buf, "%d 0 R", pdev->ResourceUsage[ID].NewObjectNumber);
+ fwrite(Buf, strlen(Buf), 1, linear_params->Lin_File.file);
+ source = next;
+ } else {
+ fwrite(source, strlen(source), 1, linear_params->Lin_File.file);
+ }
+ } while (target);
+
+ do {
+ if (Size > ScratchSize) {
+ code = fread(Scratch, ScratchSize, 1, linear_params->sfile);
+ if (code != 1)
+ return gs_error_ioerror;
+ fwrite(Scratch, ScratchSize, 1, linear_params->Lin_File.file);
+ Size -= 16384;
+ } else {
+ code = fread(Scratch, Size, 1, linear_params->sfile);
+ if (code != 1)
+ return gs_error_ioerror;
+ fwrite(Scratch, Size, 1, linear_params->Lin_File.file);
+ Size = 0;
+ }
+ } while (Size);
+
+ gs_free_object(pdev->pdf_memory, Scratch, "Free working memory for object rewriting");
+ return 0;
+}
+
+static int flush_hint_stream(pdf_linearisation_t *linear_params)
+{
+ int code;
+
+ code = fwrite(linear_params->HintBuffer, linear_params->HintByte, 1, linear_params->sfile);
+ linear_params->HintBits = 0;
+ linear_params->HintByte = 0;
+ return code;
+}
+
+static int write_hint_stream(pdf_linearisation_t *linear_params, gs_offset_t val, char size_bits)
+{
+ unsigned int input_mask, output_mask;
+
+ if (size_bits == 0)
+ return 0;
+
+ while(size_bits) {
+ input_mask = 1 << (size_bits - 1);
+ output_mask = 0x80 >> linear_params->HintBits;
+ if (input_mask & val)
+ linear_params->HintBuffer[linear_params->HintByte] |= output_mask;
+ else
+ linear_params->HintBuffer[linear_params->HintByte] &= ~output_mask;
+ size_bits--;
+ linear_params->HintBits++;
+ if (linear_params->HintBits == 8) {
+ linear_params->HintByte++;
+ if (linear_params->HintByte > 254) {
+ flush_hint_stream(linear_params);
+ memset(linear_params->HintBuffer, 0x00, 256);
+ }
+ linear_params->HintBits = 0;
+ }
+ }
+ return 0;
+}
+
+static int pdf_linearise(gx_device_pdf *pdev, pdf_linearisation_t *linear_params)
+{
+ char Buffer[1024];
+ char Header[32], Binary[9] = "%\307\354\217\242\n", LDict[1024], fileID[35], Pad;
+ int level = (int)(pdev->CompatibilityLevel * 10 + 0.5), i, j;
+ int code=0, Part1To6 = 2; /* Include space for linearisation dict */
+ int Part7To8 = 1;
+ int PartNone = 1;
+ int Part9 = 1;
+ int LDictObj, HintStreamObj, k;
+ char T;
+ int64_t mainxref, Length, HintStreamLen, HintStreamStart, HintLength, SharedHintOffset;
+
+ fileID[0] = '<';
+ fileID[33] = '>';
+ fileID[34] = 0x00;
+ for (i = 0;i< sizeof(pdev->fileID);i++) {
+ T = pdev->fileID[i] >> 4;
+ if (T > 9) {
+ fileID[(i*2) + 1] = T - 10 + 'A';
+ } else {
+ fileID[(i*2) + 1] = T + '0';
+ }
+ T = pdev->fileID[i] & 0x0f;
+ if (T > 9) {
+ fileID[(i*2) + 2] = T - 10 + 'A';
+ } else {
+ fileID[(i*2) + 2] = T + '0';
+ }
+ }
+
+ /* Make sure we've written everything to the main file */
+ sflush(pdev->strm);
+ linear_params->sfile = pdev->file;
+ linear_params->MainFileEnd = gp_ftell_64(pdev->file);
+#ifdef LINEAR_DEBUGGING
+ code = gx_device_open_output_file((gx_device *)pdev, "/temp/linear.pdf",
+ true, true, &linear_params->Lin_File.file);
+#else
+ code = pdf_open_temp_file(pdev, &linear_params->Lin_File);
+#endif
+ if (code < 0)
+ return code;
+
+ linear_params->Lin_File.strm = 0x0;
+
+ /* Count resources used by page 1 */
+ linear_params->NumPage1Resources=0;
+ for (i = 0;i < pdev->ResourceUsageSize; i++) {
+ if (pdev->ResourceUsage[i].PageUsage == 1)
+ linear_params->NumPage1Resources++;
+ if (pdev->ResourceUsage[i].PageUsage == resource_usage_part1_structure)
+ linear_params->NumPart1StructureResources++;
+ }
+
+ /* Count resources associated with pages */
+ linear_params->NumUniquePageResources=0;
+ linear_params->NumSharedResources=0;
+ for (i = 0;i < pdev->ResourceUsageSize; i++) {
+ if (pdev->ResourceUsage[i].PageUsage > 1)
+ linear_params->NumUniquePageResources++;
+ if (pdev->ResourceUsage[i].PageUsage == resource_usage_page_shared)
+ linear_params->NumSharedResources++;
+ }
+
+ /* Count resources not associated with pages */
+ linear_params->NumNonPageResources=0;
+ for (i = 1;i < pdev->ResourceUsageSize; i++) {
+ if (pdev->ResourceUsage[i].PageUsage == 0)
+ linear_params->NumNonPageResources++;
+ }
+
+ /* Count resources not associated with pages */
+ linear_params->NumPart9Resources=0;
+ for (i = 0;i < pdev->ResourceUsageSize; i++) {
+ if (pdev->ResourceUsage[i].PageUsage == resource_usage_part9_structure)
+ linear_params->NumPart9Resources++;
+ }
+
+ Part1To6 += linear_params->NumUniquePageResources + linear_params->NumSharedResources + linear_params->NumNonPageResources + linear_params->NumPart9Resources;
+ PartNone += linear_params->NumUniquePageResources + linear_params->NumSharedResources;
+ Part9 += linear_params->NumUniquePageResources + linear_params->NumSharedResources + linear_params->NumNonPageResources;
+ LDictObj = linear_params->NumUniquePageResources + linear_params->NumSharedResources + linear_params->NumNonPageResources + linear_params->NumPart9Resources + 1;
+
+ /* Record length and positions of all the objects and calculate the new object number */
+ for (i = 1;i < pdev->ResourceUsageSize; i++) {
+ int j;
+ gs_offset_t end;
+
+ pdev->ResourceUsage[i].OriginalOffset = linear_params->Offsets[i];
+
+ end = linear_params->xref;
+ for (j=0;j<=linear_params->LastResource;j++) {
+ if (linear_params->Offsets[j] > linear_params->Offsets[i] && linear_params->Offsets[j] < end)
+ end = linear_params->Offsets[j];
+ }
+ pdev->ResourceUsage[i].Length = end - linear_params->Offsets[i];
+
+ if (pdev->ResourceUsage[i].PageUsage == 1 || pdev->ResourceUsage[i].PageUsage == resource_usage_part1_structure)
+ pdev->ResourceUsage[i].NewObjectNumber = Part1To6++;
+ else {
+ if (pdev->ResourceUsage[i].PageUsage == resource_usage_not_referenced)
+ pdev->ResourceUsage[i].NewObjectNumber = PartNone++;
+ else {
+ if (pdev->ResourceUsage[i].PageUsage == resource_usage_part9_structure)
+ pdev->ResourceUsage[i].NewObjectNumber = Part9++;
+ else
+ pdev->ResourceUsage[i].NewObjectNumber = Part7To8++;
+ }
+ }
+ }
+ gs_free_object(pdev->pdf_memory, linear_params->Offsets, "free temp xref storage");
+
+#ifdef LINEAR_DEBUGGING
+ {
+ dmprintf6(pdev->pdf_memory, "NumPage1Resources %ld\tNumPart1StructureResources %ld\t\tNumUniquePageResources %ld\tNumSharedResources %ld\t\tNumNonPageResources %d\nNumPart9Resources %ld\n", linear_params->NumPage1Resources, linear_params->NumPart1StructureResources, linear_params->NumUniquePageResources, linear_params->NumSharedResources, linear_params->NumNonPageResources, linear_params->NumPart9Resources);
+ dmprintf(pdev->pdf_memory, "Old ID\tPage Usage\tNew ID\n");
+ for (i = 1;i < pdev->ResourceUsageSize; i++) {
+ dmprintf3(pdev->pdf_memory, "%d\t%d\t%d\n", i, pdev->ResourceUsage[i].PageUsage, pdev->ResourceUsage[i].NewObjectNumber);
+ }
+ }
+#endif
+ /* Linearisation. Part 1, file header */
+ gs_sprintf(Header, "%%PDF-%d.%d\n", level / 10, level % 10);
+ fwrite(Header, strlen(Header), 1, linear_params->Lin_File.file);
+ if (pdev->binary_ok)
+ fwrite(Binary, strlen(Binary), 1, linear_params->Lin_File.file);
+
+ pdf_record_usage(pdev, linear_params->LastResource + 1, resource_usage_part1_structure);
+ pdev->ResourceUsage[linear_params->LastResource + 1].OriginalOffset = 0;
+ pdev->ResourceUsage[linear_params->LastResource + 1].NewObjectNumber = LDictObj;
+ pdev->ResourceUsage[linear_params->LastResource + 1].LinearisedOffset = gp_ftell_64(linear_params->Lin_File.file);
+
+ /* Linearisation. Part 2, the Linearisation dictioanry */
+ linear_params->LDictOffset = gp_ftell_64(linear_params->Lin_File.file);
+ gs_sprintf(LDict, "%d 0 obj\n<< \n",
+ LDictObj);
+ fwrite(LDict, strlen(LDict), 1, linear_params->Lin_File.file);
+
+ /* First page cross-reference table here (Part 3) */
+ linear_params->FirstxrefOffset = gp_ftell_64(linear_params->Lin_File.file);
+ gs_sprintf(Header, "xref\n%d %d\n", LDictObj, Part1To6 - LDictObj + 1); /* +1 for the primary hint stream */
+ fwrite(Header, strlen(Header), 1, linear_params->Lin_File.file);
+
+ gs_sprintf(Header, "0000000000 00000 n \n");
+
+ for (i = LDictObj;i <= linear_params->LastResource + 2; i++) {
+ fwrite(Header, 20, 1, linear_params->Lin_File.file);
+ }
+
+ /* Size below is given as the Last Resource in the original file, +1 for object 0 (always free)
+ * +1 for the linearisation dict and +1 for the primary hint stream.
+ */
+ linear_params->FirsttrailerOffset = gp_ftell_64(linear_params->Lin_File.file);
+ gs_sprintf(LDict, "\ntrailer\n<</Size %ld/Info %d 0 R/Root %d 0 R/ID[%s%s]/Prev %d>>\nstartxref\r\n0\n%%%%EOF\n \n",
+ linear_params->LastResource + 3, pdev->ResourceUsage[linear_params->Info_id].NewObjectNumber, pdev->ResourceUsage[linear_params->Catalog_id].NewObjectNumber, fileID, fileID, 0);
+ fwrite(LDict, strlen(LDict), 1, linear_params->Lin_File.file);
+
+ /* Write document catalog (Part 4) */
+ code = rewrite_object(pdev, linear_params, linear_params->Catalog_id);
+ if (code < 0)
+ goto error;
+
+ /* In here we need the ViewerPreferences (don't think we support this),
+ * the PaegMode entry (I think this would be direct in the catalog), The
+ * Threads entry (if any), The OpenAction (again, direct object ?) The
+ * AcroForm object (don't think we support this) and (TaDa) the Encrypt entry
+ * in the first page trailer dictionary.
+ */
+
+ /* First page section (Part 6) NB we do not currently support the OpenAction
+ * In the Catalogso this is always page 0, this needs to change if we ever
+ * support the OpenAction. The 1.7 PDF Reference says in implementation note 181
+ * that Acrobat always treats page 0 as the first page for linearisation purposes
+ * EVEN IF there is an OpenAction, so we are Acrobat-compatible :-)
+ */
+ code = rewrite_object(pdev, linear_params, pdev->pages[0].Page->id);
+ if (code < 0)
+ goto error;
+ for (i = 0;i < pdev->ResourceUsageSize; i++) {
+ /* we explicitly write the page objct above, make sure when writing the
+ * 'objects uniquely used on the page' that we don't write the page object again!
+ */
+ if (pdev->ResourceUsage[i].PageUsage == 1 && i != pdev->pages[0].Page->id) {
+ code = rewrite_object(pdev, linear_params, i);
+ if (code < 0)
+ goto error;
+ }
+ }
+ linear_params->E = gp_ftell_64(linear_params->Lin_File.file);
+
+ /* Primary Hint Stream here (Part 5)
+ * this is a FIXME
+ */
+ HintStreamObj = linear_params->LastResource + 2;
+ pdf_record_usage(pdev, HintStreamObj, resource_usage_part1_structure);
+ pdev->ResourceUsage[HintStreamObj].OriginalOffset = 0;
+ pdev->ResourceUsage[HintStreamObj].NewObjectNumber = HintStreamObj;
+ pdev->ResourceUsage[HintStreamObj].LinearisedOffset = gp_ftell_64(linear_params->Lin_File.file);
+
+ /* We don't actually embed the hint stream yet. We will do this when we copy the 'linearised'
+ * PDF file from the temp file back to the main file, and will insert the hint stream at this point.
+ * We will then know all the offsets of all the objects following the hint stream, so we can
+ * write these correctly into the hint stream. Since we will then know the size of the hint
+ * stream, we can add this to the stored offsets to generate the 'real' offsets for teh xref.
+ */
+
+ /* All remaining pages (part 7) */
+ for (i = 1;i < pdev->next_page;i++) {
+ code = rewrite_object(pdev, linear_params, pdev->pages[i].Page->id);
+ if (code < 0)
+ goto error;
+ for (j = 0;j < pdev->ResourceUsageSize; j++) {
+ /* we explicitly write the page objct above, make sure when writing the
+ * 'objects uniquely used on the page' that we don't write the page object again!
+ */
+ if (pdev->ResourceUsage[j].PageUsage - 1 == i && j != pdev->pages[i].Page->id) {
+ code = rewrite_object(pdev, linear_params, j);
+ if (code < 0)
+ goto error;
+ }
+ }
+ }
+
+ /* Shared objects for all pages except the first (part 8) */
+ for (i = 0;i < pdev->ResourceUsageSize; i++) {
+ if (pdev->ResourceUsage[i].PageUsage == resource_usage_page_shared) {
+ code = rewrite_object(pdev, linear_params, i);
+ if (code < 0)
+ goto error;
+ }
+ }
+
+ /* All objects not on any page (Part 9) */
+ for (i = 1;i < pdev->ResourceUsageSize; i++) {
+ if (pdev->ResourceUsage[i].PageUsage == resource_usage_not_referenced ||
+ pdev->ResourceUsage[i].PageUsage == resource_usage_part9_structure) {
+ code = rewrite_object(pdev, linear_params, i);
+ if (code < 0)
+ goto error;
+ }
+ }
+
+ /* We won't bother with an overflow hint stream (I Hope) (part 10)
+ * Implementation Note 181 in the 1.7 PDF Reference Manual says Acrobat
+ * doesn't support overflow hint streams anyway....
+ */
+
+ /* Now copy the linearised data back to the main file, as far as the offset of the
+ * primary hint stream
+ */
+ if (gp_fseek_64(linear_params->Lin_File.file, 0, SEEK_SET) != 0) {
+ code = gs_error_ioerror;
+ goto error;
+ }
+ if (gp_fseek_64(linear_params->sfile, 0, SEEK_SET) != 0){
+ code = gs_error_ioerror;
+ goto error;
+ }
+ Length = pdev->ResourceUsage[HintStreamObj].LinearisedOffset;
+ while (Length && code >= 0) {
+ if (Length > 1024) {
+ code = fread(Buffer, 1024, 1, linear_params->Lin_File.file);
+ fwrite(Buffer, 1024, 1, linear_params->sfile);
+ Length -= 1024;
+ } else {
+ code = fread(Buffer, Length, 1, linear_params->Lin_File.file);
+ fwrite(Buffer, Length, 1, linear_params->sfile);
+ Length = 0;
+ }
+ }
+ /* insert the primary hint stream */
+ gs_sprintf(LDict, "%d 0 obj\n<</Length \n/S >>\nstream\n", HintStreamObj);
+ fwrite(LDict, strlen(LDict), 1, linear_params->sfile);
+
+ HintStreamStart = gp_ftell_64(linear_params->sfile);
+ memset(linear_params->HintBuffer, 0x00, 256);
+ linear_params->HintBits = linear_params->HintByte = 0;
+
+ linear_params->PageHints = (page_hint_stream_t *)gs_alloc_bytes(pdev->pdf_memory, pdev->next_page * sizeof(page_hint_stream_t), "Hints for the pages");
+ memset(linear_params->PageHints, 0x00, pdev->next_page * sizeof(page_hint_stream_t));
+ linear_params->NumPageHints = pdev->next_page;
+
+ linear_params->SharedHints = (shared_hint_stream_t *)gs_alloc_bytes(pdev->pdf_memory, (linear_params->NumPage1Resources + linear_params->NumSharedResources) * sizeof(shared_hint_stream_t), "Hints for the shared objects");
+ memset(linear_params->SharedHints, 0x00, (linear_params->NumPage1Resources + linear_params->NumSharedResources) * sizeof(shared_hint_stream_t));
+ linear_params->NumSharedHints = linear_params->NumPage1Resources + linear_params->NumSharedResources;
+
+ memset(&linear_params->PageHintHeader,0x00, sizeof(page_hint_stream_header_t));
+ memset(&linear_params->SharedHintHeader,0x00, sizeof(shared_hint_stream_header_t));
+
+ linear_params->PageHintHeader.LeastObjectsPerPage = 0x7fffffff;
+ linear_params->PageHintHeader.LeastPageLength = 0x7fffffff;
+ linear_params->PageHintHeader.LeastPageOffset = 0x7fffffff;
+ linear_params->PageHintHeader.LeastContentLength = 0x7fffffff;
+
+ /* Record the offset and length of the content stream for each page */
+ for (i=0;i<pdev->next_page;i++) {
+ int PageContentID = pdev->pages[i].contents_id;
+ pdf_linearisation_record_t *record = &pdev->ResourceUsage[PageContentID];
+ page_hint_stream_t *pagehint = &linear_params->PageHints[i];
+
+ pagehint->ContentOffset = record->LinearisedOffset;
+ pagehint->ContentLength = record->Length;
+ }
+
+ /* For every object, record it in the page hints and shared hints. k is a counter
+ * of the current shared object, we increment it each time we find a new one.
+ */
+ k = 0;
+ for (i = 1;i < pdev->ResourceUsageSize; i++) {
+ pdf_linearisation_record_t *record = &pdev->ResourceUsage[i];
+
+ if (record->PageUsage < resource_usage_page_shared || record->PageUsage == resource_usage_not_referenced)
+ continue;
+ if (record->PageUsage == resource_usage_page_shared) {
+ /* shared objects are recorded in the shared object hints, and also the page hints */
+ for (j=0;j<record->NumPagesUsing;j++) {
+ int page = record->PageList[j];
+ page_hint_stream_t *pagehint;
+
+ if (page >= pdev->next_page)
+ /* This can happen if the job makes marks, but does not call showpage */
+ continue;
+
+ pagehint = &linear_params->PageHints[page - 1];
+ if (pagehint->SharedObjectRef){
+ int *Temp = (int *)gs_alloc_bytes(pdev->pdf_memory, (pagehint->NumSharedObjects + 1) * sizeof(int), "realloc shared object hints");
+ memcpy(Temp, pagehint->SharedObjectRef, (pagehint->NumSharedObjects) * sizeof(int));
+ gs_free_object(pdev->pdf_memory, pagehint->SharedObjectRef, "realloc shared object hints");
+ pagehint->SharedObjectRef = (unsigned int *)Temp;
+ } else {
+ pagehint->SharedObjectRef = (unsigned int *)gs_alloc_bytes(pdev->pdf_memory, (pagehint->NumSharedObjects + 1) * sizeof(int), "shared object hints");
+ }
+ pagehint->SharedObjectRef[pagehint->NumSharedObjects] = i;
+ pagehint->NumSharedObjects++;
+ }
+ linear_params->SharedHints[k].ObjectNumber = record->NewObjectNumber;
+ linear_params->SharedHints[k].ObjectOffset = record->LinearisedOffset;
+ linear_params->SharedHints[k++].ObjectLength = record->Length;
+ } else {
+ /* Objects uniquely used on a page are stored in the page hints table, except
+ * page 1 whose objects are *also* stored in the shared objects hints.
+ */
+ int page = record->PageUsage, pageID = pdev->pages[page - 1].Page->id;
+ int64_t LinearisedPageOffset = pdev->ResourceUsage[pageID].LinearisedOffset;
+ page_hint_stream_t *pagehint;
+
+ /* If the final page makes marks but does not call showpage we don't emit it
+ * which can lead to references to non-existent pages.
+ */
+ if (page < pdev->next_page) {
+ pagehint = &linear_params->PageHints[page - 1];
+ pagehint->NumUniqueObjects++;
+ if (record->LinearisedOffset - LinearisedPageOffset > pagehint->PageLength)
+ pagehint->PageLength = (record->LinearisedOffset + record->Length) - LinearisedPageOffset;
+ if (page == 1) {
+ linear_params->SharedHintHeader.FirstPageEntries++;
+ }
+ }
+ }
+ }
+
+ linear_params->PageHintHeader.LargestSharedObject = k;
+
+ for (i = 0;i < linear_params->NumPageHints;i++) {
+ page_hint_stream_t *hint = &linear_params->PageHints[i];
+
+ if (hint->NumUniqueObjects + hint->NumSharedObjects < linear_params->PageHintHeader.LeastObjectsPerPage)
+ linear_params->PageHintHeader.LeastObjectsPerPage = hint->NumUniqueObjects + hint->NumSharedObjects;
+ if (hint->NumUniqueObjects + hint->NumSharedObjects > linear_params->PageHintHeader.MostObjectsPerPage)
+ linear_params->PageHintHeader.MostObjectsPerPage = hint->NumUniqueObjects + hint->NumSharedObjects;
+ if (hint->PageLength < linear_params->PageHintHeader.LeastPageLength)
+ linear_params->PageHintHeader.LeastPageLength = hint->PageLength;
+ if (hint->PageLength > linear_params->PageHintHeader.MostPageLength)
+ linear_params->PageHintHeader.MostPageLength = hint->PageLength;
+ if (hint->ContentOffset < linear_params->PageHintHeader.LeastPageOffset)
+ linear_params->PageHintHeader.LeastPageOffset = hint->ContentOffset;
+ if (hint->ContentOffset > linear_params->PageHintHeader.MostPageOffset)
+ linear_params->PageHintHeader.MostPageOffset = hint->ContentOffset;
+ if (hint->ContentLength < linear_params->PageHintHeader.LeastContentLength)
+ linear_params->PageHintHeader.LeastContentLength = hint->ContentLength;
+ if (hint->ContentLength > linear_params->PageHintHeader.MostContentLength)
+ linear_params->PageHintHeader.MostContentLength = hint->ContentLength;
+ if (hint->NumSharedObjects > linear_params->PageHintHeader.MostSharedObjects)
+ linear_params->PageHintHeader.MostSharedObjects = hint->NumSharedObjects;
+ }
+
+ write_hint_stream(linear_params, (gs_offset_t)linear_params->PageHintHeader.LeastObjectsPerPage, 32);
+ write_hint_stream(linear_params, (gs_offset_t)pdev->ResourceUsage[pdev->pages[0].Page->id].LinearisedOffset, 32);
+ i = (linear_params->PageHintHeader.MostObjectsPerPage - linear_params->PageHintHeader.MostObjectsPerPage + 1);
+ j = 0;
+ while (i) {
+ i = i / 2;
+ j++;
+ }
+ linear_params->PageHintHeader.ObjectNumBits = j;
+ write_hint_stream(linear_params, (gs_offset_t)linear_params->PageHintHeader.ObjectNumBits, 16);
+ write_hint_stream(linear_params, (gs_offset_t)linear_params->PageHintHeader.LeastPageLength, 32);
+ i = (linear_params->PageHintHeader.MostPageLength - linear_params->PageHintHeader.LeastPageLength + 1);
+ j = 0;
+ while (i) {
+ i = i / 2;
+ j++;
+ }
+ linear_params->PageHintHeader.PageLengthNumBits = j;
+ write_hint_stream(linear_params, (gs_offset_t)linear_params->PageHintHeader.PageLengthNumBits, 16);
+ write_hint_stream(linear_params, (gs_offset_t)linear_params->PageHintHeader.LeastPageOffset, 32);
+ i = (linear_params->PageHintHeader.MostPageOffset - linear_params->PageHintHeader.LeastPageOffset + 1);
+ j = 0;
+ while (i) {
+ i = i / 2;
+ j++;
+ }
+ linear_params->PageHintHeader.PageOffsetNumBits = j;
+ write_hint_stream(linear_params, (gs_offset_t)linear_params->PageHintHeader.PageOffsetNumBits, 16);
+ write_hint_stream(linear_params, (gs_offset_t)linear_params->PageHintHeader.LeastContentLength, 32);
+ i = (linear_params->PageHintHeader.MostContentLength - linear_params->PageHintHeader.LeastContentLength + 1);
+ j = 0;
+ while (i) {
+ i = i / 2;
+ j++;
+ }
+ linear_params->PageHintHeader.ContentLengthNumBits = j;
+ write_hint_stream(linear_params, (gs_offset_t)linear_params->PageHintHeader.ContentLengthNumBits, 16);
+ write_hint_stream(linear_params, (gs_offset_t)linear_params->PageHintHeader.MostSharedObjects, 16);
+ i = (linear_params->PageHintHeader.LargestSharedObject + 1);
+ j = 0;
+ while (i) {
+ i = i / 2;
+ j++;
+ }
+ linear_params->PageHintHeader.SharedObjectNumBits = j;
+ write_hint_stream(linear_params, (gs_offset_t)linear_params->PageHintHeader.SharedObjectNumBits, 16);
+ j = 1;
+ write_hint_stream(linear_params, (gs_offset_t)j, 16);
+ write_hint_stream(linear_params, (gs_offset_t)j, 16);
+
+#ifdef LINEAR_DEBUGGING
+ dmprintf1(pdev->pdf_memory, "LeastObjectsPerPage %d\n", linear_params->PageHintHeader.LeastObjectsPerPage);
+ dmprintf1(pdev->pdf_memory, "Page 1 Offset %"PRId64"\n", pdev->ResourceUsage[pdev->pages[0].Page->id].LinearisedOffset);
+ dmprintf1(pdev->pdf_memory, "ObjectNumBits %d\n", linear_params->PageHintHeader.ObjectNumBits);
+ dmprintf1(pdev->pdf_memory, "LeastPageLength %d\n", linear_params->PageHintHeader.LeastPageLength);
+ dmprintf1(pdev->pdf_memory, "MostPagelength %d\n", linear_params->PageHintHeader.MostPageLength);
+ dmprintf1(pdev->pdf_memory, "PaegLengthNumBits %d\n", linear_params->PageHintHeader.PageLengthNumBits);
+ dmprintf1(pdev->pdf_memory, "LeastPageOffset %d\n", linear_params->PageHintHeader.LeastPageOffset);
+ dmprintf1(pdev->pdf_memory, "MostPageOffset %d\n", linear_params->PageHintHeader.MostPageOffset);
+ dmprintf1(pdev->pdf_memory, "PageOffsetNumBits %d\n", linear_params->PageHintHeader.PageOffsetNumBits);
+ dmprintf1(pdev->pdf_memory, "LeastContentLength %d\n", linear_params->PageHintHeader.LeastContentLength);
+ dmprintf1(pdev->pdf_memory, "MostContentLength %d\n", linear_params->PageHintHeader.MostContentLength);
+ dmprintf1(pdev->pdf_memory, "COntentLengthNumBits %d\n", linear_params->PageHintHeader.ContentLengthNumBits);
+ dmprintf1(pdev->pdf_memory, "MostSharedObjects %d\n", linear_params->PageHintHeader.MostSharedObjects);
+ dmprintf1(pdev->pdf_memory, "LargetsSharedObject %d\n", linear_params->PageHintHeader.LargestSharedObject);
+ dmprintf1(pdev->pdf_memory, "SharedObjectNumBits %d\n", linear_params->PageHintHeader.SharedObjectNumBits);
+ dmprintf(pdev->pdf_memory, "Position Numerator 1\n");
+ dmprintf(pdev->pdf_memory, "Position Denominator 1\n\n");
+#endif
+
+ for (i=0;i < pdev->next_page;i++) {
+ page_hint_stream_t *hint = &linear_params->PageHints[i];
+ int Num;
+
+ Num = hint->NumUniqueObjects - linear_params->PageHintHeader.LeastObjectsPerPage;
+ write_hint_stream(linear_params, (gs_offset_t)Num, linear_params->PageHintHeader.ObjectNumBits);
+ dmprintf2(pdev->pdf_memory, "Page %d NumUniqueObjects %d\n", i, Num);
+ }
+ for (i=0;i < pdev->next_page;i++) {
+ page_hint_stream_t *hint = &linear_params->PageHints[i];
+ int Num;
+
+ Num = hint->PageLength - linear_params->PageHintHeader.LeastPageLength;
+ write_hint_stream(linear_params, (gs_offset_t)Num, linear_params->PageHintHeader.PageLengthNumBits);
+ dmprintf2(pdev->pdf_memory, "Page %d PageLength %d\n", i, Num);
+ }
+ for (i=0;i < pdev->next_page;i++) {
+ page_hint_stream_t *hint = &linear_params->PageHints[i];
+
+ if (i == 0) {
+ write_hint_stream(linear_params, (gs_offset_t)i, linear_params->PageHintHeader.SharedObjectNumBits);
+ dmprintf2(pdev->pdf_memory, "Page %d NumSharedObjects %d\n", i, 1);
+ }
+ else {
+ write_hint_stream(linear_params, (gs_offset_t)hint->NumSharedObjects, linear_params->PageHintHeader.SharedObjectNumBits);
+ dmprintf2(pdev->pdf_memory, "Page %d NumSharedObjects %d\n", i, hint->NumSharedObjects);
+ }
+ }
+ for (i=1;i < pdev->next_page;i++) {
+ page_hint_stream_t *hint = &linear_params->PageHints[i];
+
+ for (j=0;j < hint->NumSharedObjects;j++) {
+ write_hint_stream(linear_params, (gs_offset_t)hint->SharedObjectRef[j], linear_params->PageHintHeader.SharedObjectNumBits);
+ dmprintf3(pdev->pdf_memory, "Page %d SharedObject %d ObjectRef %d\n", i, j, hint->SharedObjectRef[j]);
+ }
+ }
+
+ for (i=0;i < pdev->next_page;i++) {
+ page_hint_stream_t *hint = &linear_params->PageHints[i];
+
+ for (j=0;j < hint->NumSharedObjects;j++) {
+ write_hint_stream(linear_params, (gs_offset_t)j, 1);
+ dmprintf2(pdev->pdf_memory, "Page %d SharedObject %d Position Numerator 1\n", i, j);
+ }
+ }
+ for (i=1;i < pdev->next_page;i++) {
+ page_hint_stream_t *hint = &linear_params->PageHints[i];
+ int Num;
+
+ Num = hint->ContentOffset - linear_params->PageHintHeader.LeastPageOffset;
+ write_hint_stream(linear_params, (gs_offset_t)Num, linear_params->PageHintHeader.PageOffsetNumBits);
+ dmprintf2(pdev->pdf_memory, "Page %d ContentStreamOffset %d\n", i, Num);
+ }
+ for (i=1;i < pdev->next_page;i++) {
+ page_hint_stream_t *hint = &linear_params->PageHints[i];
+ int Num;
+
+ Num = hint->ContentLength - linear_params->PageHintHeader.LeastContentLength;
+ write_hint_stream(linear_params, (gs_offset_t)Num, linear_params->PageHintHeader.ContentLengthNumBits);
+ dmprintf2(pdev->pdf_memory, "Page %d ContentStreamLength %d\n", i, Num);
+ }
+ flush_hint_stream(linear_params);
+
+ SharedHintOffset = gp_ftell_64(linear_params->sfile) - HintStreamStart;
+ linear_params->SharedHintHeader.FirstSharedObject = linear_params->SharedHints[0].ObjectNumber;
+ linear_params->SharedHintHeader.LeastObjectLength = linear_params->SharedHints[0].ObjectLength;
+ linear_params->SharedHintHeader.MostObjectLength = linear_params->SharedHints[0].ObjectLength;
+
+ for (i = 1; i< linear_params->NumSharedHints; i++) {
+ if (linear_params->SharedHints[i].ObjectNumber < linear_params->SharedHintHeader.FirstSharedObject) {
+ linear_params->SharedHintHeader.FirstSharedObject = linear_params->SharedHints[1].ObjectNumber;
+ linear_params->SharedHintHeader.FirstObjectOffset = linear_params->SharedHints[1].ObjectOffset;
+ }
+ if (linear_params->SharedHints[i].ObjectLength < linear_params->SharedHintHeader.LeastObjectLength) {
+ linear_params->SharedHintHeader.LeastObjectLength = linear_params->SharedHints[i].ObjectLength;
+ }
+ if (linear_params->SharedHints[i].ObjectLength > linear_params->SharedHintHeader.MostObjectLength) {
+ linear_params->SharedHintHeader.MostObjectLength = linear_params->SharedHints[i].ObjectLength;
+ }
+ }
+
+ linear_params->SharedHintHeader.FirstPageEntries = linear_params->NumPage1Resources;
+ linear_params->SharedHintHeader.NumSharedObjects = linear_params->NumSharedResources + linear_params->SharedHintHeader.FirstPageEntries;
+
+ write_hint_stream(linear_params, (gs_offset_t)linear_params->SharedHintHeader.FirstSharedObject, 32);
+ dmprintf1(pdev->pdf_memory, "\nFirstSharedObject %d\n", linear_params->SharedHintHeader.FirstSharedObject);
+ write_hint_stream(linear_params, (gs_offset_t)linear_params->SharedHintHeader.FirstObjectOffset, 32);
+ dmprintf1(pdev->pdf_memory, "FirstObjectOffset %"PRId64"\n", linear_params->SharedHintHeader.FirstObjectOffset);
+ write_hint_stream(linear_params, (gs_offset_t)linear_params->SharedHintHeader.FirstPageEntries, 32);
+ dmprintf1(pdev->pdf_memory, "FirstPageEntries %d\n", linear_params->SharedHintHeader.FirstPageEntries);
+ write_hint_stream(linear_params, (gs_offset_t)linear_params->SharedHintHeader.NumSharedObjects, 32);
+ dmprintf1(pdev->pdf_memory, "NumSharedObjects %d\n", linear_params->SharedHintHeader.NumSharedObjects);
+ j = 1;
+ write_hint_stream(linear_params, (gs_offset_t)j, 32);
+ dmprintf(pdev->pdf_memory, "GreatestObjectsNumBits 1\n");
+ write_hint_stream(linear_params, (gs_offset_t)linear_params->SharedHintHeader.FirstObjectOffset, 16);
+ dmprintf1(pdev->pdf_memory, "FirstObjectOffset %"PRId64"\n", linear_params->SharedHintHeader.FirstObjectOffset);
+ write_hint_stream(linear_params, (gs_offset_t)linear_params->SharedHintHeader.LeastObjectLength, 32);
+ dmprintf1(pdev->pdf_memory, "LeastObjectLength %d\n", linear_params->SharedHintHeader.LeastObjectLength);
+
+ i = (linear_params->SharedHintHeader.MostObjectLength - linear_params->SharedHintHeader.LeastObjectLength + 1) / 2;
+ j = 0;
+ while (i) {
+ i = i / 2;
+ j++;
+ }
+ linear_params->SharedHintHeader.LengthNumBits = j + 1;
+ write_hint_stream(linear_params, (gs_offset_t)linear_params->SharedHintHeader.LengthNumBits, 16);
+
+ for (i = 0; i< linear_params->NumSharedHints; i++) {
+ unsigned int Length = linear_params->SharedHints[i].ObjectLength - linear_params->SharedHintHeader.LeastObjectLength;
+
+ write_hint_stream(linear_params, (gs_offset_t)Length, linear_params->SharedHintHeader.LengthNumBits);
+ dmprintf2(pdev->pdf_memory, "Shared Object group %d, Length %d\n", i, Length);
+ }
+
+ j = 0;
+ for (i = 0; i< linear_params->NumSharedHints; i++) {
+ write_hint_stream(linear_params, (gs_offset_t)j, 1);
+ dmprintf1(pdev->pdf_memory, "Shared Object group %d, SignatureFlag false\n", i);
+ }
+ for (i = 0; i< linear_params->NumSharedHints; i++) {
+ write_hint_stream(linear_params, (gs_offset_t)j, 1);
+ dmprintf1(pdev->pdf_memory, "Shared Object group %d, NumObjects 1\n", i);
+ }
+
+ flush_hint_stream(linear_params);
+ HintLength = gp_ftell_64(linear_params->sfile) - HintStreamStart;
+
+ gs_sprintf(LDict, "\nendstream\nendobj\n");
+ fwrite(LDict, strlen(LDict), 1, linear_params->sfile);
+ /* Calculate the length of the primary hint stream */
+ HintStreamLen = gp_ftell_64(linear_params->sfile) - pdev->ResourceUsage[HintStreamObj].LinearisedOffset;
+ /* Read remainder of linearised file and write to the final file */
+ do {
+ code = fread(Buffer, 1, 1024, linear_params->Lin_File.file);
+ if (code > 0)
+ fwrite(Buffer, 1, code, linear_params->sfile);
+ } while (code > 0);
+
+ /* Main xref (part 11) */
+ mainxref = gp_ftell_64(linear_params->sfile);
+ /* Acrobat 9 and X (possibly other versions) won't recognise a file as
+ * optimised unless the file is at least 4k bytes in length (!!!)
+ * Also, it is possible that the new file might be smaller than the old one, If a
+ * frequently used object changed to a lower number (eg form object 100 to object 10)
+ * We don't close and reopen the file, so we need to make sure that any difference
+ * is filled in with white space.
+ */
+ if (linear_params->MainFileEnd < 4096)
+ Length = 4096 - (mainxref + strlen(LDict) + strlen(Header) + LDictObj * 20);
+ else
+ Length = linear_params->MainFileEnd - (mainxref + strlen(LDict) + strlen(Header) + LDictObj * 20);
+ Pad = ' ';
+
+ while(Length > 0) {
+ fwrite(&Pad, 1, 1, linear_params->sfile);
+ Length--;
+ }
+
+ /* Now the file is long enough, write the xref */
+ mainxref = gp_ftell_64(linear_params->sfile);
+ gs_sprintf(Header, "xref\n0 %d\n", LDictObj);
+ fwrite(Header, strlen(Header), 1, linear_params->sfile);
+
+ linear_params->T = gp_ftell_64(linear_params->sfile) - 1;
+ gs_sprintf(Header, "0000000000 65535 f \n");
+ fwrite(Header, strlen(Header), 1, linear_params->sfile);
+
+ for (i = 1;i < LDictObj; i++) {
+ for (j = 0; j < pdev->ResourceUsageSize;j++) {
+ if (pdev->ResourceUsage[j].NewObjectNumber == i) {
+ gs_sprintf(Header, "%010"PRId64" 00000 n \n", pdev->ResourceUsage[j].LinearisedOffset + HintStreamLen);
+ fwrite(Header, 20, 1, linear_params->sfile);
+ }
+ }
+ }
+
+ gs_sprintf(LDict, "trailer\n<</Size %d>>\nstartxref\n%"PRId64"\n%%%%EOF\n",
+ LDictObj, linear_params->FirstxrefOffset);
+ fwrite(LDict, strlen(LDict), 1, linear_params->sfile);
+
+ linear_params->FileLength = gp_ftell_64(linear_params->sfile);
+ /* Return to the linearisation dictionary and write it again filling
+ * in the missing data.
+ */
+ /* Implementation Note 178 in the PDF Reference 1.7 says that Acrobat requires
+ * a white space after the '[' for the hint stream offset. This appears not to be true
+ * for current versions, but we follow it anyway for the possible benefit of older
+ * versions.
+ */
+ gp_fseek_64(linear_params->sfile, linear_params->LDictOffset, SEEK_SET);
+ gs_sprintf(LDict, "%d 0 obj\n<</Linearized 1/L %"PRId64"/H[ ", LDictObj, linear_params->FileLength);
+ fwrite(LDict, strlen(LDict), 1, linear_params->sfile);
+
+ gs_sprintf(LDict, "%"PRId64"", pdev->ResourceUsage[HintStreamObj].LinearisedOffset);
+ fwrite(LDict, strlen(LDict), 1, linear_params->sfile);
+ gs_sprintf(LDict, " %"PRId64"]", HintStreamLen);
+ fwrite(LDict, strlen(LDict), 1, linear_params->sfile);
+ /* Implementation Note 180 in hte PDF Reference 1.7 says that Acrobat
+ * gets the 'E' value wrong. So its probably not important....
+ */
+ gs_sprintf(LDict, "/O %d/E %"PRId64"",pdev->ResourceUsage[pdev->pages[0].Page->id].NewObjectNumber, linear_params->E);
+ fwrite(LDict, strlen(LDict), 1, linear_params->sfile);
+ gs_sprintf(LDict, "/N %d/T %"PRId64">>\nendobj\n", pdev->next_page, linear_params->T);
+ fwrite(LDict, strlen(LDict), 1, linear_params->sfile);
+
+ /* Return to the secondary xref and write it again filling
+ * in the missing offsets.
+ */
+ if (gp_fseek_64(linear_params->sfile, linear_params->FirstxrefOffset, SEEK_SET) != 0) {
+ code = gs_error_ioerror;
+ goto error;
+ }
+ gs_sprintf(Header, "xref\n%d %d\n", LDictObj, Part1To6 - LDictObj + 1); /* +1 for the primary hint stream */
+ fwrite(Header, strlen(Header), 1, linear_params->sfile);
+
+ for (i = LDictObj;i <= linear_params->LastResource + 2; i++) {
+ for (j = 0; j < pdev->ResourceUsageSize;j++) {
+ if (pdev->ResourceUsage[j].NewObjectNumber == i) {
+ gs_sprintf(Header, "%010"PRId64" 00000 n \n", pdev->ResourceUsage[j].LinearisedOffset);
+ fwrite(Header, 20, 1, linear_params->sfile);
+ }
+ }
+ }
+
+ /* Return to the secondary trailer dict and write it again filling
+ * in the missing values.
+ */
+ code = gp_fseek_64(linear_params->sfile, linear_params->FirsttrailerOffset, SEEK_SET);
+ gs_sprintf(LDict, "\ntrailer\n<</Size %ld/Info %d 0 R/Root %d 0 R/ID[%s%s]/Prev %"PRId64">>\nstartxref\r\n0\n%%%%EOF\n",
+ linear_params->LastResource + 3, pdev->ResourceUsage[linear_params->Info_id].NewObjectNumber, pdev->ResourceUsage[linear_params->Catalog_id].NewObjectNumber, fileID, fileID, mainxref);
+ fwrite(LDict, strlen(LDict), 1, linear_params->sfile);
+
+ code = gp_fseek_64(linear_params->sfile, pdev->ResourceUsage[HintStreamObj].LinearisedOffset, SEEK_SET);
+ gs_sprintf(LDict, "%d 0 obj\n<</Length %10"PRId64"", HintStreamObj, HintLength);
+ fwrite(LDict, strlen(LDict), 1, linear_params->sfile);
+ gs_sprintf(LDict, "\n/S %10"PRId64">>\nstream\n", SharedHintOffset);
+ fwrite(LDict, strlen(LDict), 1, linear_params->sfile);
+
+error:
+#ifdef LINEAR_DEBUGGING
+ fclose(linear_params->Lin_File.file);
+#else
+ pdf_close_temp_file(pdev, &linear_params->Lin_File, code);
+#endif
+ /* FIXME free all the linearisation records */
+
+ for (i=0;i<pdev->next_page;i++) {
+ page_hint_stream_t *pagehint = &linear_params->PageHints[i];
+
+ if (pagehint->SharedObjectRef)
+ gs_free_object(pdev->pdf_memory, pagehint->SharedObjectRef, "Free Shared object references");
+ }
+
+ gs_free_object(pdev->pdf_memory, linear_params->PageHints, "Free Page Hint data");
+ gs_free_object(pdev->pdf_memory, linear_params->SharedHints, "Free Shared hint data");
+
+ return code;
+}
+
+int pdf_record_usage(gx_device_pdf *const pdev, long resource_id, int page_num)
+{
+ int i;
+ void *Temp;
+ pdf_linearisation_record_t *resize;
+
+ if (!pdev->Linearise)
+ return 0;
+
+ if (resource_id < 0)
+ return 0;
+
+ if (resource_id >= pdev->ResourceUsageSize) {
+ if (pdev->ResourceUsageSize == 0) {
+ pdev->ResourceUsageSize = resource_id + 1;
+ pdev->ResourceUsage = gs_alloc_struct_array(pdev->pdf_memory, resource_id + 1, pdf_linearisation_record_t,
+ &st_pdf_linearisation_record_element, "start resource usage array");
+ memset((char *)pdev->ResourceUsage, 0x00, (resource_id + 1) * sizeof(pdf_linearisation_record_t));
+ } else {
+ resize = gs_resize_object(pdev->pdf_memory, pdev->ResourceUsage, resource_id + 1, "resize resource usage array");
+ memset(&resize[pdev->ResourceUsageSize], 0x00, sizeof(pdf_linearisation_record_t) * (resource_id - pdev->ResourceUsageSize + 1));
+ pdev->ResourceUsageSize = resource_id + 1;
+ pdev->ResourceUsage = resize;
+ }
+ }
+ if (page_num > 0) {
+ if (pdev->ResourceUsage[resource_id].PageUsage == 0)
+ pdev->ResourceUsage[resource_id].PageUsage = page_num;
+ else {
+ if (pdev->ResourceUsage[resource_id].PageUsage > 1)
+ pdev->ResourceUsage[resource_id].PageUsage = resource_usage_page_shared;
+ else {
+ /* Should not happen, raise an error */
+ }
+ }
+ } else {
+ if (pdev->ResourceUsage[resource_id].PageUsage != 0)
+ /* Should not happen, raise an error */
+ ;
+ pdev->ResourceUsage[resource_id].PageUsage = page_num;
+ }
+
+ /* First check we haven't already recorded this Resource being used by this page */
+ if (pdev->ResourceUsage[resource_id].NumPagesUsing != 0) {
+ for (i=0;i < pdev->ResourceUsage[resource_id].NumPagesUsing; i++) {
+ if (pdev->ResourceUsage[resource_id].PageList[i] == page_num)
+ return 0;
+ }
+ }
+ Temp = gs_alloc_bytes(pdev->pdf_memory, (pdev->ResourceUsage[resource_id].NumPagesUsing + 1) * sizeof (int), "Page usage records");
+ memset((char *)Temp, 0x00, (pdev->ResourceUsage[resource_id].NumPagesUsing + 1) * sizeof (int));
+ memcpy((char *)Temp, pdev->ResourceUsage[resource_id].PageList, pdev->ResourceUsage[resource_id].NumPagesUsing * sizeof (int));
+ gs_free_object(pdev->pdf_memory, (byte *)pdev->ResourceUsage[resource_id].PageList, "Free old page usage records");
+ pdev->ResourceUsage[resource_id].PageList = (int *)Temp;
+ pdev->ResourceUsage[resource_id].PageList[pdev->ResourceUsage[resource_id].NumPagesUsing] = page_num;
+ pdev->ResourceUsage[resource_id].NumPagesUsing++;
+
+ return 0;
+}
+
+int pdf_record_usage_by_parent(gx_device_pdf *const pdev, long resource_id, long parent_id)
+{
+ int i;
+ if (!pdev->Linearise)
+ return 0;
+
+ if (pdev->ResourceUsage[parent_id].PageUsage >= 0)
+ pdf_record_usage(pdev, resource_id, pdev->ResourceUsage[parent_id].PageUsage);
+ else {
+ for (i = 0; i < pdev->ResourceUsage[parent_id].NumPagesUsing; i++) {
+ pdf_record_usage(pdev, resource_id, pdev->ResourceUsage[parent_id].PageList[i]);
+ }
+ }
+ return 0;
+}
+
+/* These two routines are related to the PCL interpreter. Because of the way that
+ * PCL pass through works, the PCL interpreter can shut down and free its font cache
+ * while still running. This leaves us with copies of fonts, which point to a now
+ * freed font cache. Large parts of the code which retrieve font information require
+ * that the font cache be present, and fail badly if it isn't. So we construct a
+ * font cache of our own, and when we copy fonts from the interpreter we point the
+ * copied font at this font cache, instead of the one the original font used.
+ * This allows the PCL interpreter to shut down and free its cache, thus eliminating
+ * a memory leak, while still allowing pdfwrite to retrieve the information it needs
+ * from the copied fonts.
+ * Here we need to shut down and free our font cache.
+ */
+static bool
+purge_all(const gs_memory_t * mem, cached_char * cc, void *dummy)
+{
+ return true;
+}
+
+static void pdf_free_pdf_font_cache(gx_device_pdf *pdev)
+{
+ if (pdev->pdf_font_dir) {
+ gx_purge_selected_cached_chars(pdev->pdf_font_dir,
+ purge_all,
+ (void *)NULL);
+ /* free character cache machinery */
+ gs_free_object(pdev->pdf_font_dir->memory, pdev->pdf_font_dir->fmcache.mdata, "pdf_free_pdf_font_cache");
+ {
+ /* free the circular list of memory chunks first */
+ gx_bits_cache_chunk *chunk = pdev->pdf_font_dir->ccache.chunks;
+ gx_bits_cache_chunk *start_chunk = chunk;
+ gx_bits_cache_chunk *prev_chunk;
+ while (1) {
+ if (start_chunk == chunk->next) {
+ gs_free_object(pdev->pdf_font_dir->ccache.bits_memory, chunk->data, "pdf_free_pdf_font_cache");
+ gs_free_object(pdev->pdf_font_dir->ccache.bits_memory, chunk, "pdf_free_pdf_font_cache");
+ break;
+ }
+ prev_chunk = chunk;
+ chunk = chunk->next;
+ gs_free_object(pdev->pdf_font_dir->ccache.bits_memory, prev_chunk->data, "pdf_free_pdf_font_cache");
+ gs_free_object(pdev->pdf_font_dir->ccache.bits_memory, prev_chunk, "pdf_free_pdf_font_cache");
+ }
+
+ gs_free_object(pdev->pdf_font_dir->memory, pdev->pdf_font_dir->ccache.table, "pdf_free_pdf_font_cache");
+ gs_free_object(pdev->pdf_font_dir->memory, pdev->pdf_font_dir, "pdf_free_pdf_font_cache");
+ pdev->pdf_font_dir = 0;
+ }
+ }
+}
+
+/* Close the device. */
+static int
+pdf_close(gx_device * dev)
+{
+ gx_device_pdf *const pdev = (gx_device_pdf *) dev;
+ gs_memory_t *mem = pdev->pdf_memory;
+ stream *s;
+ FILE *tfile = pdev->xref.file;
+ gs_offset_t xref = 0;
+ gs_offset_t resource_pos;
+ long Catalog_id = 0, Info_id = 0,
+ Pages_id = 0, Encrypt_id = 0;
+ long Threads_id = 0;
+ bool partial_page = (pdev->contents_id != 0 && pdev->next_page != 0);
+ int code = 0, code1, pagecount=0;
+ int64_t start_section, end_section;
+ char str[256];
+ pdf_linearisation_t linear_params;
+
+ if (!dev->is_open)
+ return gs_error_undefined;
+ dev->is_open = false;
+
+ Catalog_id = pdev->Catalog->id;
+ Info_id = pdev->Info->id;
+ Pages_id = pdev->Pages->id;
+
+ memset(&linear_params, 0x00, sizeof(linear_params));
+ linear_params.Info_id = Info_id;
+ linear_params.Pages_id = Pages_id;
+ linear_params.Catalog_id = Catalog_id;
+
+ /*
+ * If this is an EPS file, or if the file didn't end with a showpage for
+ * some other reason, or if the file has produced no marks at all, we
+ * need to tidy up a little so as not to produce illegal PDF. However,
+ * if there is at least one complete page, we discard any leftover
+ * marks.
+ */
+ if (pdev->next_page == 0) {
+ /* If we didn't get called from pdf_output_page, and we are doign file-per-page
+ * output, then the call from close_device will leave an empty file which we don't
+ * want. So here we delete teh file.
+ */
+ if (!pdev->InOutputPage && gx_outputfile_is_separate_pages(pdev->fname, pdev->memory)) {
+ code = gdev_vector_close_file((gx_device_vector *) pdev);
+ if (code != 0)
+ return code;
+ code = gx_device_delete_output_file((const gx_device *)pdev, pdev->fname);
+ if (code != 0)
+ return gs_note_error(gs_error_ioerror);
+ return code;
+ }
+ code = pdf_open_page(pdev, PDF_IN_STREAM);
+
+ if (code < 0)
+ return code;
+ }
+ if (pdev->contents_id != 0)
+ pdf_close_page(pdev, 1);
+
+ /* Write the page objects. */
+ if (!(pdev->ForOPDFRead && pdev->ProduceDSC)) {
+ for (pagecount = 1; pagecount <= pdev->next_page; ++pagecount)
+ pdf_write_page(pdev, pagecount);
+ }
+
+ if (pdev->PrintStatistics)
+ pdf_print_resource_statistics(pdev);
+
+ /* Write the font resources and related resources. */
+ code1 = pdf_write_resource_objects(pdev, resourceXObject);
+ if (code >= 0)
+ code = code1;
+ code1 = pdf_free_resource_objects(pdev, resourceXObject);
+ if (code >= 0)
+ code = code1;
+ code1 = pdf_free_resource_objects(pdev, resourceProperties);
+ if (code >= 0)
+ code = code1;
+ code1 = pdf_write_resource_objects(pdev, resourceGroup);
+ if (code >= 0)
+ code = code1;
+ code1 = pdf_free_resource_objects(pdev, resourceGroup);
+ if (code >= 0)
+ code = code1;
+ code1 = pdf_write_resource_objects(pdev, resourceSoftMaskDict);
+ if (code >= 0)
+ code = code1;
+ code1 = pdf_free_resource_objects(pdev, resourceSoftMaskDict);
+ if (code >= 0)
+ code = code1;
+ /* This was in pdf_close_document, but that made no sense, so moved here
+ * for more consistency (and ease of finding it). This code deals with
+ * emitting fonts and FontDescriptors
+ */
+ pdf_clean_standard_fonts(pdev);
+ code1 = pdf_free_font_cache(pdev);
+ if (code >= 0)
+ code = code1;
+ code1 = pdf_write_resource_objects(pdev, resourceCharProc);
+ if (code >= 0)
+ code = code1;
+ code1 = pdf_finish_resources(pdev, resourceFont, pdf_convert_truetype_font);
+ if (code >= 0)
+ code = code1;
+ code1 = pdf_finish_resources(pdev, resourceFontDescriptor, pdf_finish_FontDescriptor);
+ if (code >= 0)
+ code = code1;
+ code1 = write_font_resources(pdev, &pdev->resources[resourceCIDFont]);
+ if (code >= 0)
+ code = code1;
+ code1 = write_font_resources(pdev, &pdev->resources[resourceFont]);
+ if (code >= 0)
+ code = code1;
+ code1 = pdf_finish_resources(pdev, resourceFontDescriptor, pdf_write_FontDescriptor);
+ if (code >= 0)
+ code = code1;
+ /* If required, write the Encoding for Type 3 bitmap fonts. */
+ code1 = pdf_write_bitmap_fonts_Encoding(pdev);
+ if (code >= 0)
+ code = code1;
+
+ code1 = pdf_write_resource_objects(pdev, resourceCMap);
+ if (code >= 0)
+ code = code1;
+ code1 = pdf_free_resource_objects(pdev, resourceCMap);
+ if (code >= 0)
+ code = code1;
+ if (!(pdev->ForOPDFRead && pdev->ProduceDSC)) {
+ if (pdev->ResourcesBeforeUsage)
+ pdf_reverse_resource_chain(pdev, resourcePage);
+ code1 = pdf_write_resource_objects(pdev, resourcePage);
+ if (code >= 0)
+ code = code1;
+ code1 = pdf_free_resource_objects(pdev, resourcePage);
+ if (code >= 0)
+ code = code1;
+ }
+
+ code1 = pdf_free_resource_objects(pdev, resourceOther);
+ if (code >= 0)
+ code = code1;
+
+ /* Create the Pages tree. */
+ if (!(pdev->ForOPDFRead && pdev->ProduceDSC)) {
+ pdf_open_obj(pdev, Pages_id, resourcePagesTree);
+ pdf_record_usage(pdev, Pages_id, resource_usage_part9_structure);
+
+ s = pdev->strm;
+ stream_puts(s, "<< /Type /Pages /Kids [\n");
+ /* Omit the last page if it was incomplete. */
+ if (partial_page)
+ --(pdev->next_page);
+ {
+ int i;
+
+ for (i = 0; i < pdev->next_page; ++i)
+ pprintld1(s, "%ld 0 R\n", pdev->pages[i].Page->id);
+ }
+ pprintd1(s, "] /Count %d\n", pdev->next_page);
+
+ /* If the last file was PostScript, its possible that DSC comments might be lying around
+ * and pdf_print_orientation will use that if its present. So make sure we get rid of those
+ * before considering the dominant page direction for the Pages tree.
+ */
+ pdev->doc_dsc_info.viewing_orientation = pdev->doc_dsc_info.orientation = -1;
+ pdev->text_rotation.Rotate = pdf_dominant_rotation(&pdev->text_rotation);
+ pdf_print_orientation(pdev, NULL);
+
+ cos_dict_elements_write(pdev->Pages, pdev);
+ stream_puts(s, ">>\n");
+ pdf_end_obj(pdev, resourcePagesTree);
+
+ /* Close outlines and articles. */
+
+ if (pdev->outlines_id != 0) {
+ pdf_record_usage(pdev, pdev->outlines_id, resource_usage_part9_structure);
+ /* depth > 0 is only possible for an incomplete outline tree. */
+ while (pdev->outline_depth > 0) {
+ code1 = pdfmark_close_outline(pdev);
+ if (code >= 0)
+ code = code1;
+ }
+ code = pdfmark_close_outline(pdev);
+ if (code >= 0)
+ code = code1;
+ pdf_open_obj(pdev, pdev->outlines_id, resourceOutline);
+ pprintd1(s, "<< /Count %d", pdev->outlines_open);
+ pprintld2(s, " /First %ld 0 R /Last %ld 0 R >>\n",
+ pdev->outline_levels[0].first.id,
+ pdev->outline_levels[0].last.id);
+ pdf_end_obj(pdev, resourceOutline);
+ }
+ if (pdev->articles != 0) {
+ pdf_article_t *part;
+
+ /* Write the remaining information for each article. */
+ for (part = pdev->articles; part != 0; part = part->next)
+ pdfmark_write_article(pdev, part);
+ }
+
+ /* Write named destinations. (We can't free them yet.) */
+
+ if (pdev->CompatibilityLevel < 1.2) {
+ if (pdev->Dests) {
+ pdf_record_usage(pdev, pdev->Dests->id, resource_usage_part9_structure);
+ COS_WRITE_OBJECT(pdev->Dests, pdev, resourceDests);
+ }
+ } else {
+ if (pdev->Dests) {
+ pdf_record_usage(pdev, pdev->Dests->id, resource_usage_part9_structure);
+ cos_write_dict_as_ordered_array((cos_object_t *)pdev->Dests, pdev, resourceDests);
+ }
+ /* Write Embedded files. (We can't free them yet.) */
+
+ if (pdev->EmbeddedFiles) {
+ pdf_record_usage(pdev, pdev->EmbeddedFiles->id, resource_usage_part9_structure);
+ cos_write_dict_as_ordered_array((cos_object_t *)pdev->EmbeddedFiles, pdev, resourceEmbeddedFiles);
+ }
+ }
+
+ /* Write the PageLabel array */
+ pdfmark_end_pagelabels(pdev);
+ if (pdev->PageLabels) {
+ pdf_record_usage(pdev, pdev->PageLabels->id, resource_usage_part9_structure);
+ COS_WRITE_OBJECT(pdev->PageLabels, pdev, resourceLabels);
+ }
+
+ /* Write the document metadata. */
+ code1 = pdf_document_metadata(pdev);
+ if (code >= 0)
+ code = code1;
+
+ /* Write the Catalog. */
+
+ /*
+ * The PDF specification requires Threads to be an indirect object.
+ * Write the threads now, if any.
+ */
+ if (pdev->articles != 0) {
+ pdf_article_t *part;
+
+ Threads_id = pdf_begin_obj(pdev, resourceThread);
+ pdf_record_usage(pdev, Threads_id, resource_usage_part9_structure);
+ s = pdev->strm;
+ stream_puts(s, "[ ");
+ while ((part = pdev->articles) != 0) {
+ pdev->articles = part->next;
+ pprintld1(s, "%ld 0 R\n", part->contents->id);
+ COS_FREE(part->contents, "pdf_close(article contents)");
+ gs_free_object(mem, part, "pdf_close(article)");
+ }
+ stream_puts(s, "]\n");
+ pdf_end_obj(pdev, resourceThread);
+ }
+ pdf_open_obj(pdev, Catalog_id, resourceCatalog);
+ pdf_record_usage(pdev, Catalog_id, resource_usage_part1_structure);
+
+ s = pdev->strm;
+ stream_puts(s, "<<");
+ pprintld1(s, "/Type /Catalog /Pages %ld 0 R\n", Pages_id);
+ if (pdev->outlines_id != 0)
+ pprintld1(s, "/Outlines %ld 0 R\n", pdev->outlines_id);
+ if (Threads_id) {
+ pprintld1(s, "/Threads %ld 0 R\n", Threads_id);
+ pdf_record_usage(pdev, Threads_id, resource_usage_part1_structure);
+ }
+ if (pdev->CompatibilityLevel < 1.2) {
+ if (pdev->Dests)
+ pprintld1(s, "/Dests %ld 0 R\n", pdev->Dests->id);
+ } else {
+ if (pdev->Dests || pdev->EmbeddedFiles) {
+ stream_puts(s, "/Names <<\n");
+ if (pdev->Dests)
+ pprintld1(s, "/Dests <</Kids [%ld 0 R]>>\n", pdev->Dests->id);
+ if (pdev->EmbeddedFiles)
+ pprintld1(s, "/EmbeddedFiles << /Kids [%ld 0 R]>>\n", pdev->EmbeddedFiles->id);
+ stream_puts(s, ">>\n");
+ }
+ }
+ if (pdev->PageLabels)
+ pprintld1(s, "/PageLabels << /Nums %ld 0 R >>\n",
+ pdev->PageLabels->id);
+ cos_dict_elements_write(pdev->Catalog, pdev);
+ stream_puts(s, ">>\n");
+ pdf_end_obj(pdev, resourceCatalog);
+ if (pdev->Dests) {
+ COS_FREE(pdev->Dests, "pdf_close(Dests)");
+ pdev->Dests = 0;
+ }
+ if (pdev->EmbeddedFiles) {
+ COS_FREE(pdev->EmbeddedFiles, "pdf_close(EmbeddFiles)");
+ pdev->EmbeddedFiles = 0;
+ }
+ if (pdev->PageLabels) {
+ COS_FREE(pdev->PageLabels, "pdf_close(PageLabels)");
+ pdev->PageLabels = 0;
+ pdev->PageLabels_current_label = 0;
+ }
+
+ /* Prevent writing special named objects twice. */
+
+ pdev->Catalog->id = 0;
+ /*pdev->Info->id = 0;*/ /* Info should get written */
+ pdf_record_usage(pdev, pdev->Info->id, resource_usage_part9_structure);
+
+ } else {
+ pdev->Info->id = 0; /* Don't write Info dict for DSC PostScript */
+ }
+ /*
+ * Write the definitions of the named objects.
+ * Note that this includes Form XObjects created by BP/EP, named PS
+ * XObjects, and images named by NI.
+ */
+
+ do {
+ cos_dict_objects_write(pdev->local_named_objects, pdev);
+ } while (pdf_pop_namespace(pdev) >= 0);
+ cos_dict_objects_write(pdev->global_named_objects, pdev);
+
+ if (pdev->ForOPDFRead && pdev->ProduceDSC) {
+ int pages;
+
+ for (pages = 0; pages <= pdev->next_page; ++pages)
+ ;
+
+ code = ps2write_dsc_header(pdev, pages - 1);
+ }
+
+ /* Copy the resources into the main file. */
+
+ s = pdev->strm;
+ resource_pos = stell(s);
+ sflush(pdev->asides.strm);
+ {
+ FILE *rfile = pdev->asides.file;
+ int64_t res_end = gp_ftell_64(rfile);
+
+ gp_fseek_64(rfile, 0L, SEEK_SET);
+ pdf_copy_data(s, rfile, res_end, NULL);
+ }
+
+ if (pdev->ForOPDFRead && pdev->ProduceDSC) {
+ int j;
+
+ code = 0;
+ pagecount = 1;
+
+ /* All resources and procsets written, end the prolog */
+ stream_puts(pdev->strm, "%%EndProlog\n");
+
+ if (pdev->params.PSDocOptions.data) {
+ int i;
+ char *p = (char *)pdev->params.PSDocOptions.data;
+
+ stream_puts(pdev->strm, "%%BeginSetup\n");
+ for (i=0;i<pdev->params.PSDocOptions.size;i++)
+ stream_putc(s, *p++);
+ stream_puts(pdev->strm, "\n");
+ stream_puts(pdev->strm, "\n%%EndSetup\n");
+ }
+
+ if (pdev->ResourcesBeforeUsage)
+ pdf_reverse_resource_chain(pdev, resourcePage);
+ for (j = 0; j < NUM_RESOURCE_CHAINS && code >= 0; ++j) {
+ pdf_resource_t *pres = pdev->resources[resourcePage].chains[j];
+
+ for (; pres != 0; pres = pres->next)
+ if ((!pres->named || pdev->ForOPDFRead)
+ && !pres->object->written) {
+ pdf_page_t *page = &pdev->pages[pagecount - 1];
+
+ pprintd2(pdev->strm, "%%%%Page: %d %d\n",
+ pagecount, pagecount);
+ if (!pdev->Eps2Write)
+ pprintd2(pdev->strm, "%%%%PageBoundingBox: 0 0 %d %d\n", (int)page->MediaBox.x, (int)page->MediaBox.y);
+ stream_puts(pdev->strm, "%%BeginPageSetup\n");
+ stream_puts(pdev->strm, "/pagesave save def\n");
+
+ if (pdev->params.PSPageOptions.size) {
+ int i, index = (pagecount - 1) % pdev->params.PSPageOptions.size;
+ char *p = (char *)pdev->params.PSPageOptions.data[index].data;
+
+ for (i=0;i<pdev->params.PSPageOptions.data[index].size;i++)
+ stream_putc(s, *p++);
+ stream_puts(pdev->strm, "\n");
+ }
+
+ pdf_write_page(pdev, pagecount++);
+
+ stream_puts(pdev->strm, "%%EndPageSetup\n");
+ pprintld1(pdev->strm, "%ld 0 obj\n", pres->object->id);
+ code = cos_write(pres->object, pdev, pres->object->id);
+ stream_puts(pdev->strm, "endobj\n");
+ pres->object->written = true;
+ stream_puts(pdev->strm, "pagesave restore\n%%PageTrailer\n");
+ }
+ }
+ code1 = pdf_free_resource_objects(pdev, resourcePage);
+ if (code >= 0)
+ code = code1;
+ stream_puts(pdev->strm, "%%Trailer\n");
+ stream_puts(pdev->strm, "end\n");
+ stream_puts(pdev->strm, "%%EOF\n");
+ }
+
+ if (pdev->Linearise) {
+ linear_params.LastResource = pdev->next_id - 1;
+ linear_params.Offsets = (gs_offset_t *)gs_alloc_bytes(pdev->pdf_memory, pdev->next_id * sizeof(gs_offset_t), "temp xref storage");
+ memset(linear_params.Offsets, 0x00, linear_params.LastResource * sizeof(gs_offset_t));
+ }
+
+ if (!(pdev->ForOPDFRead && pdev->ProduceDSC)) {
+ /* Write Encrypt. */
+ if (pdev->OwnerPassword.size > 0) {
+ Encrypt_id = pdf_obj_ref(pdev);
+
+ pdf_open_obj(pdev, Encrypt_id, resourceEncrypt);
+ s = pdev->strm;
+ stream_puts(s, "<<");
+ stream_puts(s, "/Filter /Standard ");
+ pprintld1(s, "/V %ld ", pdev->EncryptionV);
+ pprintld1(s, "/Length %ld ", pdev->KeyLength);
+ pprintld1(s, "/R %ld ", pdev->EncryptionR);
+ pprintld1(s, "/P %ld ", pdev->Permissions);
+ stream_puts(s, "/O ");
+ pdf_put_string(pdev, pdev->EncryptionO, sizeof(pdev->EncryptionO));
+ stream_puts(s, "\n/U ");
+ pdf_put_string(pdev, pdev->EncryptionU, sizeof(pdev->EncryptionU));
+ stream_puts(s, ">>\n");
+ pdf_end_obj(pdev, resourceEncrypt);
+ s = pdev->strm;
+ }
+
+ /* Write the cross-reference section. */
+
+ start_section = pdev->FirstObjectNumber;
+ end_section = find_end_xref_section(pdev, tfile, start_section, resource_pos);
+
+ xref = pdf_stell(pdev) - pdev->OPDFRead_procset_length;
+ if (pdev->Linearise)
+ linear_params.xref = xref;
+
+ if (pdev->FirstObjectNumber == 1) {
+ gs_sprintf(str, "xref\n0 %"PRId64"\n0000000000 65535 f \n",
+ end_section);
+ stream_puts(s, str);
+ }
+ else {
+ gs_sprintf(str, "xref\n0 1\n0000000000 65535 f \n%"PRId64" %"PRId64"\n",
+ start_section,
+ end_section - start_section);
+ stream_puts(s, str);
+ }
+
+ do {
+ write_xref_section(pdev, tfile, start_section, end_section, resource_pos, linear_params.Offsets);
+ if (end_section >= pdev->next_id)
+ break;
+ start_section = end_section + 1;
+ end_section = find_end_xref_section(pdev, tfile, start_section, resource_pos);
+ if (end_section < 0)
+ return end_section;
+ gs_sprintf(str, "%"PRId64" %"PRId64"\n", start_section, end_section - start_section);
+ stream_puts(s, str);
+ } while (1);
+
+ /* Write the trailer. */
+
+ if (!pdev->Linearise) {
+ char xref_str[32];
+ stream_puts(s, "trailer\n");
+ pprintld3(s, "<< /Size %ld /Root %ld 0 R /Info %ld 0 R\n",
+ pdev->next_id, Catalog_id, Info_id);
+ stream_puts(s, "/ID [");
+ psdf_write_string(pdev->strm, pdev->fileID, sizeof(pdev->fileID), 0);
+ psdf_write_string(pdev->strm, pdev->fileID, sizeof(pdev->fileID), 0);
+ stream_puts(s, "]\n");
+ if (pdev->OwnerPassword.size > 0) {
+ pprintld1(s, "/Encrypt %ld 0 R ", Encrypt_id);
+ }
+ stream_puts(s, ">>\n");
+ gs_sprintf(xref_str, "startxref\n%"PRId64"\n%%%%EOF\n", xref);
+ stream_puts(s, xref_str);
+ }
+ }
+
+ if (pdev->Linearise) {
+ int i;
+
+ code = pdf_linearise(pdev, &linear_params);
+ for (i=0; i<pdev->ResourceUsageSize; i++) {
+ if (pdev->ResourceUsage[i].PageList)
+ gs_free_object(pdev->pdf_memory, pdev->ResourceUsage[i].PageList, "Free linearisation Page Usage list records");
+ }
+ gs_free_object(pdev->pdf_memory, pdev->ResourceUsage, "Free linearisation resource usage records");
+ }
+
+ /* Require special handling for Fonts, ColorSpace and Pattern resources
+ * These are tracked in pdev->last_resource, and are complex structures which may
+ * contain other memory allocations. All other resource types can be simply dicarded
+ * as above.
+ */
+ {
+ /* PDF font records and all their associated contents need to be
+ * freed by means other than the garbage collector, or the memory
+ * usage will increase with languages other than PostScript. In addition
+ * debug builds of non-PS languages take a long time to close down
+ * due to reporting the dangling memory allocations.
+ */
+ int j, code = 0;
+
+ for (j = 0; j < NUM_RESOURCE_CHAINS && code >= 0; ++j) {
+ pdf_resource_t *pres = pdev->resources[resourceFont].chains[j];
+
+ for (; pres != 0;) {
+ pdf_font_resource_t *pdfont = (pdf_font_resource_t *)pres;
+
+ font_resource_free(pdev, pdfont);
+ pres = pres->next;
+ }
+ }
+ }
+
+ {
+ int j, code = 0;
+
+ for (j = 0; j < NUM_RESOURCE_CHAINS && code >= 0; ++j) {
+ pdf_resource_t *pres = pdev->resources[resourceCIDFont].chains[j];
+
+ for (; pres != 0;) {
+ pdf_font_resource_t *pdfont = (pdf_font_resource_t *)pres;
+
+ font_resource_free(pdev, pdfont);
+ pres = pres->next;
+ }
+ }
+ }
+
+ {
+ int j, code = 0;
+
+ for (j = 0; j < NUM_RESOURCE_CHAINS && code >= 0; ++j) {
+ pdf_resource_t *pres = pdev->resources[resourceFontDescriptor].chains[j];
+ for (; pres != 0;) {
+ pdf_font_descriptor_free(pdev, pres);
+ pres = pres->next;
+ }
+ }
+ }
+
+ {
+ int j, code = 0;
+
+ for (j = 0; j < NUM_RESOURCE_CHAINS && code >= 0; ++j) {
+ pdf_resource_t *pres = pdev->resources[resourceCharProc].chains[j];
+
+ for (; pres != 0;) {
+ if (pres->object) {
+ gs_free_object(pdev->pdf_memory, (byte *)pres->object, "Free CharProc");
+ pres->object = 0;
+ }
+ pres = pres->next;
+ }
+ }
+ }
+
+ {
+ int j, code = 0;
+
+ for (j = 0; j < NUM_RESOURCE_CHAINS && code >= 0; ++j) {
+ pdf_resource_t *pres = pdev->resources[resourceColorSpace].chains[j];
+ for (; pres != 0;) {
+ free_color_space(pdev, pres);
+ pres = pres->next;
+ }
+ }
+ }
+
+ {
+ int j, code = 0;
+
+ for (j = 0; j < NUM_RESOURCE_CHAINS && code >= 0; ++j) {
+ pdf_resource_t *pres = pdev->resources[resourceExtGState].chains[j];
+
+ for (; pres != 0;) {
+ if (pres->object) {
+ cos_release(pres->object, "release ExtGState object");
+ gs_free_object(pdev->pdf_memory, pres->object, "free ExtGState");
+ pres->object = 0;
+ }
+ pres = pres->next;
+ }
+ }
+ }
+
+ {
+ int j, code = 0;
+
+ for (j = 0; j < NUM_RESOURCE_CHAINS && code >= 0; ++j) {
+ pdf_resource_t *pres = pdev->resources[resourcePattern].chains[j];
+
+ for (; pres != 0;) {
+ if (pres->object) {
+ cos_release(pres->object, "free pattern dict");
+ gs_free_object(pdev->pdf_memory, pres->object, "free pattern resources");
+ pres->object = 0;
+ }
+ pres = pres->next;
+ }
+ }
+ }
+
+ {
+ int j, code = 0;
+
+ for (j = 0; j < NUM_RESOURCE_CHAINS && code >= 0; ++j) {
+ pdf_resource_t *pres = pdev->resources[resourceShading].chains[j];
+
+ for (; pres != 0;) {
+ if (pres->object) {
+ cos_release(pres->object, "free Shading dict");
+ gs_free_object(pdev->pdf_memory, pres->object, "free Shading resources");
+ pres->object = 0;
+ }
+ pres = pres->next;
+ }
+ }
+ }
+
+ {
+ int j, code = 0;
+
+ for (j = 0; j < NUM_RESOURCE_CHAINS && code >= 0; ++j) {
+ pdf_resource_t *pres = pdev->resources[resourceGroup].chains[j];
+
+ for (; pres != 0;) {
+ if (pres->object) {
+ cos_release(pres->object, "free Group dict");
+ gs_free_object(pdev->pdf_memory, pres->object, "free Group resources");
+ pres->object = 0;
+ }
+ pres = pres->next;
+ }
+ }
+ }
+
+ {
+ int j, code = 0;
+
+ for (j = 0; j < NUM_RESOURCE_CHAINS && code >= 0; ++j) {
+ pdf_resource_t *pnext = 0, *pres = pdev->resources[resourceFunction].chains[j];
+
+ for (; pres != 0;) {
+ if (pres->object) {
+ cos_release(pres->object, "free function dict");
+ gs_free_object(pdev->pdf_memory, pres->object, "free function resources");
+ pres->object = 0;
+ }
+ pnext = pres->next;
+ pres = pnext;
+ }
+ }
+ }
+
+ {
+ int i, j;
+
+ for (i = 0; i < NUM_RESOURCE_TYPES; i++) {
+ for (j = 0; j < NUM_RESOURCE_CHAINS && code >= 0; ++j) {
+ pdev->resources[i].chains[j] = 0;
+ }
+ }
+ }
+
+ /* Release the resource records. */
+ /* So what exactly is stored in this list ? I believe the following types of resource:
+ *
+ * resourceCharProc
+ * resourceFont
+ * resourceCIDFont
+ * resourceFontDescriptor
+ * resourceColorSpace
+ * resourceExtGState
+ * resourceXObject
+ * resourceSoftMaskDict
+ * resourceGroup
+ * resourcePattern
+ * resourceShading
+ *
+ * resourceCMap resourcePage and resourceFunction don't appear to be tracked. I note
+ * that resourcePage and resourceCMap are freed above, as is resourceXObject and resourceSoftmaskDict.
+ * So presumably reourceFunction just leaks.
+ *
+ * It seems that all these types of resources are added when they are created
+ * in addition there are 'pdf_forget_resource' and pdf_cancel_resource which
+ * remove entries from this list.
+ */
+ {
+ pdf_resource_t *pres;
+ pdf_resource_t *prev;
+
+ for (prev = pdev->last_resource; (pres = prev) != 0;) {
+ prev = pres->prev;
+ gs_free_object(mem, pres, "pdf_resource_t");
+ }
+ pdev->last_resource = 0;
+ }
+
+ /* Free named objects. */
+
+ cos_release((cos_object_t *)pdev->NI_stack, "Release Name Index stack");
+ gs_free_object(mem, pdev->NI_stack, "Free Name Index stack");
+ pdev->NI_stack = 0;
+
+ cos_dict_objects_delete(pdev->local_named_objects);
+ COS_FREE(pdev->local_named_objects, "pdf_close(local_named_objects)");
+ pdev->local_named_objects = 0;
+
+ /* global resources include the Catalog object and apparently the Info dict */
+ cos_dict_objects_delete(pdev->global_named_objects);
+ COS_FREE(pdev->global_named_objects, "pdf_close(global_named_objects)");
+ pdev->global_named_objects = 0;
+
+ /* Wrap up. */
+
+ pdev->font_cache = 0;
+
+ {
+ int i;
+ for (i=0;i < pdev->next_page;i++) {
+ cos_release((cos_object_t *)pdev->pages[i].Page, "Free page dict");
+ if (pdev->pages[i].Annots) {
+ cos_release((cos_object_t *)pdev->pages[i].Annots, "Release Annots dict");
+ gs_free_object(mem, pdev->pages[i].Annots, "Free Annots dict");
+ }
+ gs_free_object(mem, pdev->pages[i].Page, "Free Page object");
+ }
+ }
+ gs_free_object(mem, pdev->pages, "pages");
+ pdev->pages = 0;
+
+ pdev->num_pages = 0;
+
+ gs_free_object(mem, pdev->sbstack, "Free sbstack");
+ pdev->sbstack = 0;
+
+ text_data_free(mem, pdev->text);
+ pdev->text = 0;
+
+ cos_release((cos_object_t *)pdev->Pages, "release Pages dict");
+ gs_free_object(mem, pdev->Pages, "Free Pages dict");
+ pdev->Pages = 0;
+
+ {
+ int i;
+ for (i=0;i < pdev->vgstack_depth;i++)
+ gs_free_object(pdev->memory->non_gc_memory, pdev->vgstack[i].dash_pattern, "pdfwrite final free stored dash in gstate");
+ }
+ gs_free_object(pdev->pdf_memory, pdev->vgstack, "pdf_close(graphics state stack)");
+ pdev->vgstack = 0;
+
+ cos_release((cos_object_t *)pdev->Namespace_stack, "release Name space stack");
+ gs_free_object(mem, pdev->Namespace_stack, "Free Name space stack");
+ pdev->Namespace_stack = 0;
+
+ pdev->Catalog = 0;
+ pdev->Info = 0;
+
+ gs_free_object(mem, pdev->outline_levels, "outline_levels array");
+ pdev->outline_levels = 0;
+ pdev->outline_depth = -1;
+ pdev->max_outline_depth = 0;
+
+ {
+ /* pdf_open_dcument could set up filters for entire document.
+ Removing them now. */
+ int status;
+
+ if (!pdev->ProduceDSC)
+ stream_putc(s, 0x04);
+ while (s->strm) {
+ s = s->strm;
+ }
+ status = s_close_filters(&pdev->strm, s);
+ if (status < 0 && code == 0)
+ code = gs_error_ioerror;
+ }
+
+ code1 = gdev_vector_close_file((gx_device_vector *) pdev);
+ if (code >= 0)
+ code = code1;
+ if (pdev->max_referred_page >= pdev->next_page + 1) {
+ /* Note : pdev->max_referred_page counts from 1,
+ and pdev->next_page counts from 0. */
+ emprintf2(pdev->memory, "ERROR: A pdfmark destination page %d "
+ "points beyond the last page %d.\n",
+ pdev->max_referred_page, pdev->next_page);
+ }
+ code = pdf_close_files(pdev, code);
+ if (code < 0)
+ return code;
+
+ pdf_free_pdf_font_cache(pdev);
+ return code;
+}
diff --git a/devices/vector/gdevpdfb.c b/devices/vector/gdevpdfb.c
new file mode 100644
index 000000000..38fdb6d8a
--- /dev/null
+++ b/devices/vector/gdevpdfb.c
@@ -0,0 +1,654 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Low-level bitmap image handling for PDF-writing driver */
+#include "string_.h"
+#include "gx.h"
+#include "gserrors.h"
+#include "gdevpdfx.h"
+#include "gdevpdfg.h"
+#include "gdevpdfo.h" /* for data stream */
+#include "gxcspace.h"
+#include "gxdcolor.h"
+#include "gxpcolor.h"
+#include "gxhldevc.h"
+#include "gxchar.h"
+#include "gdevpdtf.h" /* Required to include gdevpdti.h */
+#include "gdevpdti.h" /* For pdf_charproc_x_offset */
+#include "gsptype1.h"
+
+/* We need this color space type for constructing temporary color spaces. */
+extern const gs_color_space_type gs_color_space_type_Indexed;
+
+/* ---------------- Utilities ---------------- */
+
+/* Fill in the image parameters for a bitmap image. */
+static void
+pdf_make_bitmap_image(gs_image_t * pim, int x, int y, int w, int h)
+{
+ pim->Width = w;
+ pim->Height = h;
+ pdf_make_bitmap_matrix(&pim->ImageMatrix, x, y, w, h, h);
+}
+
+/* ---------------- Driver procedures ---------------- */
+
+/* Copy a mask bitmap. for_pattern = -1 means put the image in-line, */
+/* 1 means put the image in a resource. */
+static int
+pdf_copy_mask_data(gx_device_pdf * pdev, const byte * base, int sourcex,
+ int raster, gx_bitmap_id id, int x, int y, int w, int h,
+ gs_image_t *pim, pdf_image_writer *piw,
+ int for_pattern)
+{
+ ulong nbytes;
+ int code;
+ const byte *row_base;
+ int row_step;
+ bool in_line;
+
+ gs_image_t_init_mask(pim, true);
+ pdf_make_bitmap_image(pim, x, y, w, h);
+ nbytes = ((ulong)w * h + 7) / 8;
+
+ if (for_pattern) {
+ /*
+ * Patterns must be emitted in order of increasing user Y, i.e.,
+ * the opposite of PDF's standard image order.
+ */
+ row_base = base + (h - 1) * raster;
+ row_step = -raster;
+ in_line = for_pattern < 0;
+ } else {
+ row_base = base;
+ row_step = raster;
+ in_line = nbytes < pdev->MaxInlineImageSize;
+ pdf_put_image_matrix(pdev, &pim->ImageMatrix, 1.0);
+ /*
+ * Check whether we've already made an XObject resource for this
+ * image.
+ */
+ if (id != gx_no_bitmap_id) {
+ piw->pres = pdf_find_resource_by_gs_id(pdev, resourceXObject, id);
+ if (piw->pres)
+ return 0;
+ }
+ }
+ /*
+ * We have to be able to control whether to put Pattern images in line,
+ * to avoid trying to create an XObject resource while we're in the
+ * middle of writing a Pattern resource.
+ */
+ if (for_pattern < 0)
+ stream_puts(pdev->strm, "q ");
+ pdf_image_writer_init(piw);
+ pdev->ParamCompatibilityLevel = pdev->CompatibilityLevel;
+ if ((code = pdf_begin_write_image(pdev, piw, id, w, h, NULL, in_line)) < 0 ||
+ (code = psdf_setup_lossless_filters((gx_device_psdf *) pdev,
+ &piw->binary[0],
+ (gs_pixel_image_t *)pim, in_line)) < 0 ||
+ (code = pdf_begin_image_data(pdev, piw, (const gs_pixel_image_t *)pim,
+ NULL, 0)) < 0
+ )
+ return code;
+ pdf_copy_mask_bits(piw->binary[0].strm, row_base, sourcex, row_step, w, h, 0);
+ pdf_end_image_binary(pdev, piw, piw->height);
+ return pdf_end_write_image(pdev, piw);
+}
+
+static void
+set_image_color(gx_device_pdf *pdev, gx_color_index c)
+{
+ pdf_set_pure_color(pdev, c, &pdev->saved_fill_color,
+ &pdev->fill_used_process_color,
+ &psdf_set_fill_color_commands);
+ if (!pdev->HaveStrokeColor)
+ pdf_set_pure_color(pdev, c, &pdev->saved_stroke_color,
+ &pdev->stroke_used_process_color,
+ &psdf_set_stroke_color_commands);
+}
+
+static int
+pdf_copy_mono(gx_device_pdf *pdev,
+ const byte *base, int sourcex, int raster, gx_bitmap_id id,
+ int x, int y, int w, int h, gx_color_index zero,
+ gx_color_index one, const gx_clip_path *pcpath)
+{
+ int code;
+ gs_color_space *pcs = NULL;
+ cos_value_t cs_value;
+ cos_value_t *pcsvalue;
+ byte palette[arch_sizeof_color_index * 2];
+ gs_image_t image;
+ pdf_image_writer writer;
+ pdf_stream_position_t ipos;
+ pdf_resource_t *pres = 0;
+ byte invert = 0;
+ bool in_line = false;
+ gs_show_enum *show_enum = (gs_show_enum *)pdev->pte;
+ int x_offset, y_offset;
+ double width;
+
+ /* Update clipping. */
+ if (pdf_must_put_clip_path(pdev, pcpath)) {
+ code = pdf_open_page(pdev, PDF_IN_STREAM);
+ if (code < 0)
+ return code;
+ code = pdf_put_clip_path(pdev, pcpath);
+ if (code < 0)
+ return code;
+ }
+ /* We have 3 cases: mask, inverse mask, and solid. */
+ if (zero == gx_no_color_index) {
+ if (one == gx_no_color_index)
+ return 0;
+ /* If a mask has an id, assume it's a character. */
+ if (id != gx_no_bitmap_id && sourcex == 0 && show_enum) {
+ pdf_char_proc_t *pcp;
+
+ if (show_enum->use_wxy_float)
+ pdev->char_width.x = show_enum->wxy_float.x;
+ else
+ pdev->char_width.x = fixed2float(show_enum->wxy.x);
+ pres = pdf_find_resource_by_gs_id(pdev, resourceCharProc, id);
+ if (pres == 0) { /* Define the character in an embedded font. */
+ gs_image_t_init_mask(&image, false);
+ invert = 0xff;
+ x_offset = x - (int)show_enum->pis->current_point.x;
+ y_offset = y - (int)show_enum->pis->current_point.y;
+ x -= x_offset;
+ y -= y_offset;
+ y -= h;
+ pdf_make_bitmap_image(&image, x, y, w, h);
+ /*
+ * The Y axis of the text matrix is inverted,
+ * so we need to negate the Y offset appropriately.
+ */
+ code = pdf_begin_char_proc(pdev, w, h, 0, y_offset, x_offset, id,
+ &pcp, &ipos);
+ if (code < 0)
+ return code;
+ y_offset = -y_offset;
+ width = psdf_round(pdev->char_width.x, 100, 10); /* See
+ pdf_write_Widths about rounding. We need to provide
+ a compatible data for Tj. */
+ pprintg1(pdev->strm, "%g ", width);
+ pprintd4(pdev->strm, "0 %d %d %d %d d1\n", x_offset, -h + y_offset, w + x_offset, y_offset);
+ pprintd4(pdev->strm, "%d 0 0 %d %d %d cm\n", w, h, x_offset,
+ -h + y_offset);
+ pdf_image_writer_init(&writer);
+ code = pdf_begin_write_image(pdev, &writer, gs_no_id, w, h, NULL, true);
+ if (code < 0)
+ return code;
+ pres = (pdf_resource_t *) pcp;
+ goto wr;
+ } else if (pdev->pte) {
+ /* We're under pdf_text_process. It set a high level color. */
+ } else
+ set_image_color(pdev, one);
+ pcp = (pdf_char_proc_t *) pres;
+ x -= pdf_charproc_x_offset(pcp);
+ y -= pdf_charproc_y_offset(pcp);
+ y -= h;
+ pdf_make_bitmap_image(&image, x, y, w, h);
+ goto rx;
+ }
+ set_image_color(pdev, one);
+ gs_image_t_init_mask(&image, false);
+ invert = 0xff;
+ } else if (one == gx_no_color_index) {
+ gs_image_t_init_mask(&image, false);
+ set_image_color(pdev, zero);
+ } else if (zero == pdev->black && one == pdev->white) {
+ pcs = gs_cspace_new_DeviceGray(pdev->memory);
+ gs_image_t_init(&image, pcs);
+ } else if (zero == pdev->white && one == pdev->black) {
+ pcs = gs_cspace_new_DeviceGray(pdev->memory);
+ gs_image_t_init(&image, pcs);
+ invert = 0xff;
+ } else {
+ /*
+ * We think this code is never executed when interpreting PostScript
+ * or PDF: the library never uses monobit non-mask images
+ * internally, and high-level images don't go through this code.
+ * However, we still want the code to work.
+ */
+ gs_color_space *pcs_base;
+ gx_color_index c[2];
+ int i, j;
+ int ncomp = pdev->color_info.num_components;
+ byte *p;
+
+ code = pdf_cspace_init_Device(pdev->memory, &pcs_base, ncomp);
+ if (code < 0)
+ return code;
+ c[0] = psdf_adjust_color_index((gx_device_vector *)pdev, zero);
+ c[1] = psdf_adjust_color_index((gx_device_vector *)pdev, one);
+ pcs = gs_cspace_alloc(pdev->memory, &gs_color_space_type_Indexed);
+ if (pcs == NULL) {
+ rc_decrement_cs(pcs_base, "pdf_copy_mono");
+ return_error(gs_error_VMerror);
+ }
+ pcs->base_space = pcs_base;
+ pcs->params.indexed.hival = 1;
+ pcs->params.indexed.n_comps = ncomp;
+ p = palette;
+ for (i = 0; i < 2; ++i)
+ for (j = ncomp - 1; j >= 0; --j)
+ *p++ = (byte)(c[i] >> (j * 8));
+ pcs->params.indexed.lookup.table.data = palette;
+ pcs->params.indexed.lookup.table.size = p - palette;
+ pcs->params.indexed.use_proc = false;
+ gs_image_t_init(&image, pcs);
+ image.BitsPerComponent = 1;
+ }
+ pdf_make_bitmap_image(&image, x, y, w, h);
+ {
+ ulong nbytes = (ulong) ((w + 7) >> 3) * h;
+
+ code = pdf_open_page(pdev, PDF_IN_STREAM);
+ if (code < 0)
+ return code;
+ in_line = nbytes < pdev->MaxInlineImageSize;
+ if (in_line)
+ pdf_put_image_matrix(pdev, &image.ImageMatrix, 1.0);
+ pdf_image_writer_init(&writer);
+ code = pdf_begin_write_image(pdev, &writer, gs_no_id, w, h, NULL, in_line);
+ if (code < 0)
+ return code;
+ }
+ wr:
+ if (image.ImageMask)
+ pcsvalue = NULL;
+ else {
+ /*
+ * We don't have to worry about color space scaling: the color
+ * space is always a Device space.
+ */
+ code = pdf_color_space_named(pdev, NULL, &cs_value, NULL, pcs,
+ &writer.pin->color_spaces, in_line, NULL, 0, false);
+ if (code < 0)
+ return code;
+ pcsvalue = &cs_value;
+ }
+ /*
+ * There are 3 different cases at this point:
+ * - Writing an in-line image (pres == 0, writer.pres == 0);
+ * - Writing an XObject image (pres == 0, writer.pres != 0);
+ * - Writing the image for a CharProc (pres != 0).
+ * We handle them with in-line code followed by a switch,
+ * rather than making the shared code into a procedure,
+ * simply because there would be an awful lot of parameters
+ * that would need to be passed.
+ */
+ if (pres) {
+ if (!pdev->NoT3CCITT) {
+ /*
+ * Always use CCITTFax 2-D for character bitmaps. It takes less
+ * space to invert the data with Decode than to set BlackIs1.
+ */
+ float d0 = image.Decode[0];
+
+ image.Decode[0] = image.Decode[1];
+ image.Decode[1] = d0;
+ psdf_CFE_binary(&writer.binary[0], image.Width, image.Height, true);
+ invert ^= 0xff;
+ }
+ } else {
+ /* Use the Distiller compression parameters. */
+ pdev->ParamCompatibilityLevel = pdev->CompatibilityLevel;
+ psdf_setup_image_filters((gx_device_psdf *) pdev, &writer.binary[0],
+ (gs_pixel_image_t *)&image, NULL, NULL, true, in_line);
+ }
+ pdf_begin_image_data(pdev, &writer, (const gs_pixel_image_t *)&image,
+ pcsvalue, 0);
+ code = pdf_copy_mask_bits(writer.binary[0].strm, base, sourcex, raster,
+ w, h, invert);
+ if (code < 0)
+ return code;
+ pdf_end_image_binary(pdev, &writer, writer.height);
+ if (!pres) {
+ switch ((code = pdf_end_write_image(pdev, &writer))) {
+ default: /* error */
+ return code;
+ case 1:
+ return 0;
+ case 0:
+ return pdf_do_image(pdev, writer.pres, &image.ImageMatrix,
+ true);
+ }
+ }
+ writer.end_string = ""; /* no Q */
+ switch ((code = pdf_end_write_image(pdev, &writer))) {
+ default: /* error */
+ return code;
+ case 0: /* not possible */
+ return_error(gs_error_Fatal);
+ case 1:
+ break;
+ }
+ code = pdf_end_char_proc(pdev, &ipos);
+ if (code < 0)
+ return code;
+ rx:{
+ gs_matrix imat;
+
+ imat = image.ImageMatrix;
+ imat.xx /= w;
+ imat.xy /= h;
+ imat.yx /= w;
+ imat.yy /= h;
+ return pdf_do_char_image(pdev, (const pdf_char_proc_t *)pres, &imat);
+ }
+}
+int
+gdev_pdf_copy_mono(gx_device * dev,
+ const byte * base, int sourcex, int raster, gx_bitmap_id id,
+ int x, int y, int w, int h, gx_color_index zero,
+ gx_color_index one)
+{
+ gx_device_pdf *pdev = (gx_device_pdf *) dev;
+
+ if (w <= 0 || h <= 0)
+ return 0;
+ return pdf_copy_mono(pdev, base, sourcex, raster, id, x, y, w, h,
+ zero, one, NULL);
+}
+
+/* Copy a color bitmap. for_pattern = -1 means put the image in-line, */
+/* 1 means put the image in a resource, 2 means image is a rasterized shading. */
+int
+pdf_copy_color_data(gx_device_pdf * pdev, const byte * base, int sourcex,
+ int raster, gx_bitmap_id id, int x, int y, int w, int h,
+ gs_image_t *pim, pdf_image_writer *piw,
+ int for_pattern)
+{
+ int depth = pdev->color_info.depth;
+ int bytes_per_pixel = depth >> 3;
+ gs_color_space *pcs;
+ cos_value_t cs_value;
+ ulong nbytes;
+ int code = pdf_cspace_init_Device(pdev->memory, &pcs, bytes_per_pixel);
+ const byte *row_base;
+ int row_step;
+ bool in_line;
+
+ if (code < 0)
+ return code; /* can't happen */
+ if (!base)
+ return 1;
+ gs_image_t_init(pim, pcs);
+ pdf_make_bitmap_image(pim, x, y, w, h);
+ pim->BitsPerComponent = 8;
+ nbytes = (ulong)w * bytes_per_pixel * h;
+
+ if (for_pattern == 1) {
+ /*
+ * Patterns must be emitted in order of increasing user Y, i.e.,
+ * the opposite of PDF's standard image order.
+ */
+ row_base = base + (h - 1) * raster;
+ row_step = -raster;
+ in_line = for_pattern < 0;
+ } else {
+ row_base = base;
+ row_step = raster;
+ in_line = nbytes < pdev->MaxInlineImageSize;
+ pdf_put_image_matrix(pdev, &pim->ImageMatrix, 1.0);
+ /*
+ * Check whether we've already made an XObject resource for this
+ * image.
+ */
+ if (id != gx_no_bitmap_id) {
+ piw->pres = pdf_find_resource_by_gs_id(pdev, resourceXObject, id);
+ if (piw->pres)
+ return 0;
+ }
+ }
+ /*
+ * We have to be able to control whether to put Pattern images in line,
+ * to avoid trying to create an XObject resource while we're in the
+ * middle of writing a Pattern resource.
+ */
+ if (for_pattern < 0)
+ stream_puts(pdev->strm, "q ");
+ /*
+ * We don't have to worry about color space scaling: the color
+ * space is always a Device space.
+ */
+ pdf_image_writer_init(piw);
+ pdev->ParamCompatibilityLevel = pdev->CompatibilityLevel;
+ if ((code = pdf_begin_write_image(pdev, piw, id, w, h, NULL, in_line)) < 0 ||
+ (code = pdf_color_space_named(pdev, NULL, &cs_value, NULL, pcs,
+ &piw->pin->color_spaces, in_line, NULL, 0, false)) < 0 ||
+ (for_pattern < 2 || nbytes < 512000 ?
+ (code = psdf_setup_lossless_filters((gx_device_psdf *) pdev,
+ &piw->binary[0], (gs_pixel_image_t *)pim, false)) :
+ (code = psdf_setup_image_filters((gx_device_psdf *) pdev,
+ &piw->binary[0], (gs_pixel_image_t *)pim, NULL, NULL, false, false))
+ ) < 0 ||
+ (code = pdf_begin_image_data(pdev, piw, (const gs_pixel_image_t *)pim,
+ &cs_value, 0)) < 0
+ )
+ return code;
+ pdf_copy_color_bits(piw->binary[0].strm, row_base, sourcex, row_step, w, h,
+ bytes_per_pixel);
+ pdf_end_image_binary(pdev, piw, piw->height);
+ return pdf_end_write_image(pdev, piw);
+}
+
+int
+gdev_pdf_copy_color(gx_device * dev, const byte * base, int sourcex,
+ int raster, gx_bitmap_id id, int x, int y, int w, int h)
+{
+ gx_device_pdf *pdev = (gx_device_pdf *) dev;
+ gs_image_t image;
+ pdf_image_writer writer;
+ int code;
+
+ if (w <= 0 || h <= 0)
+ return 0;
+ code = pdf_open_page(pdev, PDF_IN_STREAM);
+ if (code < 0)
+ return code;
+ /* Make sure we aren't being clipped. */
+ code = pdf_put_clip_path(pdev, NULL);
+ if (code < 0)
+ return code;
+ code = pdf_copy_color_data(pdev, base, sourcex, raster, id, x, y, w, h,
+ &image, &writer, 0);
+ switch (code) {
+ default:
+ return code; /* error */
+ case 1:
+ return 0;
+ case 0:
+ return pdf_do_image(pdev, writer.pres, NULL, true);
+ }
+}
+
+/* Fill a mask. */
+int
+gdev_pdf_fill_mask(gx_device * dev,
+ const byte * data, int data_x, int raster, gx_bitmap_id id,
+ int x, int y, int width, int height,
+ const gx_drawing_color * pdcolor, int depth,
+ gs_logical_operation_t lop, const gx_clip_path * pcpath)
+{
+ gx_device_pdf *pdev = (gx_device_pdf *) dev;
+
+ if (width <= 0 || height <= 0)
+ return 0;
+ if (depth > 1 || (!gx_dc_is_pure(pdcolor) != 0 && !(gx_dc_is_pattern1_color(pdcolor))))
+ return gx_default_fill_mask(dev, data, data_x, raster, id,
+ x, y, width, height, pdcolor, depth, lop,
+ pcpath);
+ return pdf_copy_mono(pdev, data, data_x, raster, id, x, y, width, height,
+ gx_no_color_index, gx_dc_pure_color(pdcolor),
+ pcpath);
+}
+
+/* Tile with a bitmap. This is important for pattern fills. */
+int
+gdev_pdf_strip_tile_rectangle(gx_device * dev, const gx_strip_bitmap * tiles,
+ int x, int y, int w, int h,
+ gx_color_index color0, gx_color_index color1,
+ int px, int py)
+{
+ gx_device_pdf *const pdev = (gx_device_pdf *) dev;
+ int tw = tiles->rep_width, th = tiles->rep_height;
+ double xscale = pdev->HWResolution[0] / 72.0,
+ yscale = pdev->HWResolution[1] / 72.0;
+ bool mask;
+ int depth;
+ int (*copy_data)(gx_device_pdf *, const byte *, int, int,
+ gx_bitmap_id, int, int, int, int,
+ gs_image_t *, pdf_image_writer *, int);
+ pdf_resource_t *pres;
+ cos_value_t cs_value;
+ int code;
+
+ if (tiles->id == gx_no_bitmap_id || tiles->shift != 0 ||
+ (w < tw && h < th) ||
+ color0 != gx_no_color_index
+ )
+ goto use_default;
+ if (color1 != gx_no_color_index) {
+ /* This is a mask pattern. */
+ mask = true;
+ depth = 1;
+ copy_data = pdf_copy_mask_data;
+ code = pdf_cs_Pattern_uncolored(pdev, &cs_value);
+ } else {
+ /* This is a colored pattern. */
+ mask = false;
+ depth = pdev->color_info.depth;
+ copy_data = pdf_copy_color_data;
+ code = pdf_cs_Pattern_colored(pdev, &cs_value);
+ }
+ if (code < 0)
+ goto use_default;
+ pres = pdf_find_resource_by_gs_id(pdev, resourcePattern, tiles->id);
+ if (!pres) {
+ /* Create the Pattern resource. */
+ int code;
+ long image_id, length_id;
+ gs_offset_t start, end;
+ stream *s;
+ gs_image_t image;
+ pdf_image_writer writer;
+ long image_bytes = ((long)tw * depth + 7) / 8 * th;
+ bool in_line = image_bytes < pdev->MaxInlineImageSize;
+ ulong tile_id =
+ (tw == tiles->size.x && th == tiles->size.y ? tiles->id :
+ gx_no_bitmap_id);
+
+ if (in_line)
+ image_id = 0;
+ else if (image_bytes > 65500) {
+ /*
+ * Acrobat Reader can't handle image Patterns with more than
+ * 64K of data. :-(
+ */
+ goto use_default;
+ } else {
+ /* Write the image as an XObject resource now. */
+ code = copy_data(pdev, tiles->data, 0, tiles->raster,
+ tile_id, 0, 0, tw, th, &image, &writer, 1);
+ if (code < 0)
+ goto use_default;
+ image_id = pdf_resource_id(writer.pres);
+ }
+ code = pdf_begin_resource(pdev, resourcePattern, tiles->id, &pres);
+ if (code < 0)
+ goto use_default;
+ s = pdev->strm;
+ pprintd1(s, "/PatternType 1/PaintType %d/TilingType 1/Resources<<\n",
+ (mask ? 2 : 1));
+ if (image_id)
+ pprintld2(s, "/XObject<</R%ld %ld 0 R>>", image_id, image_id);
+ pprints1(s, "/ProcSet[/PDF/Image%s]>>\n", (mask ? "B" : "C"));
+ /*
+ * Because of bugs in Acrobat Reader's Print function, we can't use
+ * the natural BBox and Step here: they have to be 1.
+ */
+ pprintg2(s, "/Matrix[%g 0 0 %g 0 0]", tw / xscale, th / yscale);
+ stream_puts(s, "/BBox[0 0 1 1]/XStep 1/YStep 1/Length ");
+ if (image_id) {
+ char buf[MAX_REF_CHARS + 6 + 1]; /* +6 for /R# Do\n */
+
+ gs_sprintf(buf, "/R%ld Do\n", image_id);
+ pprintd1(s, "%d>>stream\n", strlen(buf));
+ if (pdev->PDFA != 0)
+ pprints1(s, "%s\nendstream\n", buf);
+ else
+ pprints1(s, "%sendstream\n", buf);
+ pdf_end_resource(pdev, resourcePattern);
+ } else {
+ length_id = pdf_obj_ref(pdev);
+ pprintld1(s, "%ld 0 R>>stream\n", length_id);
+ start = pdf_stell(pdev);
+ code = copy_data(pdev, tiles->data, 0, tiles->raster,
+ tile_id, 0, 0, tw, th, &image, &writer, -1);
+ switch (code) {
+ default:
+ return code; /* error */
+ case 1:
+ break;
+ case 0: /* not possible */
+ return_error(gs_error_Fatal);
+ }
+ end = pdf_stell(pdev);
+ stream_puts(s, "\nendstream\n");
+ pdf_end_resource(pdev, resourcePattern);
+ pdf_open_separate(pdev, length_id, resourceNone);
+ pprintld1(pdev->strm, "%ld\n", end - start);
+ pdf_end_separate(pdev, resourceNone);
+ }
+ pres->object->written = true; /* don't write at end of page */
+ }
+ /* Fill the rectangle with the Pattern. */
+ {
+ int code = pdf_open_page(pdev, PDF_IN_STREAM);
+ stream *s;
+
+ if (code < 0)
+ goto use_default;
+ /* Make sure we aren't being clipped. */
+ code = pdf_put_clip_path(pdev, NULL);
+ if (code < 0)
+ return code;
+ s = pdev->strm;
+ /*
+ * Because of bugs in Acrobat Reader's Print function, we can't
+ * leave the CTM alone here: we have to reset it to the default.
+ */
+ pprintg2(s, "q %g 0 0 %g 0 0 cm\n", xscale, yscale);
+ cos_value_write(&cs_value, pdev);
+ stream_puts(s, " cs");
+ if (mask)
+ pprintg3(s, " %g %g %g", (int)(color1 >> 16) / 255.0,
+ (int)((color1 >> 8) & 0xff) / 255.0,
+ (int)(color1 & 0xff) / 255.0);
+ pprintld1(s, "/R%ld scn", pdf_resource_id(pres));
+ pprintg4(s, " %g %g %g %g re f Q\n",
+ x / xscale, y / yscale, w / xscale, h / xscale);
+ }
+ return 0;
+use_default:
+ return gx_default_strip_tile_rectangle(dev, tiles, x, y, w, h,
+ color0, color1, px, py);
+}
diff --git a/devices/vector/gdevpdfb.h b/devices/vector/gdevpdfb.h
new file mode 100644
index 000000000..17b6c01d2
--- /dev/null
+++ b/devices/vector/gdevpdfb.h
@@ -0,0 +1,286 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* pdfwrite, ps2write device body template. */
+
+/* This file is allowed to #include several times into a single .c file.
+ The following macros to be defined in advance :
+ PDF_DEVICE_NAME - a string like "pdfwrite".
+ PDF_DEVICE_IDENT - an identifier like gs_pdfwrite_device.
+ PDF_DEVICE_MaxInlineImageSize - a value of PDF_DEVICE_MaxInlineImageSize.
+ PDF_FOR_OPDFREAD - an integer 0 (false) or 1 (true).
+ */
+
+const gx_device_pdf PDF_DEVICE_IDENT =
+{std_device_dci_type_body(gx_device_pdf, 0, PDF_DEVICE_NAME,
+ &st_device_pdfwrite,
+ DEFAULT_WIDTH_10THS * X_DPI / 10,
+ DEFAULT_HEIGHT_10THS * Y_DPI / 10,
+ X_DPI, Y_DPI,
+ 3, 24, 255, 255, 256, 256),
+ {pdf_open,
+ gx_upright_get_initial_matrix,
+ NULL, /* sync_output */
+ pdf_output_page,
+ pdf_close,
+ gx_default_rgb_map_rgb_color,
+ gx_default_rgb_map_color_rgb,
+ gdev_pdf_fill_rectangle,
+ NULL, /* tile_rectangle */
+ gdev_pdf_copy_mono,
+ gdev_pdf_copy_color,
+ NULL, /* draw_line */
+ psdf_get_bits, /* get_bits */
+ gdev_pdf_get_params,
+ gdev_pdf_put_params,
+ NULL, /* map_cmyk_color */
+ NULL, /* get_xfont_procs */
+ NULL, /* get_xfont_device */
+ NULL, /* map_rgb_alpha_color */
+ gx_page_device_get_page_device,
+ NULL, /* get_alpha_bits */
+ NULL, /* copy_alpha */
+ NULL, /* get_band */
+ NULL, /* copy_rop */
+ gdev_pdf_fill_path,
+ gdev_pdf_stroke_path,
+ gdev_pdf_fill_mask,
+ NULL, /* fill_trapezoid */
+ NULL, /* fill_parallelogram */
+ NULL, /* fill_triangle */
+ NULL, /* draw_thin_line */
+ NULL, /* begin_image */
+ NULL, /* image_data */
+ NULL, /* end_image */
+ gdev_pdf_strip_tile_rectangle,
+ NULL, /* strip_copy_rop */
+ NULL, /* get_clipping_box */
+ gdev_pdf_begin_typed_image,
+ psdf_get_bits_rectangle, /* get_bits_rectangle */
+ NULL, /* map_color_rgb_alpha */
+ gdev_pdf_create_compositor, /* create_compositor */
+ NULL, /* get_hardware_params */
+ gdev_pdf_text_begin,
+ NULL, /* finish_copydevice */
+ gdev_pdf_begin_transparency_group, /* begin_transparency_group */
+ gdev_pdf_end_transparency_group, /* end_transparency_group */
+ gdev_pdf_begin_transparency_mask, /* begin_transparency_mask */
+ gdev_pdf_end_transparency_mask, /* end_transparency_mask */
+ NULL, /* discard_transparency_layer */
+ NULL, /* get_color_mapping_procs */
+ NULL, /* get_color_comp_index */
+ NULL, /* encode_color */
+ NULL, /* decode_color */
+ NULL, /* pattern_manage */
+ gdev_pdf_fill_rectangle_hl_color, /* fill_rectangle_hl_color */
+ gdev_pdf_include_color_space, /* include_color_space */
+ NULL, /* fill_linear_color_scanline */
+ NULL, /* fill_linear_color_trapezoid */
+ NULL, /* fill_linear_color_triangle */
+ NULL, /* update_spot_equivalent_colors */
+ NULL, /* ret_devn_params */
+ gdev_pdf_fillpage, /* fillpage */
+ NULL, /* push_transparency_state */
+ NULL, /* pop_transparency_state */
+ NULL, /* put_image */
+ gdev_pdf_dev_spec_op /* dev_spec_op */
+ },
+ psdf_initial_values(PSDF_VERSION_INITIAL, 0 /*false */ ), /* (!ASCII85EncodePages) */
+ 0, /* pdf_font_dir */
+ PDF_FOR_OPDFREAD, /* is_ps2write */
+ PDF_COMPATIBILITY_LEVEL_INITIAL, /* CompatibilityLevel */
+ -1, /* EndPage */
+ 1, /* StartPage */
+ 1 /*true*/, /* Optimize */
+ 0 /*false*/, /* ParseDSCCommentsForDocInfo */
+ 1 /*true*/, /* ParseDSCComments */
+ 0 /*false*/, /* EmitDSCWarnings */
+ 0 /*false*/, /* CreateJobTicket */
+ 0 /*false*/, /* PreserveEPSInfo */
+ 1 /*true*/, /* AutoPositionEPSFiles */
+ 1 /*true*/, /* PreserveCopyPage */
+ 0 /*false*/, /* UsePrologue */
+ 0, /* OffOptimizations */
+ {0,0}, /* PDFXTrimBoxToMediaBoxOffset */
+ {0,0}, /* PDFXBleedBoxToTrimBoxOffset */
+ 1 /* true */, /* PDFXSetBleedBoxToMediaBox */
+ 1 /*true*/, /* ReAssignCharacters */
+ 1 /*true*/, /* ReEncodeCharacters */
+ 1, /* FirstObjectNumber */
+ 1 /*true*/, /* CompressFonts */
+ 0 /*false*/, /* PrintStatistics */
+ {0, 0, 0}, /* DocumentUUID */
+ {0, 0, 0}, /* InstanceUUID */
+ 0, /* DocumentTimeSeq */
+ PDF_FOR_OPDFREAD, /* ForOPDFRead */
+ PDF_FOR_EPS2WRITE, /* is_eps2write */
+ false, /* CompressEntireFile */
+ 0 /*false*/, /* ResourcesBeforeUsage */
+ 1 /*true*/, /* HavePDFWidths */
+ 0 /*false*/, /* HaveStrokeColor */
+ 1 /*true*/, /* ProduceDSC */
+ 1 /*true*/, /* HaveTransparency */
+ 0 /*false*/, /* PatternImagemask */
+ 0 /*false*/, /* PDFX */
+ 0 /*false*/, /* PDFA */
+ 0 /*false*/, /* Abort generation of PDFA or X, produce PDF */
+ 12000, /* MaxClipPathSize */ /* HP LaserJet 1320 hangs with 14000. */
+ 256000, /* MaxShadingBitmapSize */
+ PDF_DEVICE_MaxInlineImageSize, /* MaxInlineImageSize */
+ {0, 0}, /* DSCEncodingToUnicode */
+ {0, 0, 0}, /* OwnerPassword */
+ {0, 0, 0}, /* UserPassword */
+ 0, /* KeyLength */
+ -4, /* Permissions */
+ 0, /* EncryptionR */
+ {0, 0, 0}, /* NoEncrypt */
+ true, /* EncryptMetadata */
+ true, /* ComputeDocumentDigest */
+ {0}, /* EncryptionO */
+ {0}, /* EncryptionU */
+ {0}, /* EncryptionKey */
+ 0, /* EncryptionV */
+ 0 /*false*/, /* is_EPS */
+ 0, /* AccumulatingBBox */
+ {{0,0},{0,0}}, /* BBox */
+ {-1, -1}, /* doc_dsc_info */
+ {-1, -1}, /* page_dsc_info */
+ 0 /*false*/, /* fill_overprint */
+ 0 /*false*/, /* stroke_overprint */
+ 0 /*false*/, /* remap_fill_coilor */
+ 0 /*false*/, /* remap_stroke_coilor */
+ 0, /* overprint_mode */
+ gs_no_id, /* halftone_id */
+ {gs_no_id, gs_no_id, gs_no_id, gs_no_id}, /* transfer_ids */
+ 0, /* transfer_not_identity */
+ gs_no_id, /* black_generation_id */
+ gs_no_id, /* undercolor_removal_id */
+ pdf_compress_none, /* compression */
+ pdf_compress_none, /* compression_at_page_start */
+ {{0}}, /* xref */
+ {{0}}, /* asides */
+ {{0}}, /* streams */
+ {{0}}, /* pictures */
+ 0, /* next_id */
+ 0, /* Catalog */
+ 0, /* Info */
+ 0, /* Pages */
+ 0, /* outlines_id */
+ 0, /* next_page */
+ -1, /* max_referred_page */
+ 0, /* contents_id */
+ PDF_IN_NONE, /* context */
+ 0, /* contents_length_id */
+ 0, /* contents_pos */
+ NoMarks, /* procsets */
+ 0, /* text */
+ {{0}}, /* text_rotation */
+ 0, /* pages */
+ 0, /* num_pages */
+ 1, /* used_mask */
+ {
+ {
+ {0}}}, /* resources */
+ {0}, /* cs_Patterns */
+ {0}, /* Identity_ToUnicode_CMaps */
+ 0, /* last_resource */
+ 0, /* OneByteIdentityH */
+ gs_no_id, /* IdentityCIDSystemInfo_id */
+ 0, /* outline_levels */
+ -1, /* outline_depth */
+ 0, /* max_outline_depth */
+ 0, /* closed_outline_depth */
+ 0, /* outlines_open */
+ 0, /* articles */
+ 0, /* Dests */
+ 0, /* EmbeddedFiles */
+ {0}, /* fileID */
+ {0, 0}, /* uuid_time */
+ 0, /* global_named_objects */
+ 0, /* local_named_objects */
+ 0, /* NI_stack */
+ 0, /* Namespace_stack */
+ 0, /* font_cache */
+ {0, 0}, /* char_width */
+ 0, /* clip_path */
+ 0, /* PageLabels */
+ -1, /* PageLabels_current_page */
+ 0, /* PageLabels_current_label */
+ 0, /* */
+ 0, /* vgstack */
+ 0, /* vgstack_size */
+ 0, /* vgstack_depth */
+ 0, /* vgstack_bottom */
+ {0}, /* vg_initial */
+ false, /* vg_initial_set */
+ 0, /* sbstack_size */
+ 0, /* sbstack_depth */
+ 0, /* sbstack */
+ 0, /* FormDepth */
+ 0, /* HighLevelForm */
+ 0, /* PatternDepth */
+ {0,0,0,0,0,0}, /* AccumulatedPatternMatrix */
+ 0, /* substream_Resources */
+ 1, /* pcm_color_info_index == DeviceRGB */
+ false, /* skip_colors */
+ false, /* AR4_save_bug */
+ 0, /* font3 */
+ 0, /* accumulating_substream_resource */
+ {0,0,0,0,0,0,0,0,0}, /* charproc_ctm */
+ 0, /* accumulating_charproc */
+ {{0, 0}, {0, 0}}, /* Charproc BBox */
+ 0, /* charproc_just_accumulated */
+ 1, /* PS_accumulator */
+ 0, /* accumulating_a_global_object */
+ 0, /* cgp */
+ 0, /* substituted_pattern_count */
+ 0, /* substituted_pattern_drop_page */
+ 0, /* image_mask_id */
+ 0, /* image_mask_is_SMask */
+ 0, /* image_mask_skip */
+ 0, /* image_with_SMask */
+ {0,0,0,0,0,0}, /* gs_matrix converting_image_matrix */
+ 0, /* image_mask_scale */
+ NULL, /* pres_soft_mask_dict */
+ {0, 0}, /* objname */
+ 0, /* OPDFRead_procset_length */
+ 0, /* find_resource_param */
+ 0, /* last_charpath_op */
+ 0, /* type3charpath */
+ 1, /* SetPageSize */
+ 0, /* RotatePages */
+ 0, /* Fit Pages */
+ 0, /* CenterPages */
+ 0, /* DoNumCopies */
+ true, /* PreserveSeparation */
+ true, /* PreserveDeviceN */
+ 0, /* PDFACompatibilityPolicy */
+ true, /* DetectDuplicateImages */
+ false, /* AllowIncrementalCFF */
+ !PDF_FOR_OPDFREAD, /* WantsToUnicode */
+ PDF_FOR_OPDFREAD, /* AllowPSRepeatFunctions */
+ true, /* IsDistiller (true even for ps2write!) */
+ !PDF_FOR_OPDFREAD, /* PreserveSMask */
+ !PDF_FOR_OPDFREAD, /* PreserveTrMode */
+ false, /* NoT3CCITT */
+ false, /* UseOldColor */
+ false, /* Linearise */
+ 0, /* pointer to resourceusage */
+ 0, /* Size of resourceusage */
+ false, /* called from output_page */
+ false, /* FlattenFonts, writes text as outlines instead of fonts */
+ -1 /* Last Form ID, start with -1 which means 'none' */
+};
diff --git a/devices/vector/gdevpdfc.c b/devices/vector/gdevpdfc.c
new file mode 100644
index 000000000..c32ac5595
--- /dev/null
+++ b/devices/vector/gdevpdfc.c
@@ -0,0 +1,1542 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Color space management and writing for pdfwrite driver */
+#include "math_.h"
+#include "memory_.h"
+#include "gx.h"
+#include "gscspace.h" /* for gscie.h */
+#include "gscdevn.h"
+#include "gscie.h"
+#include "gscindex.h"
+#include "gscsepr.h"
+#include "stream.h"
+#include "gsicc.h"
+#include "gserrors.h"
+#include "gsfunc.h" /* required for colour space function evaluation */
+#include "gsfunc3.h" /* Required to create a replacement linear interpolation function */
+#include "gsfunc0.h" /* Required to create a sampled function for DeviceN alternate replacement */
+#include "gdevpdfx.h"
+#include "gdevpdfg.h"
+#include "gdevpdfc.h"
+#include "gdevpdfo.h"
+#include "strimpl.h"
+#include "sstring.h"
+#include "gxcspace.h"
+#include "gxcdevn.h"
+#include "gscspace.h"
+#include "gsicc_manage.h"
+#include "gsicc_cache.h"
+
+/*
+ * PDF doesn't have general CIEBased color spaces. However, it provides
+ * two methods for handling general CIE spaces:
+ *
+ * - For PDF 1.2 and above, we note that the transformation from L*a*b*
+ * space to XYZ space is invertible, so we can handle any PostScript
+ * CIEBased space by transforming color values in that space to XYZ,
+ * then inverse-transforming them to L*a*b* and using a PDF Lab space
+ * with the same WhitePoint and BlackPoint and appropriate ranges for
+ * a and b. This approach has the drawback that Y values outside the
+ * range [0..1] can't be represented: we just clamp them.
+ *
+ * - For PDF 1.3 and above, we can create an ICCBased space. This is
+ * actually necessary, not just an option, because for shadings (also
+ * introduced in PDF 1.3), we want color interpolation to occur in the
+ * original space.
+ *
+ * The Lab approach is not currently implemented, because it requires
+ * transforming all the sample values of images. The ICCBased approach is
+ * implemented for color spaces whose ranges lie within [0..1], which are
+ * the only ranges supported by the ICC standard: we think that removing
+ * this limitation would also require transforming image sample values.
+ */
+
+/* GC descriptors */
+public_st_pdf_color_space();
+
+/* ------ CIE space testing ------ */
+
+/* Test whether a cached CIE procedure is the identity function. */
+#define CIE_CACHE_IS_IDENTITY(pc)\
+ ((pc)->floats.params.is_identity)
+#define CIE_CACHE3_IS_IDENTITY(pca)\
+ (CIE_CACHE_IS_IDENTITY(&(pca)[0]) &&\
+ CIE_CACHE_IS_IDENTITY(&(pca)[1]) &&\
+ CIE_CACHE_IS_IDENTITY(&(pca)[2]))
+
+/*
+ * Test whether a cached CIE procedure is an exponential. A cached
+ * procedure is exponential iff f(x) = k*(x^p). We make a very cursory
+ * check for this: we require that f(0) = 0, set k = f(1), set p =
+ * log[a](f(a)/k), and then require that f(b) = k*(b^p), where a and b are
+ * two arbitrarily chosen values between 0 and 1. Naturally all this is
+ * done with some slop.
+ */
+#define CC_INDEX_A (gx_cie_cache_size / 3)
+#define CC_INDEX_B (gx_cie_cache_size * 2 / 3)
+#define CC_INDEX_1 (gx_cie_cache_size - 1)
+#define CC_KEY(i) ((i) / (double)CC_INDEX_1)
+#define CC_KEY_A CC_KEY(CC_INDEX_A)
+#define CC_KEY_B CC_KEY(CC_INDEX_B)
+
+static bool
+cie_values_are_exponential(double v0, double va, double vb, double k,
+ float *pexpt)
+{
+ double p;
+
+ if (fabs(v0) >= 0.001 || fabs(k) < 0.001)
+ return false;
+ if (va == 0 || (va > 0) != (k > 0))
+ return false;
+ p = log(va / k) / log(CC_KEY_A);
+ if (fabs(vb - k * pow(CC_KEY_B, p)) >= 0.001)
+ return false;
+ *pexpt = p;
+ return true;
+}
+
+static bool
+cie_scalar_cache_is_exponential(const gx_cie_scalar_cache * pc, float *pexpt)
+{
+ return cie_values_are_exponential(pc->floats.values[0],
+ pc->floats.values[CC_INDEX_A],
+ pc->floats.values[CC_INDEX_B],
+ pc->floats.values[CC_INDEX_1],
+ pexpt);
+}
+#define CIE_SCALAR3_CACHE_IS_EXPONENTIAL(pca, expts)\
+ (cie_scalar_cache_is_exponential(&(pca)[0], &(expts).u) &&\
+ cie_scalar_cache_is_exponential(&(pca)[1], &(expts).v) &&\
+ cie_scalar_cache_is_exponential(&(pca)[2], &(expts).w))
+
+static bool
+cie_vector_cache_is_exponential(const gx_cie_vector_cache * pc, float *pexpt)
+{
+ return cie_values_are_exponential(pc->vecs.values[0].u,
+ pc->vecs.values[CC_INDEX_A].u,
+ pc->vecs.values[CC_INDEX_B].u,
+ pc->vecs.values[CC_INDEX_1].u,
+ pexpt);
+}
+#define CIE_VECTOR3_CACHE_IS_EXPONENTIAL(pca, expts)\
+ (cie_vector_cache_is_exponential(&(pca)[0], &(expts).u) &&\
+ cie_vector_cache_is_exponential(&(pca)[1], &(expts).v) &&\
+ cie_vector_cache_is_exponential(&(pca)[2], &(expts).w))
+
+#undef CC_INDEX_A
+#undef CC_INDEX_B
+#undef CC_KEY_A
+#undef CC_KEY_B
+
+/*
+ * Test whether a cached CIEBasedABC space consists only of a single
+ * Decode step followed by a single Matrix step.
+ */
+static cie_cache_one_step_t
+cie_cached_abc_is_one_step(const gs_cie_abc *pcie, const gs_matrix3 **ppmat)
+{
+ /* The order of steps is, DecodeABC, MatrixABC, DecodeLMN, MatrixLMN. */
+
+ if (CIE_CACHE3_IS_IDENTITY(pcie->common.caches.DecodeLMN)) {
+ if (pcie->MatrixABC.is_identity) {
+ *ppmat = &pcie->common.MatrixLMN;
+ return ONE_STEP_ABC;
+ }
+ if (pcie->common.MatrixLMN.is_identity) {
+ *ppmat = &pcie->MatrixABC;
+ return ONE_STEP_ABC;
+ }
+ }
+ if (CIE_CACHE3_IS_IDENTITY(pcie->caches.DecodeABC.caches)) {
+ if (pcie->MatrixABC.is_identity) {
+ *ppmat = &pcie->common.MatrixLMN;
+ return ONE_STEP_LMN;
+ }
+ }
+ return ONE_STEP_NOT;
+}
+
+/*
+ * Test whether a cached CIEBasedABC space is a L*a*b* space.
+ */
+static bool
+cie_scalar_cache_is_lab_lmn(const gs_cie_abc *pcie, int i)
+{
+ double k = CC_KEY(i);
+ double g = (k >= 6.0 / 29 ? k * k * k :
+ (k - 4.0 / 29) * (108.0 / 841));
+
+#define CC_V(j,i) (pcie->common.caches.DecodeLMN[j].floats.values[i])
+#define CC_WP(uvw) (pcie->common.points.WhitePoint.uvw)
+
+ return (fabs(CC_V(0, i) - g * CC_WP(u)) < 0.001 &&
+ fabs(CC_V(1, i) - g * CC_WP(v)) < 0.001 &&
+ fabs(CC_V(2, i) - g * CC_WP(w)) < 0.001
+ );
+
+#undef CC_V
+#undef CC_WP
+}
+static bool
+cie_vector_cache_is_lab_abc(const gx_cie_vector_cache3_t *pvc, int i)
+{
+ const gx_cie_vector_cache *const pc3 = pvc->caches;
+ double k = CC_KEY(i);
+ double l0 = pc3[0].vecs.params.base,
+ l = l0 + k * (pc3[0].vecs.params.limit - l0);
+ double a0 = pc3[1].vecs.params.base,
+ a = a0 + k * (pc3[1].vecs.params.limit - a0);
+ double b0 = pc3[2].vecs.params.base,
+ b = b0 + k * (pc3[2].vecs.params.limit - b0);
+
+ return (fabs(cie_cached2float(pc3[0].vecs.values[i].u) -
+ (l + 16) / 116) < 0.001 &&
+ fabs(cie_cached2float(pc3[1].vecs.values[i].u) -
+ a / 500) < 0.001 &&
+ fabs(cie_cached2float(pc3[2].vecs.values[i].w) -
+ b / -200) < 0.001
+ );
+}
+
+static bool
+cie_is_lab(const gs_cie_abc *pcie)
+{
+ int i;
+
+ /* Check MatrixABC and MatrixLMN. */
+ if (!(pcie->MatrixABC.cu.u == 1 && pcie->MatrixABC.cu.v == 1 &&
+ pcie->MatrixABC.cu.w == 1 &&
+ pcie->MatrixABC.cv.u == 1 && pcie->MatrixABC.cv.v == 0 &&
+ pcie->MatrixABC.cv.w == 0 &&
+ pcie->MatrixABC.cw.u == 0 && pcie->MatrixABC.cw.v == 0 &&
+ pcie->MatrixABC.cw.w == -1 &&
+ pcie->common.MatrixLMN.is_identity
+ ))
+ return false;
+
+ /* Check DecodeABC and DecodeLMN. */
+ for (i = 0; i <= CC_INDEX_1; ++i)
+ if (!(cie_vector_cache_is_lab_abc(&pcie->caches.DecodeABC, i) &&
+ cie_scalar_cache_is_lab_lmn(pcie, i)
+ ))
+ return false;
+
+ return true;
+}
+
+#undef CC_INDEX_1
+#undef CC_KEY
+
+/* Test whether one or more CIE-based ranges are [0..1]. */
+static bool
+cie_ranges_are_0_1(const gs_range *prange, int n)
+{
+ int i;
+
+ for (i = 0; i < n; ++i)
+ if (prange[i].rmin != 0 || prange[i].rmax != 1)
+ return false;
+ return true;
+}
+
+/* ------ Utilities ------ */
+
+/* Add a 3-element vector to a Cos array or dictionary. */
+static int
+cos_array_add_vector3(cos_array_t *pca, const gs_vector3 *pvec)
+{
+ int code = cos_array_add_real(pca, pvec->u);
+
+ if (code >= 0)
+ code = cos_array_add_real(pca, pvec->v);
+ if (code >= 0)
+ code = cos_array_add_real(pca, pvec->w);
+ return code;
+}
+static int
+cos_dict_put_c_key_vector3(gx_device_pdf *pdev, cos_dict_t *pcd, const char *key,
+ const gs_vector3 *pvec)
+{
+ cos_array_t *pca = cos_array_alloc(pdev, "cos_array_from_vector3");
+ int code;
+
+ if (pca == 0)
+ return_error(gs_error_VMerror);
+ code = cos_array_add_vector3(pca, pvec);
+ if (code < 0) {
+ COS_FREE(pca, "cos_array_from_vector3");
+ return code;
+ }
+ return cos_dict_put_c_key_object(pcd, key, COS_OBJECT(pca));
+}
+
+/*
+ * Finish creating a CIE-based color space (Calxxx or Lab.)
+ * This procedure is exported for gdevpdfk.c.
+ */
+int
+pdf_finish_cie_space(gx_device_pdf *pdev, cos_array_t *pca, cos_dict_t *pcd,
+ const gs_cie_common *pciec)
+{
+ int code = cos_dict_put_c_key_vector3(pdev, pcd, "/WhitePoint",
+ &pciec->points.WhitePoint);
+
+ if (code < 0)
+ return code;
+ if (pciec->points.BlackPoint.u != 0 ||
+ pciec->points.BlackPoint.v != 0 ||
+ pciec->points.BlackPoint.w != 0
+ ) {
+ code = cos_dict_put_c_key_vector3(pdev, pcd, "/BlackPoint",
+ &pciec->points.BlackPoint);
+ if (code < 0)
+ return code;
+ }
+ return cos_array_add_object(pca, COS_OBJECT(pcd));
+}
+
+/* ------ Color space writing ------ */
+
+/* Define standard and short color space names. */
+const pdf_color_space_names_t pdf_color_space_names = {
+ PDF_COLOR_SPACE_NAMES
+};
+const pdf_color_space_names_t pdf_color_space_names_short = {
+ PDF_COLOR_SPACE_NAMES_SHORT
+};
+
+/*
+ * Create a local Device{Gray,RGB,CMYK} color space corresponding to the
+ * given number of components.
+ */
+int
+pdf_cspace_init_Device(gs_memory_t *mem, gs_color_space **ppcs,
+ int num_components)
+{
+ switch (num_components) {
+ case 1: *ppcs = gs_cspace_new_DeviceGray(mem); break;
+ case 3: *ppcs = gs_cspace_new_DeviceRGB(mem); break;
+ case 4: *ppcs = gs_cspace_new_DeviceCMYK(mem); break;
+ default: return_error(gs_error_rangecheck);
+ }
+ return 0;
+}
+
+int pdf_delete_sampled_base_space_function(gx_device_pdf *pdev, gs_function_t *pfn)
+{
+ gs_function_Sd_params_t *params = (gs_function_Sd_params_t *)&pfn->params;
+
+ gs_free_object(pdev->memory, (void *)params->Domain, "pdf_delete_function");
+ gs_free_object(pdev->memory, (void *)params->Range, "pdf_delete_function");
+ gs_free_string(pdev->memory, (void *)params->DataSource.data.str.data, params->DataSource.data.str.size, "pdf_dselete_function");
+ gs_free_object(pdev->memory, (void *)pfn, "pdf_delete_function");
+ return 0;
+}
+
+int pdf_delete_base_space_function(gx_device_pdf *pdev, gs_function_t *pfn)
+{
+ gs_function_ElIn_params_t *params = (gs_function_ElIn_params_t *)&pfn->params;
+
+ gs_free_object(pdev->memory, (void *)params->Domain, "pdf_delete_function");
+ gs_free_object(pdev->memory, (void *)params->Range, "pdf_delete_function");
+ gs_free_object(pdev->memory, (void *)params->C0, "pdf_delete_function");
+ gs_free_object(pdev->memory, (void *)params->C1, "pdf_delete_function");
+ gs_free_object(pdev->memory, (void *)pfn, "pdf_delete_function");
+ return 0;
+}
+
+int pdf_make_sampled_base_space_function(gx_device_pdf *pdev, gs_function_t **pfn,
+ int nSrcComp, int nDstComp, byte *data)
+{
+ gs_function_Sd_params_t params;
+ void *ptr1, *ptr2;
+ int i, code;
+ gs_const_string str;
+
+ str.data = gs_alloc_string(pdev->memory, nDstComp * nSrcComp * 2 * sizeof(float), "pdf_DeviceN");
+ str.size = nDstComp * (uint)pow(2, nSrcComp);
+ memcpy((void *)str.data, data, str.size);
+
+ params.m = nSrcComp;
+ params.n = nDstComp;
+ params.Order = 1;
+ params.BitsPerSample = 8;
+
+ ptr1 = gs_alloc_byte_array(pdev->memory, nSrcComp, sizeof(int), "pdf_make_function(Domain)");
+ for (i=0;i<nSrcComp;i++) {
+ ((int *)ptr1)[i] = 2;
+ }
+ params.Size = (const int *)ptr1;
+
+ ptr1 = (float *)
+ gs_alloc_byte_array(pdev->memory, 2 * nSrcComp, sizeof(float), "pdf_make_function(Domain)");
+ if (ptr1 == 0) {
+ return gs_note_error(gs_error_VMerror);
+ }
+ ptr2 = (float *)
+ gs_alloc_byte_array(pdev->memory, 2 * nDstComp, sizeof(float), "pdf_make_function(Range)");
+ if (ptr2 == 0) {
+ gs_free_object(pdev->memory, (void *)ptr1, "pdf_make_function(Range)");
+ return gs_note_error(gs_error_VMerror);
+ }
+ for (i=0;i<nSrcComp;i++) {
+ ((float *)ptr1)[i*2] = 0.0f;
+ ((float *)ptr1)[(i*2) + 1] = 1.0f;
+ }
+ for (i=0;i<nDstComp;i++) {
+ ((float *)ptr2)[i*2] = 0.0f;
+ ((float *)ptr2)[(i*2) + 1] = 1.0f;
+ }
+ params.Domain = ptr1;
+ params.Range = ptr2;
+ params.Encode = params.Decode = NULL;
+ data_source_init_string(&params.DataSource, str);
+
+ code = gs_function_Sd_init(pfn, &params, pdev->memory);
+ return code;
+}
+
+int pdf_make_base_space_function(gx_device_pdf *pdev, gs_function_t **pfn,
+ int ncomp, float *data_low, float *data_high)
+{
+ gs_function_ElIn_params_t params;
+ float *ptr1, *ptr2;
+ int i, code;
+
+ ptr1 = (float *)
+ gs_alloc_byte_array(pdev->memory, 2, sizeof(float), "pdf_make_function(Domain)");
+ if (ptr1 == 0) {
+ return gs_note_error(gs_error_VMerror);
+ }
+ ptr2 = (float *)
+ gs_alloc_byte_array(pdev->memory, 2 * ncomp, sizeof(float), "pdf_make_function(Range)");
+ if (ptr2 == 0) {
+ gs_free_object(pdev->memory, (void *)ptr1, "pdf_make_function(Range)");
+ return gs_note_error(gs_error_VMerror);
+ }
+ params.m = 1;
+ params.n = ncomp;
+ params.N = 1.0f;
+ ptr1[0] = 0.0f;
+ ptr1[1] = 1.0f;
+ for (i=0;i<ncomp;i++) {
+ ptr2[i*2] = 0.0f;
+ ptr2[(i*2) + 1] = 1.0f;
+ }
+ params.Domain = ptr1;
+ params.Range = ptr2;
+
+ ptr1 = (float *)gs_alloc_byte_array(pdev->memory, ncomp, sizeof(float), "pdf_make_function(C0)");
+ if (ptr1 == 0) {
+ gs_free_object(pdev->memory, (void *)params.Domain, "pdf_make_function(C0)");
+ gs_free_object(pdev->memory, (void *)params.Range, "pdf_make_function(C0)");
+ return gs_note_error(gs_error_VMerror);
+ }
+ ptr2 = (float *)gs_alloc_byte_array(pdev->memory, ncomp, sizeof(float), "pdf_make_function(C1)");
+ if (ptr2 == 0) {
+ gs_free_object(pdev->memory, (void *)params.Domain, "pdf_make_function(C1)");
+ gs_free_object(pdev->memory, (void *)params.Range, "pdf_make_function(C1)");
+ gs_free_object(pdev->memory, (void *)ptr1, "pdf_make_function(C1)");
+ return gs_note_error(gs_error_VMerror);
+ }
+
+ for (i=0;i<ncomp;i++) {
+ ptr1[i] = data_low[i];
+ ptr2[i] = data_high[i];
+ }
+ params.C0 = ptr1;
+ params.C1 = ptr2;
+ code = gs_function_ElIn_init(pfn, &params, pdev->memory);
+ if (code < 0) {
+ gs_free_object(pdev->memory, (void *)params.Domain, "pdf_make_function");
+ gs_free_object(pdev->memory, (void *)params.Range, "pdf_make_function");
+ gs_free_object(pdev->memory, (void *)params.C0, "pdf_make_function");
+ gs_free_object(pdev->memory, (void *)params.C1, "pdf_make_function");
+ }
+ return code;
+}
+
+static void pdf_SepRGB_ConvertToCMYK (float *in, float *out)
+{
+ float CMYK[4];
+ int i;
+
+ if (in[0] <= in[1] && in[0] <= in[2]) {
+ CMYK[3] = 1.0 - in[0];
+ } else {
+ if (in[1]<= in[0] && in[1] <= in[2]) {
+ CMYK[3] = 1.0 - in[1];
+ } else {
+ CMYK[3] = 1.0 - in[2];
+ }
+ }
+ CMYK[0] = 1.0 - in[0] - CMYK[3];
+ CMYK[1] = 1.0 - in[1] - CMYK[3];
+ CMYK[2] = 1.0 - in[2] - CMYK[3];
+ for (i=0;i<4;i++)
+ out[i] = CMYK[i];
+}
+
+static void pdf_SepCMYK_ConvertToRGB (float *in, float *out)
+{
+ float RGB[3];
+
+ RGB[0] = in[0] + in[3];
+ RGB[1] = in[1] + in[3];
+ RGB[2] = in[2] + in[3];
+
+ if (RGB[0] > 1)
+ out[0] = 0.0f;
+ else
+ out[0] = 1 - RGB[0];
+ if (RGB[1] > 1)
+ out[1] = 0.0f;
+ else
+ out[1] = 1 - RGB[1];
+ if (RGB[2] > 1)
+ out[2] = 0.0f;
+ else
+ out[2] = 1 - RGB[2];
+}
+
+/* Create a Separation or DeviceN color space (internal). */
+static int
+pdf_separation_color_space(gx_device_pdf *pdev, const gs_imager_state * pis,
+ cos_array_t *pca, const char *csname,
+ const cos_value_t *snames,
+ const gs_color_space *alt_space,
+ const gs_function_t *pfn,
+ const pdf_color_space_names_t *pcsn,
+ const cos_value_t *v_attributes)
+{
+ cos_value_t v;
+ const gs_range_t *ranges;
+ int code, csi;
+
+ /* We need to think about the alternate space. If we are producing
+ * PDF/X or PDF/A we can't produce some device spaces, and the code in
+ * pdf_color_space_named always allows device spaces. We could alter
+ * that code, but by then we don't know its an Alternate space, and have
+ * lost the tin transform procedure. So instead we check here.
+ */
+ csi = gs_color_space_get_index(alt_space);
+ /* Note that if csi is ICC, check to see if this was one of
+ the default substitutes that we introduced for DeviceGray,
+ DeviceRGB or DeviceCMYK. If it is, then just write
+ the default color. Depending upon the flavor of PDF,
+ or other options, we may want to actually have all
+ the colors defined by ICC profiles and not do the following
+ substituion of the Device space. */
+ if (csi == gs_color_space_index_ICC) {
+ csi = gsicc_get_default_type(alt_space->cmm_icc_profile_data);
+ }
+ if (csi == gs_color_space_index_DeviceRGB && (pdev->PDFX ||
+ (pdev->PDFA != 0 && (pdev->pcm_color_info_index == gs_color_space_index_DeviceCMYK)))) {
+
+ /* We have a DeviceRGB alternate, but are producing either PDF/X or
+ * PDF/A with a DeviceCMYK process color model. So we need to convert
+ * the alternate space into CMYK. We do this by evaluating the function
+ * at each end of the Separation space (0 and 1), convert the resulting
+ * RGB colours into CMYK and create a new function which linearly
+ * interpolates between these points.
+ */
+ gs_function_t *new_pfn = 0;
+ float in[1] = {0.0f};
+ float out_low[4];
+ float out_high[4];
+
+ code = gs_function_evaluate(pfn, in, out_low);
+ if (code < 0)
+ return code;
+ pdf_SepRGB_ConvertToCMYK((float *)&out_low, (float *)&out_low);
+
+ in[0] = 1.0f;
+ code = gs_function_evaluate(pfn, in, out_high);
+ if (code < 0)
+ return code;
+ pdf_SepRGB_ConvertToCMYK((float *)&out_high, (float *)&out_high);
+
+ code = pdf_make_base_space_function(pdev, &new_pfn, 4, out_low, out_high);
+ if (code < 0)
+ return code;
+
+ code = cos_array_add(pca, cos_c_string_value(&v, csname));
+ if (code >= 0) {
+ code = cos_array_add_no_copy(pca, snames);
+ if (code >= 0) {
+ cos_c_string_value(&v, (const char *)pcsn->DeviceCMYK);
+ code = cos_array_add(pca, &v);
+ if (code >= 0) {
+ code = pdf_function_scaled(pdev, new_pfn, 0x00, &v);
+ if (code >= 0) {
+ code = cos_array_add(pca, &v);
+ if (code >= 0 && v_attributes != NULL)
+ code = cos_array_add(pca, v_attributes);
+ }
+ }
+ }
+ }
+ pdf_delete_base_space_function(pdev, new_pfn);
+ return code;
+ }
+ if (csi == gs_color_space_index_DeviceCMYK &&
+ (pdev->PDFA != 0 && (pdev->pcm_color_info_index == gs_color_space_index_DeviceRGB))) {
+ /* We have a DeviceCMYK alternate, but are producingPDF/A with a
+ * DeviceRGB process color model. See comment above re DviceRGB.
+ */
+ gs_function_t *new_pfn = 0;
+ float in[1] = {0.0f};
+ float out_low[4];
+ float out_high[4];
+
+ code = gs_function_evaluate(pfn, in, out_low);
+ if (code < 0)
+ return code;
+ pdf_SepCMYK_ConvertToRGB((float *)&out_low, (float *)&out_low);
+
+ in[0] = 1.0f;
+ code = gs_function_evaluate(pfn, in, out_high);
+ if (code < 0)
+ return code;
+ pdf_SepCMYK_ConvertToRGB((float *)&out_high, (float *)&out_high);
+
+ code = pdf_make_base_space_function(pdev, &new_pfn, 3, out_low, out_high);
+ if (code < 0)
+ return code;
+
+ code = cos_array_add(pca, cos_c_string_value(&v, csname));
+ if (code >= 0) {
+ code = cos_array_add_no_copy(pca, snames);
+ if (code >= 0) {
+ cos_c_string_value(&v, pcsn->DeviceRGB);
+ code = cos_array_add(pca, &v);
+ if (code >= 0) {
+ code = pdf_function_scaled(pdev, new_pfn, 0x00, &v);
+ if (code >= 0) {
+ code = cos_array_add(pca, &v);
+ if (code >= 0 && v_attributes != NULL)
+ code = cos_array_add(pca, v_attributes);
+ }
+ }
+ }
+ }
+ pdf_delete_base_space_function(pdev, new_pfn);
+ return code;
+ }
+
+ if ((code = cos_array_add(pca, cos_c_string_value(&v, csname))) < 0 ||
+ (code = cos_array_add_no_copy(pca, snames)) < 0 ||
+ (code = pdf_color_space_named(pdev, pis, &v, &ranges, alt_space, pcsn, false, NULL, 0, false)) < 0 ||
+ (code = cos_array_add(pca, &v)) < 0 ||
+ (code = pdf_function_scaled(pdev, pfn, ranges, &v)) < 0 ||
+ (code = cos_array_add(pca, &v)) < 0 ||
+ (v_attributes != NULL ? code = cos_array_add(pca, v_attributes) : 0) < 0
+ )
+ return code;
+ return 0;
+}
+
+/*
+ * Create an Indexed color space. This is a single-use procedure,
+ * broken out only for readability.
+ */
+int
+pdf_indexed_color_space(gx_device_pdf *pdev, const gs_imager_state * pis, cos_value_t *pvalue,
+ const gs_color_space *pcs, cos_array_t *pca, cos_value_t *cos_base)
+{
+ const gs_indexed_params *pip = &pcs->params.indexed;
+ const gs_color_space *base_space = pcs->base_space;
+ int num_entries = pip->hival + 1;
+ int num_components = gs_color_space_num_components(base_space);
+ uint table_size = num_entries * num_components;
+ /* Guess at the extra space needed for PS string encoding. */
+ uint string_size = 2 + table_size * 4;
+ uint string_used;
+ byte buf[100]; /* arbitrary */
+ stream_AXE_state st;
+ stream s, es;
+ gs_memory_t *mem = pdev->pdf_memory;
+ byte *table;
+ byte *palette;
+ cos_value_t v;
+ int code;
+
+ /* PDF doesn't support Indexed color spaces with more than 256 entries. */
+ if (num_entries > 256)
+ return_error(gs_error_rangecheck);
+ if (pdev->CompatibilityLevel < 1.3 && !pdev->ForOPDFRead) {
+ switch (gs_color_space_get_index(pcs)) {
+ case gs_color_space_index_Pattern:
+ case gs_color_space_index_Separation:
+ case gs_color_space_index_Indexed:
+ case gs_color_space_index_DeviceN:
+ return_error(gs_error_rangecheck);
+ default: DO_NOTHING;
+ }
+
+ }
+ table = gs_alloc_string(mem, string_size, "pdf_color_space(table)");
+ palette = gs_alloc_string(mem, table_size, "pdf_color_space(palette)");
+ if (table == 0 || palette == 0) {
+ gs_free_string(mem, palette, table_size,
+ "pdf_color_space(palette)");
+ gs_free_string(mem, table, string_size,
+ "pdf_color_space(table)");
+ return_error(gs_error_VMerror);
+ }
+ s_init(&s, mem);
+ swrite_string(&s, table, string_size);
+ s_init(&es, mem);
+ s_init_state((stream_state *)&st, &s_PSSE_template, NULL);
+ s_init_filter(&es, (stream_state *)&st, buf, sizeof(buf), &s);
+ sputc(&s, '(');
+ if (pcs->params.indexed.use_proc) {
+ gs_client_color cmin, cmax;
+ byte *pnext = palette;
+ int i, j;
+
+ /* Find the legal range for the color components. */
+ for (j = 0; j < num_components; ++j)
+ cmin.paint.values[j] = (float)min_long,
+ cmax.paint.values[j] = (float)max_long;
+ gs_color_space_restrict_color(&cmin, base_space);
+ gs_color_space_restrict_color(&cmax, base_space);
+ /*
+ * Compute the palette values, with the legal range for each
+ * one mapped to [0 .. 255].
+ */
+ for (i = 0; i < num_entries; ++i) {
+ gs_client_color cc;
+
+ gs_cspace_indexed_lookup(pcs, i, &cc);
+ for (j = 0; j < num_components; ++j) {
+ float v = (cc.paint.values[j] - cmin.paint.values[j])
+ * 255 / (cmax.paint.values[j] - cmin.paint.values[j]);
+
+ *pnext++ = (v <= 0 ? 0 : v >= 255 ? 255 : (byte)v);
+ }
+ }
+ } else
+ memcpy(palette, pip->lookup.table.data, table_size);
+ if (gs_color_space_get_index(base_space) ==
+ gs_color_space_index_DeviceRGB
+ ) {
+ /* Check for an all-gray palette3. */
+ int i;
+
+ for (i = table_size; (i -= 3) >= 0; )
+ if (palette[i] != palette[i + 1] ||
+ palette[i] != palette[i + 2]
+ )
+ break;
+ if (i < 0) {
+ /* Change the color space to DeviceGray. */
+ for (i = 0; i < num_entries; ++i)
+ palette[i] = palette[i * 3];
+ table_size = num_entries;
+ base_space = gs_cspace_new_DeviceGray(mem);
+ }
+ }
+ stream_write(&es, palette, table_size);
+ gs_free_string(mem, palette, table_size, "pdf_color_space(palette)");
+ sclose(&es);
+ sflush(&s);
+ string_used = (uint)stell(&s);
+ table = gs_resize_string(mem, table, string_size, string_used,
+ "pdf_color_space(table)");
+ /*
+ * Since the array is always referenced by name as a resource
+ * rather than being written as a value, even for in-line images,
+ * always use the full name for the color space.
+ *
+ * We don't have to worry about the range of the base space:
+ * in PDF, unlike PostScript, the values from the lookup table are
+ * scaled automatically.
+ */
+ if (pdev->UseOldColor || cos_base == NULL) {
+ if ((code = pdf_color_space_named(pdev, pis, pvalue, NULL, base_space,
+ &pdf_color_space_names, false, NULL, 0, false)) < 0 ||
+ (code = cos_array_add(pca,
+ cos_c_string_value(&v,
+ pdf_color_space_names.Indexed
+ /*pcsn->Indexed*/))) < 0 ||
+ (code = cos_array_add(pca, pvalue)) < 0 ||
+ (code = cos_array_add_int(pca, pip->hival)) < 0 ||
+ (code = cos_array_add_no_copy(pca,
+ cos_string_value(&v, table,
+ string_used))) < 0
+ )
+ return code;
+ } else {
+ code = cos_array_add(pca, cos_c_string_value(&v, pdf_color_space_names.Indexed));
+ code = cos_array_add(pca, cos_base);
+ code = cos_array_add_int(pca, pip->hival);
+ code = cos_array_add_no_copy(pca, cos_string_value(&v, table, string_used));
+ }
+ return 0;
+}
+
+/*
+ * Find a color space resource by seriialized data.
+ */
+static pdf_resource_t *
+pdf_find_cspace_resource(gx_device_pdf *pdev, const byte *serialized, uint serialized_size)
+{
+ pdf_resource_t **pchain = pdev->resources[resourceColorSpace].chains;
+ pdf_resource_t *pres;
+ int i;
+
+ for (i = 0; i < NUM_RESOURCE_CHAINS; i++) {
+ for (pres = pchain[i]; pres != 0; pres = pres->next) {
+ const pdf_color_space_t *const ppcs =
+ (const pdf_color_space_t *)pres;
+ if (ppcs->serialized_size != serialized_size)
+ continue;
+ if (!memcmp(ppcs->serialized, serialized, ppcs->serialized_size))
+ return pres;
+ }
+ }
+ return NULL;
+}
+
+int pdf_convert_ICC(gx_device_pdf *pdev,
+ const gs_color_space *pcs, cos_value_t *pvalue,
+ const pdf_color_space_names_t *pcsn)
+{
+ gs_color_space_index csi;
+ int code;
+
+ csi = gs_color_space_get_index(pcs);
+ if (csi == gs_color_space_index_ICC) {
+ csi = gsicc_get_default_type(pcs->cmm_icc_profile_data);
+ }
+ if (csi == gs_color_space_index_Indexed) {
+ pcs = pcs->base_space;
+ csi = gs_color_space_get_index(pcs);
+ }
+ if (csi == gs_color_space_index_ICC) {
+ if (pcs->cmm_icc_profile_data == NULL ||
+ pdev->CompatibilityLevel < 1.3
+ ) {
+ if (pcs->base_space != NULL) {
+ return 0;
+ } else {
+ int num_des_comps;
+ cmm_dev_profile_t *dev_profile;
+
+ /* determine number of components in device space */
+ code = dev_proc((gx_device *)pdev, get_profile)((gx_device *)pdev, &dev_profile);
+ if (code < 0)
+ return code;
+
+ num_des_comps = gsicc_get_device_profile_comps(dev_profile);
+ /* Set image color space to be device space */
+ switch( num_des_comps ) {
+ case 1:
+ cos_c_string_value(pvalue, pcsn->DeviceGray);
+ /* negative return means we do conversion */
+ return -1;
+ case 3:
+ cos_c_string_value(pvalue, pcsn->DeviceRGB);
+ return -1;
+ case 4:
+ cos_c_string_value(pvalue, pcsn->DeviceCMYK);
+ return -1;
+ default:
+ break;
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+/*
+ * Create a PDF color space corresponding to a PostScript color space.
+ * For parameterless color spaces, set *pvalue to a (literal) string with
+ * the color space name; for other color spaces, create a cos_array_t if
+ * necessary and set *pvalue to refer to it. In the latter case, if
+ * by_name is true, return a string /Rxxxx rather than a reference to
+ * the actual object.
+ *
+ * If ppranges is not NULL, then if the domain of the color space had
+ * to be scaled (to convert a CIEBased space to ICCBased), store a pointer
+ * to the ranges in *ppranges, otherwise set *ppranges to 0.
+ */
+int
+pdf_color_space_named(gx_device_pdf *pdev, const gs_imager_state * pis,
+ cos_value_t *pvalue,
+ const gs_range_t **ppranges,
+ const gs_color_space *pcs_in,
+ const pdf_color_space_names_t *pcsn,
+ bool by_name, const byte *res_name, int name_length, bool keepICC)
+{
+ const gs_color_space *pcs;
+ gs_color_space_index csi;
+ cos_array_t *pca;
+ cos_dict_t *pcd;
+ cos_value_t v;
+ const gs_cie_common *pciec;
+ gs_function_t *pfn;
+ const gs_range_t *ranges = 0;
+ uint serialized_size = 0;
+ byte *serialized = NULL, serialized0[100];
+ pdf_resource_t *pres = NULL;
+ int code;
+ bool is_lab = false;
+
+ /* If color space is CIE based and we have compatibility then go ahead and use the ICC alternative */
+ if ((pdev->CompatibilityLevel < 1.3) || !gs_color_space_is_PSCIE(pcs_in) ) {
+ pcs = pcs_in;
+ } else {
+ pcs = pcs_in;
+ /* The snippet below creates an ICC equivalent profile for the PS
+ color space. This is disabled until I add the capability to
+ specify the profile version to ensure compatability with
+ the PDF versions */
+#ifdef DEPRECATED_906
+ if (pcs_in->icc_equivalent != NULL) {
+ pcs = pcs_in->icc_equivalent;
+ } else {
+ /* Need to create the equivalent object */
+ gs_colorspace_set_icc_equivalent((gs_color_space *)pcs_in, &islab, pdev->memory);
+ pcs = pcs_in->icc_equivalent;
+ }
+#endif
+ }
+ csi = gs_color_space_get_index(pcs);
+ /* Note that if csi is ICC, check to see if this was one of
+ the default substitutes that we introduced for DeviceGray,
+ DeviceRGB or DeviceCMYK. If it is, then just write
+ the default color. Depending upon the flavor of PDF,
+ or other options, we may want to actually have all
+ the colors defined by ICC profiles and not do the following
+ substituion of the Device space. */
+ if (csi == gs_color_space_index_ICC && !keepICC) {
+ csi = gsicc_get_default_type(pcs->cmm_icc_profile_data);
+ }
+ if (ppranges)
+ *ppranges = 0; /* default */
+ switch (csi) {
+ case gs_color_space_index_DeviceGray:
+ cos_c_string_value(pvalue, pcsn->DeviceGray);
+ return 0;
+ case gs_color_space_index_DeviceRGB:
+ cos_c_string_value(pvalue, pcsn->DeviceRGB);
+ return 0;
+ case gs_color_space_index_DeviceCMYK:
+ cos_c_string_value(pvalue, pcsn->DeviceCMYK);
+ return 0;
+ case gs_color_space_index_Pattern:
+ if (!pcs->params.pattern.has_base_space) {
+ cos_c_string_value(pvalue, "/Pattern");
+ return 0;
+ }
+ break;
+ case gs_color_space_index_ICC:
+ /*
+ * Take a special early exit for unrecognized ICCBased color spaces,
+ * or for PDF 1.2 output (ICCBased color spaces date from PDF 1.3).
+ */
+
+ if (pcs->cmm_icc_profile_data == NULL ||
+ pdev->CompatibilityLevel < 1.3
+ ) {
+ if (res_name != NULL)
+ return 0; /* Ignore .includecolorspace */
+ if (pcs->base_space != NULL) {
+ return pdf_color_space_named( pdev, pis, pvalue, ppranges,
+ pcs->base_space,
+ pcsn, by_name, NULL, 0, keepICC);
+ } else {
+ switch( cs_num_components(pcs) ) {
+ case 1:
+ cos_c_string_value(pvalue, pcsn->DeviceGray);
+ return 0;
+ case 3:
+ cos_c_string_value(pvalue, pcsn->DeviceRGB);
+ return 0;
+ case 4:
+ cos_c_string_value(pvalue, pcsn->DeviceCMYK);
+ return 0;
+ default:
+ break;
+ }
+ }
+ }
+
+ break;
+ default:
+ break;
+ }
+ if (pdev->UseOldColor) {
+ if (pdev->params.ColorConversionStrategy == ccs_CMYK &&
+ csi != gs_color_space_index_DeviceCMYK &&
+ csi != gs_color_space_index_DeviceGray &&
+ csi != gs_color_space_index_Pattern) {
+ emprintf(pdev->memory,
+ "\nUnable to convert color space to CMYK, reverting strategy to LeaveColorUnchanged.\n");
+ pdev->params.ColorConversionStrategy = ccs_LeaveColorUnchanged;
+ }
+ if (pdev->params.ColorConversionStrategy == ccs_sRGB &&
+ csi != gs_color_space_index_DeviceRGB &&
+ csi != gs_color_space_index_DeviceGray &&
+ csi != gs_color_space_index_Pattern) {
+ emprintf(pdev->memory,
+ "\nUnable to convert color space to sRGB, reverting strategy to LeaveColorUnchanged.\n");
+ pdev->params.ColorConversionStrategy = ccs_LeaveColorUnchanged;
+ }
+ if (pdev->params.ColorConversionStrategy == ccs_Gray &&
+ csi != gs_color_space_index_DeviceGray &&
+ csi != gs_color_space_index_Pattern) {
+ emprintf(pdev->memory,
+ "\nUnable to convert color space to Gray, reverting strategy to LeaveColorUnchanged.\n");
+ pdev->params.ColorConversionStrategy = ccs_LeaveColorUnchanged;
+ }
+ }
+ /* Check whether we already have a PDF object for this color space. */
+ if (pcs->id != gs_no_id)
+ pres = pdf_find_resource_by_gs_id(pdev, resourceColorSpace, pcs->id);
+ if (pres == NULL) {
+ stream s;
+
+ s_init(&s, pdev->memory);
+ swrite_position_only(&s);
+ code = cs_serialize(pcs, &s);
+ if (code < 0)
+ return_error(gs_error_unregistered); /* Must not happen. */
+ serialized_size = stell(&s);
+ sclose(&s);
+ if (serialized_size <= sizeof(serialized0))
+ serialized = serialized0;
+ else {
+ serialized = gs_alloc_bytes(pdev->pdf_memory, serialized_size, "pdf_color_space");
+ if (serialized == NULL)
+ return_error(gs_error_VMerror);
+ }
+ swrite_string(&s, serialized, serialized_size);
+ code = cs_serialize(pcs, &s);
+ if (code < 0)
+ return_error(gs_error_unregistered); /* Must not happen. */
+ if (stell(&s) != serialized_size)
+ return_error(gs_error_unregistered); /* Must not happen. */
+ sclose(&s);
+ pres = pdf_find_cspace_resource(pdev, serialized, serialized_size);
+ if (pres != NULL) {
+ if (serialized != serialized0)
+ gs_free_object(pdev->pdf_memory, serialized, "pdf_color_space");
+ serialized = NULL;
+ }
+ }
+ if (pres) {
+ const pdf_color_space_t *const ppcs =
+ (const pdf_color_space_t *)pres;
+
+ if (ppranges != 0 && ppcs->ranges != 0)
+ *ppranges = ppcs->ranges;
+ pca = (cos_array_t *)pres->object;
+ goto ret;
+ }
+
+ /* Space has parameters -- create an array. */
+ pca = cos_array_alloc(pdev, "pdf_color_space");
+ if (pca == 0)
+ return_error(gs_error_VMerror);
+
+ switch (csi) {
+
+ case gs_color_space_index_ICC:
+ code = pdf_iccbased_color_space(pdev, pis, pvalue, pcs, pca);
+ break;
+
+ case gs_color_space_index_CIEA: {
+ /* Check that we can represent this as a CalGray space. */
+ const gs_cie_a *pcie = pcs->params.a;
+ bool unitary = cie_ranges_are_0_1(&pcie->RangeA, 1);
+ bool identityA = (pcie->MatrixA.u == 1 && pcie->MatrixA.v == 1 &&
+ pcie->MatrixA.w == 1);
+ gs_vector3 expts;
+
+ pciec = (const gs_cie_common *)pcie;
+ if (!pcie->common.MatrixLMN.is_identity) {
+ if (!pdev->UseOldColor && !pdev->ForOPDFRead) {
+ if (pcs->icc_equivalent == 0) {
+ code = gs_colorspace_set_icc_equivalent((gs_color_space *)pcs, &is_lab, pdev->memory);
+ if (code < 0)
+ return code;
+ }
+ code = pdf_iccbased_color_space(pdev, pis, pvalue, pcs->icc_equivalent, pca);
+ if (pcs->params.a->RangeA.rmin < 0.0 || pcs->params.a->RangeA.rmax > 1.0)
+ ranges = &pcs->params.a->RangeA;
+ } else {
+ code = pdf_convert_cie_space(pdev, pca, pcs, "GRAY", pciec,
+ &pcie->RangeA, ONE_STEP_NOT, NULL,
+ &ranges);
+ }
+ break;
+ }
+ if (unitary && identityA &&
+ CIE_CACHE_IS_IDENTITY(&pcie->caches.DecodeA) &&
+ CIE_SCALAR3_CACHE_IS_EXPONENTIAL(pcie->common.caches.DecodeLMN, expts) &&
+ expts.v == expts.u && expts.w == expts.u
+ ) {
+ DO_NOTHING;
+ } else if (unitary && identityA &&
+ CIE_CACHE3_IS_IDENTITY(pcie->common.caches.DecodeLMN) &&
+ cie_vector_cache_is_exponential(&pcie->caches.DecodeA, &expts.u)
+ ) {
+ DO_NOTHING;
+ } else {
+ if (!pdev->UseOldColor && !pdev->ForOPDFRead) {
+ if (pcs->icc_equivalent == 0) {
+ code = gs_colorspace_set_icc_equivalent((gs_color_space *)pcs, &is_lab, pdev->memory);
+ if (code < 0)
+ return code;
+ }
+ code = pdf_iccbased_color_space(pdev, pis, pvalue, pcs->icc_equivalent, pca);
+ if (pcs->params.a->RangeA.rmin < 0.0 || pcs->params.a->RangeA.rmax > 1.0)
+ ranges = &pcs->params.a->RangeA;
+ } else {
+ code = pdf_convert_cie_space(pdev, pca, pcs, "GRAY", pciec,
+ &pcie->RangeA, ONE_STEP_NOT, NULL,
+ &ranges);
+ }
+ break;
+ }
+ code = cos_array_add(pca, cos_c_string_value(&v, "/CalGray"));
+ if (code < 0)
+ return code;
+ pcd = cos_dict_alloc(pdev, "pdf_color_space(dict)");
+ if (pcd == 0)
+ return_error(gs_error_VMerror);
+ if (expts.u != 1) {
+ code = cos_dict_put_c_key_real(pcd, "/Gamma", expts.u);
+ if (code < 0)
+ return code;
+ }
+ }
+ cal:
+ /* Finish handling a CIE-based color space (Calxxx or Lab). */
+ if (code < 0)
+ return code;
+ code = pdf_finish_cie_space(pdev, pca, pcd, pciec);
+ break;
+
+ case gs_color_space_index_CIEABC: {
+ /* Check that we can represent this as a CalRGB space. */
+ const gs_cie_abc *pcie = pcs->params.abc;
+ bool unitary = cie_ranges_are_0_1(pcie->RangeABC.ranges, 3);
+ gs_vector3 expts;
+ const gs_matrix3 *pmat = NULL;
+ cie_cache_one_step_t one_step =
+ cie_cached_abc_is_one_step(pcie, &pmat);
+
+ pciec = (const gs_cie_common *)pcie;
+ if (unitary) {
+ switch (one_step) {
+ case ONE_STEP_ABC:
+ if (CIE_VECTOR3_CACHE_IS_EXPONENTIAL(pcie->caches.DecodeABC.caches, expts))
+ goto calrgb;
+ break;
+ case ONE_STEP_LMN:
+ if (CIE_SCALAR3_CACHE_IS_EXPONENTIAL(pcie->common.caches.DecodeLMN, expts))
+ goto calrgb;
+ default:
+ break;
+ }
+ }
+ if (cie_is_lab(pcie)) {
+ /* Represent this as a Lab space. */
+ pcd = cos_dict_alloc(pdev, "pdf_color_space(dict)");
+ if (pcd == 0)
+ return_error(gs_error_VMerror);
+ code = pdf_put_lab_color_space(pdev, pca, pcd, pcie->RangeABC.ranges);
+ goto cal;
+ } else {
+ if (!pdev->UseOldColor && !pdev->ForOPDFRead) {
+ int i;
+
+ if (pcs->icc_equivalent == 0) {
+ code = gs_colorspace_set_icc_equivalent((gs_color_space *)pcs, &is_lab, pdev->memory);
+ if (code < 0)
+ return code;
+ }
+ code = pdf_iccbased_color_space(pdev, pis, pvalue, pcs->icc_equivalent, pca);
+ for (i = 0; i < 3; ++i) {
+ double rmin = pcs->params.abc->RangeABC.ranges[i].rmin, rmax = pcs->params.abc->RangeABC.ranges[i].rmax;
+
+ if (rmin < 0.0 || rmax > 1.0)
+ ranges = pcs->params.abc->RangeABC.ranges;
+ }
+ } else {
+ code = pdf_convert_cie_space(pdev, pca, pcs, "RGB ", pciec,
+ pcie->RangeABC.ranges,
+ one_step, pmat, &ranges);
+ }
+ break;
+ }
+ calrgb:
+ code = cos_array_add(pca, cos_c_string_value(&v, "/CalRGB"));
+ if (code < 0)
+ return code;
+ pcd = cos_dict_alloc(pdev, "pdf_color_space(dict)");
+ if (pcd == 0)
+ return_error(gs_error_VMerror);
+ if (expts.u != 1 || expts.v != 1 || expts.w != 1) {
+ code = cos_dict_put_c_key_vector3(pdev, pcd, "/Gamma", &expts);
+ if (code < 0)
+ return code;
+ }
+ if (!pmat->is_identity) {
+ cos_array_t *pcma =
+ cos_array_alloc(pdev, "pdf_color_space(Matrix)");
+
+ if (pcma == 0)
+ return_error(gs_error_VMerror);
+ if ((code = cos_array_add_vector3(pcma, &pmat->cu)) < 0 ||
+ (code = cos_array_add_vector3(pcma, &pmat->cv)) < 0 ||
+ (code = cos_array_add_vector3(pcma, &pmat->cw)) < 0 ||
+ (code = cos_dict_put(pcd, (const byte *)"/Matrix", 7,
+ COS_OBJECT_VALUE(&v, pcma))) < 0
+ )
+ return code;
+ }
+ }
+ goto cal;
+
+ case gs_color_space_index_CIEDEF:
+ if (!pdev->UseOldColor && !pdev->ForOPDFRead) {
+ int i;
+ if (pcs->icc_equivalent == 0) {
+ code = gs_colorspace_set_icc_equivalent((gs_color_space *)pcs, &is_lab, pdev->memory);
+ if (code < 0)
+ return code;
+ }
+ code = pdf_iccbased_color_space(pdev, pis, pvalue, pcs->icc_equivalent, pca);
+ for (i = 0; i < 3; ++i) {
+ double rmin = pcs->params.def->RangeDEF.ranges[i].rmin, rmax = pcs->params.def->RangeDEF.ranges[i].rmax;
+
+ if (rmin < 0.0 || rmax > 1.0)
+ ranges = pcs->params.def->RangeDEF.ranges;
+ }
+ } else {
+ code = pdf_convert_cie_space(pdev, pca, pcs, "RGB ",
+ (const gs_cie_common *)pcs->params.def,
+ pcs->params.def->RangeDEF.ranges,
+ ONE_STEP_NOT, NULL, &ranges);
+ }
+ break;
+
+ case gs_color_space_index_CIEDEFG:
+ if (!pdev->UseOldColor && !pdev->ForOPDFRead) {
+ int i;
+ if (pcs->icc_equivalent == 0) {
+ code = gs_colorspace_set_icc_equivalent((gs_color_space *)pcs, &is_lab, pdev->memory);
+ if (code < 0)
+ return code;
+ }
+ code = pdf_iccbased_color_space(pdev, pis, pvalue, pcs->icc_equivalent, pca);
+ for (i = 0; i < 4; ++i) {
+ double rmin = pcs->params.defg->RangeDEFG.ranges[i].rmin, rmax = pcs->params.defg->RangeDEFG.ranges[i].rmax;
+
+ if (rmin < 0.0 || rmax > 1.0)
+ ranges = pcs->params.defg->RangeDEFG.ranges;
+ }
+ } else {
+ code = pdf_convert_cie_space(pdev, pca, pcs, "CMYK",
+ (const gs_cie_common *)pcs->params.defg,
+ pcs->params.defg->RangeDEFG.ranges,
+ ONE_STEP_NOT, NULL, &ranges);
+ }
+ break;
+
+ case gs_color_space_index_Indexed:
+ code = pdf_indexed_color_space(pdev, pis, pvalue, pcs, pca, NULL);
+ break;
+
+ case gs_color_space_index_DeviceN:
+ if (!pdev->PreserveDeviceN)
+ return_error(gs_error_rangecheck);
+ if (pdev->CompatibilityLevel < 1.3)
+ return_error(gs_error_rangecheck);
+ pfn = gs_cspace_get_devn_function(pcs);
+ /****** CURRENTLY WE ONLY HANDLE Functions ******/
+ if (pfn == 0)
+ return_error(gs_error_rangecheck);
+ {
+ cos_array_t *psna =
+ cos_array_alloc(pdev, "pdf_color_space(DeviceN)");
+ int i;
+ byte *name_string;
+ uint name_string_length;
+ cos_value_t v_attriburtes, *va = NULL;
+
+ if (psna == 0)
+ return_error(gs_error_VMerror);
+ for (i = 0; i < pcs->params.device_n.num_components; ++i) {
+ if ((code = pcs->params.device_n.get_colorname_string(
+ pdev->memory,
+ pcs->params.device_n.names[i], &name_string,
+ &name_string_length)) < 0 ||
+ (code = pdf_string_to_cos_name(pdev, name_string,
+ name_string_length, &v)) < 0 ||
+ (code = cos_array_add_no_copy(psna, &v)) < 0)
+ return code;
+ }
+ COS_OBJECT_VALUE(&v, psna);
+ if (pcs->params.device_n.colorants != NULL) {
+ cos_dict_t *colorants = cos_dict_alloc(pdev, "pdf_color_space(DeviceN)");
+ cos_value_t v_colorants, v_separation, v_colorant_name;
+ const gs_device_n_attributes *csa;
+ pdf_resource_t *pres_attributes;
+
+ if (colorants == NULL)
+ return_error(gs_error_VMerror);
+ code = pdf_alloc_resource(pdev, resourceOther, 0, &pres_attributes, -1);
+ if (code < 0)
+ return code;
+ cos_become(pres_attributes->object, cos_type_dict);
+ COS_OBJECT_VALUE(&v_colorants, colorants);
+ code = cos_dict_put((cos_dict_t *)pres_attributes->object,
+ (const byte *)"/Colorants", 10, &v_colorants);
+ if (code < 0)
+ return code;
+ for (csa = pcs->params.device_n.colorants; csa != NULL; csa = csa->next) {
+ code = pcs->params.device_n.get_colorname_string(pdev->memory,
+ csa->colorant_name, &name_string, &name_string_length);
+ if (code < 0)
+ return code;
+ code = pdf_color_space_named(pdev, pis, &v_separation, NULL, csa->cspace, pcsn, false, NULL, 0, keepICC);
+ if (code < 0)
+ return code;
+ code = pdf_string_to_cos_name(pdev, name_string, name_string_length, &v_colorant_name);
+ if (code < 0)
+ return code;
+ code = cos_dict_put(colorants, v_colorant_name.contents.chars.data,
+ v_colorant_name.contents.chars.size, &v_separation);
+ if (code < 0)
+ return code;
+ }
+ code = pdf_substitute_resource(pdev, &pres_attributes, resourceOther, NULL, true);
+ if (code < 0)
+ return code;
+ pres_attributes->where_used |= pdev->used_mask;
+ va = &v_attriburtes;
+ COS_OBJECT_VALUE(va, pres_attributes->object);
+ }
+ if ((code = pdf_separation_color_space(pdev, pis, pca, "/DeviceN", &v,
+ pcs->base_space,
+ pfn, &pdf_color_space_names, va)) < 0)
+ return code;
+ }
+ break;
+
+ case gs_color_space_index_Separation:
+ if (!pdev->PreserveSeparation)
+ return_error(gs_error_rangecheck);
+ pfn = gs_cspace_get_sepr_function(pcs);
+ /****** CURRENTLY WE ONLY HANDLE Functions ******/
+ if (pfn == 0)
+ return_error(gs_error_rangecheck);
+ {
+ byte *name_string;
+ uint name_string_length;
+ if ((code = pcs->params.separation.get_colorname_string(
+ pdev->memory,
+ pcs->params.separation.sep_name, &name_string,
+ &name_string_length)) < 0 ||
+ (code = pdf_string_to_cos_name(pdev, name_string,
+ name_string_length, &v)) < 0 ||
+ (code = pdf_separation_color_space(pdev, pis, pca, "/Separation", &v,
+ pcs->base_space,
+ pfn, &pdf_color_space_names, NULL)) < 0)
+ return code;
+ }
+ break;
+
+ case gs_color_space_index_Pattern:
+ if ((code = pdf_color_space_named(pdev, pis, pvalue, ppranges,
+ pcs->base_space,
+ &pdf_color_space_names, false, NULL, 0, false)) < 0 ||
+ (code = cos_array_add(pca,
+ cos_c_string_value(&v, "/Pattern"))) < 0 ||
+ (code = cos_array_add(pca, pvalue)) < 0
+ )
+ return code;
+ break;
+
+ default:
+ return_error(gs_error_rangecheck);
+ }
+
+ /*
+ * Register the color space as a resource, since it must be referenced
+ * by name rather than directly.
+ */
+ {
+ pdf_color_space_t *ppcs;
+
+ if (code < 0 ||
+ (code = pdf_alloc_resource(pdev, resourceColorSpace, pcs->id,
+ &pres, -1)) < 0
+ ) {
+ COS_FREE(pca, "pdf_color_space");
+ return code;
+ }
+ pdf_reserve_object_id(pdev, pres, 0);
+ if (res_name != NULL) {
+ int l = min(name_length, sizeof(pres->rname) - 1);
+
+ memcpy(pres->rname, res_name, l);
+ pres->rname[l] = 0;
+ }
+ ppcs = (pdf_color_space_t *)pres;
+ if (serialized == serialized0) {
+ serialized = gs_alloc_bytes(pdev->pdf_memory, serialized_size, "pdf_color_space");
+ if (serialized == NULL)
+ return_error(gs_error_VMerror);
+ memcpy(serialized, serialized0, serialized_size);
+ }
+ ppcs->serialized = serialized;
+ ppcs->serialized_size = serialized_size;
+ if (ranges) {
+ int num_comp = gs_color_space_num_components(pcs);
+ gs_range_t *copy_ranges = (gs_range_t *)
+ gs_alloc_byte_array(pdev->pdf_memory, num_comp,
+ sizeof(gs_range_t), "pdf_color_space");
+
+ if (copy_ranges == 0) {
+ COS_FREE(pca, "pdf_color_space");
+ return_error(gs_error_VMerror);
+ }
+ memcpy(copy_ranges, ranges, num_comp * sizeof(gs_range_t));
+ ppcs->ranges = copy_ranges;
+ if (ppranges)
+ *ppranges = copy_ranges;
+ } else
+ ppcs->ranges = 0;
+ pca->id = pres->object->id;
+ COS_FREE(pres->object, "pdf_color_space");
+ pres->object = (cos_object_t *)pca;
+ cos_write_object(COS_OBJECT(pca), pdev, resourceColorSpace);
+ }
+ ret:
+ if (by_name) {
+ /* Return a resource name rather than an object reference. */
+ discard(COS_RESOURCE_VALUE(pvalue, pca));
+ } else
+ discard(COS_OBJECT_VALUE(pvalue, pca));
+ if (pres != NULL) {
+ pres->where_used |= pdev->used_mask;
+ code = pdf_add_resource(pdev, pdev->substream_Resources, "/ColorSpace", pres);
+ if (code < 0)
+ return code;
+ }
+ return 0;
+}
+
+int free_color_space(gx_device_pdf *pdev, pdf_resource_t *pres)
+{
+ pdf_color_space_t *ppcs = (pdf_color_space_t *)pres;
+
+ if (ppcs->serialized)
+ gs_free_object(pdev->pdf_memory, ppcs->serialized, "free serialized colour space");
+ if (pres->object) {
+ cos_release(pres->object, "release ColorSpace object");
+ gs_free_object(pdev->pdf_memory, pres->object, "free ColorSpace object");
+ pres->object = 0;
+ }
+ return 0;
+}
+
+/* ---------------- Miscellaneous ---------------- */
+
+/* Create colored and uncolored Pattern color spaces. */
+static int
+pdf_pattern_space(gx_device_pdf *pdev, cos_value_t *pvalue,
+ pdf_resource_t **ppres, const char *cs_name)
+{
+ int code;
+
+ if (!*ppres) {
+ int code = pdf_begin_resource_body(pdev, resourceColorSpace, gs_no_id,
+ ppres);
+
+ if (code < 0)
+ return code;
+ pprints1(pdev->strm, "%s\n", cs_name);
+ pdf_end_resource(pdev, resourceColorSpace);
+ (*ppres)->object->written = true; /* don't write at end */
+ ((pdf_color_space_t *)*ppres)->ranges = 0;
+ ((pdf_color_space_t *)*ppres)->serialized = 0;
+ }
+ code = pdf_add_resource(pdev, pdev->substream_Resources, "/ColorSpace", *ppres);
+ if (code < 0)
+ return code;
+ cos_resource_value(pvalue, (*ppres)->object);
+ return 0;
+}
+int
+pdf_cs_Pattern_colored(gx_device_pdf *pdev, cos_value_t *pvalue)
+{
+ return pdf_pattern_space(pdev, pvalue, &pdev->cs_Patterns[0],
+ "[/Pattern]");
+}
+int
+pdf_cs_Pattern_uncolored(gx_device_pdf *pdev, cos_value_t *pvalue)
+{
+ /* Only for process colors. */
+ int ncomp = pdev->color_info.num_components;
+ static const char *const pcs_names[5] = {
+ 0, "[/Pattern /DeviceGray]", 0, "[/Pattern /DeviceRGB]",
+ "[/Pattern /DeviceCMYK]"
+ };
+
+ return pdf_pattern_space(pdev, pvalue, &pdev->cs_Patterns[ncomp],
+ pcs_names[ncomp]);
+}
+int
+pdf_cs_Pattern_uncolored_hl(gx_device_pdf *pdev,
+ const gs_color_space *pcs, cos_value_t *pvalue, const gs_imager_state * pis)
+{
+ /* Only for high level colors. */
+ return pdf_color_space_named(pdev, pis, pvalue, NULL, pcs, &pdf_color_space_names, true, NULL, 0, false);
+}
+
+/* Set the ProcSets bits corresponding to an image color space. */
+void
+pdf_color_space_procsets(gx_device_pdf *pdev, const gs_color_space *pcs)
+{
+ const gs_color_space *pbcs = pcs;
+
+ csw:
+ switch (gs_color_space_get_index(pbcs)) {
+ case gs_color_space_index_DeviceGray:
+ case gs_color_space_index_CIEA:
+ /* We only handle CIEBasedA spaces that map to CalGray. */
+ pdev->procsets |= ImageB;
+ break;
+ case gs_color_space_index_Indexed:
+ pdev->procsets |= ImageI;
+ pbcs = pcs->base_space;
+ goto csw;
+ default:
+ pdev->procsets |= ImageC;
+ break;
+ }
+}
diff --git a/devices/vector/gdevpdfc.h b/devices/vector/gdevpdfc.h
new file mode 100644
index 000000000..f75409c21
--- /dev/null
+++ b/devices/vector/gdevpdfc.h
@@ -0,0 +1,66 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Internal color space writing interfaces for pdfwrite driver. */
+
+#ifndef gdevpdfc_INCLUDED
+# define gdevpdfc_INCLUDED
+
+int pdf_delete_sampled_base_space_function(gx_device_pdf *pdev, gs_function_t *pfn);
+int pdf_delete_base_space_function(gx_device_pdf *pdev, gs_function_t *pfn);
+int pdf_make_sampled_base_space_function(gx_device_pdf *pdev, gs_function_t **pfn, int nSrcComp, int nDstComp, byte *data);
+int pdf_make_base_space_function(gx_device_pdf *pdev, gs_function_t **pfn, int ncomp, float *data_low, float *data_high);
+
+/* ------ Exported by gdevpdfc.c for gdevpdfk.c ------ */
+
+/* Define the special cases for CIEBased spaces. */
+typedef enum {
+ ONE_STEP_NOT, /* not one-step */
+ ONE_STEP_LMN, /* DecodeLMN (scalar cache) + matrix */
+ ONE_STEP_ABC /* DecodeABC (vector cache) + matrix */
+} cie_cache_one_step_t;
+
+/*
+ * Finish creating a CIE-based color space (Calxxx or Lab.)
+ */
+int pdf_finish_cie_space(gx_device_pdf *pdev, cos_array_t *pca, cos_dict_t *pcd,
+ const gs_cie_common *pciec);
+
+/* ------ Exported by gdevpdfk.c for gdevpdfc.c ------ */
+
+/*
+ * Create an ICCBased color space. This is a single-use procedure,
+ * broken out only for readability.
+ */
+int pdf_iccbased_color_space(gx_device_pdf *pdev, const gs_imager_state * pis, cos_value_t *pvalue,
+ const gs_color_space *pcs, cos_array_t *pca);
+
+/*
+ * Convert a CIEBased space to Lab or ICCBased.
+ */
+int pdf_convert_cie_space(gx_device_pdf *pdev, cos_array_t *pca,
+ const gs_color_space *pcs, const char *dcsname,
+ const gs_cie_common *pciec, const gs_range *prange,
+ cie_cache_one_step_t one_step,
+ const gs_matrix3 *pmat, const gs_range_t **pprange);
+
+/*
+ * Create a Lab color space object.
+ */
+int pdf_put_lab_color_space(gx_device_pdf *pdev, cos_array_t *pca, cos_dict_t *pcd,
+ const gs_range ranges[3] /* only [1] and [2] used */);
+
+#endif /* gdevpdfc_INCLUDED */
diff --git a/devices/vector/gdevpdfd.c b/devices/vector/gdevpdfd.c
new file mode 100644
index 000000000..2f17411a2
--- /dev/null
+++ b/devices/vector/gdevpdfd.c
@@ -0,0 +1,1552 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Path drawing procedures for pdfwrite driver */
+#include "math_.h"
+#include "memory_.h"
+#include "gx.h"
+#include "gxdevice.h"
+#include "gxfixed.h"
+#include "gxistate.h"
+#include "gxpaint.h"
+#include "gxcoord.h"
+#include "gxdevmem.h"
+#include "gxcolor2.h"
+#include "gxhldevc.h"
+#include "gsstate.h"
+#include "gxstate.h"
+#include "gserrors.h"
+#include "gsptype2.h"
+#include "gsshade.h"
+#include "gzpath.h"
+#include "gzcpath.h"
+#include "gdevpdfx.h"
+#include "gdevpdfg.h"
+#include "gdevpdfo.h"
+#include "gsutil.h"
+#include "gdevpdtf.h"
+#include "gdevpdts.h"
+#include "gxdevsop.h"
+
+/* ---------------- Drawing ---------------- */
+
+/* Fill a rectangle. */
+int
+gdev_pdf_fill_rectangle(gx_device * dev, int x, int y, int w, int h,
+ gx_color_index color)
+{
+ gx_device_pdf *pdev = (gx_device_pdf *) dev;
+ int code;
+
+ if (pdev->Eps2Write) {
+ float x0, y0, x1, y1;
+ gs_rect *Box;
+
+ if (!pdev->accumulating_charproc) {
+ Box = &pdev->BBox;
+ x0 = x / (pdev->HWResolution[0] / 72.0);
+ y0 = y / (pdev->HWResolution[1] / 72.0);
+ x1 = x0 + (w / (pdev->HWResolution[0] / 72.0));
+ y1 = y0 + (h / (pdev->HWResolution[1] / 72.0));
+ }
+ else {
+ Box = &pdev->charproc_BBox;
+ x0 = (float)x / 100;
+ y0 = (float)y / 100;
+ x1 = x0 + (w / 100);
+ y1 = y0 + (h / 100);
+ }
+
+ if (Box->p.x > x0)
+ Box->p.x = x0;
+ if (Box->p.y > y0)
+ Box->p.y = y0;
+ if (Box->q.x < x1)
+ Box->q.x = x1;
+ if (Box->q.y < y1)
+ Box->q.y = y1;
+ if (pdev->AccumulatingBBox)
+ return 0;
+ }
+ code = pdf_open_page(pdev, PDF_IN_STREAM);
+ if (code < 0)
+ return code;
+ /* Make sure we aren't being clipped. */
+ code = pdf_put_clip_path(pdev, NULL);
+ if (code < 0)
+ return code;
+ pdf_set_pure_color(pdev, color, &pdev->saved_fill_color,
+ &pdev->fill_used_process_color,
+ &psdf_set_fill_color_commands);
+ if (!pdev->HaveStrokeColor)
+ pdev->saved_stroke_color = pdev->saved_fill_color;
+ pprintd4(pdev->strm, "%d %d %d %d re f\n", x, y, w, h);
+ return 0;
+}
+
+/* ---------------- Path drawing ---------------- */
+
+/* ------ Vector device implementation ------ */
+
+static int
+pdf_setlinewidth(gx_device_vector * vdev, double width)
+{
+ /* Acrobat Reader doesn't accept negative line widths. */
+ return psdf_setlinewidth(vdev, fabs(width));
+}
+
+static int
+pdf_can_handle_hl_color(gx_device_vector * vdev, const gs_imager_state * pis,
+ const gx_drawing_color * pdc)
+{
+ return pis != NULL;
+}
+
+static int
+pdf_setfillcolor(gx_device_vector * vdev, const gs_imager_state * pis,
+ const gx_drawing_color * pdc)
+{
+ gx_device_pdf *const pdev = (gx_device_pdf *)vdev;
+ bool hl_color = (*vdev_proc(vdev, can_handle_hl_color)) (vdev, pis, pdc);
+ const gs_imager_state *pis_for_hl_color = (hl_color ? pis : NULL);
+
+ if (!pdev->HaveStrokeColor) {
+ /* opdfread.ps assumes same color for stroking and non-stroking operations. */
+ int code = pdf_set_drawing_color(pdev, pis_for_hl_color, pdc, &pdev->saved_stroke_color,
+ &pdev->stroke_used_process_color,
+ &psdf_set_stroke_color_commands);
+ if (code < 0)
+ return code;
+ }
+ return pdf_set_drawing_color(pdev, pis_for_hl_color, pdc, &pdev->saved_fill_color,
+ &pdev->fill_used_process_color,
+ &psdf_set_fill_color_commands);
+}
+
+static int
+pdf_setstrokecolor(gx_device_vector * vdev, const gs_imager_state * pis,
+ const gx_drawing_color * pdc)
+{
+ gx_device_pdf *const pdev = (gx_device_pdf *)vdev;
+ bool hl_color = (*vdev_proc(vdev, can_handle_hl_color)) (vdev, pis, pdc);
+ const gs_imager_state *pis_for_hl_color = (hl_color ? pis : NULL);
+
+ if (!pdev->HaveStrokeColor) {
+ /* opdfread.ps assumes same color for stroking and non-stroking operations. */
+ int code = pdf_set_drawing_color(pdev, pis_for_hl_color, pdc, &pdev->saved_fill_color,
+ &pdev->fill_used_process_color,
+ &psdf_set_fill_color_commands);
+ if (code < 0)
+ return code;
+ }
+ return pdf_set_drawing_color(pdev, pis_for_hl_color, pdc, &pdev->saved_stroke_color,
+ &pdev->stroke_used_process_color,
+ &psdf_set_stroke_color_commands);
+}
+
+static int
+pdf_dorect(gx_device_vector * vdev, fixed x0, fixed y0, fixed x1, fixed y1,
+ gx_path_type_t type)
+{
+ gx_device_pdf *pdev = (gx_device_pdf *)vdev;
+ fixed xmax = int2fixed(32766), ymax = int2fixed(32766);
+ int bottom = (pdev->ResourcesBeforeUsage ? 1 : 0);
+ fixed xmin = (pdev->sbstack_depth > bottom ? -xmax : 0);
+ fixed ymin = (pdev->sbstack_depth > bottom ? -ymax : 0);
+
+ /*
+ * If we're doing a stroke operation, expand the checking box by the
+ * stroke width.
+ */
+ if (type & gx_path_type_stroke) {
+ double w = vdev->state.line_params.half_width;
+ double xw = w * (fabs(vdev->state.ctm.xx) + fabs(vdev->state.ctm.yx));
+ int d = float2fixed(xw) + fixed_1;
+
+ xmin -= d;
+ xmax += d;
+ ymin -= d;
+ ymax += d;
+ }
+ if (!(type & gx_path_type_clip) &&
+ (x0 > xmax || x1 < xmin || y0 > ymax || y1 < ymin ||
+ x0 > x1 || y0 > y1)
+ )
+ return 0; /* nothing to fill or stroke */
+ /*
+ * Clamp coordinates to avoid tripping over Acrobat Reader's limit
+ * of 32K on user coordinate values.
+ */
+ if (x0 < xmin)
+ x0 = xmin;
+ if (x1 > xmax)
+ x1 = xmax;
+ if (y0 < ymin)
+ y0 = ymin;
+ if (y1 > ymax)
+ y1 = ymax;
+ return psdf_dorect(vdev, x0, y0, x1, y1, type);
+}
+
+static int
+pdf_endpath(gx_device_vector * vdev, gx_path_type_t type)
+{
+ return 0; /* always handled by caller */
+}
+
+const gx_device_vector_procs pdf_vector_procs = {
+ /* Page management */
+ NULL,
+ /* Imager state */
+ pdf_setlinewidth,
+ psdf_setlinecap,
+ psdf_setlinejoin,
+ psdf_setmiterlimit,
+ psdf_setdash,
+ psdf_setflat,
+ psdf_setlogop,
+ /* Other state */
+ pdf_can_handle_hl_color,
+ pdf_setfillcolor,
+ pdf_setstrokecolor,
+ /* Paths */
+ psdf_dopath,
+ pdf_dorect,
+ psdf_beginpath,
+ psdf_moveto,
+ psdf_lineto,
+ psdf_curveto,
+ psdf_closepath,
+ pdf_endpath
+};
+
+/* ------ Utilities ------ */
+
+/* Store a copy of clipping path. */
+int
+pdf_remember_clip_path(gx_device_pdf * pdev, const gx_clip_path * pcpath)
+{
+ /* Used for skipping redundant clip paths. SF bug #624168. */
+ if (pdev->clip_path != 0) {
+ gx_path_free(pdev->clip_path, "pdf clip path");
+ }
+ if (pcpath == 0) {
+ pdev->clip_path = 0;
+ return 0;
+ }
+ pdev->clip_path = gx_path_alloc(pdev->pdf_memory, "pdf clip path");
+ if (pdev->clip_path == 0)
+ return_error(gs_error_VMerror);
+ return gx_cpath_to_path((gx_clip_path *)pcpath, pdev->clip_path);
+}
+
+/* Check if same clipping path. */
+static int
+pdf_is_same_clip_path(gx_device_pdf * pdev, const gx_clip_path * pcpath)
+{
+ /* Used for skipping redundant clip paths. SF bug #624168. */
+ gs_cpath_enum cenum;
+ gs_path_enum penum;
+ gs_fixed_point vs0[3], vs1[3];
+ int code, pe_op;
+
+ if ((pdev->clip_path != 0) != (pcpath != 0))
+ return 0;
+ /* Both clip paths are empty, so the same */
+ if (pdev->clip_path == 0)
+ return 1;
+ code = gx_path_enum_init(&penum, pdev->clip_path);
+ if (code < 0)
+ return code;
+ code = gx_cpath_enum_init(&cenum, (gx_clip_path *)pcpath);
+ if (code < 0)
+ return code;
+ /* This flags a warning in Coverity, uninitialised variable cenum.first_visit */
+ /* This is because gx_cpath_enum_init doesn't initialise first_visit, but the */
+ /* variable can be used in enum_next. However, this is not truly used this */
+ /* way. The enum_init sets the 'state' to 'scan', and the first thing that happens */
+ /* in enum_next when state is 'scan' is to set first_visit. */
+ while ((code = gx_cpath_enum_next(&cenum, vs0)) > 0) {
+ pe_op = gx_path_enum_next(&penum, vs1);
+ if (pe_op < 0)
+ return pe_op;
+ if (pe_op != code)
+ return 0;
+ switch (pe_op) {
+ case gs_pe_curveto:
+ if (vs0[1].x != vs1[1].x || vs0[1].y != vs1[1].y ||
+ vs0[2].x != vs1[2].x || vs0[2].y != vs1[2].y)
+ return 0;
+ case gs_pe_moveto:
+ case gs_pe_lineto:
+ case gs_pe_gapto:
+ if (vs0[0].x != vs1[0].x || vs0[0].y != vs1[0].y)
+ return 0;
+ }
+ }
+ if (code < 0)
+ return code;
+ code = gx_path_enum_next(&penum, vs1);
+ if (code < 0)
+ return code;
+ return (code == 0);
+}
+
+/* Test whether we will need to put the clipping path. */
+bool
+pdf_must_put_clip_path(gx_device_pdf * pdev, const gx_clip_path * pcpath)
+{
+ if (pcpath == NULL) {
+ if (pdev->clip_path_id == pdev->no_clip_path_id)
+ return false;
+ } else {
+ if (pdev->clip_path_id == pcpath->id)
+ return false;
+ if (gx_cpath_includes_rectangle(pcpath, fixed_0, fixed_0,
+ int2fixed(pdev->width),
+ int2fixed(pdev->height)))
+ if (pdev->clip_path_id == pdev->no_clip_path_id)
+ return false;
+ if (pdf_is_same_clip_path(pdev, pcpath) > 0) {
+ pdev->clip_path_id = pcpath->id;
+ return false;
+ }
+ }
+ return true;
+}
+
+/* Put a single element of a clipping path list. */
+static int
+pdf_put_clip_path_list_elem(gx_device_pdf * pdev, gx_cpath_path_list *e,
+ gs_path_enum *cenum, gdev_vector_dopath_state_t *state,
+ gs_fixed_point vs[3])
+{ /* This recursive function provides a reverse order of the list elements. */
+ int pe_op;
+
+ if (e->next != NULL) {
+ int code = pdf_put_clip_path_list_elem(pdev, e->next, cenum, state, vs);
+
+ if (code != 0)
+ return code;
+ }
+ gx_path_enum_init(cenum, &e->path);
+ while ((pe_op = gx_path_enum_next(cenum, vs)) > 0)
+ gdev_vector_dopath_segment(state, pe_op, vs);
+ pprints1(pdev->strm, "%s n\n", (e->rule <= 0 ? "W" : "W*"));
+ if (pe_op < 0)
+ return pe_op;
+ return 0;
+}
+
+/* Put a clipping path on the output file. */
+int
+pdf_put_clip_path(gx_device_pdf * pdev, const gx_clip_path * pcpath)
+{
+ int code;
+ stream *s = pdev->strm;
+ gs_id new_id;
+
+ /* Check for no update needed. */
+ if (pcpath == NULL) {
+ if (pdev->clip_path_id == pdev->no_clip_path_id)
+ return 0;
+ new_id = pdev->no_clip_path_id;
+ } else {
+ if (pdev->clip_path_id == pcpath->id)
+ return 0;
+ new_id = pcpath->id;
+ if (gx_cpath_includes_rectangle(pcpath, fixed_0, fixed_0,
+ int2fixed(pdev->width),
+ int2fixed(pdev->height))
+ ) {
+ if (pdev->clip_path_id == pdev->no_clip_path_id)
+ return 0;
+ new_id = pdev->no_clip_path_id;
+ }
+ code = pdf_is_same_clip_path(pdev, pcpath);
+ if (code < 0)
+ return code;
+ if (code) {
+ pdev->clip_path_id = new_id;
+ return 0;
+ }
+ }
+ /*
+ * The contents must be open already, so the following will only exit
+ * text or string context.
+ */
+ code = pdf_open_contents(pdev, PDF_IN_STREAM);
+ if (code < 0)
+ return code;
+ /* Use Q to unwind the old clipping path. */
+ if (pdev->vgstack_depth > pdev->vgstack_bottom) {
+ code = pdf_restore_viewer_state(pdev, s);
+ if (code < 0)
+ return code;
+ }
+ if (new_id != pdev->no_clip_path_id) {
+ gs_fixed_rect rect;
+
+ /* Use q to allow the new clipping path to unwind. */
+ code = pdf_save_viewer_state(pdev, s);
+ if (code < 0)
+ return code;
+ if (cpath_is_rectangle(pcpath, &rect)) {
+ /* Use unrounded coordinates. */
+ pprintg4(s, "%g %g %g %g re",
+ fixed2float(rect.p.x), fixed2float(rect.p.y),
+ fixed2float(rect.q.x - rect.p.x),
+ fixed2float(rect.q.y - rect.p.y));
+ pprints1(s, " %s n\n", (pcpath->rule <= 0 ? "W" : "W*"));
+ } else {
+ gdev_vector_dopath_state_t state;
+ gs_fixed_point vs[3];
+ int pe_op;
+
+ gdev_vector_dopath_init(&state, (gx_device_vector *)pdev,
+ gx_path_type_fill, NULL);
+ if (pcpath->path_list == NULL) {
+ /*
+ * We think this should be never executed.
+ * This obsolete branch writes a clip path intersection
+ * as a set of rectangles computed by
+ * gx_cpath_intersect_path_slow.
+ * Those rectangles use coordinates rounded to pixels,
+ * therefore the precision may be unsatisfactory -
+ * see Bug 688407.
+ */
+ gs_cpath_enum cenum;
+
+ /*
+ * We have to break 'const' here because the clip path
+ * enumeration logic uses some internal mark bits.
+ * This is very unfortunate, but until we can come up with
+ * a better algorithm, it's necessary.
+ */
+ gx_cpath_enum_init(&cenum, (gx_clip_path *) pcpath);
+ while ((pe_op = gx_cpath_enum_next(&cenum, vs)) > 0)
+ gdev_vector_dopath_segment(&state, pe_op, vs);
+ pprints1(s, "%s n\n", (pcpath->rule <= 0 ? "W" : "W*"));
+ if (pe_op < 0)
+ return pe_op;
+ } else {
+ gs_path_enum cenum;
+
+ code = pdf_put_clip_path_list_elem(pdev, pcpath->path_list, &cenum, &state, vs);
+ if (code < 0)
+ return code;
+ }
+ }
+ }
+ pdev->clip_path_id = new_id;
+ return pdf_remember_clip_path(pdev,
+ (pdev->clip_path_id == pdev->no_clip_path_id ? NULL : pcpath));
+}
+
+/*
+ * Compute the scaling to ensure that user coordinates for a path are within
+ * Acrobat's range. Return true if scaling was needed. In this case, the
+ * CTM will be multiplied by *pscale, and all coordinates will be divided by
+ * *pscale.
+ */
+static bool
+make_rect_scaling(const gx_device_pdf *pdev, const gs_fixed_rect *bbox,
+ double prescale, double *pscale)
+{
+ double bmin, bmax;
+
+ bmin = min(bbox->p.x / pdev->scale.x, bbox->p.y / pdev->scale.y) * prescale;
+ bmax = max(bbox->q.x / pdev->scale.x, bbox->q.y / pdev->scale.y) * prescale;
+ if (bmin <= int2fixed(-MAX_USER_COORD) ||
+ bmax > int2fixed(MAX_USER_COORD)
+ ) {
+ /* Rescale the path. */
+ *pscale = max(bmin / int2fixed(-MAX_USER_COORD),
+ bmax / int2fixed(MAX_USER_COORD));
+ return true;
+ } else {
+ *pscale = 1;
+ return false;
+ }
+}
+
+/*
+ * Prepare a fill with a color anc a clipping path.
+ * Return 1 if there is nothing to paint.
+ * Changes *box to the clipping box.
+ */
+static int
+prepare_fill_with_clip(gx_device_pdf *pdev, const gs_imager_state * pis,
+ gs_fixed_rect *box, bool have_path,
+ const gx_drawing_color * pdcolor, const gx_clip_path * pcpath)
+{
+ bool new_clip;
+ int code;
+
+ /*
+ * Check for an empty clipping path.
+ */
+ if (pcpath) {
+ gs_fixed_rect cbox;
+
+ gx_cpath_outer_box(pcpath, &cbox);
+ if (cbox.p.x >= cbox.q.x || cbox.p.y >= cbox.q.y)
+ return 1; /* empty clipping path */
+ *box = cbox;
+ }
+ new_clip = pdf_must_put_clip_path(pdev, pcpath);
+ if (have_path || pdev->context == PDF_IN_NONE || new_clip) {
+ if (new_clip)
+ code = pdf_unclip(pdev);
+ else
+ code = pdf_open_page(pdev, PDF_IN_STREAM);
+ if (code < 0)
+ return code;
+ }
+ code = pdf_prepare_fill(pdev, pis);
+ if (code < 0)
+ return code;
+ return pdf_put_clip_path(pdev, pcpath);
+}
+
+/* -------------A local image converter device. -----------------------------*/
+
+public_st_pdf_lcvd_t();
+
+static int
+lcvd_copy_color_shifted(gx_device * dev,
+ const byte * base, int sourcex, int sraster, gx_bitmap_id id,
+ int x, int y, int w, int h)
+{
+ pdf_lcvd_t *cvd = (pdf_lcvd_t *)dev;
+
+ return cvd->std_copy_color((gx_device *)&cvd->mdev, base, sourcex, sraster, id,
+ x - cvd->mdev.mapped_x, y - cvd->mdev.mapped_y, w, h);
+}
+
+static int
+lcvd_fill_rectangle_shifted(gx_device *dev, int x, int y, int width, int height, gx_color_index color)
+{
+ pdf_lcvd_t *cvd = (pdf_lcvd_t *)dev;
+
+ return cvd->std_fill_rectangle((gx_device *)&cvd->mdev,
+ x - cvd->mdev.mapped_x, y - cvd->mdev.mapped_y, width, height, color);
+}
+static int
+lcvd_fill_rectangle_shifted2(gx_device *dev, int x, int y, int width, int height, gx_color_index color)
+{
+ pdf_lcvd_t *cvd = (pdf_lcvd_t *)dev;
+ int code;
+
+ code = (*dev_proc(cvd->mask, fill_rectangle))((gx_device *)cvd->mask,
+ x - cvd->mdev.mapped_x, y - cvd->mdev.mapped_y, width, height, (gx_color_index)1);
+ if (code < 0)
+ return code;
+ return cvd->std_fill_rectangle((gx_device *)&cvd->mdev,
+ x - cvd->mdev.mapped_x, y - cvd->mdev.mapped_y, width, height, color);
+}
+static void
+lcvd_get_clipping_box_shifted_from_mdev(gx_device *dev, gs_fixed_rect *pbox)
+{
+ fixed ofs;
+ pdf_lcvd_t *cvd = (pdf_lcvd_t *)dev;
+
+ cvd->std_get_clipping_box((gx_device *)&cvd->mdev, pbox);
+ ofs = int2fixed(cvd->mdev.mapped_x);
+ pbox->p.x += ofs;
+ pbox->q.x += ofs;
+ ofs = int2fixed(cvd->mdev.mapped_y);
+ pbox->p.y += ofs;
+ pbox->q.y += ofs;
+}
+static int
+lcvd_dev_spec_op(gx_device *pdev1, int dev_spec_op,
+ void *data, int size)
+{
+ switch (dev_spec_op) {
+ case gxdso_pattern_shading_area:
+ return 1; /* Request shading area. */
+ case gxdso_pattern_can_accum:
+ case gxdso_pattern_start_accum:
+ case gxdso_pattern_finish_accum:
+ case gxdso_pattern_load:
+ case gxdso_pattern_is_cpath_accum:
+ case gxdso_pattern_shfill_doesnt_need_path:
+ case gxdso_pattern_handles_clip_path:
+ return 0;
+ }
+ return gx_default_dev_spec_op(pdev1, dev_spec_op, data, size);
+}
+static int
+lcvd_close_device_with_writing(gx_device *pdev)
+{
+ /* Assuming 'mdev' is being closed before 'mask' - see gx_image3_end_image. */
+ pdf_lcvd_t *cvd = (pdf_lcvd_t *)pdev;
+ int code, code1;
+
+ code = pdf_dump_converted_image(cvd->pdev, cvd);
+ code1 = cvd->std_close_device((gx_device *)&cvd->mdev);
+ return code < 0 ? code : code1;
+}
+
+static int
+write_image(gx_device_pdf *pdev, gx_device_memory *mdev, gs_matrix *m)
+{
+ gs_image_t image;
+ pdf_image_writer writer;
+ const int sourcex = 0;
+ int code;
+
+ if (m != NULL)
+ pdf_put_matrix(pdev, NULL, m, " cm\n");
+ code = pdf_copy_color_data(pdev, mdev->base, sourcex,
+ mdev->raster, gx_no_bitmap_id, 0, 0, mdev->width, mdev->height,
+ &image, &writer, 2);
+ if (code == 1)
+ code = 0; /* Empty image. */
+ else if (code == 0)
+ code = pdf_do_image(pdev, writer.pres, NULL, true);
+ return code;
+}
+static int
+write_mask(gx_device_pdf *pdev, gx_device_memory *mdev, gs_matrix *m)
+{
+ const int sourcex = 0;
+ gs_id save_clip_id = pdev->clip_path_id;
+ bool save_skip_color = pdev->skip_colors;
+ int code;
+
+ if (m != NULL)
+ pdf_put_matrix(pdev, NULL, m, " cm\n");
+ pdev->clip_path_id = pdev->no_clip_path_id;
+ pdev->skip_colors = true;
+ code = gdev_pdf_copy_mono((gx_device *)pdev, mdev->base, sourcex,
+ mdev->raster, gx_no_bitmap_id, 0, 0, mdev->width, mdev->height,
+ gx_no_color_index, (gx_color_index)0);
+ pdev->clip_path_id = save_clip_id;
+ pdev->skip_colors = save_skip_color;
+ return code;
+}
+
+static void
+max_subimage_width(int width, byte *base, int x0, long count1, int *x1, long *count)
+{
+ long c = 0, c1 = count1 - 1;
+ int x = x0;
+ byte p = 1; /* The inverse of the previous bit. */
+ byte r; /* The inverse of the current bit. */
+ byte *q = base + (x / 8), m = 0x80 >> (x % 8);
+
+ for (; x < width; x++) {
+ r = !(*q & m);
+ if (p != r) {
+ if (c >= c1) {
+ if (!r)
+ goto ex; /* stop before the upgrade. */
+ }
+ c++;
+ }
+ p = r;
+ m >>= 1;
+ if (!m) {
+ m = 0x80;
+ q++;
+ }
+ }
+ if (p)
+ c++; /* Account the last downgrade. */
+ex:
+ *count = c;
+ *x1 = x;
+}
+
+static void
+compute_subimage(int width, int height, int raster, byte *base,
+ int x0, int y0, long MaxClipPathSize, int *x1, int *y1)
+{
+ int bytes = (width + 7) / 8;
+ /* Returns a semiopen range : [x0:x1)*[y0:y1). */
+ if (x0 != 0) {
+ long count;
+
+ /* A partial single scanline. */
+ max_subimage_width(width, base + y0 * raster, x0, MaxClipPathSize / 4, x1, &count);
+ *y1 = y0;
+ } else {
+ int xx, y = y0, yy;
+ long count, count1 = MaxClipPathSize / 4;
+
+ for(; y < height && count1 > 0; ) {
+ max_subimage_width(width, base + y * raster, 0, count1, &xx, &count);
+ if (xx < width) {
+ if (y == y0) {
+ /* Partial single scanline. */
+ *y1 = y + 1;
+ *x1 = xx;
+ return;
+ } else {
+ /* Full lines before this scanline. */
+ break;
+ }
+ }
+ count1 -= count;
+ yy = y + 1;
+ for (; yy < height; yy++)
+ if (memcmp(base + raster * y, base + raster * yy, bytes))
+ break;
+ y = yy;
+
+ }
+ *y1 = y;
+ *x1 = width;
+ }
+}
+
+static int
+image_line_to_clip(gx_device_pdf *pdev, byte *base, int x0, int x1, int y0, int y1, bool started)
+{ /* returns the number of segments or error code. */
+ int x = x0, xx;
+ byte *q = base + (x / 8), m = 0x80 >> (x % 8);
+ long c = 0;
+
+ for (;;) {
+ /* Look for upgrade : */
+ for (; x < x1; x++) {
+ if (*q & m)
+ break;
+ m >>= 1;
+ if (!m) {
+ m = 0x80;
+ q++;
+ }
+ }
+ if (x == x1)
+ return c;
+ xx = x;
+ /* Look for downgrade : */
+ for (; x < x1; x++) {
+ if (!(*q & m))
+ break;
+ m >>= 1;
+ if (!m) {
+ m = 0x80;
+ q++;
+ }
+ }
+ /* Found the interval [xx:x). */
+ if (!started) {
+ stream_puts(pdev->strm, "n\n");
+ started = true;
+ }
+ pprintld2(pdev->strm, "%ld %ld m ", xx, y0);
+ pprintld2(pdev->strm, "%ld %ld l ", x, y0);
+ pprintld2(pdev->strm, "%ld %ld l ", x, y1);
+ pprintld2(pdev->strm, "%ld %ld l h\n", xx, y1);
+ c += 4;
+ }
+ return c;
+}
+
+static int
+mask_to_clip(gx_device_pdf *pdev, int width, int height,
+ int raster, byte *base, int x0, int y0, int x1, int y1)
+{
+ int y, yy, code = 0, bytes = (width + 7) / 8;
+ bool has_segments = false;
+
+ for (y = y0; y < y1 && code >= 0;) {
+ yy = y + 1;
+ if (x0 == 0) {
+ for (; yy < y1; yy++)
+ if (memcmp(base + raster * y, base + raster * yy, bytes))
+ break;
+ }
+ code = image_line_to_clip(pdev, base + raster * y, x0, x1, y, yy, has_segments);
+ if (code > 0)
+ has_segments = true;
+ y = yy;
+ }
+ if (has_segments)
+ stream_puts(pdev->strm, "W n\n");
+ return code < 0 ? code : has_segments ? 1 : 0;
+}
+
+static int
+write_subimage(gx_device_pdf *pdev, gx_device_memory *mdev, int x, int y, int x1, int y1)
+{
+ gs_image_t image;
+ pdf_image_writer writer;
+ /* expand in 1 pixel to provide a proper color interpolation */
+ int X = max(0, x - 1);
+ int Y = max(0, y - 1);
+ int X1 = min(mdev->width, x1 + 1);
+ int Y1 = min(mdev->height, y1 + 1);
+ int code;
+
+ code = pdf_copy_color_data(pdev, mdev->base + mdev->raster * Y, X,
+ mdev->raster, gx_no_bitmap_id,
+ X, Y, X1 - X, Y1 - Y,
+ &image, &writer, 2);
+ if (code < 0)
+ return code;
+ if (!writer.pres)
+ return 0; /* inline image. */
+ return pdf_do_image(pdev, writer.pres, NULL, true);
+}
+
+static int
+write_image_with_clip(gx_device_pdf *pdev, pdf_lcvd_t *cvd)
+{
+ int x = 0, y = 0;
+ int code, code1;
+
+ if (cvd->write_matrix)
+ pdf_put_matrix(pdev, NULL, &cvd->m, " cm q\n");
+ for(;;) {
+ int x1, y1;
+
+ compute_subimage(cvd->mask->width, cvd->mask->height,
+ cvd->mask->raster, cvd->mask->base,
+ x, y, max(pdev->MaxClipPathSize, 100), &x1, &y1);
+ code = mask_to_clip(pdev,
+ cvd->mask->width, cvd->mask->height,
+ cvd->mask->raster, cvd->mask->base,
+ x, y, x1, y1);
+ if (code < 0)
+ return code;
+ if (code > 0) {
+ code1 = write_subimage(pdev, &cvd->mdev, x, y, x1, y1);
+ if (code1 < 0)
+ return code1;
+ }
+ if (x1 >= cvd->mdev.width && y1 >= cvd->mdev.height)
+ break;
+ if (code > 0)
+ stream_puts(pdev->strm, "Q q\n");
+ if (x1 == cvd->mask->width) {
+ x = 0;
+ y = y1;
+ } else {
+ x = x1;
+ y = y1;
+ }
+ }
+ if (cvd->write_matrix)
+ stream_puts(pdev->strm, "Q\n");
+ return 0;
+}
+
+int
+pdf_dump_converted_image(gx_device_pdf *pdev, pdf_lcvd_t *cvd)
+{
+ int code = 0;
+
+ if (!cvd->path_is_empty || cvd->has_background) {
+ if (!cvd->has_background)
+ stream_puts(pdev->strm, "W n\n");
+ code = write_image(pdev, &cvd->mdev, (cvd->write_matrix ? &cvd->m : NULL));
+ cvd->path_is_empty = true;
+ } else if (!cvd->mask_is_empty && pdev->PatternImagemask) {
+ /* Convert to imagemask with a pattern color. */
+ /* See also use_image_as_pattern in gdevpdfi.c . */
+ gs_imager_state s;
+ gs_pattern1_instance_t inst;
+ gs_id id = gs_next_ids(cvd->mdev.memory, 1);
+ cos_value_t v;
+ const pdf_resource_t *pres;
+
+ memset(&s, 0, sizeof(s));
+ s.ctm.xx = cvd->m.xx;
+ s.ctm.xy = cvd->m.xy;
+ s.ctm.yx = cvd->m.yx;
+ s.ctm.yy = cvd->m.yy;
+ s.ctm.tx = cvd->m.tx;
+ s.ctm.ty = cvd->m.ty;
+ memset(&inst, 0, sizeof(inst));
+ inst.saved = (gs_state *)&s; /* HACK : will use s.ctm only. */
+ inst.templat.PaintType = 1;
+ inst.templat.TilingType = 1;
+ inst.templat.BBox.p.x = inst.templat.BBox.p.y = 0;
+ inst.templat.BBox.q.x = cvd->mdev.width;
+ inst.templat.BBox.q.y = cvd->mdev.height;
+ inst.templat.XStep = (float)cvd->mdev.width;
+ inst.templat.YStep = (float)cvd->mdev.height;
+
+ {
+ pattern_accum_param_s param;
+ param.pinst = (void *)&inst;
+ param.graphics_state = (void *)&s;
+ param.pinst_id = inst.id;
+
+ code = (*dev_proc(pdev, dev_spec_op))((gx_device *)pdev,
+ gxdso_pattern_start_accum, &param, sizeof(pattern_accum_param_s));
+ }
+
+ if (code >= 0) {
+ stream_puts(pdev->strm, "W n\n");
+ code = write_image(pdev, &cvd->mdev, NULL);
+ }
+ pres = pdev->accumulating_substream_resource;
+ if (code >= 0) {
+ pattern_accum_param_s param;
+ param.pinst = (void *)&inst;
+ param.graphics_state = (void *)&s;
+ param.pinst_id = inst.id;
+
+ code = (*dev_proc(pdev, dev_spec_op))((gx_device *)pdev,
+ gxdso_pattern_finish_accum, &param, id);
+ }
+ if (code >= 0)
+ code = (*dev_proc(pdev, dev_spec_op))((gx_device *)pdev,
+ gxdso_pattern_load, &inst, id);
+ if (code >= 0)
+ code = pdf_cs_Pattern_colored(pdev, &v);
+ if (code >= 0) {
+ cos_value_write(&v, pdev);
+ pprintld1(pdev->strm, " cs /R%ld scn ", pdf_resource_id(pres));
+ }
+ if (code >= 0)
+ code = write_mask(pdev, cvd->mask, (cvd->write_matrix ? &cvd->m : NULL));
+ cvd->mask_is_empty = true;
+ } else if (!cvd->mask_is_empty && !pdev->PatternImagemask) {
+ /* Convert to image with a clipping path. */
+ stream_puts(pdev->strm, "q\n");
+ code = write_image_with_clip(pdev, cvd);
+ stream_puts(pdev->strm, "Q\n");
+ }
+ if (code > 0)
+ code = (*dev_proc(&cvd->mdev, fill_rectangle))((gx_device *)&cvd->mdev,
+ 0, 0, cvd->mdev.width, cvd->mdev.height, (gx_color_index)0);
+ return code;
+}
+static int
+lcvd_handle_fill_path_as_shading_coverage(gx_device *dev,
+ const gs_imager_state *pis, gx_path *ppath,
+ const gx_fill_params *params,
+ const gx_drawing_color *pdcolor, const gx_clip_path *pcpath)
+{
+ pdf_lcvd_t *cvd = (pdf_lcvd_t *)dev;
+ gx_device_pdf *pdev = (gx_device_pdf *)cvd->mdev.target;
+ int code;
+
+ if (cvd->has_background)
+ return 0;
+ if (gx_path_is_null(ppath)) {
+ /* use the mask. */
+ if (!cvd->path_is_empty) {
+ code = pdf_dump_converted_image(pdev, cvd);
+ if (code < 0)
+ return code;
+ stream_puts(pdev->strm, "Q q\n");
+ dev_proc(&cvd->mdev, fill_rectangle) = lcvd_fill_rectangle_shifted2;
+ }
+ if (!cvd->mask_is_clean || !cvd->path_is_empty) {
+ code = (*dev_proc(cvd->mask, fill_rectangle))((gx_device *)cvd->mask,
+ 0, 0, cvd->mask->width, cvd->mask->height, (gx_color_index)0);
+ if (code < 0)
+ return code;
+ cvd->mask_is_clean = true;
+ }
+ cvd->path_is_empty = true;
+ cvd->mask_is_empty = false;
+ } else {
+ gs_matrix m;
+
+ gs_make_translation(cvd->path_offset.x, cvd->path_offset.y, &m);
+ /* use the clipping. */
+ if (!cvd->mask_is_empty) {
+ code = pdf_dump_converted_image(pdev, cvd);
+ if (code < 0)
+ return code;
+ stream_puts(pdev->strm, "Q q\n");
+ dev_proc(&cvd->mdev, fill_rectangle) = lcvd_fill_rectangle_shifted;
+ cvd->mask_is_empty = true;
+ }
+ code = gdev_vector_dopath((gx_device_vector *)pdev, ppath,
+ gx_path_type_fill | gx_path_type_optimize, &m);
+ if (code < 0)
+ return code;
+ stream_puts(pdev->strm, "h\n");
+ cvd->path_is_empty = false;
+ }
+ return 0;
+}
+
+int
+pdf_setup_masked_image_converter(gx_device_pdf *pdev, gs_memory_t *mem, const gs_matrix *m, pdf_lcvd_t **pcvd,
+ bool need_mask, int x, int y, int w, int h, bool write_on_close)
+{
+ int code;
+ gx_device_memory *mask = 0;
+ pdf_lcvd_t *cvd = *pcvd;
+
+ if (cvd == NULL) {
+ cvd = gs_alloc_struct(mem, pdf_lcvd_t, &st_pdf_lcvd_t, "pdf_setup_masked_image_converter");
+ if (cvd == NULL)
+ return_error(gs_error_VMerror);
+ *pcvd = cvd;
+ }
+ cvd->pdev = pdev;
+ gs_make_mem_device(&cvd->mdev, gdev_mem_device_for_bits(pdev->color_info.depth),
+ mem, 0, (gx_device *)pdev);
+ cvd->mdev.width = w;
+ cvd->mdev.height = h;
+ cvd->mdev.mapped_x = x;
+ cvd->mdev.mapped_y = y;
+ cvd->mdev.bitmap_memory = mem;
+ cvd->mdev.color_info = pdev->color_info;
+ cvd->path_is_empty = true;
+ cvd->mask_is_empty = true;
+ cvd->mask_is_clean = false;
+ cvd->has_background = false;
+ cvd->mask = 0;
+ cvd->write_matrix = true;
+ code = (*dev_proc(&cvd->mdev, open_device))((gx_device *)&cvd->mdev);
+ if (code < 0)
+ return code;
+ code = (*dev_proc(&cvd->mdev, fill_rectangle))((gx_device *)&cvd->mdev,
+ 0, 0, cvd->mdev.width, cvd->mdev.height, (gx_color_index)0);
+ if (code < 0)
+ return code;
+ if (need_mask) {
+ mask = gs_alloc_struct(mem, gx_device_memory, &st_device_memory, "pdf_setup_masked_image_converter");
+ if (mask == NULL)
+ return_error(gs_error_VMerror);
+ cvd->mask = mask;
+ gs_make_mem_mono_device(mask, mem, (gx_device *)pdev);
+ mask->width = cvd->mdev.width;
+ mask->height = cvd->mdev.height;
+ mask->raster = gx_device_raster((gx_device *)mask, 1);
+ mask->bitmap_memory = mem;
+ code = (*dev_proc(mask, open_device))((gx_device *)mask);
+ if (code < 0)
+ return code;
+ if (write_on_close) {
+ code = (*dev_proc(mask, fill_rectangle))((gx_device *)mask,
+ 0, 0, mask->width, mask->height, (gx_color_index)0);
+ if (code < 0)
+ return code;
+ }
+ }
+ cvd->std_copy_color = dev_proc(&cvd->mdev, copy_color);
+ cvd->std_fill_rectangle = dev_proc(&cvd->mdev, fill_rectangle);
+ cvd->std_close_device = dev_proc(&cvd->mdev, close_device);
+ cvd->std_get_clipping_box = dev_proc(&cvd->mdev, get_clipping_box);
+ if (!write_on_close) {
+ /* Type 3 images will write to the mask directly. */
+ dev_proc(&cvd->mdev, fill_rectangle) = (need_mask ? lcvd_fill_rectangle_shifted2
+ : lcvd_fill_rectangle_shifted);
+ dev_proc(&cvd->mdev, get_clipping_box) = lcvd_get_clipping_box_shifted_from_mdev;
+ } else {
+ dev_proc(&cvd->mdev, fill_rectangle) = lcvd_fill_rectangle_shifted;
+ dev_proc(&cvd->mdev, get_clipping_box) = lcvd_get_clipping_box_shifted_from_mdev;
+ }
+ dev_proc(&cvd->mdev, copy_color) = lcvd_copy_color_shifted;
+ dev_proc(&cvd->mdev, dev_spec_op) = lcvd_dev_spec_op;
+ dev_proc(&cvd->mdev, fill_path) = lcvd_handle_fill_path_as_shading_coverage;
+ cvd->m = *m;
+ if (write_on_close) {
+ cvd->mdev.is_open = true;
+ if (mask)
+ mask->is_open = true;
+ dev_proc(&cvd->mdev, close_device) = lcvd_close_device_with_writing;
+ }
+ return 0;
+}
+
+void
+pdf_remove_masked_image_converter(gx_device_pdf *pdev, pdf_lcvd_t *cvd, bool need_mask)
+{
+ (*dev_proc(&cvd->mdev, close_device))((gx_device *)&cvd->mdev);
+ if (cvd->mask) {
+ (*dev_proc(cvd->mask, close_device))((gx_device *)cvd->mask);
+ gs_free_object(cvd->mask->memory, cvd->mask, "pdf_remove_masked_image_converter");
+ }
+}
+
+/* ------ Driver procedures ------ */
+
+/* Fill a path. */
+int
+gdev_pdf_fill_path(gx_device * dev, const gs_imager_state * pis, gx_path * ppath,
+ const gx_fill_params * params,
+ const gx_drawing_color * pdcolor, const gx_clip_path * pcpath)
+{
+ gx_device_pdf *pdev = (gx_device_pdf *) dev;
+ int code;
+ /*
+ * HACK: we fill an empty path in order to set the clipping path
+ * and the color for writing text. If it weren't for this, we
+ * could detect and skip empty paths before putting out the clip
+ * path or the color. We also clip with an empty path in order
+ * to advance currentpoint for show operations without actually
+ * drawing anything.
+ */
+ bool have_path;
+ gs_fixed_rect box = {{0, 0}, {0, 0}}, box1;
+
+ if (pdev->Eps2Write) {
+ gx_path_bbox(ppath, &box1);
+ if (box1.p.x != 0 || box1.p.y != 0 || box1.q.x != 0 || box1.q.y != 0){
+ if (pcpath != 0)
+ rect_intersect(box1, pcpath->outer_box);
+ if (fixed2int(box1.p.x) < pdev->BBox.p.x)
+ pdev->BBox.p.x = fixed2int(box1.p.x);
+ if (fixed2int(box1.p.y) < pdev->BBox.p.y)
+ pdev->BBox.p.y = fixed2int(box1.p.y);
+ if (fixed2int(box1.q.x) > pdev->BBox.q.x)
+ pdev->BBox.q.x = fixed2int(box1.q.x);
+ if (fixed2int(box1.q.y) > pdev->BBox.q.y)
+ pdev->BBox.q.y = fixed2int(box1.q.y);
+ }
+ if (pdev->AccumulatingBBox)
+ return 0;
+ }
+ have_path = !gx_path_is_void(ppath);
+ if (!have_path && !pdev->vg_initial_set) {
+ /* See lib/gs_pdfwr.ps about "initial graphic state". */
+ pdf_prepare_initial_viewer_state(pdev, pis);
+ pdf_reset_graphics(pdev);
+ return 0;
+ }
+ if (have_path) {
+ code = gx_path_bbox(ppath, &box);
+ if (code < 0)
+ return code;
+ }
+ box1 = box;
+
+ code = prepare_fill_with_clip(pdev, pis, &box, have_path, pdcolor, pcpath);
+ if (code == gs_error_rangecheck) {
+ /* Fallback to the default implermentation for handling
+ a transparency with CompatibilityLevel<=1.3 . */
+ return gx_default_fill_path((gx_device *)pdev, pis, ppath, params, pdcolor, pcpath);
+ }
+ if (code < 0)
+ return code;
+ if (code == 1)
+ return 0; /* Nothing to paint. */
+ if (!have_path)
+ return 0;
+ code = pdf_setfillcolor((gx_device_vector *)pdev, pis, pdcolor);
+ if (code == gs_error_rangecheck) {
+ const bool convert_to_image = ((pdev->CompatibilityLevel <= 1.2 ||
+ pdev->params.ColorConversionStrategy != ccs_LeaveColorUnchanged) &&
+ gx_dc_is_pattern2_color(pdcolor));
+
+ if (!convert_to_image) {
+ /* Fallback to the default implermentation for handling
+ a shading with CompatibilityLevel<=1.2 . */
+ return gx_default_fill_path(dev, pis, ppath, params, pdcolor, pcpath);
+ } else {
+ /* Convert a shading into a bitmap
+ with CompatibilityLevel<=1.2 . */
+ pdf_lcvd_t cvd, *pcvd = &cvd;
+ int sx, sy;
+ gs_fixed_rect bbox, bbox1;
+ bool need_mask = gx_dc_pattern2_can_overlap(pdcolor);
+ gs_matrix m, save_ctm = ctm_only(pis), ms, msi, mm;
+ gs_int_point rect_size;
+ /* double scalex = 1.9, scaley = 1.4; debug purpose only. */
+ double scale, scalex, scaley;
+ int log2_scale_x = 0, log2_scale_y = 0;
+ gx_drawing_color dc = *pdcolor;
+ gs_pattern2_instance_t pi = *(gs_pattern2_instance_t *)dc.ccolor.pattern;
+ gs_state *pgs = gs_state_copy(pi.saved, gs_state_memory(pi.saved));
+
+ if (pgs == NULL)
+ return_error(gs_error_VMerror);
+ dc.ccolor.pattern = (gs_pattern_instance_t *)&pi;
+ pi.saved = pgs;
+ code = gx_path_bbox(ppath, &bbox);
+ if (code < 0)
+ return code;
+ rect_intersect(bbox, box);
+ code = gx_dc_pattern2_get_bbox(pdcolor, &bbox1);
+ if (code < 0)
+ return code;
+ if (code)
+ rect_intersect(bbox, bbox1);
+ if (bbox.p.x >= bbox.q.x || bbox.p.y >= bbox.q.y)
+ return 0;
+ sx = fixed2int(bbox.p.x);
+ sy = fixed2int(bbox.p.y);
+ gs_make_identity(&m);
+ rect_size.x = fixed2int(bbox.q.x + fixed_half) - sx;
+ rect_size.y = fixed2int(bbox.q.y + fixed_half) - sy;
+ if (rect_size.x == 0 || rect_size.y == 0)
+ return 0;
+ m.tx = (float)sx;
+ m.ty = (float)sy;
+ cvd.path_offset.x = sx;
+ cvd.path_offset.y = sy;
+ scale = (double)rect_size.x * rect_size.y * pdev->color_info.num_components /
+ pdev->MaxShadingBitmapSize;
+ if (scale > 1) {
+ /* This section (together with the call to 'path_scale' below)
+ sets up a downscaling when converting the shading into bitmap.
+ We used floating point numbers to debug it, but in production
+ we prefer to deal only with integers being powers of 2
+ in order to avoid possible distorsions when scaling paths.
+ */
+ log2_scale_x = log2_scale_y = ilog2((int)ceil(sqrt(scale)));
+ if ((double)(1 << log2_scale_x) * (1 << log2_scale_y) < scale)
+ log2_scale_y++;
+ if ((double)(1 << log2_scale_x) * (1 << log2_scale_y) < scale)
+ log2_scale_x++;
+ scalex = (double)(1 << log2_scale_x);
+ scaley = (double)(1 << log2_scale_y);
+ rect_size.x = (int)floor(rect_size.x / scalex + 0.5);
+ rect_size.y = (int)floor(rect_size.y / scaley + 0.5);
+ gs_make_scaling(1.0 / scalex, 1.0 / scaley, &ms);
+ gs_make_scaling(scalex, scaley, &msi);
+ gs_matrix_multiply(&msi, &m, &m);
+ gs_matrix_multiply(&ctm_only(pis), &ms, &mm);
+ gs_setmatrix((gs_state *)pis, &mm);
+ gs_matrix_multiply(&ctm_only((gs_imager_state *)pgs), &ms, &mm);
+ gs_setmatrix((gs_state *)pgs, &mm);
+ sx = fixed2int(bbox.p.x / (int)scalex);
+ sy = fixed2int(bbox.p.y / (int)scaley);
+ cvd.path_offset.x = sx; /* m.tx / scalex */
+ cvd.path_offset.y = sy;
+ }
+ code = pdf_setup_masked_image_converter(pdev, pdev->memory, &m, &pcvd, need_mask, sx, sy,
+ rect_size.x, rect_size.y, false);
+ pcvd->has_background = gx_dc_pattern2_has_background(pdcolor);
+ stream_puts(pdev->strm, "q\n");
+ if (code >= 0) {
+ code = gdev_vector_dopath((gx_device_vector *)pdev, ppath,
+ gx_path_type_clip, NULL);
+ if (code >= 0)
+ stream_puts(pdev->strm, (params->rule < 0 ? "W n\n" : "W* n\n"));
+ }
+ pdf_put_matrix(pdev, NULL, &cvd.m, " cm q\n");
+ cvd.write_matrix = false;
+ if (code >= 0)
+ code = gs_shading_do_fill_rectangle(pi.templat.Shading,
+ NULL, (gx_device *)&cvd.mdev, (gs_imager_state *)pgs, !pi.shfill);
+ if (code >= 0)
+ code = pdf_dump_converted_image(pdev, &cvd);
+ stream_puts(pdev->strm, "Q Q\n");
+ pdf_remove_masked_image_converter(pdev, &cvd, need_mask);
+ gs_setmatrix((gs_state *)pis, &save_ctm);
+ gs_state_free(pgs);
+ return code;
+ }
+ }
+ if (code < 0)
+ return code;
+ {
+ stream *s = pdev->strm;
+ double scale;
+ gs_matrix smat;
+ gs_matrix *psmat = NULL;
+
+ if (pcpath) {
+ rect_intersect(box1, box);
+ if (box1.p.x > box1.q.x || box1.p.y > box1.q.y)
+ return 0; /* outside the clipping path */
+ }
+ if (params->flatness != pdev->state.flatness) {
+ pprintg1(s, "%g i\n", params->flatness);
+ pdev->state.flatness = params->flatness;
+ }
+ if (make_rect_scaling(pdev, &box1, 1.0, &scale)) {
+ gs_make_scaling(pdev->scale.x * scale, pdev->scale.y * scale,
+ &smat);
+ pdf_put_matrix(pdev, "q ", &smat, "cm\n");
+ psmat = &smat;
+ }
+ gdev_vector_dopath((gx_device_vector *)pdev, ppath,
+ gx_path_type_fill | gx_path_type_optimize,
+ psmat);
+ stream_puts(s, (params->rule < 0 ? "f\n" : "f*\n"));
+ if (psmat)
+ stream_puts(s, "Q\n");
+ }
+ return 0;
+}
+
+/* Stroke a path. */
+int
+gdev_pdf_stroke_path(gx_device * dev, const gs_imager_state * pis,
+ gx_path * ppath, const gx_stroke_params * params,
+ const gx_drawing_color * pdcolor, const gx_clip_path * pcpath)
+{
+ gx_device_pdf *pdev = (gx_device_pdf *) dev;
+ stream *s;
+ int code;
+ double scale, path_scale;
+ bool set_ctm;
+ gs_matrix mat;
+ double prescale = 1;
+ gs_fixed_rect bbox;
+
+ if (gx_path_is_void(ppath))
+ return 0; /* won't mark the page */
+ if (pdf_must_put_clip_path(pdev, pcpath))
+ code = pdf_unclip(pdev);
+ else if ((pdev->last_charpath_op & TEXT_DO_FALSE_CHARPATH) && ppath->current_subpath &&
+ (ppath->last_charpath_segment == ppath->current_subpath->last) && !pdev->ForOPDFRead) {
+ bool hl_color = pdf_can_handle_hl_color((gx_device_vector *)pdev, pis, pdcolor);
+ const gs_imager_state *pis_for_hl_color = (hl_color ? pis : NULL);
+
+ if (pdf_modify_text_render_mode(pdev->text->text_state, 1)) {
+ /* Set the colour for the stroke */
+ code = pdf_reset_color(pdev, pis_for_hl_color, pdcolor, &pdev->saved_stroke_color,
+ &pdev->stroke_used_process_color, &psdf_set_stroke_color_commands);
+ if (code == 0) {
+ s = pdev->strm;
+ /* Text is emitted scaled so that the CTM is an identity matrix, the line width
+ * needs to be scaled to match otherwise we will get the default, or the current
+ * width scaled by the CTM before the text, either of which would be wrong.
+ */
+ scale = 72 / pdev->HWResolution[0];
+ scale *= pis->ctm.xx;
+ pprintg1(s, "%g w\n", (pis->line_params.half_width * 2) * (float)scale);
+ /* Some trickery here. We have altered the colour, text render mode and linewidth,
+ * we don't want those to persist. By switching to a stream context we will flush the
+ * pending text. This has the beneficial side effect of executing a grestore. So
+ * everything works out neatly.
+ */
+ code = pdf_open_page(pdev, PDF_IN_STREAM);
+ return(code);
+ }
+ }
+ /* Can only get here if any of the above steps fail, in which case we proceed to
+ * emit the charpath as a normal path, and stroke it.
+ */
+ code = pdf_open_page(pdev, PDF_IN_STREAM);
+ } else
+ code = pdf_open_page(pdev, PDF_IN_STREAM);
+ if (code < 0)
+ return code;
+ code = pdf_prepare_stroke(pdev, pis);
+ if (code == gs_error_rangecheck) {
+ /* Fallback to the default implermentation for handling
+ a transparency with CompatibilityLevel<=1.3 . */
+ return gx_default_stroke_path((gx_device *)dev, pis, ppath, params, pdcolor, pcpath);
+ }
+ if (code < 0)
+ return code;
+ code = pdf_put_clip_path(pdev, pcpath);
+ if (code < 0)
+ return code;
+ /*
+ * If the CTM is not uniform, stroke width depends on angle.
+ * We'd like to avoid resetting the CTM, so we check for uniform
+ * CTMs explicitly. Note that in PDF, unlike PostScript, it is
+ * the CTM at the time of the stroke operation, not the CTM at
+ * the time the path was constructed, that is used for transforming
+ * the points of the path; so if we have to reset the CTM, we must
+ * do it before constructing the path, and inverse-transform all
+ * the coordinates.
+ */
+ set_ctm = (bool)gdev_vector_stroke_scaling((gx_device_vector *)pdev,
+ pis, &scale, &mat);
+ if (set_ctm && ((pis->ctm.xx == 0 && pis->ctm.xy == 0) ||
+ (pis->ctm.yx == 0 && pis->ctm.yy == 0))) {
+ /* Acrobat Reader 5 and Adobe Reader 6 issues
+ the "Wrong operand type" error with matrices, which have 3 zero coefs.
+ Besides that, we found that Acrobat Reader 4, Acrobat Reader 5
+ and Adobe Reader 6 all store the current path in user space
+ and apply CTM in the time of stroking - See the bug 687901.
+ Therefore a precise conversion of Postscript to PDF isn't possible in this case.
+ Adobe viewers render a line with a constant width instead.
+ At last, with set_ctm == true we need the inverse matrix in
+ gdev_vector_dopath. Therefore we exclude projection matrices
+ (see bug 688363). */
+ set_ctm = false;
+ scale = fabs(pis->ctm.xx + pis->ctm.xy + pis->ctm.yx + pis->ctm.yy) /* Using the non-zero coeff. */
+ / sqrt(2); /* Empirically from Adobe. */
+ }
+ if (set_ctm) {
+ /*
+ * We want a scaling factor that will bring the largest reasonable
+ * user coordinate within bounds. We choose a factor based on the
+ * minor axis of the transformation. Thanks to Raph Levien for
+ * the following formula.
+ */
+ double a = mat.xx, b = mat.xy, c = mat.yx, d = mat.yy;
+ double u = fabs(a * d - b * c);
+ double v = a * a + b * b + c * c + d * d;
+ double minor = (sqrt(v + 2 * u) - sqrt(v - 2 * u)) * 0.5;
+
+ prescale = (minor == 0 || minor > 1 ? 1 : 1 / minor);
+ }
+ gx_path_bbox(ppath, &bbox);
+ {
+ /* Check whether a painting appears inside the clipping box.
+ Doing so after writing the clipping path due to /SP pdfmark
+ uses a special hack with painting outside the clipping box
+ for synchronizing the clipping path (see lib/gs_pdfwr.ps).
+ That hack appeared because there is no way to pass
+ the imager state through gdev_pdf_put_params,
+ which pdfmark is implemented with.
+ */
+ gs_fixed_rect clip_box, stroke_bbox = bbox;
+ gs_point d0, d1;
+ gs_fixed_point p0, p1;
+ fixed bbox_expansion_x, bbox_expansion_y;
+
+ gs_distance_transform(pis->line_params.half_width, 0, &ctm_only(pis), &d0);
+ gs_distance_transform(0, pis->line_params.half_width, &ctm_only(pis), &d1);
+ p0.x = float2fixed(any_abs(d0.x));
+ p0.y = float2fixed(any_abs(d0.y));
+ p1.x = float2fixed(any_abs(d1.x));
+ p1.y = float2fixed(any_abs(d1.y));
+ bbox_expansion_x = max(p0.x, p1.x) + fixed_1 * 2;
+ bbox_expansion_y = max(p0.y, p1.y) + fixed_1 * 2;
+ stroke_bbox.p.x -= bbox_expansion_x;
+ stroke_bbox.p.y -= bbox_expansion_y;
+ stroke_bbox.q.x += bbox_expansion_x;
+ stroke_bbox.q.y += bbox_expansion_y;
+ gx_cpath_outer_box(pcpath, &clip_box);
+ rect_intersect(stroke_bbox, clip_box);
+ if (stroke_bbox.q.x < stroke_bbox.p.x || stroke_bbox.q.y < stroke_bbox.p.y)
+ return 0;
+ }
+ if (make_rect_scaling(pdev, &bbox, prescale, &path_scale)) {
+ scale /= path_scale;
+ if (set_ctm)
+ gs_matrix_scale(&mat, path_scale, path_scale, &mat);
+ else {
+ gs_make_scaling(path_scale, path_scale, &mat);
+ set_ctm = true;
+ }
+ }
+ code = gdev_vector_prepare_stroke((gx_device_vector *)pdev, pis, params,
+ pdcolor, scale);
+ if (code < 0)
+ return gx_default_stroke_path(dev, pis, ppath, params, pdcolor,
+ pcpath);
+ if (!pdev->HaveStrokeColor)
+ pdev->saved_fill_color = pdev->saved_stroke_color;
+ if (set_ctm)
+ pdf_put_matrix(pdev, "q ", &mat, "cm\n");
+ code = gdev_vector_dopath((gx_device_vector *)pdev, ppath,
+ gx_path_type_stroke | gx_path_type_optimize,
+ (set_ctm ? &mat : (const gs_matrix *)0));
+ if (code < 0)
+ return code;
+ s = pdev->strm;
+ stream_puts(s, (code ? "s" : "S"));
+ stream_puts(s, (set_ctm ? " Q\n" : "\n"));
+ if (pdev->Eps2Write) {
+ pdev->AccumulatingBBox++;
+ code = gx_default_stroke_path(dev, pis, ppath, params, pdcolor,
+ pcpath);
+ pdev->AccumulatingBBox--;
+ if (code < 0)
+ return code;
+ }
+ return 0;
+}
+
+/*
+ The fill_rectangle_hl_color device method.
+ See gxdevcli.h about return codes.
+ */
+int
+gdev_pdf_fill_rectangle_hl_color(gx_device *dev, const gs_fixed_rect *rect,
+ const gs_imager_state *pis, const gx_drawing_color *pdcolor,
+ const gx_clip_path *pcpath)
+{
+ int code;
+ gs_fixed_rect box1 = *rect, box = box1;
+ gx_device_pdf *pdev = (gx_device_pdf *) dev;
+ double scale;
+ gs_matrix smat;
+ gs_matrix *psmat = NULL;
+ const bool convert_to_image = (pdev->CompatibilityLevel <= 1.2 &&
+ gx_dc_is_pattern2_color(pdcolor));
+
+ if (rect->p.x == rect->q.x)
+ return 0;
+ if (!convert_to_image) {
+ code = prepare_fill_with_clip(pdev, pis, &box, true, pdcolor, pcpath);
+ if (code < 0)
+ return code;
+ if (code == 1)
+ return 0; /* Nothing to paint. */
+ code = pdf_setfillcolor((gx_device_vector *)pdev, pis, pdcolor);
+ if (code < 0)
+ return code;
+ if (pcpath)
+ rect_intersect(box1, box);
+ if (box1.p.x > box1.q.x || box1.p.y > box1.q.y)
+ return 0; /* outside the clipping path */
+ if (make_rect_scaling(pdev, &box1, 1.0, &scale)) {
+ gs_make_scaling(pdev->scale.x * scale, pdev->scale.y * scale, &smat);
+ pdf_put_matrix(pdev, "q ", &smat, "cm\n");
+ psmat = &smat;
+ }
+ pprintg4(pdev->strm, "%g %g %g %g re f\n",
+ fixed2float(box1.p.x) / scale, fixed2float(box1.p.y) / scale,
+ fixed2float(box1.q.x - box1.p.x) / scale, fixed2float(box1.q.y - box1.p.y) / scale);
+ if (psmat)
+ stream_puts(pdev->strm, "Q\n");
+ if (pdev->Eps2Write) {
+ gs_rect *Box;
+
+ if (!pdev->accumulating_charproc)
+ Box = &pdev->BBox;
+ else
+ Box = &pdev->charproc_BBox;
+
+ if (fixed2float(box1.p.x) / (pdev->HWResolution[0] / 72.0) < Box->p.x)
+ Box->p.x = fixed2float(box1.p.x) / (pdev->HWResolution[0] / 72.0);
+ if (fixed2float(box1.p.y) / (pdev->HWResolution[1] / 72.0) < Box->p.y)
+ Box->p.y = fixed2float(box1.p.y) / (pdev->HWResolution[1] / 72.0);
+ if (fixed2float(box1.q.x) / (pdev->HWResolution[0] / 72.0) > Box->q.x)
+ Box->q.x = fixed2float(box1.q.x) / (pdev->HWResolution[0] / 72.0);
+ if (fixed2float(box1.q.y) / (pdev->HWResolution[1] / 72.0) > Box->q.y)
+ Box->q.y = fixed2float(box1.q.y) / (pdev->HWResolution[1] / 72.0);
+ }
+ return 0;
+ } else {
+ gx_fill_params params;
+ gx_path path;
+
+ params.rule = 1; /* Not important because the path is a rectange. */
+ params.adjust.x = params.adjust.y = 0;
+ params.flatness = pis->flatness;
+ gx_path_init_local(&path, pis->memory);
+ code = gx_path_add_rectangle(&path, rect->p.x, rect->p.y, rect->q.x, rect->q.y);
+ if (code < 0)
+ return code;
+ code = gdev_pdf_fill_path(dev, pis, &path, &params, pdcolor, pcpath);
+ if (code < 0)
+ return code;
+ gx_path_free(&path, "gdev_pdf_fill_rectangle_hl_color");
+ return code;
+
+ }
+}
+
+int
+gdev_pdf_fillpage(gx_device *dev, gs_imager_state * pis, gx_device_color *pdevc)
+{
+ gx_device_pdf *pdev = (gx_device_pdf *) dev;
+ int bottom = (pdev->ResourcesBeforeUsage ? 1 : 0);
+
+ if (gx_dc_pure_color(pdevc) == pdev->white && !is_in_page(pdev) && pdev->sbstack_depth <= bottom) {
+ /* PDF doesn't need to erase the page if its plain white */
+ return 0;
+ }
+ else
+ return gx_default_fillpage(dev, pis, pdevc);
+}
diff --git a/devices/vector/gdevpdfe.c b/devices/vector/gdevpdfe.c
new file mode 100644
index 000000000..402d81e47
--- /dev/null
+++ b/devices/vector/gdevpdfe.c
@@ -0,0 +1,876 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Metadata writer. */
+#include "gx.h"
+#include "gserrors.h"
+#include "string_.h"
+#include "time_.h"
+#include "stream.h"
+#include "gp.h"
+#include "smd5.h"
+#include "gscdefs.h"
+#include "gdevpdfx.h"
+#include "gdevpdfg.h"
+#include "gdevpdfo.h"
+#include "ConvertUTF.h"
+
+char PDFDocEncodingLookup [92] = {
+ 0x20, 0x22, 0x20, 0x20, 0x20, 0x21, 0x20, 0x26,
+ 0x20, 0x14, 0x20, 0x13, 0x01, 0x92, 0x20, 0x44,
+ 0x20, 0x39, 0x20, 0x3A, 0x22, 0x12, 0x20, 0x30,
+ 0x20, 0x1E, 0x20, 0x1C, 0x20, 0x1D, 0x20, 0x18,
+ 0x20, 0x19, 0x20, 0x1A, 0x21, 0x22, 0xFB, 0x01,
+ 0xFB, 0x02, 0x01, 0x41, 0x01, 0x52, 0x01, 0x60,
+ 0x01, 0x78, 0x01, 0x7D, 0x01, 0x31, 0x01, 0x42,
+ 0x01, 0x53, 0x01, 0x61, 0x01, 0x7E, 0x00, 0x00,
+ 0x20, 0xAC, 0x00, 0xA1, 0x00, 0xA2, 0x00, 0xA3,
+ 0x00, 0xA4, 0x00, 0xA5, 0x00, 0xA6, 0x00, 0xA7,
+ 0x00, 0xA8, 0x00, 0xA9, 0x00, 0xAA, 0x00, 0xAB,
+ 0x00, 0xAC, 0x00, 0x00
+};
+
+static void
+copy_bytes(stream *s, const byte **data, int *data_length, int n)
+{
+ while (n-- && (*data_length)--) {
+ stream_putc(s, *((*data)++));
+ }
+}
+
+/* Write XML data */
+static void
+pdf_xml_data_write(stream *s, const byte *data, int data_length)
+{
+ int l = data_length;
+ const byte *p = data;
+
+ while (l > 0) {
+ switch (*p) {
+ case '<' : stream_puts(s, "&lt;"); l--; p++; break;
+ case '>' : stream_puts(s, "&gt;"); l--; p++; break;
+ case '&' : stream_puts(s, "&amp;"); l--; p++; break;
+ case '\'': stream_puts(s, "&apos;"); l--; p++; break;
+ case '"' : stream_puts(s, "&quot;"); l--; p++; break;
+ default:
+ if (*p < 32) {
+ /* Not allowed in XML. */
+ pprintd1(s, "&#%d;", *p);
+ l--; p++;
+ } else if (*p >= 0x7F && *p <= 0x9f) {
+ /* Control characters are discouraged in XML. */
+ pprintd1(s, "&#%d;", *p);
+ l--; p++;
+ } else if ((*p & 0xE0) == 0xC0) {
+ /* A 2-byte UTF-8 sequence */
+ copy_bytes(s, &p, &l, 2);
+ } else if ((*p & 0xF0) == 0xE0) {
+ /* A 3-byte UTF-8 sequence */
+ copy_bytes(s, &p, &l, 3);
+ } else if ((*p & 0xF0) == 0xF0) {
+ /* A 4-byte UTF-8 sequence */
+ copy_bytes(s, &p, &l, 4);
+ } else {
+ stream_putc(s, *p);
+ l--; p++;
+ }
+ }
+ }
+}
+
+/* Write XML string */
+static inline void
+pdf_xml_string_write(stream *s, const char *data)
+{
+ pdf_xml_data_write(s, (const byte *)data, strlen(data));
+}
+
+/* Begin an opening XML tag */
+static inline void
+pdf_xml_tag_open_beg(stream *s, const char *data)
+{
+ stream_putc(s, '<');
+ stream_puts(s, data);
+}
+
+/* End an XML tag */
+static inline void
+pdf_xml_tag_end(stream *s)
+{
+ stream_putc(s, '>');
+}
+
+/* End an empty XML tag */
+static inline void
+pdf_xml_tag_end_empty(stream *s)
+{
+ stream_puts(s, "/>");
+}
+
+/* Write an opening XML tag */
+static inline void
+pdf_xml_tag_open(stream *s, const char *data)
+{
+ stream_putc(s, '<');
+ stream_puts(s, data);
+ stream_putc(s, '>');
+}
+
+/* Write a closing XML tag */
+static inline void
+pdf_xml_tag_close(stream *s, const char *data)
+{
+ stream_puts(s, "</");
+ stream_puts(s, data);
+ stream_putc(s, '>');
+}
+
+/* Write an attribute name */
+static inline void
+pdf_xml_attribute_name(stream *s, const char *data)
+{
+ stream_putc(s, ' ');
+ stream_puts(s, data);
+ stream_putc(s, '=');
+}
+
+/* Write a attribute value */
+static inline void
+pdf_xml_attribute_value(stream *s, const char *data)
+{
+ stream_putc(s, '\'');
+ pdf_xml_string_write(s, data);
+ stream_putc(s, '\'');
+}
+/* Write a attribute value */
+static inline void
+pdf_xml_attribute_value_data(stream *s, const byte *data, int data_length)
+{
+ stream_putc(s, '\'');
+ pdf_xml_data_write(s, data, data_length);
+ stream_putc(s, '\'');
+}
+
+/* Begin an XML instruction */
+static inline void
+pdf_xml_ins_beg(stream *s, const char *data)
+{
+ stream_puts(s, "<?");
+ stream_puts(s, data);
+}
+
+/* End an XML instruction */
+static inline void
+pdf_xml_ins_end(stream *s)
+{
+ stream_puts(s, "?>");
+}
+
+/* Write a newline character */
+static inline void
+pdf_xml_newline(stream *s)
+{
+ stream_puts(s, "\n");
+}
+
+/* Copy to XML output */
+static inline void
+pdf_xml_copy(stream *s, const char *data)
+{
+ stream_puts(s, data);
+}
+
+/* -------------------------------------------- */
+
+static int
+pdf_xmp_time(char *buf, int buf_length)
+{
+ /* We don't write a day time because we don't have a time zone. */
+ struct tm tms;
+ time_t t;
+ char buf1[4+1+2+1+2+1]; /* yyyy-mm-dd\0 */
+
+ time(&t);
+ tms = *localtime(&t);
+ gs_sprintf(buf1,
+ "%04d-%02d-%02d",
+ tms.tm_year + 1900, tms.tm_mon + 1, tms.tm_mday);
+ strncpy(buf, buf1, buf_length);
+ return strlen(buf);
+}
+
+static int
+pdf_xmp_convert_time(char *dt, int dtl, char *buf, int bufl)
+{ /* The 'dt' buffer is of same size as 'buf'. */
+ /* Input sample : D:199812231952?08'00' */
+ /* Output sample : 1997-07-16T19:20:30+01:00 */
+ int l = dtl;
+
+ if (l > bufl)
+ l = bufl;
+ if (dt[0] == 'D' && dt[1] == ':') {
+ l -= 2;
+ memcpy(buf, dt + 2, l);
+ } else
+ memcpy(buf, dt, l);
+ memcpy(dt, buf, 4); /* year */
+ if (l <= 4)
+ return 4;
+
+ dt[4] = '-';
+ memcpy(dt + 5, buf + 4, 2); /* month */
+ if (l <= 6)
+ return 7;
+
+ dt[7] = '-';
+ memcpy(dt + 8, buf + 6, 2); /* day */
+ if (l <= 8)
+ return 10;
+
+ dt[10] = 'T';
+ memcpy(dt + 11, buf + 8, 2); /* hour */
+ dt[13] = ':';
+ memcpy(dt + 14, buf + 10, 2); /* minute */
+ if (l <= 12) {
+ dt[16] = 'Z'; /* Default time zone 0. */
+ return 17;
+ }
+
+ dt[16] = ':';
+ memcpy(dt + 17, buf + 12, 2); /* second */
+ if (l <= 14) {
+ dt[18] = 'Z'; /* Default time zone 0. */
+ return 19;
+ }
+
+ dt[19] = buf[14]; /* designator */
+ if (dt[19] == 'Z')
+ return 20;
+ if (l <= 15)
+ return 20;
+ memcpy(dt + 20, buf + 15, 2); /* Time zone hour difference. */
+ if (l <= 17)
+ return 22;
+
+ dt[22] = ':';
+ /* Skipping '\'' in 'buf'. */
+ memcpy(dt + 23, buf + 18, 2); /* Time zone hour difference. */
+ return 25;
+}
+
+int
+pdf_get_docinfo_item(gx_device_pdf *pdev, const char *key, char *buf, int buf_length)
+{
+ const cos_value_t *v = cos_dict_find(pdev->Info, (const byte *)key, strlen(key));
+ int l;
+ const byte *s;
+
+ if (v != NULL && (v->value_type == COS_VALUE_SCALAR ||
+ v->value_type == COS_VALUE_CONST)) {
+ if (v->contents.chars.size > 2 && v->contents.chars.data[0] == '(') {
+ s = v->contents.chars.data + 1;
+ l = v->contents.chars.size - 2;
+ } else {
+ s = v->contents.chars.data;
+ l = v->contents.chars.size;
+ }
+ } else
+ return 0;
+ if (l < 0)
+ l = 0;
+ if (l > buf_length)
+ l = buf_length;
+ memcpy(buf, s, l);
+ return l;
+}
+
+static inline byte
+decode_escape(const byte *data, int data_length, int *index)
+{
+ byte c;
+
+ (*index)++; /* skip '\' */
+ if (*index >= data_length)
+ return 0; /* Must_not_happen, because the string is PS encoded. */
+ c = data[*index];
+ switch (c) {
+ case '(': return '(';
+ case ')': return ')';
+ case '\\': return '\\';
+ case 'n': return '\n';
+ case 'r': return '\r';
+ case 't': return '\t';
+ case 'b': return '\b';
+ case 'f': return '\f';
+ default:
+ break;
+ }
+ if (c >= '0' && c <= '7') {
+ int oct_loop;
+ /* octal */
+ byte v = c - '0';
+
+ /* Octal values should always be three digits, one is consumed above! */
+ for (oct_loop = 0;oct_loop < 2; oct_loop++) {
+ (*index)++;
+ if (*index >= data_length)
+ /* Ran out of data, return what we found */
+ return v;
+ c = data[*index];
+ if (c < '0' || c > '7') {
+ /* Ran out of numeric data, return what we found */
+ /* Need to 'unget' the non-numeric character */
+ (*index)--;
+ break;
+ }
+ v = v * 8 + (c - '0');
+ }
+ return v;
+ }
+ return c; /* A wrong escapement sequence. */
+}
+
+static int
+pdf_xmp_write_translated(gx_device_pdf *pdev, stream *s, const byte *data, int data_length,
+ void(*write)(stream *s, const byte *data, int data_length))
+{
+ if (pdev->DSCEncodingToUnicode.data == 0) {
+ int i, j=0;
+ unsigned char *buf0;
+
+ buf0 = (unsigned char *)gs_alloc_bytes(pdev->memory, data_length * sizeof(unsigned char),
+ "pdf_xmp_write_translated");
+ if (buf0 == NULL)
+ return_error(gs_error_VMerror);
+ for (i = 0; i < data_length; i++) {
+ byte c = data[i];
+
+ if (c == '\\')
+ c = decode_escape(data, data_length, &i);
+ buf0[j] = c;
+ j++;
+ }
+ if (buf0[0] != 0xfe || buf0[1] != 0xff) {
+ unsigned char *buf1;
+ /* We must assume that the information is PDFDocEncoding. In this case
+ * we need to convert it into UTF-8. If we just convert it to UTF-16
+ * then we can safely fall through to the code below.
+ */
+ /* NB the code below skips the BOM in positions 0 and 1, so we need
+ * two extra bytes, to be ignored.
+ */
+ buf1 = (unsigned char *)gs_alloc_bytes(pdev->memory, (j * sizeof(UTF16)) + 2,
+ "pdf_xmp_write_translated");
+ if (buf1 == NULL) {
+ gs_free_object(pdev->memory, buf0, "pdf_xmp_write_translated");
+ return_error(gs_error_VMerror);
+ }
+ memset(buf1, 0x00, (j * sizeof(UTF16)) + 2);
+ for (i = 0; i < j; i++) {
+ if (buf0[i] <= 0x7f || buf0[i] >= 0xAE) {
+ if (buf0[i] == 0x7f) {
+ emprintf1(pdev->memory, "PDFDocEncoding %x cannot be represented in Unicode\n",
+ buf0[i]);
+ } else
+ buf1[(i * 2) + 3] = buf0[i];
+ } else {
+ buf1[(i * 2) + 2] = PDFDocEncodingLookup[(buf0[i] - 0x80) * 2];
+ buf1[(i * 2) + 3] = PDFDocEncodingLookup[((buf0[i] - 0x80) * 2) + 1];
+ if (PDFDocEncodingLookup[((buf0[i] - 0x80) * 2) + 1] == 0x00)
+ emprintf1(pdev->memory, "PDFDocEncoding %x cannot be represented in Unicode\n",
+ PDFDocEncodingLookup[((buf0[i] - 0x80) * 2) + 1]);
+ }
+ }
+ gs_free_object(pdev->memory, buf0, "pdf_xmp_write_translated");
+ buf0 = buf1;
+ data_length = j = (j * 2) + 2;
+ }
+ {
+ /* Its a Unicode (UTF-16BE) string, convert to UTF-8 */
+ UTF16 *buf0b, U16;
+ UTF8 *buf1, *buf1b;
+
+ /* A single UTF-16 (2 bytes) can end up as 4 bytes in UTF-8 */
+ buf1 = (UTF8 *)gs_alloc_bytes(pdev->memory, data_length * 2 * sizeof(unsigned char),
+ "pdf_xmp_write_translated");
+ if (buf1 == NULL) {
+ gs_free_object(pdev->memory, buf0, "pdf_xmp_write_translated");
+ return_error(gs_error_VMerror);
+ }
+ buf1b = buf1;
+ /* Skip the Byte Order Mark (0xfe 0xff) */
+ buf0b = (UTF16 *)(buf0 + 2);
+ /* ConvertUTF16to UTF8 expects a buffer of UTF16s in the local
+ * endian-ness, but the data is big-endian. In case this is a little-endian
+ * machine, process the buffer from big-endian to whatever is right for this platform.
+ */
+ for (i = 2; i < j; i+=2) {
+ U16 = (buf0[i] << 8) + buf0[i + 1];
+ *(buf0b++) = U16;
+ }
+ buf0b = (UTF16 *)(buf0 + 2);
+ switch (ConvertUTF16toUTF8((const UTF16**)&buf0b, (UTF16 *)(buf0 + j),
+ &buf1b, buf1 + (data_length * 2 * sizeof(unsigned char)), strictConversion)) {
+ case conversionOK:
+ write(s, buf1, buf1b - buf1);
+ gs_free_object(pdev->memory, buf1, "pdf_xmp_write_translated");
+ break;
+ case sourceExhausted:
+ case targetExhausted:
+ case sourceIllegal:
+ default:
+ gs_free_object(pdev->memory, buf0, "pdf_xmp_write_translated");
+ gs_free_object(pdev->memory, buf1, "pdf_xmp_write_translated");
+ return_error(gs_error_rangecheck);
+ }
+ }
+ gs_free_object(pdev->memory, buf0, "pdf_xmp_write_translated");
+ return 0;
+ } else {
+ UTF16 *buf0;
+ const UTF16 *buf0b;
+ UTF8 *buf1, *buf1b;
+ int i, j = 0;
+
+ buf0 = (UTF16 *)gs_alloc_bytes(pdev->memory, data_length * sizeof(UTF16),
+ "pdf_xmp_write_translated");
+ if (buf0 == NULL)
+ return_error(gs_error_VMerror);
+ buf1 = (UTF8 *)gs_alloc_bytes(pdev->memory, data_length * 2,
+ "pdf_xmp_write_translated");
+ if (buf1 == NULL) {
+ gs_free_object(pdev->memory, buf0, "pdf_xmp_write_translated");
+ return_error(gs_error_VMerror);
+ }
+ buf0b = buf0;
+ buf1b = buf1;
+ for (i = 0; i < data_length; i++) {
+ byte c = data[i];
+ int v;
+
+ if (c == '\\')
+ c = decode_escape(data, data_length, &i);
+ if (c > pdev->DSCEncodingToUnicode.size) {
+ gs_free_object(pdev->memory, buf0, "pdf_xmp_write_translated");
+ gs_free_object(pdev->memory, buf1, "pdf_xmp_write_translated");
+ return_error(gs_error_rangecheck);
+ }
+
+ v = pdev->DSCEncodingToUnicode.data[c];
+ if (v == -1)
+ v = '?'; /* Arbitrary. */
+ buf0[j] = v;
+ j++;
+ }
+ switch (ConvertUTF16toUTF8(&buf0b, buf0 + j,
+ &buf1b, buf1 + data_length * 2, strictConversion)) {
+ case conversionOK:
+ write(s, buf1, buf1b - buf1);
+ break;
+ case sourceExhausted:
+ case targetExhausted:
+ case sourceIllegal:
+ default:
+ gs_free_object(pdev->memory, buf0, "pdf_xmp_write_translated");
+ gs_free_object(pdev->memory, buf1, "pdf_xmp_write_translated");
+ return_error(gs_error_rangecheck);
+ }
+ gs_free_object(pdev->memory, buf0, "pdf_xmp_write_translated");
+ gs_free_object(pdev->memory, buf1, "pdf_xmp_write_translated");
+ return 0;
+ }
+}
+
+static int
+pdf_xmp_write_docinfo_item(gx_device_pdf *pdev, stream *s, const char *key, const char *default_value,
+ void(*write)(stream *s, const byte *data, int data_length))
+{
+ const cos_value_t *v = cos_dict_find(pdev->Info, (const byte *)key, strlen(key));
+
+ if (v != NULL && (v->value_type == COS_VALUE_SCALAR ||
+ v->value_type == COS_VALUE_CONST)) {
+ if (v->contents.chars.size >= 2 && v->contents.chars.data[0] == '(')
+ return pdf_xmp_write_translated(pdev, s, v->contents.chars.data + 1,
+ v->contents.chars.size - 2, write);
+ else
+ return pdf_xmp_write_translated(pdev, s, v->contents.chars.data,
+ v->contents.chars.size, write);
+ } else {
+ stream_puts(s, default_value);
+ return 0;
+ }
+}
+
+static uint64_t
+pdf_uuid_time(gx_device_pdf *pdev)
+{
+ long *dt = pdev->uuid_time; /* In seconds since Jan. 1, 1980 and fraction in nanoseconds. */
+ uint64_t t;
+
+ /* UUIDs use time in 100ns ticks since Oct 15, 1582. */
+ t = (uint64_t)10000000 * dt[0] + dt[0] / 100; /* since Jan. 1, 1980 */
+ t += (uint64_t) (1000*1000*10) /* seconds */
+ * (uint64_t) (60 * 60 * 24) /* days */
+ * (uint64_t) (17+30+31+365*397+99); /* # of days */
+ return t;
+}
+
+static void writehex(char **p, ulong v, int l)
+{
+ int i = l * 2;
+ static const char digit[] = "0123456789abcdef";
+
+ for (; i--;)
+ *((*p)++) = digit[v >> (i * 4) & 15];
+}
+
+static void
+pdf_make_uuid(const byte node[6], uint64_t uuid_time, ulong time_seq, char *buf, int buf_length)
+{
+ char b[45], *p = b;
+ ulong uuid_time_lo = (ulong)(uuid_time & 0xFFFFFFFF); /* MSVC 7.1.3088 */
+ ushort uuid_time_md = (ushort)((uuid_time >> 32) & 0xFFFF); /* cannot compile this */
+ ushort uuid_time_hi = (ushort)((uuid_time >> 48) & 0x0FFF); /* as function arguments. */
+
+ writehex(&p, uuid_time_lo, 4); /* time_low */
+ *(p++) = '-';
+ writehex(&p, uuid_time_md, 2); /* time_mid */
+ *(p++) = '-';
+ writehex(&p, uuid_time_hi | (ushort)(1 << 12), 2); /* time_hi_and_version */
+ *(p++) = '-';
+ writehex(&p, (time_seq & 0x3F00) >> 8, 1); /* clock_seq_hi_and_reserved */
+ writehex(&p, time_seq & 0xFF, 1); /* clock_seq & 0xFF */
+ *(p++) = '-';
+ writehex(&p, node[0], 1);
+ writehex(&p, node[1], 1);
+ writehex(&p, node[2], 1);
+ writehex(&p, node[3], 1);
+ writehex(&p, node[4], 1);
+ writehex(&p, node[5], 1);
+ *p = 0;
+ strncpy(buf, b, buf_length);
+}
+
+static int
+pdf_make_instance_uuid(gx_device_pdf *pdev, const byte digest[6], char *buf, int buf_length)
+{
+ char URI_prefix[5] = "uuid:";
+
+ memcpy(buf, URI_prefix, 5);
+ if (pdev->InstanceUUID.size) {
+ int l = min(buf_length - 6, pdev->InstanceUUID.size);
+
+ memcpy(buf+5, pdev->InstanceUUID.data, l);
+ buf[l] = 0;
+ } else
+ pdf_make_uuid(digest, pdf_uuid_time(pdev), pdev->DocumentTimeSeq, buf + 5, buf_length - 5);
+ return 0;
+}
+
+static int
+pdf_make_document_uuid(gx_device_pdf *pdev, const byte digest[6], char *buf, int buf_length)
+{
+ char URI_prefix[5] = "uuid:";
+
+ memcpy(buf, URI_prefix, 5);
+ if (pdev->DocumentUUID.size) {
+ int l = min(buf_length - 6, pdev->DocumentUUID.size);
+
+ memcpy(buf+5, pdev->DocumentUUID.data, l);
+ buf[l] = 0;
+ } else
+ pdf_make_uuid(digest, pdf_uuid_time(pdev), pdev->DocumentTimeSeq, buf+5, buf_length - 5);
+ return 0;
+}
+
+static const char dd[]={'\'', '\357', '\273', '\277', '\'', 0};
+
+/* -------------------------------------------- */
+
+/* Write Document metadata */
+static int
+pdf_write_document_metadata(gx_device_pdf *pdev, const byte digest[6])
+{
+ char instance_uuid[45], document_uuid[45], cre_date_time[40], mod_date_time[40], date_time_buf[40];
+ int cre_date_time_len, mod_date_time_len;
+ int code;
+ stream *s = pdev->strm;
+
+ code = pdf_make_instance_uuid(pdev, digest, instance_uuid, sizeof(instance_uuid));
+ if (code < 0)
+ return code;
+ code = pdf_make_document_uuid(pdev, digest, document_uuid, sizeof(document_uuid));
+ if (code < 0)
+ return code;
+
+ /* PDF/A XMP reference recommends setting UUID to empty. If not empty must be a URI */
+ if (pdev->PDFA != 0)
+ instance_uuid[0] = 0x00;
+
+ cre_date_time_len = pdf_get_docinfo_item(pdev, "/CreationDate", cre_date_time, sizeof(cre_date_time));
+ if (!cre_date_time_len)
+ cre_date_time_len = pdf_xmp_time(cre_date_time, sizeof(cre_date_time));
+ else
+ cre_date_time_len = pdf_xmp_convert_time(cre_date_time, cre_date_time_len, date_time_buf, sizeof(date_time_buf));
+ mod_date_time_len = pdf_get_docinfo_item(pdev, "/ModDate", mod_date_time, sizeof(mod_date_time));
+ if (!mod_date_time_len)
+ mod_date_time_len = pdf_xmp_time(mod_date_time, sizeof(mod_date_time));
+ else
+ mod_date_time_len = pdf_xmp_convert_time(mod_date_time, mod_date_time_len, date_time_buf, sizeof(date_time_buf));
+
+ pdf_xml_ins_beg(s, "xpacket");
+ pdf_xml_attribute_name(s, "begin");
+ pdf_xml_copy(s, dd);
+ pdf_xml_attribute_name(s, "id");
+ pdf_xml_attribute_value(s, "W5M0MpCehiHzreSzNTczkc9d");
+ pdf_xml_ins_end(s);
+ pdf_xml_newline(s);
+
+ pdf_xml_copy(s, "<?adobe-xap-filters esc=\"CRLF\"?>\n");
+ pdf_xml_copy(s, "<x:xmpmeta xmlns:x='adobe:ns:meta/'"
+ " x:xmptk='XMP toolkit 2.9.1-13, framework 1.6'>\n");
+ {
+ pdf_xml_copy(s, "<rdf:RDF xmlns:rdf='http://www.w3.org/1999/02/22-rdf-syntax-ns#' "
+ "xmlns:iX='http://ns.adobe.com/iX/1.0/'>\n");
+ {
+
+ pdf_xml_tag_open_beg(s, "rdf:Description");
+ pdf_xml_attribute_name(s, "rdf:about");
+ pdf_xml_attribute_value(s, instance_uuid);
+ pdf_xml_attribute_name(s, "xmlns:pdf");
+ pdf_xml_attribute_value(s, "http://ns.adobe.com/pdf/1.3/");
+
+ if (cos_dict_find(pdev->Info, (const byte *)"/Keywords", 9)) {
+ pdf_xml_tag_end(s);
+ pdf_xml_tag_open_beg(s, "pdf:Producer");
+ pdf_xml_tag_end(s);
+ code = pdf_xmp_write_docinfo_item(pdev, s, "/Producer", "UnknownProducer",
+ pdf_xml_data_write);
+ if (code < 0)
+ return code;
+ pdf_xml_tag_close(s, "pdf:Producer");
+ pdf_xml_newline(s);
+
+ pdf_xml_tag_open_beg(s, "pdf:Keywords");
+ pdf_xml_tag_end(s);
+ code = pdf_xmp_write_docinfo_item(pdev, s, "/Keywords", "Unknown",
+ pdf_xml_data_write);
+ if (code < 0)
+ return code;
+ pdf_xml_tag_close(s, "pdf:Keywords");
+ pdf_xml_newline(s);
+
+ pdf_xml_tag_close(s, "rdf:Description");
+ pdf_xml_newline(s);
+ } else {
+ pdf_xml_attribute_name(s, "pdf:Producer");
+ code = pdf_xmp_write_docinfo_item(pdev, s, "/Producer", "UnknownProducer",
+ pdf_xml_attribute_value_data);
+ if (code < 0)
+ return code;
+ pdf_xml_tag_end_empty(s);
+ pdf_xml_newline(s);
+ }
+
+ pdf_xml_tag_open_beg(s, "rdf:Description");
+ pdf_xml_attribute_name(s, "rdf:about");
+ pdf_xml_attribute_value(s, instance_uuid);
+ pdf_xml_attribute_name(s, "xmlns:xmp");
+ pdf_xml_attribute_value(s, "http://ns.adobe.com/xap/1.0/");
+ pdf_xml_tag_end(s);
+ {
+ pdf_xml_tag_open_beg(s, "xmp:ModifyDate");
+ pdf_xml_tag_end(s);
+ mod_date_time[mod_date_time_len] = 0x00;
+ pdf_xml_copy(s, mod_date_time);
+ pdf_xml_tag_close(s, "xmp:ModifyDate");
+ pdf_xml_newline(s);
+ }
+ {
+ pdf_xml_tag_open_beg(s, "xmp:CreateDate");
+ pdf_xml_tag_end(s);
+ cre_date_time[cre_date_time_len] = 0x00;
+ pdf_xml_copy(s, cre_date_time);
+ pdf_xml_tag_close(s, "xmp:CreateDate");
+ pdf_xml_newline(s);
+ }
+ {
+ pdf_xml_tag_open_beg(s, "xmp:CreatorTool");
+ pdf_xml_tag_end(s);
+ code = pdf_xmp_write_docinfo_item(pdev, s, "/Creator", "UnknownApplication",
+ pdf_xml_data_write);
+ if (code < 0)
+ return code;
+ pdf_xml_tag_close(s, "xmp:CreatorTool");
+ }
+ pdf_xml_tag_close(s, "rdf:Description");
+ pdf_xml_newline(s);
+
+ pdf_xml_tag_open_beg(s, "rdf:Description");
+ pdf_xml_attribute_name(s, "rdf:about");
+ pdf_xml_attribute_value(s, instance_uuid);
+ pdf_xml_attribute_name(s, "xmlns:xapMM");
+ pdf_xml_attribute_value(s, "http://ns.adobe.com/xap/1.0/mm/");
+ pdf_xml_attribute_name(s, "xapMM:DocumentID");
+ pdf_xml_attribute_value(s, document_uuid);
+ pdf_xml_tag_end_empty(s);
+ pdf_xml_newline(s);
+
+ pdf_xml_tag_open_beg(s, "rdf:Description");
+ pdf_xml_attribute_name(s, "rdf:about");
+ pdf_xml_attribute_value(s, instance_uuid);
+ pdf_xml_attribute_name(s, "xmlns:dc");
+ pdf_xml_attribute_value(s, "http://purl.org/dc/elements/1.1/");
+ pdf_xml_attribute_name(s, "dc:format");
+ pdf_xml_attribute_value(s,"application/pdf");
+ pdf_xml_tag_end(s);
+ {
+ pdf_xml_tag_open(s, "dc:title");
+ {
+ pdf_xml_tag_open(s, "rdf:Alt");
+ {
+ pdf_xml_tag_open_beg(s, "rdf:li");
+ pdf_xml_attribute_name(s, "xml:lang");
+ pdf_xml_attribute_value(s, "x-default");
+ pdf_xml_tag_end(s);
+ {
+ code = pdf_xmp_write_docinfo_item(pdev, s, "/Title", "Untitled",
+ pdf_xml_data_write);
+ if (code < 0)
+ return code;
+ }
+ pdf_xml_tag_close(s, "rdf:li");
+ }
+ pdf_xml_tag_close(s, "rdf:Alt");
+ }
+ pdf_xml_tag_close(s, "dc:title");
+
+ if (cos_dict_find(pdev->Info, (const byte *)"/Author", 7)) {
+ pdf_xml_tag_open(s, "dc:creator");
+ { /* According to the PDF/A specification
+ "it shall be represented by an ordered Text array of
+ length one whose single entry shall consist
+ of one or more names". */
+ pdf_xml_tag_open(s, "rdf:Seq");
+ {
+ pdf_xml_tag_open(s, "rdf:li");
+ {
+ code = pdf_xmp_write_docinfo_item(pdev, s, "/Author", "Unknown",
+ pdf_xml_data_write);
+ if (code < 0)
+ return code;
+ }
+ pdf_xml_tag_close(s, "rdf:li");
+ }
+ pdf_xml_tag_close(s, "rdf:Seq");
+ }
+ pdf_xml_tag_close(s, "dc:creator");
+ }
+ if (cos_dict_find(pdev->Info, (const byte *)"/Subject", 8)) {
+ pdf_xml_tag_open(s, "dc:description");
+ {
+ pdf_xml_tag_open(s, "rdf:Alt");
+ {
+ pdf_xml_tag_open_beg(s, "rdf:li");
+ pdf_xml_attribute_name(s, "xml:lang");
+ pdf_xml_attribute_value(s, "x-default");
+ pdf_xml_tag_end(s);
+ {
+ code = pdf_xmp_write_docinfo_item(pdev, s, "/Subject", "No Subject",
+ pdf_xml_data_write);
+ if (code < 0)
+ return code;
+ }
+ pdf_xml_tag_close(s, "rdf:li");
+ }
+ pdf_xml_tag_close(s, "rdf:Alt");
+ }
+ pdf_xml_tag_close(s, "dc:description");
+ }
+ }
+ pdf_xml_tag_close(s, "rdf:Description");
+ pdf_xml_newline(s);
+ if (pdev->PDFA != 0) {
+ pdf_xml_tag_open_beg(s, "rdf:Description");
+ pdf_xml_attribute_name(s, "rdf:about");
+ pdf_xml_attribute_value(s, instance_uuid);
+ pdf_xml_attribute_name(s, "xmlns:pdfaid");
+ pdf_xml_attribute_value(s, "http://www.aiim.org/pdfa/ns/id/");
+ pdf_xml_attribute_name(s, "pdfaid:part");
+ if (pdev->PDFA == 1)
+ pdf_xml_attribute_value(s,"1");
+ else
+ pdf_xml_attribute_value(s,"2");
+ pdf_xml_attribute_name(s, "pdfaid:conformance");
+ pdf_xml_attribute_value(s,"B");
+ pdf_xml_tag_end_empty(s);
+ }
+ }
+ pdf_xml_copy(s, "</rdf:RDF>\n");
+ }
+ pdf_xml_copy(s, "</x:xmpmeta>\n");
+
+ pdf_xml_copy(s, " \n");
+ pdf_xml_copy(s, " \n");
+ pdf_xml_copy(s, "<?xpacket end='w'?>");
+ return 0;
+}
+
+int
+pdf_document_metadata(gx_device_pdf *pdev)
+{
+ if (pdev->CompatibilityLevel < 1.4)
+ return 0;
+ if (pdev->ParseDSCCommentsForDocInfo || pdev->PreserveEPSInfo) {
+ pdf_resource_t *pres;
+ char buf[20];
+ byte digest[6] = {0,0,0,0,0,0};
+ int code;
+ int options = DATA_STREAM_NOT_BINARY;
+
+ sflush(pdev->strm);
+ s_MD5C_get_digest(pdev->strm, digest, sizeof(digest));
+ if (pdev->EncryptMetadata)
+ options |= DATA_STREAM_ENCRYPT;
+ code = pdf_open_aside(pdev, resourceMetadata, gs_no_id, &pres, true, options);
+ if (code < 0)
+ return code;
+ code = cos_dict_put_c_key_string((cos_dict_t *)pres->object, "/Type", (const byte *)"/Metadata", 9);
+ if (code < 0)
+ return code;
+ code = cos_dict_put_c_key_string((cos_dict_t *)pres->object, "/Subtype", (const byte *)"/XML", 4);
+ if (code < 0)
+ return code;
+ code = pdf_write_document_metadata(pdev, digest);
+ if (code < 0)
+ return code;
+ code = pdf_close_aside(pdev);
+ if (code < 0)
+ return code;
+ code = COS_WRITE_OBJECT(pres->object, pdev, resourceNone);
+ if (code < 0)
+ return code;
+ gs_sprintf(buf, "%ld 0 R", pres->object->id);
+ pdf_record_usage(pdev, pres->object->id, resource_usage_part9_structure);
+
+ code = cos_dict_put_c_key_object(pdev->Catalog, "/Metadata", pres->object);
+ if (code < 0)
+ return code;
+ }
+ return 0;
+}
+
+/* -------------------------------------------- */
diff --git a/devices/vector/gdevpdfg.c b/devices/vector/gdevpdfg.c
new file mode 100644
index 000000000..768605ced
--- /dev/null
+++ b/devices/vector/gdevpdfg.c
@@ -0,0 +1,3101 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Graphics state management for pdfwrite driver */
+#include "math_.h"
+#include "string_.h"
+#include "memory_.h"
+#include "gx.h"
+#include "gserrors.h"
+#include "gsfunc0.h"
+#include "gsstate.h"
+#include "gxbitmap.h" /* for gxhttile.h in gzht.h */
+#include "gxdht.h"
+#include "gxfarith.h" /* for gs_sin/cos_degrees */
+#include "gxfmap.h"
+#include "gxht.h"
+#include "gxistate.h"
+#include "gxdcolor.h"
+#include "gxpcolor.h"
+#include "gsptype2.h"
+#include "gzht.h"
+#include "gdevpdfx.h"
+#include "gdevpdfg.h"
+#include "gdevpdfo.h"
+#include "gscspace.h"
+#include "gsicc_manage.h"
+#include "gsicc_cache.h"
+#include "gsccolor.h"
+#include "gxcdevn.h"
+#include "gscie.h"
+
+/* ------ Exported by gdevpdfc.c for gdevpdfg.c ------ */
+int pdf_make_sampled_base_space_function(gx_device_pdf *pdev, gs_function_t **pfn,
+ int nSrcComp, int nDstComp, byte *data);
+int pdf_delete_sampled_base_space_function(gx_device_pdf *pdev, gs_function_t *pfn);
+int pdf_make_base_space_function(gx_device_pdf *pdev, gs_function_t **pfn,
+ int ncomp, float *data_low, float *data_high);
+int pdf_delete_base_space_function(gx_device_pdf *pdev, gs_function_t *pfn);
+
+/* ---------------- Miscellaneous ---------------- */
+
+/* Save the viewer's graphic state. */
+int
+pdf_save_viewer_state(gx_device_pdf *pdev, stream *s)
+{
+ const int i = pdev->vgstack_depth;
+
+ if (pdev->vgstack_depth >= pdev->vgstack_size) {
+ pdf_viewer_state *new_vgstack = (pdf_viewer_state *)gs_alloc_bytes(pdev->pdf_memory,
+ (pdev->vgstack_size + 5) * sizeof(pdf_viewer_state), "increase graphics state stack size");
+ if (new_vgstack == 0)
+ return_error(gs_error_VMerror);
+ memset(new_vgstack, 0x00, (pdev->vgstack_size + 5) * sizeof(pdf_viewer_state));
+ memcpy(new_vgstack, pdev->vgstack, pdev->vgstack_size * sizeof(pdf_viewer_state));
+ gs_free_object(pdev->pdf_memory, pdev->vgstack, "resize graphics state stack, free old stack)");
+ pdev->vgstack = new_vgstack;
+ pdev->vgstack_size += 5;
+ }
+
+ pdev->vgstack[i].transfer_ids[0] = pdev->transfer_ids[0];
+ pdev->vgstack[i].transfer_ids[1] = pdev->transfer_ids[1];
+ pdev->vgstack[i].transfer_ids[2] = pdev->transfer_ids[2];
+ pdev->vgstack[i].transfer_ids[3] = pdev->transfer_ids[3];
+ pdev->vgstack[i].transfer_not_identity = pdev->transfer_not_identity;
+ pdev->vgstack[i].opacity_alpha = pdev->state.opacity.alpha;
+ pdev->vgstack[i].shape_alpha = pdev->state.shape.alpha;
+ pdev->vgstack[i].blend_mode = pdev->state.blend_mode;
+ pdev->vgstack[i].halftone_id = pdev->halftone_id;
+ pdev->vgstack[i].black_generation_id = pdev->black_generation_id;
+ pdev->vgstack[i].undercolor_removal_id = pdev->undercolor_removal_id;
+ pdev->vgstack[i].overprint_mode = pdev->overprint_mode;
+ pdev->vgstack[i].smoothness = pdev->state.smoothness;
+ pdev->vgstack[i].flatness = pdev->state.flatness;
+ pdev->vgstack[i].text_knockout = pdev->state.text_knockout;
+ pdev->vgstack[i].fill_overprint = pdev->fill_overprint;
+ pdev->vgstack[i].stroke_overprint = pdev->stroke_overprint;
+ pdev->vgstack[i].stroke_adjust = pdev->state.stroke_adjust;
+ pdev->vgstack[i].fill_used_process_color = pdev->fill_used_process_color;
+ pdev->vgstack[i].stroke_used_process_color = pdev->stroke_used_process_color;
+ pdev->vgstack[i].saved_fill_color = pdev->saved_fill_color;
+ pdev->vgstack[i].saved_stroke_color = pdev->saved_stroke_color;
+ pdev->vgstack[i].line_params = pdev->state.line_params;
+ pdev->vgstack[i].line_params.dash.pattern = 0; /* Use pdev->dash_pattern instead. */
+ if (pdev->dash_pattern) {
+ if (pdev->vgstack[i].dash_pattern)
+ gs_free_object(pdev->memory->non_gc_memory, pdev->vgstack[i].dash_pattern, "free gstate copy dash");
+ pdev->vgstack[i].dash_pattern = (float *)gs_alloc_bytes(pdev->memory->non_gc_memory, pdev->dash_pattern_size * sizeof(float), "gstate copy dash");
+ memcpy(pdev->vgstack[i].dash_pattern, pdev->dash_pattern, pdev->dash_pattern_size * sizeof(float));
+ pdev->vgstack[i].dash_pattern_size = pdev->dash_pattern_size;
+ } else {
+ if (pdev->vgstack[i].dash_pattern) {
+ gs_free_object(pdev->memory->non_gc_memory, pdev->vgstack[i].dash_pattern, "free gstate copy dash");
+ pdev->vgstack[i].dash_pattern = 0;
+ pdev->vgstack[i].dash_pattern_size = 0;
+ }
+ }
+ pdev->vgstack_depth++;
+ if (s)
+ stream_puts(s, "q\n");
+ return 0;
+}
+
+/* Load the viewer's graphic state. */
+static void
+pdf_load_viewer_state(gx_device_pdf *pdev, pdf_viewer_state *s)
+{
+ pdev->transfer_ids[0] = s->transfer_ids[0];
+ pdev->transfer_ids[1] = s->transfer_ids[1];
+ pdev->transfer_ids[2] = s->transfer_ids[2];
+ pdev->transfer_ids[3] = s->transfer_ids[3];
+ pdev->transfer_not_identity = s->transfer_not_identity;
+ pdev->state.opacity.alpha = s->opacity_alpha;
+ pdev->state.shape.alpha = s->shape_alpha;
+ pdev->state.blend_mode = s->blend_mode;
+ pdev->halftone_id = s->halftone_id;
+ pdev->black_generation_id = s->black_generation_id;
+ pdev->undercolor_removal_id = s->undercolor_removal_id;
+ pdev->overprint_mode = s->overprint_mode;
+ pdev->state.smoothness = s->smoothness;
+ pdev->state.flatness = s->flatness;
+ pdev->state.text_knockout = s->text_knockout;
+ pdev->fill_overprint = s->fill_overprint;
+ pdev->stroke_overprint = s->stroke_overprint;
+ pdev->state.stroke_adjust = s->stroke_adjust;
+ pdev->fill_used_process_color = s->fill_used_process_color;
+ pdev->stroke_used_process_color = s->stroke_used_process_color;
+ pdev->saved_fill_color = s->saved_fill_color;
+ pdev->saved_stroke_color = s->saved_stroke_color;
+ pdev->state.line_params = s->line_params;
+ if (s->dash_pattern) {
+ if (pdev->dash_pattern)
+ gs_free_object(pdev->memory->stable_memory, pdev->dash_pattern, "vector free dash pattern");
+ pdev->dash_pattern = (float *)gs_alloc_bytes(pdev->memory->stable_memory, s->dash_pattern_size * sizeof(float), "vector allocate dash pattern");
+ pdev->dash_pattern_size = s->dash_pattern_size;
+ } else {
+ if (pdev->dash_pattern) {
+ gs_free_object(pdev->memory->stable_memory, pdev->dash_pattern, "vector free dash pattern");
+ pdev->dash_pattern = 0;
+ pdev->dash_pattern_size = 0;
+ }
+ }
+}
+
+/* Restore the viewer's graphic state. */
+int
+pdf_restore_viewer_state(gx_device_pdf *pdev, stream *s)
+{ const int i = --pdev->vgstack_depth;
+
+ if (i < pdev->vgstack_bottom || i < 0)
+ return_error(gs_error_unregistered); /* Must not happen. */
+ if (s)
+ stream_puts(s, "Q\n");
+ pdf_load_viewer_state(pdev, pdev->vgstack + i);
+ return 0;
+}
+
+/* Set initial color. */
+void
+pdf_set_initial_color(gx_device_pdf * pdev, gx_hl_saved_color *saved_fill_color,
+ gx_hl_saved_color *saved_stroke_color,
+ bool *fill_used_process_color, bool *stroke_used_process_color)
+{
+ gx_device_color black;
+
+ pdev->black = gx_device_black((gx_device *)pdev);
+ pdev->white = gx_device_white((gx_device *)pdev);
+ set_nonclient_dev_color(&black, pdev->black);
+ gx_hld_save_color(NULL, &black, saved_fill_color);
+ gx_hld_save_color(NULL, &black, saved_stroke_color);
+ *fill_used_process_color = true;
+ *stroke_used_process_color = true;
+}
+
+/* Prepare intitial values for viewer's graphics state parameters. */
+static void
+pdf_viewer_state_from_imager_state_aux(pdf_viewer_state *pvs, const gs_imager_state *pis)
+{
+ pvs->transfer_not_identity =
+ (pis->set_transfer.red != NULL ? pis->set_transfer.red->proc != gs_identity_transfer : 0) * 1 +
+ (pis->set_transfer.green != NULL ? pis->set_transfer.green->proc != gs_identity_transfer : 0) * 2 +
+ (pis->set_transfer.blue != NULL ? pis->set_transfer.blue->proc != gs_identity_transfer : 0) * 4 +
+ (pis->set_transfer.gray != NULL ? pis->set_transfer.gray->proc != gs_identity_transfer : 0) * 8;
+ pvs->transfer_ids[0] = (pis->set_transfer.red != NULL ? pis->set_transfer.red->id : 0);
+ pvs->transfer_ids[1] = (pis->set_transfer.green != NULL ? pis->set_transfer.green->id : 0);
+ pvs->transfer_ids[2] = (pis->set_transfer.blue != NULL ? pis->set_transfer.blue->id : 0);
+ pvs->transfer_ids[3] = (pis->set_transfer.gray != NULL ? pis->set_transfer.gray->id : 0);
+ pvs->opacity_alpha = pis->opacity.alpha;
+ pvs->shape_alpha = pis->shape.alpha;
+ pvs->blend_mode = pis->blend_mode;
+ pvs->halftone_id = (pis->dev_ht != 0 ? pis->dev_ht->id : 0);
+ pvs->black_generation_id = (pis->black_generation != 0 ? pis->black_generation->id : 0);
+ pvs->undercolor_removal_id = (pis->undercolor_removal != 0 ? pis->undercolor_removal->id : 0);
+ pvs->overprint_mode = 0;
+ pvs->flatness = pis->flatness;
+ pvs->smoothness = pis->smoothness;
+ pvs->text_knockout = pis->text_knockout;
+ pvs->fill_overprint = false;
+ pvs->stroke_overprint = false;
+ pvs->stroke_adjust = false;
+ pvs->line_params.half_width = 0.5;
+ pvs->line_params.start_cap = 0;
+ pvs->line_params.end_cap = 0;
+ pvs->line_params.dash_cap = 0;
+ pvs->line_params.join = 0;
+ pvs->line_params.curve_join = 0;
+ pvs->line_params.miter_limit = 10.0;
+ pvs->line_params.miter_check = 0;
+ pvs->line_params.dot_length = pis->line_params.dot_length;
+ pvs->line_params.dot_length_absolute = pis->line_params.dot_length_absolute;
+ pvs->line_params.dot_orientation = pis->line_params.dot_orientation;
+ memset(&pvs->line_params.dash, 0 , sizeof(pvs->line_params.dash));
+ pvs->dash_pattern = 0;
+ pvs->dash_pattern_size = 0;
+}
+
+/* Copy viewer state from images state. */
+void
+pdf_viewer_state_from_imager_state(gx_device_pdf * pdev,
+ const gs_imager_state *pis, const gx_device_color *pdevc)
+{
+ pdf_viewer_state vs;
+
+ pdf_viewer_state_from_imager_state_aux(&vs, pis);
+ gx_hld_save_color(pis, pdevc, &vs.saved_fill_color);
+ gx_hld_save_color(pis, pdevc, &vs.saved_stroke_color);
+ vs.fill_used_process_color = 0;
+ vs.stroke_used_process_color = 0;
+ pdf_load_viewer_state(pdev, &vs);
+}
+
+/* Prepare intitial values for viewer's graphics state parameters. */
+void
+pdf_prepare_initial_viewer_state(gx_device_pdf * pdev, const gs_imager_state *pis)
+{
+ /* Parameter values, which are specified in PDF spec, are set here.
+ * Parameter values, which are specified in PDF spec as "installation dependent",
+ * are set here to intial values used with PS interpreter.
+ * This allows to write differences to the output file
+ * and skip initial values.
+ */
+
+ pdf_set_initial_color(pdev, &pdev->vg_initial.saved_fill_color, &pdev->vg_initial.saved_stroke_color,
+ &pdev->vg_initial.fill_used_process_color, &pdev->vg_initial.stroke_used_process_color);
+ pdf_viewer_state_from_imager_state_aux(&pdev->vg_initial, pis);
+ pdev->vg_initial_set = true;
+ /*
+ * Some parameters listed in PDF spec are missed here :
+ * text state - it is initialized per page.
+ * rendering intent - not sure why, fixme.
+ */
+}
+
+/* Reset the graphics state parameters to initial values. */
+/* Used if pdf_prepare_initial_viewer_state was not callad. */
+static void
+pdf_reset_graphics_old(gx_device_pdf * pdev)
+{
+
+ pdf_set_initial_color(pdev, &pdev->saved_fill_color, &pdev->saved_stroke_color,
+ &pdev->fill_used_process_color, &pdev->stroke_used_process_color);
+ pdev->state.flatness = -1;
+ {
+ static const gx_line_params lp_initial = {
+ gx_line_params_initial
+ };
+
+ pdev->state.line_params = lp_initial;
+ }
+ pdev->fill_overprint = false;
+ pdev->stroke_overprint = false;
+ pdev->remap_fill_color = false;
+ pdev->remap_stroke_color = false;
+ pdf_reset_text(pdev);
+}
+
+/* Reset the graphics state parameters to initial values. */
+void
+pdf_reset_graphics(gx_device_pdf * pdev)
+{
+ if (pdev->vg_initial_set)
+ pdf_load_viewer_state(pdev, &pdev->vg_initial);
+ else
+ pdf_reset_graphics_old(pdev);
+ pdf_reset_text(pdev);
+}
+
+/* Write client color. */
+static int
+pdf_write_ccolor(gx_device_pdf * pdev, const gs_imager_state * pis,
+ const gs_client_color *pcc)
+{
+ int i, n = gx_hld_get_number_color_components(pis);
+
+ pprintg1(pdev->strm, "%g", psdf_round(pcc->paint.values[0], 255, 8));
+ for (i = 1; i < n; i++) {
+ pprintg1(pdev->strm, " %g", psdf_round(pcc->paint.values[i], 255, 8));
+ }
+ return 0;
+}
+
+static inline bool
+is_cspace_allowed_in_strategy(gx_device_pdf * pdev, gs_color_space_index csi)
+{
+ if (pdev->params.ColorConversionStrategy == ccs_CMYK &&
+ csi != gs_color_space_index_DeviceCMYK &&
+ csi != gs_color_space_index_DeviceGray)
+ return false;
+ if (pdev->params.ColorConversionStrategy == ccs_sRGB &&
+ csi != gs_color_space_index_DeviceRGB &&
+ csi != gs_color_space_index_DeviceGray)
+ return false;
+ if (pdev->params.ColorConversionStrategy == ccs_RGB &&
+ csi != gs_color_space_index_DeviceRGB &&
+ csi != gs_color_space_index_DeviceGray)
+ return false;
+ if (pdev->params.ColorConversionStrategy == ccs_Gray &&
+ csi != gs_color_space_index_DeviceGray)
+ return false;
+ return true;
+}
+
+static inline bool
+is_pattern2_allowed_in_strategy(gx_device_pdf * pdev, const gx_drawing_color *pdc)
+{
+ const gs_color_space *pcs2 = gx_dc_pattern2_get_color_space(pdc);
+ gs_color_space_index csi = gs_color_space_get_index(pcs2);
+
+ return is_cspace_allowed_in_strategy(pdev, csi);
+}
+
+
+static int write_color_as_process(gx_device_pdf * pdev, const gs_imager_state * pis, const gs_color_space *pcs,
+ const gx_drawing_color *pdc, bool *used_process_color,
+ const psdf_set_color_commands_t *ppscc, gs_client_color *pcc)
+{
+ int code, i;
+ frac conc[GS_CLIENT_COLOR_MAX_COMPONENTS];
+ gs_color_space_index csi, csi2;
+ gs_color_space *pcs2 = (gs_color_space *)pcs;
+ gx_drawing_color dc;
+ int num_des_comps;
+ cmm_dev_profile_t *dev_profile;
+
+ dc.type = gx_dc_type_pure;
+ dc.colors.pure = 0;
+ csi = gs_color_space_get_index(pcs);
+
+ if (csi == gs_color_space_index_Indexed ||
+ csi == gs_color_space_index_DeviceN ||
+ csi == gs_color_space_index_Separation) {
+ const char *command = NULL;
+
+ *used_process_color = true;
+
+ memset (&conc, 0x00, sizeof(frac) * GS_CLIENT_COLOR_MAX_COMPONENTS);
+ pcs->type->concretize_color(pcc, pcs, conc, pis, (gx_device *)pdev);
+
+ do{
+ pcs2 = pcs2->base_space;
+ csi2 = gs_color_space_get_index(pcs2);
+ } while(csi2 != gs_color_space_index_ICC && pcs2->base_space);
+ csi2 = gs_color_space_get_index(pcs2);
+
+ switch (csi2) {
+ case gs_color_space_index_DeviceGray:
+ case gs_color_space_index_DeviceRGB:
+ case gs_color_space_index_DeviceCMYK:
+ switch (pdev->color_info.num_components) {
+ case 1:
+ command = ppscc->setgray;
+ break;
+ case 3:
+ command = ppscc->setrgbcolor;
+ break;
+ case 4:
+ command = ppscc->setcmykcolor;
+ break;
+ default:
+ /* Can't happen since we already check the colour space */
+ return gs_error_rangecheck;
+ }
+ pprintg1(pdev->strm, "%g", psdf_round(frac2float(conc[0]), 255, 8));
+ for (i = 1; i < pdev->color_info.num_components; i++) {
+ pprintg1(pdev->strm, " %g", psdf_round(frac2float(conc[i]), 255, 8));
+ }
+ pprints1(pdev->strm, " %s\n", command);
+ return 0;
+ break;
+ case gs_color_space_index_CIEDEFG:
+ case gs_color_space_index_CIEDEF:
+ case gs_color_space_index_CIEABC:
+ case gs_color_space_index_CIEA:
+ case gs_color_space_index_ICC:
+ code = dev_proc((gx_device *)pdev, get_profile)((gx_device *)pdev, &dev_profile);
+ if (code < 0)
+ return code;
+ num_des_comps = gsicc_get_device_profile_comps(dev_profile);
+ for (i = 0;i < num_des_comps;i++)
+ dc.colors.pure = (dc.colors.pure << 8) + (int)(frac2float(conc[i]) * 255);
+ code = psdf_set_color((gx_device_vector *)pdev, &dc, ppscc, pdev->UseOldColor);
+ return code;
+ break;
+ default: /* can't happen, simply silences compiler warnings */
+ break;
+ }
+ pcs = pcs2;
+ } else {
+ if (csi >= gs_color_space_index_CIEDEFG &&
+ csi <= gs_color_space_index_CIEA) {
+ memset (&conc, 0x00, sizeof(frac) * GS_CLIENT_COLOR_MAX_COMPONENTS);
+ pcs->type->concretize_color(pcc, pcs, conc, pis, (gx_device *)pdev);
+ code = dev_proc((gx_device *)pdev, get_profile)((gx_device *)pdev, &dev_profile);
+ if (code < 0)
+ return code;
+ num_des_comps = gsicc_get_device_profile_comps(dev_profile);
+ for (i = 0;i < num_des_comps;i++)
+ dc.colors.pure = (dc.colors.pure << 8) + (int)(frac2float(conc[i]) * 255);
+ code = psdf_set_color((gx_device_vector *)pdev, &dc, ppscc, pdev->UseOldColor);
+ *used_process_color = true;
+ return code;
+ } else {
+ memset (&conc, 0x00, sizeof(frac) * GS_CLIENT_COLOR_MAX_COMPONENTS);
+ /* Special case handling for Lab spaces */
+ if (pcs->cmm_icc_profile_data->data_cs == gsCIELAB || pcs->cmm_icc_profile_data->islab) {
+ gs_client_color cc;
+ /* Get the data in a form that is concrete for the CMM */
+ cc.paint.values[0] = pcc->paint.values[0] / 100.0;
+ cc.paint.values[1] = (pcc->paint.values[1]+128)/255.0;
+ cc.paint.values[2] = (pcc->paint.values[2]+128)/255.0;
+ pcs->type->concretize_color((const gs_client_color *)&cc, pcs, conc, pis, (gx_device *)pdev);
+ } else
+ pcs->type->concretize_color(pcc, pcs, conc, pis, (gx_device *)pdev);
+ code = dev_proc((gx_device *)pdev, get_profile)((gx_device *)pdev, &dev_profile);
+ if (code < 0)
+ return code;
+ num_des_comps = gsicc_get_device_profile_comps(dev_profile);
+ for (i = 0;i < num_des_comps;i++)
+ dc.colors.pure = (dc.colors.pure << 8) + (int)(frac2float(conc[i]) * 255);
+ code = psdf_set_color((gx_device_vector *)pdev, &dc, ppscc, pdev->UseOldColor);
+ return code;
+ }
+ }
+/* rendering_params.black_point_comp = pis->blackptcomp;
+ rendering_params.graphics_type_tag = pdev->graphics_type_tag;
+ rendering_params.override_icc = false;
+ rendering_params.preserve_black = gsBKPRESNOTSPECIFIED;
+ rendering_params.rendering_intent = pis->renderingintent;
+ rendering_params.cmm = gsCMM_DEFAULT;
+ icc_link = gsicc_get_link(pis, (gx_device *)pdev, pcs,
+ NULL, &rendering_params,
+ pis->memory);
+ (icc_link->procs.map_color)((gx_device *)pdev, icc_link, Source, Converted, 2);
+ gsicc_release_link(icc_link);
+ ((gx_drawing_color *)pdc)->colors.pure = 0;
+ for (i = 0;i < pdev->color_info.num_components;i++)
+ dc.colors.pure = (dc.colors.pure << 8) + (Converted[i] / 256);
+ code = psdf_set_color((gx_device_vector *)pdev, &dc, ppscc, pdev->UseOldColor);
+ if (code < 0)
+ return code;
+ *used_process_color = true;
+ return 0;*/
+ return gs_error_unknownerror;
+}
+
+static int write_color_unchanged(gx_device_pdf * pdev, const gs_imager_state * pis,
+ gs_client_color *pcc, gx_hl_saved_color *current,
+ gx_hl_saved_color * psc, const psdf_set_color_commands_t *ppscc,
+ bool *used_process_color, const gs_color_space *pcs,
+ const gx_drawing_color *pdc)
+{
+ gs_color_space_index csi, csi2;
+ int code;
+ const char *command = NULL;
+ gs_range_t *ranges = 0;
+
+ csi = csi2 = gs_color_space_get_index(pcs);
+ if (csi == gs_color_space_index_ICC) {
+ csi2 = gsicc_get_default_type(pcs->cmm_icc_profile_data);
+ }
+
+ switch (csi2) {
+ case gs_color_space_index_DeviceGray:
+ command = ppscc->setgray;
+ code = pdf_write_ccolor(pdev, pis, pcc);
+ if (code < 0)
+ return code;
+ pprints1(pdev->strm, " %s\n", command);
+ break;
+ case gs_color_space_index_DeviceRGB:
+ command = ppscc->setrgbcolor;
+ code = pdf_write_ccolor(pdev, pis, pcc);
+ if (code < 0)
+ return code;
+ pprints1(pdev->strm, " %s\n", command);
+ break;
+ case gs_color_space_index_DeviceCMYK:
+ command = ppscc->setcmykcolor;
+ code = pdf_write_ccolor(pdev, pis, pcc);
+ if (code < 0)
+ return code;
+ pprints1(pdev->strm, " %s\n", command);
+ break;
+ default:
+ csi = gs_color_space_get_index(pcs);
+ if (!gx_hld_saved_color_same_cspace(current, psc) || (csi2 >= gs_color_space_index_CIEDEFG && csi2 <= gs_color_space_index_CIEA)) {
+ cos_value_t cs_value;
+
+ code = pdf_color_space_named(pdev, pis, &cs_value, (const gs_range_t **)&ranges, pcs,
+ &pdf_color_space_names, true, NULL, 0, false);
+ /* fixme : creates redundant PDF objects. */
+ if (code == gs_error_rangecheck) {
+ *used_process_color = true;
+ if (pdev->ForOPDFRead) {
+ int save = 0;
+ /* The color space can't write to PDF. This should never happen */
+ save = pdev->UseOldColor;
+ pdev->UseOldColor = 1;
+ code = psdf_set_color((gx_device_vector *)pdev, pdc, ppscc, pdev->UseOldColor);
+ pdev->UseOldColor = save;
+ } else {
+ code = write_color_as_process(pdev, pis, pcs, pdc, used_process_color, ppscc, pcc);
+ }
+ return code;
+ }
+ if (code < 0)
+ return code;
+ code = cos_value_write(&cs_value, pdev);
+ if (code < 0)
+ return code;
+ pprints1(pdev->strm, " %s\n", ppscc->setcolorspace);
+ if (ranges && (csi2 >= gs_color_space_index_CIEDEFG && csi2 <= gs_color_space_index_CIEA)) {
+ gs_client_color dcc = *pcc;
+ switch (csi2) {
+ case gs_color_space_index_CIEDEFG:
+ rescale_cie_color(ranges, 4, pcc, &dcc);
+ break;
+ case gs_color_space_index_CIEDEF:
+ rescale_cie_color(ranges, 3, pcc, &dcc);
+ break;
+ case gs_color_space_index_CIEABC:
+ rescale_cie_color(ranges, 3, pcc, &dcc);
+ break;
+ case gs_color_space_index_CIEA:
+ rescale_cie_color(ranges, 1, pcc, &dcc);
+ break;
+ default:
+ /* can't happen but silences compiler warnings */
+ break;
+ }
+ code = pdf_write_ccolor(pdev, pis, &dcc);
+ } else {
+ code = pdf_write_ccolor(pdev, pis, pcc);
+ }
+ *used_process_color = false;
+ if (code < 0)
+ return code;
+ pprints1(pdev->strm, " %s\n", ppscc->setcolorn);
+ } else if (*used_process_color) {
+ *used_process_color = true;
+ if (pdev->ForOPDFRead) {
+ int save = 0;
+ /* The color space can't write to PDF. This should never happen */
+ save = pdev->UseOldColor;
+ pdev->UseOldColor = 1;
+ code = psdf_set_color((gx_device_vector *)pdev, pdc, ppscc, pdev->UseOldColor);
+ pdev->UseOldColor = save;
+ } else {
+ code = write_color_as_process(pdev, pis, pcs, pdc, used_process_color, ppscc, pcc);
+ }
+ return code;
+ }
+ else {
+ code = pdf_write_ccolor(pdev, pis, pcc);
+ if (code < 0)
+ return code;
+ pprints1(pdev->strm, " %s\n", ppscc->setcolorn);
+ }
+ break;
+ }
+ *used_process_color = false;
+
+ return 0;
+}
+
+static int write_color_as_process_ICC(gx_device_pdf * pdev, const gs_imager_state * pis, const gs_color_space *pcs,
+ const gx_drawing_color *pdc, gx_hl_saved_color * psc, bool *used_process_color,
+ const psdf_set_color_commands_t *ppscc, gs_client_color *pcc,
+ gx_hl_saved_color *current)
+{
+ int i, code;
+ cos_value_t cs_value;
+
+ if (!gx_hld_saved_color_same_cspace(current, psc)) {
+ code = pdf_color_space_named(pdev, pis, &cs_value, NULL, pcs,
+ &pdf_color_space_names, true, NULL, 0, true);
+ /* fixme : creates redundant PDF objects. */
+ if (code == gs_error_rangecheck) {
+ /* The color space can't write to PDF. This should never happen */
+ return write_color_as_process(pdev, pis, pcs, pdc, used_process_color, ppscc, pcc);
+ }
+ if (code < 0)
+ return code;
+ code = cos_value_write(&cs_value, pdev);
+ if (code < 0)
+ return code;
+ pprints1(pdev->strm, " %s\n", ppscc->setcolorspace);
+ *used_process_color = false;
+ pprintg1(pdev->strm, "%g", psdf_round(pcc->paint.values[0], 255, 8));
+ for (i = 1; i < pcs->type->num_components(pcs); i++) {
+ pprintg1(pdev->strm, " %g", psdf_round(pcc->paint.values[i], 255, 8));
+ }
+ pprints1(pdev->strm, " %s\n", ppscc->setcolorn);
+ } else {
+ *used_process_color = false;
+ pprintg1(pdev->strm, "%g", psdf_round(pcc->paint.values[0], 255, 8));
+ for (i = 1; i < pcs->type->num_components(pcs); i++) {
+ pprintg1(pdev->strm, " %g", psdf_round(pcc->paint.values[i], 255, 8));
+ }
+ pprints1(pdev->strm, " %s\n", ppscc->setcolorn);
+ }
+ return 0;
+}
+
+int convert_DeviceN_alternate(gx_device_pdf * pdev, const gs_imager_state * pis, const gs_color_space *pcs,
+ const gx_drawing_color *pdc, bool *used_process_color,
+ const psdf_set_color_commands_t *ppscc, gs_client_color *pcc, cos_value_t *pvalue, bool by_name)
+{
+ gs_color_space_index csi;
+ gs_function_t *new_pfn = 0;
+ int code, i, samples=0, loop;
+ cos_array_t *pca, *pca1;
+ cos_value_t v;
+ byte *data_buff;
+ pdf_resource_t *pres = NULL;
+ gs_color_space *pcs_save = NULL;
+
+ csi = gs_color_space_get_index(pcs);
+ if (csi == gs_color_space_index_Indexed) {
+ pcs_save = (gs_color_space *)pcs;
+ pcs = pcs->base_space;
+ }
+
+ pca = cos_array_alloc(pdev, "pdf_color_space");
+ if (pca == 0)
+ return_error(gs_error_VMerror);
+
+ samples = (unsigned int)pow(2, pcs->params.device_n.num_components);
+ data_buff = gs_alloc_bytes(pdev->memory, pdev->color_info.num_components * samples, "Convert DeviceN");
+ if (data_buff == 0) {
+ COS_FREE(pca, "convert DeviceN");
+ return_error(gs_error_VMerror);
+ }
+ memset(data_buff, 0x00, pdev->color_info.num_components * samples);
+
+ {
+ frac conc[GS_CLIENT_COLOR_MAX_COMPONENTS];
+ gs_client_color cc;
+ int i;
+ gs_color_space *icc_space = (gs_color_space *)pcs;
+ gs_color_space *sep_space = (gs_color_space *)pcs;
+ gs_color_space_index csi2;
+
+ csi = gs_color_space_get_index(pcs);
+ if (csi == gs_color_space_index_Indexed)
+ sep_space = pcs->base_space;
+
+ do{
+ icc_space = icc_space->base_space;
+ csi2 = gs_color_space_get_index(icc_space);
+ } while(csi2 != gs_color_space_index_ICC && icc_space->base_space);
+
+ memset(&cc.paint.values, 0x00, GS_CLIENT_COLOR_MAX_COMPONENTS);
+
+ for (loop=0;loop < samples;loop++) {
+ if (loop > 0) {
+ if (cc.paint.values[0] == 0)
+ cc.paint.values[0] = 1;
+ else {
+ int cascade = 0;
+ while (cc.paint.values[cascade] == 1 && cascade < samples) {
+ cc.paint.values[cascade++] = 0;
+ }
+ cc.paint.values[cascade] = 1;
+ }
+ }
+
+
+ memset (&conc, 0x00, sizeof(frac) * GS_CLIENT_COLOR_MAX_COMPONENTS);
+ sep_space->type->concretize_color(&cc, sep_space, conc, pis, (gx_device *)pdev);
+ for (i = 0;i < pdev->color_info.num_components;i++)
+ data_buff[(loop * pdev->color_info.num_components) + i] = (int)(frac2float(conc[i]) * 255);
+ }
+ }
+
+ switch(pdev->params.ColorConversionStrategy) {
+ case ccs_Gray:
+ code = pdf_make_sampled_base_space_function(pdev, &new_pfn, pcs->params.device_n.num_components, 1, data_buff);
+ break;
+ case ccs_sRGB:
+ case ccs_RGB:
+ code = pdf_make_sampled_base_space_function(pdev, &new_pfn, pcs->params.device_n.num_components, 3, data_buff);
+ break;
+ case ccs_CMYK:
+ code = pdf_make_sampled_base_space_function(pdev, &new_pfn, pcs->params.device_n.num_components, 4, data_buff);
+ break;
+ default:
+ code = gs_error_rangecheck;
+ break;
+ }
+ gs_free_object(pdev->memory, data_buff, "Convert DeviceN");
+ if (code < 0) {
+ COS_FREE(pca, "convert DeviceN");
+ return code;
+ }
+
+ code = cos_array_add(pca, cos_c_string_value(&v, "/DeviceN"));
+ if (code < 0) {
+ COS_FREE(pca, "pdf_color_space");
+ return code;
+ }
+
+ if (code >= 0) {
+ byte *name_string;
+ uint name_string_length;
+ cos_value_t v_attriburtes, *va = NULL;
+ cos_array_t *psna =
+ cos_array_alloc(pdev, "pdf_color_space(DeviceN)");
+
+ if (psna == 0) {
+ COS_FREE(pca, "convert DeviceN");
+ return_error(gs_error_VMerror);
+ }
+
+ for (i = 0; i < pcs->params.device_n.num_components; ++i) {
+ code = pcs->params.device_n.get_colorname_string(
+ pdev->memory,
+ pcs->params.device_n.names[i], &name_string,
+ &name_string_length);
+ if (code < 0) {
+ COS_FREE(pca, "convert DeviceN");
+ return code;
+ }
+ code = pdf_string_to_cos_name(pdev, name_string,
+ name_string_length, &v);
+ if (code < 0) {
+ COS_FREE(pca, "convert DeviceN");
+ return code;
+ }
+ code = cos_array_add(psna, &v);
+ if (code < 0) {
+ COS_FREE(pca, "convert DeviceN");
+ return code;
+ }
+ }
+ COS_OBJECT_VALUE(&v, psna);
+ code = cos_array_add(pca, &v);
+ if (code <0) {
+ COS_FREE(pca, "convert DeviceN");
+ return_error(gs_error_VMerror);
+ }
+
+ if (pcs->params.device_n.colorants != NULL) {
+ cos_dict_t *colorants = cos_dict_alloc(pdev, "pdf_color_space(DeviceN)");
+ cos_value_t v_colorants, v_separation, v_colorant_name;
+ const gs_device_n_attributes *csa;
+ pdf_resource_t *pres_attributes;
+
+ if (colorants == NULL)
+ return_error(gs_error_VMerror);
+ code = pdf_alloc_resource(pdev, resourceOther, 0, &pres_attributes, -1);
+ if (code < 0) {
+ COS_FREE(pca, "convert DeviceN");
+ return code;
+ }
+ cos_become(pres_attributes->object, cos_type_dict);
+ COS_OBJECT_VALUE(&v_colorants, colorants);
+ code = cos_dict_put((cos_dict_t *)pres_attributes->object,
+ (const byte *)"/Colorants", 10, &v_colorants);
+ if (code < 0){
+ COS_FREE(pca, "convert DeviceN");
+ return code;
+ }
+ for (csa = pcs->params.device_n.colorants; csa != NULL; csa = csa->next) {
+ code = pcs->params.device_n.get_colorname_string(pdev->memory,
+ csa->colorant_name, &name_string, &name_string_length);
+ if (code < 0) {
+ COS_FREE(pca, "convert DeviceN");
+ return code;
+ }
+ code = pdf_color_space_named(pdev, pis, &v_separation, NULL, csa->cspace, &pdf_color_space_names, false, NULL, 0, false);
+ if (code < 0) {
+ COS_FREE(pca, "convert DeviceN");
+ return code;
+ }
+ code = pdf_string_to_cos_name(pdev, name_string, name_string_length, &v_colorant_name);
+ if (code < 0) {
+ COS_FREE(pca, "convert DeviceN");
+ return code;
+ }
+ code = cos_dict_put(colorants, v_colorant_name.contents.chars.data,
+ v_colorant_name.contents.chars.size, &v_separation);
+ if (code < 0) {
+ COS_FREE(pca, "convert DeviceN");
+ return code;
+ }
+ }
+ code = pdf_substitute_resource(pdev, &pres_attributes, resourceOther, NULL, true);
+ if (code < 0) {
+ COS_FREE(pca, "convert DeviceN");
+ return code;
+ }
+ pres_attributes->where_used |= pdev->used_mask;
+ va = &v_attriburtes;
+ COS_OBJECT_VALUE(va, pres_attributes->object);
+ code = cos_array_add(pca, va);
+ if (code < 0) {
+ COS_FREE(pca, "convert DeviceN");
+ return code;
+ }
+ }
+
+ switch(pdev->params.ColorConversionStrategy) {
+ case ccs_Gray:
+ cos_c_string_value(&v, (const char *)pdf_color_space_names.DeviceGray);
+ break;
+ case ccs_sRGB:
+ case ccs_RGB:
+ cos_c_string_value(&v, (const char *)pdf_color_space_names.DeviceRGB);
+ break;
+ case ccs_CMYK:
+ cos_c_string_value(&v, (const char *)pdf_color_space_names.DeviceCMYK);
+ break;
+ default:
+ break;
+ }
+ code = cos_array_add(pca, &v);
+ if (code >= 0) {
+ code = pdf_function_scaled(pdev, new_pfn, 0x00, &v);
+ if (code >= 0)
+ code = cos_array_add(pca, &v);
+ else {
+ COS_FREE(pca, "convert DeviceN");
+ return code;
+ }
+ }
+ }
+ pdf_delete_sampled_base_space_function(pdev, new_pfn);
+
+ /*
+ * Register the color space as a resource, since it must be referenced
+ * by name rather than directly.
+ */
+ {
+ pdf_color_space_t *ppcs;
+
+ if (code < 0 ||
+ (code = pdf_alloc_resource(pdev, resourceColorSpace, pcs->id,
+ &pres, -1)) < 0
+ ) {
+ COS_FREE(pca, "pdf_color_space");
+ return code;
+ }
+ pdf_reserve_object_id(pdev, pres, 0);
+ ppcs = (pdf_color_space_t *)pres;
+ ppcs->serialized = NULL;
+ ppcs->serialized_size = 0;
+
+ ppcs->ranges = 0;
+ pca->id = pres->object->id;
+ COS_FREE(pres->object, "pdf_color_space");
+ pres->object = (cos_object_t *)pca;
+ cos_write_object(COS_OBJECT(pca), pdev, resourceColorSpace);
+ csi = gs_color_space_get_index(pcs);
+ if (pcs_save == NULL && ppscc != NULL)
+ pprints1(pdev->strm, "/%s", ppcs->rname);
+ }
+ pres->where_used |= pdev->used_mask;
+ code = pdf_add_resource(pdev, pdev->substream_Resources, "/ColorSpace", pres);
+ if (code < 0)
+ return code;
+
+ if (pcs_save != NULL) {
+ cos_value_t value;
+
+ pcs = pcs_save;
+ discard(COS_OBJECT_VALUE(&value, pca));
+ pca1 = cos_array_alloc(pdev, "pdf_color_space");
+ code = pdf_indexed_color_space(pdev, pis, &value, pcs, pca1, (cos_value_t *)&value);
+ pca = pca1;
+
+ /*
+ * Register the color space as a resource, since it must be referenced
+ * by name rather than directly.
+ */
+ {
+ pdf_color_space_t *ppcs;
+
+ if (code < 0 ||
+ (code = pdf_alloc_resource(pdev, resourceColorSpace, pcs->id,
+ &pres, -1)) < 0
+ ) {
+ COS_FREE(pca, "pdf_color_space");
+ return code;
+ }
+ pdf_reserve_object_id(pdev, pres, 0);
+ ppcs = (pdf_color_space_t *)pres;
+ ppcs->serialized = NULL;
+ ppcs->serialized_size = 0;
+
+ ppcs->ranges = 0;
+ pca->id = pres->object->id;
+ COS_FREE(pres->object, "pdf_color_space");
+ pres->object = (cos_object_t *)pca;
+ cos_write_object(COS_OBJECT(pca), pdev, resourceColorSpace);
+ if (ppscc != NULL)
+ pprints1(pdev->strm, "/%s", ppcs->rname);
+ }
+ pres->where_used |= pdev->used_mask;
+ code = pdf_add_resource(pdev, pdev->substream_Resources, "/ColorSpace", pres);
+ if (code < 0)
+ return code;
+ }
+
+ if (ppscc != NULL) {
+ pprints1(pdev->strm, " %s\n", ppscc->setcolorspace);
+ *used_process_color = false;
+ if (pcs_save == NULL) {
+ for (i = 0; i < pcs->params.device_n.num_components; ++i)
+ pprintg1(pdev->strm, "%g ", psdf_round(pcc->paint.values[i], 255, 8));
+ } else
+ pprintg1(pdev->strm, "%g ", psdf_round(pcc->paint.values[0], 255, 8));
+ pprints1(pdev->strm, "%s\n", ppscc->setcolorn);
+ }
+ if (pvalue != NULL) {
+ if (by_name) {
+ /* Return a resource name rather than an object reference. */
+ discard(COS_RESOURCE_VALUE(pvalue, pca));
+ } else
+ discard(COS_OBJECT_VALUE(pvalue, pca));
+ }
+ return 0;
+}
+
+int convert_separation_alternate(gx_device_pdf * pdev, const gs_imager_state * pis, const gs_color_space *pcs,
+ const gx_drawing_color *pdc, bool *used_process_color,
+ const psdf_set_color_commands_t *ppscc, gs_client_color *pcc, cos_value_t *pvalue, bool by_name)
+{
+ gs_color_space_index csi;
+ gs_function_t *new_pfn = 0;
+ float out_low[4];
+ float out_high[4];
+ int code;
+ cos_array_t *pca, *pca1;
+ cos_value_t v;
+ byte *name_string;
+ uint name_string_length;
+ pdf_resource_t *pres = NULL;
+
+ pca = cos_array_alloc(pdev, "pdf_color_space");
+ if (pca == 0)
+ return_error(gs_error_VMerror);
+
+ {
+ frac conc[GS_CLIENT_COLOR_MAX_COMPONENTS];
+ gs_client_color cc;
+ int i;
+ gs_color_space *icc_space = (gs_color_space *)pcs;
+ gs_color_space *sep_space = (gs_color_space *)pcs;
+ gs_color_space_index csi2;
+
+ csi = gs_color_space_get_index(pcs);
+ if (csi == gs_color_space_index_Indexed)
+ sep_space = pcs->base_space;
+
+ do{
+ icc_space = icc_space->base_space;
+ csi2 = gs_color_space_get_index(icc_space);
+ } while(csi2 != gs_color_space_index_ICC && icc_space->base_space);
+
+ memset(&cc.paint.values, 0x00, GS_CLIENT_COLOR_MAX_COMPONENTS);
+ cc.paint.values[0] = 0;
+ memset (&conc, 0x00, sizeof(frac) * GS_CLIENT_COLOR_MAX_COMPONENTS);
+ sep_space->type->concretize_color(&cc, sep_space, conc, pis, (gx_device *)pdev);
+ for (i = 0;i < pdev->color_info.num_components;i++)
+ out_low[i] = frac2float(conc[i]);
+
+ cc.paint.values[0] = 1;
+ memset (&conc, 0x00, sizeof(frac) * GS_CLIENT_COLOR_MAX_COMPONENTS);
+ sep_space->type->concretize_color(&cc, sep_space, conc, pis, (gx_device *)pdev);
+ for (i = 0;i < pdev->color_info.num_components;i++)
+ out_high[i] = frac2float(conc[i]);
+ }
+ switch(pdev->params.ColorConversionStrategy) {
+ case ccs_Gray:
+ code = pdf_make_base_space_function(pdev, &new_pfn, 1, out_low, out_high);
+ break;
+ case ccs_sRGB:
+ case ccs_RGB:
+ code = pdf_make_base_space_function(pdev, &new_pfn, 3, out_low, out_high);
+ break;
+ case ccs_CMYK:
+ code = pdf_make_base_space_function(pdev, &new_pfn, 4, out_low, out_high);
+ break;
+ default:
+ code = gs_error_rangecheck;
+ break;
+ }
+
+ if (code < 0) {
+ COS_FREE(pca, "pdf_color_space");
+ return code;
+ }
+
+ code = cos_array_add(pca, cos_c_string_value(&v, "/Separation"));
+ if (code < 0) {
+ COS_FREE(pca, "pdf_color_space");
+ return code;
+ }
+
+ if (code >= 0) {
+ csi = gs_color_space_get_index(pcs);
+ if (csi == gs_color_space_index_Indexed)
+ code = pcs->base_space->params.separation.get_colorname_string(
+ pdev->memory,
+ pcs->base_space->params.separation.sep_name, &name_string,
+ &name_string_length);
+ else
+ code = pcs->params.separation.get_colorname_string(
+ pdev->memory,
+ pcs->params.separation.sep_name, &name_string,
+ &name_string_length);
+ if (code < 0) {
+ COS_FREE(pca, "pdf_color_space");
+ return code;
+ }
+
+ code = pdf_string_to_cos_name(pdev, name_string,
+ name_string_length, &v);
+ if (code < 0) {
+ COS_FREE(pca, "pdf_color_space");
+ return code;
+ }
+
+ code = cos_array_add(pca, &v);
+ if (code < 0) {
+ COS_FREE(pca, "pdf_color_space");
+ return code;
+ }
+ if (code >= 0) {
+ switch(pdev->params.ColorConversionStrategy) {
+ case ccs_Gray:
+ cos_c_string_value(&v, (const char *)pdf_color_space_names.DeviceGray);
+ break;
+ case ccs_RGB:
+ case ccs_sRGB:
+ cos_c_string_value(&v, (const char *)pdf_color_space_names.DeviceRGB);
+ break;
+ case ccs_CMYK:
+ cos_c_string_value(&v, (const char *)pdf_color_space_names.DeviceCMYK);
+ break;
+ default:
+ break;
+ }
+ code = cos_array_add(pca, &v);
+ if (code >= 0) {
+ code = pdf_function_scaled(pdev, new_pfn, 0x00, &v);
+ if (code >= 0) {
+ code = cos_array_add(pca, &v);
+ }
+ }
+ }
+ }
+ pdf_delete_base_space_function(pdev, new_pfn);
+
+ /*
+ * Register the color space as a resource, since it must be referenced
+ * by name rather than directly.
+ */
+ {
+ pdf_color_space_t *ppcs;
+
+ if (code < 0 ||
+ (code = pdf_alloc_resource(pdev, resourceColorSpace, pcs->id,
+ &pres, -1)) < 0
+ ) {
+ COS_FREE(pca, "pdf_color_space");
+ return code;
+ }
+ pdf_reserve_object_id(pdev, pres, 0);
+ ppcs = (pdf_color_space_t *)pres;
+ ppcs->serialized = NULL;
+ ppcs->serialized_size = 0;
+
+ ppcs->ranges = 0;
+ pca->id = pres->object->id;
+ COS_FREE(pres->object, "pdf_color_space");
+ pres->object = (cos_object_t *)pca;
+ cos_write_object(COS_OBJECT(pca), pdev, resourceColorSpace);
+ csi = gs_color_space_get_index(pcs);
+ if (csi != gs_color_space_index_Indexed && ppscc != NULL)
+ pprints1(pdev->strm, "/%s", ppcs->rname);
+ }
+ pres->where_used |= pdev->used_mask;
+ code = pdf_add_resource(pdev, pdev->substream_Resources, "/ColorSpace", pres);
+ if (code < 0)
+ return code;
+
+ csi = gs_color_space_get_index(pcs);
+ if (csi == gs_color_space_index_Indexed) {
+ cos_value_t value;
+
+ discard(COS_OBJECT_VALUE(&value, pca));
+ pca1 = cos_array_alloc(pdev, "pdf_color_space");
+ code = pdf_indexed_color_space(pdev, pis, &value, pcs, pca1, (cos_value_t *)&value);
+ pca = pca1;
+
+ /*
+ * Register the color space as a resource, since it must be referenced
+ * by name rather than directly.
+ */
+ {
+ pdf_color_space_t *ppcs;
+
+ if (code < 0 ||
+ (code = pdf_alloc_resource(pdev, resourceColorSpace, pcs->id,
+ &pres, -1)) < 0
+ ) {
+ COS_FREE(pca, "pdf_color_space");
+ return code;
+ }
+ pdf_reserve_object_id(pdev, pres, 0);
+ ppcs = (pdf_color_space_t *)pres;
+ ppcs->serialized = NULL;
+ ppcs->serialized_size = 0;
+
+ ppcs->ranges = 0;
+ pca->id = pres->object->id;
+ COS_FREE(pres->object, "pdf_color_space");
+ pres->object = (cos_object_t *)pca;
+ cos_write_object(COS_OBJECT(pca), pdev, resourceColorSpace);
+ if (ppscc != NULL)
+ pprints1(pdev->strm, "/%s", ppcs->rname);
+ }
+ pres->where_used |= pdev->used_mask;
+ code = pdf_add_resource(pdev, pdev->substream_Resources, "/ColorSpace", pres);
+ if (code < 0)
+ return code;
+ }
+
+ if (ppscc != NULL) {
+ pprints1(pdev->strm, " %s\n", ppscc->setcolorspace);
+ *used_process_color = false;
+ pprintg1(pdev->strm, "%g", psdf_round(pcc->paint.values[0], 255, 8));
+ pprints1(pdev->strm, " %s\n", ppscc->setcolorn);
+ }
+ if (pvalue != NULL) {
+ if (by_name) {
+ /* Return a resource name rather than an object reference. */
+ discard(COS_RESOURCE_VALUE(pvalue, pca));
+ } else
+ discard(COS_OBJECT_VALUE(pvalue, pca));
+ }
+ return 0;
+}
+
+void
+rescale_cie_color(gs_range_t *ranges, int num_colorants,
+ const gs_client_color *src, gs_client_color *des)
+{
+ int k;
+
+ for (k = 0; k < num_colorants; k++) {
+ des->paint.values[k] =
+ (src->paint.values[k]-ranges[k].rmin)/
+ (ranges[k].rmax-ranges[k].rmin);
+ }
+}
+
+static int new_pdf_reset_color(gx_device_pdf * pdev, const gs_imager_state * pis,
+ const gx_drawing_color *pdc, gx_hl_saved_color * psc,
+ bool *used_process_color,
+ const psdf_set_color_commands_t *ppscc)
+{
+ int code=0, code1=0;
+ gx_hl_saved_color temp;
+ gs_client_color *pcc; /* fixme: not needed due to gx_hld_get_color_component. */
+ cos_value_t cs_value;
+ gs_color_space_index csi;
+ gs_color_space_index csi2;
+ const gs_color_space *pcs, *pcs2;
+
+ if (pdev->skip_colors)
+ return 0;
+
+ /* Get a copy of the current colour so we can examine it. */
+ gx_hld_save_color(pis, pdc, &temp);
+
+ /* Since pdfwrite never applies halftones and patterns, but monitors
+ * halftone/pattern IDs separately, we don't need to compare
+ * halftone/pattern bodies here.
+ */
+ if (gx_hld_saved_color_equal(&temp, psc))
+ /* New colour (and space) same as old colour, no need to do anything */
+ return 0;
+
+ /* Do we have a Pattern colour space ? */
+ switch (gx_hld_get_color_space_and_ccolor(pis, pdc, &pcs, (const gs_client_color **)&pcc)) {
+ case pattern_color_space:
+ {
+ pdf_resource_t *pres;
+
+ if (pdc->type == gx_dc_type_pattern) {
+ /* Can't handle tiling patterns in levels 1.0 and 1.1, and
+ * unlike shading patterns we have no fallback.
+ */
+ if (pdev->CompatibilityLevel < 1.2) {
+ return gs_error_undefined;
+ }
+ code = pdf_put_colored_pattern(pdev, pdc, pcs,
+ ppscc, pis, &pres);
+ }
+ else if (pdc->type == &gx_dc_pure_masked) {
+ code = pdf_put_uncolored_pattern(pdev, pdc, pcs,
+ ppscc, pis, &pres);
+ if (code < 0 || pres == 0) {
+ /* replaced a pattern with a flat fill, but we still
+ * need to change the 'saved' colour or we will
+ * get out of step with the PDF content.
+ */
+ *psc = temp;
+ return code;
+ }
+ if (pis && pis->have_pattern_streams)
+ code = pdf_write_ccolor(pdev, pis, pcc);
+ } else if (pdc->type == &gx_dc_pattern2) {
+ if (pdev->CompatibilityLevel <= 1.2)
+ return_error(gs_error_rangecheck);
+ if (!is_pattern2_allowed_in_strategy(pdev, pdc))
+ return_error(gs_error_rangecheck);
+ code1 = pdf_put_pattern2(pdev, pis, pdc, ppscc, &pres);
+ if (code1 < 0)
+ return code1;
+ } else
+ return_error(gs_error_rangecheck);
+ if (code < 0)
+ return code;
+ code = pdf_add_resource(pdev, pdev->substream_Resources, "/Pattern", pres);
+ if (code >= 0) {
+ cos_value_write(cos_resource_value(&cs_value, pres->object), pdev);
+ pprints1(pdev->strm, " %s\n", ppscc->setcolorn);
+ }
+ else {
+ pres->where_used = 0;
+ return code;
+ }
+ *used_process_color = false;
+ }
+ break;
+ case non_pattern_color_space:
+ pcs2 = pcs;
+ csi = csi2 = gs_color_space_get_index(pcs);
+ if (csi == gs_color_space_index_ICC) {
+ csi2 = gsicc_get_default_type(pcs->cmm_icc_profile_data);
+ }
+ /* Figure out what to do if we are outputting to really ancient versions of PDF */
+ /* NB ps2write sets CompatibilityLevel to 1.2 so we cater for it here */
+ if (pdev->CompatibilityLevel <= 1.2) {
+
+ /* If we have an /Indexed space, we need to look at the base space */
+ if (csi2 == gs_color_space_index_Indexed) {
+ pcs2 = pcs->base_space;
+ csi2 = gs_color_space_get_index(pcs2);
+ }
+
+ switch (csi2) {
+ case gs_color_space_index_DeviceGray:
+ if (pdev->params.ColorConversionStrategy == ccs_LeaveColorUnchanged ||
+ pdev->params.ColorConversionStrategy == ccs_Gray)
+ code = write_color_unchanged(pdev, pis, pcc, &temp, psc, ppscc, used_process_color, pcs, pdc);
+ else
+ code = write_color_as_process(pdev, pis, pcs, pdc, used_process_color, ppscc, pcc);
+ break;
+ case gs_color_space_index_DeviceRGB:
+ if (pdev->params.ColorConversionStrategy == ccs_LeaveColorUnchanged ||
+ pdev->params.ColorConversionStrategy == ccs_RGB)
+ code = write_color_unchanged(pdev, pis, pcc, &temp, psc, ppscc, used_process_color, pcs, pdc);
+ else
+ code = write_color_as_process(pdev, pis, pcs, pdc, used_process_color, ppscc, pcc);
+ break;
+ case gs_color_space_index_DeviceCMYK:
+ if (pdev->params.ColorConversionStrategy == ccs_LeaveColorUnchanged ||
+ pdev->params.ColorConversionStrategy == ccs_CMYK)
+ code = write_color_unchanged(pdev, pis, pcc, &temp, psc, ppscc, used_process_color, pcs, pdc);
+ else
+ code = write_color_as_process(pdev, pis, pcs, pdc, used_process_color, ppscc, pcc);
+ break;
+ case gs_color_space_index_CIEA:
+ case gs_color_space_index_CIEABC:
+ case gs_color_space_index_CIEDEF:
+ case gs_color_space_index_CIEDEFG:
+ if (pdev->ForOPDFRead) {
+ switch (pdev->params.ColorConversionStrategy) {
+ case ccs_ByObjectType:
+ /* Object type not implemented yet */
+ case ccs_UseDeviceDependentColor:
+ /* DeviceDependentColor deprecated */
+ case ccs_UseDeviceIndependentColorForImages:
+ /* If only correcting images, then leave unchanged */
+ case ccs_LeaveColorUnchanged:
+ code = write_color_unchanged(pdev, pis, pcc, &temp, psc, ppscc, used_process_color, pcs, pdc);
+ break;
+ default:
+ code = write_color_as_process(pdev, pis, pcs, pdc, used_process_color, ppscc, pcc);
+ break;
+ }
+ }
+ else
+ code = write_color_as_process(pdev, pis, pcs, pdc, used_process_color, ppscc, pcc);
+ break;
+ case gs_color_space_index_Separation:
+ switch (pdev->params.ColorConversionStrategy) {
+ case ccs_ByObjectType:
+ /* Object type not implemented yet */
+ case ccs_UseDeviceDependentColor:
+ /* DeviceDependentColor deprecated */
+ case ccs_UseDeviceIndependentColorForImages:
+ /* If only correcting images, then leave unchanged */
+ case ccs_LeaveColorUnchanged:
+ code = write_color_unchanged(pdev, pis, pcc, &temp, psc, ppscc, used_process_color, pcs, pdc);
+ break;
+ case ccs_UseDeviceIndependentColor:
+ code = write_color_as_process(pdev, pis, pcs, pdc, used_process_color, ppscc, pcc);
+ break;
+ case ccs_sRGB:
+ default:
+ code = convert_separation_alternate(pdev, pis, pcs, pdc, used_process_color, ppscc, pcc, NULL, false);
+ break;
+ }
+ break;
+ case gs_color_space_index_DeviceN:
+ switch (pdev->params.ColorConversionStrategy) {
+ case ccs_ByObjectType:
+ /* Object type not implemented yet */
+ case ccs_UseDeviceDependentColor:
+ /* DeviceDependentColor deprecated */
+ case ccs_UseDeviceIndependentColorForImages:
+ /* If only correcting images, then leave unchanged */
+ case ccs_LeaveColorUnchanged:
+ code = write_color_unchanged(pdev, pis, pcc, &temp, psc, ppscc, used_process_color, pcs, pdc);
+ break;
+ case ccs_UseDeviceIndependentColor:
+ code = write_color_as_process(pdev, pis, pcs, pdc, used_process_color, ppscc, pcc);
+ break;
+ case ccs_sRGB:
+ default:
+ code = convert_DeviceN_alternate(pdev, pis, pcs, pdc, used_process_color, ppscc, pcc, NULL, false);
+ break;
+ }
+ break;
+ case gs_color_space_index_ICC:
+ /* Note that if csi is ICC, check to see if this was one of
+ the default substitutes that we introduced for DeviceGray,
+ DeviceRGB or DeviceCMYK. If it is, then just write
+ the default color. Depending upon the flavor of PDF,
+ or other options, we may want to actually have all
+ the colors defined by ICC profiles and not do the following
+ substituion of the Device space. */
+ csi2 = gsicc_get_default_type(pcs2->cmm_icc_profile_data);
+
+ switch (csi2) {
+ case gs_color_space_index_DeviceGray:
+ if (pdev->params.ColorConversionStrategy == ccs_Gray ||
+ pdev->params.ColorConversionStrategy == ccs_LeaveColorUnchanged) {
+ code = write_color_unchanged(pdev, pis, pcc, &temp, psc, ppscc, used_process_color, pcs, pdc);
+ *psc = temp;
+ return code;
+ }
+ break;
+ case gs_color_space_index_DeviceRGB:
+ if (pdev->params.ColorConversionStrategy == ccs_RGB ||
+ pdev->params.ColorConversionStrategy == ccs_LeaveColorUnchanged) {
+ code = write_color_unchanged(pdev, pis, pcc, &temp, psc, ppscc, used_process_color, pcs, pdc);
+ *psc = temp;
+ return code;
+ }
+ break;
+ case gs_color_space_index_DeviceCMYK:
+ if (pdev->params.ColorConversionStrategy == ccs_CMYK ||
+ pdev->params.ColorConversionStrategy == ccs_LeaveColorUnchanged) {
+ code = write_color_unchanged(pdev, pis, pcc, &temp, psc, ppscc, used_process_color, pcs, pdc);
+ *psc = temp;
+ return code;
+ }
+ break;
+ default:
+ break;
+ }
+ /* Fall through if its not a device substitute, or not one we want to preserve */
+ case gs_color_space_index_DevicePixel:
+ case gs_color_space_index_Indexed:
+ code = write_color_as_process(pdev, pis, pcs, pdc, used_process_color, ppscc, pcc);
+ break;
+ default:
+ return (gs_note_error(gs_error_rangecheck));
+ break;
+ }
+ } else {
+ switch(pdev->params.ColorConversionStrategy) {
+ case ccs_ByObjectType:
+ /* Object type not implemented yet */
+ case ccs_UseDeviceDependentColor:
+ /* DeviceDependentCOlor deprecated */
+ case ccs_UseDeviceIndependentColorForImages:
+ /* If only correcting images, then leave unchanged */
+ case ccs_LeaveColorUnchanged:
+ code = write_color_unchanged(pdev, pis, pcc, &temp, psc, ppscc, used_process_color, pcs, pdc);
+ break;
+ case ccs_UseDeviceIndependentColor:
+ code = write_color_as_process_ICC(pdev, pis, pcs, pdc, psc, used_process_color, ppscc, pcc, &temp);
+ break;
+ case ccs_CMYK:
+ switch(csi2) {
+ case gs_color_space_index_DeviceGray:
+ case gs_color_space_index_DeviceCMYK:
+ code = write_color_unchanged(pdev, pis, pcc, &temp, psc, ppscc, used_process_color, pcs, pdc);
+ break;
+ case gs_color_space_index_Separation:
+ pcs2 = pcs2->base_space;
+ csi = gs_color_space_get_index(pcs2);
+ if (csi == gs_color_space_index_ICC)
+ csi = gsicc_get_default_type(pcs2->cmm_icc_profile_data);
+ if (csi != gs_color_space_index_DeviceCMYK)
+ code = convert_separation_alternate(pdev, pis, pcs, pdc, used_process_color, ppscc, pcc, NULL, false);
+ else
+ code = write_color_unchanged(pdev, pis, pcc, &temp, psc, ppscc, used_process_color, pcs, pdc);
+ break;
+ case gs_color_space_index_DeviceN:
+ pcs2 = pcs2->base_space;
+ csi = gs_color_space_get_index(pcs2);
+ if (csi == gs_color_space_index_ICC)
+ csi = gsicc_get_default_type(pcs2->cmm_icc_profile_data);
+ if (csi != gs_color_space_index_DeviceCMYK)
+ code = convert_DeviceN_alternate(pdev, pis, pcs, pdc, used_process_color, ppscc, pcc, NULL, false);
+ else
+ code = write_color_unchanged(pdev, pis, pcc, &temp, psc, ppscc, used_process_color, pcs, pdc);
+ break;
+ case gs_color_space_index_Indexed:
+ pcs2 = pcs->base_space;
+ csi = gs_color_space_get_index(pcs2);
+ if (csi == gs_color_space_index_ICC)
+ csi = gsicc_get_default_type(pcs2->cmm_icc_profile_data);
+ switch(csi) {
+ case gs_color_space_index_DeviceGray:
+ case gs_color_space_index_DeviceCMYK:
+ code = write_color_unchanged(pdev, pis, pcc, &temp, psc, ppscc, used_process_color, pcs, pdc);
+ break;
+ case gs_color_space_index_Separation:
+ pcs2 = pcs2->base_space;
+ csi = gs_color_space_get_index(pcs2);
+ if (csi == gs_color_space_index_ICC)
+ csi = gsicc_get_default_type(pcs2->cmm_icc_profile_data);
+ if (csi != gs_color_space_index_DeviceCMYK)
+ code = convert_separation_alternate(pdev, pis, pcs, pdc, used_process_color, ppscc, pcc, NULL, false);
+ else
+ code = write_color_unchanged(pdev, pis, pcc, &temp, psc, ppscc, used_process_color, pcs, pdc);
+ break;
+ case gs_color_space_index_DeviceN:
+ pcs2 = pcs2->base_space;
+ csi = gs_color_space_get_index(pcs2);
+ if (csi == gs_color_space_index_ICC)
+ csi = gsicc_get_default_type(pcs2->cmm_icc_profile_data);
+ if (csi != gs_color_space_index_DeviceCMYK)
+ code = convert_DeviceN_alternate(pdev, pis, pcs, pdc, used_process_color, ppscc, pcc, NULL, false);
+ else
+ code = write_color_unchanged(pdev, pis, pcc, &temp, psc, ppscc, used_process_color, pcs, pdc);
+ break;
+ default:
+ code = write_color_as_process(pdev, pis, pcs, pdc, used_process_color, ppscc, pcc);
+ break;
+ }
+ break;
+ default:
+ code = write_color_as_process(pdev, pis, pcs, pdc, used_process_color, ppscc, pcc);
+ break;
+ }
+ break;
+ case ccs_Gray:
+ switch(csi2) {
+ case gs_color_space_index_DeviceGray:
+ code = write_color_unchanged(pdev, pis, pcc, &temp, psc, ppscc, used_process_color, pcs, pdc);
+ break;
+ case gs_color_space_index_Separation:
+ pcs2 = pcs2->base_space;
+ csi = gs_color_space_get_index(pcs2);
+ if (csi == gs_color_space_index_ICC)
+ csi = gsicc_get_default_type(pcs2->cmm_icc_profile_data);
+ if (csi != gs_color_space_index_DeviceGray)
+ code = convert_separation_alternate(pdev, pis, pcs, pdc, used_process_color, ppscc, pcc, NULL, false);
+ else
+ code = write_color_unchanged(pdev, pis, pcc, &temp, psc, ppscc, used_process_color, pcs, pdc);
+ break;
+ case gs_color_space_index_DeviceN:
+ pcs2 = pcs2->base_space;
+ csi = gs_color_space_get_index(pcs2);
+ if (csi == gs_color_space_index_ICC)
+ csi = gsicc_get_default_type(pcs2->cmm_icc_profile_data);
+ if (csi != gs_color_space_index_DeviceGray)
+ code = convert_DeviceN_alternate(pdev, pis, pcs, pdc, used_process_color, ppscc, pcc, NULL, false);
+ else
+ code = write_color_unchanged(pdev, pis, pcc, &temp, psc, ppscc, used_process_color, pcs, pdc);
+ break;
+ case gs_color_space_index_Indexed:
+ pcs2 = pcs->base_space;
+ csi = gs_color_space_get_index(pcs2);
+ if (csi == gs_color_space_index_ICC)
+ csi = gsicc_get_default_type(pcs2->cmm_icc_profile_data);
+ switch(csi) {
+ case gs_color_space_index_DeviceGray:
+ code = write_color_unchanged(pdev, pis, pcc, &temp, psc, ppscc, used_process_color, pcs, pdc);
+ break;
+ case gs_color_space_index_Separation:
+ pcs2 = pcs2->base_space;
+ csi = gs_color_space_get_index(pcs2);
+ if (csi == gs_color_space_index_ICC)
+ csi = gsicc_get_default_type(pcs2->cmm_icc_profile_data);
+ if (csi != gs_color_space_index_DeviceGray)
+ code = convert_separation_alternate(pdev, pis, pcs, pdc, used_process_color, ppscc, pcc, NULL, false);
+ else
+ code = write_color_unchanged(pdev, pis, pcc, &temp, psc, ppscc, used_process_color, pcs, pdc);
+ break;
+ case gs_color_space_index_DeviceN:
+ pcs2 = pcs2->base_space;
+ csi = gs_color_space_get_index(pcs2);
+ if (csi == gs_color_space_index_ICC)
+ csi = gsicc_get_default_type(pcs2->cmm_icc_profile_data);
+ if (csi != gs_color_space_index_DeviceGray)
+ code = convert_DeviceN_alternate(pdev, pis, pcs, pdc, used_process_color, ppscc, pcc, NULL, false);
+ else
+ code = write_color_unchanged(pdev, pis, pcc, &temp, psc, ppscc, used_process_color, pcs, pdc);
+ break;
+ default:
+ code = write_color_as_process(pdev, pis, pcs, pdc, used_process_color, ppscc, pcc);
+ break;
+ }
+ break;
+ default:
+ code = write_color_as_process(pdev, pis, pcs, pdc, used_process_color, ppscc, pcc);
+ break;
+ }
+ break;
+ case ccs_sRGB:
+ case ccs_RGB:
+ switch(csi2) {
+ case gs_color_space_index_DeviceGray:
+ case gs_color_space_index_DeviceRGB:
+ code = write_color_unchanged(pdev, pis, pcc, &temp, psc, ppscc, used_process_color, pcs, pdc);
+ break;
+ case gs_color_space_index_Separation:
+ pcs2 = pcs2->base_space;
+ csi = gs_color_space_get_index(pcs2);
+ if (csi == gs_color_space_index_ICC)
+ csi = gsicc_get_default_type(pcs2->cmm_icc_profile_data);
+ if (csi != gs_color_space_index_DeviceRGB)
+ code = convert_separation_alternate(pdev, pis, pcs, pdc, used_process_color, ppscc, pcc, NULL, false);
+ else
+ code = write_color_unchanged(pdev, pis, pcc, &temp, psc, ppscc, used_process_color, pcs, pdc);
+ break;
+ case gs_color_space_index_DeviceN:
+ pcs2 = pcs2->base_space;
+ csi = gs_color_space_get_index(pcs2);
+ if (csi == gs_color_space_index_ICC)
+ csi = gsicc_get_default_type(pcs2->cmm_icc_profile_data);
+ if (csi != gs_color_space_index_DeviceRGB)
+ code = convert_DeviceN_alternate(pdev, pis, pcs, pdc, used_process_color, ppscc, pcc, NULL, false);
+ else
+ code = write_color_unchanged(pdev, pis, pcc, &temp, psc, ppscc, used_process_color, pcs, pdc);
+ break;
+ case gs_color_space_index_Indexed:
+ pcs2 = pcs->base_space;
+ csi = gs_color_space_get_index(pcs2);
+ if (csi == gs_color_space_index_ICC)
+ csi = gsicc_get_default_type(pcs2->cmm_icc_profile_data);
+ switch(csi) {
+ case gs_color_space_index_DeviceGray:
+ case gs_color_space_index_DeviceRGB:
+ code = write_color_unchanged(pdev, pis, pcc, &temp, psc, ppscc, used_process_color, pcs, pdc);
+ break;
+ case gs_color_space_index_Separation:
+ pcs2 = pcs2->base_space;
+ csi = gs_color_space_get_index(pcs2);
+ if (csi == gs_color_space_index_ICC)
+ csi = gsicc_get_default_type(pcs2->cmm_icc_profile_data);
+ if (csi != gs_color_space_index_DeviceRGB)
+ code = convert_separation_alternate(pdev, pis, pcs, pdc, used_process_color, ppscc, pcc, NULL, false);
+ else
+ code = write_color_unchanged(pdev, pis, pcc, &temp, psc, ppscc, used_process_color, pcs, pdc);
+ break;
+ case gs_color_space_index_DeviceN:
+ pcs2 = pcs2->base_space;
+ csi = gs_color_space_get_index(pcs2);
+ if (csi == gs_color_space_index_ICC)
+ csi = gsicc_get_default_type(pcs2->cmm_icc_profile_data);
+ if (csi != gs_color_space_index_DeviceRGB)
+ code = convert_DeviceN_alternate(pdev, pis, pcs, pdc, used_process_color, ppscc, pcc, NULL, false);
+ else
+ code = write_color_unchanged(pdev, pis, pcc, &temp, psc, ppscc, used_process_color, pcs, pdc);
+ break;
+ default:
+ code = write_color_as_process(pdev, pis, pcs, pdc, used_process_color, ppscc, pcc);
+ break;
+ }
+ break;
+ default:
+ code = write_color_as_process(pdev, pis, pcs, pdc, used_process_color, ppscc, pcc);
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ break;
+ default: /* must not happen. */
+ case use_process_color:
+ code = psdf_set_color((gx_device_vector *)pdev, pdc, ppscc, pdev->UseOldColor);
+ if (code < 0)
+ return code;
+ *used_process_color = true;
+ break;
+ }
+ *psc = temp;
+ return code;
+}
+
+/* Set the fill or stroke color. */
+int
+pdf_reset_color(gx_device_pdf * pdev, const gs_imager_state * pis,
+ const gx_drawing_color *pdc, gx_hl_saved_color * psc,
+ bool *used_process_color,
+ const psdf_set_color_commands_t *ppscc)
+{
+ int code = 0;
+ gx_hl_saved_color temp;
+ const gs_color_space *pcs, *pcs2;
+ const gs_client_color *pcc; /* fixme: not needed due to gx_hld_get_color_component. */
+ cos_value_t cs_value;
+ const char *command;
+ int code1 = 0;
+ gs_color_space_index csi;
+ gs_color_space_index csi2;
+ gs_range_t *ranges = 0;
+
+
+ if (!pdev->UseOldColor)
+ return new_pdf_reset_color(pdev, pis, pdc, psc, used_process_color, ppscc);
+
+ if (pdev->skip_colors)
+ return 0;
+ gx_hld_save_color(pis, pdc, &temp);
+ /* Since pdfwrite never applies halftones and patterns, but monitors
+ * halftone/pattern IDs separately, we don't need to compare
+ * halftone/pattern bodies here.
+ */
+ if (gx_hld_saved_color_equal(&temp, psc))
+ return 0;
+
+ switch (gx_hld_get_color_space_and_ccolor(pis, pdc, &pcs, &pcc)) {
+ case non_pattern_color_space:
+ csi2 = gs_color_space_get_index(pcs);
+ if (csi2 == gs_color_space_index_ICC) {
+ csi2 = gsicc_get_default_type(pcs->cmm_icc_profile_data);
+ }
+ switch (csi2) {
+ case gs_color_space_index_DeviceGray:
+ command = ppscc->setgray;
+ break;
+ case gs_color_space_index_DeviceRGB:
+ if (pdev->params.ColorConversionStrategy == ccs_CMYK)
+ goto write_process_color;
+ if (pdev->params.ColorConversionStrategy == ccs_Gray)
+ goto write_process_color;
+ command = ppscc->setrgbcolor;
+ break;
+ case gs_color_space_index_DeviceCMYK:
+ if (pdev->params.ColorConversionStrategy == ccs_sRGB)
+ goto write_process_color;
+ if (pdev->params.ColorConversionStrategy == ccs_Gray)
+ goto write_process_color;
+ command = ppscc->setcmykcolor;
+ break;
+ case gs_color_space_index_Indexed:
+ if (pdev->CompatibilityLevel <= 1.2) {
+ pcs2 = pcs->base_space;
+ csi = gs_color_space_get_index(pcs2);
+ if (!is_cspace_allowed_in_strategy(pdev, csi))
+ goto write_process_color;
+ if (csi == gs_color_space_index_Separation) {
+ pcs2 = pcs->base_space;
+ goto check_pcs2;
+ }
+ goto check_pcs2;
+ }
+ goto scn;
+ case gs_color_space_index_Separation:
+ if (pdev->CompatibilityLevel <= 1.2) {
+ pcs2 = pcs->base_space;
+ check_pcs2:
+ csi = gs_color_space_get_index(pcs2);
+ if (!is_cspace_allowed_in_strategy(pdev, csi))
+ goto write_process_color;
+ switch(gs_color_space_get_index(pcs2)) {
+ case gs_color_space_index_DevicePixel :
+ case gs_color_space_index_DeviceN:
+ case gs_color_space_index_ICC:
+ goto write_process_color;
+ default:
+ DO_NOTHING;
+ }
+ }
+ goto scn;
+ case gs_color_space_index_ICC:
+ case gs_color_space_index_DevicePixel:
+ case gs_color_space_index_DeviceN:
+ if (pdev->CompatibilityLevel <= 1.2)
+ goto write_process_color;
+ goto scn;
+ default :
+ if (pdev->params.ColorConversionStrategy == ccs_CMYK)
+ goto write_process_color;
+ if (pdev->params.ColorConversionStrategy == ccs_sRGB)
+ goto write_process_color;
+ if (pdev->params.ColorConversionStrategy == ccs_Gray)
+ goto write_process_color;
+ scn:
+ command = ppscc->setcolorn;
+ if (!gx_hld_saved_color_same_cspace(&temp, psc)) {
+ code = pdf_color_space_named(pdev, pis, &cs_value, (const gs_range_t **)&ranges, pcs,
+ &pdf_color_space_names, true, NULL, 0, false);
+ /* fixme : creates redundant PDF objects. */
+ if (code == gs_error_rangecheck) {
+ /* The color space can't write to PDF. */
+ goto write_process_color;
+ }
+ if (code < 0)
+ return code;
+ code = cos_value_write(&cs_value, pdev);
+ if (code < 0)
+ return code;
+ pprints1(pdev->strm, " %s\n", ppscc->setcolorspace);
+ if (ranges && (csi2 >= gs_color_space_index_CIEDEFG && csi2 <= gs_color_space_index_CIEA)) {
+ gs_client_color *dcc = (gs_client_color *)pcc;
+ switch (csi2) {
+ case gs_color_space_index_CIEDEFG:
+ rescale_cie_color(ranges, 4, pcc, dcc);
+ break;
+ case gs_color_space_index_CIEDEF:
+ rescale_cie_color(ranges, 3, pcc, dcc);
+ break;
+ case gs_color_space_index_CIEABC:
+ rescale_cie_color(ranges, 3, pcc, dcc);
+ break;
+ case gs_color_space_index_CIEA:
+ rescale_cie_color(ranges, 1, pcc, dcc);
+ break;
+ default:
+ /* Can't happen, but silences compiler warnings */
+ break;
+ }
+ }
+ } else if (*used_process_color)
+ goto write_process_color;
+ break;
+ }
+ *used_process_color = false;
+ code = pdf_write_ccolor(pdev, pis, pcc);
+ if (code < 0)
+ return code;
+ pprints1(pdev->strm, " %s\n", command);
+ break;
+ case pattern_color_space:
+ { pdf_resource_t *pres;
+
+ if (pdc->type == gx_dc_type_pattern)
+ code = pdf_put_colored_pattern(pdev, pdc, pcs,
+ ppscc, pis, &pres);
+ else if (pdc->type == &gx_dc_pure_masked) {
+ code = pdf_put_uncolored_pattern(pdev, pdc, pcs,
+ ppscc, pis, &pres);
+ if (code < 0 || pres == 0) {
+ /* replaced a pattern with a flat fill, but we still
+ * need to change the 'saved' colour or we will
+ * get out of step with the PDF content.
+ */
+ *psc = temp;
+ return code;
+ }
+ if (pis->have_pattern_streams)
+ code = pdf_write_ccolor(pdev, pis, pcc);
+ } else if (pdc->type == &gx_dc_pattern2) {
+ if (pdev->CompatibilityLevel <= 1.2)
+ return_error(gs_error_rangecheck);
+ if (!is_pattern2_allowed_in_strategy(pdev, pdc))
+ return_error(gs_error_rangecheck);
+ code1 = pdf_put_pattern2(pdev, pis, pdc, ppscc, &pres);
+ } else
+ return_error(gs_error_rangecheck);
+ if (code < 0)
+ return code;
+ code = pdf_add_resource(pdev, pdev->substream_Resources, "/Pattern", pres);
+ if (code1 != gs_error_rangecheck) {
+ cos_value_write(cos_resource_value(&cs_value, pres->object), pdev);
+ pprints1(pdev->strm, " %s\n", ppscc->setcolorn);
+ }
+ else
+ pres->where_used = 0;
+ if (code < 0)
+ return code;
+ }
+ *used_process_color = false;
+ break;
+ default: /* must not happen. */
+ case use_process_color:
+ write_process_color:
+ code = psdf_set_color((gx_device_vector *)pdev, pdc, ppscc, pdev->UseOldColor);
+ if (code < 0)
+ return code;
+ *used_process_color = true;
+ }
+ *psc = temp;
+ return code1;
+}
+int
+pdf_set_drawing_color(gx_device_pdf * pdev, const gs_imager_state * pis,
+ const gx_drawing_color *pdc,
+ gx_hl_saved_color * psc,
+ bool *used_process_color,
+ const psdf_set_color_commands_t *ppscc)
+{
+ gx_hl_saved_color temp;
+ int code;
+
+ /* This section of code was in pdf_reset_color above, but was moved into this
+ * routine (and below) in order to isolate the switch to a stream context. This
+ * now allows us the opportunity to write colours in any context, in particular
+ * when in a text context, by using pdf_reset_color.
+ */
+ if (pdev->skip_colors)
+ return 0;
+ gx_hld_save_color(pis, pdc, &temp);
+ /* Since pdfwrite never applies halftones and patterns, but monitors
+ * halftone/pattern IDs separately, we don't need to compare
+ * halftone/pattern bodies here.
+ */
+ if (gx_hld_saved_color_equal(&temp, psc))
+ return 0;
+ /*
+ * In principle, we can set colors in either stream or text
+ * context. However, since we currently enclose all text
+ * strings inside a gsave/grestore, this causes us to lose
+ * track of the color when we leave text context. Therefore,
+ * we require stream context for setting colors.
+ */
+ code = pdf_open_page(pdev, PDF_IN_STREAM);
+ if (code < 0)
+ return code;
+
+ return pdf_reset_color(pdev, pis, pdc, psc, used_process_color, ppscc);
+}
+int
+pdf_set_pure_color(gx_device_pdf * pdev, gx_color_index color,
+ gx_hl_saved_color * psc,
+ bool *used_process_color,
+ const psdf_set_color_commands_t *ppscc)
+{
+ gx_drawing_color dcolor;
+ gx_hl_saved_color temp;
+ int code;
+
+ set_nonclient_dev_color(&dcolor, color);
+
+ if (pdev->skip_colors)
+ return 0;
+ gx_hld_save_color(NULL, &dcolor, &temp);
+ /* Since pdfwrite never applies halftones and patterns, but monitors
+ * halftone/pattern IDs separately, we don't need to compare
+ * halftone/pattern bodies here.
+ */
+ if (gx_hld_saved_color_equal(&temp, psc))
+ return 0;
+ /*
+ * In principle, we can set colors in either stream or text
+ * context. However, since we currently enclose all text
+ * strings inside a gsave/grestore, this causes us to lose
+ * track of the color when we leave text context. Therefore,
+ * we require stream context for setting colors.
+ */
+ code = pdf_open_page(pdev, PDF_IN_STREAM);
+ if (code < 0)
+ return code;
+
+ return pdf_reset_color(pdev, NULL, &dcolor, psc, used_process_color, ppscc);
+}
+
+/*
+ * Convert a string into cos name.
+ */
+int
+pdf_string_to_cos_name(gx_device_pdf *pdev, const byte *str, uint len,
+ cos_value_t *pvalue)
+{
+ byte *chars = gs_alloc_string(pdev->pdf_memory, len + 1,
+ "pdf_string_to_cos_name");
+
+ if (chars == 0)
+ return_error(gs_error_VMerror);
+ chars[0] = '/';
+ memcpy(chars + 1, str, len);
+ cos_string_value(pvalue, chars, len + 1);
+ return 0;
+}
+
+/* ---------------- Graphics state updating ---------------- */
+
+/* ------ Functions ------ */
+
+/* Define the maximum size of a Function reference. */
+#define MAX_FN_NAME_CHARS 9 /* /Default, /Identity */
+#define MAX_FN_CHARS max(MAX_REF_CHARS + 4, MAX_FN_NAME_CHARS)
+
+/*
+ * Create and write a Function for a gx_transfer_map. We use this for
+ * transfer, BG, and UCR functions. If check_identity is true, check for
+ * an identity map. Return 1 if the map is the identity map, otherwise
+ * return 0.
+ */
+static data_source_proc_access(transfer_map_access); /* check prototype */
+static int
+transfer_map_access(const gs_data_source_t *psrc, ulong start, uint length,
+ byte *buf, const byte **ptr)
+{
+ const gx_transfer_map *map = (const gx_transfer_map *)psrc->data.str.data;
+ uint i;
+
+ if (ptr)
+ *ptr = buf;
+ for (i = 0; i < length; ++i)
+ buf[i] = frac2byte(map->values[(uint)start + i]);
+ return 0;
+}
+static int
+transfer_map_access_signed(const gs_data_source_t *psrc,
+ ulong start, uint length,
+ byte *buf, const byte **ptr)
+{
+ /* To prevent numeric errors, we need to map 0 to an integer.
+ * We can't apply a general expression, because Decode isn't accessible here.
+ * Assuming this works for UCR only.
+ * Assuming the range of UCR is always [-1, 1].
+ * Assuming BitsPerSample = 8.
+ */
+ const gx_transfer_map *map = (const gx_transfer_map *)psrc->data.str.data;
+ uint i;
+
+ *ptr = buf;
+ for (i = 0; i < length; ++i)
+ buf[i] = (byte)
+ ((frac2float(map->values[(uint)start + i]) + 1) * 127);
+ return 0;
+}
+static int
+pdf_write_transfer_map(gx_device_pdf *pdev, const gx_transfer_map *map,
+ int range0, bool check_identity,
+ const char *key, char *ids)
+{
+ gs_memory_t *mem = pdev->pdf_memory;
+ gs_function_Sd_params_t params;
+ static const float domain01[2] = { 0, 1 };
+ static const int size = transfer_map_size;
+ float range01[2], decode[2];
+ gs_function_t *pfn;
+ long id;
+ int code;
+
+ if (map == 0) {
+ *ids = 0; /* no map */
+ return 1;
+ }
+ if (check_identity) {
+ /* Check for an identity map. */
+ int i;
+
+ if (map->proc == gs_identity_transfer)
+ i = transfer_map_size;
+ else
+ for (i = 0; i < transfer_map_size; ++i) {
+ fixed d = map->values[i] - bits2frac(i, log2_transfer_map_size);
+ if (any_abs(d) > fixed_epsilon) /* ignore small noise */
+ break;
+ }
+ if (i == transfer_map_size) {
+ strcpy(ids, key);
+ strcat(ids, "/Identity");
+ return 1;
+ }
+ }
+ params.m = 1;
+ params.Domain = domain01;
+ params.n = 1;
+ range01[0] = (float)range0, range01[1] = 1.0;
+ params.Range = range01;
+ params.Order = 1;
+ params.DataSource.access =
+ (range0 < 0 ? transfer_map_access_signed : transfer_map_access);
+ params.DataSource.data.str.data = (const byte *)map; /* bogus */
+ /* DataSource */
+ params.BitsPerSample = 8; /* could be 16 */
+ params.Encode = 0;
+ if (range01[0] < 0 && range01[1] > 0) {
+ /* This works for UCR only.
+ * Map 0 to an integer.
+ * Rather the range of UCR is always [-1, 1],
+ * we prefer a general expression.
+ */
+ int r0 = (int)( -range01[0] * ((1 << params.BitsPerSample) - 1)
+ / (range01[1] - range01[0]) ); /* Round down. */
+ float r1 = r0 * range01[1] / -range01[0]; /* r0 + r1 <= (1 << params.BitsPerSample) - 1 */
+
+ decode[0] = range01[0];
+ decode[1] = range01[0] + (range01[1] - range01[0]) * ((1 << params.BitsPerSample) - 1)
+ / (r0 + r1);
+ params.Decode = decode;
+ } else
+ params.Decode = 0;
+ params.Size = &size;
+ code = gs_function_Sd_init(&pfn, &params, mem);
+ if (code < 0)
+ return code;
+ code = pdf_write_function(pdev, pfn, &id);
+ gs_function_free(pfn, false, mem);
+ if (code < 0)
+ return code;
+ gs_sprintf(ids, "%s%s%ld 0 R", key, (key[0] && key[0] != ' ' ? " " : ""), id);
+ return 0;
+}
+static int
+pdf_write_transfer(gx_device_pdf *pdev, const gx_transfer_map *map,
+ const char *key, char *ids)
+{
+ return pdf_write_transfer_map(pdev, map, 0, true, key, ids);
+}
+
+/* ------ Halftones ------ */
+
+/*
+ * Recognize the predefined PDF halftone functions. Note that because the
+ * corresponding PostScript functions use single-precision floats, the
+ * functions used for testing must do the same in order to get identical
+ * results. Currently we only do this for a few of the functions.
+ */
+#define HT_FUNC(name, expr)\
+ static double name(double xd, double yd) {\
+ float x = (float)xd, y = (float)yd;\
+ return d2f(expr);\
+ }
+
+/*
+ * In most versions of gcc (e.g., 2.7.2.3, 2.95.4), return (float)xxx
+ * doesn't actually do the coercion. Force this here. Note that if we
+ * use 'inline', it doesn't work.
+ */
+static float
+d2f(double d)
+{
+ float f = (float)d;
+ return f;
+}
+static double
+ht_Round(double xf, double yf)
+{
+ float x = (float)xf, y = (float)yf;
+ float xabs = fabs(x), yabs = fabs(y);
+
+ if (d2f(xabs + yabs) <= 1)
+ return d2f(1 - d2f(d2f(x * x) + d2f(y * y)));
+ xabs -= 1, yabs -= 1;
+ return d2f(d2f(d2f(xabs * xabs) + d2f(yabs * yabs)) - 1);
+}
+static double
+ht_Diamond(double xf, double yf)
+{
+ float x = (float)xf, y = (float)yf;
+ float xabs = fabs(x), yabs = fabs(y);
+
+ if (d2f(xabs + yabs) <= 0.75)
+ return d2f(1 - d2f(d2f(x * x) + d2f(y * y)));
+ if (d2f(xabs + yabs) <= d2f(1.23))
+ return d2f(1 - d2f(d2f(d2f(0.85) * xabs) + yabs));
+ xabs -= 1, yabs -= 1;
+ return d2f(d2f(d2f(xabs * xabs) + d2f(yabs * yabs)) - 1);
+}
+static double
+ht_Ellipse(double xf, double yf)
+{
+ float x = (float)xf, y = (float)yf;
+ float xabs = fabs(x), yabs = fabs(y);
+ /*
+ * The PDF Reference, 2nd edition, incorrectly specifies the
+ * computation w = 4 * |x| + 3 * |y| - 3. The PostScript code in the
+ * same book correctly implements w = 3 * |x| + 4 * |y| - 3.
+ */
+ float w = (float)(d2f(d2f(3 * xabs) + d2f(4 * yabs)) - 3);
+
+ if (w < 0) {
+ yabs /= 0.75;
+ return d2f(1 - d2f((d2f(x * x) + d2f(yabs * yabs)) / 4));
+ }
+ if (w > 1) {
+ xabs = 1 - xabs, yabs = d2f(1 - yabs) / 0.75;
+ return d2f(d2f((d2f(xabs * xabs) + d2f(yabs * yabs)) / 4) - 1);
+ }
+ return d2f(0.5 - w);
+}
+/*
+ * Most of these are recognized properly even without d2f. We've only
+ * added d2f where it apparently makes a difference.
+ */
+static float
+d2fsin_d(double x) {
+ return d2f(gs_sin_degrees(d2f(x)));
+}
+static float
+d2fcos_d(double x) {
+ return d2f(gs_cos_degrees(d2f(x)));
+}
+HT_FUNC(ht_EllipseA, 1 - (x * x + 0.9 * y * y))
+HT_FUNC(ht_InvertedEllipseA, x * x + 0.9 * y * y - 1)
+HT_FUNC(ht_EllipseB, 1 - sqrt(x * x + 0.625 * y * y))
+HT_FUNC(ht_EllipseC, 1 - (0.9 * x * x + y * y))
+HT_FUNC(ht_InvertedEllipseC, 0.9 * x * x + y * y - 1)
+HT_FUNC(ht_Line, -fabs((x - x) + y)) /* quiet compiler (unused variable x) */
+HT_FUNC(ht_LineX, (y - y) + x) /* quiet compiler (unused variable y) */
+HT_FUNC(ht_LineY, (x - x) + y) /* quiet compiler (unused variable x) */
+HT_FUNC(ht_Square, -max(fabs(x), fabs(y)))
+HT_FUNC(ht_Cross, -min(fabs(x), fabs(y)))
+HT_FUNC(ht_Rhomboid, (0.9 * fabs(x) + fabs(y)) / 2)
+HT_FUNC(ht_DoubleDot, (d2fsin_d(x * 360) + d2fsin_d(y * 360)) / 2)
+HT_FUNC(ht_InvertedDoubleDot, -(d2fsin_d(x * 360) + d2fsin_d(y * 360)) / 2)
+HT_FUNC(ht_SimpleDot, 1 - d2f(d2f(x * x) + d2f(y * y)))
+HT_FUNC(ht_InvertedSimpleDot, d2f(d2f(x * x) + d2f(y * y)) - 1)
+HT_FUNC(ht_CosineDot, (d2fcos_d(x * 180) + d2fcos_d(y * 180)) / 2)
+HT_FUNC(ht_Double, (d2fsin_d(x * 180) + d2fsin_d(y * 360)) / 2)
+HT_FUNC(ht_InvertedDouble, -(d2fsin_d(x * 180) + d2fsin_d(y * 360)) / 2)
+typedef struct ht_function_s {
+ const char *fname;
+ double (*proc)(double, double);
+} ht_function_t;
+static const ht_function_t ht_functions[] = {
+ {"Round", ht_Round},
+ {"Diamond", ht_Diamond},
+ {"Ellipse", ht_Ellipse},
+ {"EllipseA", ht_EllipseA},
+ {"InvertedEllipseA", ht_InvertedEllipseA},
+ {"EllipseB", ht_EllipseB},
+ {"EllipseC", ht_EllipseC},
+ {"InvertedEllipseC", ht_InvertedEllipseC},
+ {"Line", ht_Line},
+ {"LineX", ht_LineX},
+ {"LineY", ht_LineY},
+ {"Square", ht_Square},
+ {"Cross", ht_Cross},
+ {"Rhomboid", ht_Rhomboid},
+ {"DoubleDot", ht_DoubleDot},
+ {"InvertedDoubleDot", ht_InvertedDoubleDot},
+ {"SimpleDot", ht_SimpleDot},
+ {"InvertedSimpleDot", ht_InvertedSimpleDot},
+ {"CosineDot", ht_CosineDot},
+ {"Double", ht_Double},
+ {"InvertedDouble", ht_InvertedDouble}
+};
+
+/* Write each kind of halftone. */
+static int
+pdf_write_spot_function(gx_device_pdf *pdev, const gx_ht_order *porder,
+ long *pid)
+{
+ /****** DOESN'T HANDLE STRIP HALFTONES ******/
+ int w = porder->width, h = porder->height;
+ uint num_bits = porder->num_bits;
+ gs_function_Sd_params_t params;
+ static const float domain_spot[4] = { -1, 1, -1, 1 };
+ static const float range_spot[4] = { -1, 1 };
+ int size[2];
+ gs_memory_t *mem = pdev->pdf_memory;
+ /*
+ * Even though the values are logically ushort, we must always store
+ * them in big-endian order, so we access them as bytes.
+ */
+ byte *values;
+ gs_function_t *pfn;
+ uint i;
+ int code = 0;
+
+ params.array_size = 0;
+ params.m = 2;
+ params.Domain = domain_spot;
+ params.n = 1;
+ params.Range = range_spot;
+ params.Order = 0; /* default */
+ /*
+ * We could use 8, 16, or 32 bits per sample to save space, but for
+ * simplicity, we always use 16.
+ */
+ if (num_bits > 0x10000)
+ /* rangecheck is a 'special case' in gdev_pdf_fill_path, if this error is encountered
+ * then it 'falls back' to a different method assuming its handling transparency in an
+ * old PDF output version. But if we fail to write the halftone, we want to abort
+ * so use limitcheck instead.
+ */
+ return_error(gs_error_limitcheck);
+ params.BitsPerSample = 16;
+ params.Encode = 0;
+ /*
+ * The default Decode array maps the actual data values [1 .. w*h] to a
+ * sub-interval of the Range, but that's OK, since all that matters is
+ * the relative values, not the absolute values.
+ */
+ params.Decode = 0;
+ size[0] = w;
+ size[1] = h;
+ params.Size = size;
+ /* Create the (temporary) threshold array. */
+ values = gs_alloc_byte_array(mem, num_bits, 2, "pdf_write_spot_function");
+ if (values == 0)
+ return_error(gs_error_VMerror);
+ for (i = 0; i < num_bits; ++i) {
+ gs_int_point pt;
+ int value;
+
+ if ((code = porder->procs->bit_index(porder, i, &pt)) < 0)
+ break;
+ value = pt.y * w + pt.x;
+ /* Always store the values in big-endian order. */
+ values[i * 2] = (byte)(value >> 8);
+ values[i * 2 + 1] = (byte)value;
+ }
+ data_source_init_bytes(&params.DataSource, (const byte *)values,
+ sizeof(*values) * num_bits);
+ if (code >= 0 &&
+ /* Warning from COverity that params.array_size is uninitialised. Correct */
+ /* but immeidiately after copying the data Sd_init sets the copied value */
+ /* to zero, so it is not actually used uninitialised. */
+ (code = gs_function_Sd_init(&pfn, &params, mem)) >= 0
+ ) {
+ code = pdf_write_function(pdev, pfn, pid);
+ gs_function_free(pfn, false, mem);
+ }
+ gs_free_object(mem, values, "pdf_write_spot_function");
+ return code;
+}
+
+/* if (memcmp(order.levels, porder->levels, order.num_levels * sizeof(*order.levels))) */
+static int
+compare_gx_ht_order_levels(const gx_ht_order *order1, const gx_ht_order *order2) {
+ int i;
+ for (i=0; i<order1->num_levels; i++) {
+ if (order1->levels[i] != order2->levels[i])
+ return(1);
+ }
+ return(0);
+}
+
+static int
+pdf_write_spot_halftone(gx_device_pdf *pdev, const gs_spot_halftone *psht,
+ const gx_ht_order *porder, long *pid)
+{
+ char trs[17 + MAX_FN_CHARS + 1];
+ int code = pdf_write_transfer(pdev, porder->transfer, "/TransferFunction",
+ trs);
+ long spot_id;
+ stream *s;
+ int i = countof(ht_functions);
+ gs_memory_t *mem = pdev->pdf_memory;
+
+ if (code < 0)
+ return code;
+ /*
+ * See if we can recognize the spot function, by comparing its sampled
+ * values against those in the order.
+ */
+ { gs_screen_enum senum;
+ gx_ht_order order;
+ int code;
+
+ order = *porder;
+ code = gs_screen_order_alloc(&order, mem);
+ if (code < 0)
+ goto notrec;
+ for (i = 0; i < countof(ht_functions); ++i) {
+ double (*spot_proc)(double, double) = ht_functions[i].proc;
+ gs_point pt;
+
+ gs_screen_enum_init_memory(&senum, &order, NULL, &psht->screen,
+ mem);
+ while ((code = gs_screen_currentpoint(&senum, &pt)) == 0 &&
+ gs_screen_next(&senum, spot_proc(pt.x, pt.y)) >= 0)
+ DO_NOTHING;
+ if (code < 0)
+ continue;
+ /* Compare the bits and levels arrays. */
+ if (compare_gx_ht_order_levels(&order,porder))
+ continue;
+ if (memcmp(order.bit_data, porder->bit_data,
+ order.num_bits * porder->procs->bit_data_elt_size))
+ continue;
+ /* We have a match. */
+ break;
+ }
+ gx_ht_order_release(&order, mem, false);
+ }
+ notrec:
+ if (i == countof(ht_functions)) {
+ /* Create and write a Function for the spot function. */
+ code = pdf_write_spot_function(pdev, porder, &spot_id);
+ if (code < 0)
+ return code;
+ }
+ *pid = pdf_begin_separate(pdev, resourceHalftone);
+ s = pdev->strm;
+ /* Use the original, requested frequency and angle. */
+ pprintg2(s, "<</Type/Halftone/HalftoneType 1/Frequency %g/Angle %g",
+ psht->screen.frequency, psht->screen.angle);
+ if (i < countof(ht_functions))
+ pprints1(s, "/SpotFunction/%s", ht_functions[i].fname);
+ else
+ pprintld1(s, "/SpotFunction %ld 0 R", spot_id);
+ stream_puts(s, trs);
+ if (psht->accurate_screens)
+ stream_puts(s, "/AccurateScreens true");
+ stream_puts(s, ">>\n");
+ return pdf_end_separate(pdev, resourceHalftone);
+}
+static int
+pdf_write_screen_halftone(gx_device_pdf *pdev, const gs_screen_halftone *psht,
+ const gx_ht_order *porder, long *pid)
+{
+ gs_spot_halftone spot;
+
+ spot.screen = *psht;
+ spot.accurate_screens = false;
+ spot.transfer = 0;
+ spot.transfer_closure.proc = 0;
+ return pdf_write_spot_halftone(pdev, &spot, porder, pid);
+}
+static int
+pdf_write_colorscreen_halftone(gx_device_pdf *pdev,
+ const gs_colorscreen_halftone *pcsht,
+ const gx_device_halftone *pdht, long *pid)
+{
+ int i;
+ stream *s;
+ long ht_ids[4];
+
+ for (i = 0; i < pdht->num_comp ; ++i) {
+ int code = pdf_write_screen_halftone(pdev, &pcsht->screens.indexed[i],
+ &pdht->components[i].corder,
+ &ht_ids[i]);
+ if (code < 0)
+ return code;
+ }
+ *pid = pdf_begin_separate(pdev, resourceHalftone);
+ s = pdev->strm;
+ /* Use Black, Gray as the Default unless we are in RGB colormodel */
+ /* (num_comp < 4) in which case we use Green (arbitrarily) */
+ pprintld1(s, "<</Type/Halftone/HalftoneType 5/Default %ld 0 R\n",
+ pdht->num_comp > 3 ? ht_ids[3] : ht_ids[1]);
+ pprintld2(s, "/Red %ld 0 R/Cyan %ld 0 R", ht_ids[0], ht_ids[0]);
+ pprintld2(s, "/Green %ld 0 R/Magenta %ld 0 R", ht_ids[1], ht_ids[1]);
+ pprintld2(s, "/Blue %ld 0 R/Yellow %ld 0 R", ht_ids[2], ht_ids[2]);
+ if (pdht->num_comp > 3)
+ pprintld2(s, "/Gray %ld 0 R/Black %ld 0 R", ht_ids[3], ht_ids[3]);
+ stream_puts(s, ">>\n");
+ return pdf_end_separate(pdev, resourceHalftone);
+}
+
+#define CHECK(expr)\
+ BEGIN if ((code = (expr)) < 0) return code; END
+
+static int
+pdf_write_threshold_halftone(gx_device_pdf *pdev,
+ const gs_threshold_halftone *ptht,
+ const gx_ht_order *porder, long *pid)
+{
+ char trs[17 + MAX_FN_CHARS + 1];
+ pdf_data_writer_t writer;
+ int code = pdf_write_transfer(pdev, porder->transfer, "",
+ trs);
+
+ if (code < 0)
+ return code;
+ CHECK(pdf_begin_data(pdev, &writer));
+ *pid = writer.pres->object->id;
+ CHECK(cos_dict_put_c_strings((cos_dict_t *)writer.pres->object,
+ "/Type", "/Halftone"));
+ CHECK(cos_dict_put_c_strings((cos_dict_t *)writer.pres->object,
+ "/HalftoneType", "6"));
+ CHECK(cos_dict_put_c_key_int((cos_dict_t *)writer.pres->object,
+ "/Width", ptht->width));
+ CHECK(cos_dict_put_c_key_int((cos_dict_t *)writer.pres->object,
+ "/Height", ptht->height));
+ if (*trs != 0)
+ CHECK(cos_dict_put_c_strings((cos_dict_t *)writer.pres->object,
+ "/TransferFunction", trs));
+ stream_write(writer.binary.strm, ptht->thresholds.data, ptht->thresholds.size);
+ return pdf_end_data(&writer);
+}
+static int
+pdf_write_threshold2_halftone(gx_device_pdf *pdev,
+ const gs_threshold2_halftone *ptht,
+ const gx_ht_order *porder, long *pid)
+{
+ char trs[17 + MAX_FN_CHARS + 1];
+ stream *s;
+ pdf_data_writer_t writer;
+ int code = pdf_write_transfer(pdev, porder->transfer, "/TransferFunction",
+ trs);
+
+ if (code < 0)
+ return code;
+ CHECK(pdf_begin_data(pdev, &writer));
+ *pid = writer.pres->object->id;
+ CHECK(cos_dict_put_c_strings((cos_dict_t *)writer.pres->object,
+ "/Type", "/Halftone"));
+ CHECK(cos_dict_put_c_strings((cos_dict_t *)writer.pres->object,
+ "/HalftoneType", "16"));
+ CHECK(cos_dict_put_c_key_int((cos_dict_t *)writer.pres->object,
+ "/Width", ptht->width));
+ CHECK(cos_dict_put_c_key_int((cos_dict_t *)writer.pres->object,
+ "/Height", ptht->height));
+ if (ptht->width2 && ptht->height2) {
+ CHECK(cos_dict_put_c_key_int((cos_dict_t *)writer.pres->object,
+ "/Width2", ptht->width2));
+ CHECK(cos_dict_put_c_key_int((cos_dict_t *)writer.pres->object,
+ "/Height2", ptht->height2));
+ }
+ if (*trs != 0)
+ CHECK(cos_dict_put_c_strings((cos_dict_t *)writer.pres->object,
+ "/TransferFunction", trs));
+ s = writer.binary.strm;
+ if (ptht->bytes_per_sample == 2)
+ stream_write(s, ptht->thresholds.data, ptht->thresholds.size);
+ else {
+ /* Expand 1-byte to 2-byte samples. */
+ int i;
+
+ for (i = 0; i < ptht->thresholds.size; ++i) {
+ byte b = ptht->thresholds.data[i];
+
+ stream_putc(s, b);
+ stream_putc(s, b);
+ }
+ }
+ return pdf_end_data(&writer);
+}
+static int
+pdf_get_halftone_component_index(const gs_multiple_halftone *pmht,
+ const gx_device_halftone *pdht,
+ int dht_index)
+{
+ int j;
+
+ for (j = 0; j < pmht->num_comp; j++)
+ if (pmht->components[j].comp_number == dht_index)
+ break;
+ if (j == pmht->num_comp) {
+ /* Look for Default. */
+ for (j = 0; j < pmht->num_comp; j++)
+ if (pmht->components[j].comp_number == GX_DEVICE_COLOR_MAX_COMPONENTS)
+ break;
+ if (j == pmht->num_comp)
+ return_error(gs_error_undefined);
+ }
+ return j;
+}
+static int
+pdf_write_multiple_halftone(gx_device_pdf *pdev,
+ const gs_multiple_halftone *pmht,
+ const gx_device_halftone *pdht, long *pid)
+{
+ stream *s;
+ int i, code, last_comp = 0;
+ gs_memory_t *mem = pdev->pdf_memory;
+ long *ids;
+ bool done_Default = false;
+
+ ids = (long *)gs_alloc_byte_array(mem, pmht->num_comp, sizeof(long),
+ "pdf_write_multiple_halftone");
+ if (ids == 0)
+ return_error(gs_error_VMerror);
+ for (i = 0; i < pdht->num_comp; ++i) {
+ const gs_halftone_component *phtc;
+ const gx_ht_order *porder;
+
+ code = pdf_get_halftone_component_index(pmht, pdht, i);
+ if (code < 0)
+ return code;
+ if (pmht->components[code].comp_number == GX_DEVICE_COLOR_MAX_COMPONENTS) {
+ if (done_Default)
+ continue;
+ done_Default = true;
+ }
+ phtc = &pmht->components[code];
+ porder = (pdht->components == 0 ? &pdht->order :
+ &pdht->components[i].corder);
+ switch (phtc->type) {
+ case ht_type_spot:
+ code = pdf_write_spot_halftone(pdev, &phtc->params.spot,
+ porder, &ids[i]);
+ break;
+ case ht_type_threshold:
+ code = pdf_write_threshold_halftone(pdev, &phtc->params.threshold,
+ porder, &ids[i]);
+ break;
+ case ht_type_threshold2:
+ code = pdf_write_threshold2_halftone(pdev,
+ &phtc->params.threshold2,
+ porder, &ids[i]);
+ break;
+ default:
+ code = gs_note_error(gs_error_rangecheck);
+ }
+ if (code < 0) {
+ gs_free_object(mem, ids, "pdf_write_multiple_halftone");
+ return code;
+ }
+ }
+ *pid = pdf_begin_separate(pdev, resourceHalftone);
+ s = pdev->strm;
+ stream_puts(s, "<</Type/Halftone/HalftoneType 5\n");
+ done_Default = false;
+ for (i = 0; i < pdht->num_comp; ++i) {
+ const gs_halftone_component *phtc;
+ byte *str;
+ uint len;
+ cos_value_t value;
+
+ code = pdf_get_halftone_component_index(pmht, pdht, i);
+ if (code < 0)
+ return code;
+ if (pmht->components[code].comp_number == GX_DEVICE_COLOR_MAX_COMPONENTS) {
+ if (done_Default)
+ continue;
+ done_Default = true;
+ }
+ phtc = &pmht->components[code];
+ if ((code = pmht->get_colorname_string(pdev->memory, phtc->cname, &str, &len)) < 0 ||
+ (code = pdf_string_to_cos_name(pdev, str, len, &value)) < 0)
+ return code;
+ cos_value_write(&value, pdev);
+ gs_free_string(mem, value.contents.chars.data,
+ value.contents.chars.size,
+ "pdf_write_multiple_halftone");
+ pprintld1(s, " %ld 0 R\n", ids[i]);
+ last_comp = i;
+ }
+ if (!done_Default) {
+ /*
+ * BOGUS: Type 5 halftones must contain Default component.
+ * Perhaps we have no way to obtain it,
+ * because pdht contains ProcessColorModel components only.
+ * We copy the last component as Default one.
+ */
+ pprintld1(s, " /Default %ld 0 R\n", ids[last_comp]);
+ }
+ stream_puts(s, ">>\n");
+ gs_free_object(mem, ids, "pdf_write_multiple_halftone");
+ return pdf_end_separate(pdev, resourceHalftone);
+}
+
+/*
+ * Update the halftone. This is a separate procedure only for
+ * readability.
+ */
+static int
+pdf_update_halftone(gx_device_pdf *pdev, const gs_imager_state *pis,
+ char *hts)
+{
+ const gs_halftone *pht = pis->halftone;
+ const gx_device_halftone *pdht = pis->dev_ht;
+ int code;
+ long id;
+
+ switch (pht->type) {
+ case ht_type_screen:
+ code = pdf_write_screen_halftone(pdev, &pht->params.screen,
+ &pdht->components[0].corder, &id);
+ break;
+ case ht_type_colorscreen:
+ code = pdf_write_colorscreen_halftone(pdev, &pht->params.colorscreen,
+ pdht, &id);
+ break;
+ case ht_type_spot:
+ code = pdf_write_spot_halftone(pdev, &pht->params.spot,
+ &pdht->components[0].corder, &id);
+ break;
+ case ht_type_threshold:
+ code = pdf_write_threshold_halftone(pdev, &pht->params.threshold,
+ &pdht->components[0].corder, &id);
+ break;
+ case ht_type_threshold2:
+ code = pdf_write_threshold2_halftone(pdev, &pht->params.threshold2,
+ &pdht->components[0].corder, &id);
+ break;
+ case ht_type_multiple:
+ case ht_type_multiple_colorscreen:
+ code = pdf_write_multiple_halftone(pdev, &pht->params.multiple,
+ pdht, &id);
+ break;
+ default:
+ return_error(gs_error_rangecheck);
+ }
+ if (code < 0)
+ return code;
+ gs_sprintf(hts, "%ld 0 R", id);
+ pdev->halftone_id = pis->dev_ht->id;
+ return code;
+}
+
+/* ------ Graphics state updating ------ */
+
+static inline cos_dict_t *
+resource_dict(pdf_resource_t *pres)
+{
+ return (cos_dict_t *)pres->object;
+}
+
+/* Open an ExtGState. */
+static int
+pdf_open_gstate(gx_device_pdf *pdev, pdf_resource_t **ppres)
+{
+ int code;
+
+ if (*ppres)
+ return 0;
+ /*
+ * We write gs command only in stream context.
+ * If we are clipped, and the clip path is about to change,
+ * the old clipping must be undone before writing gs.
+ */
+ if (pdev->context != PDF_IN_STREAM) {
+ /* We apparently use gs_error_interrupt as a request to change context. */
+ return gs_error_interrupt;
+ }
+ code = pdf_alloc_resource(pdev, resourceExtGState, gs_no_id, ppres, -1L);
+ if (code < 0)
+ return code;
+ cos_become((*ppres)->object, cos_type_dict);
+ code = cos_dict_put_c_key_string(resource_dict(*ppres), "/Type", (const byte *)"/ExtGState", 10);
+ if (code < 0)
+ return code;
+ return 0;
+}
+
+/* Finish writing an ExtGState. */
+int
+pdf_end_gstate(gx_device_pdf *pdev, pdf_resource_t *pres)
+{
+ if (pres) {
+ int code = pdf_substitute_resource(pdev, &pres, resourceExtGState, NULL, true);
+
+ if (code < 0)
+ return code;
+ pres->where_used |= pdev->used_mask;
+ code = pdf_open_page(pdev, PDF_IN_STREAM);
+ if (code < 0)
+ return code;
+ code = pdf_add_resource(pdev, pdev->substream_Resources, "/ExtGState", pres);
+ if (code < 0)
+ return code;
+ pprintld1(pdev->strm, "/R%ld gs\n", pdf_resource_id(pres));
+ pres->where_used |= pdev->used_mask;
+ }
+ return 0;
+}
+
+/*
+ * Update the transfer functions(s). This is a separate procedure only
+ * for readability.
+ */
+static int
+pdf_update_transfer(gx_device_pdf *pdev, const gs_imager_state *pis,
+ char *trs)
+{
+ int i, pi = -1;
+ bool multiple = false, update = false;
+ gs_id transfer_ids[4];
+ int code = 0;
+ const gx_transfer_map *tm[4];
+
+ tm[0] = pis->set_transfer.red;
+ tm[1] = pis->set_transfer.green;
+ tm[2] = pis->set_transfer.blue;
+ tm[3] = pis->set_transfer.gray;
+ for (i = 0; i < 4; ++i)
+ if (tm[i] != NULL) {
+ transfer_ids[i] = tm[i]->id;
+ if (pdev->transfer_ids[i] != tm[i]->id)
+ update = true;
+ if (pi != -1 && transfer_ids[i] != transfer_ids[pi])
+ multiple = true;
+ pi = i;
+ } else
+ transfer_ids[i] = -1;
+ if (update) {
+ int mask;
+
+ if (!multiple) {
+ code = pdf_write_transfer(pdev, tm[pi], "", trs);
+ if (code < 0)
+ return code;
+ mask = code == 0;
+ } else {
+ strcpy(trs, "[");
+ mask = 0;
+ for (i = 0; i < 4; ++i)
+ if (tm[i] != NULL) {
+ code = pdf_write_transfer_map(pdev,
+ tm[i],
+ 0, true, " ", trs + strlen(trs));
+ if (code < 0)
+ return code;
+ mask |= (code == 0) << i;
+ }
+ strcat(trs, "]");
+ }
+ memcpy(pdev->transfer_ids, transfer_ids, sizeof(pdev->transfer_ids));
+ pdev->transfer_not_identity = mask;
+ }
+ return code;
+}
+
+/*
+ * Update the current alpha if necessary. Note that because Ghostscript
+ * stores separate opacity and shape alpha, a rangecheck will occur if
+ * both are different from the current setting.
+ */
+static int
+pdf_update_alpha(gx_device_pdf *pdev, const gs_imager_state *pis,
+ pdf_resource_t **ppres)
+{
+ bool ais;
+ double alpha;
+ int code;
+
+ if (pdev->state.soft_mask_id != pis->soft_mask_id) {
+ char buf[20];
+
+ if (pis->soft_mask_id == 0)
+ strcpy(buf, "/None");
+ else
+ gs_sprintf(buf, "%ld 0 R", pis->soft_mask_id);
+ code = pdf_open_gstate(pdev, ppres);
+ if (code < 0)
+ return code;
+ code = cos_dict_put_c_key_string(resource_dict(*ppres),
+ "/SMask", (byte *)buf, strlen(buf));
+ if (code < 0)
+ return code;
+ pdev->state.soft_mask_id = pis->soft_mask_id;
+ }
+ if (pdev->state.opacity.alpha != pis->opacity.alpha) {
+ if (pdev->state.shape.alpha != pis->shape.alpha)
+ return_error(gs_error_rangecheck);
+ ais = false;
+ alpha = pdev->state.opacity.alpha = pis->opacity.alpha;
+ } else if (pdev->state.shape.alpha != pis->shape.alpha) {
+ ais = true;
+ alpha = pdev->state.shape.alpha = pis->shape.alpha;
+ } else
+ return 0;
+ code = pdf_open_gstate(pdev, ppres);
+ if (code < 0)
+ return code;
+ code = cos_dict_put_c_key_bool(resource_dict(*ppres), "/AIS", ais);
+ if (code < 0)
+ return code;
+ /* we never do the 'both' operations (b, B, b*, B*) so we set both */
+ /* CA and ca the same so that we stay in sync with state.*.alpha */
+ code = cos_dict_put_c_key_real(resource_dict(*ppres), "/CA", alpha);
+ if (code < 0)
+ return code;
+ return cos_dict_put_c_key_real(resource_dict(*ppres), "/ca", alpha);
+}
+
+/*
+ * Update the graphics subset common to all high-level drawing operations.
+ */
+int
+pdf_prepare_drawing(gx_device_pdf *pdev, const gs_imager_state *pis,
+ pdf_resource_t **ppres)
+{
+ int code = 0;
+ int bottom;
+
+ if (pdev->CompatibilityLevel >= 1.4) {
+ if (pdev->state.blend_mode != pis->blend_mode) {
+ static const char *const bm_names[] = { GS_BLEND_MODE_NAMES };
+ char buf[20];
+
+ code = pdf_open_gstate(pdev, ppres);
+ if (code < 0)
+ return code;
+ buf[0] = '/';
+ strncpy(buf + 1, bm_names[pis->blend_mode], sizeof(buf) - 2);
+ code = cos_dict_put_string_copy(resource_dict(*ppres), "/BM", buf);
+ if (code < 0)
+ return code;
+ pdev->state.blend_mode = pis->blend_mode;
+ }
+ code = pdf_update_alpha(pdev, pis, ppres);
+ if (code < 0)
+ return code;
+ } else {
+ /*
+ * If the graphics state calls for any transparency functions,
+ * we can't represent them, so return a rangecheck.
+ */
+ if (pis->opacity.alpha != 1 ||
+ pis->shape.alpha != 1)
+ return_error(gs_error_rangecheck);
+ }
+ /*
+ * We originally thought the remaining items were only needed for
+ * fill and stroke, but in fact they are needed for images as well.
+ */
+ /*
+ * Update halftone, transfer function, black generation, undercolor
+ * removal, halftone phase, overprint mode, smoothness, blend mode, text
+ * knockout.
+ */
+ bottom = (pdev->ResourcesBeforeUsage ? 1 : 0);
+ /* When ResourcesBeforeUsage != 0, one sbstack element
+ appears from the page contents stream. */
+ if (pdev->sbstack_depth == bottom) {
+ gs_int_point phase, dev_phase;
+ char hts[5 + MAX_FN_CHARS + 1],
+ trs[5 + MAX_FN_CHARS * 4 + 6 + 1],
+ bgs[5 + MAX_FN_CHARS + 1],
+ ucrs[6 + MAX_FN_CHARS + 1];
+
+ hts[0] = trs[0] = bgs[0] = ucrs[0] = 0;
+ if (pdev->params.PreserveHalftoneInfo &&
+ pdev->halftone_id != pis->dev_ht->id &&
+ !pdev->PDFX
+ ) {
+ code = pdf_update_halftone(pdev, pis, hts);
+ if (code < 0)
+ return code;
+ }
+ if (pdev->params.TransferFunctionInfo == tfi_Preserve &&
+ !pdev->PDFX && pdev->PDFA == 0
+ ) {
+ code = pdf_update_transfer(pdev, pis, trs);
+ if (code < 0)
+ return code;
+ }
+ if (pdev->params.UCRandBGInfo == ucrbg_Preserve) {
+ if (pis->black_generation && pdev->black_generation_id != pis->black_generation->id) {
+ code = pdf_write_transfer_map(pdev, pis->black_generation,
+ 0, false, "", bgs);
+ if (code < 0)
+ return code;
+ pdev->black_generation_id = pis->black_generation->id;
+ }
+ if (pis->undercolor_removal && pdev->undercolor_removal_id != pis->undercolor_removal->id) {
+ code = pdf_write_transfer_map(pdev, pis->undercolor_removal,
+ -1, false, "", ucrs);
+ if (code < 0)
+ return code;
+ pdev->undercolor_removal_id = pis->undercolor_removal->id;
+ }
+ }
+ if (hts[0] || trs[0] || bgs[0] || ucrs[0]) {
+ code = pdf_open_gstate(pdev, ppres);
+ if (code < 0)
+ return code;
+ }
+ if (hts[0]) {
+ code = cos_dict_put_string_copy(resource_dict(*ppres), "/HT", hts);
+ if (code < 0)
+ return code;
+ }
+ if (trs[0]) {
+ code = cos_dict_put_string_copy(resource_dict(*ppres), "/TR", trs);
+ if (code < 0)
+ return code;
+ }
+ if (bgs[0]) {
+ code = cos_dict_put_string_copy(resource_dict(*ppres), "/BG", bgs);
+ if (code < 0)
+ return code;
+ }
+ if (ucrs[0]) {
+ code = cos_dict_put_string_copy(resource_dict(*ppres), "/UCR", ucrs);
+ if (code < 0)
+ return code;
+ }
+ if (!pdev->PDFX) {
+ gs_currentscreenphase_pis(pis, &phase, 0);
+ gs_currentscreenphase_pis(&pdev->state, &dev_phase, 0);
+ if (dev_phase.x != phase.x || dev_phase.y != phase.y) {
+ char buf[sizeof(int) * 3 + 5];
+
+ code = pdf_open_gstate(pdev, ppres);
+ if (code < 0)
+ return code;
+ gs_sprintf(buf, "[%d %d]", phase.x, phase.y);
+ code = cos_dict_put_string_copy(resource_dict(*ppres), "/HTP", buf);
+ if (code < 0)
+ return code;
+ gx_imager_setscreenphase(&pdev->state, phase.x, phase.y,
+ gs_color_select_all);
+ }
+ }
+ }
+ if (pdev->CompatibilityLevel >= 1.3 && pdev->sbstack_depth == bottom) {
+ if (pdev->overprint_mode != pdev->params.OPM) {
+ code = pdf_open_gstate(pdev, ppres);
+ if (code < 0)
+ return code;
+ code = cos_dict_put_c_key_int(resource_dict(*ppres), "/OPM", pdev->params.OPM);
+ if (code < 0)
+ return code;
+ pdev->overprint_mode = pdev->params.OPM;
+ }
+ if (pdev->state.smoothness != pis->smoothness) {
+ code = pdf_open_gstate(pdev, ppres);
+ if (code < 0)
+ return code;
+ code = cos_dict_put_c_key_real(resource_dict(*ppres), "/SM", pis->smoothness);
+ if (code < 0)
+ return code;
+ pdev->state.smoothness = pis->smoothness;
+ }
+ if (pdev->CompatibilityLevel >= 1.4) {
+ if (pdev->state.text_knockout != pis->text_knockout) {
+ code = pdf_open_gstate(pdev, ppres);
+ if (code < 0)
+ return code;
+ code = cos_dict_put_c_key_bool(resource_dict(*ppres), "/TK", pis->text_knockout);
+ if (code < 0)
+ return code;
+ pdev->state.text_knockout = pis->text_knockout;
+ }
+ }
+ }
+ return code;
+}
+
+/* Update the graphics state for filling. */
+int
+pdf_try_prepare_fill(gx_device_pdf *pdev, const gs_imager_state *pis)
+{
+ pdf_resource_t *pres = 0;
+ int code = pdf_prepare_drawing(pdev, pis, &pres);
+
+ if (code < 0)
+ return code;
+ /* Update overprint. */
+ if (pdev->params.PreserveOverprintSettings &&
+ (pdev->fill_overprint != pis->overprint ||
+ pdev->font3) && !pdev->skip_colors
+ ) {
+ code = pdf_open_gstate(pdev, &pres);
+ if (code < 0)
+ return code;
+ /* PDF 1.2 only has a single overprint setting. */
+ if (pdev->CompatibilityLevel < 1.3) {
+ code = cos_dict_put_c_key_bool(resource_dict(pres), "/OP", pis->overprint);
+ if (code < 0)
+ return code;
+ pdev->stroke_overprint = pis->overprint;
+ } else {
+ code = cos_dict_put_c_key_bool(resource_dict(pres), "/op", pis->overprint);
+ if (code < 0)
+ return code;
+ }
+ pdev->fill_overprint = pis->overprint;
+ }
+ return pdf_end_gstate(pdev, pres);
+}
+int
+pdf_prepare_fill(gx_device_pdf *pdev, const gs_imager_state *pis)
+{
+ int code;
+
+ if (pdev->context != PDF_IN_STREAM) {
+ code = pdf_try_prepare_fill(pdev, pis);
+ if (code != gs_error_interrupt) /* See pdf_open_gstate */
+ return code;
+ code = pdf_open_contents(pdev, PDF_IN_STREAM);
+ if (code < 0)
+ return code;
+ }
+ return pdf_try_prepare_fill(pdev, pis);
+}
+
+/* Update the graphics state for stroking. */
+static int
+pdf_try_prepare_stroke(gx_device_pdf *pdev, const gs_imager_state *pis)
+{
+ pdf_resource_t *pres = 0;
+ int code = pdf_prepare_drawing(pdev, pis, &pres);
+
+ if (code < 0)
+ return code;
+ /* Update overprint, stroke adjustment. */
+ if (pdev->params.PreserveOverprintSettings &&
+ pdev->stroke_overprint != pis->overprint &&
+ !pdev->skip_colors
+ ) {
+ code = pdf_open_gstate(pdev, &pres);
+ if (code < 0)
+ return code;
+ code = cos_dict_put_c_key_bool(resource_dict(pres), "/OP", pis->overprint);
+ if (code < 0)
+ return code;
+ pdev->stroke_overprint = pis->overprint;
+ if (pdev->CompatibilityLevel < 1.3) {
+ /* PDF 1.2 only has a single overprint setting. */
+ pdev->fill_overprint = pis->overprint;
+ } else {
+ /* According to PDF>=1.3 spec, OP also sets op,
+ if there is no /op in same garphic state object.
+ We don't write /op, so monitor the viewer's state here : */
+ pdev->fill_overprint = pis->overprint;
+ }
+ }
+ if (pdev->state.stroke_adjust != pis->stroke_adjust) {
+ code = pdf_open_gstate(pdev, &pres);
+ if (code < 0)
+ return code;
+ code = cos_dict_put_c_key_bool(resource_dict(pres), "/SA", pis->stroke_adjust);
+ if (code < 0)
+ return code;
+ pdev->state.stroke_adjust = pis->stroke_adjust;
+ }
+ return pdf_end_gstate(pdev, pres);
+}
+int
+pdf_prepare_stroke(gx_device_pdf *pdev, const gs_imager_state *pis)
+{
+ int code;
+
+ if (pdev->context != PDF_IN_STREAM) {
+ code = pdf_try_prepare_stroke(pdev, pis);
+ if (code != gs_error_interrupt) /* See pdf_open_gstate */
+ return code;
+ code = pdf_open_contents(pdev, PDF_IN_STREAM);
+ if (code < 0)
+ return code;
+ }
+ return pdf_try_prepare_stroke(pdev, pis);
+}
+
+/* Update the graphics state for an image other than an ImageType 1 mask. */
+int
+pdf_prepare_image(gx_device_pdf *pdev, const gs_imager_state *pis)
+{
+ /*
+ * As it turns out, this requires updating the same parameters as for
+ * filling.
+ */
+ return pdf_prepare_fill(pdev, pis);
+}
+
+/* Update the graphics state for an ImageType 1 mask. */
+int
+pdf_prepare_imagemask(gx_device_pdf *pdev, const gs_imager_state *pis,
+ const gx_drawing_color *pdcolor)
+{
+ int code = pdf_prepare_image(pdev, pis);
+
+ if (code < 0)
+ return code;
+ return pdf_set_drawing_color(pdev, pis, pdcolor, &pdev->saved_fill_color,
+ &pdev->fill_used_process_color,
+ &psdf_set_fill_color_commands);
+}
diff --git a/devices/vector/gdevpdfg.h b/devices/vector/gdevpdfg.h
new file mode 100644
index 000000000..9c99a5326
--- /dev/null
+++ b/devices/vector/gdevpdfg.h
@@ -0,0 +1,365 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Internal graphics interfaces for PDF-writing driver. */
+
+#ifndef gdevpdfg_INCLUDED
+# define gdevpdfg_INCLUDED
+
+#include "gscspace.h" /* for gs_separation_name */
+
+#ifndef pdf_base_font_DEFINED
+# define pdf_base_font_DEFINED
+typedef struct pdf_base_font_s pdf_base_font_t;
+#endif
+
+/* ---------------- Exported by gdevpdfc.c ---------------- */
+
+/* Define standard and short color space names. */
+/*
+ * Based on Adobe's published PDF documentation, it appears that the
+ * abbreviations for the calibrated color spaces were introduced in
+ * PDF 1.1 (added to Table 7.3) and removed in PDF 1.2 (Table 8.1)!
+ */
+typedef struct pdf_color_space_names_s {
+ const char *DeviceCMYK;
+ const char *DeviceGray;
+ const char *DeviceRGB;
+ const char *Indexed;
+} pdf_color_space_names_t;
+#define PDF_COLOR_SPACE_NAMES\
+ "/DeviceCMYK", "/DeviceGray", "/DeviceRGB", "/Indexed"
+#define PDF_COLOR_SPACE_NAMES_SHORT\
+ "/CMYK", "/G", "/RGB", "/I"
+extern const pdf_color_space_names_t
+ pdf_color_space_names,
+ pdf_color_space_names_short;
+
+/* Define an abstract color space type. */
+#ifndef gs_color_space_DEFINED
+# define gs_color_space_DEFINED
+typedef struct gs_color_space_s gs_color_space;
+#endif
+
+/*
+ * Define a ColorSpace resource. We need to retain the range scaling
+ * information (if any).
+ */
+typedef struct pdf_color_space_s pdf_color_space_t;
+struct pdf_color_space_s {
+ pdf_resource_common(pdf_color_space_t);
+ const gs_range_t *ranges;
+ uint serialized_size;
+ byte *serialized;
+};
+/* The descriptor is public because it is for a resource type. */
+#define public_st_pdf_color_space() /* in gdevpdfc.c */\
+ gs_public_st_suffix_add2(st_pdf_color_space, pdf_color_space_t,\
+ "pdf_color_space_t", pdf_color_space_enum_ptrs,\
+ pdf_color_space_reloc_ptrs, st_pdf_resource, ranges, serialized)
+
+/*
+ * Create a local Device{Gray,RGB,CMYK} color space corresponding to the
+ * given number of components.
+ */
+int pdf_cspace_init_Device(gs_memory_t *mem, gs_color_space **ppcs, int num_components);
+
+/* test an (image) colour space to see if its ICCBased. If so we will
+ * convert to current device space
+ */
+int pdf_convert_ICC(gx_device_pdf *pdev,
+ const gs_color_space *pcs, cos_value_t *pvalue,
+ const pdf_color_space_names_t *pcsn);
+/*
+ * Create a PDF color space corresponding to a PostScript color space.
+ * For parameterless color spaces, set *pvalue to a (literal) string with
+ * the color space name; for other color spaces, create a cos_array_t if
+ * necessary and set *pvalue to refer to it. In the latter case, if
+ * by_name is true, return a string /Rxxxx rather than a reference to
+ * the actual object.
+ *
+ * If ppranges is not NULL, then if the domain of the color space had
+ * to be scaled (to convert a CIEBased space to ICCBased), store a pointer
+ * to the ranges in *ppranges, otherwise set *ppranges to 0.
+ */
+int pdf_color_space_named(gx_device_pdf *pdev, const gs_imager_state * pis,
+ cos_value_t *pvalue,
+ const gs_range_t **ppranges,
+ const gs_color_space *pcs,
+ const pdf_color_space_names_t *pcsn,
+ bool by_name, const byte *res_name, int name_length, bool keepICC);
+
+int free_color_space(gx_device_pdf *pdev, pdf_resource_t *pres);
+int pdf_indexed_color_space(gx_device_pdf *pdev, const gs_imager_state * pis, cos_value_t *pvalue,
+ const gs_color_space *pcs, cos_array_t *pca, cos_value_t *cos_base);
+
+int convert_separation_alternate(gx_device_pdf * pdev, const gs_imager_state * pis, const gs_color_space *pcs,
+ const gx_drawing_color *pdc, bool *used_process_color,
+ const psdf_set_color_commands_t *ppscc, gs_client_color *pcc, cos_value_t *pvalue, bool by_name);
+int convert_DeviceN_alternate(gx_device_pdf * pdev, const gs_imager_state * pis, const gs_color_space *pcs,
+ const gx_drawing_color *pdc, bool *used_process_color,
+ const psdf_set_color_commands_t *ppscc, gs_client_color *pcc, cos_value_t *pvalue, bool by_name);
+/* Create colored and uncolored Pattern color spaces. */
+int pdf_cs_Pattern_colored(gx_device_pdf *pdev, cos_value_t *pvalue);
+int pdf_cs_Pattern_uncolored(gx_device_pdf *pdev, cos_value_t *pvalue);
+int pdf_cs_Pattern_uncolored_hl(gx_device_pdf *pdev,
+ const gs_color_space *pcs, cos_value_t *pvalue, const gs_imager_state * pis);
+
+/* Set the ProcSets bits corresponding to an image color space. */
+void pdf_color_space_procsets(gx_device_pdf *pdev,
+ const gs_color_space *pcs);
+
+/* ---------------- Exported by gdevpdfg.c ---------------- */
+
+/* Copy viewer state from images state. */
+void pdf_viewer_state_from_imager_state(gx_device_pdf * pdev,
+ const gs_imager_state *pis, const gx_device_color *pdevc);
+
+/* Prepare intitial values for viewer's graphics state parameters. */
+void pdf_prepare_initial_viewer_state(gx_device_pdf * pdev, const gs_imager_state *pis);
+
+/* Reset the graphics state parameters to initial values. */
+void pdf_reset_graphics(gx_device_pdf *pdev);
+
+/* Set initial color. */
+void pdf_set_initial_color(gx_device_pdf * pdev, gx_hl_saved_color *saved_fill_color,
+ gx_hl_saved_color *saved_stroke_color,
+ bool *fill_used_process_color, bool *stroke_used_process_color);
+
+void rescale_cie_color(gs_range_t *ranges, int num_colorants,
+ const gs_client_color *src, gs_client_color *des);
+/* Set the fill or stroke color. */
+int pdf_reset_color(gx_device_pdf * pdev, const gs_imager_state * pis,
+ const gx_drawing_color *pdc, gx_hl_saved_color * psc,
+ bool *used_process_color,
+ const psdf_set_color_commands_t *ppscc);
+/* pdecolor is &pdev->fill_color or &pdev->stroke_color. */
+int pdf_set_pure_color(gx_device_pdf * pdev, gx_color_index color,
+ gx_hl_saved_color * psc,
+ bool *used_process_color,
+ const psdf_set_color_commands_t *ppscc);
+int pdf_set_drawing_color(gx_device_pdf * pdev, const gs_imager_state * pis,
+ const gx_drawing_color *pdc,
+ gx_hl_saved_color * psc,
+ bool *used_process_color,
+ const psdf_set_color_commands_t *ppscc);
+/*
+ * Bring the graphics state up to date for a drawing operation.
+ * (Text uses either fill or stroke.)
+ */
+int pdf_try_prepare_fill(gx_device_pdf *pdev, const gs_imager_state *pis);
+int pdf_prepare_drawing(gx_device_pdf *pdev, const gs_imager_state *pis, pdf_resource_t **ppres);
+int pdf_prepare_fill(gx_device_pdf *pdev, const gs_imager_state *pis);
+int pdf_prepare_stroke(gx_device_pdf *pdev, const gs_imager_state *pis);
+int pdf_prepare_image(gx_device_pdf *pdev, const gs_imager_state *pis);
+int pdf_prepare_imagemask(gx_device_pdf *pdev, const gs_imager_state *pis,
+ const gx_drawing_color *pdcolor);
+int pdf_save_viewer_state(gx_device_pdf *pdev, stream *s);
+int pdf_restore_viewer_state(gx_device_pdf *pdev, stream *s);
+int pdf_end_gstate(gx_device_pdf *pdev, pdf_resource_t *pres);
+
+/*
+ * Convert a string into cos name.
+ */
+int pdf_string_to_cos_name(gx_device_pdf *pdev, const byte *str, uint len,
+ cos_value_t *pvalue);
+
+/* ---------------- Exported by gdevpdfi.c ---------------- */
+
+typedef struct pdf_pattern_s pdf_pattern_t;
+struct pdf_pattern_s {
+ pdf_resource_common(pdf_pattern_t);
+ pdf_pattern_t *substitute;
+};
+/* The descriptor is public because it is for a resource type. */
+#define private_st_pdf_pattern() /* in gdevpdfc.c */\
+ gs_private_st_suffix_add1(st_pdf_pattern, pdf_pattern_t,\
+ "pdf_pattern_t", pdf_pattern_enum_ptrs,\
+ pdf_pattern_reloc_ptrs, st_pdf_resource, substitute)
+
+pdf_resource_t *pdf_substitute_pattern(pdf_resource_t *pres);
+
+/* ---------------- Exported by gdevpdfj.c ---------------- */
+
+/* ------ Image stream dictionaries ------ */
+
+/* Define the long and short versions of the keys in an image dictionary, */
+/* and other strings for images. */
+typedef struct pdf_image_names_s {
+ pdf_color_space_names_t color_spaces;
+ pdf_filter_names_t filter_names;
+ const char *BitsPerComponent;
+ const char *ColorSpace;
+ const char *Decode;
+ const char *Height;
+ const char *ImageMask;
+ const char *Interpolate;
+ const char *Width;
+} pdf_image_names_t;
+#define PDF_IMAGE_PARAM_NAMES\
+ "/BitsPerComponent", "/ColorSpace", "/Decode",\
+ "/Height", "/ImageMask", "/Interpolate", "/Width"
+#define PDF_IMAGE_PARAM_NAMES_SHORT\
+ "/BPC", "/CS", "/D", "/H", "/IM", "/I", "/W"
+extern const pdf_image_names_t pdf_image_names_full, pdf_image_names_short;
+
+/* Store the values of image parameters other than filters. */
+/* pdev is used only for updating procsets. */
+/* pcsvalue is not used for masks. */
+int pdf_put_image_values(cos_dict_t *pcd, gx_device_pdf *pdev,
+ const gs_pixel_image_t *pic,
+ const pdf_image_names_t *pin,
+ const cos_value_t *pcsvalue);
+
+/* Store filters for an image. */
+/* Currently this only saves parameters for CCITTFaxDecode. */
+int pdf_put_image_filters(cos_dict_t *pcd, gx_device_pdf *pdev,
+ const psdf_binary_writer * pbw,
+ const pdf_image_names_t *pin);
+
+/* ------ Image writing ------ */
+
+/*
+ * Fill in the image parameters for a device space bitmap.
+ * PDF images are always specified top-to-bottom.
+ * data_h is the actual number of data rows, which may be less than h.
+ */
+void pdf_make_bitmap_matrix(gs_matrix * pmat, int x, int y, int w, int h,
+ int h_actual);
+
+/* Put out the gsave and matrix for an image. */
+void pdf_put_image_matrix(gx_device_pdf * pdev, const gs_matrix * pmat,
+ double y_scale);
+
+/* Put out a reference to an image resource. */
+int pdf_do_image_by_id(gx_device_pdf * pdev, double scale,
+ const gs_matrix * pimat, bool in_contents, gs_id id);
+int pdf_do_image(gx_device_pdf * pdev, const pdf_resource_t * pres,
+ const gs_matrix * pimat, bool in_contents);
+
+#define pdf_image_writer_num_alt_streams 4
+
+/* Define the structure for writing an image. */
+typedef struct pdf_image_writer_s {
+ psdf_binary_writer binary[pdf_image_writer_num_alt_streams];
+ int alt_writer_count; /* no. of active elements in writer[] (1,2,3) */
+ const pdf_image_names_t *pin;
+ pdf_resource_t *pres; /* XObject resource iff not in-line */
+ int height; /* initially specified image height */
+ cos_stream_t *data;
+ const char *end_string; /* string to write after EI if in-line */
+ cos_dict_t *named; /* named dictionary from NI */
+ pdf_resource_t *pres_mask; /* PS2WRITE only : XObject resource for mask */
+} pdf_image_writer;
+extern_st(st_pdf_image_writer); /* public for gdevpdfi.c */
+#define public_st_pdf_image_writer() /* in gdevpdfj.c */\
+ gs_public_st_composite(st_pdf_image_writer, pdf_image_writer,\
+ "pdf_image_writer", pdf_image_writer_enum_ptrs, pdf_image_writer_reloc_ptrs)
+#define pdf_image_writer_max_ptrs (psdf_binary_writer_max_ptrs * pdf_image_writer_num_alt_streams + 4)
+
+/* Initialize image writer. */
+void pdf_image_writer_init(pdf_image_writer * piw);
+
+/*
+ * Begin writing an image, creating the resource if not in-line, and setting
+ * up the binary writer. If pnamed != 0, it is a dictionary object created
+ * by a NI pdfmark.
+ */
+int pdf_begin_write_image(gx_device_pdf * pdev, pdf_image_writer * piw,
+ gx_bitmap_id id, int w, int h,
+ cos_dict_t *pnamed, bool in_line);
+
+/* Begin writing the image data, setting up the dictionary and filters. */
+int pdf_begin_image_data(gx_device_pdf * pdev, pdf_image_writer * piw,
+ const gs_pixel_image_t * pim,
+ const cos_value_t *pcsvalue,
+ int alt_writer_index);
+
+/* Copy the data for a mask or monobit bitmap. */
+int pdf_copy_mask_bits(stream *s, const byte *base, int sourcex,
+ int raster, int w, int h, byte invert);
+
+/* Copy the data for a colored image (device pixels). */
+int pdf_copy_color_bits(stream *s, const byte *base, int sourcex,
+ int raster, int w, int h, int bytes_per_pixel);
+
+/* Complete image data. */
+int
+pdf_complete_image_data(gx_device_pdf *pdev, pdf_image_writer *piw, int data_h,
+ int width, int bits_per_pixel);
+
+/* Finish writing the binary image data. */
+int pdf_end_image_binary(gx_device_pdf *pdev, pdf_image_writer *piw,
+ int data_h);
+
+/*
+ * Finish writing an image. If in-line, write the BI/dict/ID/data/EI and
+ * return 1; if a resource, write the resource definition and return 0.
+ */
+int pdf_end_write_image(gx_device_pdf * pdev, pdf_image_writer * piw);
+
+/*
+ * Make alternative stream for image compression choice.
+ */
+int pdf_make_alt_stream(gx_device_pdf * pdev, psdf_binary_writer * piw);
+
+/*
+ * End binary with choosing image compression.
+ */
+int pdf_choose_compression(pdf_image_writer * piw, bool end_binary);
+
+/* If the current substream is a charproc, register a font used in it. */
+int pdf_register_charproc_resource(gx_device_pdf *pdev, gs_id id, pdf_resource_type_t type);
+
+/* ---------------- Exported by gdevpdfv.c ---------------- */
+
+/* Store pattern 1 parameters to cos dictionary. */
+int pdf_store_pattern1_params(gx_device_pdf *pdev, pdf_resource_t *pres,
+ gs_pattern1_instance_t *pinst);
+
+/* Write a colored Pattern color. */
+int pdf_put_colored_pattern(gx_device_pdf *pdev, const gx_drawing_color *pdc,
+ const gs_color_space *pcs,
+ const psdf_set_color_commands_t *ppscc,
+ const gs_imager_state * pis, pdf_resource_t **ppres);
+
+/* Write an uncolored Pattern color. */
+int pdf_put_uncolored_pattern(gx_device_pdf *pdev, const gx_drawing_color *pdc,
+ const gs_color_space *pcs,
+ const psdf_set_color_commands_t *ppscc,
+ const gs_imager_state *pis, pdf_resource_t **ppres);
+
+/* Write a PatternType 2 (shading pattern) color. */
+int pdf_put_pattern2(gx_device_pdf *pdev, const gs_imager_state * pis, const gx_drawing_color *pdc,
+ const psdf_set_color_commands_t *ppscc,
+ pdf_resource_t **ppres);
+
+/* ---------------- Exported by gdevpdfb.c ---------------- */
+
+/* Copy a color bitmap. for_pattern = -1 means put the image in-line, */
+/* 1 means put the image in a resource. */
+int pdf_copy_color_data(gx_device_pdf * pdev, const byte * base, int sourcex,
+ int raster, gx_bitmap_id id, int x, int y, int w, int h,
+ gs_image_t *pim, pdf_image_writer *piw,
+ int for_pattern);
+
+#endif /* gdevpdfg_INCLUDED */
+
+/* ---------------- Exported by gdevpdfe.c ---------------- */
+
+/* Write metadata */
+int pdf_document_metadata(gx_device_pdf *pdev);
+int pdf_get_docinfo_item(gx_device_pdf *pdev, const char *key, char *buf, int buf_length);
diff --git a/devices/vector/gdevpdfi.c b/devices/vector/gdevpdfi.c
new file mode 100644
index 000000000..87b76b9ef
--- /dev/null
+++ b/devices/vector/gdevpdfi.c
@@ -0,0 +1,2925 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Image handling for PDF-writing driver */
+#include "memory_.h"
+#include "math_.h"
+#include "gx.h"
+#include "gserrors.h"
+#include "gsdevice.h"
+#include "gsflip.h"
+#include "gsstate.h"
+#include "gscolor2.h"
+#include "gdevpdfx.h"
+#include "gdevpdfg.h"
+#include "gdevpdfo.h" /* for data stream */
+#include "gxcspace.h"
+#include "gximage3.h"
+#include "gximag3x.h"
+#include "gsiparm4.h"
+#include "gxdcolor.h"
+#include "gxpcolor.h"
+#include "gxcolor2.h"
+#include "gxhldevc.h"
+#include "gxdevsop.h"
+#include "gsicc_manage.h"
+#include "gsform1.h"
+#include "gxpath.h"
+
+/* Forward references */
+static image_enum_proc_plane_data(pdf_image_plane_data);
+static image_enum_proc_end_image(pdf_image_end_image);
+static image_enum_proc_end_image(pdf_image_end_image_object);
+static image_enum_proc_end_image(pdf_image_end_image_object2);
+static image_enum_proc_end_image(pdf_image_end_image_cvd);
+static IMAGE3_MAKE_MID_PROC(pdf_image3_make_mid);
+static IMAGE3_MAKE_MCDE_PROC(pdf_image3_make_mcde);
+static IMAGE3X_MAKE_MID_PROC(pdf_image3x_make_mid);
+static IMAGE3X_MAKE_MCDE_PROC(pdf_image3x_make_mcde);
+
+static const gx_image_enum_procs_t pdf_image_enum_procs = {
+ pdf_image_plane_data,
+ pdf_image_end_image
+};
+static const gx_image_enum_procs_t pdf_image_object_enum_procs = {
+ pdf_image_plane_data,
+ pdf_image_end_image_object
+};
+static const gx_image_enum_procs_t pdf_image_object_enum_procs2 = {
+ pdf_image_plane_data,
+ pdf_image_end_image_object2
+};
+static const gx_image_enum_procs_t pdf_image_cvd_enum_procs = {
+ gx_image1_plane_data,
+ pdf_image_end_image_cvd,
+ gx_image1_flush
+};
+
+/* ---------------- Driver procedures ---------------- */
+
+/* Define the structure for keeping track of progress through an image. */
+typedef struct pdf_image_enum_s {
+ gx_image_enum_common;
+ int width;
+ int bits_per_pixel; /* bits per pixel (per plane) */
+ int rows_left;
+ pdf_image_writer writer;
+ gs_matrix mat;
+} pdf_image_enum;
+gs_private_st_composite(st_pdf_image_enum, pdf_image_enum, "pdf_image_enum",
+ pdf_image_enum_enum_ptrs, pdf_image_enum_reloc_ptrs);
+/* GC procedures */
+static ENUM_PTRS_WITH(pdf_image_enum_enum_ptrs, pdf_image_enum *pie)
+ if (index < pdf_image_writer_max_ptrs) {
+ gs_ptr_type_t ret =
+ ENUM_USING(st_pdf_image_writer, &pie->writer, sizeof(pie->writer),
+ index);
+
+ if (ret == 0) /* don't stop early */
+ ENUM_RETURN(0);
+ return ret;
+ }
+ return ENUM_USING_PREFIX(st_gx_image_enum_common,
+ pdf_image_writer_max_ptrs);
+ENUM_PTRS_END
+static RELOC_PTRS_WITH(pdf_image_enum_reloc_ptrs, pdf_image_enum *pie)
+{
+ RELOC_USING(st_pdf_image_writer, &pie->writer, sizeof(pie->writer));
+ RELOC_USING(st_gx_image_enum_common, vptr, size);
+}
+RELOC_PTRS_END
+
+/*
+ * Test whether we can write an image in-line. This is always true,
+ * because we only support PDF 1.2 and later.
+ */
+static bool
+can_write_image_in_line(const gx_device_pdf *pdev, const gs_image_t *pim)
+{
+ return true;
+}
+
+/*
+ * Convert a Type 4 image to a Type 1 masked image if possible.
+ * Type 1 masked images are more compact, and are supported in all PDF
+ * versions, whereas general masked images require PDF 1.3 or higher.
+ * Also, Acrobat 5 for Windows has a bug that causes an error for images
+ * with a color-key mask, at least for 1-bit-deep images using an Indexed
+ * color space.
+ */
+static int
+color_is_black_or_white(gx_device *dev, const gx_drawing_color *pdcolor)
+{
+ return (!color_is_pure(pdcolor) ? -1 :
+ gx_dc_pure_color(pdcolor) == gx_device_black(dev) ? 0 :
+ gx_dc_pure_color(pdcolor) == gx_device_white(dev) ? 1 : -1);
+}
+static int
+pdf_convert_image4_to_image1(gx_device_pdf *pdev,
+ const gs_imager_state *pis,
+ const gx_drawing_color *pbcolor,
+ const gs_image4_t *pim4, gs_image_t *pim1,
+ gx_drawing_color *pdcolor)
+{
+ if (pim4->BitsPerComponent == 1 &&
+ pim4->ColorSpace->type->num_components(pim4->ColorSpace) == 1 &&
+ (pim4->MaskColor_is_range ?
+ pim4->MaskColor[0] | pim4->MaskColor[1] :
+ pim4->MaskColor[0]) <= 1
+ ) {
+ gx_device *const dev = (gx_device *)pdev;
+ const gs_color_space *pcs = pim4->ColorSpace;
+ bool write_1s = !pim4->MaskColor[0];
+ gs_client_color cc;
+ int code;
+
+ /*
+ * Prepare the drawing color. (pdf_prepare_imagemask will set it.)
+ * This is the other color in the image (the one that isn't the
+ * mask key), taking Decode into account.
+ */
+
+ cc.paint.values[0] = pim4->Decode[(int)write_1s];
+ cc.pattern = 0;
+ code = pcs->type->remap_color(&cc, pcs, pdcolor, pis, dev,
+ gs_color_select_texture);
+ if (code < 0)
+ return code;
+
+ /*
+ * The PDF imaging model doesn't support RasterOp. We can convert a
+ * Type 4 image to a Type 1 imagemask only if the effective RasterOp
+ * passes through the source color unchanged. "Effective" means we
+ * take into account CombineWithColor, and whether the source and/or
+ * texture are black, white, or neither.
+ */
+ {
+ gs_logical_operation_t lop = pis->log_op;
+ int black_or_white = color_is_black_or_white(dev, pdcolor);
+
+ switch (black_or_white) {
+ case 0: lop = lop_know_S_0(lop); break;
+ case 1: lop = lop_know_S_1(lop); break;
+ default: DO_NOTHING;
+ }
+ if (pim4->CombineWithColor)
+ switch (color_is_black_or_white(dev, pbcolor)) {
+ case 0: lop = lop_know_T_0(lop); break;
+ case 1: lop = lop_know_T_1(lop); break;
+ default: DO_NOTHING;
+ }
+ else
+ lop = lop_know_T_0(lop);
+ switch (lop_rop(lop)) {
+ case rop3_0:
+ if (black_or_white != 0)
+ return -1;
+ break;
+ case rop3_1:
+ if (black_or_white != 1)
+ return -1;
+ break;
+ case rop3_S:
+ break;
+ default:
+ return -1;
+ }
+ if ((lop & lop_S_transparent) && black_or_white == 1)
+ return -1;
+ }
+
+ /* All conditions are met. Convert to a masked image. */
+
+ gs_image_t_init_mask_adjust(pim1, write_1s, false);
+#define COPY_ELEMENT(e) pim1->e = pim4->e
+ COPY_ELEMENT(ImageMatrix);
+ COPY_ELEMENT(Width);
+ COPY_ELEMENT(Height);
+ pim1->BitsPerComponent = 1;
+ /* not Decode */
+ COPY_ELEMENT(Interpolate);
+ pim1->format = gs_image_format_chunky; /* BPC = 1, doesn't matter */
+#undef COPY_ELEMENT
+ return 0;
+ }
+ return -1; /* arbitrary <0 */
+}
+
+static int
+pdf_begin_image_data_decoded(gx_device_pdf *pdev, int num_components, const gs_range_t *pranges, int i,
+ gs_pixel_image_t *pi, cos_value_t *cs_value, pdf_image_enum *pie)
+{
+
+ if (pranges) {
+ /* Rescale the Decode values for the image data. */
+ const gs_range_t *pr = pranges;
+ float *decode = pi->Decode;
+ int j;
+
+ for (j = 0; j < num_components; ++j, ++pr, decode += 2) {
+ double vmin = decode[0], vmax = decode[1];
+ double base = pr->rmin, factor = pr->rmax - base;
+
+ decode[1] = (vmax - vmin) / factor + (vmin - base);
+ decode[0] = vmin - base;
+ }
+ }
+ return pdf_begin_image_data(pdev, &pie->writer, pi, cs_value, i);
+}
+
+static int
+make_device_color_space(gx_device_pdf *pdev,
+ gs_color_space_index output_cspace_index,
+ gs_color_space **ppcs)
+{
+ gs_color_space *cs;
+ gs_memory_t *mem = pdev->v_memory;
+
+ switch (output_cspace_index) {
+ case gs_color_space_index_DeviceGray:
+ cs = gs_cspace_new_DeviceGray(mem);
+ break;
+ case gs_color_space_index_DeviceRGB:
+ cs = gs_cspace_new_DeviceRGB(mem);
+ break;
+ case gs_color_space_index_DeviceCMYK:
+ cs = gs_cspace_new_DeviceCMYK(mem);
+ break;
+ default:
+ /* Notify the user and terminate.
+ Don't emit rangecheck becuause it would fall back
+ to a default implementation (rasterisation).
+ */
+ emprintf(mem, "Unsupported ProcessColorModel");
+ return_error(gs_error_undefined);
+ }
+ if (cs == NULL)
+ return_error(gs_error_VMerror);
+ *ppcs = cs;
+ return 0;
+}
+
+static bool
+check_image_color_space(gs_pixel_image_t * pim, gs_color_space_index index)
+{
+ if (pim->ColorSpace->type->index == index)
+ return true;
+ if (pim->ColorSpace->type->index == gs_color_space_index_Indexed)
+ if (pim->ColorSpace->base_space->type->index == index)
+ return true;
+ return false;
+}
+
+/*
+ * Start processing an image. This procedure takes extra arguments because
+ * it has to do something slightly different for the parts of an ImageType 3
+ * image.
+ */
+typedef enum {
+ PDF_IMAGE_DEFAULT,
+ PDF_IMAGE_TYPE3_MASK, /* no in-line, don't render */
+ PDF_IMAGE_TYPE3_DATA /* no in-line */
+} pdf_typed_image_context_t;
+
+/*
+ * We define this union because psdf_setup_image_filters may alter the
+ * gs_pixel_image_t part, but pdf_begin_image_data must also have access
+ * to the type-specific parameters.
+ */
+typedef union image_union_s {
+ gs_pixel_image_t pixel; /* we may change some components */
+ gs_image1_t type1;
+ gs_image3_t type3;
+ gs_image3x_t type3x;
+ gs_image4_t type4;
+} image_union_t;
+
+static int pdf_begin_typed_image(gx_device_pdf *pdev,
+ const gs_imager_state * pis, const gs_matrix *pmat,
+ const gs_image_common_t *pic, const gs_int_rect * prect,
+ const gx_drawing_color * pdcolor, const gx_clip_path * pcpath,
+ gs_memory_t * mem, gx_image_enum_common_t ** pinfo,
+ pdf_typed_image_context_t context);
+
+static int
+pdf_begin_typed_image_impl(gx_device_pdf *pdev, const gs_imager_state * pis,
+ const gs_matrix *pmat, const gs_image_common_t *pic,
+ const gs_int_rect * prect,
+ const gx_drawing_color * pdcolor,
+ const gx_clip_path * pcpath, gs_memory_t * mem,
+ gx_image_enum_common_t ** pinfo,
+ pdf_typed_image_context_t context,
+ image_union_t *image)
+{
+ cos_dict_t *pnamed = 0;
+ const gs_pixel_image_t *pim;
+ int code, i;
+ pdf_image_enum *pie;
+ gs_image_format_t format;
+ const gs_color_space *pcs;
+ cos_value_t cs_value;
+ int num_components;
+ bool is_mask = false, in_line = false;
+ gs_int_rect rect;
+ int width, height;
+ const gs_range_t *pranges = 0;
+ const pdf_color_space_names_t *names;
+ bool convert_to_process_colors = false;
+ gs_color_space *pcs_device = NULL;
+ gs_color_space *pcs_orig = NULL;
+ int BPC_orig = 1, BPC_device = 8;
+ pdf_lcvd_t *cvd = NULL;
+ bool force_lossless = false;
+
+ /*
+ * Pop the image name from the NI stack. We must do this, to keep the
+ * stack in sync, even if it turns out we can't handle the image.
+ */
+ {
+ cos_value_t ni_value;
+
+ if (cos_array_unadd(pdev->NI_stack, &ni_value) >= 0)
+ pnamed = (cos_dict_t *)ni_value.contents.object;
+ }
+
+ /* An initialization for pdf_end_and_do_image :
+ We need to delay adding the "Mask" entry into a type 3 image dictionary
+ until the mask is completed due to equal image merging. */
+ pdev->image_mask_id = gs_no_id;
+
+ /* Check for the image types we can handle. */
+ switch (pic->type->index) {
+ case 1: {
+ const gs_image_t *pim1 = (const gs_image_t *)pic;
+
+ if (pim1->Alpha != gs_image_alpha_none)
+ goto nyi;
+ is_mask = pim1->ImageMask;
+ if (is_mask) {
+ /* If parameters are invalid, use the default implementation. */
+ if (!(gx_dc_is_pattern1_color(pdcolor)))
+ if (pim1->BitsPerComponent != 1 ||
+ !((pim1->Decode[0] == 0.0 && pim1->Decode[1] == 1.0) ||
+ (pim1->Decode[0] == 1.0 && pim1->Decode[1] == 0.0))
+ )
+ goto nyi;
+ }
+ /* If image is not type 3X and we can write in-line then make it so */
+
+ in_line = context == PDF_IMAGE_DEFAULT &&
+ can_write_image_in_line(pdev, pim1);
+
+ image[0].type1 = *pim1;
+ break;
+ }
+ case 3: {
+ const gs_image3_t *pim3 = (const gs_image3_t *)pic;
+ gs_image3_t pim3a;
+ const gs_image_common_t *pic1 = pic;
+ gs_matrix m, mi;
+ const gs_matrix *pmat1 = pmat;
+
+ pdev->image_mask_is_SMask = false;
+ if (pdev->CompatibilityLevel < 1.2)
+ goto nyi;
+ if (prect && !(prect->p.x == 0 && prect->p.y == 0 &&
+ prect->q.x == pim3->Width &&
+ prect->q.y == pim3->Height))
+ goto nyi;
+ if (pdev->CompatibilityLevel < 1.3 && !pdev->PatternImagemask) {
+ if (pdf_must_put_clip_path(pdev, pcpath))
+ code = pdf_unclip(pdev);
+ else
+ code = pdf_open_page(pdev, PDF_IN_STREAM);
+ if (code < 0)
+ return code;
+ code = pdf_put_clip_path(pdev, pcpath);
+ if (code < 0)
+ return code;
+ gs_make_identity(&m);
+ pmat1 = &m;
+ m.tx = floor(pis->ctm.tx + 0.5); /* Round the origin against the image size distorsions */
+ m.ty = floor(pis->ctm.ty + 0.5);
+ pim3a = *pim3;
+ gs_matrix_invert(&pim3a.ImageMatrix, &mi);
+ gs_make_identity(&pim3a.ImageMatrix);
+ if (pim3a.Width < pim3a.MaskDict.Width && pim3a.Width > 0) {
+ int sx = (pim3a.MaskDict.Width + pim3a.Width - 1) / pim3a.Width;
+
+ gs_matrix_scale(&mi, 1.0 / sx, 1, &mi);
+ gs_matrix_scale(&pim3a.ImageMatrix, 1.0 / sx, 1, &pim3a.ImageMatrix);
+ }
+ if (pim3a.Height < pim3a.MaskDict.Height && pim3a.Height > 0) {
+ int sy = (pim3a.MaskDict.Height + pim3a.Height - 1) / pim3a.Height;
+
+ gs_matrix_scale(&mi, 1, 1.0 / sy, &mi);
+ gs_matrix_scale(&pim3a.ImageMatrix, 1, 1.0 / sy, &pim3a.ImageMatrix);
+ }
+ gs_matrix_multiply(&mi, &pim3a.MaskDict.ImageMatrix, &pim3a.MaskDict.ImageMatrix);
+ pic1 = (gs_image_common_t *)&pim3a;
+ /* Setting pdev->converting_image_matrix to communicate with pdf_image3_make_mcde. */
+ gs_matrix_multiply(&mi, &ctm_only(pis), &pdev->converting_image_matrix);
+ }
+ /*
+ * We handle ImageType 3 images in a completely different way:
+ * the default implementation sets up the enumerator.
+ */
+ return gx_begin_image3_generic((gx_device *)pdev, pis, pmat1, pic1,
+ prect, pdcolor, pcpath, mem,
+ pdf_image3_make_mid,
+ pdf_image3_make_mcde, pinfo);
+ }
+ case IMAGE3X_IMAGETYPE: {
+ /* See ImageType3 above for more information. */
+ const gs_image3x_t *pim3x = (const gs_image3x_t *)pic;
+
+ if (pdev->CompatibilityLevel < 1.4)
+ goto nyi;
+ if (prect && !(prect->p.x == 0 && prect->p.y == 0 &&
+ prect->q.x == pim3x->Width &&
+ prect->q.y == pim3x->Height))
+ goto nyi;
+ pdev->image_mask_is_SMask = true;
+ return gx_begin_image3x_generic((gx_device *)pdev, pis, pmat, pic,
+ prect, pdcolor, pcpath, mem,
+ pdf_image3x_make_mid,
+ pdf_image3x_make_mcde, pinfo);
+ }
+ case 4: {
+ /* Try to convert the image to a plain masked image. */
+ gx_drawing_color icolor;
+
+ pdev->image_mask_is_SMask = false;
+ if (pdf_convert_image4_to_image1(pdev, pis, pdcolor,
+ (const gs_image4_t *)pic,
+ &image[0].type1, &icolor) >= 0) {
+ gs_state *pgs = (gs_state *)gx_hld_get_gstate_ptr(pis);
+
+ if (pgs == NULL)
+ return_error(gs_error_unregistered); /* Must not happen. */
+
+ /* Undo the pop of the NI stack if necessary. */
+ if (pnamed)
+ cos_array_add_object(pdev->NI_stack, COS_OBJECT(pnamed));
+ /* HACK: temporary patch the color space, to allow
+ pdf_prepare_imagemask to write the right color for the imagemask. */
+ code = gs_gsave(pgs);
+ if (code < 0)
+ return code;
+ /* {csrc}: const cast warning */
+ code = gs_setcolorspace(pgs, ((const gs_image4_t *)pic)->ColorSpace);
+ if (code < 0)
+ return code;
+ code = pdf_begin_typed_image(pdev, pis, pmat,
+ (gs_image_common_t *)&image[0].type1,
+ prect, &icolor, pcpath, mem,
+ pinfo, context);
+ if (code < 0)
+ return code;
+ return gs_grestore(pgs);
+ }
+ /* No luck. Masked images require PDF 1.3 or higher. */
+ if (pdev->CompatibilityLevel < 1.2 || pdev->params.ColorConversionStrategy != ccs_LeaveColorUnchanged)
+ goto nyi;
+ if (pdev->CompatibilityLevel < 1.3 && !pdev->PatternImagemask) {
+ gs_matrix m, m1, mi;
+ gs_image4_t pi4 = *(const gs_image4_t *)pic;
+
+ if (pdf_must_put_clip_path(pdev, pcpath))
+ code = pdf_unclip(pdev);
+ else
+ code = pdf_open_page(pdev, PDF_IN_STREAM);
+ if (code < 0)
+ return code;
+ code = pdf_put_clip_path(pdev, pcpath);
+ if (code < 0)
+ return code;
+ gs_make_identity(&m1);
+ gs_matrix_invert(&pic->ImageMatrix, &mi);
+ gs_matrix_multiply(&mi, &ctm_only(pis), &m);
+ code = pdf_setup_masked_image_converter(pdev, mem, &m, &cvd,
+ true, 0, 0, pi4.Width, pi4.Height, false);
+ if (code < 0)
+ return code;
+ cvd->mdev.is_open = true; /* fixme: same as above. */
+ cvd->mask->is_open = true; /* fixme: same as above. */
+ cvd->mask_is_empty = false;
+ code = (*dev_proc(cvd->mask, fill_rectangle))((gx_device *)cvd->mask,
+ 0, 0, cvd->mask->width, cvd->mask->height, (gx_color_index)0);
+ if (code < 0)
+ return code;
+ gx_device_retain((gx_device *)cvd, true);
+ gx_device_retain((gx_device *)cvd->mask, true);
+ gs_make_identity(&pi4.ImageMatrix);
+ code = gx_default_begin_typed_image((gx_device *)cvd,
+ pis, &m1, (gs_image_common_t *)&pi4, prect, pdcolor, NULL, mem, pinfo);
+ if (code < 0)
+ return code;
+ (*pinfo)->procs = &pdf_image_cvd_enum_procs;
+ return 0;
+ }
+ image[0].type4 = *(const gs_image4_t *)pic;
+ break;
+ }
+ default:
+ goto nyi;
+ }
+ pim = (const gs_pixel_image_t *)pic;
+ format = pim->format;
+ switch (format) {
+ case gs_image_format_chunky:
+ case gs_image_format_component_planar:
+ break;
+ default:
+ goto nyi;
+ }
+ /* AR5 on Windows doesn't support 0-size images. Skipping. */
+ if (pim->Width == 0 || pim->Height == 0)
+ goto nyi;
+ /* PDF doesn't support images with more than 8 bits per component. */
+ switch (pim->BitsPerComponent) {
+ case 1:
+ case 2:
+ case 4:
+ case 8:
+ break;
+ case 12:
+ case 16:
+ goto nyi;
+ default:
+ return_error(gs_error_rangecheck);
+ }
+ pcs = pim->ColorSpace;
+ num_components = (is_mask ? 1 : gs_color_space_num_components(pcs));
+
+ if (pdf_must_put_clip_path(pdev, pcpath))
+ code = pdf_unclip(pdev);
+ else
+ code = pdf_open_page(pdev, PDF_IN_STREAM);
+ if (code < 0)
+ return code;
+ if (context == PDF_IMAGE_TYPE3_MASK) {
+ /*
+ * The soft mask for an ImageType 3x image uses a DevicePixel
+ * color space, which pdf_color_space() can't handle. Patch it
+ * to DeviceGray here.
+ */
+ /* {csrc} make sure this gets freed */
+ pcs = gs_cspace_new_DeviceGray(pdev->memory);
+ } else if (is_mask)
+ code = pdf_prepare_imagemask(pdev, pis, pdcolor);
+ else
+ code = pdf_prepare_image(pdev, pis);
+ if (code < 0)
+ goto nyi;
+ if (prect)
+ rect = *prect;
+ else {
+ rect.p.x = rect.p.y = 0;
+ rect.q.x = pim->Width, rect.q.y = pim->Height;
+ }
+ pie = gs_alloc_struct(mem, pdf_image_enum, &st_pdf_image_enum,
+ "pdf_begin_image");
+ if (pie == 0)
+ return_error(gs_error_VMerror);
+ memset(pie, 0, sizeof(*pie)); /* cleanup entirely for GC to work in all cases. */
+ *pinfo = (gx_image_enum_common_t *) pie;
+ gx_image_enum_common_init(*pinfo, (const gs_data_image_t *) pim,
+ ((pdev->CompatibilityLevel >= 1.3) ?
+ (context == PDF_IMAGE_TYPE3_MASK ?
+ &pdf_image_object_enum_procs :
+ &pdf_image_enum_procs) :
+ context == PDF_IMAGE_TYPE3_MASK ?
+ &pdf_image_object_enum_procs :
+ context == PDF_IMAGE_TYPE3_DATA ?
+ &pdf_image_object_enum_procs2 :
+ &pdf_image_enum_procs),
+ (gx_device *)pdev, num_components, format);
+ pie->memory = mem;
+ width = rect.q.x - rect.p.x;
+ pie->width = width;
+ height = rect.q.y - rect.p.y;
+ pie->bits_per_pixel =
+ pim->BitsPerComponent * num_components / pie->num_planes;
+ pie->rows_left = height;
+ if (pnamed != 0) /* Don't in-line the image if it is named. */
+ in_line = false;
+ else {
+ double nbytes = (double)(((ulong) pie->width * pie->bits_per_pixel + 7) >> 3) *
+ pie->num_planes * pie->rows_left;
+
+ in_line &= (nbytes < pdev->MaxInlineImageSize);
+ }
+ if (rect.p.x != 0 || rect.p.y != 0 ||
+ rect.q.x != pim->Width || rect.q.y != pim->Height ||
+ (is_mask && pim->CombineWithColor)
+ /* Color space setup used to be done here: see SRZB comment below. */
+ ) {
+ gs_free_object(mem, pie, "pdf_begin_image");
+ goto nyi;
+ }
+ if (pmat == 0)
+ pmat = &ctm_only(pis);
+ {
+ gs_matrix mat;
+ gs_matrix bmat;
+ int code;
+
+ pdf_make_bitmap_matrix(&bmat, -rect.p.x, -rect.p.y,
+ pim->Width, pim->Height, height);
+ if ((code = gs_matrix_invert(&pim->ImageMatrix, &mat)) < 0 ||
+ (code = gs_matrix_multiply(&bmat, &mat, &mat)) < 0 ||
+ (code = gs_matrix_multiply(&mat, pmat, &pie->mat)) < 0
+ ) {
+ gs_free_object(mem, pie, "pdf_begin_image");
+ return code;
+ }
+ /* AR3,AR4 show no image when CTM is singular; AR5 reports an error */
+ if (pie->mat.xx * pie->mat.yy == pie->mat.xy * pie->mat.yx) {
+ gs_free_object(mem, pie, "pdf_begin_image");
+ goto nyi;
+ }
+ }
+ code = pdf_put_clip_path(pdev, pcpath);
+ if (code < 0)
+ return code;
+ pdf_image_writer_init(&pie->writer);
+ pie->writer.alt_writer_count = (in_line ||
+ (pim->Width <= 64 && pim->Height <= 64)
+ ? 1 : 2);
+ if ((image[0].pixel.ColorSpace != NULL &&
+ image[0].pixel.ColorSpace->type->index == gs_color_space_index_Indexed
+ && pdev->params.ColorImage.DownsampleType != ds_Subsample) ||
+ pdev->transfer_not_identity)
+ force_lossless = true;
+
+ image[1] = image[0];
+ names = (in_line ? &pdf_color_space_names_short : &pdf_color_space_names);
+ if (!is_mask) {
+ if (psdf_is_converting_image_to_RGB((gx_device_psdf *)pdev, pis, pim)) {
+ /* psdf_setup_image_filters may change the color space
+ * (in case of pdev->params.ConvertCMYKImagesToRGB == true).
+ * Account it here.
+ */
+ cos_c_string_value(&cs_value, names->DeviceRGB);
+ } else {
+ /* A minor hack to deal with CIELAB images. */
+ if (pcs->cmm_icc_profile_data != NULL &&
+ pcs->cmm_icc_profile_data->islab) {
+ gscms_set_icc_range((cmm_profile_t **)&(pcs->cmm_icc_profile_data));
+ }
+ code = pdf_convert_ICC(pdev, pcs, &cs_value, names);
+ if (code == 0) {
+ code = pdf_color_space_named(pdev, pis, &cs_value, &pranges, pcs, names,
+ in_line, NULL, 0, false);
+ }
+ if (pcs->cmm_icc_profile_data != NULL &&
+ pcs->cmm_icc_profile_data->islab) {
+ gsicc_setrange_lab(pcs->cmm_icc_profile_data);
+ }
+ if (code < 0)
+ convert_to_process_colors = true;
+ }
+ }
+ if (image[0].pixel.ColorSpace != NULL && !(context == PDF_IMAGE_TYPE3_MASK)) {
+ /* Not an imagemask. Also not a SMask, we carefully made a Gray space
+ * for SMasks above, do not destroy it now!
+ */
+ if ((pdev->params.ColorConversionStrategy == ccs_Gray &&
+ !check_image_color_space(&image[0].pixel, gs_color_space_index_DeviceGray)) ||
+ (pdev->params.ColorConversionStrategy == ccs_sRGB &&
+ !psdf_is_converting_image_to_RGB((const gx_device_psdf *)pdev, pis, &image[0].pixel) &&
+ !check_image_color_space(&image[0].pixel, gs_color_space_index_DeviceGray) &&
+ !check_image_color_space(&image[0].pixel, gs_color_space_index_DeviceRGB)) ||
+ (pdev->params.ColorConversionStrategy == ccs_CMYK &&
+ !check_image_color_space(&image[0].pixel, gs_color_space_index_DeviceGray) &&
+ !check_image_color_space(&image[0].pixel, gs_color_space_index_DeviceCMYK))) {
+ /* fixme : as a rudiment of old code,
+ the case psdf_is_converting_image_to_RGB
+ is handled with the 'cmyk_to_rgb' branch
+ in psdf_setup_image_filters. */
+ if ((pdev->params.ColorConversionStrategy == ccs_CMYK &&
+ strcmp(pdev->color_info.cm_name, "DeviceCMYK")) ||
+ (pdev->params.ColorConversionStrategy == ccs_sRGB &&
+ strcmp(pdev->color_info.cm_name, "DeviceRGB")) ||
+ (pdev->params.ColorConversionStrategy == ccs_Gray &&
+ strcmp(pdev->color_info.cm_name, "DeviceGray"))) {
+ emprintf(pdev->memory, "ColorConversionStrategy isn't "
+ "compatible to ProcessColorModel.");
+ return_error(gs_error_rangecheck);
+ }
+ convert_to_process_colors = true;
+ }
+ }
+ if (convert_to_process_colors) {
+ const char *sname;
+
+ switch (pdev->pcm_color_info_index) {
+ case gs_color_space_index_DeviceGray: sname = names->DeviceGray; break;
+ case gs_color_space_index_DeviceRGB: sname = names->DeviceRGB; break;
+ case gs_color_space_index_DeviceCMYK: sname = names->DeviceCMYK; break;
+ default:
+ emprintf(pdev->memory, "Unsupported ProcessColorModel.");
+ return_error(gs_error_undefined);
+ }
+ cos_c_string_value(&cs_value, sname);
+ pcs_orig = image[0].pixel.ColorSpace;
+ BPC_orig = image[0].pixel.BitsPerComponent;
+ code = make_device_color_space(pdev, pdev->pcm_color_info_index, &pcs_device);
+ if (code < 0)
+ goto fail;
+ image[0].pixel.ColorSpace = pcs_device;
+ image[0].pixel.BitsPerComponent = BPC_device = 8;
+ }
+ pdev->ParamCompatibilityLevel = pdev->CompatibilityLevel;
+
+ /* This is rather hacky. the gs_image_pixel_t union has copies of the image
+ * information, including the ColorSpace, but it does not increment the
+ * reference counts of the counted objects (teh ColorSpace) when it makes
+ * copies. However, if psdf_setup_image_filters() should change the colour
+ * space into DeviceCMYK because it converts the image, it will decrement
+ * the reference count of the original space. There is at least one place
+ * where it is called with the original space, so we can't simply remove
+ * the decrement. Handling this properly would entail incrementing the
+ * reference count when we make the copy, and correctly decrementing it
+ * in all the error conditions. Its easier to isolate the changes to
+ * this piece of code.
+ */
+ rc_increment_cs(image[0].pixel.ColorSpace);
+ code = pdf_begin_write_image(pdev, &pie->writer, gs_no_id, width,
+ height, pnamed, in_line);
+ if (code < 0)
+ goto fail;
+ if (pie->writer.alt_writer_count == 1)
+ code = psdf_setup_lossless_filters((gx_device_psdf *) pdev,
+ &pie->writer.binary[0],
+ &image[0].pixel, in_line);
+ else {
+ if (force_lossless) {
+ /*
+ * Some regrettable PostScript code (such as LanguageLevel 1 output
+ * from Microsoft's PSCRIPT.DLL driver) misuses the transfer
+ * function to accomplish the equivalent of indexed color.
+ * Downsampling (well, only averaging) or JPEG compression are not
+ * compatible with this. Play it safe by using only lossless
+ * filters if the transfer function(s) is/are other than the
+ * identity and by setting the downsample type to Subsample..
+ */
+ int saved_downsample = pdev->params.ColorImage.DownsampleType;
+
+ pdev->params.ColorImage.DownsampleType = ds_Subsample;
+ code = psdf_setup_image_filters((gx_device_psdf *) pdev,
+ &pie->writer.binary[0], &image[0].pixel,
+ pmat, pis, true, in_line);
+ pdev->params.ColorImage.DownsampleType = saved_downsample;
+ } else {
+ if (!convert_to_process_colors)
+ code = psdf_setup_image_filters((gx_device_psdf *) pdev,
+ &pie->writer.binary[0], &image[0].pixel,
+ pmat, pis, true, in_line);
+ }
+ }
+ if (code < 0) {
+ if (image[0].pixel.ColorSpace == pim->ColorSpace)
+ rc_decrement_only_cs(pim->ColorSpace, "psdf_setup_image_filters");
+ goto fail;
+ }
+
+ if (convert_to_process_colors) {
+ image[0].pixel.ColorSpace = pcs_orig;
+ image[0].pixel.BitsPerComponent = BPC_orig;
+ code = psdf_setup_image_colors_filter(&pie->writer.binary[0],
+ (gx_device_psdf *)pdev, &image[0].pixel, pis);
+ if (code < 0)
+ goto fail;
+ image[0].pixel.ColorSpace = pcs_device;
+ image[0].pixel.BitsPerComponent = BPC_device;
+ }
+
+ /* See the comment above about reference counting of the colour space */
+ rc_increment_cs(image[1].pixel.ColorSpace);
+ if (pie->writer.alt_writer_count > 1) {
+ code = pdf_make_alt_stream(pdev, &pie->writer.binary[1]);
+ if (code)
+ goto fail;
+ if (convert_to_process_colors) {
+ image[1].pixel.ColorSpace = pcs_device;
+ image[1].pixel.BitsPerComponent = BPC_device;
+ }
+ code = psdf_setup_image_filters((gx_device_psdf *) pdev,
+ &pie->writer.binary[1], &image[1].pixel,
+ pmat, pis, force_lossless, in_line);
+ if (code == gs_error_rangecheck) {
+
+ for (i=1;i < pie->writer.alt_writer_count; i++) {
+ stream *s = pie->writer.binary[i].strm;
+ cos_stream_t *pcos = cos_stream_from_pipeline(pie->writer.binary[i].strm);
+ s_close_filters(&s, NULL);
+ gs_free_object(pdev->pdf_memory, s, "compressed image stream");
+ pcos->cos_procs->release((cos_object_t *)pcos, "pdf_begin_typed_image_impl");
+ gs_free_object(pdev->pdf_memory, pcos, "compressed image cos_stream");
+ }
+ /* setup_image_compression rejected the alternative compression. */
+ pie->writer.alt_writer_count = 1;
+ memset(pie->writer.binary + 1, 0, sizeof(pie->writer.binary[1]));
+ memset(pie->writer.binary + 2, 0, sizeof(pie->writer.binary[1]));
+ } else if (code) {
+ if (image[1].pixel.ColorSpace == pim->ColorSpace)
+ rc_decrement_only_cs(pim->ColorSpace, "psdf_setup_image_filters");
+ goto fail;
+ }
+ else if (convert_to_process_colors) {
+ int Width = image[1].pixel.Width;
+ int Height = image[1].pixel.Height;
+ image[1].pixel.ColorSpace = pcs_orig;
+ image[1].pixel.BitsPerComponent = BPC_orig;
+ image[1].pixel.Width = image[0].pixel.Width;
+ image[1].pixel.Height = image[0].pixel.Height;
+ code = psdf_setup_image_colors_filter(&pie->writer.binary[1],
+ (gx_device_psdf *)pdev, &image[1].pixel, pis);
+ if (code < 0) {
+ if (image[1].pixel.ColorSpace == pim->ColorSpace)
+ rc_decrement_only_cs(pim->ColorSpace, "psdf_setup_image_filters");
+ goto fail;
+ }
+ image[1].pixel.Width = Width;
+ image[1].pixel.Height = Height;
+ image[1].pixel.ColorSpace = pcs_device;
+ image[1].pixel.BitsPerComponent = BPC_device;
+ }
+ }
+ if (image[1].pixel.ColorSpace == pim->ColorSpace)
+ rc_decrement_only_cs(pim->ColorSpace, "psdf_setup_image_filters");
+
+ for (i = 0; i < pie->writer.alt_writer_count; i++) {
+ code = pdf_begin_image_data_decoded(pdev, num_components, pranges, i,
+ &image[i].pixel, &cs_value, pie);
+ if (code < 0)
+ goto fail;
+ }
+ if (pie->writer.alt_writer_count == 2) {
+ psdf_setup_compression_chooser(&pie->writer.binary[2],
+ (gx_device_psdf *)pdev, pim->Width, pim->Height,
+ num_components, pim->BitsPerComponent);
+ pie->writer.alt_writer_count = 3;
+ }
+ if (pic->type->index == 4 && pdev->CompatibilityLevel < 1.3) {
+ int i;
+
+ /* Create a stream for writing the mask. */
+ i = pie->writer.alt_writer_count;
+ gs_image_t_init_mask_adjust((gs_image_t *)&image[i].type1, true, false);
+ image[i].type1.Width = image[0].pixel.Width;
+ image[i].type1.Height = image[0].pixel.Height;
+ /* Won't use image[2]. */
+ code = pdf_begin_write_image(pdev, &pie->writer, gs_no_id, width,
+ height, NULL, false);
+ if (code)
+ goto fail;
+ code = psdf_setup_image_filters((gx_device_psdf *) pdev,
+ &pie->writer.binary[i], &image[i].pixel,
+ pmat, pis, force_lossless, in_line);
+ if (code < 0)
+ goto fail;
+ psdf_setup_image_to_mask_filter(&pie->writer.binary[i],
+ (gx_device_psdf *)pdev, pim->Width, pim->Height,
+ num_components, pim->BitsPerComponent, image[i].type4.MaskColor);
+ code = pdf_begin_image_data_decoded(pdev, num_components, pranges, i,
+ &image[i].pixel, &cs_value, pie);
+ if (code < 0)
+ goto fail;
+ ++pie->writer.alt_writer_count;
+ /* Note : Possible values for alt_writer_count are 1,2,3, 4.
+ 1 means no alternative streams.
+ 2 means the main image stream and a mask stream while converting
+ an Image Type 4.
+ 3 means the main image stream, alternative image compression stream,
+ and the compression chooser.
+ 4 meams 3 and a mask stream while convertingh an Image Type 4.
+ */
+ }
+ return 0;
+ fail:
+ /****** SHOULD FREE STRUCTURES AND CLEAN UP HERE ******/
+ /* Fall back to the default implementation. */
+ nyi:
+ return gx_default_begin_typed_image
+ ((gx_device *)pdev, pis, pmat, pic, prect, pdcolor, pcpath, mem,
+ pinfo);
+}
+
+static int
+old_pdf_begin_typed_image(gx_device_pdf *pdev, const gs_imager_state * pis,
+ const gs_matrix *pmat, const gs_image_common_t *pic,
+ const gs_int_rect * prect,
+ const gx_drawing_color * pdcolor,
+ const gx_clip_path * pcpath, gs_memory_t * mem,
+ gx_image_enum_common_t ** pinfo,
+ pdf_typed_image_context_t context)
+{
+ int code;
+ image_union_t *image = (image_union_t *)gs_malloc(mem->non_gc_memory, 4,
+ sizeof(image_union_t), "pdf_begin_typed_image(image)");
+ if (image == 0)
+ return_error(gs_error_VMerror);
+ code = pdf_begin_typed_image_impl(pdev, pis, pmat, pic, prect,
+ pdcolor, pcpath, mem, pinfo, context, image);
+ gs_free(mem->non_gc_memory, image, 4, sizeof(image_union_t),
+ "pdf_begin_typed_image(image)");
+ return code;
+}
+
+static int setup_type1_image(gx_device_pdf *pdev, const gs_image_common_t *pic,
+ const gx_drawing_color * pdcolor, image_union_t *image,
+ pdf_typed_image_context_t context)
+{
+ const gs_image_t *pim1 = (const gs_image_t *)pic;
+
+ if (pim1->Alpha != gs_image_alpha_none)
+ return -1;
+ if (pim1->ImageMask) {
+ /* If parameters are invalid, use the fallback implementation. */
+ if (!(gx_dc_is_pattern1_color(pdcolor)))
+ if (pim1->BitsPerComponent != 1 ||
+ !((pim1->Decode[0] == 0.0 && pim1->Decode[1] == 1.0) ||
+ (pim1->Decode[0] == 1.0 && pim1->Decode[1] == 0.0))
+ )
+ return -1;
+ }
+ image[0].type1 = *pim1;
+ /* If we can write in-line then make it so */
+ return (context == PDF_IMAGE_DEFAULT &&
+ can_write_image_in_line(pdev, pim1));
+}
+
+static int setup_type3_image(gx_device_pdf *pdev, const gs_imager_state * pis,
+ const gs_matrix *pmat, const gs_image_common_t *pic,
+ const gs_int_rect * prect,
+ const gx_drawing_color * pdcolor,
+ const gx_clip_path * pcpath, gs_memory_t * mem,
+ gx_image_enum_common_t ** pinfo,
+ image_union_t *image)
+{
+ const gs_image3_t *pim3 = (const gs_image3_t *)pic;
+ gs_image3_t pim3a;
+ const gs_image_common_t *pic1 = pic;
+ gs_matrix m, mi;
+ const gs_matrix *pmat1 = pmat;
+ int code;
+
+ if (pdev->CompatibilityLevel < 1.3 && !pdev->PatternImagemask) {
+ if (pdf_must_put_clip_path(pdev, pcpath))
+ code = pdf_unclip(pdev);
+ else
+ code = pdf_open_page(pdev, PDF_IN_STREAM);
+ if (code < 0)
+ return code;
+ code = pdf_put_clip_path(pdev, pcpath);
+ if (code < 0)
+ return code;
+ gs_make_identity(&m);
+ pmat1 = &m;
+ m.tx = floor(pis->ctm.tx + 0.5); /* Round the origin against the image size distorsions */
+ m.ty = floor(pis->ctm.ty + 0.5);
+ pim3a = *pim3;
+ gs_matrix_invert(&pim3a.ImageMatrix, &mi);
+ gs_make_identity(&pim3a.ImageMatrix);
+ if (pim3a.Width < pim3a.MaskDict.Width && pim3a.Width > 0) {
+ int sx = (pim3a.MaskDict.Width + pim3a.Width - 1) / pim3a.Width;
+
+ gs_matrix_scale(&mi, 1.0 / sx, 1, &mi);
+ gs_matrix_scale(&pim3a.ImageMatrix, 1.0 / sx, 1, &pim3a.ImageMatrix);
+ }
+ if (pim3a.Height < pim3a.MaskDict.Height && pim3a.Height > 0) {
+ int sy = (pim3a.MaskDict.Height + pim3a.Height - 1) / pim3a.Height;
+
+ gs_matrix_scale(&mi, 1, 1.0 / sy, &mi);
+ gs_matrix_scale(&pim3a.ImageMatrix, 1, 1.0 / sy, &pim3a.ImageMatrix);
+ }
+ gs_matrix_multiply(&mi, &pim3a.MaskDict.ImageMatrix, &pim3a.MaskDict.ImageMatrix);
+ pic1 = (gs_image_common_t *)&pim3a;
+ /* Setting pdev->converting_image_matrix to communicate with pdf_image3_make_mcde. */
+ gs_matrix_multiply(&mi, &ctm_only(pis), &pdev->converting_image_matrix);
+ }
+ /*
+ * We handle ImageType 3 images in a completely different way:
+ * the default implementation sets up the enumerator.
+ */
+ return gx_begin_image3_generic((gx_device *)pdev, pis, pmat1, pic1,
+ prect, pdcolor, pcpath, mem,
+ pdf_image3_make_mid,
+ pdf_image3_make_mcde, pinfo);
+}
+
+static int convert_type4_image(gx_device_pdf *pdev, const gs_imager_state * pis,
+ const gs_matrix *pmat, const gs_image_common_t *pic,
+ const gs_int_rect * prect,
+ const gx_drawing_color * pdcolor,
+ const gx_clip_path * pcpath, gs_memory_t * mem,
+ gx_image_enum_common_t ** pinfo,
+ pdf_typed_image_context_t context, image_union_t *image,
+ cos_dict_t *pnamed)
+{
+ /* Try to convert the image to a plain masked image. */
+ gx_drawing_color icolor;
+ int code;
+
+ pdev->image_mask_is_SMask = false;
+ if (pdf_convert_image4_to_image1(pdev, pis, pdcolor,
+ (const gs_image4_t *)pic,
+ &image[0].type1, &icolor) >= 0) {
+ gs_state *pgs = (gs_state *)gx_hld_get_gstate_ptr(pis);
+
+ if (pgs == NULL)
+ return_error(gs_error_unregistered); /* Must not happen. */
+
+ /* Undo the pop of the NI stack if necessary. */
+ if (pnamed)
+ cos_array_add_object(pdev->NI_stack, COS_OBJECT(pnamed));
+ /* HACK: temporary patch the color space, to allow
+ pdf_prepare_imagemask to write the right color for the imagemask. */
+ code = gs_gsave(pgs);
+ if (code < 0)
+ return code;
+ /* {csrc}: const cast warning */
+ code = gs_setcolorspace(pgs, ((const gs_image4_t *)pic)->ColorSpace);
+ if (code < 0)
+ return code;
+ code = pdf_begin_typed_image(pdev, pis, pmat,
+ (gs_image_common_t *)&image[0].type1,
+ prect, &icolor, pcpath, mem,
+ pinfo, context);
+ if (code < 0)
+ return code;
+ return gs_grestore(pgs);
+ }
+ return 1;
+}
+
+static int convert_type4_to_masked_image(gx_device_pdf *pdev, const gs_imager_state * pis,
+ const gs_image_common_t *pic,
+ const gs_int_rect * prect,
+ const gx_drawing_color * pdcolor,
+ const gx_clip_path * pcpath, gs_memory_t * mem,
+ gx_image_enum_common_t ** pinfo)
+{
+ gs_matrix m, m1, mi;
+ gs_image4_t pi4 = *(const gs_image4_t *)pic;
+ int code;
+ pdf_lcvd_t *cvd = NULL;
+
+ if (pdf_must_put_clip_path(pdev, pcpath))
+ code = pdf_unclip(pdev);
+ else
+ code = pdf_open_page(pdev, PDF_IN_STREAM);
+ if (code < 0)
+ return code;
+ code = pdf_put_clip_path(pdev, pcpath);
+ if (code < 0)
+ return code;
+ gs_make_identity(&m1);
+ gs_matrix_invert(&pic->ImageMatrix, &mi);
+ gs_matrix_multiply(&mi, &ctm_only(pis), &m);
+ code = pdf_setup_masked_image_converter(pdev, mem, &m, &cvd,
+ true, 0, 0, pi4.Width, pi4.Height, false);
+ if (code < 0)
+ return code;
+ cvd->mdev.is_open = true; /* fixme: same as above. */
+ cvd->mask->is_open = true; /* fixme: same as above. */
+ cvd->mask_is_empty = false;
+ code = (*dev_proc(cvd->mask, fill_rectangle))((gx_device *)cvd->mask,
+ 0, 0, cvd->mask->width, cvd->mask->height, (gx_color_index)0);
+ if (code < 0)
+ return code;
+ gx_device_retain((gx_device *)cvd, true);
+ gx_device_retain((gx_device *)cvd->mask, true);
+ gs_make_identity(&pi4.ImageMatrix);
+ code = gx_default_begin_typed_image((gx_device *)cvd,
+ pis, &m1, (gs_image_common_t *)&pi4, prect, pdcolor, NULL, mem, pinfo);
+ if (code < 0)
+ return code;
+ (*pinfo)->procs = &pdf_image_cvd_enum_procs;
+ return 0;
+}
+
+static int setup_image_process_colorspace(gx_device_pdf *pdev, image_union_t *image, gs_color_space **pcs_orig,
+ const char *sname, cos_value_t *cs_value)
+{
+ int code;
+ gs_color_space *pcs_device = NULL;
+
+ cos_c_string_value(cs_value, sname);
+ *pcs_orig = image->pixel.ColorSpace;
+ code = make_device_color_space(pdev, pdev->pcm_color_info_index, &pcs_device);
+ if (code < 0)
+ return code;
+ image->pixel.ColorSpace = pcs_device;
+ return 0;
+}
+
+/* 0 = write unchanged
+ 1 = convert to process
+ 2 = write as ICC
+ 3 = convert base space (Separation)
+ 4 = convert base space (DeviceN)
+ */
+static int setup_image_colorspace(gx_device_pdf *pdev, image_union_t *image, const gs_color_space *pcs, gs_color_space **pcs_orig,
+ const pdf_color_space_names_t *names, cos_value_t *cs_value)
+{
+ int code=0;
+ gs_color_space_index csi;
+ gs_color_space_index csi2;
+ const gs_color_space *pcs2 = pcs;
+
+ csi = csi2 = gs_color_space_get_index(pcs);
+ if (csi == gs_color_space_index_ICC) {
+ csi2 = gsicc_get_default_type(pcs->cmm_icc_profile_data);
+ }
+ /* Figure out what to do if we are outputting to really ancient versions of PDF */
+ /* NB ps2write sets CompatibilityLevel to 1.2 so we cater for it here */
+ if (pdev->CompatibilityLevel <= 1.2) {
+
+ /* If we have an /Indexed space, we need to look at the base space */
+ if (csi2 == gs_color_space_index_Indexed) {
+ pcs2 = pcs->base_space;
+ csi2 = gs_color_space_get_index(pcs2);
+ }
+
+ switch (csi2) {
+ case gs_color_space_index_DeviceGray:
+ if (pdev->params.ColorConversionStrategy == ccs_LeaveColorUnchanged ||
+ pdev->params.ColorConversionStrategy == ccs_Gray) {
+ return 0;
+ }
+ else {
+ code = setup_image_process_colorspace(pdev, image, pcs_orig, names->DeviceGray, cs_value);
+ if (code < 0)
+ return code;
+ return 1;
+ }
+ break;
+ case gs_color_space_index_DeviceRGB:
+ if (pdev->params.ColorConversionStrategy == ccs_LeaveColorUnchanged ||
+ pdev->params.ColorConversionStrategy == ccs_RGB || pdev->params.ColorConversionStrategy == ccs_sRGB)
+ return 0;
+ else {
+ code = setup_image_process_colorspace(pdev, image, pcs_orig, names->DeviceRGB, cs_value);
+ if (code < 0)
+ return code;
+ return 1;
+ }
+ break;
+ case gs_color_space_index_DeviceCMYK:
+ if ((pdev->params.ColorConversionStrategy == ccs_LeaveColorUnchanged ||
+ pdev->params.ColorConversionStrategy == ccs_CMYK) && !pdev->params.ConvertCMYKImagesToRGB)
+ return 0;
+ else {
+ code = setup_image_process_colorspace(pdev, image, pcs_orig, names->DeviceCMYK, cs_value);
+ if (code < 0)
+ return code;
+ return 1;
+ }
+ break;
+ case gs_color_space_index_CIEA:
+ case gs_color_space_index_CIEABC:
+ case gs_color_space_index_CIEDEF:
+ case gs_color_space_index_CIEDEFG:
+ case gs_color_space_index_Separation:
+ if (pdev->ForOPDFRead) {
+ switch (pdev->params.ColorConversionStrategy) {
+ case ccs_ByObjectType:
+ /* Object type not implemented yet */
+ case ccs_UseDeviceDependentColor:
+ /* DeviceDependentColor deprecated */
+ case ccs_UseDeviceIndependentColorForImages:
+ /* If only correcting images, then leave unchanged */
+ case ccs_LeaveColorUnchanged:
+ if (csi2 == gs_color_space_index_Separation)
+ return 0;
+ /* Fall through and convert CIE to the device space */
+ default:
+ switch (pdev->pcm_color_info_index) {
+ case gs_color_space_index_DeviceGray:
+ code = setup_image_process_colorspace(pdev, image, pcs_orig, names->DeviceGray, cs_value);
+ break;
+ case gs_color_space_index_DeviceRGB:
+ code = setup_image_process_colorspace(pdev, image, pcs_orig, names->DeviceRGB, cs_value);
+ break;
+ case gs_color_space_index_DeviceCMYK:
+ code = setup_image_process_colorspace(pdev, image, pcs_orig, names->DeviceCMYK, cs_value);
+ break;
+ default:
+ emprintf(pdev->memory, "Unsupported ProcessColorModel.");
+ return_error(gs_error_undefined);
+ }
+ if (code < 0)
+ return code;
+ return 1;
+ break;
+ }
+ }
+ else
+ return 1;
+ break;
+
+ case gs_color_space_index_ICC:
+ /* Note that if csi is ICC, check to see if this was one of
+ the default substitutes that we introduced for DeviceGray,
+ DeviceRGB or DeviceCMYK. If it is, then just write
+ the default color. Depending upon the flavor of PDF,
+ or other options, we may want to actually have all
+ the colors defined by ICC profiles and not do the following
+ substituion of the Device space. */
+ csi2 = gsicc_get_default_type(pcs2->cmm_icc_profile_data);
+
+ switch (csi2) {
+ case gs_color_space_index_DeviceGray:
+ if (pdev->params.ColorConversionStrategy == ccs_Gray ||
+ pdev->params.ColorConversionStrategy == ccs_LeaveColorUnchanged)
+ return 0;
+ break;
+ case gs_color_space_index_DeviceRGB:
+ if (pdev->params.ColorConversionStrategy == ccs_RGB || pdev->params.ColorConversionStrategy == ccs_sRGB ||
+ pdev->params.ColorConversionStrategy == ccs_LeaveColorUnchanged)
+ return 0;
+ break;
+ case gs_color_space_index_DeviceCMYK:
+ if (pdev->params.ColorConversionStrategy == ccs_CMYK ||
+ pdev->params.ColorConversionStrategy == ccs_LeaveColorUnchanged)
+ return 0;
+ break;
+ default:
+ break;
+ }
+ /* Fall through for non-handled cases */
+ case gs_color_space_index_DeviceN:
+ case gs_color_space_index_DevicePixel:
+ case gs_color_space_index_Indexed:
+ switch (pdev->pcm_color_info_index) {
+ case gs_color_space_index_DeviceGray:
+ code = setup_image_process_colorspace(pdev, image, pcs_orig, names->DeviceGray, cs_value);
+ break;
+ case gs_color_space_index_DeviceRGB:
+ code = setup_image_process_colorspace(pdev, image, pcs_orig, names->DeviceRGB, cs_value);
+ break;
+ case gs_color_space_index_DeviceCMYK:
+ code = setup_image_process_colorspace(pdev, image, pcs_orig, names->DeviceCMYK, cs_value);
+ break;
+ default:
+ emprintf(pdev->memory, "Unsupported ProcessColorModel.");
+ return_error(gs_error_undefined);
+ }
+ if (code < 0)
+ return code;
+ return 1;
+ break;
+ default:
+ return (gs_note_error(gs_error_rangecheck));
+ break;
+ }
+ } else {
+ switch(pdev->params.ColorConversionStrategy) {
+ case ccs_ByObjectType:
+ /* Object type not implemented yet */
+ case ccs_UseDeviceDependentColor:
+ /* DeviceDependentCOlor deprecated */
+ case ccs_UseDeviceIndependentColorForImages:
+ /* If only correcting images, then leave unchanged */
+ case ccs_LeaveColorUnchanged:
+ return 0;
+ break;
+ case ccs_UseDeviceIndependentColor:
+ return 2;
+ break;
+ case ccs_CMYK:
+ switch(csi2) {
+ case gs_color_space_index_DeviceGray:
+ case gs_color_space_index_DeviceCMYK:
+ return 0;
+ break;
+ case gs_color_space_index_Separation:
+ pcs2 = pcs;
+ while (pcs2->base_space)
+ pcs2 = pcs2->base_space;
+ csi = gs_color_space_get_index(pcs2);
+ if (csi == gs_color_space_index_ICC)
+ csi = gsicc_get_default_type(pcs2->cmm_icc_profile_data);
+ if (csi == gs_color_space_index_DeviceCMYK)
+ return 0;
+ else
+ return 3;
+ break;
+ case gs_color_space_index_DeviceN:
+ pcs2 = pcs;
+ while (pcs2->base_space)
+ pcs2 = pcs2->base_space;
+ csi = gs_color_space_get_index(pcs2);
+ if (csi == gs_color_space_index_ICC)
+ csi = gsicc_get_default_type(pcs2->cmm_icc_profile_data);
+ if (csi == gs_color_space_index_DeviceCMYK)
+ return 0;
+ else
+ return 4;
+ break;
+ case gs_color_space_index_Indexed:
+ pcs2 = pcs->base_space;
+ csi = gs_color_space_get_index(pcs2);
+ if (csi == gs_color_space_index_ICC)
+ csi = gsicc_get_default_type(pcs2->cmm_icc_profile_data);
+ switch(csi) {
+ case gs_color_space_index_DeviceGray:
+ case gs_color_space_index_DeviceCMYK:
+ return 0;
+ break;
+ case gs_color_space_index_Separation:
+ pcs2 = pcs;
+ while (pcs2->base_space)
+ pcs2 = pcs2->base_space;
+ csi = gs_color_space_get_index(pcs2);
+ if (csi == gs_color_space_index_ICC)
+ csi = gsicc_get_default_type(pcs2->cmm_icc_profile_data);
+ if (csi == gs_color_space_index_DeviceCMYK)
+ return 0;
+ else
+ return 3;
+ break;
+ case gs_color_space_index_DeviceN:
+ pcs2 = pcs;
+ while (pcs2->base_space)
+ pcs2 = pcs2->base_space;
+ csi = gs_color_space_get_index(pcs2);
+ if (csi == gs_color_space_index_ICC)
+ csi = gsicc_get_default_type(pcs2->cmm_icc_profile_data);
+ if (csi == gs_color_space_index_DeviceCMYK)
+ return 0;
+ else
+ return 4;
+ break;
+ default:
+ switch (pdev->pcm_color_info_index) {
+ case gs_color_space_index_DeviceGray:
+ code = setup_image_process_colorspace(pdev, image, pcs_orig, names->DeviceGray, cs_value);
+ break;
+ case gs_color_space_index_DeviceRGB:
+ code = setup_image_process_colorspace(pdev, image, pcs_orig, names->DeviceRGB, cs_value);
+ break;
+ case gs_color_space_index_DeviceCMYK:
+ code = setup_image_process_colorspace(pdev, image, pcs_orig, names->DeviceCMYK, cs_value);
+ break;
+ default:
+ emprintf(pdev->memory, "Unsupported ProcessColorModel.");
+ return_error(gs_error_undefined);
+ }
+ if (code < 0)
+ return code;
+ return 1;
+ break;
+ }
+ break;
+ default:
+ code = setup_image_process_colorspace(pdev, image, pcs_orig, names->DeviceCMYK, cs_value);
+ if (code < 0)
+ return code;
+ return 1;
+ break;
+ }
+ break;
+ case ccs_Gray:
+ switch(csi2) {
+ case gs_color_space_index_DeviceGray:
+ return 0;
+ break;
+ case gs_color_space_index_Separation:
+ pcs2 = pcs;
+ while (pcs2->base_space)
+ pcs2 = pcs2->base_space;
+ csi = gs_color_space_get_index(pcs2);
+ if (csi == gs_color_space_index_ICC)
+ csi = gsicc_get_default_type(pcs2->cmm_icc_profile_data);
+ if (csi == gs_color_space_index_DeviceGray)
+ return 0;
+ else
+ return 3;
+ break;
+ case gs_color_space_index_DeviceN:
+ pcs2 = pcs;
+ while (pcs2->base_space)
+ pcs2 = pcs2->base_space;
+ csi = gs_color_space_get_index(pcs2);
+ if (csi == gs_color_space_index_ICC)
+ csi = gsicc_get_default_type(pcs2->cmm_icc_profile_data);
+ if (csi == gs_color_space_index_DeviceGray)
+ return 0;
+ else
+ return 4;
+ break;
+ case gs_color_space_index_Indexed:
+ pcs2 = pcs->base_space;
+ csi = gs_color_space_get_index(pcs2);
+ if (csi == gs_color_space_index_ICC)
+ csi = gsicc_get_default_type(pcs2->cmm_icc_profile_data);
+ switch(csi) {
+ case gs_color_space_index_DeviceGray:
+ return 0;
+ break;
+ case gs_color_space_index_Separation:
+ pcs2 = pcs;
+ while (pcs2->base_space)
+ pcs2 = pcs2->base_space;
+ csi = gs_color_space_get_index(pcs2);
+ if (csi == gs_color_space_index_ICC)
+ csi = gsicc_get_default_type(pcs2->cmm_icc_profile_data);
+ if (csi == gs_color_space_index_DeviceGray)
+ return 0;
+ else
+ return 3;
+ break;
+ case gs_color_space_index_DeviceN:
+ pcs2 = pcs;
+ while (pcs2->base_space)
+ pcs2 = pcs2->base_space;
+ csi = gs_color_space_get_index(pcs2);
+ if (csi == gs_color_space_index_ICC)
+ csi = gsicc_get_default_type(pcs2->cmm_icc_profile_data);
+ if (csi == gs_color_space_index_DeviceGray)
+ return 0;
+ else
+ return 4;
+ break;
+ default:
+ code = setup_image_process_colorspace(pdev, image, pcs_orig, names->DeviceGray, cs_value);
+ if (code < 0)
+ return code;
+ return 1;
+ break;
+ }
+ break;
+ default:
+ code = setup_image_process_colorspace(pdev, image, pcs_orig, names->DeviceGray, cs_value);
+ if (code < 0)
+ return code;
+ return 1;
+ break;
+ }
+ break;
+ case ccs_sRGB:
+ case ccs_RGB:
+ switch(csi2) {
+ case gs_color_space_index_DeviceGray:
+ case gs_color_space_index_DeviceRGB:
+ return 0;
+ break;
+ case gs_color_space_index_Separation:
+ pcs2 = pcs;
+ while (pcs2->base_space)
+ pcs2 = pcs2->base_space;
+ csi = gs_color_space_get_index(pcs2);
+ if (csi == gs_color_space_index_ICC)
+ csi = gsicc_get_default_type(pcs2->cmm_icc_profile_data);
+ if (csi == gs_color_space_index_DeviceRGB)
+ return 0;
+ else
+ return 3;
+ break;
+ case gs_color_space_index_DeviceN:
+ pcs2 = pcs;
+ while (pcs2->base_space)
+ pcs2 = pcs2->base_space;
+ csi = gs_color_space_get_index(pcs2);
+ if (csi == gs_color_space_index_ICC)
+ csi = gsicc_get_default_type(pcs2->cmm_icc_profile_data);
+ if (csi == gs_color_space_index_DeviceRGB)
+ return 0;
+ else
+ return 4;
+ break;
+ case gs_color_space_index_Indexed:
+ pcs2 = pcs->base_space;
+ csi = gs_color_space_get_index(pcs2);
+ if (csi == gs_color_space_index_ICC)
+ csi = gsicc_get_default_type(pcs2->cmm_icc_profile_data);
+ switch(csi) {
+ case gs_color_space_index_DeviceGray:
+ case gs_color_space_index_DeviceRGB:
+ return 0;
+ break;
+ case gs_color_space_index_Separation:
+ pcs2 = pcs;
+ while (pcs2->base_space)
+ pcs2 = pcs2->base_space;
+ csi = gs_color_space_get_index(pcs2);
+ if (csi == gs_color_space_index_ICC)
+ csi = gsicc_get_default_type(pcs2->cmm_icc_profile_data);
+ if (csi == gs_color_space_index_DeviceRGB)
+ return 0;
+ else
+ return 3;
+ break;
+ case gs_color_space_index_DeviceN:
+ pcs2 = pcs;
+ while (pcs2->base_space)
+ pcs2 = pcs2->base_space;
+ csi = gs_color_space_get_index(pcs2);
+ if (csi == gs_color_space_index_ICC)
+ csi = gsicc_get_default_type(pcs2->cmm_icc_profile_data);
+ if (csi == gs_color_space_index_DeviceRGB)
+ return 0;
+ else
+ return 4;
+ break;
+ default:
+ code = setup_image_process_colorspace(pdev, image, pcs_orig, names->DeviceRGB, cs_value);
+ if (code < 0)
+ return code;
+ return 1;
+ break;
+ }
+ break;
+ default:
+ code = setup_image_process_colorspace(pdev, image, pcs_orig, names->DeviceRGB, cs_value);
+ if (code < 0)
+ return code;
+ return 1;
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ return 0;
+}
+
+static int
+new_pdf_begin_typed_image(gx_device_pdf *pdev, const gs_imager_state * pis,
+ const gs_matrix *pmat, const gs_image_common_t *pic,
+ const gs_int_rect * prect,
+ const gx_drawing_color * pdcolor,
+ const gx_clip_path * pcpath, gs_memory_t * mem,
+ gx_image_enum_common_t ** pinfo,
+ pdf_typed_image_context_t context)
+{
+ int code, i;
+ unsigned int use_fallback = 0, in_line = 0, is_mask = 0,
+ force_lossless = 0, convert_to_process_colors = 0;
+ int width, height;
+ cos_dict_t *pnamed = 0;
+ image_union_t *image;
+ const gs_pixel_image_t *pim;
+ gs_int_rect rect;
+ gs_image_format_t format;
+ const gs_color_space *pcs;
+ int num_components;
+ pdf_image_enum *pie;
+ const pdf_color_space_names_t *names;
+ gs_color_space *pcs_orig = NULL;
+ gs_color_space *pcs_device = NULL;
+ cos_value_t cs_value;
+ const gs_range_t *pranges = 0;
+
+ image = (image_union_t *)gs_malloc(mem->non_gc_memory, 4,
+ sizeof(image_union_t), "pdf_begin_typed_image(image)");
+ if (image == 0)
+ return_error(gs_error_VMerror);
+
+ /*
+ * Pop the image name from the NI stack. We must do this, to keep the
+ * stack in sync, even if it turns out we can't handle the image.
+ */
+ {
+ cos_value_t ni_value;
+
+ if (cos_array_unadd(pdev->NI_stack, &ni_value) >= 0)
+ pnamed = (cos_dict_t *)ni_value.contents.object;
+ }
+
+ /* An initialization for pdf_end_and_do_image :
+ We need to delay adding the "Mask" entry into a type 3 image dictionary
+ until the mask is completed due to equal image merging. */
+ pdev->image_mask_id = gs_no_id;
+
+ /* Check for the image types we can handle. */
+ switch (pic->type->index) {
+ case 1:
+ is_mask = ((const gs_image_t *)pic)->ImageMask;
+ code = setup_type1_image(pdev, pic, pdcolor, image, context);
+ if (code < 0) {
+ use_fallback = 1;
+ }
+ else
+ in_line = code;
+ break;
+
+ case 3:
+ pdev->image_mask_is_SMask = false;
+ if (pdev->CompatibilityLevel < 1.2 ||
+ (prect && !(prect->p.x == 0 && prect->p.y == 0 &&
+ prect->q.x == ((const gs_image3_t *)pic)->Width &&
+ prect->q.y == ((const gs_image3_t *)pic)->Height))) {
+ gs_free(mem->non_gc_memory, image, 4, sizeof(image_union_t),
+ "pdf_begin_typed_image(image)");
+ return (gx_default_begin_typed_image((gx_device *)pdev, pis, pmat, pic, prect, pdcolor,
+ pcpath, mem, pinfo));
+ }
+ gs_free(mem->non_gc_memory, image, 4, sizeof(image_union_t),
+ "pdf_begin_typed_image(image)");
+ return (setup_type3_image(pdev, pis, pmat, pic, prect, pdcolor, pcpath, mem, pinfo, image));
+ break;
+
+ case IMAGE3X_IMAGETYPE:
+ gs_free(mem->non_gc_memory, image, 4, sizeof(image_union_t),
+ "pdf_begin_typed_image(image)");
+ if (pdev->CompatibilityLevel < 1.4 ||
+ (prect && !(prect->p.x == 0 && prect->p.y == 0 &&
+ prect->q.x == ((const gs_image3x_t *)pic)->Width &&
+ prect->q.y == ((const gs_image3x_t *)pic)->Height))) {
+ return (gx_default_begin_typed_image((gx_device *)pdev, pis, pmat, pic, prect, pdcolor,
+ pcpath, mem, pinfo));
+ }
+ pdev->image_mask_is_SMask = true;
+ return gx_begin_image3x_generic((gx_device *)pdev, pis, pmat, pic,
+ prect, pdcolor, pcpath, mem,
+ pdf_image3x_make_mid,
+ pdf_image3x_make_mcde, pinfo);
+ break;
+
+ case 4:
+ code = convert_type4_image(pdev, pis, pmat, pic, prect, pdcolor,
+ pcpath, mem, pinfo, context, image, pnamed);
+ if (code < 0) {
+ use_fallback = 1;
+ }
+ if (code == 0) {
+ gs_free(mem->non_gc_memory, image, 4, sizeof(image_union_t),
+ "pdf_begin_typed_image(image)");
+ return code;
+ }
+ /* No luck. Masked images require PDF 1.3 or higher. */
+ if (pdev->CompatibilityLevel < 1.2) {
+ use_fallback = 1;
+ }
+ if (pdev->CompatibilityLevel < 1.3 && !pdev->PatternImagemask) {
+ gs_free(mem->non_gc_memory, image, 4, sizeof(image_union_t),
+ "pdf_begin_typed_image(image)");
+ return (convert_type4_to_masked_image(pdev, pis, pic, prect, pdcolor,
+ pcpath, mem,pinfo));
+ }
+ image[0].type4 = *(const gs_image4_t *)pic;
+ break;
+
+ default:
+ use_fallback = 1;
+ break;
+ }
+
+ pim = (const gs_pixel_image_t *)pic;
+ format = pim->format;
+ switch (format) {
+ case gs_image_format_chunky:
+ case gs_image_format_component_planar:
+ break;
+ default:
+ use_fallback = 1;
+ }
+ /* AR5 on Windows doesn't support 0-size images. Skipping. */
+ if (pim->Width == 0 || pim->Height == 0)
+ use_fallback = 1;
+ /* PDF doesn't support images with more than 8 bits per component. */
+ switch (pim->BitsPerComponent) {
+ case 1:
+ case 2:
+ case 4:
+ case 8:
+ break;
+ case 12:
+ case 16:
+ break;
+ default:
+ gs_free(mem->non_gc_memory, image, 4, sizeof(image_union_t),
+ "pdf_begin_typed_image(image)");
+ return_error(gs_error_rangecheck);
+ }
+ if (prect)
+ rect = *prect;
+ else {
+ rect.p.x = rect.p.y = 0;
+ rect.q.x = pim->Width, rect.q.y = pim->Height;
+ }
+ if (rect.p.x != 0 || rect.p.y != 0 ||
+ rect.q.x != pim->Width || rect.q.y != pim->Height ||
+ (is_mask && pim->CombineWithColor))
+ use_fallback = 1;
+
+ if (pdev->Eps2Write) {
+ gs_rect sbox, dbox, *Box;
+ gs_point corners[4];
+ gs_fixed_rect ibox;
+ gs_matrix * pmat1 = (gs_matrix *)pmat;
+ gs_matrix mat;
+
+ if (!pdev->accumulating_charproc)
+ Box = &pdev->BBox;
+ else
+ Box = &pdev->charproc_BBox;
+ if (pmat1 == 0)
+ pmat1 = (gs_matrix *)&ctm_only(pis);
+ if ((code = gs_matrix_invert(&pic->ImageMatrix, &mat)) < 0 ||
+ (code = gs_matrix_multiply(&mat, pmat1, &mat)) < 0)
+ return code;
+ sbox.p.x = rect.p.x;
+ sbox.p.y = rect.p.y;
+ sbox.q.x = rect.q.x;
+ sbox.q.y = rect.q.y;
+ gs_bbox_transform_only(&sbox, &mat, corners);
+ gs_points_bbox(corners, &dbox);
+ ibox.p.x = float2fixed(dbox.p.x);
+ ibox.p.y = float2fixed(dbox.p.y);
+ ibox.q.x = float2fixed(dbox.q.x);
+ ibox.q.y = float2fixed(dbox.q.y);
+ if (pcpath != NULL &&
+ !gx_cpath_includes_rectangle(pcpath, ibox.p.x, ibox.p.y,
+ ibox.q.x, ibox.q.y)
+ ) {
+ /* Let the target do the drawing, but drive two triangles */
+ /* through the clipping path to get an accurate bounding box. */
+ gx_device_clip cdev;
+ gx_drawing_color devc;
+
+ fixed x0 = float2fixed(corners[0].x), y0 = float2fixed(corners[0].y);
+ fixed bx2 = float2fixed(corners[2].x) - x0, by2 = float2fixed(corners[2].y) - y0;
+
+ pdev->AccumulatingBBox++;
+ gx_make_clip_device_on_stack(&cdev, pcpath, (gx_device *)pdev);
+ set_nonclient_dev_color(&devc, gx_device_black((gx_device *)pdev)); /* any non-white color will do */
+ gx_default_fill_triangle((gx_device *) & cdev, x0, y0,
+ float2fixed(corners[1].x) - x0,
+ float2fixed(corners[1].y) - y0,
+ bx2, by2, &devc, lop_default);
+ gx_default_fill_triangle((gx_device *) & cdev, x0, y0,
+ float2fixed(corners[3].x) - x0,
+ float2fixed(corners[3].y) - y0,
+ bx2, by2, &devc, lop_default);
+ pdev->AccumulatingBBox--;
+ } else {
+ /* Just use the bounding box. */
+ float x0, y0, x1, y1;
+ x0 = fixed2float(ibox.p.x) / (pdev->HWResolution[0] / 72.0);
+ y0 = fixed2float(ibox.p.y) / (pdev->HWResolution[1] / 72.0);
+ x1 = fixed2float(ibox.q.x) / (pdev->HWResolution[0] / 72.0);
+ y1 = fixed2float(ibox.q.y) / (pdev->HWResolution[1] / 72.0);
+ if (Box->p.x > x0)
+ Box->p.x = x0;
+ if (Box->p.y > y0)
+ Box->p.y = y0;
+ if (Box->q.x < x1)
+ Box->q.x = x1;
+ if (Box->q.y < y1)
+ Box->q.y = y1;
+ }
+ }
+
+ if (use_fallback) {
+ gs_free(mem->non_gc_memory, image, 4, sizeof(image_union_t),
+ "pdf_begin_typed_image(image)");
+ return gx_default_begin_typed_image
+ ((gx_device *)pdev, pis, pmat, pic, prect, pdcolor, pcpath, mem,
+ pinfo);
+ }
+
+ pcs = pim->ColorSpace;
+ num_components = (is_mask ? 1 : gs_color_space_num_components(pcs));
+
+ if (pdf_must_put_clip_path(pdev, pcpath))
+ code = pdf_unclip(pdev);
+ else
+ code = pdf_open_page(pdev, PDF_IN_STREAM);
+ if (code < 0) {
+ gs_free(mem->non_gc_memory, image, 4, sizeof(image_union_t),
+ "pdf_begin_typed_image(image)");
+ return gx_default_begin_typed_image
+ ((gx_device *)pdev, pis, pmat, pic, prect, pdcolor, pcpath, mem,
+ pinfo);
+ }
+
+ if (context == PDF_IMAGE_TYPE3_MASK) {
+ /*
+ * The soft mask for an ImageType 3x image uses a DevicePixel
+ * color space, which pdf_color_space() can't handle. Patch it
+ * to DeviceGray here.
+ */
+ /* {csrc} make sure this gets freed */
+ pcs = gs_cspace_new_DeviceGray(pdev->memory);
+ } else if (is_mask)
+ code = pdf_prepare_imagemask(pdev, pis, pdcolor);
+ else
+ code = pdf_prepare_image(pdev, pis);
+ if (code < 0) {
+ gs_free(mem->non_gc_memory, image, 4, sizeof(image_union_t),
+ "pdf_begin_typed_image(image)");
+ return gx_default_begin_typed_image
+ ((gx_device *)pdev, pis, pmat, pic, prect, pdcolor, pcpath, mem,
+ pinfo);
+ }
+
+ pie = gs_alloc_struct(mem, pdf_image_enum, &st_pdf_image_enum,
+ "pdf_begin_image");
+ if (pie == 0) {
+ gs_free(mem->non_gc_memory, image, 4, sizeof(image_union_t),
+ "pdf_begin_typed_image(image)");
+ return_error(gs_error_VMerror);
+ }
+ memset(pie, 0, sizeof(*pie)); /* cleanup entirely for GC to work in all cases. */
+ *pinfo = (gx_image_enum_common_t *) pie;
+ gx_image_enum_common_init(*pinfo, (const gs_data_image_t *) pim,
+ ((pdev->CompatibilityLevel >= 1.3) ?
+ (context == PDF_IMAGE_TYPE3_MASK ?
+ &pdf_image_object_enum_procs :
+ &pdf_image_enum_procs) :
+ context == PDF_IMAGE_TYPE3_MASK ?
+ &pdf_image_object_enum_procs :
+ context == PDF_IMAGE_TYPE3_DATA ?
+ &pdf_image_object_enum_procs2 :
+ &pdf_image_enum_procs),
+ (gx_device *)pdev, num_components, format);
+ pie->memory = mem;
+ width = rect.q.x - rect.p.x;
+ pie->width = width;
+ height = rect.q.y - rect.p.y;
+ pie->bits_per_pixel =
+ pim->BitsPerComponent * num_components / pie->num_planes;
+ pie->rows_left = height;
+ if (pnamed != 0) /* Don't in-line the image if it is named. */
+ in_line = false;
+ else {
+ double nbytes = (double)(((ulong) pie->width * pie->bits_per_pixel + 7) >> 3) *
+ pie->num_planes * pie->rows_left;
+
+ in_line &= (nbytes < pdev->MaxInlineImageSize);
+ }
+
+ if (pmat == 0)
+ pmat = &ctm_only(pis);
+ {
+ gs_matrix mat;
+ gs_matrix bmat;
+ int code;
+
+ pdf_make_bitmap_matrix(&bmat, -rect.p.x, -rect.p.y,
+ pim->Width, pim->Height, height);
+ if ((code = gs_matrix_invert(&pim->ImageMatrix, &mat)) < 0 ||
+ (code = gs_matrix_multiply(&bmat, &mat, &mat)) < 0 ||
+ (code = gs_matrix_multiply(&mat, pmat, &pie->mat)) < 0
+ ) {
+ gs_free(mem->non_gc_memory, image, 4, sizeof(image_union_t),
+ "pdf_begin_typed_image(image)");
+ gs_free_object(mem, pie, "pdf_begin_image");
+ return code;
+ }
+ /* AR3,AR4 show no image when CTM is singular; AR5 reports an error */
+ if (pie->mat.xx * pie->mat.yy == pie->mat.xy * pie->mat.yx)
+ goto fail_and_fallback;
+ }
+
+ code = pdf_put_clip_path(pdev, pcpath);
+ if (code < 0) {
+ gs_free(mem->non_gc_memory, image, 4, sizeof(image_union_t),
+ "pdf_begin_typed_image(image)");
+ gs_free_object(mem, pie, "pdf_begin_image");
+ return code;
+ }
+ pdf_image_writer_init(&pie->writer);
+ /* Note : Possible values for alt_writer_count are 1,2,3,4.
+ 1 means no alternative streams.
+ 2 means the main image stream and a mask stream while converting
+ an Image Type 4.
+ 3 means the main image stream, alternative image compression stream,
+ and the compression chooser.
+ 4 meams 3 and a mask stream while convertingh an Image Type 4.
+ */
+ pie->writer.alt_writer_count = (in_line ||
+ (pim->Width <= 64 && pim->Height <= 64)
+ ? 1 : 2);
+ if ((image[0].pixel.ColorSpace != NULL &&
+ image[0].pixel.ColorSpace->type->index == gs_color_space_index_Indexed
+ && pdev->params.ColorImage.DownsampleType != ds_Subsample) ||
+ pdev->transfer_not_identity)
+ force_lossless = true;
+
+ if ((image[0].pixel.ColorSpace != NULL && image[0].pixel.ColorSpace->type->index == gs_color_space_index_Indexed)
+ || force_lossless)
+ pie->writer.alt_writer_count = 1;
+
+ names = (in_line ? &pdf_color_space_names_short : &pdf_color_space_names);
+
+ /* We don't want to change the colour space of a mask, or an SMask (both of which are Gray) */
+ if (!is_mask) {
+ if (image[0].pixel.ColorSpace != NULL && !(context == PDF_IMAGE_TYPE3_MASK))
+ convert_to_process_colors = setup_image_colorspace(pdev, &image[0], pcs, &pcs_orig, names, &cs_value);
+ if (pim->BitsPerComponent > 8 && convert_to_process_colors)
+ goto fail_and_fallback;
+ if (convert_to_process_colors == 4) {
+ code = convert_DeviceN_alternate(pdev, pis, pcs, NULL, NULL, NULL, NULL, &cs_value, in_line);
+ if (code < 0)
+ goto fail_and_fallback;
+ }
+ if (convert_to_process_colors == 3) {
+ code = convert_separation_alternate(pdev, pis, pcs, NULL, NULL, NULL, NULL, &cs_value, in_line);
+ if (code < 0)
+ goto fail_and_fallback;
+ }
+ if (convert_to_process_colors == 1) {
+ code = make_device_color_space(pdev, pdev->pcm_color_info_index, &pcs_device);
+ if (code < 0)
+ goto fail_and_fallback;
+ image[0].pixel.ColorSpace = pcs_device;
+ code = pdf_color_space_named(pdev, pis, &cs_value, &pranges, pcs_device, names,
+ in_line, NULL, 0, false);
+ if (code < 0)
+ goto fail_and_fallback;
+ } else {
+ if (convert_to_process_colors == 2) {
+ convert_to_process_colors = 0;
+ code = pdf_color_space_named(pdev, pis, &cs_value, &pranges, pcs, names,
+ in_line, NULL, 0, true);
+ if (code < 0)
+ goto fail_and_fallback;
+ } else {
+ convert_to_process_colors = 0;
+ code = pdf_color_space_named(pdev, pis, &cs_value, &pranges, pcs, names,
+ in_line, NULL, 0, false);
+ if (code < 0)
+ goto fail_and_fallback;
+ }
+ }
+ }
+
+ image[1] = image[0];
+
+ pdev->ParamCompatibilityLevel = pdev->CompatibilityLevel;
+
+ code = pdf_begin_write_image(pdev, &pie->writer, gs_no_id, width,
+ height, pnamed, in_line);
+ if (code < 0)
+ goto fail_and_fallback;
+
+ /* Code below here deals with setting up the multiple data stream writing.
+ * We can have up to 4 stream writers, which we keep in an array. We must
+ * always have at least one which writes the uncompressed stream. If we
+ * are writing compressed streams, we have one for the compressed stream
+ * and one for the compression chooser.
+ * For type 4 images being converted (for old versions of PDF or for ps2write)
+ * we need an additional stream to write a mask, which masks the real
+ * image.
+ * For colour conversion we will place an additional filter in front of all
+ * the streams which does the conversion.
+ */
+ if (in_line) {
+ code = new_setup_lossless_filters((gx_device_psdf *) pdev,
+ &pie->writer.binary[0],
+ &image[0].pixel, in_line, convert_to_process_colors);
+ } else {
+ if (force_lossless) {
+ /*
+ * Some regrettable PostScript code (such as LanguageLevel 1 output
+ * from Microsoft's PSCRIPT.DLL driver) misuses the transfer
+ * function to accomplish the equivalent of indexed color.
+ * Downsampling (well, only averaging) or JPEG compression are not
+ * compatible with this. Play it safe by using only lossless
+ * filters if the transfer function(s) is/are other than the
+ * identity and by setting the downsample type to Subsample..
+ */
+ int saved_downsample = pdev->params.ColorImage.DownsampleType;
+
+ pdev->params.ColorImage.DownsampleType = ds_Subsample;
+ code = new_setup_image_filters((gx_device_psdf *) pdev,
+ &pie->writer.binary[0], &image[0].pixel,
+ pmat, pis, true, in_line, convert_to_process_colors);
+ pdev->params.ColorImage.DownsampleType = saved_downsample;
+ } else {
+ code = new_setup_image_filters((gx_device_psdf *) pdev,
+ &pie->writer.binary[0], &image[0].pixel,
+ pmat, pis, true, in_line, convert_to_process_colors);
+ }
+ }
+
+ if (code < 0)
+ goto fail_and_fallback;
+
+ if (convert_to_process_colors) {
+ image[0].pixel.ColorSpace = pcs_orig;
+ code = psdf_setup_image_colors_filter(&pie->writer.binary[0],
+ (gx_device_psdf *)pdev, &image[0].pixel, pis);
+ if (code < 0)
+ goto fail_and_fallback;
+ image[0].pixel.ColorSpace = pcs_device;
+ }
+
+ if (pie->writer.alt_writer_count > 1) {
+ code = pdf_make_alt_stream(pdev, &pie->writer.binary[1]);
+ if (code) {
+ goto fail_and_fallback;
+ }
+ code = new_setup_image_filters((gx_device_psdf *) pdev,
+ &pie->writer.binary[1], &image[1].pixel,
+ pmat, pis, force_lossless, in_line, convert_to_process_colors);
+ if (code == gs_error_rangecheck) {
+
+ for (i=1;i < pie->writer.alt_writer_count; i++) {
+ stream *s = pie->writer.binary[i].strm;
+ cos_stream_t *pcos = cos_stream_from_pipeline(pie->writer.binary[i].strm);
+ s_close_filters(&s, NULL);
+ gs_free_object(pdev->pdf_memory, s, "compressed image stream");
+ pcos->cos_procs->release((cos_object_t *)pcos, "pdf_begin_typed_image_impl");
+ gs_free_object(pdev->pdf_memory, pcos, "compressed image cos_stream");
+ }
+ /* setup_image_compression rejected the alternative compression. */
+ pie->writer.alt_writer_count = 1;
+ memset(pie->writer.binary + 1, 0, sizeof(pie->writer.binary[1]));
+ memset(pie->writer.binary + 2, 0, sizeof(pie->writer.binary[1]));
+ } else if (code) {
+ goto fail_and_fallback;
+ } else if (convert_to_process_colors) {
+ image[1].pixel.ColorSpace = pcs_orig;
+ code = psdf_setup_image_colors_filter(&pie->writer.binary[1],
+ (gx_device_psdf *)pdev, &image[1].pixel, pis);
+ if (code < 0) {
+ goto fail_and_fallback;
+ }
+ image[1].pixel.ColorSpace = pcs_device;
+ }
+ }
+
+ for (i = 0; i < pie->writer.alt_writer_count; i++) {
+ code = pdf_begin_image_data_decoded(pdev, num_components, pranges, i,
+ &image[i].pixel, &cs_value, pie);
+ if (code < 0)
+ goto fail_and_fallback;
+ }
+ if (pie->writer.alt_writer_count == 2) {
+ psdf_setup_compression_chooser(&pie->writer.binary[2],
+ (gx_device_psdf *)pdev, pim->Width, pim->Height,
+ num_components, pim->BitsPerComponent);
+ pie->writer.alt_writer_count = 3;
+ }
+ if (pic->type->index == 4 && pdev->CompatibilityLevel < 1.3) {
+ int i;
+
+ /* Create a stream for writing the mask. */
+ i = pie->writer.alt_writer_count;
+ gs_image_t_init_mask_adjust((gs_image_t *)&image[i].type1, true, false);
+ image[i].type1.Width = image[0].pixel.Width;
+ image[i].type1.Height = image[0].pixel.Height;
+ /* Won't use image[2]. */
+ code = pdf_begin_write_image(pdev, &pie->writer, gs_no_id, width,
+ height, NULL, false);
+ if (code)
+ goto fail_and_fallback;
+ code = psdf_setup_image_filters((gx_device_psdf *) pdev,
+ &pie->writer.binary[i], &image[i].pixel,
+ pmat, pis, force_lossless, in_line);
+ if (code < 0)
+ goto fail_and_fallback;
+ psdf_setup_image_to_mask_filter(&pie->writer.binary[i],
+ (gx_device_psdf *)pdev, pim->Width, pim->Height,
+ num_components, pim->BitsPerComponent, image[i].type4.MaskColor);
+ code = pdf_begin_image_data_decoded(pdev, num_components, pranges, i,
+ &image[i].pixel, &cs_value, pie);
+ if (code < 0)
+ goto fail_and_fallback;
+ ++pie->writer.alt_writer_count;
+ }
+
+ gs_free(mem->non_gc_memory, image, 4, sizeof(image_union_t),
+ "pdf_begin_typed_image(image)");
+ return 0;
+
+fail_and_fallback:
+ gs_free(mem->non_gc_memory, image, 4, sizeof(image_union_t),
+ "pdf_begin_typed_image(image)");
+ gs_free_object(mem, pie, "pdf_begin_image");
+ return gx_default_begin_typed_image
+ ((gx_device *)pdev, pis, pmat, pic, prect, pdcolor, pcpath, mem,
+ pinfo);
+}
+
+static int pdf_begin_typed_image(gx_device_pdf *pdev,
+ const gs_imager_state * pis, const gs_matrix *pmat,
+ const gs_image_common_t *pic, const gs_int_rect * prect,
+ const gx_drawing_color * pdcolor, const gx_clip_path * pcpath,
+ gs_memory_t * mem, gx_image_enum_common_t ** pinfo,
+ pdf_typed_image_context_t context)
+{
+ if (!pdev->UseOldColor) {
+ return new_pdf_begin_typed_image(pdev, pis, pmat, pic, prect,
+ pdcolor, pcpath, mem, pinfo,
+ context);
+ } else {
+ return old_pdf_begin_typed_image(pdev, pis, pmat, pic, prect,
+ pdcolor, pcpath, mem, pinfo,
+ context);
+ }
+}
+
+int
+gdev_pdf_begin_typed_image(gx_device * dev, const gs_imager_state * pis,
+ const gs_matrix *pmat, const gs_image_common_t *pic,
+ const gs_int_rect * prect,
+ const gx_drawing_color * pdcolor,
+ const gx_clip_path * pcpath, gs_memory_t * mem,
+ gx_image_enum_common_t ** pinfo)
+{
+ return pdf_begin_typed_image((gx_device_pdf *)dev, pis, pmat, pic, prect,
+ pdcolor, pcpath, mem, pinfo,
+ PDF_IMAGE_DEFAULT);
+}
+
+/* ---------------- All images ---------------- */
+
+/* Process the next piece of an image. */
+static int
+pdf_image_plane_data_alt(gx_image_enum_common_t * info,
+ const gx_image_plane_t * planes, int height,
+ int *rows_used, int alt_writer_index)
+{
+ pdf_image_enum *pie = (pdf_image_enum *) info;
+ int h = height;
+ int y;
+ /****** DOESN'T HANDLE IMAGES WITH VARYING WIDTH PER PLANE ******/
+ uint width_bits = pie->width * pie->plane_depths[0];
+ /****** DOESN'T HANDLE NON-ZERO data_x CORRECTLY ******/
+ uint ignore;
+ int nplanes = pie->num_planes;
+ int status = 0;
+ uint bcount = (width_bits + 7) >> 3;
+
+ if (h > pie->rows_left)
+ h = pie->rows_left;
+ for (y = 0; y < h; ++y) {
+ if (nplanes > 1) {
+ /*
+ * We flip images in blocks, and each block except the last one
+ * must contain an integral number of pixels. The easiest way
+ * to meet this condition is for all blocks except the last to
+ * be a multiple of 3 source bytes (guaranteeing an integral
+ * number of 1/2/4/8/12-bit samples), i.e., 3*nplanes flipped
+ * bytes. This requires a buffer of at least
+ * 3*GS_IMAGE_MAX_COMPONENTS bytes.
+ */
+ int pi;
+ uint count = bcount;
+ uint offset = 0;
+#define ROW_BYTES max(200 /*arbitrary*/, 3 * GS_IMAGE_MAX_COMPONENTS)
+ const byte *bit_planes[GS_IMAGE_MAX_COMPONENTS];
+ int block_bytes = ROW_BYTES / (3 * nplanes) * 3;
+ byte row[ROW_BYTES];
+
+ for (pi = 0; pi < nplanes; ++pi)
+ bit_planes[pi] = planes[pi].data + planes[pi].raster * y;
+ while (count) {
+ uint flip_count;
+ uint flipped_count;
+
+ if (count > block_bytes) {
+ flip_count = block_bytes;
+ flipped_count = block_bytes * nplanes;
+ } else {
+ flip_count = count;
+ flipped_count =
+ (width_bits % (block_bytes * 8) * nplanes + 7) >> 3;
+ /* In case the width of the image is a precise multiple of our block size */
+ if (flipped_count == 0)
+ flipped_count = block_bytes * nplanes;
+ }
+ image_flip_planes(row, bit_planes, offset, flip_count,
+ nplanes, pie->plane_depths[0]);
+ status = sputs(pie->writer.binary[alt_writer_index].strm, row,
+ flipped_count, &ignore);
+ if (status < 0)
+ break;
+ offset += flip_count;
+ count -= flip_count;
+ }
+ } else {
+ status = sputs(pie->writer.binary[alt_writer_index].strm,
+ planes[0].data + planes[0].raster * y, bcount,
+ &ignore);
+ }
+ if (status < 0)
+ break;
+ }
+ *rows_used = h;
+ if (status < 0)
+ return_error(gs_error_ioerror);
+ return !pie->rows_left;
+#undef ROW_BYTES
+}
+
+static int
+pdf_image_plane_data(gx_image_enum_common_t * info,
+ const gx_image_plane_t * planes, int height,
+ int *rows_used)
+{
+ pdf_image_enum *pie = (pdf_image_enum *) info;
+ int i;
+
+ for (i = 0; i < pie->writer.alt_writer_count; i++) {
+ int code = pdf_image_plane_data_alt(info, planes, height, rows_used, i);
+ if (code)
+ return code;
+ }
+ pie->rows_left -= *rows_used;
+ if (pie->writer.alt_writer_count > 2)
+ pdf_choose_compression(&pie->writer, false);
+
+ return !pie->rows_left;
+}
+
+static int
+use_image_as_pattern(gx_device_pdf *pdev, pdf_resource_t *pres1,
+ const gs_matrix *pmat, gs_id id)
+{ /* See also dump_image in gdevpdfd.c . */
+ gs_imager_state s;
+ gs_pattern1_instance_t inst;
+ cos_value_t v;
+ const pdf_resource_t *pres;
+ int code;
+
+ memset(&s, 0, sizeof(s));
+ s.ctm.xx = pmat->xx;
+ s.ctm.xy = pmat->xy;
+ s.ctm.yx = pmat->yx;
+ s.ctm.yy = pmat->yy;
+ s.ctm.tx = pmat->tx;
+ s.ctm.ty = pmat->ty;
+ memset(&inst, 0, sizeof(inst));
+ inst.saved = (gs_state *)&s; /* HACK : will use s.ctm only. */
+ inst.templat.PaintType = 1;
+ inst.templat.TilingType = 1;
+ inst.templat.BBox.p.x = inst.templat.BBox.p.y = 0;
+ inst.templat.BBox.q.x = 1;
+ inst.templat.BBox.q.y = 1;
+ inst.templat.XStep = 2; /* Set 2 times bigger step against artifacts. */
+ inst.templat.YStep = 2;
+
+ {
+ pattern_accum_param_s param;
+ param.pinst = (void *)&inst;
+ param.graphics_state = (void *)&s;
+ param.pinst_id = inst.id;
+
+ code = (*dev_proc(pdev, dev_spec_op))((gx_device *)pdev,
+ gxdso_pattern_start_accum, &param, sizeof(pattern_accum_param_s));
+ }
+
+ if (code >= 0)
+ pprintld1(pdev->strm, "/R%ld Do\n", pdf_resource_id(pres1));
+ pres = pdev->accumulating_substream_resource;
+ if (code >= 0)
+ code = pdf_add_resource(pdev, pdev->substream_Resources, "/XObject", pres1);
+ if (code >= 0) {
+ pattern_accum_param_s param;
+ param.pinst = (void *)&inst;
+ param.graphics_state = (void *)&s;
+ param.pinst_id = inst.id;
+
+ code = (*dev_proc(pdev, dev_spec_op))((gx_device *)pdev,
+ gxdso_pattern_finish_accum, &param, id);
+ }
+ if (code >= 0)
+ code = (*dev_proc(pdev, dev_spec_op))((gx_device *)pdev,
+ gxdso_pattern_load, &inst, id);
+ if (code >= 0) {
+ stream_puts(pdev->strm, "q ");
+ code = pdf_cs_Pattern_colored(pdev, &v);
+ }
+ if (code >= 0) {
+ cos_value_write(&v, pdev);
+ pprintld1(pdev->strm, " cs /R%ld scn ", pdf_resource_id(pres));
+ }
+ if (code >= 0) {
+ /* The image offset weas broken in gx_begin_image3_generic,
+ (see 'origin' in there).
+ As a temporary hack use the offset of the image.
+ fixme : This isn't generally correct,
+ because the mask may be "transpozed" against the image. */
+ gs_matrix m = pdev->converting_image_matrix;
+
+ m.tx = pmat->tx;
+ m.ty = pmat->ty;
+ code = pdf_do_image_by_id(pdev, pdev->image_mask_scale,
+ &m, true, pdev->image_mask_id);
+ stream_puts(pdev->strm, "Q\n");
+ }
+ return code;
+}
+
+typedef enum {
+ USE_AS_MASK,
+ USE_AS_IMAGE,
+ USE_AS_PATTERN
+} pdf_image_usage_t;
+
+/* Close PDF image and do it. */
+static int
+pdf_end_and_do_image(gx_device_pdf *pdev, pdf_image_writer *piw,
+ const gs_matrix *mat, gs_id ps_bitmap_id, pdf_image_usage_t do_image)
+{
+ int code = pdf_end_write_image(pdev, piw);
+ pdf_resource_t *pres = piw->pres;
+
+ switch (code) {
+ default:
+ return code; /* error */
+ case 1:
+ code = 0;
+ break;
+ case 0:
+ if (do_image == USE_AS_IMAGE) {
+ if (pdev->image_mask_id != gs_no_id) {
+ char buf[20];
+
+ gs_sprintf(buf, "%ld 0 R", pdev->image_mask_id);
+ code = cos_dict_put_string_copy((cos_dict_t *)pres->object,
+ pdev->image_mask_is_SMask ? "/SMask" : "/Mask", buf);
+ if (code < 0)
+ return code;
+ }
+ if (pdev->image_mask_skip)
+ code = 0;
+ else
+ code = pdf_do_image(pdev, pres, mat, true);
+ } else if (do_image == USE_AS_MASK) {
+ /* Provide data for pdf_do_image_by_id, which will be called through
+ use_image_as_pattern during the next call to this function.
+ See pdf_do_image about the meaning of 'scale'. */
+ const pdf_x_object_t *const pxo = (const pdf_x_object_t *)pres;
+
+ pdev->image_mask_scale = (double)pxo->data_height / pxo->height;
+ pdev->image_mask_id = pdf_resource_id(pres);
+ pdev->converting_image_matrix = *mat;
+ } else if (do_image == USE_AS_PATTERN)
+ code = use_image_as_pattern(pdev, pres, mat, ps_bitmap_id);
+ }
+ return code;
+}
+
+/* Clean up by releasing the buffers. */
+static int
+pdf_image_end_image_data(gx_image_enum_common_t * info, bool draw_last,
+ pdf_image_usage_t do_image)
+{
+ gx_device_pdf *pdev = (gx_device_pdf *)info->dev;
+ pdf_image_enum *pie = (pdf_image_enum *)info;
+ int height = pie->writer.height;
+ int data_height = height - pie->rows_left;
+ int code = 0;
+
+ if (pie->writer.pres)
+ ((pdf_x_object_t *)pie->writer.pres)->data_height = data_height;
+ else if (data_height > 0)
+ pdf_put_image_matrix(pdev, &pie->mat, (double)data_height / height);
+ if (data_height > 0) {
+ if (pie->writer.pres) {
+ code = pdf_complete_image_data(pdev, &pie->writer, data_height,
+ pie->width, pie->bits_per_pixel);
+ if (code < 0)
+ return code;
+ }
+ code = pdf_end_image_binary(pdev, &pie->writer, data_height);
+ /* The call above possibly decreases pie->writer.alt_writer_count in 2. */
+ if (code < 0)
+ return code;
+ if (pie->writer.alt_writer_count == 2) {
+ /* We're converting a type 4 image into an imagemask with a pattern color. */
+ /* Since the type 3 image writes the mask first, do so here. */
+ pdf_image_writer writer = pie->writer;
+
+ writer.binary[0] = pie->writer.binary[1];
+ writer.pres = pie->writer.pres_mask;
+ writer.alt_writer_count = 1;
+ memset(&pie->writer.binary[1], 0, sizeof(pie->writer.binary[1]));
+ pie->writer.alt_writer_count--; /* For GC. */
+ pie->writer.pres_mask = 0; /* For GC. */
+ code = pdf_end_image_binary(pdev, &writer, data_height);
+ if (code < 0)
+ return code;
+ code = pdf_end_and_do_image(pdev, &writer, &pie->mat, info->id, USE_AS_MASK);
+ if (code < 0)
+ return code;
+ code = pdf_end_and_do_image(pdev, &pie->writer, &pie->mat, info->id, USE_AS_PATTERN);
+ } else
+ code = pdf_end_and_do_image(pdev, &pie->writer, &pie->mat, info->id, do_image);
+ pie->writer.alt_writer_count--; /* For GC. */
+ }
+ gx_image_free_enum(&info);
+ return code;
+}
+
+/* End a normal image, drawing it. */
+static int
+pdf_image_end_image(gx_image_enum_common_t * info, bool draw_last)
+{
+ return pdf_image_end_image_data(info, draw_last, USE_AS_IMAGE);
+}
+
+/* End an image converted with pdf_lcvd_t. */
+static int
+pdf_image_end_image_cvd(gx_image_enum_common_t * info, bool draw_last)
+{ pdf_lcvd_t *cvd = (pdf_lcvd_t *)info->dev;
+ int code = pdf_dump_converted_image(cvd->pdev, cvd);
+ int code1 = gx_image1_end_image(info, draw_last);
+ int code2 = gs_closedevice((gx_device *)cvd->mask);
+ int code3 = gs_closedevice((gx_device *)cvd);
+
+ gs_free_object(cvd->mask->memory, (gx_device *)cvd->mask, "pdf_image_end_image_cvd");
+ gs_free_object(cvd->mdev.memory, (gx_device *)cvd, "pdf_image_end_image_cvd");
+ return code < 0 ? code : code1 < 0 ? code1 : code2 < 0 ? code2 : code3;
+}
+/* ---------------- Type 3/3x images ---------------- */
+
+/*
+ * For both types of masked images, we create temporary dummy (null) devices
+ * that forward the begin_typed_image call to the implementation above.
+ */
+static int
+pdf_make_mxd(gx_device **pmxdev, gx_device *tdev, gs_memory_t *mem)
+{
+ gx_device *fdev;
+ int code = gs_copydevice(&fdev, (const gx_device *)&gs_null_device, mem);
+
+ if (code < 0)
+ return code;
+ gx_device_set_target((gx_device_forward *)fdev, tdev);
+ *pmxdev = fdev;
+ return 0;
+}
+
+/* End the mask of an ImageType 3 image, not drawing it. */
+static int
+pdf_image_end_image_object(gx_image_enum_common_t * info, bool draw_last)
+{
+ return pdf_image_end_image_data(info, draw_last, USE_AS_MASK);
+}
+/* End the data of an ImageType 3 image, converting it into pattern. */
+static int
+pdf_image_end_image_object2(gx_image_enum_common_t * info, bool draw_last)
+{
+ return pdf_image_end_image_data(info, draw_last, USE_AS_PATTERN);
+}
+
+/* ---------------- Type 3 images ---------------- */
+
+/* Implement the mask image device. */
+static dev_proc_begin_typed_image(pdf_mid_begin_typed_image);
+static int
+pdf_image3_make_mid(gx_device **pmidev, gx_device *dev, int width, int height,
+ gs_memory_t *mem)
+{
+ gx_device_pdf *pdev = (gx_device_pdf *)dev;
+
+ if (pdev->CompatibilityLevel < 1.3 && !pdev->PatternImagemask) {
+ gs_matrix m;
+ pdf_lcvd_t *cvd = NULL;
+ int code;
+
+ gs_make_identity(&m);
+ code = pdf_setup_masked_image_converter(pdev, mem, &m, &cvd,
+ true, 0, 0, width, height, true);
+ if (code < 0)
+ return code;
+ cvd->mask->target = (gx_device *)cvd; /* Temporary, just to communicate with
+ pdf_image3_make_mcde. The latter will reset it. */
+ cvd->mask_is_empty = false;
+ *pmidev = (gx_device *)cvd->mask;
+ return 0;
+ } else {
+ int code = pdf_make_mxd(pmidev, dev, mem);
+
+ if (code < 0)
+ return code;
+ set_dev_proc(*pmidev, begin_typed_image, pdf_mid_begin_typed_image);
+ return 0;
+ }
+}
+static int
+pdf_mid_begin_typed_image(gx_device * dev, const gs_imager_state * pis,
+ const gs_matrix *pmat, const gs_image_common_t *pic,
+ const gs_int_rect * prect,
+ const gx_drawing_color * pdcolor,
+ const gx_clip_path * pcpath, gs_memory_t * mem,
+ gx_image_enum_common_t ** pinfo)
+{
+ /* The target of the null device is the pdfwrite device. */
+ gx_device_pdf *const pdev = (gx_device_pdf *)
+ ((gx_device_null *)dev)->target;
+ return pdf_begin_typed_image
+ (pdev, pis, pmat, pic, prect, pdcolor, pcpath, mem, pinfo,
+ PDF_IMAGE_TYPE3_MASK);
+}
+
+/* Implement the mask clip device. */
+static int
+pdf_image3_make_mcde(gx_device *dev, const gs_imager_state *pis,
+ const gs_matrix *pmat, const gs_image_common_t *pic,
+ const gs_int_rect *prect, const gx_drawing_color *pdcolor,
+ const gx_clip_path *pcpath, gs_memory_t *mem,
+ gx_image_enum_common_t **pinfo,
+ gx_device **pmcdev, gx_device *midev,
+ gx_image_enum_common_t *pminfo,
+ const gs_int_point *origin)
+{
+ int code;
+ gx_device_pdf *pdev = (gx_device_pdf *)dev;
+
+ if (pdev->CompatibilityLevel < 1.3 && !pdev->PatternImagemask) {
+ /* pdf_image3_make_mid must set midev with a pdf_lcvd_t instance.*/
+ pdf_lcvd_t *cvd = (pdf_lcvd_t *)((gx_device_memory *)midev)->target;
+
+ ((gx_device_memory *)midev)->target = NULL;
+ cvd->m = pdev->converting_image_matrix;
+ cvd->mdev.mapped_x = origin->x;
+ cvd->mdev.mapped_y = origin->y;
+ *pmcdev = (gx_device *)&cvd->mdev;
+ code = gx_default_begin_typed_image
+ ((gx_device *)&cvd->mdev, pis, pmat, pic, prect, pdcolor, NULL, mem,
+ pinfo);
+ if (code < 0)
+ return code;
+ } else {
+ code = pdf_make_mxd(pmcdev, midev, mem);
+ if (code < 0)
+ return code;
+ code = pdf_begin_typed_image
+ ((gx_device_pdf *)dev, pis, pmat, pic, prect, pdcolor, pcpath, mem,
+ pinfo, PDF_IMAGE_TYPE3_DATA);
+ if (code < 0)
+ return code;
+ }
+ /* Due to equal image merging, we delay the adding of the "Mask" entry into
+ a type 3 image dictionary until the mask is completed.
+ Will do in pdf_end_and_do_image.*/
+ return 0;
+}
+
+/* ---------------- Type 3x images ---------------- */
+
+/* Implement the mask image device. */
+static int
+pdf_image3x_make_mid(gx_device **pmidev, gx_device *dev, int width, int height,
+ int depth, gs_memory_t *mem)
+{
+ int code = pdf_make_mxd(pmidev, dev, mem);
+
+ if (code < 0)
+ return code;
+ set_dev_proc(*pmidev, begin_typed_image, pdf_mid_begin_typed_image);
+ return 0;
+}
+
+/* Implement the mask clip device. */
+static int
+pdf_image3x_make_mcde(gx_device *dev, const gs_imager_state *pis,
+ const gs_matrix *pmat, const gs_image_common_t *pic,
+ const gs_int_rect *prect,
+ const gx_drawing_color *pdcolor,
+ const gx_clip_path *pcpath, gs_memory_t *mem,
+ gx_image_enum_common_t **pinfo,
+ gx_device **pmcdev, gx_device *midev[2],
+ gx_image_enum_common_t *pminfo[2],
+ const gs_int_point origin[2],
+ const gs_image3x_t *pim)
+{
+ int code;
+ pdf_image_enum *pmie;
+ int i;
+ const gs_image3x_mask_t *pixm;
+
+ if (midev[0]) {
+ if (midev[1])
+ return_error(gs_error_rangecheck);
+ i = 0, pixm = &pim->Opacity;
+ } else if (midev[1])
+ i = 1, pixm = &pim->Shape;
+ else
+ return_error(gs_error_rangecheck);
+ code = pdf_make_mxd(pmcdev, midev[i], mem);
+ if (code < 0)
+ return code;
+ code = pdf_begin_typed_image
+ ((gx_device_pdf *)dev, pis, pmat, pic, prect, pdcolor, pcpath, mem,
+ pinfo, PDF_IMAGE_TYPE3_DATA);
+ if (code < 0)
+ return code;
+ if ((*pinfo)->procs != &pdf_image_enum_procs) {
+ /* We couldn't handle the image. Bail out. */
+ gx_image_end(*pinfo, false);
+ gs_free_object(mem, *pmcdev, "pdf_image3x_make_mcde");
+ return_error(gs_error_rangecheck);
+ }
+ pmie = (pdf_image_enum *)pminfo[i];
+ /*
+ * Add the SMask entry to the image dictionary, and, if needed,
+ * the Matte entry to the mask dictionary.
+ */
+ if (pixm->has_Matte) {
+ int num_components =
+ gs_color_space_num_components(pim->ColorSpace);
+
+ code = cos_dict_put_c_key_floats((gx_device_pdf *)dev,
+ (cos_dict_t *)pmie->writer.pres->object,
+ "/Matte", pixm->Matte,
+ num_components);
+ if (code < 0)
+ return code;
+ }
+/* Don't put SMask here because pmie->writer.pres->object may be substituted
+ * after the image stream is accummulated. pdf_end_and_do_image will set
+ * SMask with the right value. Bug 690345.
+ */
+ return 0;
+}
+
+pdf_resource_t *pdf_substitute_pattern(pdf_resource_t *pres)
+{
+ pdf_pattern_t *ppat = (pdf_pattern_t *)pres;
+
+ return (pdf_resource_t *)(ppat->substitute != 0 ? ppat->substitute : ppat);
+}
+
+static int
+check_unsubstituted2(gx_device_pdf * pdev, pdf_resource_t *pres0, pdf_resource_t *pres1)
+{
+ pdf_pattern_t *ppat0 = (pdf_pattern_t *)pres0;
+ pdf_pattern_t *ppat1 = (pdf_pattern_t *)pres1;
+
+ return (ppat0->substitute == NULL && ppat1->substitute == NULL);
+}
+
+static int
+check_unsubstituted1(gx_device_pdf * pdev, pdf_resource_t *pres0)
+{
+ pdf_pattern_t *ppat = (pdf_pattern_t *)pres0;
+
+ return ppat->substitute != NULL;
+}
+
+/*
+ The device specific operations - just pattern management.
+ See gxdevcli.h about return codes.
+ */
+int
+gdev_pdf_dev_spec_op(gx_device *pdev1, int dev_spec_op, void *data, int size)
+{
+ gx_device_pdf *pdev = (gx_device_pdf *)pdev1;
+ int code=0;
+ pdf_resource_t *pres, *pres1;
+ gx_bitmap_id id = (gx_bitmap_id)size;
+
+ switch (dev_spec_op) {
+ case gxdso_pattern_can_accum:
+ return 1;
+ case gxdso_form_begin:
+ if (pdev->HighLevelForm == 0 && pdev->PatternDepth == 0) {
+ gs_form_template_t *tmplate = (gs_form_template_t *)data;
+ float arry[6];
+ cos_dict_t *pcd = NULL, *pcd_Resources = NULL;
+
+ /* Make sure the document and page stream are open */
+ code = pdfwrite_pdf_open_document(pdev);
+ if (code < 0)
+ return code;
+ code = pdf_open_contents(pdev, PDF_IN_STREAM);
+ if (code < 0)
+ return code;
+ /* Put any extant clip out before we start the form */
+ code = pdf_put_clip_path(pdev, tmplate->pcpath);
+ if (code < 0)
+ return code;
+ /* Set the CTM to be the one passed in from the interpreter,
+ * this allows us to spot forms even when translation/rotation takes place
+ * as we remove the CTN from the form stream before capture
+ */
+ pprintg6(pdev->strm, "q %g %g %g %g %g %g cm\n", tmplate->CTM.xx, tmplate->CTM.xy,
+ tmplate->CTM.yx, tmplate->CTM.yy, tmplate->CTM.tx, tmplate->CTM.ty);
+
+ /* star capturing the form stream */
+ code = pdf_enter_substream(pdev, resourceXObject, id, &pres, false,
+ pdev->CompressFonts/* Have no better switch.*/);
+ if (code < 0)
+ return code;
+ pcd = cos_stream_dict((cos_stream_t *)pres->object);
+ pcd_Resources = cos_dict_alloc(pdev, "pdf_pattern(Resources)");
+ if (pcd == NULL || pcd_Resources == NULL)
+ return_error(gs_error_VMerror);
+ code = cos_dict_put_c_strings(pcd, "/Type", "/XObject");
+ if (code >= 0)
+ code = cos_dict_put_c_strings(pcd, "/Subtype", "/Form");
+ if (code >= 0)
+ code = cos_dict_put_c_strings(pcd, "/FormType", "1");
+ if (code >= 0)
+ code = cos_dict_put_c_key_object(pcd, "/Resources", COS_OBJECT(pcd_Resources));
+ arry[0] = tmplate->BBox.p.x;
+ arry[1] = tmplate->BBox.p.y;
+ arry[2] = tmplate->BBox.q.x;
+ arry[3] = tmplate->BBox.q.y;
+ if (code >= 0)
+ code = cos_dict_put_c_key_floats(pdev, pcd, "/BBox", arry, 4);
+ if (code < 0)
+ return code;
+
+ arry[0] = tmplate->form_matrix.xx;
+ arry[1] = tmplate->form_matrix.xy;
+ arry[2] = tmplate->form_matrix.yx;
+ arry[3] = tmplate->form_matrix.yy;
+ arry[4] = tmplate->form_matrix.tx;
+ arry[5] = tmplate->form_matrix.ty;
+ code = cos_dict_put_c_key_floats(pdev, pcd, "/Matrix", arry, 6);
+ pprintg2(pdev->strm, "%g 0 0 %g 0 0 cm\n",
+ 72.0 / pdev->HWResolution[0], 72.0 / pdev->HWResolution[1]);
+
+ /* We'll return this to the interpreter and have it set
+ * as the CTM, so that we remove the prior CTM before capturing the form.
+ * This is safe because forms are always run inside a gsave/grestore, so
+ * CTM will be put back for us.
+ */
+ tmplate->CTM.xx = pdev->HWResolution[0] / 72;
+ tmplate->CTM.xy = 0.0;
+ tmplate->CTM.yx = 0.0;
+ tmplate->CTM.yy = pdev->HWResolution[0] / 72;
+ tmplate->CTM.tx = 0.0;
+ tmplate->CTM.ty = 0.0;
+
+ pdev->substream_Resources = pcd_Resources;
+ pres->rid = id;
+ if (code >= 0)
+ pdev->HighLevelForm++;
+ return 1;
+ }
+ return code;
+ case gxdso_form_end:
+ /* This test must be the same as the one in gxdso_form_begin, above */
+ if (pdev->HighLevelForm == 1 && pdev->PatternDepth == 0) {
+ code = pdf_add_procsets(pdev->substream_Resources, pdev->procsets);
+ if (code < 0)
+ return code;
+ pres = pres1 = pdev->accumulating_substream_resource;
+ code = pdf_exit_substream(pdev);
+ if (code < 0)
+ return code;
+ code = pdf_find_same_resource(pdev, resourceXObject, &pres, check_unsubstituted2);
+ if (code < 0)
+ return code;
+ if (code > 0) {
+ code = pdf_cancel_resource(pdev, pres1, resourceXObject);
+ if (code < 0)
+ return code;
+ pres->where_used |= pdev->used_mask;
+ } else if (pres->object->id < 0)
+ pdf_reserve_object_id(pdev, pres, 0);
+ pprintld1(pdev->strm, "/R%ld Do Q\n", pdf_resource_id(pres));
+ pdev->HighLevelForm--;
+ pdev->LastFormID = pdf_resource_id(pres);
+ }
+ return 0;
+ case gxdso_get_form_ID:
+ {
+ int *ID = data;
+ *ID = pdev->LastFormID;
+ }
+ return 0;
+ case gxdso_repeat_form:
+ {
+ gs_form_template_t *tmplate = (gs_form_template_t *)data;
+
+ /* Make sure the document and page stream are open */
+ code = pdfwrite_pdf_open_document(pdev);
+ if (code < 0)
+ return code;
+ code = pdf_open_contents(pdev, PDF_IN_STREAM);
+ if (code < 0)
+ return code;
+ /* Put any extant clip out before we start the form */
+ code = pdf_put_clip_path(pdev, tmplate->pcpath);
+ if (code < 0)
+ return code;
+ /* Set the CTM to be the one passed in from the interpreter,
+ * this allows us to spot forms even when translation/rotation takes place
+ * as we remove the CTN from the form stream before capture
+ */
+ pprintg6(pdev->strm, "q %g %g %g %g %g %g cm\n", tmplate->CTM.xx, tmplate->CTM.xy,
+ tmplate->CTM.yx, tmplate->CTM.yy, tmplate->CTM.tx, tmplate->CTM.ty);
+ pprintld1(pdev->strm, "/R%ld Do Q\n", tmplate->FormID);
+ pres = pdf_find_resource_by_resource_id(pdev, resourceXObject, tmplate->FormID);
+ pres->where_used |= pdev->used_mask;
+ }
+ return 0;
+ case gxdso_pattern_start_accum:
+ {
+ pattern_accum_param_s *param = (pattern_accum_param_s *)data;
+ gs_pattern1_instance_t *pinst = param->pinst;
+ id = param->pinst_id;
+
+ code = pdf_enter_substream(pdev, resourcePattern, id, &pres, false,
+ pdev->CompressFonts/* Have no better switch.*/);
+ if (code < 0)
+ return code;
+ pres->rid = id;
+ code = pdf_store_pattern1_params(pdev, pres, pinst);
+ if (code < 0)
+ return code;
+ /* Scale the coordinate system, because object handlers assume so. See none_to_stream. */
+ pprintg2(pdev->strm, "%g 0 0 %g 0 0 cm\n",
+ 72.0 / pdev->HWResolution[0], 72.0 / pdev->HWResolution[1]);
+ pdev->PatternDepth++;
+ }
+ return 1;
+ case gxdso_pattern_finish_accum:
+ code = pdf_add_procsets(pdev->substream_Resources, pdev->procsets);
+ if (code < 0)
+ return code;
+ pres = pres1 = pdev->accumulating_substream_resource;
+ code = pdf_exit_substream(pdev);
+ if (code < 0)
+ return code;
+ if (pdev->substituted_pattern_count > 300 &&
+ pdev->substituted_pattern_drop_page != pdev->next_page) { /* arbitrary */
+ pdf_drop_resources(pdev, resourcePattern, check_unsubstituted1);
+ pdev->substituted_pattern_count = 0;
+ pdev->substituted_pattern_drop_page = pdev->next_page;
+ }
+ code = pdf_find_same_resource(pdev, resourcePattern, &pres, check_unsubstituted2);
+ if (code < 0)
+ return code;
+ if (code > 0) {
+ pdf_pattern_t *ppat = (pdf_pattern_t *)pres1;
+
+ code = pdf_cancel_resource(pdev, pres1, resourcePattern);
+ if (code < 0)
+ return code;
+ /* Do not remove pres1, because it keeps the substitution. */
+ ppat->substitute = (pdf_pattern_t *)pres;
+ pres->where_used |= pdev->used_mask;
+ pdev->substituted_pattern_count++;
+ } else if (pres->object->id < 0)
+ pdf_reserve_object_id(pdev, pres, 0);
+ pdev->PatternDepth--;
+ return 1;
+ case gxdso_pattern_load:
+ pres = pdf_find_resource_by_gs_id(pdev, resourcePattern, id);
+ if (pres == 0)
+ return gs_error_undefined;
+ pres = pdf_substitute_pattern(pres);
+ pres->where_used |= pdev->used_mask;
+ code = pdf_add_resource(pdev, pdev->substream_Resources, "/Pattern", pres);
+ if (code < 0)
+ return code;
+ return 1;
+ case gxdso_pattern_shading_area:
+ return 0;
+ case gxdso_pattern_is_cpath_accum:
+ return 0;
+ case gxdso_pattern_shfill_doesnt_need_path:
+ return 0; /* gdev_pdf_fill_path still does need a path. */
+ case gxdso_pattern_handles_clip_path:
+ /* This is important when the default implementation of
+ of fill_path is called due to a failure in setcolor
+ or so, for example when a shading is incorrect.
+ The test case is the unfixed (buggy) Genoa test 446-01.ps .
+ In this case pdfwrite converts the object into rectangles,
+ and the clipping device has to be set up. */
+ return 0;
+ case gxdso_supports_hlcolor:
+ /* This is used due to some aliasing between the rect_hl color
+ filling used by pdfwrite vs. that used by the planar device
+ which is actually a devn vs. the pattern type for pdfwrite.
+ We use this to distingush between the two */
+ return 1;
+ case gxdso_needs_invariant_palette:
+ /* Indicates that it is not permissible to change /Indexed colour space
+ * palette entries after the colour space has been set.
+ */
+ return 1;
+ case gxdso_get_dev_param:
+ {
+ int code;
+ dev_param_req_t *request = (dev_param_req_t *)data;
+ code = gdev_pdf_get_param(pdev1, request->Param, request->list);
+ if (code != gs_error_undefined)
+ return code;
+ }
+ }
+ return gx_default_dev_spec_op(pdev1, dev_spec_op, data, size);
+}
diff --git a/devices/vector/gdevpdfj.c b/devices/vector/gdevpdfj.c
new file mode 100644
index 000000000..fa7a51528
--- /dev/null
+++ b/devices/vector/gdevpdfj.c
@@ -0,0 +1,677 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Image-writing utilities for pdfwrite driver */
+#include "memory_.h"
+#include "gx.h"
+#include "gserrors.h"
+#include "gdevpdfx.h"
+#include "gdevpdfg.h"
+#include "gdevpdfo.h"
+#include "gxcspace.h"
+#include "gsiparm4.h"
+#include "gdevpsds.h"
+#include "spngpx.h"
+#include <stdlib.h> /* for atoi */
+
+#define CHECK(expr)\
+ BEGIN if ((code = (expr)) < 0) return code; END
+
+/* GC descriptors */
+public_st_pdf_image_writer();
+static ENUM_PTRS_WITH(pdf_image_writer_enum_ptrs, pdf_image_writer *piw)
+ index -= 4;
+ if (index < psdf_binary_writer_max_ptrs * piw->alt_writer_count) {
+ gs_ptr_type_t ret =
+ ENUM_USING(st_psdf_binary_writer, &piw->binary[index / psdf_binary_writer_max_ptrs],
+ sizeof(psdf_binary_writer), index % psdf_binary_writer_max_ptrs);
+
+ if (ret == 0) /* don't stop early */
+ ENUM_RETURN(0);
+ return ret;
+ }
+ return 0;
+case 0: ENUM_RETURN(piw->pres);
+case 1: ENUM_RETURN(piw->data);
+case 2: ENUM_RETURN(piw->named);
+case 3: ENUM_RETURN(piw->pres_mask);
+ENUM_PTRS_END
+static RELOC_PTRS_WITH(pdf_image_writer_reloc_ptrs, pdf_image_writer *piw)
+{
+ int i;
+
+ for (i = 0; i < piw->alt_writer_count; ++i)
+ RELOC_USING(st_psdf_binary_writer, &piw->binary[i],
+ sizeof(psdf_binary_writer));
+ RELOC_VAR(piw->pres);
+ RELOC_VAR(piw->data);
+ RELOC_VAR(piw->named);
+ RELOC_VAR(piw->pres_mask);
+}
+RELOC_PTRS_END
+
+/* ---------------- Image stream dictionaries ---------------- */
+
+const pdf_image_names_t pdf_image_names_full = {
+ { PDF_COLOR_SPACE_NAMES },
+ { PDF_FILTER_NAMES },
+ PDF_IMAGE_PARAM_NAMES
+};
+const pdf_image_names_t pdf_image_names_short = {
+ { PDF_COLOR_SPACE_NAMES_SHORT },
+ { PDF_FILTER_NAMES_SHORT },
+ PDF_IMAGE_PARAM_NAMES_SHORT
+};
+
+/* Store the values of image parameters other than filters. */
+/* pdev is used only for updating procsets. */
+/* pcsvalue is not used for masks. */
+static int
+pdf_put_pixel_image_values(cos_dict_t *pcd, gx_device_pdf *pdev,
+ const gs_pixel_image_t *pim,
+ const gs_color_space *pcs,
+ const pdf_image_names_t *pin,
+ const cos_value_t *pcsvalue)
+{
+ int num_components;
+ float indexed_decode[2];
+ const float *default_decode = NULL;
+ int code;
+
+ if (pcs) {
+ CHECK(cos_dict_put_c_key(pcd, pin->ColorSpace, pcsvalue));
+ pdf_color_space_procsets(pdev, pcs);
+ num_components = gs_color_space_num_components(pcs);
+ if (gs_color_space_get_index(pcs) == gs_color_space_index_Indexed) {
+ indexed_decode[0] = 0;
+ indexed_decode[1] = (float)((1 << pim->BitsPerComponent) - 1);
+ default_decode = indexed_decode;
+ }
+ } else
+ num_components = 1;
+ CHECK(cos_dict_put_c_key_int(pcd, pin->Width, pim->Width));
+ CHECK(cos_dict_put_c_key_int(pcd, pin->Height, pim->Height));
+ CHECK(cos_dict_put_c_key_int(pcd, pin->BitsPerComponent,
+ pim->BitsPerComponent));
+ {
+ int i;
+
+ for (i = 0; i < num_components * 2; ++i) {
+ if (pim->Decode[i] !=
+ (default_decode ? default_decode[i] : i & 1)
+ )
+ break;
+ }
+ if (i < num_components * 2) {
+ cos_array_t *pca =
+ cos_array_alloc(pdev, "pdf_put_pixel_image_values(decode)");
+
+ if (pca == 0)
+ return_error(gs_error_VMerror);
+ if (pcs == NULL) {
+ /* 269-01.ps sets /Decode[0 100] with a mask image. */
+ for (i = 0; i < num_components * 2; ++i)
+ CHECK(cos_array_add_real(pca, min(pim->Decode[i], 1)));
+ } else {
+ for (i = 0; i < num_components * 2; ++i)
+ CHECK(cos_array_add_real(pca, pim->Decode[i]));
+ }
+ CHECK(cos_dict_put_c_key_object(pcd, pin->Decode,
+ COS_OBJECT(pca)));
+ }
+ }
+ if (pim->Interpolate) {
+ if (pdev->PDFA != 0)
+ emprintf(pdev->memory,
+ "PDFA doesn't allow images with Interpolate true.\n");
+ else
+ CHECK(cos_dict_put_c_strings(pcd, pin->Interpolate, "true"));
+ }
+ return 0;
+}
+int
+pdf_put_image_values(cos_dict_t *pcd, gx_device_pdf *pdev,
+ const gs_pixel_image_t *pic,
+ const pdf_image_names_t *pin,
+ const cos_value_t *pcsvalue)
+{
+ const gs_color_space *pcs = pic->ColorSpace;
+ int code;
+
+ switch (pic->type->index) {
+ case 1: {
+ const gs_image1_t *pim = (const gs_image1_t *)pic;
+
+ if (pim->ImageMask) {
+ CHECK(cos_dict_put_c_strings(pcd, pin->ImageMask, "true"));
+ pdev->procsets |= ImageB;
+ pcs = NULL;
+ }
+ }
+ break;
+ case 3: {
+ /*
+ * Clients must treat this as a special case: they must call
+ * pdf_put_image_values for the MaskDict separately, and must
+ * add the Mask entry to the main image stream (dictionary).
+ */
+ /*const gs_image3_t *pim = (const gs_image3_t *)pic;*/
+
+ /* Masked images are only supported starting in PDF 1.3. */
+ if (pdev->CompatibilityLevel < 1.3)
+ return_error(gs_error_rangecheck);
+ }
+ break;
+ case 4: {
+ const gs_image4_t *pim = (const gs_image4_t *)pic;
+ int num_components = gs_color_space_num_components(pcs);
+ cos_array_t *pca;
+ int i;
+
+ /* Masked images are only supported starting in PDF 1.3. */
+ if (pdev->CompatibilityLevel < 1.3)
+ break; /* Will convert into an imagemask with a pattern color. */
+ pca = cos_array_alloc(pdev, "pdf_put_image_values(mask)");
+ if (pca == 0)
+ return_error(gs_error_VMerror);
+ for (i = 0; i < num_components; ++i) {
+ int lo, hi;
+
+ if (pim->MaskColor_is_range)
+ lo = pim->MaskColor[i * 2], hi = pim->MaskColor[i * 2 + 1];
+ else
+ lo = hi = pim->MaskColor[i];
+ CHECK(cos_array_add_int(pca, lo));
+ CHECK(cos_array_add_int(pca, hi));
+ }
+ CHECK(cos_dict_put_c_key_object(pcd, "/Mask", COS_OBJECT(pca)));
+ }
+ break;
+ default:
+ return_error(gs_error_rangecheck);
+ }
+ return pdf_put_pixel_image_values(pcd, pdev, pic, pcs, pin, pcsvalue);
+}
+
+/* Store filters for an image. */
+/* Currently this only saves parameters for CCITTFaxDecode. */
+int
+pdf_put_image_filters(cos_dict_t *pcd, gx_device_pdf *pdev,
+ const psdf_binary_writer * pbw,
+ const pdf_image_names_t *pin)
+{
+ return pdf_put_filters(pcd, pdev, pbw->strm, &pin->filter_names);
+}
+
+/* ---------------- Image writing ---------------- */
+
+/*
+ * Fill in the image parameters for a device space bitmap.
+ * PDF images are always specified top-to-bottom.
+ * data_h is the actual number of data rows, which may be less than h.
+ */
+void
+pdf_make_bitmap_matrix(gs_matrix * pmat, int x, int y, int w, int h,
+ int h_actual)
+{
+ pmat->xx = (float)w;
+ pmat->xy = 0;
+ pmat->yx = 0;
+ pmat->yy = (float)(-h_actual);
+ pmat->tx = (float)x;
+ pmat->ty = (float)(y + h);
+}
+
+/*
+ * Put out the gsave and matrix for an image. y_scale adjusts the matrix
+ * for images that end prematurely.
+ */
+void
+pdf_put_image_matrix(gx_device_pdf * pdev, const gs_matrix * pmat,
+ double y_scale)
+{
+ gs_matrix imat = {1, 0, 0, 1, 0 ,0};
+
+ gs_matrix_translate(pmat, 0.0, 1.0 - y_scale, &imat);
+ gs_matrix_scale(&imat, 1.0, y_scale, &imat);
+ pdf_put_matrix(pdev, "q ", &imat, "cm\n");
+}
+
+/* Put out a reference to an image resource. */
+int
+pdf_do_image_by_id(gx_device_pdf * pdev, double scale,
+ const gs_matrix * pimat, bool in_contents, gs_id id)
+{
+ /* fixme : in_contents is always true (there are no calls with false). */
+ if (in_contents) {
+ int code = pdf_open_contents(pdev, PDF_IN_STREAM);
+
+ if (code < 0)
+ return code;
+ }
+ if (pimat)
+ pdf_put_image_matrix(pdev, pimat, scale);
+ pprintld1(pdev->strm, "/R%ld Do\nQ\n", id);
+ return 0;
+}
+int
+pdf_do_image(gx_device_pdf * pdev, const pdf_resource_t * pres,
+ const gs_matrix * pimat, bool in_contents)
+{
+ /* fixme : call pdf_do_image_by_id when pimam == NULL. */
+ double scale = 1;
+
+ if (pimat) {
+ /* Adjust the matrix to account for short images. */
+ const pdf_x_object_t *const pxo = (const pdf_x_object_t *)pres;
+ scale = (double)pxo->data_height / pxo->height;
+ }
+ return pdf_do_image_by_id(pdev, scale, pimat, in_contents, pdf_resource_id(pres));
+}
+
+/* ------ Begin / finish ------ */
+
+/* Initialize image writer. */
+void
+pdf_image_writer_init(pdf_image_writer * piw)
+{
+ memset(piw, 0, sizeof(*piw));
+ piw->alt_writer_count = 1; /* Default. */
+}
+
+/*
+ * Begin writing an image, creating the resource if not in-line, and setting
+ * up the binary writer. If pnamed != 0, it is a stream object created by a
+ * NI pdfmark.
+ */
+int
+pdf_begin_write_image(gx_device_pdf * pdev, pdf_image_writer * piw,
+ gx_bitmap_id id, int w, int h, cos_dict_t *named,
+ bool in_line)
+{
+ /* Patch pdev->strm so the right stream gets into the writer. */
+ stream *save_strm = pdev->strm;
+ cos_stream_t *data;
+ bool mask = (piw->data != NULL);
+ int alt_stream_index = (!mask ? 0 : piw->alt_writer_count);
+ int code;
+
+ if (in_line) {
+ piw->pres = 0;
+ piw->pin = &pdf_image_names_short;
+ data = cos_stream_alloc(pdev, "pdf_begin_image_data");
+ if (data == 0)
+ return_error(gs_error_VMerror);
+ piw->end_string = " Q";
+ piw->named = 0; /* must have named == 0 */
+ } else {
+ pdf_x_object_t *pxo;
+ cos_stream_t *pcos;
+ pdf_resource_t *pres;
+
+ /*
+ * Note that if named != 0, there are two objects with the same id
+ * while the image is being accumulated: named, and pres->object.
+ */
+ code = pdf_alloc_resource(pdev, resourceXObject, id, &pres,
+ (named ? named->id : -1L));
+ if (code < 0)
+ return code;
+ *(mask ? &piw->pres_mask : &piw->pres) = pres;
+ cos_become(pres->object, cos_type_stream);
+ pres->rid = id;
+ piw->pin = &pdf_image_names_full;
+ pxo = (pdf_x_object_t *)pres;
+ pcos = (cos_stream_t *)pxo->object;
+ CHECK(cos_dict_put_c_strings(cos_stream_dict(pcos), "/Subtype",
+ "/Image"));
+ pxo->width = w;
+ pxo->height = h;
+ /* Initialize data_height for the benefit of copy_{mono,color}. */
+ pxo->data_height = h;
+ data = pcos;
+ if (!mask)
+ piw->named = named;
+ }
+ pdev->strm = pdev->streams.strm;
+ pdev->strm = cos_write_stream_alloc(data, pdev, "pdf_begin_write_image");
+ if (pdev->strm == 0)
+ return_error(gs_error_VMerror);
+ if (!mask)
+ piw->data = data;
+ piw->height = h;
+ code = psdf_begin_binary((gx_device_psdf *) pdev, &piw->binary[alt_stream_index]);
+ piw->binary[alt_stream_index].target = NULL; /* We don't need target with cos_write_stream. */
+ pdev->strm = save_strm;
+ return code;
+}
+
+/*
+ * Make alternative stream for image compression choice.
+ */
+int
+pdf_make_alt_stream(gx_device_pdf * pdev, psdf_binary_writer * pbw)
+{
+ stream *save_strm = pdev->strm;
+ cos_stream_t *pcos = cos_stream_alloc(pdev, "pdf_make_alt_stream");
+ int code;
+
+ if (pcos == 0)
+ return_error(gs_error_VMerror);
+ pcos->id = 0;
+ CHECK(cos_dict_put_c_strings(cos_stream_dict(pcos), "/Subtype", "/Image"));
+ pbw->strm = cos_write_stream_alloc(pcos, pdev, "pdf_make_alt_stream");
+ if (pbw->strm == 0)
+ return_error(gs_error_VMerror);
+ pbw->dev = (gx_device_psdf *)pdev;
+ pbw->memory = pdev->pdf_memory;
+ pdev->strm = pbw->strm;
+ code = psdf_begin_binary((gx_device_psdf *) pdev, pbw);
+ pdev->strm = save_strm;
+ pbw->target = NULL; /* We don't need target with cos_write_stream. */
+ return code;
+}
+
+/* Begin writing the image data, setting up the dictionary and filters. */
+int
+pdf_begin_image_data(gx_device_pdf * pdev, pdf_image_writer * piw,
+ const gs_pixel_image_t * pim, const cos_value_t *pcsvalue,
+ int alt_writer_index)
+{
+
+ cos_stream_t *s = cos_stream_from_pipeline(piw->binary[alt_writer_index].strm);
+ cos_dict_t *pcd = cos_stream_dict(s);
+ int code = pdf_put_image_values(pcd, pdev, pim, piw->pin, pcsvalue);
+
+ if (code >= 0)
+ code = pdf_put_image_filters(pcd, pdev, &piw->binary[alt_writer_index], piw->pin);
+ if (code < 0) {
+ if (!piw->pres)
+ COS_FREE(piw->data, "pdf_begin_image_data");
+ piw->data = 0;
+ }
+ return code;
+}
+
+/* Complete image data. */
+int
+pdf_complete_image_data(gx_device_pdf *pdev, pdf_image_writer *piw, int data_h,
+ int width, int bits_per_pixel)
+{
+ if (data_h != piw->height) {
+ if (piw->binary[0].strm->procs.process == s_DCTE_template.process ||
+ piw->binary[0].strm->procs.process == s_PNGPE_template.process ) {
+ /* Since DCTE and PNGPE can't safely close with incomplete data,
+ we add stub data to complete the stream.
+ */
+ int bytes_per_line = (width * bits_per_pixel + 7) / 8;
+ int lines_left = piw->height - data_h;
+ byte buf[256];
+ const uint lb = sizeof(buf);
+ int i, l;
+ uint ignore;
+
+ memset(buf, 128, lb);
+ for (; lines_left; lines_left--)
+ for (i = 0; i < piw->alt_writer_count; i++) {
+ for (l = bytes_per_line; l > 0; l -= lb)
+ if ((sputs(piw->binary[i].strm, buf, min(l, lb),
+ &ignore)) < 0)
+ return_error(gs_error_ioerror);
+ }
+ }
+ }
+ return 0;
+}
+
+/* Finish writing the binary image data. */
+int
+pdf_end_image_binary(gx_device_pdf *pdev, pdf_image_writer *piw, int data_h)
+{
+ int code, code1 = 0;
+
+ if (piw->alt_writer_count > 2)
+ code = pdf_choose_compression(piw, true);
+ else
+ code = psdf_end_binary(&piw->binary[0]);
+ /* If the image ended prematurely, update the Height. */
+ if (data_h != piw->height) {
+ char data[256];
+ int OutHeight;
+ cos_value_t *value;
+ value = (cos_value_t *)cos_dict_find(cos_stream_dict(piw->data),
+ (const byte *)piw->pin->Height, strlen(piw->pin->Height));
+ if (!value || value->contents.chars.size > 255)
+ return(gs_error_rangecheck);
+ strncpy((char *)&data, (const char *)value->contents.chars.data, value->contents.chars.size);
+ data[value->contents.chars.size] = 0x00;
+ OutHeight = atoi(data);
+ if (OutHeight != piw->height) {
+ /* Looks like we are downsampling, so we can't use the number
+ * of rows of data actually received, we must divide those by
+ * the sampling factor.
+ */
+ float factor = (float)OutHeight / piw->height;
+ OutHeight = (int)(factor * data_h);
+ code1 = cos_dict_put_c_key_int(cos_stream_dict(piw->data),
+ piw->pin->Height, OutHeight);
+ } else {
+
+ code1 = cos_dict_put_c_key_int(cos_stream_dict(piw->data),
+ piw->pin->Height, data_h);
+ }
+ }
+ return code < 0 ? code : code1;
+}
+
+/*
+ * Finish writing an image. If in-line, write the BI/dict/ID/data/EI and
+ * return 1; if a resource, write the resource definition and return 0.
+ */
+int
+pdf_end_write_image(gx_device_pdf * pdev, pdf_image_writer * piw)
+{
+ pdf_resource_t *pres = piw->pres;
+
+ if (pres) { /* image resource */
+ cos_object_t *const pco = pres->object;
+ cos_stream_t *const pcs = (cos_stream_t *)pco;
+ cos_dict_t *named = piw->named;
+ int code;
+
+ if (named) {
+ if (pdev->ForOPDFRead) {
+ code = cos_dict_put_c_key_bool(named, "/.Global", true);
+ if (code < 0)
+ return code;
+ }
+ /*
+ * This image was named by NI. Copy any dictionary elements
+ * from the named dictionary to the image stream, and then
+ * associate the name with the stream.
+ */
+ code = cos_dict_move_all(cos_stream_dict(pcs), named);
+ if (code < 0)
+ return code;
+ pres->named = true;
+ /*
+ * We need to make the entry in the name dictionary point to
+ * the stream (pcs) rather than the object created by NI (named).
+ * Unfortunately, we no longer know what dictionary to use.
+ * Instead, overwrite the latter with the former's contents,
+ * and change the only relevant pointer.
+ */
+ *(cos_object_t *)named = *pco;
+ pres->object = COS_OBJECT(named);
+ } else if (!pres->named) { /* named objects are written at the end */
+ if (pdev->DetectDuplicateImages) {
+ pdf_x_object_t *pxo = (pdf_x_object_t *)piw->pres;
+ int height = pxo->height, width = pxo->width;
+
+ code = pdf_substitute_resource(pdev, &piw->pres, resourceXObject, NULL, false);
+ if (code < 0)
+ return code;
+
+ /* These values are related to the image matrix and should *not* be
+ * substituted if we found a duplicate image, or the matrix calculation
+ * will be incorrect! This only seems to matter for the PCL interpreter.
+ */
+ pxo = (pdf_x_object_t *)piw->pres;
+ pxo->height = height;
+ pxo->width = width;
+ } else {
+ pdf_reserve_object_id(pdev, piw->pres, gs_no_id);
+ }
+ /* Warning : If the substituted image used alternate streams,
+ its space in the pdev->streams.strm file won't be released. */
+ piw->pres->where_used |= pdev->used_mask;
+ }
+ code = pdf_add_resource(pdev, pdev->substream_Resources, "/XObject", piw->pres);
+ if (code < 0)
+ return code;
+ return 0;
+ } else { /* in-line image */
+ stream *s = pdev->strm;
+ uint KeyLength = pdev->KeyLength;
+
+ stream_puts(s, "BI\n");
+ cos_stream_elements_write(piw->data, pdev);
+ stream_puts(s, (pdev->binary_ok ? "ID " : "ID\n"));
+ pdev->KeyLength = 0; /* Disable encryption for the inline image. */
+ cos_stream_contents_write(piw->data, pdev);
+ pdev->KeyLength = KeyLength;
+ pprints1(s, "\nEI%s\n", piw->end_string);
+ COS_FREE(piw->data, "pdf_end_write_image");
+ return 1;
+ }
+}
+
+/* ------ Copy data ------ */
+
+/* Copy the data for a mask or monobit bitmap. */
+int
+pdf_copy_mask_bits(stream *s, const byte *base, int sourcex, int raster,
+ int w, int h, byte invert)
+{
+ int yi;
+
+ for (yi = 0; yi < h; ++yi) {
+ const byte *data = base + yi * raster + (sourcex >> 3);
+ int sbit = sourcex & 7;
+
+ if (sbit == 0) {
+ int nbytes = (w + 7) >> 3;
+ int i;
+
+ for (i = 0; i < nbytes; ++data, ++i)
+ sputc(s, (byte)(*data ^ invert));
+ } else {
+ int wleft = w;
+ int rbit = 8 - sbit;
+
+ for (; wleft + sbit > 8; ++data, wleft -= 8)
+ sputc(s, (byte)(((*data << sbit) + (data[1] >> rbit)) ^ invert));
+ if (wleft > 0)
+ sputc(s, (byte)(((*data << sbit) ^ invert) &
+ (byte) (0xff00 >> wleft)));
+ }
+ }
+ return 0;
+}
+
+/* Copy the data for a colored image (device pixels). */
+int
+pdf_copy_color_bits(stream *s, const byte *base, int sourcex, int raster,
+ int w, int h, int bytes_per_pixel)
+{
+ int yi;
+
+ for (yi = 0; yi < h; ++yi) {
+ uint ignore;
+
+ sputs(s, base + sourcex * bytes_per_pixel + yi * raster,
+ w * bytes_per_pixel, &ignore);
+ }
+ return 0;
+}
+
+/* Choose image compression - auxiliary procs */
+static inline bool much_bigger__DL(long l1, long l2)
+{
+ return l1 > 1024*1024 && l2 < l1 / 3;
+}
+static void
+pdf_choose_compression_cos(pdf_image_writer *piw, cos_stream_t *s[2], bool force)
+{ /* Assume s[0] is Flate, s[1] is DCT, s[2] is chooser. */
+ long l0, l1;
+ int k0, k1;
+
+ l0 = cos_stream_length(s[0]);
+ l1 = cos_stream_length(s[1]);
+
+ if ((force && l0 <= l1) || l1 == -1)
+ k0 = 1; /* Use Flate if it is not longer. Or if the DCT failed */
+ else {
+ k0 = s_compr_chooser__get_choice(
+ (stream_compr_chooser_state *)piw->binary[2].strm->state, force);
+ if (k0 && l0 > 0 && l1 > 0)
+ k0--;
+ else if (much_bigger__DL(l0, l1))
+ k0 = 0;
+ else if (much_bigger__DL(l1, l0) || force)
+ k0 = 1;
+ else
+ return;
+ }
+ k1 = 1 - k0;
+ s_close_filters(&piw->binary[k0].strm, piw->binary[k0].target);
+ s[k0]->cos_procs->release((cos_object_t *)s[k0], "pdf_image_choose_filter");
+ s[k0]->written = 1;
+ piw->binary[0].strm = piw->binary[k1].strm;
+ s_close_filters(&piw->binary[2].strm, piw->binary[2].target);
+ piw->binary[1].strm = piw->binary[2].strm = 0; /* for GC */
+ piw->binary[1].target = piw->binary[2].target = 0;
+ s[k1]->id = piw->pres->object->id;
+ piw->pres->object = (cos_object_t *)s[k1];
+ piw->data = s[k1];
+ if (piw->alt_writer_count > 3) {
+ piw->binary[1] = piw->binary[3];
+ piw->binary[3].strm = 0; /* for GC */
+ piw->binary[3].target = 0;
+ }
+ piw->alt_writer_count -= 2;
+}
+
+/* End binary with choosing image compression. */
+int
+pdf_choose_compression(pdf_image_writer * piw, bool end_binary)
+{
+ cos_stream_t *s[2];
+ s[0] = cos_stream_from_pipeline(piw->binary[0].strm);
+ s[1] = cos_stream_from_pipeline(piw->binary[1].strm);
+ if (end_binary) {
+ int status;
+
+ status = s_close_filters(&piw->binary[0].strm, piw->binary[0].target);
+ if (status < 0)
+ return_error(gs_error_ioerror);
+ status = s_close_filters(&piw->binary[1].strm, piw->binary[1].target);
+ if (status < 0)
+ s[1]->length = -1;
+ }
+ pdf_choose_compression_cos(piw, s, end_binary);
+ return 0;
+}
diff --git a/devices/vector/gdevpdfk.c b/devices/vector/gdevpdfk.c
new file mode 100644
index 000000000..1810c2714
--- /dev/null
+++ b/devices/vector/gdevpdfk.c
@@ -0,0 +1,883 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Lab and ICCBased color space writing */
+#include "math_.h"
+#include "memory_.h"
+#include "gx.h"
+#include "gxcspace.h"
+#include "stream.h"
+#include "gsicc.h"
+#include "gserrors.h"
+#include "gxcie.h"
+#include "gdevpdfx.h"
+#include "gdevpdfg.h"
+#include "gdevpdfc.h"
+#include "gdevpdfo.h"
+#include "strimpl.h"
+#include "gsicc_create.h"
+#include "gsicc_manage.h"
+
+/* ------ CIE space synthesis ------ */
+
+/* Add a /Range entry to a CIE-based color space dictionary. */
+static int
+pdf_cie_add_ranges(gx_device_pdf *pdev, cos_dict_t *pcd, const gs_range *prange, int n, bool clamp)
+{
+ cos_array_t *pca = cos_array_alloc(pdev, "pdf_cie_add_ranges");
+ int code = 0, i;
+
+ if (pca == 0)
+ return_error(gs_error_VMerror);
+ for (i = 0; i < n; ++i) {
+ double rmin = prange[i].rmin, rmax = prange[i].rmax;
+
+ if (clamp) {
+ if (rmin < 0) rmin = 0;
+ if (rmax > 1) rmax = 1;
+ }
+ if ((code = cos_array_add_real(pca, rmin)) < 0 ||
+ (code = cos_array_add_real(pca, rmax)) < 0
+ )
+ break;
+ }
+ if (code >= 0)
+ code = cos_dict_put_c_key_object(pcd, "/Range", COS_OBJECT(pca));
+ if (code < 0)
+ COS_FREE(pca, "pdf_cie_add_ranges");
+ return code;
+}
+
+/* Transform a CIEBased color to XYZ. */
+static int
+cie_to_xyz(const double *in, double out[3], const gs_color_space *pcs,
+ const gs_imager_state *pis, const gs_cie_common *pciec)
+{
+ gs_client_color cc;
+ frac xyz[3];
+ int ncomp = gs_color_space_num_components(pcs);
+ int i;
+ gx_device dev;
+ cmm_dev_profile_t dev_profile;
+
+ gs_color_space_index cs_index;
+ const gs_vector3 *const pWhitePoint = &pciec->points.WhitePoint;
+ float xyz_float[3];
+
+ cs_index = gs_color_space_get_index(pcs);
+ /* Need a device profile */
+ dev_profile.device_profile[0] = pcs->cmm_icc_profile_data;
+ dev_profile.device_profile[1] = NULL;
+ dev_profile.device_profile[2] = NULL;
+ dev_profile.device_profile[3] = NULL;
+ dev_profile.link_profile = NULL;
+ dev_profile.proof_profile = NULL;
+ dev_profile.spotnames = NULL;
+ dev_profile.oi_profile = NULL;
+ dev.icc_struct = &(dev_profile);
+
+ for (i = 0; i < ncomp; ++i)
+ cc.paint.values[i] = in[i];
+
+ /* The standard concretization makes use of the equivalent ICC profile
+ to ensure that all color management is handled by the CMM.
+ Unfortunately, we can't do that here since we have no access to the
+ icc manager. Also the PDF write outputs have restrictions on the
+ ICC profiles that can be embedded so we must use this older form.
+ Need to add an ICC version number into the icc creator to enable
+ creation to and from various versions */
+
+ switch (cs_index) {
+ case gs_color_space_index_CIEA:
+ gx_psconcretize_CIEA(&cc, pcs, xyz, xyz_float, pis);
+ break;
+ case gs_color_space_index_CIEABC:
+ gx_psconcretize_CIEABC(&cc, pcs, xyz, xyz_float, pis);
+ break;
+ case gs_color_space_index_CIEDEF:
+ gx_psconcretize_CIEDEF(&cc, pcs, xyz, xyz_float, pis);
+ break;
+ case gs_color_space_index_CIEDEFG:
+ gx_psconcretize_CIEDEFG(&cc, pcs, xyz, xyz_float, pis);
+ break;
+ default:
+ /* Only to silence a Coverity uninitialised variable warning */
+ memset(&xyz_float, 0x00, sizeof(xyz_float));
+ break;
+ }
+ if (cs_index == gs_color_space_index_CIEA) {
+ /* AR forces this case to always be achromatic. We will
+ do the same even though it does not match the PS
+ specification */
+ /* Use the resulting Y value to scale the wp Illumination.
+ note that we scale to the whitepoint here. Matrix out
+ handles mapping to CIE D50. This forces an achromatic result */
+ xyz_float[0] = pWhitePoint->u * xyz_float[1];
+ xyz_float[2] = pWhitePoint->w * xyz_float[1];
+ }
+
+ /* Do wp mapping to D50 in XYZ for now. We should do bradford correction.
+ Will add that in next release */
+ out[0] = xyz_float[0]*0.9642/pWhitePoint->u;
+ out[1] = xyz_float[1];
+ out[2] = xyz_float[2]*0.8249/pWhitePoint->w;
+ return 0;
+}
+
+/* ------ Lab space writing and synthesis ------ */
+
+/* Transform XYZ values to Lab. */
+static double
+lab_g_inverse(double v)
+{
+ if (v >= (6.0 * 6.0 * 6.0) / (29 * 29 * 29))
+ return pow(v, 1.0 / 3); /* use cbrt if available? */
+ else
+ return (v * (841.0 / 108) + 4.0 / 29);
+}
+static void
+xyz_to_lab(const double xyz[3], double lab[3], const gs_cie_common *pciec)
+{
+ const gs_vector3 *const pWhitePoint = &pciec->points.WhitePoint;
+ double L, lunit;
+
+ /* Calculate L* first. */
+ L = lab_g_inverse(xyz[1] / pWhitePoint->v) * 116 - 16;
+ /* Clamp L* to the PDF range [0..100]. */
+ if (L < 0)
+ L = 0;
+ else if (L > 100)
+ L = 100;
+ lab[1] = L;
+ lunit = (L + 16) / 116;
+
+ /* Calculate a* and b*. */
+ lab[0] = (lab_g_inverse(xyz[0] / pWhitePoint->u) - lunit) * 500;
+ lab[2] = (lab_g_inverse(xyz[2] / pWhitePoint->w) - lunit) * -200;
+}
+
+/* Create a PDF Lab color space corresponding to a CIEBased color space. */
+static int
+lab_range(gs_range range_out[3] /* only [1] and [2] used */,
+ const gs_color_space *pcs, const gs_cie_common *pciec,
+ const gs_range *ranges, gs_memory_t *mem)
+{
+ /*
+ * Determine the range of a* and b* by evaluating the color space
+ * mapping at all of its extrema.
+ */
+ int ncomp = gs_color_space_num_components(pcs);
+ gs_imager_state *pis;
+ int code = gx_cie_to_xyz_alloc(&pis, pcs, mem);
+ int i, j;
+
+ if (code < 0)
+ return code;
+ for (j = 1; j < 3; ++j)
+ range_out[j].rmin = 1000.0, range_out[j].rmax = -1000.0;
+ for (i = 0; i < 1 << ncomp; ++i) {
+ double in[4], xyz[3];
+
+ for (j = 0; j < ncomp; ++j)
+ in[j] = (i & (1 << j) ? ranges[j].rmax : ranges[j].rmin);
+ if (cie_to_xyz(in, xyz, pcs, pis, pciec) >= 0) {
+ double lab[3];
+
+ xyz_to_lab(xyz, lab, pciec);
+ for (j = 1; j < 3; ++j) {
+ range_out[j].rmin = min(range_out[j].rmin, lab[j]);
+ range_out[j].rmax = max(range_out[j].rmax, lab[j]);
+ }
+ }
+ }
+ gx_cie_to_xyz_free(pis);
+ return 0;
+}
+/*
+ * Create a Lab color space object.
+ * This procedure is exported for Lab color spaces in gdevpdfc.c.
+ */
+int
+pdf_put_lab_color_space(gx_device_pdf *pdev, cos_array_t *pca, cos_dict_t *pcd,
+ const gs_range ranges[3] /* only [1] and [2] used */)
+{
+ int code;
+ cos_value_t v;
+
+ if ((code = cos_array_add(pca, cos_c_string_value(&v, "/Lab"))) >= 0)
+ code = pdf_cie_add_ranges(pdev, pcd, ranges + 1, 2, false);
+ return code;
+}
+
+/*
+ * Create a Lab color space for a CIEBased space that can't be represented
+ * directly as a Calxxx or Lab space.
+ */
+static int
+pdf_convert_cie_to_lab(gx_device_pdf *pdev, cos_array_t *pca,
+ const gs_color_space *pcs,
+ const gs_cie_common *pciec, const gs_range *prange)
+{
+ cos_dict_t *pcd;
+ gs_range ranges[3];
+ int code;
+
+ /****** NOT IMPLEMENTED YET, REQUIRES TRANSFORMING VALUES ******/
+ if (1) return_error(gs_error_rangecheck);
+ pcd = cos_dict_alloc(pdev, "pdf_convert_cie_to_lab(dict)");
+ if (pcd == 0)
+ return_error(gs_error_VMerror);
+ if ((code = lab_range(ranges, pcs, pciec, prange, pdev->pdf_memory)) < 0 ||
+ (code = pdf_put_lab_color_space(pdev, pca, pcd, ranges)) < 0 ||
+ (code = pdf_finish_cie_space(pdev, pca, pcd, pciec)) < 0
+ )
+ COS_FREE(pcd, "pdf_convert_cie_to_lab(dict)");
+ return code;
+}
+
+/* ------ ICCBased space writing and synthesis ------ */
+
+/*
+ * Create an ICCBased color space object (internal). The client must write
+ * the profile data on *ppcstrm.
+ */
+static int
+pdf_make_iccbased(gx_device_pdf *pdev, const gs_imager_state * pis,
+ cos_array_t *pca, int ncomps,
+ const gs_range *prange /*[4]*/,
+ const gs_color_space *pcs_alt,
+ cos_stream_t **ppcstrm,
+ const gs_range_t **pprange /* if scaling is needed */)
+
+{
+ cos_value_t v;
+ int code;
+ cos_stream_t * pcstrm = 0;
+ cos_array_t * prngca = 0;
+ bool std_ranges = true;
+ bool scale_inputs = false;
+ int i;
+
+ /* This code makes no sense to me, and if I remove it we get better
+ * results. So we'll chop it out for the new colour work.
+ */
+ if (pdev->UseOldColor) {
+ /* Check the ranges. */
+ if (pprange)
+ *pprange = 0;
+ for (i = 0; i < ncomps; ++i) {
+ double rmin = prange[i].rmin, rmax = prange[i].rmax;
+
+ if (rmin < 0.0 || rmax > 1.0) {
+ /* We'll have to scale the inputs. :-( */
+ if (pprange == 0)
+ return_error(gs_error_rangecheck); /* scaling not allowed */
+ *pprange = prange;
+ scale_inputs = true;
+ }
+ else if (rmin > 0.0 || rmax < 1.0)
+ std_ranges = false;
+ }
+ }
+
+ /* Range values are a bit tricky to check.
+ For example, CIELAB ICC profiles have
+ a unique range. I am not convinced
+ that a check is needed in the new
+ color architecture as I am carefull
+ to get them properly set during
+ creation of the ICC profile data. */
+
+ /* ICCBased color spaces are essentially copied to the output. */
+ if ((code = cos_array_add(pca, cos_c_string_value(&v, "/ICCBased"))) < 0)
+ return code;
+
+ /* Create a stream for the output. */
+ if ((pcstrm = cos_stream_alloc(pdev, "pdf_make_iccbased(stream)")) == 0) {
+ code = gs_note_error(gs_error_VMerror);
+ goto fail;
+ }
+
+ /* Indicate the number of components. */
+ code = cos_dict_put_c_key_int(cos_stream_dict(pcstrm), "/N", ncomps);
+ if (code < 0)
+ goto fail;
+
+ /* Indicate the range, if needed. */
+ if (!std_ranges && !scale_inputs) {
+ code = pdf_cie_add_ranges(pdev, cos_stream_dict(pcstrm), prange, ncomps, true);
+ if (code < 0)
+ goto fail;
+ }
+
+ /* In the new design there may not be a specified alternate color space */
+ if (pcs_alt != NULL){
+
+ /* Output the alternate color space, if necessary. */
+ switch (gs_color_space_get_index(pcs_alt)) {
+ case gs_color_space_index_DeviceGray:
+ case gs_color_space_index_DeviceRGB:
+ case gs_color_space_index_DeviceCMYK:
+ break; /* implicit (default) */
+ default:
+ if ((code = pdf_color_space_named(pdev, pis, &v, NULL, pcs_alt,
+ &pdf_color_space_names, false, NULL, 0, true)) < 0 ||
+ (code = cos_dict_put_c_key(cos_stream_dict(pcstrm), "/Alternate",
+ &v)) < 0
+ )
+ goto fail;
+ }
+
+ } else {
+ if (ncomps != 1 && ncomps != 3 && ncomps != 4) {
+ /* We can only use a default for Gray, RGB or CMYK. For anything else we need
+ * to convert to the base space, we can't legally preserve the ICC profile.
+ */
+ code = gs_error_rangecheck;
+ goto fail;
+ }
+ }
+
+ /* Wrap up. */
+ if ((code = cos_array_add_object(pca, COS_OBJECT(pcstrm))) < 0)
+ goto fail;
+ *ppcstrm = pcstrm;
+ return code;
+ fail:
+ if (prngca)
+ COS_FREE(prngca, "pdf_make_iccbased(Range)");
+ if (pcstrm)
+ COS_FREE(pcstrm, "pdf_make_iccbased(stream)");
+ return code;
+}
+/*
+ * Finish writing the data stream for an ICCBased color space object.
+ */
+static int
+pdf_finish_iccbased(gx_device_pdf *pdev, cos_stream_t *pcstrm)
+{
+ /*
+ * The stream must be an indirect object. Assign an ID, and write the
+ * object out now.
+ */
+ pcstrm->id = pdf_obj_ref(pdev);
+ return cos_write_object(COS_OBJECT(pcstrm), pdev, resourceICC);
+}
+
+/*
+ * Create an ICCBased color space for a CIEBased space that can't be
+ * represented directly as a Calxxx or Lab space.
+ */
+
+typedef struct profile_table_s profile_table_t;
+struct profile_table_s {
+ const char *tag;
+ const byte *data;
+ uint length;
+ uint data_length; /* may be < length if write != 0 */
+ int (*write)(gx_device_pdf *pdev, cos_stream_t *, const profile_table_t *, gs_memory_t *,
+ const gs_cie_common *pciec);
+ const void *write_data;
+ const gs_range_t *ranges;
+};
+static profile_table_t *
+add_table(profile_table_t **ppnt, const char *tag, const byte *data,
+ uint length)
+{
+ profile_table_t *pnt = (*ppnt)++;
+
+ pnt->tag = tag, pnt->data = data, pnt->length = length;
+ pnt->data_length = length;
+ pnt->write = NULL;
+ /* write_data not set */
+ pnt->ranges = NULL;
+ return pnt;
+}
+static void
+set_uint32(byte bytes[4], uint value)
+{
+ bytes[0] = (byte)(value >> 24);
+ bytes[1] = (byte)(value >> 16);
+ bytes[2] = (byte)(value >> 8);
+ bytes[3] = (byte)value;
+}
+static void
+set_XYZ(byte bytes[4], double value)
+{
+ set_uint32(bytes, (uint)(int)(value * 65536));
+}
+static void
+add_table_xyz3(profile_table_t **ppnt, const char *tag, byte bytes[20],
+ const gs_vector3 *pv)
+{
+ memcpy(bytes, "XYZ \000\000\000\000", 8);
+ set_XYZ(bytes + 8, pv->u);
+ set_XYZ(bytes + 12, pv->v);
+ set_XYZ(bytes + 16, pv->w);
+ DISCARD(add_table(ppnt, tag, bytes, 20));
+}
+static void
+set_sample16(byte *p, double v)
+{
+ int value = (int)(v * 65535);
+
+ if (value < 0)
+ value = 0;
+ else if (value > 65535)
+ value = 65535;
+ p[0] = (byte)(value >> 8);
+ p[1] = (byte)value;
+}
+/* Create and write a TRC curve table. */
+static int write_trc_abc(gx_device_pdf *pdev, cos_stream_t *, const profile_table_t *, gs_memory_t *, const gs_cie_common *);
+static int write_trc_lmn(gx_device_pdf *pdev, cos_stream_t *, const profile_table_t *, gs_memory_t *, const gs_cie_common *);
+static profile_table_t *
+add_trc(gx_device_pdf *pdev, profile_table_t **ppnt, const char *tag, byte bytes[12],
+ const gs_cie_common *pciec, cie_cache_one_step_t one_step)
+{
+ const int count = gx_cie_cache_size;
+ profile_table_t *pnt;
+
+ memcpy(bytes, "curv\000\000\000\000", 8);
+ set_uint32(bytes + 8, count);
+ pnt = add_table(ppnt, tag, bytes, 12);
+ pnt->length += count * 2;
+ pnt->write = (one_step == ONE_STEP_ABC ? write_trc_abc : write_trc_lmn);
+ pnt->write_data = (const gs_cie_abc *)pciec;
+ return pnt;
+}
+static int
+rgb_to_index(const profile_table_t *pnt)
+{
+ switch (pnt->tag[0]) {
+ case 'r': return 0;
+ case 'g': return 1;
+ case 'b': default: /* others can't happen */ return 2;
+ }
+}
+static double
+cache_arg(int i, int denom, const gs_range_t *range)
+{
+ double arg = i / (double)denom;
+
+ if (range) {
+ /* Sample over the range [range->rmin .. range->rmax]. */
+ arg = arg * (range->rmax - range->rmin) + range->rmin;
+ }
+ return arg;
+}
+
+static int
+write_trc_abc(gx_device_pdf *pdev, cos_stream_t *pcstrm, const profile_table_t *pnt,
+ gs_memory_t *ignore_mem, const gs_cie_common *unused)
+{
+ /* Write the curve table from DecodeABC. */
+ const gs_cie_abc *pabc = pnt->write_data;
+ int ci = rgb_to_index(pnt);
+ gs_cie_abc_proc proc = pabc->DecodeABC.procs[ci];
+ byte samples[gx_cie_cache_size * 2];
+ byte *p = samples;
+ int i;
+
+ for (i = 0; i < gx_cie_cache_size; ++i, p += 2)
+ set_sample16(p, proc(cache_arg(i, gx_cie_cache_size - 1, pnt->ranges),
+ pabc));
+ return cos_stream_add_bytes(pdev, pcstrm, samples, gx_cie_cache_size * 2);
+}
+static int
+write_trc_lmn(gx_device_pdf *pdev, cos_stream_t *pcstrm, const profile_table_t *pnt,
+ gs_memory_t *ignore_mem, const gs_cie_common *unused)
+{
+ const gs_cie_common *pciec = pnt->write_data;
+ int ci = rgb_to_index(pnt);
+ gs_cie_common_proc proc = pciec->DecodeLMN.procs[ci];
+ byte samples[gx_cie_cache_size * 2];
+ byte *p = samples;
+ int i;
+
+ /* Write the curve table from DecodeLMN. */
+ for (i = 0; i < gx_cie_cache_size; ++i, p += 2)
+ set_sample16(p, proc(cache_arg(i, gx_cie_cache_size - 1, pnt->ranges),
+ pciec));
+ return cos_stream_add_bytes(pdev, pcstrm, samples, gx_cie_cache_size * 2);
+}
+/* Create and write an a2b0 lookup table. */
+#define NUM_IN_ENTRIES 2 /* assume linear interpolation */
+#define NUM_OUT_ENTRIES 2 /* ibid. */
+#define MAX_CLUT_ENTRIES 2500 /* enough for 7^4 */
+typedef struct icc_a2b0_s {
+ byte header[52];
+ const gs_color_space *pcs;
+ int num_points; /* on each axis of LUT */
+ int count; /* total # of entries in LUT */
+} icc_a2b0_t;
+static int write_a2b0(gx_device_pdf *pdev, cos_stream_t *, const profile_table_t *, gs_memory_t *,
+ const gs_cie_common *pciec);
+static profile_table_t *
+add_a2b0(profile_table_t **ppnt, icc_a2b0_t *pa2b, int ncomps,
+ const gs_color_space *pcs)
+{
+ static const byte a2b0_data[sizeof(pa2b->header)] = {
+ 'm', 'f', 't', '2', /* type signature */
+ 0, 0, 0, 0, /* reserved, 0 */
+ 0, /* # of input channels **VARIABLE** */
+ 3, /* # of output channels */
+ 0, /* # of CLUT points **VARIABLE** */
+ 0, /* reserved, padding */
+ 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* matrix column 0 */
+ 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, /* matrix column 1 */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, /* matrix column 2 */
+ 0, NUM_IN_ENTRIES, /* # of input table entries */
+ 0, NUM_OUT_ENTRIES /* # of output table entries */
+ };
+ int num_points = (int)floor(pow(MAX_CLUT_ENTRIES, 1.0 / ncomps));
+ profile_table_t *pnt;
+
+ num_points = min(num_points, 255);
+ memcpy(pa2b->header, a2b0_data, sizeof(a2b0_data));
+ pa2b->header[8] = ncomps;
+ pa2b->header[10] = num_points;
+ pa2b->pcs = pcs;
+ pa2b->num_points = num_points;
+ pa2b->count = (int)pow(num_points, ncomps);
+ pnt = add_table(ppnt, "A2B0", pa2b->header,
+ sizeof(pa2b->header) +
+ ncomps * 2 * NUM_IN_ENTRIES + /* in */
+ pa2b->count * (3 * 2) + /* clut: XYZ, 16-bit values */
+ 3 * 2 * NUM_OUT_ENTRIES /* out */
+ );
+ pnt->data_length = sizeof(pa2b->header); /* only write fixed part */
+ pnt->write = write_a2b0;
+ pnt->write_data = pa2b;
+ return pnt;
+}
+static int
+write_a2b0(gx_device_pdf *pdev, cos_stream_t *pcstrm, const profile_table_t *pnt,
+ gs_memory_t *mem, const gs_cie_common *pciec)
+{
+ const icc_a2b0_t *pa2b = pnt->write_data;
+ const gs_color_space *pcs = pa2b->pcs;
+ int ncomps = pa2b->header[8];
+ int num_points = pa2b->num_points;
+ int i;
+#define MAX_NCOMPS 4 /* CIEBasedDEFG */
+ static const byte v01[MAX_NCOMPS * 2 * 2] = {
+ 0,0, 255,255, 0,0, 255,255, 0,0, 255,255, 0,0, 255,255
+ };
+ gs_imager_state *pis;
+ int code;
+
+ /* Write the input table. */
+
+ if ((code = cos_stream_add_bytes(pdev, pcstrm, v01, ncomps * 4)) < 0
+ )
+ return code;
+
+ /* Write the lookup table. */
+
+ code = gx_cie_to_xyz_alloc(&pis, pcs, mem);
+ if (code < 0)
+ return code;
+ for (i = 0; i < pa2b->count; ++i) {
+ double in[MAX_NCOMPS], xyz[3];
+ byte entry[3 * 2];
+ byte *p = entry;
+ int n, j;
+
+ for (n = i, j = ncomps - 1; j >= 0; --j, n /= num_points)
+ in[j] = cache_arg(n % num_points, num_points - 1,
+ (pnt->ranges ? pnt->ranges + j : NULL));
+ cie_to_xyz(in, xyz, pcs, pis, pciec);
+ /*
+ * NOTE: Due to an obscure provision of the ICC Profile
+ * specification, values in a2b0 lookup tables do *not* represent
+ * the range [0 .. 1], but rather the range [0
+ * .. MAX_ICC_XYZ_VALUE]. This caused us a lot of grief before we
+ * figured it out!
+ */
+#define MAX_ICC_XYZ_VALUE (1 + 32767.0/32768)
+ for (j = 0; j < 3; ++j, p += 2)
+ set_sample16(p, xyz[j] / MAX_ICC_XYZ_VALUE);
+#undef MAX_ICC_XYZ_VALUE
+ if ((code = cos_stream_add_bytes(pdev, pcstrm, entry, sizeof(entry))) < 0)
+ break;
+ }
+ gx_cie_to_xyz_free(pis);
+ if (code < 0)
+ return code;
+
+ /* Write the output table. */
+
+ return cos_stream_add_bytes(pdev, pcstrm, v01, 3 * 4);
+}
+
+/* XYZ wp mapping for now. Will replace later with Bradford or other */
+static void
+adjust_wp(const gs_vector3 *color_in, const gs_vector3 *wp_in,
+ gs_vector3 *color_out, const gs_vector3 *wp_out)
+{
+ color_out->u = color_in->u * wp_out->u / wp_in->u;
+ color_out->v = color_in->v * wp_out->v / wp_in->v;
+ color_out->w = color_in->w * wp_out->w / wp_in->w;
+}
+
+static int
+pdf_convert_cie_to_iccbased(gx_device_pdf *pdev, cos_array_t *pca,
+ const gs_color_space *pcs, const char *dcsname,
+ const gs_cie_common *pciec, const gs_range *prange,
+ cie_cache_one_step_t one_step,
+ const gs_matrix3 *pmat, const gs_range_t **pprange)
+{
+ /*
+ * We have two options for creating an ICCBased color space to represent
+ * a CIEBased space. For CIEBasedABC spaces using only a single
+ * Decode step followed by a single Matrix step, we can use [rgb]TRC
+ * and [rgb]XYZ; for CIEBasedA spaces using only DecodeA, we could use
+ * kTRC (but don't); otherwise, we must use a mft2 LUT.
+ */
+ int code;
+ int ncomps = gs_color_space_num_components(pcs);
+ gs_color_space *alt_space;
+ cos_stream_t *pcstrm;
+ gs_vector3 white_d50;
+ gs_vector3 temp_xyz;
+ /*
+ * because it requires random access to the output stream
+ * we construct the ICC profile by hand.
+ */
+ /* Header */
+ byte header[128];
+ static const byte header_data[] = {
+ 0, 0, 0, 0, /* profile size **VARIABLE** */
+ 0, 0, 0, 0, /* CMM type signature */
+ 0x02, 0x20, 0, 0, /* profile version number */
+ 's', 'c', 'n', 'r', /* profile class signature */
+ 0, 0, 0, 0, /* data color space **VARIABLE** */
+ 'X', 'Y', 'Z', ' ', /* connection color space */
+ 2002 / 256, 2002 % 256, 0, 1, 0, 1, /* date (1/1/2002) */
+ 0, 0, 0, 0, 0, 0, /* time */
+ 'a', 'c', 's', 'p', /* profile file signature */
+ 0, 0, 0, 0, /* primary platform signature */
+ 0, 0, 0, 3, /* profile flags (embedded use only) */
+ 0, 0, 0, 0, 0, 0, 0, 0, /* device manufacturer */
+ 0, 0, 0, 0, /* device model */
+ 0, 0, 0, 0, 0, 0, 0, 2 /* device attributes */
+ /* Remaining fields are zero or variable. */
+ /* [4] */ /* rendering intent */
+ /* 3 * [4] */ /* illuminant */
+ };
+ /* Description */
+#define DESC_LENGTH 5 /* "adhoc" */
+ byte desc[12 + DESC_LENGTH + 1 + 11 + 67];
+ static const byte desc_data[] = {
+ 'd', 'e', 's', 'c', /* type signature */
+ 0, 0, 0, 0, /* reserved, 0 */
+ 0, 0, 0, DESC_LENGTH + 1, /* ASCII description length */
+ 'a', 'd', 'h', 'o', 'c', 0, /* ASCII description */
+ /* Remaining fields are zero. */
+ };
+ /* White point */
+ byte wtpt[20];
+ /* Copyright (useless, but required by icclib) */
+ static const byte cprt_data[] = {
+ 't', 'e', 'x', 't', /* type signature */
+ 0, 0, 0, 0, /* reserved, 0 */
+ 'n', 'o', 'n', 'e', 0 /* must be null-terminated (!) */
+ };
+ /* Lookup table */
+ icc_a2b0_t a2b0;
+ /* [rgb]TRC */
+ byte rTRC[12], gTRC[12], bTRC[12];
+ /* [rgb]XYZ */
+ byte rXYZ[20], gXYZ[20], bXYZ[20];
+ /* Table structures */
+#define MAX_NUM_TABLES 9 /* desc, [rgb]TRC, [rgb]xYZ, wtpt, cprt */
+ profile_table_t tables[MAX_NUM_TABLES];
+ profile_table_t *next_table = tables;
+
+ /* White point must be D50 */
+ white_d50.u = 0.9642f;
+ white_d50.v = 1.0f;
+ white_d50.w = 0.8249f;
+
+ pdf_cspace_init_Device(pdev->memory, &alt_space, ncomps); /* can't fail */
+ code = pdf_make_iccbased(pdev, NULL, pca, ncomps, prange, alt_space,
+ &pcstrm, pprange);
+ rc_decrement_cs(alt_space, "pdf_convert_cie_to_iccbased");
+ if (code < 0)
+ return code;
+
+ /* Fill in most of the header, except for the total size. */
+
+ memset(header, 0, sizeof(header));
+ memcpy(header, header_data, sizeof(header_data));
+ memcpy(header + 16, dcsname, 4);
+
+ /* Construct the tables. */
+
+ /* desc */
+ memset(desc, 0, sizeof(desc));
+ memcpy(desc, desc_data, sizeof(desc_data));
+ DISCARD(add_table(&next_table, "desc", desc, sizeof(desc)));
+
+ /* wtpt. must be D50 */
+ add_table_xyz3(&next_table, "wtpt", wtpt, &white_d50);
+ memcpy(header + 68, wtpt + 8, 12); /* illuminant = white point */
+
+ /* cprt */
+ /* (We have no use for this tag, but icclib requires it.) */
+ DISCARD(add_table(&next_table, "cprt", cprt_data, sizeof(cprt_data)));
+
+ /* Use TRC + XYZ if possible, otherwise AToB. */
+ if ((one_step == ONE_STEP_ABC || one_step == ONE_STEP_LMN) && pmat != 0) {
+ /* Use TRC + XYZ. */
+ profile_table_t *tr =
+ add_trc(pdev, &next_table, "rTRC", rTRC, pciec, one_step);
+ profile_table_t *tg =
+ add_trc(pdev, &next_table, "gTRC", gTRC, pciec, one_step);
+ profile_table_t *tb =
+ add_trc(pdev, &next_table, "bTRC", bTRC, pciec, one_step);
+
+ if (*pprange) {
+ tr->ranges = *pprange;
+ tg->ranges = *pprange + 1;
+ tb->ranges = *pprange + 2;
+ }
+ /* These values need to be adjusted to D50. Again
+ use XYZ wp mapping for now. Later we will add in
+ the bradford stuff */
+ adjust_wp(&(pmat->cu), &(pciec->points.WhitePoint), &temp_xyz, &white_d50);
+ add_table_xyz3(&next_table, "rXYZ", rXYZ, &temp_xyz);
+ adjust_wp(&(pmat->cv), &(pciec->points.WhitePoint), &temp_xyz, &white_d50);
+ add_table_xyz3(&next_table, "gXYZ", gXYZ, &temp_xyz);
+ adjust_wp(&(pmat->cw), &(pciec->points.WhitePoint), &temp_xyz, &white_d50);
+ add_table_xyz3(&next_table, "bXYZ", bXYZ, &temp_xyz);
+ } else {
+ /* General case, use a lookup table. */
+ /* AToB (mft2) */
+ profile_table_t *pnt = add_a2b0(&next_table, &a2b0, ncomps, pcs);
+
+ pnt->ranges = *pprange;
+ }
+
+ /* Write the profile. */
+ {
+ byte bytes[4 + MAX_NUM_TABLES * 12];
+ int num_tables = next_table - tables;
+ int i;
+ byte *p;
+ uint table_size = 4 + num_tables * 12;
+ uint offset = sizeof(header) + table_size;
+
+ set_uint32(bytes, next_table - tables);
+ for (i = 0, p = bytes + 4; i < num_tables; ++i, p += 12) {
+ memcpy(p, tables[i].tag, 4);
+ set_uint32(p + 4, offset);
+ set_uint32(p + 8, tables[i].length);
+ offset += round_up(tables[i].length, 4);
+ }
+ set_uint32(header, offset);
+ if ((code = cos_stream_add_bytes(pdev, pcstrm, header, sizeof(header))) < 0 ||
+ (code = cos_stream_add_bytes(pdev, pcstrm, bytes, table_size)) < 0
+ )
+ return code;
+ for (i = 0; i < num_tables; ++i) {
+ uint len = tables[i].data_length;
+ static const byte pad[3] = {0, 0, 0};
+
+ if ((code = cos_stream_add_bytes(pdev, pcstrm, tables[i].data, len)) < 0 ||
+ (tables[i].write != 0 &&
+ (code = tables[i].write(pdev, pcstrm, &tables[i], pdev->pdf_memory, pciec)) < 0) ||
+ (code = cos_stream_add_bytes(pdev, pcstrm, pad,
+ -(int)(tables[i].length) & 3)) < 0
+ )
+ return code;
+ }
+ }
+
+ return pdf_finish_iccbased(pdev, pcstrm);
+}
+
+/* ------ Entry points (from gdevpdfc.c) ------ */
+
+/*
+ * Create an ICCBased color space. This is a single-use procedure,
+ * broken out only for readability.
+ */
+int
+pdf_iccbased_color_space(gx_device_pdf *pdev, const gs_imager_state * pis, cos_value_t *pvalue,
+ const gs_color_space *pcs, cos_array_t *pca)
+{
+ /*
+ * This would arise only in a pdf ==> pdf translation, but we
+ * should allow for it anyway.
+ */
+ cos_stream_t * pcstrm;
+ int code =
+ pdf_make_iccbased(pdev, pis, pca, pcs->cmm_icc_profile_data->num_comps,
+ pcs->cmm_icc_profile_data->Range.ranges,
+ pcs->base_space,
+ &pcstrm, NULL);
+
+ if (code < 0)
+ return code;
+
+ /* Transfer the buffer data */
+
+ /* PDF/A-1 only supports version 2 ICC profiles, and the PDF spec
+ * only supports Version 4 profiles in PDF 1.5 and above
+ */
+ if (pdev->CompatibilityLevel < 1.5 || pdev->PDFA == 1) {
+ byte *v2_buffer;
+ int size;
+
+ if (pis == NULL)
+ return (gs_error_undefined);
+ if (pcs->cmm_icc_profile_data->profile_handle == NULL)
+ gsicc_initialize_default_profile(pcs->cmm_icc_profile_data);
+ v2_buffer = gsicc_create_getv2buffer(pis, pcs->cmm_icc_profile_data, &size);
+ code = cos_stream_add_bytes(pdev, pcstrm, v2_buffer, size);
+ }else{
+ code = cos_stream_add_bytes(pdev, pcstrm, pcs->cmm_icc_profile_data->buffer,
+ pcs->cmm_icc_profile_data->buffer_size);
+ }
+
+ if (code >= 0)
+ code = pdf_finish_iccbased(pdev, pcstrm);
+ /*
+ * The stream has been added to the array: in case of failure, the
+ * caller will free the array, so there is no need to free the stream
+ * explicitly here.
+ */
+ return code;
+}
+
+/* Convert a CIEBased space to Lab or ICCBased. */
+int
+pdf_convert_cie_space(gx_device_pdf *pdev, cos_array_t *pca,
+ const gs_color_space *pcs, const char *dcsname,
+ const gs_cie_common *pciec, const gs_range *prange,
+ cie_cache_one_step_t one_step, const gs_matrix3 *pmat,
+ const gs_range_t **pprange)
+{
+ return (pdev->CompatibilityLevel < 1.3 ?
+ /* PDF 1.2 or earlier, use a Lab space. */
+ pdf_convert_cie_to_lab(pdev, pca, pcs, pciec, prange) :
+ /* PDF 1.3 or later, use an ICCBased space. */
+ pdf_convert_cie_to_iccbased(pdev, pca, pcs, dcsname, pciec, prange,
+ one_step, pmat, pprange)
+ );
+}
diff --git a/devices/vector/gdevpdfm.c b/devices/vector/gdevpdfm.c
new file mode 100644
index 000000000..ee3737c03
--- /dev/null
+++ b/devices/vector/gdevpdfm.c
@@ -0,0 +1,2656 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* pdfmark processing for PDF-writing driver */
+#include "math_.h"
+#include "memory_.h"
+#include "gx.h"
+#include "gserrors.h"
+#include "gsutil.h" /* for bytes_compare */
+#include "gdevpdfx.h"
+#include "gdevpdfo.h"
+#include "szlibx.h"
+#include "slzwx.h"
+
+/* GC descriptors */
+private_st_pdf_article();
+
+/*
+ * The pdfmark pseudo-parameter indicates the occurrence of a pdfmark
+ * operator in the input file. Its "value" is the arguments of the operator,
+ * passed through essentially unchanged:
+ * (key, value)*, CTM, type
+ */
+
+/*
+ * Define an entry in a table of pdfmark-processing procedures.
+ * (The actual table is at the end of this file, to avoid the need for
+ * forward declarations for the procedures.)
+ */
+#define PDFMARK_NAMEABLE 1 /* allows _objdef */
+#define PDFMARK_ODD_OK 2 /* OK if odd # of parameters */
+#define PDFMARK_KEEP_NAME 4 /* don't substitute reference for name */
+ /* in 1st argument */
+#define PDFMARK_NO_REFS 8 /* don't substitute references for names */
+ /* anywhere */
+#define PDFMARK_TRUECTM 16 /* pass the true CTM to the procedure, */
+ /* not the one transformed to reflect the default user space */
+typedef struct pdfmark_name_s {
+ const char *mname;
+ pdfmark_proc((*proc));
+ byte options;
+} pdfmark_name;
+
+/* ---------------- Public utilities ---------------- */
+
+/* Compare a C string and a gs_param_string. */
+bool
+pdf_key_eq(const gs_param_string * pcs, const char *str)
+{
+ return (strlen(str) == pcs->size && pcs->data &&
+ !strncmp(str, (const char *)pcs->data, pcs->size));
+}
+
+/* Scan an integer out of a parameter string. */
+int
+pdfmark_scan_int(const gs_param_string * pstr, int *pvalue)
+{
+#define MAX_INT_STR 20
+ uint size = pstr->size;
+ char str[MAX_INT_STR + 1];
+
+ if (size > MAX_INT_STR)
+ return_error(gs_error_limitcheck);
+ memcpy(str, pstr->data, size);
+ str[size] = 0;
+ return (sscanf(str, "%d", pvalue) == 1 ? 0 :
+ gs_note_error(gs_error_rangecheck));
+#undef MAX_INT_STR
+}
+
+/* ---------------- Private utilities ---------------- */
+
+/* Find a key in a dictionary. */
+static bool
+pdfmark_find_key(const char *key, const gs_param_string * pairs, uint count,
+ gs_param_string * pstr)
+{
+ uint i;
+
+ for (i = 0; i < count; i += 2)
+ if (pdf_key_eq(&pairs[i], key)) {
+ *pstr = pairs[i + 1];
+ return true;
+ }
+ pstr->data = 0;
+ pstr->size = 0;
+ return false;
+}
+
+/*
+ * Get the page number for a page referenced by number or as /Next or /Prev.
+ * The result may be 0 if the page number is 0 or invalid.
+ */
+static int
+pdfmark_page_number(gx_device_pdf * pdev, const gs_param_string * pnstr)
+{
+ int page = pdev->next_page + 1;
+
+ if (pnstr->data == 0);
+ else if (pdf_key_eq(pnstr, "/Next"))
+ ++page;
+ else if (pdf_key_eq(pnstr, "/Prev"))
+ --page;
+ else if (pdfmark_scan_int(pnstr, &page) < 0)
+ page = 0;
+ return page;
+}
+
+/*
+ * This routine checks the page number is inside the valid range FirstPage->LastPage
+ * and if it is, and FirstPage is not 0, it updates the page number by rebasing it to
+ * the new FirstPage. If all conditions are met we also update the 'max_referred_page'
+ * which we check during closedown, and emit a warning if a reference should somehow
+ * point outside the valid range of pages in the document. This can happen if we reference
+ * a destination page that we haven't created yet, and when we get to the end of the
+ * job we discover we never did create it.
+ */
+static int
+update_max_page_reference(gx_device_pdf * pdev, int *page)
+{
+ if (*page < pdev->FirstPage || (pdev->LastPage != 0 && *page > pdev->LastPage)) {
+ emprintf1(pdev->memory, "Destination page %d lies outside the valid page range.\n", *page);
+ return -1;
+ }
+ else {
+ if (pdev->FirstPage != 0)
+ *page = (*page - pdev->FirstPage) + 1;
+
+ if (pdev->max_referred_page < *page)
+ pdev->max_referred_page = *page;
+ }
+ return 0;
+}
+
+/* Construct a destination string specified by /Page and/or /View. */
+/* Return 0 if none (but still fill in a default), 1 or 2 if present */
+/* (1 if only one of /Page or /View, 2 if both), <0 if error. */
+static int
+pdfmark_make_dest(char dstr[MAX_DEST_STRING], gx_device_pdf * pdev,
+ const char *Page_key, const char *View_key,
+ const gs_param_string * pairs, uint count, uint RequirePage)
+{
+ gs_param_string page_string, view_string;
+ int present =
+ pdfmark_find_key(Page_key, pairs, count, &page_string) +
+ pdfmark_find_key(View_key, pairs, count, &view_string);
+ int page=0;
+ gs_param_string action;
+ int len, code = 0;
+
+ if (present || RequirePage)
+ page = pdfmark_page_number(pdev, &page_string);
+
+ if (view_string.size == 0)
+ param_string_from_string(view_string, "[/XYZ null null null]");
+ if (page == 0)
+ strcpy(dstr, "[null ");
+ else if (pdfmark_find_key("/Action", pairs, count, &action) &&
+ pdf_key_eq(&action, "/GoToR")
+ )
+ gs_sprintf(dstr, "[%d ", page - 1);
+ else {
+ code = update_max_page_reference(pdev, &page);
+ if (code < 0)
+ return code;
+ gs_sprintf(dstr, "[%ld 0 R ", pdf_page_id(pdev, page));
+ }
+ len = strlen(dstr);
+ if (len + view_string.size > MAX_DEST_STRING)
+ return_error(gs_error_limitcheck);
+ if (view_string.data[0] != '[' ||
+ view_string.data[view_string.size - 1] != ']'
+ )
+ return_error(gs_error_rangecheck);
+ memcpy(dstr + len, view_string.data + 1, view_string.size - 1);
+ dstr[len + view_string.size - 1] = 0;
+ return present;
+}
+
+/*
+ * If a named destination is specified by a string, convert it to a name,
+ * update *dstr, and return 1; otherwise return 0.
+ */
+static int
+pdfmark_coerce_dest(gs_param_string *dstr, char dest[MAX_DEST_STRING])
+{
+ const byte *data = dstr->data;
+ uint size = dstr->size;
+
+ if (size == 0 || data[0] != '(')
+ return 0;
+ /****** HANDLE ESCAPES ******/
+ memcpy(dest, data, size - 1);
+ dest[0] = '/';
+ dest[size - 1] = 0;
+ dstr->data = (byte *)dest;
+ dstr->size = size - 1;
+ return 1;
+}
+
+/* Put pairs in a dictionary. */
+static int
+pdfmark_put_c_pair(cos_dict_t *pcd, const char *key,
+ const gs_param_string * pvalue)
+{
+ return cos_dict_put_c_key_string(pcd, key, pvalue->data, pvalue->size);
+}
+static int
+pdfmark_put_pair(cos_dict_t *pcd, const gs_param_string * pair)
+{
+ return cos_dict_put_string(pcd, pair->data, pair->size,
+ pair[1].data, pair[1].size);
+}
+
+/* Scan a Rect value. */
+static int
+pdfmark_scan_rect(gs_rect * prect, const gs_param_string * str,
+ const gs_matrix * pctm)
+{
+ uint size = str->size;
+ double v[4];
+#define MAX_RECT_STRING 100
+ char chars[MAX_RECT_STRING + 3];
+ int end_check;
+
+ if (str->size > MAX_RECT_STRING)
+ return_error(gs_error_limitcheck);
+ memcpy(chars, str->data, size);
+ strcpy(chars + size, " 0");
+ if (sscanf(chars, "[%lg %lg %lg %lg]%d",
+ &v[0], &v[1], &v[2], &v[3], &end_check) != 5
+ )
+ return_error(gs_error_rangecheck);
+ gs_point_transform(v[0], v[1], pctm, &prect->p);
+ gs_point_transform(v[2], v[3], pctm, &prect->q);
+ return 0;
+}
+
+/* Make a Rect value. */
+static void
+pdfmark_make_rect(char str[MAX_RECT_STRING], const gs_rect * prect)
+{
+ /*
+ * We have to use a stream and pprintf, rather than sprintf,
+ * because printf formats can't express the PDF restrictions on
+ * the form of the output.
+ */
+ stream s;
+
+ s_init(&s, NULL);
+ swrite_string(&s, (byte *)str, MAX_RECT_STRING - 1);
+ pprintg4(&s, "[%g %g %g %g]",
+ prect->p.x, prect->p.y, prect->q.x, prect->q.y);
+ str[stell(&s)] = 0;
+}
+
+/* Write a transformed Border value on a stream. */
+static int
+pdfmark_write_border(stream *s, const gs_param_string *str,
+ const gs_matrix *pctm)
+{
+ /*
+ * We don't preserve the entire CTM in the output, and it isn't clear
+ * what CTM is applicable to annotations anyway: we only attempt to
+ * handle well-behaved CTMs here.
+ */
+ uint size = str->size;
+#define MAX_BORDER_STRING 100
+ char chars[MAX_BORDER_STRING + 1];
+ double bx, by, c;
+ gs_point bpt, cpt;
+ const char *next;
+
+ if (str->size > MAX_BORDER_STRING)
+ return_error(gs_error_limitcheck);
+ memcpy(chars, str->data, size);
+ chars[size] = 0;
+ if (sscanf(chars, "[%lg %lg %lg", &bx, &by, &c) != 3)
+ return_error(gs_error_rangecheck);
+ gs_distance_transform(bx, by, pctm, &bpt);
+ gs_distance_transform(0.0, c, pctm, &cpt);
+ pprintg3(s, "[%g %g %g", fabs(bpt.x), fabs(bpt.y), fabs(cpt.x + cpt.y));
+ /*
+ * We don't attempt to do 100% reliable syntax checking here --
+ * it's just not worth the trouble.
+ */
+ next = strchr(chars + 1, ']');
+ if (next == 0)
+ return_error(gs_error_rangecheck);
+ if (next[1] != 0) {
+ /* Handle a dash array. This is tiresome. */
+ double v;
+
+ stream_putc(s, '[');
+ while (next != 0 && sscanf(++next, "%lg", &v) == 1) {
+ gs_point vpt;
+
+ gs_distance_transform(0.0, v, pctm, &vpt);
+ pprintg1(s, "%g ", fabs(vpt.x + vpt.y));
+ next = strchr(next, ' ');
+ }
+ stream_putc(s, ']');
+ }
+ stream_putc(s, ']');
+ return 0;
+}
+
+/* Put an element in a stream's dictionary. */
+static int
+cos_stream_put_c_strings(cos_stream_t *pcs, const char *key, const char *value)
+{
+ return cos_dict_put_c_strings(cos_stream_dict(pcs), key, value);
+}
+
+/* Setup pdfmak stream compression. */
+static int
+setup_pdfmark_stream_compression(gx_device_psdf *pdev0,
+ cos_stream_t *pco)
+{
+ /* This function is for pdfwrite only. */
+ gx_device_pdf *pdev = (gx_device_pdf *)pdev0;
+ gs_memory_t *mem = pdev->pdf_memory;
+ static const pdf_filter_names_t fnames = {
+ PDF_FILTER_NAMES
+ };
+ const stream_template *templat =
+ (pdev->params.UseFlateCompression &&
+ pdev->version >= psdf_version_ll3 ?
+ &s_zlibE_template : &s_LZWE_template);
+ stream_state *st;
+
+ pco->input_strm = cos_write_stream_alloc(pco, pdev,
+ "setup_pdfmark_stream_compression");
+ if (pco->input_strm == 0)
+ return_error(gs_error_VMerror);
+ if (!pdev->binary_ok) {
+ stream_state *ss = s_alloc_state(mem, s_A85E_template.stype,
+ "setup_pdfmark_stream_compression");
+ if (ss == 0)
+ return_error(gs_error_VMerror);
+ if (s_add_filter(&pco->input_strm, &s_A85E_template, ss, mem) == 0) {
+ gs_free_object(mem, ss, "setup_image_compression");
+ return_error(gs_error_VMerror);
+ }
+ }
+ st = s_alloc_state(mem, templat->stype,
+ "setup_pdfmark_stream_compression");
+ if (st == 0)
+ return_error(gs_error_VMerror);
+ if (templat->set_defaults)
+ (*templat->set_defaults) (st);
+ if (s_add_filter(&pco->input_strm, templat, st, mem) == 0) {
+ gs_free_object(mem, st, "setup_image_compression");
+ return_error(gs_error_VMerror);
+ }
+ return pdf_put_filters(cos_stream_dict(pco), pdev, pco->input_strm, &fnames);
+}
+
+static int
+pdfmark_bind_named_object(gx_device_pdf *pdev, const gs_const_string *objname,
+ pdf_resource_t **pres)
+{
+ int code;
+
+ if (objname != NULL && objname->size) {
+ const cos_value_t *v = cos_dict_find(pdev->local_named_objects, objname->data, objname->size);
+
+ if (v != NULL) {
+ if (v->value_type == COS_VALUE_OBJECT) {
+ if (cos_type(v->contents.object) == &cos_generic_procs) {
+ /* The object was referred but not defined.
+ Use the old object id.
+ The old object stub to be dropped. */
+ pdf_reserve_object_id(pdev, *pres, v->contents.object->id);
+ } else if (!v->contents.object->written) {
+ /* We can't know whether the old object was referred or not.
+ Write it out for a consistent result in any case. */
+ code = cos_write_object(v->contents.object, pdev, resourceOther);
+
+ if (code < 0)
+ return code;
+ v->contents.object->written = true;
+ }
+ } else
+ return_error(gs_error_rangecheck); /* Must not happen. */
+ }
+ }
+ if ((*pres)->object->id == -1) {
+ if(objname != NULL && objname->size)
+ code = pdf_substitute_resource(pdev, pres, resourceXObject, NULL, false);
+ else
+ code = pdf_substitute_resource(pdev, pres, resourceXObject, NULL, true);
+ (*pres)->where_used |= pdev->used_mask;
+ if (code < 0)
+ return code;
+ } else {
+ /* Unfortunately we can't apply pdf_substitute_resource,
+ because the object may already be referred by its id.
+ Redundant objects may happen in this case.
+ For better results users should define objects before usage.
+ */
+ }
+ if (objname != NULL && objname->size) {
+ cos_value_t value;
+
+ code = cos_dict_put(pdev->local_named_objects, objname->data,
+ objname->size, cos_object_value(&value, (cos_object_t *)(*pres)->object));
+ if (code < 0)
+ return code;
+ }
+ return 0;
+}
+
+/* ---------------- Miscellaneous pdfmarks ---------------- */
+
+/*
+ * Create the dictionary for an annotation or outline. For some
+ * unfathomable reason, PDF requires the following key substitutions
+ * relative to pdfmarks:
+ * In annotation and link dictionaries:
+ * /Action => /A, /Color => /C, /Title => /T
+ * In outline directionaries:
+ * /Action => /A, but *not* /Color or /Title
+ * In Action subdictionaries:
+ * /Dest => /D, /File => /F, /Subtype => /S
+ * and also the following substitutions:
+ * /Action /Launch /File xxx =>
+ * /A << /S /Launch /F xxx >>
+ * /Action /GoToR /File xxx /Dest yyy =>
+ * /A << /S /GoToR /F xxx /D yyy' >>
+ * /Action /Article /Dest yyy =>
+ * /A << /S /Thread /D yyy' >>
+ * /Action /GoTo => drop the Action key
+ * Also, \n in Contents strings must be replaced with \r.
+ * Also, an outline dictionary with no action, Dest, Page, or View has an
+ * implied GoTo action with Dest = [{ThisPage} /XYZ null null null].
+ * Note that for Thread actions, the Dest is not a real destination,
+ * and must not be processed as one.
+ *
+ * We always treat /A and /F as equivalent to /Action and /File
+ * respectively. The pdfmark and PDF documentation is so confused on the
+ * issue of when the long and short names should be used that we only give
+ * this a 50-50 chance of being right.
+ *
+ * Note that we must transform Rect and Border coordinates.
+ */
+
+typedef struct ao_params_s {
+ gx_device_pdf *pdev; /* for pdfmark_make_dest */
+ const char *subtype; /* default Subtype in top-level dictionary */
+ long src_pg; /* set to SrcPg - 1 if any */
+} ao_params_t;
+static int
+pdfmark_put_ao_pairs(gx_device_pdf * pdev, cos_dict_t *pcd,
+ const gs_param_string * pairs, uint count,
+ const gs_matrix * pctm, ao_params_t * params,
+ bool for_outline)
+{
+ const gs_param_string *Action = 0;
+ const gs_param_string *File = 0;
+ const gs_param_string *URI = 0;
+ gs_param_string Dest;
+ gs_param_string Subtype;
+ uint i;
+ int code;
+ char dest[MAX_DEST_STRING];
+ bool coerce_dest = false;
+
+ Subtype.data = 0;
+ Subtype.size = 0;
+ Dest.data = 0;
+ Dest.size = 0;
+ if (params->subtype)
+ param_string_from_string(Subtype, params->subtype);
+ else
+ Subtype.data = 0;
+ for (i = 0; i < count; i += 2) {
+ const gs_param_string *pair = &pairs[i];
+ long src_pg;
+
+ if (pdf_key_eq(pair, "/SrcPg")){
+ unsigned char *buf0 = (unsigned char *)gs_alloc_bytes(pdev->memory, (pair[1].size + 1) * sizeof(unsigned char),
+ "pdf_xmp_write_translated");
+ memcpy(buf0, pair[1].data, pair[1].size);
+ buf0[pair[1].size] = 0x00;
+ if (sscanf((char *)buf0, "%ld", &src_pg) == 1)
+ params->src_pg = src_pg - 1;
+ gs_free_object(pdev->memory, buf0, "pdf_xmp_write_translated");
+ }
+ else if (!for_outline && pdf_key_eq(pair, "/Color"))
+ pdfmark_put_c_pair(pcd, "/C", pair + 1);
+ else if (!for_outline && pdf_key_eq(pair, "/Title"))
+ pdfmark_put_c_pair(pcd, "/T", pair + 1);
+ else if (pdf_key_eq(pair, "/Action") || pdf_key_eq(pair, "/A"))
+ Action = pair;
+ /* Previously also catered for '/F', but at the top level (outside an
+ * Action dict which is handled below), a /F can only be the Flags for
+ * the annotation, not a File or JavaScript action.
+ */
+ else if (pdf_key_eq(pair, "/File") /* || pdf_key_eq(pair, "/F")*/)
+ File = pair;
+ else if (pdf_key_eq(pair, "/Dest")) {
+ Dest = pair[1];
+ coerce_dest = true;
+ }
+ else if (pdf_key_eq(pair, "/URI")) {
+ URI = pair; /* save it for placing into the Action dict */
+ }
+ else if (pdf_key_eq(pair, "/Page") || pdf_key_eq(pair, "/View")) {
+ /* Make a destination even if this is for an outline. */
+ if (Dest.data == 0) {
+ code = pdfmark_make_dest(dest, params->pdev, "/Page", "/View",
+ pairs, count, 0);
+ if (code >= 0) {
+ param_string_from_string(Dest, dest);
+ if (for_outline)
+ coerce_dest = false;
+ } else {
+ emprintf(pdev->memory, " **** Warning: Outline has invalid link that was discarded.\n");
+ return gs_note_error(gs_error_rangecheck);
+ }
+ }
+ } else if (pdf_key_eq(pair, "/Subtype"))
+ Subtype = pair[1];
+ /*
+ * We also have to replace all occurrences of \n in Contents
+ * strings with \r. Unfortunately, they probably have already
+ * been converted to \012....
+ */
+ else if (pdf_key_eq(pair, "/Contents")) {
+ byte *cstr;
+ uint csize = pair[1].size;
+ cos_value_t *pcv;
+ uint i, j;
+
+ /*
+ * Copy the string into value storage, then update it in place.
+ */
+ pdfmark_put_pair(pcd, pair);
+ /* Break const so we can update the (copied) string. */
+ pcv = (cos_value_t *)cos_dict_find_c_key(pcd, "/Contents");
+ if (pcv == NULL)
+ return_error(gs_error_ioerror); /* shouldn't be possible */
+ cstr = pcv->contents.chars.data;
+ /* Loop invariant: j <= i < csize. */
+ for (i = j = 0; i < csize;)
+ if (csize - i >= 2 && !memcmp(cstr + i, "\\n", 2) &&
+ (i == 0 || cstr[i - 1] != '\\')
+ ) {
+ cstr[j] = '\\', cstr[j + 1] = 'r';
+ i += 2, j += 2;
+ } else if (csize - i >= 4 && !memcmp(cstr + i, "\\012", 4) &&
+ (i == 0 || cstr[i - 1] != '\\')
+ ) {
+ cstr[j] = '\\', cstr[j + 1] = 'r';
+ i += 4, j += 2;
+ } else
+ cstr[j++] = cstr[i++];
+ if (j != i)
+ pcv->contents.chars.data =
+ gs_resize_string(pdev->pdf_memory, cstr, csize, j,
+ "pdfmark_put_ao_pairs");
+ } else if (pdf_key_eq(pair, "/Rect")) {
+ gs_rect rect;
+ char rstr[MAX_RECT_STRING];
+ int code = pdfmark_scan_rect(&rect, pair + 1, pctm);
+
+ if (code < 0)
+ return code;
+ pdfmark_make_rect(rstr, &rect);
+ cos_dict_put_c_key_string(pcd, "/Rect", (byte *)rstr,
+ strlen(rstr));
+ } else if (pdf_key_eq(pair, "/Border")) {
+ stream s;
+ char bstr[MAX_BORDER_STRING + 1];
+ int code;
+
+ s_init(&s, NULL);
+ swrite_string(&s, (byte *)bstr, MAX_BORDER_STRING + 1);
+ code = pdfmark_write_border(&s, pair + 1, pctm);
+ if (code < 0)
+ return code;
+ if (stell(&s) > MAX_BORDER_STRING)
+ return_error(gs_error_limitcheck);
+ bstr[stell(&s)] = 0;
+ cos_dict_put_c_key_string(pcd, "/Border", (byte *)bstr,
+ strlen(bstr));
+ } else if (for_outline && pdf_key_eq(pair, "/Count"))
+ DO_NOTHING;
+ else {
+ int i, j=0;
+ unsigned char *temp, *buf0 = (unsigned char *)gs_alloc_bytes(pdev->memory, pair[1].size * 2 * sizeof(unsigned char),
+ "pdf_xmp_write_translated");
+ if (buf0 == NULL)
+ return_error(gs_error_VMerror);
+ for (i = 0; i < pair[1].size; i++) {
+ byte c = pair[1].data[i];
+
+ buf0[j++] = c;
+ /* Acrobat doesn't like the 'short' escapes. and wants them as octal....
+ * I beliwvw this should be considered an Acrtobat bug, either escapes
+ * can be used, or not, we shouldn't have to force them to octal.
+ */
+ if (c == '\\') {
+ switch(pair[1].data[i + 1]) {
+ case 'b':
+ buf0[j++] = '0';
+ buf0[j++] = '0';
+ buf0[j++] = '7';
+ i++;
+ break;
+ case 'f':
+ buf0[j++] = '0';
+ buf0[j++] = '1';
+ buf0[j++] = '4';
+ i++;
+ break;
+ case 'n':
+ buf0[j++] = '0';
+ buf0[j++] = '1';
+ buf0[j++] = '2';
+ i++;
+ break;
+ case 'r':
+ buf0[j++] = '0';
+ buf0[j++] = '1';
+ buf0[j++] = '5';
+ i++;
+ break;
+ case 't':
+ buf0[j++] = '0';
+ buf0[j++] = '1';
+ buf0[j++] = '1';
+ i++;
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ i = pair[1].size;
+ temp = (unsigned char *)pair[1].data;
+ ((gs_param_string *)pair)[1].data = buf0;
+ ((gs_param_string *)pair)[1].size = j;
+
+ pdfmark_put_pair(pcd, pair);
+
+ ((gs_param_string *)pair)[1].data = temp;
+ ((gs_param_string *)pair)[1].size = i;
+ gs_free_object(pdev->memory, buf0, "pdf_xmp_write_translated");
+ }
+ }
+ if (!for_outline && pdf_key_eq(&Subtype, "/Link")) {
+ if (Action) {
+ /* Don't delete the Dest for GoTo or file-GoToR. */
+ if (pdf_key_eq(Action + 1, "/GoTo") ||
+ (File && pdf_key_eq(Action + 1, "/GoToR"))
+ )
+ DO_NOTHING;
+ else
+ Dest.data = 0;
+ }
+ }
+
+ /* Now handle the deferred keys. */
+ if (Action) {
+ const byte *astr = Action[1].data;
+ const uint asize = Action[1].size;
+
+ if ((File != 0 || Dest.data != 0 || URI != 0) &&
+ (pdf_key_eq(Action + 1, "/Launch") ||
+ (pdf_key_eq(Action + 1, "/GoToR") && File) ||
+ pdf_key_eq(Action + 1, "/Article"))
+ ) {
+ cos_dict_t *adict = cos_dict_alloc(pdev, "action dict");
+ cos_value_t avalue;
+
+ if (adict == 0)
+ return_error(gs_error_VMerror);
+ if (!for_outline) {
+ /* We aren't sure whether this is really needed.... */
+ cos_dict_put_c_strings(adict, "/Type", "/Action");
+ }
+ if (pdf_key_eq(Action + 1, "/Article")) {
+ cos_dict_put_c_strings(adict, "/S", "/Thread");
+ coerce_dest = false; /* Dest is not a real destination */
+ }
+ else
+ pdfmark_put_c_pair(adict, "/S", Action + 1);
+ if (Dest.data) {
+ if (coerce_dest)
+ pdfmark_coerce_dest(&Dest, dest);
+ pdfmark_put_c_pair(adict, "/D", &Dest);
+ Dest.data = 0; /* so we don't write it again */
+ }
+ if (File) {
+ pdfmark_put_c_pair(adict, "/F", File + 1);
+ File = 0; /* so we don't write it again */
+ }
+ if (URI) {
+ /* Adobe Distiller puts a /URI key from pdfmark into the */
+ /* Action dict with /S /URI as Subtype */
+ pdfmark_put_pair(adict, URI);
+ cos_dict_put_c_strings(adict, "/S", "/URI");
+ }
+ cos_dict_put(pcd, (const byte *)"/A", 2,
+ COS_OBJECT_VALUE(&avalue, adict));
+ } else if (asize >= 4 && !memcmp(astr, "<<", 2)) {
+ /* Replace occurrences of /Dest, /File, and /Subtype. */
+ const byte *scan = astr + 2;
+ const byte *end = astr + asize;
+ gs_param_string key, value;
+ cos_dict_t *adict = cos_dict_alloc(pdev, "action dict");
+ cos_value_t avalue;
+ int code = 0;
+
+ if (adict == 0)
+ return_error(gs_error_VMerror);
+ if (URI) {
+ /* Adobe Distiller puts a /URI key from pdfmark into the */
+ /* Action dict with /S /URI as Subtype */
+ pdfmark_put_pair(adict, URI);
+ if(cos_dict_put_c_strings(adict, "/S", "/URI") < 0)
+ return code;
+ }
+ while ((code = pdf_scan_token(&scan, end, &key.data)) > 0) {
+ key.size = scan - key.data;
+ if (key.data[0] != '/' ||
+ (code = pdf_scan_token_composite(&scan, end, &value.data)) != 1)
+ break;
+ value.size = scan - value.data;
+ if (pdf_key_eq(&key, "/Dest") || pdf_key_eq(&key, "/D")) {
+ param_string_from_string(key, "/D");
+ if (value.data[0] == '(') {
+ /****** HANDLE ESCAPES ******/
+ pdfmark_coerce_dest(&value, dest);
+ }
+ } else if (pdf_key_eq(&key, "/File"))
+ param_string_from_string(key, "/F");
+ else if (pdf_key_eq(&key, "/Subtype"))
+ param_string_from_string(key, "/S");
+ cos_dict_put_string(adict, key.data, key.size,
+ value.data, value.size);
+ }
+ if (code <= 0 || !pdf_key_eq(&key, ">>"))
+ return_error(gs_error_rangecheck);
+ cos_dict_put(pcd, (const byte *)"/A", 2,
+ COS_OBJECT_VALUE(&avalue, adict));
+ } else if (pdf_key_eq(Action + 1, "/GoTo"))
+ pdfmark_put_pair(pcd, Action);
+ else if (Action[1].size < 30) {
+ /* Hack: we could substitute names in pdfmark_process,
+ now should recognize whether it was done.
+ Not a perfect method though.
+ Go with it for a while. */
+ char buf[30];
+ int d0, d1;
+
+ memcpy(buf, Action[1].data, Action[1].size);
+ buf[Action[1].size] = 0;
+ if (sscanf(buf, "%d %d R", &d0, &d1) == 2)
+ pdfmark_put_pair(pcd, Action);
+ }
+ }
+ /*
+ * If we have /Dest or /File without the right kind of action,
+ * simply write it at the top level. This doesn't seem right,
+ * but I'm not sure what else to do.
+ */
+ if (Dest.data) {
+ if (coerce_dest)
+ pdfmark_coerce_dest(&Dest, dest);
+ /*
+ * For PDF 1.2 or better we write a Names tree, but in this case the names
+ * are required to be (counter-intuitively) strings, NOT name objects....
+ */
+ if (pdev->CompatibilityLevel > 1.1) {
+ gs_param_string DestString;
+ int i = 0, j;
+ char *D;
+
+ /*
+ * If the name has any 'unusual' characters, it is 'escaped' by starting with NULLs
+ * I suspect we no longer need that, but here we remove the escaping NULLs
+ */
+ if (Dest.size > 3 && Dest.data[0] == 0x00 && Dest.data[1] == 0x00 && Dest.data[Dest.size - 1] == 0x00) {
+ i = 2;
+ if (Dest.size > 5 && Dest.data[2] == 0x00 && Dest.data[3] == 0x00)
+ i += 2;
+ }
+
+ if (Dest.data[i] == '/') {
+ i++;
+
+ DestString.data = gs_alloc_bytes(pdev->memory->stable_memory, (Dest.size * 2) + 1, "DEST pdfmark temp string");
+ if (DestString.data == 0)
+ return_error(gs_error_VMerror);
+ DestString.size = (Dest.size * 2) + 1;
+ DestString.persistent = 0;
+ D = (char *)DestString.data;
+ D[0] = '(';
+ for (j=1;i<Dest.size;i++, j++) {
+ if (Dest.data[i] == '(' || Dest.data[i] == ')') {
+ D[j++] = '\\';
+ }
+ D[j] = Dest.data[i];
+ }
+ D[j++] = ')';
+ DestString.size = j;
+ pdfmark_put_c_pair(pcd, "/Dest", &DestString);
+ gs_free_object(pdev->memory->stable_memory, D, "DEST pdfmark temp string");
+ } else
+ pdfmark_put_c_pair(pcd, "/Dest", &Dest);
+ } else
+ pdfmark_put_c_pair(pcd, "/Dest", &Dest);
+ } else if (for_outline && !Action) {
+ /* Make an implicit destination. */
+ char dstr[1 + (sizeof(long) * 8 / 3 + 1) + 25 + 1];
+ long page_id = pdf_page_id(pdev, pdev->next_page + 1);
+
+ gs_sprintf(dstr, "[%ld 0 R /XYZ null null null]", page_id);
+ cos_dict_put_c_key_string(pcd, "/Dest", (const unsigned char*) dstr,
+ strlen(dstr));
+ }
+ if (File)
+ pdfmark_put_pair(pcd, File);
+ if (Subtype.data)
+ pdfmark_put_c_pair(pcd, "/Subtype", &Subtype);
+ return 0;
+}
+
+/* Copy an annotation dictionary. */
+static int
+pdfmark_annot(gx_device_pdf * pdev, gs_param_string * pairs, uint count,
+ const gs_matrix * pctm, const gs_param_string *objname,
+ const char *subtype)
+{
+ ao_params_t params;
+ cos_dict_t *pcd;
+ int page_index = pdev->next_page;
+ cos_array_t *annots;
+ cos_value_t value;
+ int code;
+
+ /* Annotations are only permitted in PDF/A if they have the
+ * Print flag enabled, so we need to prescan for that here.
+ */
+ if(pdev->PDFA != 0) {
+ int i, Flags = 0;
+ /* Check all the keys to see if we have a /F (Flags) key/value pair defined */
+ for (i = 0; i < count; i += 2) {
+ const gs_param_string *pair = &pairs[i];
+
+ if (pdf_key_eq(pair, "/F")) {
+ code = sscanf((const char *)pair[1].data, "%ld", (long *)&Flags);
+ if (code != 1)
+ emprintf(pdev->memory,
+ "Annotation has an invalid /Flags attribute\n");
+ break;
+ }
+ }
+ /* Check the Print flag, PDF/A annotations *must* be set to print */
+ if ((Flags & 4) == 0){
+ switch (pdev->PDFACompatibilityPolicy) {
+ /* Default behaviour matches Adobe Acrobat, warn and continue,
+ * output file will not be PDF/A compliant
+ */
+ case 0:
+ emprintf(pdev->memory,
+ "Annotation set to non-printing,\n not permitted in PDF/A, reverting to normal PDF output\n");
+ pdev->AbortPDFAX = true;
+ pdev->PDFA = 0;
+ break;
+ /* Since the annotation would break PDF/A compatibility, do not
+ * include it, but warn the user that it has been dropped.
+ */
+ case 1:
+ emprintf(pdev->memory,
+ "Annotation set to non-printing,\n not permitted in PDF/A, annotation will not be present in output file\n");
+ return 0;
+ break;
+ case 2:
+ emprintf(pdev->memory,
+ "Annotation set to non-printing,\n not permitted in PDF/A, aborting conversion\n");
+ return gs_error_invalidfont;
+ break;
+ default:
+ emprintf(pdev->memory,
+ "Annotation set to non-printing,\n not permitted in PDF/A, unrecognised PDFACompatibilityLevel,\nreverting to normal PDF output\n");
+ pdev->AbortPDFAX = true;
+ pdev->PDFA = 0;
+ break;
+ }
+ }
+ }
+ if (pdev->PDFX != 0) {
+ gs_param_string Subtype;
+ bool discard = true;
+ pdf_page_t *page = 0L;
+
+ page = &pdev->pages[pdev->next_page];
+
+ if (subtype) {
+ param_string_from_string(Subtype, subtype);
+ if (pdf_key_eq(&Subtype, "/TrapNet") ||
+ pdf_key_eq(&Subtype, "/PrinterMark"))
+ discard = false;
+ }
+ if (discard) {
+ int i;
+ for (i = 0; i < count; i += 2) {
+ const gs_param_string *pair = &pairs[i];
+ if (pdf_key_eq(pair, "/Rect")) {
+ gs_rect rect;
+ const cos_value_t *v_trimbox, *v_bleedbox, *v_artbox, *v_cropbox;
+ const byte *p;
+ char buf[100];
+ int size, code;
+ float temp[4]; /* the type is float for sscanf. */
+ double pagebox[4] = {0, 0};
+
+ pagebox[2] = pdev->MediaSize[0];
+ pagebox[3] = pdev->MediaSize[1];
+
+ v_cropbox = v_bleedbox = v_trimbox = v_artbox = 0x0L;
+
+ code = pdfmark_scan_rect(&rect, pair + 1, pctm);
+
+ if (code < 0)
+ return code;
+
+ if (page && page->Page) {
+ v_trimbox = cos_dict_find_c_key(page->Page, "/TrimBox");
+ v_bleedbox = cos_dict_find_c_key(page->Page, "/BleedBox");
+ v_artbox = cos_dict_find_c_key(page->Page, "/ArtBox");
+ v_cropbox = cos_dict_find_c_key(page->Page, "/CropBox");
+ }
+
+ if (v_cropbox != NULL && v_cropbox->value_type == COS_VALUE_SCALAR) {
+ p = v_cropbox->contents.chars.data;
+ size = min (v_cropbox->contents.chars.size, sizeof(buf) - 1);
+ memcpy(buf, p, size);
+ buf[size] = 0;
+ if (sscanf(buf, "[ %g %g %g %g ]",
+ &temp[0], &temp[1], &temp[2], &temp[3]) == 4) {
+ if (temp[0] > pagebox[0]) pagebox[0] = temp[0];
+ if (temp[1] > pagebox[1]) pagebox[1] = temp[1];
+ }
+ }
+ if (v_bleedbox != NULL && v_bleedbox->value_type == COS_VALUE_SCALAR) {
+ p = v_bleedbox->contents.chars.data;
+ size = min (v_bleedbox->contents.chars.size, sizeof(buf) - 1);
+ memcpy(buf, p, size);
+ buf[size] = 0;
+ if (sscanf(buf, "[ %g %g %g %g ]",
+ &temp[0], &temp[1], &temp[2], &temp[3]) == 4) {
+ if (temp[0] > pagebox[0]) pagebox[0] = temp[0];
+ if (temp[1] > pagebox[1]) pagebox[1] = temp[1];
+ }
+ }
+ if (v_trimbox != NULL && v_trimbox->value_type == COS_VALUE_SCALAR) {
+ p = v_trimbox->contents.chars.data;
+ size = min (v_trimbox->contents.chars.size, sizeof(buf) - 1);
+ memcpy(buf, p, size);
+ buf[size] = 0;
+ if (sscanf(buf, "[ %g %g %g %g ]",
+ &temp[0], &temp[1], &temp[2], &temp[3]) == 4) {
+ if (temp[0] > pagebox[0]) pagebox[0] = temp[0];
+ if (temp[1] > pagebox[1]) pagebox[1] = temp[1];
+ }
+ }
+ if (v_artbox != NULL && v_artbox->value_type == COS_VALUE_SCALAR) {
+ p = v_artbox->contents.chars.data;
+ size = min (v_artbox->contents.chars.size, sizeof(buf) - 1);
+ memcpy(buf, p, size);
+ buf[size] = 0;
+ if (sscanf(buf, "[ %g %g %g %g ]",
+ &temp[0], &temp[1], &temp[2], &temp[3]) == 4) {
+ if (temp[0] > pagebox[0]) pagebox[0] = temp[0];
+ if (temp[1] > pagebox[1]) pagebox[1] = temp[1];
+ }
+ }
+ if (v_cropbox == 0 && v_trimbox == 0 && v_artbox == 0 && v_bleedbox == 0) {
+ if (pdev->PDFXTrimBoxToMediaBoxOffset.size >= 4 &&
+ pdev->PDFXTrimBoxToMediaBoxOffset.data[0] >= 0 &&
+ pdev->PDFXTrimBoxToMediaBoxOffset.data[1] >= 0 &&
+ pdev->PDFXTrimBoxToMediaBoxOffset.data[2] >= 0 &&
+ pdev->PDFXTrimBoxToMediaBoxOffset.data[3] >= 0) {
+ pagebox[0] += pdev->PDFXTrimBoxToMediaBoxOffset.data[0];
+ pagebox[1] += pdev->PDFXTrimBoxToMediaBoxOffset.data[3];
+ pagebox[2] -= pdev->PDFXTrimBoxToMediaBoxOffset.data[1];
+ pagebox[3] -= pdev->PDFXTrimBoxToMediaBoxOffset.data[2];
+ } else if (pdev->PDFXBleedBoxToTrimBoxOffset.size >= 4 &&
+ pdev->PDFXBleedBoxToTrimBoxOffset.data[0] >= 0 &&
+ pdev->PDFXBleedBoxToTrimBoxOffset.data[1] >= 0 &&
+ pdev->PDFXBleedBoxToTrimBoxOffset.data[2] >= 0 &&
+ pdev->PDFXBleedBoxToTrimBoxOffset.data[3] >= 0) {
+ pagebox[0] -= pdev->PDFXBleedBoxToTrimBoxOffset.data[0];
+ pagebox[1] -= pdev->PDFXBleedBoxToTrimBoxOffset.data[3];
+ pagebox[2] += pdev->PDFXBleedBoxToTrimBoxOffset.data[1];
+ pagebox[3] += pdev->PDFXBleedBoxToTrimBoxOffset.data[2];
+ }
+ }
+
+ if (rect.p.x > pagebox[2] || rect.q.x < pagebox[0] ||
+ rect.p.y > pagebox[3] || rect.q.y < pagebox[2])
+ break;
+ switch (pdev->PDFACompatibilityPolicy) {
+ /* Default behaviour matches Adobe Acrobat, warn and continue,
+ * output file will not be PDF/A compliant
+ */
+ case 0:
+ emprintf(pdev->memory,
+ "Annotation (not TrapNet or PrinterMark) on page,\n not permitted in PDF/X, reverting to normal PDF output\n");
+ pdev->AbortPDFAX = true;
+ pdev->PDFX = 0;
+ break;
+ /* Since the annotation would break PDF/A compatibility, do not
+ * include it, but warn the user that it has been dropped.
+ */
+ case 1:
+ emprintf(pdev->memory,
+ "Annotation (not TrapNet or PrinterMark) on page,\n not permitted in PDF/X, annotation will not be present in output file\n");
+ return 0;
+ break;
+ case 2:
+ emprintf(pdev->memory,
+ "Annotation (not TrapNet or PrinterMark) on page,\n not permitted in PDF/X, aborting conversion\n");
+ return gs_error_invalidfont;
+ break;
+ default:
+ emprintf(pdev->memory,
+ "Annotation s(not TrapNet or PrinterMark) on page,\n not permitted in PDF/A, unrecognised PDFACompatibilityLevel,\nreverting to normal PDF output\n");
+ pdev->AbortPDFAX = true;
+ pdev->PDFX = 0;
+ break;
+ }
+ break;
+ }
+ }
+ if (i > count) {
+ switch (pdev->PDFACompatibilityPolicy) {
+ /* Default behaviour matches Adobe Acrobat, warn and continue,
+ * output file will not be PDF/A compliant
+ */
+ case 0:
+ emprintf(pdev->memory,
+ "Annotation (not TrapNet or PrinterMark) potentially on page (no /Rect in dict),\n not permitted in PDF/X, reverting to normal PDF output\n");
+ pdev->AbortPDFAX = true;
+ pdev->PDFX = 0;
+ break;
+ /* Since the annotation would break PDF/A compatibility, do not
+ * include it, but warn the user that it has been dropped.
+ */
+ case 1:
+ emprintf(pdev->memory,
+ "Annotation (not TrapNet or PrinterMark) potentially on page (no /Rect in dict),\n not permitted in PDF/X, annotation will not be present in output file\n");
+ return 0;
+ break;
+ case 2:
+ emprintf(pdev->memory,
+ "Annotation (not TrapNet or PrinterMark) potentially on page (no /Rect in dict),\n not permitted in PDF/X, aborting conversion\n");
+ return gs_error_invalidfont;
+ break;
+ default:
+ emprintf(pdev->memory,
+ "Annotation s(not TrapNet or PrinterMark) potentially on page (no /Rect in dict),\n not permitted in PDF/A, unrecognised PDFACompatibilityLevel,\nreverting to normal PDF output\n");
+ pdev->AbortPDFAX = true;
+ pdev->PDFX = 0;
+ break;
+ }
+ }
+ }
+ }
+ params.pdev = pdev;
+ params.subtype = subtype;
+ params.src_pg = -1;
+ code = pdf_make_named_dict(pdev, objname, &pcd, true);
+ if (code < 0)
+ return code;
+ code = cos_dict_put_c_strings(pcd, "/Type", "/Annot");
+ if (code < 0)
+ return code;
+ code = pdfmark_put_ao_pairs(pdev, pcd, pairs, count, pctm, &params, false);
+ if (code < 0)
+ return code;
+ if (params.src_pg >= 0)
+ page_index = params.src_pg;
+ if (pdf_page_id(pdev, page_index + 1) <= 0)
+ return_error(gs_error_rangecheck);
+ annots = pdev->pages[page_index].Annots;
+ if (annots == 0) {
+ annots = cos_array_alloc(pdev, "pdfmark_annot");
+ if (annots == 0)
+ return_error(gs_error_VMerror);
+ pdev->pages[page_index].Annots = annots;
+ }
+ if (!objname) {
+ /* Write the annotation now. */
+ COS_WRITE_OBJECT(pcd, pdev, resourceAnnotation);
+ COS_RELEASE(pcd, "pdfmark_annot");
+ }
+ return cos_array_add(annots,
+ cos_object_value(&value, COS_OBJECT(pcd)));
+}
+
+/* ANN pdfmark */
+static int
+pdfmark_ANN(gx_device_pdf * pdev, gs_param_string * pairs, uint count,
+ const gs_matrix * pctm, const gs_param_string * objname)
+{
+ return pdfmark_annot(pdev, pairs, count, pctm, objname, "/Text");
+}
+
+/* LNK pdfmark (obsolescent) */
+static int
+pdfmark_LNK(gx_device_pdf * pdev, gs_param_string * pairs, uint count,
+ const gs_matrix * pctm, const gs_param_string * objname)
+{
+ return pdfmark_annot(pdev, pairs, count, pctm, objname, "/Link");
+}
+
+/* Write and release one node of the outline tree. */
+static int
+pdfmark_write_outline(gx_device_pdf * pdev, pdf_outline_node_t * pnode,
+ long next_id)
+{
+ stream *s;
+ int code = 0;
+
+ pdf_open_separate(pdev, pnode->id, resourceOutline);
+ if (pnode->action != NULL)
+ pnode->action->id = pnode->id;
+ else {
+ emprintf1(pdev->memory,
+ "pdfmark error: Outline node %ld has no action or destination.\n",
+ pnode->id);
+ code = gs_note_error(gs_error_undefined);
+ }
+ s = pdev->strm;
+ stream_puts(s, "<< ");
+ if (pnode->action != NULL)
+ cos_dict_elements_write(pnode->action, pdev);
+ if (pnode->count)
+ pprintd1(s, "/Count %d ", pnode->count);
+ pprintld1(s, "/Parent %ld 0 R\n", pnode->parent_id);
+ if (pnode->prev_id)
+ pprintld1(s, "/Prev %ld 0 R\n", pnode->prev_id);
+ if (next_id)
+ pprintld1(s, "/Next %ld 0 R\n", next_id);
+ if (pnode->first_id)
+ pprintld2(s, "/First %ld 0 R /Last %ld 0 R\n",
+ pnode->first_id, pnode->last_id);
+ stream_puts(s, ">>\n");
+ pdf_end_separate(pdev, resourceOutline);
+ if (pnode->action != NULL)
+ COS_FREE(pnode->action, "pdfmark_write_outline");
+ pnode->action = 0;
+ return code;
+}
+
+/* Adjust the parent's count when writing an outline node. */
+static void
+pdfmark_adjust_parent_count(pdf_outline_level_t * plevel)
+{
+ pdf_outline_level_t *parent = plevel - 1;
+ int count = plevel->last.count;
+
+ if (count > 0) {
+ if (parent->last.count < 0)
+ parent->last.count -= count;
+ else
+ parent->last.count += count;
+ }
+}
+
+/*
+ * Close the current level of the outline tree. Note that if we are at
+ * the end of the document, some of the levels may be incomplete if the
+ * Count values were incorrect.
+ */
+int
+pdfmark_close_outline(gx_device_pdf * pdev)
+{
+ int depth = pdev->outline_depth;
+ pdf_outline_level_t *plevel = &pdev->outline_levels[depth];
+ int code = 0;
+
+ if (plevel->last.id) { /* check for incomplete tree */
+ code = pdfmark_write_outline(pdev, &plevel->last, 0);
+ }
+ if (depth > 0) {
+ plevel[-1].last.last_id = plevel->last.id;
+ pdfmark_adjust_parent_count(plevel);
+ --plevel;
+ if (plevel->last.count < 0)
+ pdev->closed_outline_depth--;
+ pdev->outline_depth--;
+ }
+ return code;
+}
+
+/* OUT pdfmark */
+static int
+pdfmark_OUT(gx_device_pdf * pdev, gs_param_string * pairs, uint count,
+ const gs_matrix * pctm, const gs_param_string * no_objname)
+{
+ int depth = pdev->outline_depth;
+ pdf_outline_level_t *plevel = &pdev->outline_levels[depth];
+ int sub_count = 0;
+ uint i;
+ pdf_outline_node_t node;
+ ao_params_t ao;
+ int code;
+
+ for (i = 0; i < count; i += 2) {
+ const gs_param_string *pair = &pairs[i];
+
+ if (pdf_key_eq(pair, "/Count"))
+ pdfmark_scan_int(pair + 1, &sub_count);
+ }
+ if (sub_count != 0 && depth == pdev->max_outline_depth - 1) {
+ pdf_outline_level_t *new_ptr;
+
+ new_ptr = (pdf_outline_level_t *)gs_alloc_bytes(pdev->pdf_memory, (pdev->max_outline_depth + INITIAL_MAX_OUTLINE_DEPTH) * sizeof(pdf_outline_level_t) * sizeof(pdf_outline_level_t), "outline_levels array");
+ if (!new_ptr)
+ return_error(gs_error_VMerror);
+ memcpy (new_ptr, pdev->outline_levels, pdev->max_outline_depth * sizeof(pdf_outline_level_t));
+ gs_free_object(pdev->pdf_memory, pdev->outline_levels, "outline_levels array");
+ pdev->outline_levels = new_ptr;
+ pdev->max_outline_depth += INITIAL_MAX_OUTLINE_DEPTH;
+ plevel = &pdev->outline_levels[depth];
+ }
+ node.action = cos_dict_alloc(pdev, "pdfmark_OUT");
+ if (node.action == 0)
+ return_error(gs_error_VMerror);
+ ao.pdev = pdev;
+ ao.subtype = 0;
+ ao.src_pg = -1;
+ code = pdfmark_put_ao_pairs(pdev, node.action, pairs, count, pctm, &ao,
+ true);
+ if (code < 0)
+ return code;
+ if (pdev->outlines_id == 0)
+ pdev->outlines_id = pdf_obj_ref(pdev);
+ node.id = pdf_obj_ref(pdev);
+ node.parent_id =
+ (depth == 0 ? pdev->outlines_id : plevel[-1].last.id);
+ node.prev_id = plevel->last.id;
+ node.first_id = node.last_id = 0;
+ node.count = sub_count;
+ /* Add this node to the outline at the current level. */
+ if (plevel->first.id == 0) { /* First node at this level. */
+ if (depth > 0)
+ plevel[-1].last.first_id = node.id;
+ node.prev_id = 0;
+ plevel->first = node;
+ plevel->first.action = 0; /* never used */
+ } else { /* Write the previous node. */
+ if (depth > 0)
+ pdfmark_adjust_parent_count(plevel);
+ pdfmark_write_outline(pdev, &plevel->last, node.id);
+ }
+ plevel->last = node;
+ plevel->left--;
+ if (!pdev->closed_outline_depth)
+ pdev->outlines_open++;
+ /* If this node has sub-nodes, descend one level. */
+ if (sub_count != 0) {
+ pdev->outline_depth++;
+ ++plevel;
+ plevel->left = (sub_count > 0 ? sub_count : -sub_count);
+ plevel->first.id = 0;
+ plevel->first.action = plevel->last.action = 0; /* for GC */
+ if (sub_count < 0)
+ pdev->closed_outline_depth++;
+ } else {
+ while ((depth = pdev->outline_depth) > 0 &&
+ pdev->outline_levels[depth].left == 0
+ )
+ pdfmark_close_outline(pdev);
+ }
+ return 0;
+}
+
+/* Write an article bead. */
+static int
+pdfmark_write_bead(gx_device_pdf * pdev, const pdf_bead_t * pbead)
+{
+ stream *s;
+ char rstr[MAX_RECT_STRING];
+
+ pdf_open_separate(pdev, pbead->id, resourceArticle);
+ s = pdev->strm;
+ pprintld3(s, "<</T %ld 0 R/V %ld 0 R/N %ld 0 R",
+ pbead->article_id, pbead->prev_id, pbead->next_id);
+ if (pbead->page_id != 0)
+ pprintld1(s, "/P %ld 0 R", pbead->page_id);
+ pdfmark_make_rect(rstr, &pbead->rect);
+ pprints1(s, "/R%s>>\n", rstr);
+ return pdf_end_separate(pdev, resourceArticle);
+}
+
+/* Finish writing an article, and release its data. */
+int
+pdfmark_write_article(gx_device_pdf * pdev, const pdf_article_t * part)
+{
+ pdf_article_t art;
+ stream *s;
+
+ art = *part;
+ if (art.last.id == 0) {
+ /* Only one bead in the article. */
+ art.first.prev_id = art.first.next_id = art.first.id;
+ } else {
+ /* More than one bead in the article. */
+ art.first.prev_id = art.last.id;
+ art.last.next_id = art.first.id;
+ pdfmark_write_bead(pdev, &art.last);
+ }
+ pdfmark_write_bead(pdev, &art.first);
+ pdf_open_separate(pdev, art.contents->id, resourceArticle);
+ s = pdev->strm;
+ pprintld1(s, "<</F %ld 0 R/I<<", art.first.id);
+ cos_dict_elements_write(art.contents, pdev);
+ stream_puts(s, ">> >>\n");
+ return pdf_end_separate(pdev, resourceArticle);
+}
+
+/* ARTICLE pdfmark */
+static int
+pdfmark_ARTICLE(gx_device_pdf * pdev, gs_param_string * pairs, uint count,
+ const gs_matrix * pctm, const gs_param_string * no_objname)
+{
+ gs_memory_t *mem = pdev->pdf_memory;
+ gs_param_string title;
+ gs_param_string rectstr;
+ gs_rect rect;
+ long bead_id;
+ pdf_article_t *part;
+ int code;
+
+ if (!pdfmark_find_key("/Title", pairs, count, &title) ||
+ !pdfmark_find_key("/Rect", pairs, count, &rectstr)
+ )
+ return_error(gs_error_rangecheck);
+ if ((code = pdfmark_scan_rect(&rect, &rectstr, pctm)) < 0)
+ return code;
+ bead_id = pdf_obj_ref(pdev);
+
+ /* Find the article with this title, or create one. */
+ for (part = pdev->articles; part != 0; part = part->next) {
+ const cos_value_t *a_title =
+ cos_dict_find_c_key(part->contents, "/Title");
+
+ if (a_title != 0 && !COS_VALUE_IS_OBJECT(a_title) &&
+ !bytes_compare(a_title->contents.chars.data,
+ a_title->contents.chars.size,
+ title.data, title.size))
+ break;
+ }
+ if (part == 0) { /* Create the article. */
+ cos_dict_t *contents =
+ cos_dict_alloc(pdev, "pdfmark_ARTICLE(contents)");
+
+ if (contents == 0)
+ return_error(gs_error_VMerror);
+ part = gs_alloc_struct(mem, pdf_article_t, &st_pdf_article,
+ "pdfmark_ARTICLE(article)");
+ if (part == 0 || contents == 0) {
+ gs_free_object(mem, part, "pdfmark_ARTICLE(article)");
+ if (contents)
+ COS_FREE(contents, "pdfmark_ARTICLE(contents)");
+ return_error(gs_error_VMerror);
+ }
+ contents->id = pdf_obj_ref(pdev);
+ part->next = pdev->articles;
+ pdev->articles = part;
+ cos_dict_put_string(contents, (const byte *)"/Title", 6,
+ title.data, title.size);
+ part->first.id = part->last.id = 0;
+ part->contents = contents;
+ }
+ /*
+ * Add the bead to the article. This is similar to what we do for
+ * outline nodes, except that articles have only a page number and
+ * not View/Dest.
+ */
+ if (part->last.id == 0) {
+ part->first.next_id = bead_id;
+ part->last.id = part->first.id;
+ } else {
+ part->last.next_id = bead_id;
+ pdfmark_write_bead(pdev, &part->last);
+ }
+ part->last.prev_id = part->last.id;
+ part->last.id = bead_id;
+ part->last.article_id = part->contents->id;
+ part->last.next_id = 0;
+ part->last.rect = rect;
+ {
+ gs_param_string page_string;
+ int page = 0;
+ uint i;
+
+ pdfmark_find_key("/Page", pairs, count, &page_string);
+ page = pdfmark_page_number(pdev, &page_string);
+ code = update_max_page_reference(pdev, &page);
+ if (code < 0)
+ return code;
+ part->last.page_id = pdf_page_id(pdev, page);
+ for (i = 0; i < count; i += 2) {
+ if (pdf_key_eq(&pairs[i], "/Rect") || pdf_key_eq(&pairs[i], "/Page"))
+ continue;
+ pdfmark_put_pair(part->contents, &pairs[i]);
+ }
+ }
+ if (part->first.id == 0) { /* This is the first bead of the article. */
+ part->first = part->last;
+ part->last.id = 0;
+ }
+ return 0;
+}
+
+/* EMBED pdfmark */
+static int
+pdfmark_EMBED(gx_device_pdf * pdev, gs_param_string * pairs, uint count,
+ const gs_matrix * pctm, const gs_param_string * objname)
+{
+ gs_param_string key;
+ int i;
+
+ if (pdev->CompatibilityLevel < 1.4)
+ return_error(gs_error_undefined);
+ if (!pdfmark_find_key("/FS", pairs, count, &key))
+ return_error(gs_error_rangecheck);
+ if (!pdfmark_find_key("/Name", pairs, count, &key))
+ return_error(gs_error_rangecheck);
+ if (!pdev->EmbeddedFiles) {
+ pdev->EmbeddedFiles = cos_dict_alloc(pdev, "pdfmark_EMBED(EmbeddedFiles)");
+ if (pdev->EmbeddedFiles == 0)
+ return_error(gs_error_VMerror);
+ pdev->EmbeddedFiles->id = pdf_obj_ref(pdev);
+ }
+
+ for (i = 0; i < count; i += 2) {
+ if (pdf_key_eq(&pairs[i], "/FS")) {
+ return cos_dict_put_string(pdev->EmbeddedFiles, key.data, key.size,
+ pairs[i+1].data, pairs[i+1].size);
+ }
+ }
+ return 0;
+}
+
+/* DEST pdfmark */
+static int
+pdfmark_DEST(gx_device_pdf * pdev, gs_param_string * pairs, uint count,
+ const gs_matrix * pctm, const gs_param_string * objname)
+{
+ int present;
+ char dest[MAX_DEST_STRING];
+ gs_param_string key;
+ cos_value_t value;
+ cos_dict_t *ddict;
+ int i, code;
+
+ if (!pdfmark_find_key("/Dest", pairs, count, &key) ||
+ (present =
+ pdfmark_make_dest(dest, pdev, "/Page", "/View", pairs, count, 1)) < 0
+ )
+ return_error(gs_error_rangecheck);
+ cos_string_value(&value, (byte *)dest, strlen(dest));
+ if (!pdev->Dests) {
+ pdev->Dests = cos_dict_alloc(pdev, "pdfmark_DEST(Dests)");
+ if (pdev->Dests == 0)
+ return_error(gs_error_VMerror);
+ pdev->Dests->id = pdf_obj_ref(pdev);
+ }
+
+ /*
+ * Create the destination as a dictionary with a D key.
+ */
+ code = pdf_make_named_dict(pdev, objname, &ddict, false);
+ ddict->id = pdf_obj_ref(pdev);
+
+ if (code < 0)
+ return code;
+ code = cos_dict_put_c_key_string(ddict, "/D", (byte *)dest,
+ strlen(dest));
+ for (i = 0; code >= 0 && i < count; i += 2)
+ if (!pdf_key_eq(&pairs[i], "/Dest") &&
+ !pdf_key_eq(&pairs[i], "/Page") &&
+ !pdf_key_eq(&pairs[i], "/View")
+ )
+ code = pdfmark_put_pair(ddict, &pairs[i]);
+ if (code < 0)
+ return code;
+ COS_WRITE_OBJECT(ddict, pdev, resourceOther);
+ COS_OBJECT_VALUE(&value, ddict);
+ COS_RELEASE(ddict, "pdfmark_DEST(Dests dict)");
+
+ return cos_dict_put(pdev->Dests, key.data, key.size, &value);
+}
+
+/* Check that pass-through PostScript code is a string. */
+static bool
+ps_source_ok(const gs_memory_t *mem, const gs_param_string * psource)
+{
+ if (psource->size >= 2 && psource->data[0] == '(' &&
+ psource->data[psource->size - 1] == ')'
+ )
+ return true;
+ else {
+ int i;
+ lprintf("bad PS passthrough: ");
+ for (i=0; i<psource->size; i++)
+ errprintf(mem, "%c", psource->data[i]);
+ errprintf(mem, "\n");
+ return false;
+ }
+}
+
+/* Write the contents of pass-through PostScript code. */
+/* Return the size written on the file. */
+static uint
+pdfmark_write_ps(stream *s, const gs_param_string * psource)
+{
+ /****** REMOVE ESCAPES WITH PSSDecode, SEE gdevpdfr p. 2 ******/
+ uint size = psource->size - 2;
+
+ stream_write(s, psource->data + 1, size);
+ stream_putc(s, '\n');
+ return size + 1;
+}
+
+/* Start a XObject. */
+static int
+start_XObject(gx_device_pdf * pdev, bool compress, cos_stream_t **ppcs)
+{ pdf_resource_t *pres;
+ cos_stream_t *pcs;
+ int code;
+
+ code = pdf_open_page(pdev, PDF_IN_STREAM);
+ if (code < 0)
+ return code;
+ code = pdf_enter_substream(pdev, resourceXObject, gs_no_id, &pres, false,
+ pdev->CompressFonts /* Have no better switch*/);
+ if (code < 0)
+ return code;
+ pdev->accumulating_a_global_object = true;
+ pcs = (cos_stream_t *)pres->object;
+ pdev->substream_Resources = cos_dict_alloc(pdev, "start_XObject");
+ if (!pdev->substream_Resources)
+ return_error(gs_error_VMerror);
+ if (pdev->ForOPDFRead) {
+ code = cos_dict_put_c_key_bool((cos_dict_t *)pres->object, "/.Global", true);
+ if (code < 0)
+ return code;
+ }
+ pres->named = true;
+ pres->where_used = 0; /* initially not used */
+ pcs->pres = pres;
+ *ppcs = pcs;
+ return 0;
+}
+
+/* PS pdfmark */
+#define MAX_PS_INLINE 100
+static int
+pdfmark_PS(gx_device_pdf * pdev, gs_param_string * pairs, uint count,
+ const gs_matrix * pctm, const gs_param_string * objname)
+{
+ gs_param_string source;
+ gs_param_string level1;
+
+ if (!pdfmark_find_key("/DataSource", pairs, count, &source) ||
+ !ps_source_ok(pdev->memory, &source) ||
+ (pdfmark_find_key("/Level1", pairs, count, &level1) &&
+ !ps_source_ok(pdev->memory, &level1))
+ )
+ return_error(gs_error_rangecheck);
+ if (level1.data == 0 && source.size <= MAX_PS_INLINE && objname == 0) {
+ /* Insert the PostScript code in-line */
+ int code = pdf_open_contents(pdev, PDF_IN_STREAM);
+ stream *s;
+
+ if (code < 0)
+ return code;
+ s = pdev->strm;
+ stream_write(s, source.data, source.size);
+ stream_puts(s, " PS\n");
+ } else {
+ /* Put the PostScript code in a resource. */
+ cos_stream_t *pcs;
+ int code;
+ gs_id level1_id = gs_no_id;
+ pdf_resource_t *pres;
+
+ if (level1.data != 0) {
+ pdf_resource_t *pres;
+
+ code = pdf_enter_substream(pdev,
+ resourceXObject,
+ gs_no_id, &pres, true,
+ pdev->CompressFonts /* Have no better switch*/);
+ if (code < 0)
+ return code;
+ pcs = (cos_stream_t *)pres->object;
+ if (pdev->ForOPDFRead && objname != 0) {
+ code = cos_dict_put_c_key_bool((cos_dict_t *)pres->object, "/.Global", true);
+ if (code < 0)
+ return code;
+ }
+ pres->named = (objname != 0);
+ pres->where_used = 0;
+ pcs->pres = pres;
+ DISCARD(pdfmark_write_ps(pdev->strm, &level1));
+ code = pdf_exit_substream(pdev);
+ if (code < 0)
+ return code;
+ code = cos_write_object(pres->object, pdev, resourceOther);
+ if (code < 0)
+ return code;
+ level1_id = pres->object->id;
+ }
+ code = start_XObject(pdev, pdev->params.CompressPages, &pcs);
+ if (code < 0)
+ return code;
+ pres = pdev->accumulating_substream_resource;
+ code = cos_stream_put_c_strings(pcs, "/Type", "/XObject");
+ if (code < 0)
+ return code;
+ code = cos_stream_put_c_strings(pcs, "/Subtype", "/PS");
+ if (code < 0)
+ return code;
+ if (level1_id != gs_no_id) {
+ char r[MAX_DEST_STRING];
+
+ gs_sprintf(r, "%ld 0 R", level1_id);
+ code = cos_dict_put_c_key_string(cos_stream_dict(pcs), "/Level1",
+ (byte *)r, strlen(r));
+ if (code < 0)
+ return code;
+ }
+ DISCARD(pdfmark_write_ps(pdev->strm, &source));
+ code = pdf_exit_substream(pdev);
+ if (code < 0)
+ return code;
+ { gs_const_string objname1, *pon = NULL;
+
+ if (objname != NULL) {
+ objname1.data = objname->data;
+ objname1.size = objname->size;
+ pon = &objname1;
+ }
+ code = pdfmark_bind_named_object(pdev, pon, &pres);
+ if (code < 0)
+ return code;
+ }
+ code = pdf_open_contents(pdev, PDF_IN_STREAM);
+ if (code < 0)
+ return code;
+ pcs->pres->where_used |= pdev->used_mask;
+ pprintld1(pdev->strm, "/R%ld Do\n", pcs->id);
+ }
+ return 0;
+}
+
+/* Common code for pdfmarks that do PUT into a specific object. */
+static int
+pdfmark_put_pairs(cos_dict_t *pcd, gs_param_string * pairs, uint count)
+{
+ int code = 0, i;
+
+ if (count & 1)
+ return_error(gs_error_rangecheck);
+ for (i = 0; code >= 0 && i < count; i += 2)
+ code = pdfmark_put_pair(pcd, pairs + i);
+ return code;
+}
+
+/* PAGES pdfmark */
+static int
+pdfmark_PAGES(gx_device_pdf * pdev, gs_param_string * pairs, uint count,
+ const gs_matrix * pctm, const gs_param_string * no_objname)
+{
+ return pdfmark_put_pairs(pdev->Pages, pairs, count);
+}
+
+/* PAGE pdfmark */
+static int
+pdfmark_PAGE(gx_device_pdf * pdev, gs_param_string * pairs, uint count,
+ const gs_matrix * pctm, const gs_param_string * no_objname)
+{
+ return pdfmark_put_pairs(pdf_current_page_dict(pdev), pairs, count);
+}
+
+/* Add a page label for the current page. The last label on a page
+ * overrides all previous labels for this page. Unlabeled pages will get
+ * empty page labels. label == NULL flushes the last label */
+static int
+pdfmark_add_pagelabel(gx_device_pdf * pdev, const gs_param_string *label)
+{
+ cos_value_t value;
+ cos_dict_t *dict = 0;
+ int code = 0;
+
+ /* create label dict (and page label array if not present yet) */
+ if (label != 0) {
+ if (!pdev->PageLabels) {
+ pdev->PageLabels = cos_array_alloc(pdev,
+ "pdfmark_add_pagelabel(PageLabels)");
+ if (pdev->PageLabels == 0)
+ return_error(gs_error_VMerror);
+ pdev->PageLabels->id = pdf_obj_ref(pdev);
+
+ /* empty label for unlabled pages before first labled page */
+ pdev->PageLabels_current_page = 0;
+ pdev->PageLabels_current_label = cos_dict_alloc(pdev,
+ "pdfmark_add_pagelabel(first)");
+ if (pdev->PageLabels_current_label == 0)
+ return_error(gs_error_VMerror);
+ }
+
+ dict = cos_dict_alloc(pdev, "pdfmark_add_pagelabel(dict)");
+ if (dict == 0)
+ return_error(gs_error_VMerror);
+
+ code = cos_dict_put_c_key(dict, "/P", cos_string_value(&value,
+ label->data, label->size));
+ if (code < 0) {
+ COS_FREE(dict, "pdfmark_add_pagelabel(dict)");
+ return code;
+ }
+ }
+
+ /* flush current label */
+ if (label == 0 || pdev->next_page != pdev->PageLabels_current_page) {
+ /* handle current label */
+ if (pdev->PageLabels_current_label) {
+ if (code >= 0) {
+ code = cos_array_add_int(pdev->PageLabels,
+ pdev->PageLabels_current_page);
+ if (code >= 0)
+ code = cos_array_add(pdev->PageLabels,
+ COS_OBJECT_VALUE(&value,
+ pdev->PageLabels_current_label));
+ }
+ pdev->PageLabels_current_label = 0;
+ }
+
+ /* handle unlabled pages between current labeled page and
+ * next labeled page */
+ if (pdev->PageLabels) {
+ if (pdev->next_page - pdev->PageLabels_current_page > 1) {
+ cos_dict_t *tmp = cos_dict_alloc(pdev,
+ "pdfmark_add_pagelabel(tmp)");
+ if (tmp == 0)
+ return_error(gs_error_VMerror);
+
+ code = cos_array_add_int(pdev->PageLabels,
+ pdev->PageLabels_current_page + 1);
+ if (code >= 0)
+ code = cos_array_add(pdev->PageLabels,
+ COS_OBJECT_VALUE(&value, tmp));
+ }
+ }
+ }
+
+ /* new current label */
+ if (pdev->PageLabels_current_label)
+ COS_FREE(pdev->PageLabels_current_label,
+ "pdfmark_add_pagelabel(current_label)");
+ pdev->PageLabels_current_label = dict;
+ pdev->PageLabels_current_page = pdev->next_page;
+
+ return code;
+}
+
+/* Close the pagelabel numtree.*/
+int
+pdfmark_end_pagelabels(gx_device_pdf * pdev)
+{
+ return pdfmark_add_pagelabel(pdev, 0);
+}
+
+/* [ /Label string /PlateColor string pdfmark */
+/* FIXME: /PlateColor is ignored */
+static int
+pdfmark_PAGELABEL(gx_device_pdf * pdev, gs_param_string * pairs, uint count,
+ const gs_matrix * pctm, const gs_param_string * no_objname)
+{
+ gs_param_string key;
+
+ if (pdev->CompatibilityLevel >= 1.3) {
+ if (pdfmark_find_key("/Label", pairs, count, &key)) {
+ return pdfmark_add_pagelabel(pdev, &key);
+ }
+ }
+ return 0;
+}
+
+/* DOCINFO pdfmark */
+static int
+pdfmark_DOCINFO(gx_device_pdf * pdev, gs_param_string * pairs, uint count,
+ const gs_matrix * pctm, const gs_param_string * no_objname)
+{
+ /*
+ * We could use pdfmark_put_pairs(pdev->Info, pairs, count), except
+ * that we want to replace "Distiller" with our own name as the
+ * Producer.
+ */
+ cos_dict_t *const pcd = pdev->Info;
+ int code = 0, i;
+ gs_memory_t *mem = pdev->pdf_memory;
+
+ if (count & 1)
+ return_error(gs_error_rangecheck);
+ for (i = 0; code >= 0 && i < count; i += 2) {
+ const gs_param_string *pair = pairs + i;
+ gs_param_string alt_pair[2];
+ const byte *vdata; /* alt_pair[1].data */
+ uint vsize; /* alt_pair[1].size */
+ byte *str = 0;
+
+ vsize = 0x0badf00d; /* Quiet compiler. */
+
+ if (pdf_key_eq(pairs + i, "/Producer")) {
+ /*
+ * If the string "Distiller" appears anywhere in the Producer,
+ * replace the Producer (or the part after a " + ") with our
+ * own name.
+ */
+ string_match_params params;
+
+ memcpy(alt_pair, pairs + i, sizeof(alt_pair));
+ vdata = alt_pair[1].data;
+ vsize = alt_pair[1].size;
+ params = string_match_params_default;
+ params.ignore_case = true;
+ if (string_match(vdata, vsize, (const byte *)"*Distiller*",
+ 11, &params) ||
+ string_match(vdata, vsize,
+ (const byte *)"*\000D\000i\000s\000t\000i\000l\000l\000e\000r*",
+ 20, &params)
+ ) {
+ uint j;
+ char buf[PDF_MAX_PRODUCER];
+ int len;
+
+ for (j = vsize; j > 0 && vdata[--j] != '+'; )
+ DO_NOTHING;
+ if (vsize - j > 2 && vdata[j] == '+') {
+ ++j;
+ while (j < vsize && vdata[j] == ' ')
+ ++j;
+ }
+ /*
+ * Replace vdata[j .. vsize) with our name. Note that both
+ * vdata/vstr and the default producer string are enclosed
+ * in ().
+ */
+ pdf_store_default_Producer(buf);
+ len = strlen(buf) - 1;
+ str = gs_alloc_string(mem, j + len, "Producer");
+ if (str == 0)
+ return_error(gs_error_VMerror);
+ memcpy(str, vdata, j);
+ memcpy(str + j, buf + 1, len);
+ alt_pair[1].data = str;
+ alt_pair[1].size = vsize = j + len;
+ pair = alt_pair;
+ }
+ }
+ code = pdfmark_put_pair(pcd, pair);
+ if (str)
+ gs_free_string(mem, str, vsize, "Producer");
+ }
+ return code;
+}
+
+/* DOCVIEW pdfmark */
+static int
+pdfmark_DOCVIEW(gx_device_pdf * pdev, gs_param_string * pairs, uint count,
+ const gs_matrix * pctm, const gs_param_string * no_objname)
+{
+ char dest[MAX_DEST_STRING];
+ int code = 0;
+
+ if (count & 1)
+ return_error(gs_error_rangecheck);
+ code = pdfmark_make_dest(dest, pdev, "/Page", "/View", pairs, count, 0);
+ if (code < 0)
+ return gs_note_error(gs_error_rangecheck);
+
+ if (code > 0) {
+ int i;
+
+ code = cos_dict_put_c_key_string(pdev->Catalog, "/OpenAction",
+ (byte *)dest, strlen(dest));
+ for (i = 0; code >= 0 && i < count; i += 2)
+ if (!(pdf_key_eq(&pairs[i], "/Page") ||
+ pdf_key_eq(&pairs[i], "/View"))
+ )
+ code = pdfmark_put_pair(pdev->Catalog, pairs + i);
+ return code;
+ } else
+ return pdfmark_put_pairs(pdev->Catalog, pairs, count);
+}
+
+/* ---------------- Named object pdfmarks ---------------- */
+
+/* [ /BBox [llx lly urx ury] /_objdef {obj} /BP pdfmark */
+static int
+pdfmark_BP(gx_device_pdf * pdev, gs_param_string * pairs, uint count,
+ const gs_matrix * pctm, const gs_param_string * objname)
+{
+ gs_rect bbox;
+ cos_stream_t *pcs;
+ int code;
+ gs_matrix ictm;
+ byte bbox_str[6 + 6 * 15], matrix_str[6 + 6 * 15];
+ char chars[MAX_RECT_STRING + 1];
+ int bbox_str_len, matrix_str_len;
+ stream s;
+
+ if (objname == 0 || count != 2 || !pdf_key_eq(&pairs[0], "/BBox"))
+ return_error(gs_error_rangecheck);
+ code = gs_matrix_invert(pctm, &ictm);
+ if (code < 0)
+ return code;
+ if (pairs[1].size > MAX_RECT_STRING)
+ return_error(gs_error_limitcheck);
+ memcpy(chars, pairs[1].data, pairs[1].size);
+ chars[pairs[1].size] = 0;
+ if (sscanf(chars, "[%lg %lg %lg %lg]",
+ &bbox.p.x, &bbox.p.y, &bbox.q.x, &bbox.q.y) != 4)
+ return_error(gs_error_rangecheck);
+ if ((pdev->used_mask << 1) == 0)
+ return_error(gs_error_limitcheck);
+ code = start_XObject(pdev, pdev->params.CompressPages, &pcs);
+ if (code < 0)
+ return code;
+ { byte *s = gs_alloc_string(pdev->memory, objname->size, "pdfmark_PS");
+
+ if (s == NULL)
+ return_error(gs_error_VMerror);
+ memcpy(s, objname->data, objname->size);
+ pdev->objname.data = s;
+ pdev->objname.size = objname->size;
+ }
+ pcs->is_graphics = true;
+ gs_bbox_transform(&bbox, pctm, &bbox);
+ s_init(&s, NULL);
+ swrite_string(&s, bbox_str, sizeof(bbox_str));
+ pprintg4(&s, "[%g %g %g %g]",
+ bbox.p.x, bbox.p.y, bbox.q.x, bbox.q.y);
+ bbox_str_len = stell(&s);
+ swrite_string(&s, matrix_str, sizeof(bbox_str));
+ pprintg6(&s, "[%g %g %g %g %g %g]",
+ ictm.xx, ictm.xy, ictm.yx, ictm.yy, ictm.tx, ictm.ty);
+ matrix_str_len = stell(&s);
+ if ((code = cos_stream_put_c_strings(pcs, "/Type", "/XObject")) < 0 ||
+ (code = cos_stream_put_c_strings(pcs, "/Subtype", "/Form")) < 0 ||
+ (code = cos_stream_put_c_strings(pcs, "/FormType", "1")) < 0 ||
+ (code = cos_dict_put_c_key_string(cos_stream_dict(pcs), "/BBox",
+ bbox_str, bbox_str_len)) < 0 ||
+ (code = cos_dict_put_c_key_string(cos_stream_dict(pcs), "/Matrix",
+ matrix_str, matrix_str_len)) < 0 ||
+ (code = cos_dict_put_c_key_object(cos_stream_dict(pcs), "/Resources",
+ COS_OBJECT(pdev->substream_Resources))) < 0
+ )
+ return code;
+ /* Don't add to local_named_objects untill the object is created
+ to prevent pending references, which may appear
+ if /PUT pdfmark executes before pdf_substitute_resource in pdfmark_EP
+ drops this object.
+ */
+ pdev->FormDepth++;
+ return 0;
+}
+
+/* [ /EP pdfmark */
+static int
+pdfmark_EP(gx_device_pdf * pdev, gs_param_string * pairs, uint count,
+ const gs_matrix * pctm, const gs_param_string * no_objname)
+{
+ int code;
+ pdf_resource_t *pres = pdev->accumulating_substream_resource;
+ gs_const_string objname = pdev->objname;
+
+ code = pdf_add_procsets(pdev->substream_Resources, pdev->procsets);
+ if (code < 0)
+ return code;
+ code = pdf_exit_substream(pdev);
+ if (code < 0)
+ return code;
+ code = pdfmark_bind_named_object(pdev, &objname, &pres);
+ if (code < 0)
+ return 0;
+ gs_free_const_string(pdev->memory, objname.data, objname.size, "pdfmark_EP");
+ pdev->FormDepth--;
+ return 0;
+}
+
+/* [ {obj} /SP pdfmark */
+static int
+pdfmark_SP(gx_device_pdf * pdev, gs_param_string * pairs, uint count,
+ const gs_matrix * pctm, const gs_param_string * no_objname)
+{
+ cos_object_t *pco; /* stream */
+ int code;
+
+ if (count != 1)
+ return_error(gs_error_rangecheck);
+ if ((code = pdf_get_named(pdev, &pairs[0], cos_type_stream, &pco)) < 0)
+ return code;
+ if (pco->is_open || !pco->is_graphics)
+ return_error(gs_error_rangecheck);
+ code = pdf_open_contents(pdev, PDF_IN_STREAM);
+ if (code < 0)
+ return code;
+ pdf_put_matrix(pdev, "q ", pctm, "cm");
+ pprintld1(pdev->strm, "/R%ld Do Q\n", pco->id);
+ pco->pres->where_used |= pdev->used_mask;
+
+ code = pdf_add_resource(pdev, pdev->substream_Resources, "/XObject", pco->pres);
+ if (code < 0)
+ return code;
+
+ return 0;
+}
+
+/* [ /_objdef {array} /type /array /OBJ pdfmark */
+/* [ /_objdef {dict} /type /dict /OBJ pdfmark */
+/* [ /_objdef {stream} /type /stream /OBJ pdfmark */
+static int
+pdfmark_OBJ(gx_device_pdf * pdev, gs_param_string * pairs, uint count,
+ const gs_matrix * pctm, const gs_param_string * objname)
+{
+ cos_type_t cotype;
+ cos_object_t *pco;
+ bool stream = false;
+ int code;
+
+ if (objname == 0 || count != 2 || !pdf_key_eq(&pairs[0], "/type"))
+ return_error(gs_error_rangecheck);
+ if (pdf_key_eq(&pairs[1], "/array"))
+ cotype = cos_type_array;
+ else if (pdf_key_eq(&pairs[1], "/dict"))
+ cotype = cos_type_dict;
+ else if ((stream = pdf_key_eq(&pairs[1], "/stream")))
+ cotype = cos_type_stream;
+ else
+ return_error(gs_error_rangecheck);
+ if ((code = pdf_make_named(pdev, objname, cotype, &pco, true)) < 0) {
+ /*
+ * For Distiller compatibility, allows multiple /OBJ pdfmarks with
+ * the same name and type, even though the pdfmark specification
+ * doesn't say anything about this being legal.
+ */
+ if (code == gs_error_rangecheck &&
+ pdf_refer_named(pdev, objname, &pco) >= 0 &&
+ cos_type(pco) == cotype
+ )
+ return 0; /* already exists, but OK */
+ return code;
+ }
+ if (stream)
+ return setup_pdfmark_stream_compression((gx_device_psdf *)pdev,
+ (cos_stream_t *)pco);
+ return 0;
+}
+
+/* [ {array} index value /PUT pdfmark */
+/* Dictionaries are converted to .PUTDICT */
+/* Streams are converted to .PUTSTREAM */
+static int
+pdfmark_PUT(gx_device_pdf * pdev, gs_param_string * pairs, uint count,
+ const gs_matrix * pctm, const gs_param_string * no_objname)
+{
+ cos_object_t *pco;
+ cos_value_t value;
+ int code, index;
+
+ if (count != 3)
+ return_error(gs_error_rangecheck);
+ if ((code = pdf_get_named(pdev, &pairs[0], cos_type_array, &pco)) < 0)
+ return code;
+ if ((code = pdfmark_scan_int(&pairs[1], &index)) < 0)
+ return code;
+ if (index < 0)
+ return_error(gs_error_rangecheck);
+ if (pco->written)
+ return_error(gs_error_rangecheck);
+ return cos_array_put((cos_array_t *)pco, index,
+ cos_string_value(&value, pairs[2].data, pairs[2].size));
+}
+
+/* [ {dict} key value ... /.PUTDICT pdfmark */
+/* [ {stream} key value ... /.PUTDICT pdfmark */
+/*
+ * Adobe's pdfmark documentation doesn't allow PUTDICT with a stream,
+ * but it's reasonable and unambiguous, and Acrobat Distiller accepts it,
+ * so we do too.
+ */
+static int
+pdfmark_PUTDICT(gx_device_pdf * pdev, gs_param_string * pairs, uint count,
+ const gs_matrix * pctm, const gs_param_string * no_objname)
+{
+ cos_object_t *pco;
+ int code;
+
+ if ((code = pdf_refer_named(pdev, &pairs[0], &pco)) < 0)
+ return code;
+ if (cos_type(pco) != cos_type_dict && cos_type(pco) != cos_type_stream)
+ return_error(gs_error_typecheck);
+ if (pco->written)
+ return_error(gs_error_rangecheck);
+ return pdfmark_put_pairs((cos_dict_t *)pco, pairs + 1, count - 1);
+}
+
+/* [ {stream} string ... /.PUTSTREAM pdfmark */
+static int
+pdfmark_PUTSTREAM(gx_device_pdf * pdev, gs_param_string * pairs, uint count,
+ const gs_matrix * pctm, const gs_param_string * no_objname)
+{
+ cos_object_t *pco;
+ int code, i;
+ uint l;
+
+ if (count < 2)
+ return_error(gs_error_rangecheck);
+ if ((code = pdf_get_named(pdev, &pairs[0], cos_type_stream, &pco)) < 0)
+ return code;
+ if (!pco->is_open)
+ return_error(gs_error_rangecheck);
+ for (i = 1; i < count; ++i)
+ if (sputs(pco->input_strm, pairs[i].data, pairs[i].size, &l) != 0)
+ return_error(gs_error_ioerror);
+ if (pco->written)
+ return_error(gs_error_rangecheck);
+ return code;
+}
+
+/* [ {array} value /APPEND pdfmark */
+static int
+pdfmark_APPEND(gx_device_pdf * pdev, gs_param_string * pairs, uint count,
+ const gs_matrix * pctm, const gs_param_string * objname)
+{
+ cos_object_t *pco;
+ cos_value_t value;
+ int code;
+
+ if (count != 2)
+ return_error(gs_error_rangecheck);
+ if ((code = pdf_get_named(pdev, &pairs[0], cos_type_array, &pco)) < 0)
+ return code;
+ return cos_array_add((cos_array_t *)pco,
+ cos_string_value(&value, pairs[1].data, pairs[1].size));
+}
+
+/* [ {array} index value ... /.PUTINTERVAL pdfmark */
+static int
+pdfmark_PUTINTERVAL(gx_device_pdf * pdev, gs_param_string * pairs, uint count,
+ const gs_matrix * pctm, const gs_param_string * no_objname)
+{
+ cos_object_t *pco;
+ cos_value_t value;
+ int code, index, i;
+
+ if (count < 2)
+ return_error(gs_error_rangecheck);
+ if ((code = pdf_get_named(pdev, &pairs[0], cos_type_array, &pco)) < 0)
+ return code;
+ if ((code = pdfmark_scan_int(&pairs[1], &index)) < 0)
+ return code;
+ if (index < 0)
+ return_error(gs_error_rangecheck);
+ for (i = 2; code >= 0 && i < count; ++i)
+ code = cos_array_put((cos_array_t *)pco, index + i - 2,
+ cos_string_value(&value, pairs[i].data, pairs[i].size));
+ return code;
+}
+
+/* [ {stream} /CLOSE pdfmark */
+static int
+pdfmark_CLOSE(gx_device_pdf * pdev, gs_param_string * pairs, uint count,
+ const gs_matrix * pctm, const gs_param_string * no_objname)
+{
+ cos_object_t *pco;
+ int code;
+
+ if (count != 1)
+ return_error(gs_error_rangecheck);
+ if ((code = pdf_get_named(pdev, &pairs[0], cos_type_stream, &pco)) < 0)
+ return code;
+ if (!pco->is_open)
+ return_error(gs_error_rangecheck);
+ /* Currently we don't do anything special when closing a stream. */
+ pco->is_open = false;
+ return 0;
+}
+
+/* [ /NamespacePush pdfmark */
+static int
+pdfmark_NamespacePush(gx_device_pdf *pdev, gs_param_string *pairs, uint count,
+ const gs_matrix *pctm, const gs_param_string *objname)
+{
+ if (count != 0)
+ return_error(gs_error_rangecheck);
+ return pdf_push_namespace(pdev);
+}
+
+/* [ /NamespacePop pdfmark */
+static int
+pdfmark_NamespacePop(gx_device_pdf *pdev, gs_param_string *pairs, uint count,
+ const gs_matrix *pctm, const gs_param_string *objname)
+{
+ if (count != 0)
+ return_error(gs_error_rangecheck);
+ cos_dict_objects_write(pdev->local_named_objects, pdev);
+ return pdf_pop_namespace(pdev);
+}
+
+/* [ /_objdef {image} /NI pdfmark */
+static int
+pdfmark_NI(gx_device_pdf *pdev, gs_param_string *pairs, uint count,
+ const gs_matrix *pctm, const gs_param_string *objname)
+{
+ cos_object_t *pco;
+ int code;
+
+ if (objname == 0 || count != 0)
+ return_error(gs_error_rangecheck);
+ code = pdf_make_named(pdev, objname, cos_type_dict, &pco, true);
+ if (code < 0)
+ return code;
+ return cos_array_add_object(pdev->NI_stack, pco);
+}
+
+/* ---------------- Named content pdfmarks ---------------- */
+
+/* [ tag /MP pdfmark */
+static int
+pdfmark_MP(gx_device_pdf *pdev, gs_param_string *pairs, uint count,
+ const gs_matrix *pctm, const gs_param_string *objname)
+{
+ return 0; /****** NOT IMPLEMENTED YET ******/
+}
+
+/* [ tag propdict /DP pdfmark */
+static int
+pdfmark_DP(gx_device_pdf *pdev, gs_param_string *pairs, uint count,
+ const gs_matrix *pctm, const gs_param_string *objname)
+{
+ return 0; /****** NOT IMPLEMENTED YET ******/
+}
+
+/* [ tag /BMC pdfmark */
+static int
+pdfmark_BMC(gx_device_pdf *pdev, gs_param_string *pairs, uint count,
+ const gs_matrix *pctm, const gs_param_string *objname)
+{
+ int code;
+ char *tag;
+
+ if (count != 1) return_error(gs_error_rangecheck);
+
+ tag = (char *)gs_alloc_bytes(pdev->memory, (pairs[0].size + 1) * sizeof(unsigned char),
+ "pdfmark_BMC");
+ memcpy(tag, pairs[0].data, pairs[0].size);
+ tag[pairs[0].size] = 0x00;
+
+ code = pdf_open_contents(pdev, PDF_IN_STREAM);
+ if (code < 0) return code;
+
+ pprints1(pdev->strm, "%s", tag);
+
+ gs_free_object(pdev->memory, tag, "pdfmark_BMC");
+ return 0;
+}
+
+/* [ tag propdict /BDC pdfmark */
+static int
+pdfmark_BDC(gx_device_pdf *pdev, gs_param_string *pairs, uint count,
+ const gs_matrix *pctm, const gs_param_string *objname)
+{
+ int code;
+ cos_object_t *pco;
+ char *cstring;
+ pdf_resource_t *pres;
+
+ if (count != 2) return_error(gs_error_rangecheck);
+
+ /* check tag for /Name object syntax */
+ if ((pairs[0].data)[0] != '/') return_error(gs_error_rangecheck);
+
+ /* check propdict for {object name} syntax */
+ if (pdf_objname_is_valid(pairs[1].data, pairs[1].size))
+ {
+ code = pdf_refer_named(pdev, &pairs[1], &pco);
+ if(code < 0) return code;
+ }
+ else /* << inline prop dict >> */
+ {
+ /* strip << and >> */
+ if ((pairs[1].data)[0]=='<'&&(pairs[1].data)[1]=='<')
+ {
+ pairs[1].data=&(pairs[1].data[2]);
+ pairs[1].size-=2;
+ }
+ else
+ return_error(gs_error_rangecheck);
+
+ if ((pairs[1].data)[pairs[1].size-1]=='>'&&(pairs[1].data)[pairs[1].size-2]=='>')
+ pairs[1].size-=2;
+
+ /* convert inline propdict to C string with object names replaced by refs */
+ code = pdf_replace_names(pdev, &pairs[1], &pairs[1]);
+ if (code<0) return code;
+ cstring = (char *)gs_alloc_bytes(pdev->memory, (pairs[1].size + 1) * sizeof(unsigned char),
+ "pdfmark_BDC");
+ memcpy(cstring, pairs[1].data, pairs[1].size);
+ cstring[pairs[1].size] = 0x00;
+
+ code = pdf_make_named_dict(pdev, NULL, (cos_dict_t**) &pco, true);
+ if (code<0) return code;
+
+ /* copy inline propdict to new object */
+ code = cos_dict_put_c_strings((cos_dict_t*) pco, cstring, "");
+ if(code < 0) return code;
+ COS_WRITE_OBJECT(pco, pdev, resourceProperties);
+ COS_RELEASE(pco, "pdfmark_BDC");
+ gs_free_object(pdev->memory, cstring, "pdfmark_BDC");
+ }
+
+ pres = pdf_find_resource_by_resource_id(pdev, resourceProperties, pco->id);
+ if (pres==0){
+ if ((code = pdf_alloc_resource(pdev, resourceProperties, pco->id, &(pco->pres), pco->id))<0)
+ return code;
+ }
+
+ cstring = (char *)gs_alloc_bytes(pdev->memory, (pairs[0].size + 1) * sizeof(unsigned char),
+ "pdfmark_BDC");
+ memcpy(cstring, pairs[0].data, pairs[0].size);
+ cstring[pairs[0].size] = 0x00;
+
+ /* make sure we write to the correct stream */
+ code = pdf_open_contents(pdev, PDF_IN_STREAM);
+ if (code < 0) return code;
+
+ pprints1(pdev->strm, "%s", cstring); /* write tag */
+ pprintld1(pdev->strm, "/R%ld BDC\n", pco->id);
+ pco->pres->where_used |= pdev->used_mask;
+ if ((code = pdf_add_resource(pdev, pdev->substream_Resources, "/Properties", pco->pres))<0)
+ return code;
+
+ gs_free_object(pdev->memory, cstring, "pdfmark_BDC");
+ return 0;
+}
+
+/* [ /EMC pdfmark */
+static int
+pdfmark_EMC(gx_device_pdf *pdev, gs_param_string *pairs, uint count,
+ const gs_matrix *pctm, const gs_param_string *objname)
+{
+ int code;
+
+ code = pdf_open_contents(pdev, PDF_IN_STREAM);
+ if (code < 0) return code;
+ stream_puts(pdev->strm, "EMC\n");
+
+ return 0;
+}
+
+/* ---------------- Document structure pdfmarks ---------------- */
+
+/* [ newsubtype1 stdsubtype1 ... /StRoleMap pdfmark */
+static int
+pdfmark_StRoleMap(gx_device_pdf *pdev, gs_param_string *pairs, uint count,
+ const gs_matrix *pctm, const gs_param_string *objname)
+{
+ return 0; /****** NOT IMPLEMENTED YET ******/
+}
+
+/* [ class1 {attrobj1} ... /StClassMap pdfmark */
+static int
+pdfmark_StClassMap(gx_device_pdf *pdev, gs_param_string *pairs, uint count,
+ const gs_matrix *pctm, const gs_param_string *objname)
+{
+ return 0; /****** NOT IMPLEMENTED YET ******/
+}
+
+/*
+ * [ [/_objdef {objname}] /Subtype name [/Title string] [/Alt string]
+ * [/ID string] [/Class name] [/At index] [/Bookmark dict] [action_pairs...]
+ * /StPNE pdfmark
+ */
+static int
+pdfmark_StPNE(gx_device_pdf *pdev, gs_param_string *pairs, uint count,
+ const gs_matrix *pctm, const gs_param_string *objname)
+{
+ return 0; /****** NOT IMPLEMENTED YET ******/
+}
+
+/* [ [/Title string] [/Open bool] [action_pairs...] /StBookmarkRoot pdfmark */
+static int
+pdfmark_StBookmarkRoot(gx_device_pdf *pdev, gs_param_string *pairs, uint count,
+ const gs_matrix *pctm, const gs_param_string *objname)
+{
+ return 0; /****** NOT IMPLEMENTED YET ******/
+}
+
+/* [ [/E {elt}] /StPush pdfmark */
+static int
+pdfmark_StPush(gx_device_pdf *pdev, gs_param_string *pairs, uint count,
+ const gs_matrix *pctm, const gs_param_string *objname)
+{
+ return 0; /****** NOT IMPLEMENTED YET ******/
+}
+
+/* [ /StPop pdfmark */
+static int
+pdfmark_StPop(gx_device_pdf *pdev, gs_param_string *pairs, uint count,
+ const gs_matrix *pctm, const gs_param_string *objname)
+{
+ return 0; /****** NOT IMPLEMENTED YET ******/
+}
+
+/* [ /StPopAll pdfmark */
+static int
+pdfmark_StPopAll(gx_device_pdf *pdev, gs_param_string *pairs, uint count,
+ const gs_matrix *pctm, const gs_param_string *objname)
+{
+ return 0; /****** NOT IMPLEMENTED YET ******/
+}
+
+/* [ [/T tagname] [/At index] /StBMC pdfmark */
+static int
+pdfmark_StBMC(gx_device_pdf *pdev, gs_param_string *pairs, uint count,
+ const gs_matrix *pctm, const gs_param_string *objname)
+{
+ return 0; /****** NOT IMPLEMENTED YET ******/
+}
+
+/* [ [/P propdict] [/T tagname] [/At index] /StBDC pdfmark */
+static int
+pdfmark_StBDC(gx_device_pdf *pdev, gs_param_string *pairs, uint count,
+ const gs_matrix *pctm, const gs_param_string *objname)
+{
+ return 0; /****** NOT IMPLEMENTED YET ******/
+}
+
+/* [ /Obj {obj} [/At index] /StOBJ pdfmark */
+static int
+pdfmark_StOBJ(gx_device_pdf *pdev, gs_param_string *pairs, uint count,
+ const gs_matrix *pctm, const gs_param_string *objname)
+{
+ return 0; /****** NOT IMPLEMENTED YET ******/
+}
+
+/* [ /Obj {obj} /StAttr pdfmark */
+static int
+pdfmark_StAttr(gx_device_pdf *pdev, gs_param_string *pairs, uint count,
+ const gs_matrix *pctm, const gs_param_string *objname)
+{
+ return 0; /****** NOT IMPLEMENTED YET ******/
+}
+
+/* [ /StoreName name /StStore pdfmark */
+static int
+pdfmark_StStore(gx_device_pdf *pdev, gs_param_string *pairs, uint count,
+ const gs_matrix *pctm, const gs_param_string *objname)
+{
+ return 0; /****** NOT IMPLEMENTED YET ******/
+}
+
+/* [ /StoreName name /StRetrieve pdfmark */
+static int
+pdfmark_StRetrieve(gx_device_pdf *pdev, gs_param_string *pairs, uint count,
+ const gs_matrix *pctm, const gs_param_string *objname)
+{
+ return 0; /****** NOT IMPLEMENTED YET ******/
+}
+
+/* ---------------- Dispatch ---------------- */
+
+/*
+ * Define the pdfmark types we know about.
+ */
+static const pdfmark_name mark_names[] =
+{
+ /* Miscellaneous. */
+ {"ANN", pdfmark_ANN, PDFMARK_NAMEABLE},
+ {"LNK", pdfmark_LNK, PDFMARK_NAMEABLE},
+ {"OUT", pdfmark_OUT, 0},
+ {"ARTICLE", pdfmark_ARTICLE, 0},
+ {"DEST", pdfmark_DEST, PDFMARK_NAMEABLE},
+ {"EMBED", pdfmark_EMBED, PDFMARK_NAMEABLE},
+ {"PS", pdfmark_PS, PDFMARK_NAMEABLE},
+ {"PAGES", pdfmark_PAGES, 0},
+ {"PAGE", pdfmark_PAGE, 0},
+ {"PAGELABEL", pdfmark_PAGELABEL, 0},
+ {"DOCINFO", pdfmark_DOCINFO, 0},
+ {"DOCVIEW", pdfmark_DOCVIEW, 0},
+ /* Named objects. */
+ {"BP", pdfmark_BP, PDFMARK_NAMEABLE | PDFMARK_TRUECTM},
+ {"EP", pdfmark_EP, 0},
+ {"SP", pdfmark_SP, PDFMARK_ODD_OK | PDFMARK_KEEP_NAME | PDFMARK_TRUECTM},
+ {"OBJ", pdfmark_OBJ, PDFMARK_NAMEABLE},
+ {"PUT", pdfmark_PUT, PDFMARK_ODD_OK | PDFMARK_KEEP_NAME},
+ {".PUTDICT", pdfmark_PUTDICT, PDFMARK_ODD_OK | PDFMARK_KEEP_NAME},
+ {".PUTINTERVAL", pdfmark_PUTINTERVAL, PDFMARK_ODD_OK | PDFMARK_KEEP_NAME},
+ {".PUTSTREAM", pdfmark_PUTSTREAM, PDFMARK_ODD_OK | PDFMARK_KEEP_NAME |
+ PDFMARK_NO_REFS},
+ {"APPEND", pdfmark_APPEND, PDFMARK_KEEP_NAME},
+ {"CLOSE", pdfmark_CLOSE, PDFMARK_ODD_OK | PDFMARK_KEEP_NAME},
+ {"NamespacePush", pdfmark_NamespacePush, 0},
+ {"NamespacePop", pdfmark_NamespacePop, 0},
+ {"NI", pdfmark_NI, PDFMARK_NAMEABLE},
+ /* Marked content. */
+ {"MP", pdfmark_MP, PDFMARK_ODD_OK},
+ {"DP", pdfmark_DP, 0},
+ {"BMC", pdfmark_BMC, PDFMARK_ODD_OK},
+ {"BDC", pdfmark_BDC, PDFMARK_NO_REFS},
+ {"EMC", pdfmark_EMC, 0},
+ /* Document structure. */
+ {"StRoleMap", pdfmark_StRoleMap, 0},
+ {"StClassMap", pdfmark_StClassMap, 0},
+ {"StPNE", pdfmark_StPNE, PDFMARK_NAMEABLE},
+ {"StBookmarkRoot", pdfmark_StBookmarkRoot, 0},
+ {"StPush", pdfmark_StPush, 0},
+ {"StPop", pdfmark_StPop, 0},
+ {"StPopAll", pdfmark_StPopAll, 0},
+ {"StBMC", pdfmark_StBMC, 0},
+ {"StBDC", pdfmark_StBDC, 0},
+ /* EMC is listed under "Marked content" above. */
+ {"StOBJ", pdfmark_StOBJ, 0},
+ {"StAttr", pdfmark_StAttr, 0},
+ {"StStore", pdfmark_StStore, 0},
+ {"StRetrieve", pdfmark_StRetrieve, 0},
+ /* End of list. */
+ {0, 0}
+};
+
+/* Process a pdfmark. */
+int
+pdfmark_process(gx_device_pdf * pdev, const gs_param_string_array * pma)
+{
+ const gs_param_string *data = pma->data;
+ uint size = pma->size;
+ const gs_param_string *pts = &data[size - 1];
+ const gs_param_string *objname = 0;
+ gs_matrix ctm;
+ const pdfmark_name *pmn;
+ int code = 0;
+
+ { int cnt, len = pts[-1].size;
+ char buf[200]; /* 6 doubles should fit (%g == -0.14285714285714285e-101 = 25 chars) */
+
+ if (len > sizeof(buf) - 1)
+ return_error(gs_error_rangecheck);
+ memcpy(buf, pts[-1].data, len);
+ buf[len] = 0;
+ cnt = sscanf(buf, "[%g %g %g %g %g %g]",
+ &ctm.xx, &ctm.xy, &ctm.yx, &ctm.yy, &ctm.tx, &ctm.ty);
+ if (cnt != 6)
+ return_error(gs_error_rangecheck);
+ }
+ size -= 2; /* remove CTM & pdfmark name */
+ for (pmn = mark_names; pmn->mname != 0; ++pmn)
+ if (pdf_key_eq(pts, pmn->mname)) {
+ gs_memory_t *mem = pdev->pdf_memory;
+ int odd_ok = (pmn->options & PDFMARK_ODD_OK) != 0;
+ gs_param_string *pairs;
+ int j;
+
+ /*
+ * Our coordinate system is scaled so that user space is always
+ * default user space. Adjust the CTM to match this, except if this
+ * particular pdfmark requires the "true" CTM.
+ */
+ if (pmn->options & PDFMARK_TRUECTM)
+ DO_NOTHING;
+ else {
+ double xscale = 72.0 / pdev->HWResolution[0],
+ yscale = 72.0 / pdev->HWResolution[1];
+ ctm.xx *= xscale, ctm.xy *= yscale;
+ ctm.yx *= xscale, ctm.yy *= yscale;
+ ctm.tx *= xscale, ctm.ty *= yscale;
+ }
+ if (size & !odd_ok)
+ return_error(gs_error_rangecheck);
+ if (pmn->options & PDFMARK_NAMEABLE) {
+ /* Look for an object name. */
+ for (j = 0; j < size; j += 2) {
+ if (pdf_key_eq(&data[j], "/_objdef")) {
+ objname = &data[j + 1];
+ if (!pdf_objname_is_valid(objname->data,
+ objname->size)
+ )
+ return_error(gs_error_rangecheck);
+ /* Save the pairs without the name. */
+ size -= 2;
+ pairs = (gs_param_string *)
+ gs_alloc_byte_array(mem, size,
+ sizeof(gs_param_string),
+ "pdfmark_process(pairs)");
+ if (!pairs)
+ return_error(gs_error_VMerror);
+ memcpy(pairs, data, j * sizeof(*data));
+ memcpy(pairs + j, data + j + 2,
+ (size - j) * sizeof(*data));
+ goto copied;
+ }
+ }
+ }
+ /* Save all the pairs. */
+ pairs = (gs_param_string *)
+ gs_alloc_byte_array(mem, size, sizeof(gs_param_string),
+ "pdfmark_process(pairs)");
+ if (!pairs)
+ return_error(gs_error_VMerror);
+ memcpy(pairs, data, size * sizeof(*data));
+ copied: /* Substitute object references for names. */
+ if (!(pmn->options & PDFMARK_NO_REFS)) {
+ for (j = (pmn->options & PDFMARK_KEEP_NAME ? 1 : 1 - odd_ok);
+ j < size; j += 2 - odd_ok
+ ) {
+ code = pdf_replace_names(pdev, &pairs[j], &pairs[j]);
+ if (code < 0) {
+ gs_free_object(mem, pairs, "pdfmark_process(pairs)");
+ return code;
+ }
+ }
+ }
+ code = (*pmn->proc) (pdev, pairs, size, &ctm, objname);
+ gs_free_object(mem, pairs, "pdfmark_process(pairs)");
+ break;
+ }
+ return code;
+}
diff --git a/devices/vector/gdevpdfo.c b/devices/vector/gdevpdfo.c
new file mode 100644
index 000000000..a70ba7793
--- /dev/null
+++ b/devices/vector/gdevpdfo.c
@@ -0,0 +1,2083 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Cos object support */
+#include "memory_.h"
+#include "gx.h"
+#include "gserrors.h"
+#include "gsparam.h"
+#include "gsutil.h" /* for bytes_compare */
+#include "gdevpdfx.h"
+#include "gdevpdfo.h"
+#include "strimpl.h"
+#include "sa85x.h"
+#include "sarc4.h"
+#include "sstring.h"
+
+#define CHECK(expr)\
+ BEGIN if ((code = (expr)) < 0) return code; END
+
+/* ---------------- Structure definitions ---------------- */
+
+/*
+ * Define the generic structure for elements of arrays and
+ * dictionaries/streams.
+ */
+#define cos_element_common(etype)\
+ etype *next
+struct cos_element_s {
+ cos_element_common(cos_element_t);
+};
+#define private_st_cos_element() /* in gdevpdfo.c */\
+ gs_private_st_ptrs1(st_cos_element, cos_element_t, "cos_element_t",\
+ cos_element_enum_ptrs, cos_element_reloc_ptrs, next)
+#define cos_element_num_ptrs 1
+
+/*
+ * Define the structure for a piece of stream contents.
+ */
+struct cos_stream_piece_s {
+ cos_element_common(cos_stream_piece_t);
+ gs_offset_t position; /* in streams file */
+ uint size;
+};
+#define private_st_cos_stream_piece() /* in gdevpdfo.c */\
+ gs_private_st_suffix_add0_local(st_cos_stream_piece, cos_stream_piece_t,\
+ "cos_stream_piece_t", cos_element_enum_ptrs, cos_element_reloc_ptrs,\
+ st_cos_element)
+
+/*
+ * Define Cos arrays, dictionaries, and streams.
+ */
+ /* array */
+struct cos_array_element_s {
+ cos_element_common(cos_array_element_t);
+ long index;
+ cos_value_t value;
+};
+#define private_st_cos_array_element() /* in gdevpdfo.c */\
+ gs_private_st_composite(st_cos_array_element, cos_array_element_t,\
+ "cos_array_element_t", cos_array_element_enum_ptrs, cos_array_element_reloc_ptrs)
+ /* dict */
+struct cos_dict_element_s {
+ cos_element_common(cos_dict_element_t);
+ gs_string key;
+ bool owns_key; /* if false, key is shared, do not trace or free */
+ cos_value_t value;
+};
+#define private_st_cos_dict_element() /* in gdevpdfo.c */\
+ gs_private_st_composite(st_cos_dict_element, cos_dict_element_t,\
+ "cos_dict_element_t", cos_dict_element_enum_ptrs, cos_dict_element_reloc_ptrs)
+
+/* GC descriptors */
+private_st_cos_element();
+private_st_cos_stream_piece();
+private_st_cos_object();
+private_st_cos_value();
+private_st_cos_array_element();
+private_st_cos_dict_element();
+
+/* GC procedures */
+static
+ENUM_PTRS_WITH(cos_value_enum_ptrs, cos_value_t *pcv) return 0;
+ case 0:
+ switch (pcv->value_type) {
+ case COS_VALUE_SCALAR:
+ return ENUM_STRING(&pcv->contents.chars);
+ case COS_VALUE_CONST:
+ break;
+ case COS_VALUE_OBJECT:
+ case COS_VALUE_RESOURCE:
+ return ENUM_OBJ(pcv->contents.object);
+ }
+ return 0;
+ENUM_PTRS_END
+static
+RELOC_PTRS_WITH(cos_value_reloc_ptrs, cos_value_t *pcv)
+{
+ switch (pcv->value_type) {
+ case COS_VALUE_SCALAR:
+ RELOC_STRING_VAR(pcv->contents.chars);
+ case COS_VALUE_CONST:
+ break;
+ case COS_VALUE_OBJECT:
+ case COS_VALUE_RESOURCE:
+ RELOC_VAR(pcv->contents.object);
+ break;
+ }
+}
+RELOC_PTRS_END
+static
+ENUM_PTRS_WITH(cos_array_element_enum_ptrs, cos_array_element_t *pcae)
+{
+ return (index < cos_element_num_ptrs ?
+ ENUM_USING_PREFIX(st_cos_element, 0) :
+ ENUM_USING(st_cos_value, &pcae->value, sizeof(cos_value_t),
+ index - cos_element_num_ptrs));
+}
+ENUM_PTRS_END
+static
+RELOC_PTRS_WITH(cos_array_element_reloc_ptrs, cos_array_element_t *pcae)
+{
+ RELOC_PREFIX(st_cos_element);
+ RELOC_USING(st_cos_value, &pcae->value, sizeof(cos_value_t));
+}
+RELOC_PTRS_END
+static
+ENUM_PTRS_WITH(cos_dict_element_enum_ptrs, cos_dict_element_t *pcde)
+{
+ return (index < cos_element_num_ptrs ?
+ ENUM_USING_PREFIX(st_cos_element, 0) :
+ (index -= cos_element_num_ptrs) > 0 ?
+ ENUM_USING(st_cos_value, &pcde->value, sizeof(cos_value_t),
+ index - 1) :
+ pcde->owns_key ? ENUM_STRING(&pcde->key) : ENUM_OBJ(NULL));
+}
+ENUM_PTRS_END
+static
+RELOC_PTRS_WITH(cos_dict_element_reloc_ptrs, cos_dict_element_t *pcde)
+{
+ RELOC_PREFIX(st_cos_element);
+ if (pcde->owns_key)
+ RELOC_STRING_VAR(pcde->key);
+ RELOC_USING(st_cos_value, &pcde->value, sizeof(cos_value_t));
+}
+RELOC_PTRS_END
+
+/* ---------------- Generic support ---------------- */
+
+/* Initialize a just-allocated cos object. */
+static void
+cos_object_init(cos_object_t *pco, gx_device_pdf *pdev,
+ const cos_object_procs_t *procs)
+{
+ if (pco) {
+ pco->cos_procs = procs;
+ pco->id = 0;
+ pco->elements = 0;
+ pco->pieces = 0;
+ pco->mem = pdev->pdf_memory;
+ pco->pres = 0;
+ pco->is_open = true;
+ pco->is_graphics = false;
+ pco->written = false;
+ pco->length = 0;
+ pco->input_strm = 0;
+ pco->md5_valid = 0;
+ pco->stream_md5_valid = 0;
+ memset(&pco->hash, 0x00, 16);
+ }
+}
+
+/* Get the allocator for a Cos object. */
+gs_memory_t *
+cos_object_memory(const cos_object_t *pco)
+{
+ return pco->mem;
+}
+
+/* Change a generic cos object into one of a specific type. */
+int
+cos_become(cos_object_t *pco, cos_type_t cotype)
+{
+ if (cos_type(pco) != cos_type_generic)
+ return_error(gs_error_typecheck);
+ cos_type(pco) = cotype;
+ return 0;
+}
+
+/* Release a cos object. */
+cos_proc_release(cos_release); /* check prototype */
+void
+cos_release(cos_object_t *pco, client_name_t cname)
+{
+ pco->cos_procs->release(pco, cname);
+}
+
+/* Free a cos object. */
+void
+cos_free(cos_object_t *pco, client_name_t cname)
+{
+ cos_release(pco, cname);
+ gs_free_object(cos_object_memory(pco), pco, cname);
+}
+
+/* Write a cos object on the output. */
+cos_proc_write(cos_write); /* check prototype */
+int
+cos_write(const cos_object_t *pco, gx_device_pdf *pdev, gs_id object_id)
+{
+ return pco->cos_procs->write(pco, pdev, object_id);
+}
+
+/* Write a cos object as a PDF object. */
+int
+cos_write_object(cos_object_t *pco, gx_device_pdf *pdev, pdf_resource_type_t type)
+{
+ int code;
+
+ if (pco->id == 0 || pco->written)
+ return_error(gs_error_Fatal);
+ pdf_open_separate(pdev, pco->id, type);
+ code = cos_write(pco, pdev, pco->id);
+ pdf_end_separate(pdev, type);
+ pco->written = true;
+ return code;
+}
+
+/* Make a value to store into a composite object. */
+const cos_value_t *
+cos_string_value(cos_value_t *pcv, const byte *data, uint size)
+{
+ /*
+ * It's OK to break const here, because the value will be copied
+ * before being stored in the collection.
+ */
+ pcv->contents.chars.data = (byte *)data;
+ pcv->contents.chars.size = size;
+ pcv->value_type = COS_VALUE_SCALAR;
+ return pcv;
+}
+const cos_value_t *
+cos_c_string_value(cos_value_t *pcv, const char *str)
+{
+ /*
+ * We shouldn't break const here, because the value will not be copied
+ * or freed (or traced), but that would require a lot of bothersome
+ * casting elsewhere.
+ */
+ pcv->contents.chars.data = (byte *)str;
+ pcv->contents.chars.size = strlen(str);
+ pcv->value_type = COS_VALUE_CONST;
+ return pcv;
+}
+const cos_value_t *
+cos_object_value(cos_value_t *pcv, cos_object_t *pco)
+{
+ pcv->contents.object = pco;
+ pcv->value_type = COS_VALUE_OBJECT;
+ return pcv;
+}
+const cos_value_t *
+cos_resource_value(cos_value_t *pcv, cos_object_t *pco)
+{
+ pcv->contents.object = pco;
+ pcv->value_type = COS_VALUE_RESOURCE;
+ return pcv;
+}
+
+/* Free a value. */
+void
+cos_value_free(const cos_value_t *pcv, const cos_object_t *pco,
+ client_name_t cname)
+{
+ switch (pcv->value_type) {
+ case COS_VALUE_SCALAR:
+ gs_free_string(cos_object_memory(pco), pcv->contents.chars.data,
+ pcv->contents.chars.size, cname);
+ case COS_VALUE_CONST:
+ break;
+ case COS_VALUE_OBJECT:
+ /* Free the object if this is the only reference to it. */
+ if (pcv->contents.object != NULL) /* see cos_dict_objects_delete. */
+ if (!pcv->contents.object->id)
+ cos_free(pcv->contents.object, cname);
+ case COS_VALUE_RESOURCE:
+ break;
+ }
+}
+
+/* Write a value on the output. */
+static int
+cos_value_write_spaced(const cos_value_t *pcv, gx_device_pdf *pdev,
+ bool do_space, gs_id object_id)
+{
+ stream *s = pdev->strm;
+
+ switch (pcv->value_type) {
+ case COS_VALUE_SCALAR:
+ case COS_VALUE_CONST:
+ if (do_space)
+ switch (pcv->contents.chars.data[0]) {
+ case '/': case '(': case '<': break;
+ default: stream_putc(s, ' ');
+ }
+ return pdf_write_value(pdev, pcv->contents.chars.data,
+ pcv->contents.chars.size, object_id);
+ case COS_VALUE_RESOURCE:
+ pprintld1(s, "/R%ld", pcv->contents.object->id);
+ break;
+ case COS_VALUE_OBJECT: {
+ cos_object_t *pco = pcv->contents.object;
+
+ if (!pco->id) {
+ if (do_space &&
+ !(pco->cos_procs == cos_type_array ||
+ pco->cos_procs == cos_type_dict)
+ ) {
+ /* Arrays and dictionaries (only) are self-delimiting. */
+ stream_putc(s, ' ');
+ }
+ return cos_write(pco, pdev, object_id);
+ }
+ if (do_space)
+ stream_putc(s, ' ');
+ pprintld1(s, "%ld 0 R", pco->id);
+ if (pco->cos_procs == cos_type_reference)
+ pco->id = 0;
+ break;
+ }
+ default: /* can't happen */
+ return_error(gs_error_Fatal);
+ }
+ return 0;
+}
+int
+cos_value_write(const cos_value_t *pcv, gx_device_pdf *pdev)
+{
+ return cos_value_write_spaced(pcv, pdev, false, 0);
+}
+
+/* Copy a value if necessary for putting into an array or dictionary. */
+static int
+cos_copy_element_value(cos_value_t *pcv, gs_memory_t *mem,
+ const cos_value_t *pvalue, bool copy)
+{
+ *pcv = *pvalue;
+ if (pvalue->value_type == COS_VALUE_SCALAR && copy) {
+ byte *value_data = gs_alloc_string(mem, pvalue->contents.chars.size,
+ "cos_copy_element_value");
+
+ if (value_data == 0)
+ return_error(gs_error_VMerror);
+ memcpy(value_data, pvalue->contents.chars.data,
+ pvalue->contents.chars.size);
+ pcv->contents.chars.data = value_data;
+ }
+ return 0;
+}
+
+/* Release a value copied for putting, if the operation fails. */
+static void
+cos_uncopy_element_value(cos_value_t *pcv, gs_memory_t *mem, bool copy)
+{
+ if (pcv->value_type == COS_VALUE_SCALAR && copy)
+ gs_free_string(mem, pcv->contents.chars.data, pcv->contents.chars.size,
+ "cos_uncopy_element_value");
+}
+
+static int cos_value_hash(cos_value_t *pcv0, gs_md5_state_t *md5, gs_md5_byte_t *hash, gx_device_pdf *pdev)
+{
+ int code;
+ switch (pcv0->value_type) {
+ case COS_VALUE_SCALAR:
+ case COS_VALUE_CONST:
+ gs_md5_append(md5, pcv0->contents.chars.data, pcv0->contents.chars.size);
+ break;
+
+ case COS_VALUE_OBJECT:
+ code = pcv0->contents.object->cos_procs->hash(pcv0->contents.object, md5, hash, pdev);
+ if (code < 0)
+ return code;
+ break;
+ case COS_VALUE_RESOURCE:
+ break;
+ }
+ return 0;
+}
+
+/* ---------------- Specific object types ---------------- */
+
+/* ------ Generic objects ------ */
+
+static cos_proc_release(cos_reference_release);
+static cos_proc_release(cos_generic_release);
+static cos_proc_write(cos_generic_write);
+static cos_proc_equal(cos_generic_equal);
+static cos_proc_hash(cos_generic_hash);
+const cos_object_procs_t cos_generic_procs = {
+ cos_generic_release, cos_generic_write, cos_generic_equal, cos_generic_hash
+};
+const cos_object_procs_t cos_reference_procs = {
+ cos_reference_release, cos_generic_write, cos_generic_equal, cos_generic_hash
+};
+
+cos_object_t *
+cos_object_alloc(gx_device_pdf *pdev, client_name_t cname)
+{
+ gs_memory_t *mem = pdev->pdf_memory;
+ cos_object_t *pco =
+ gs_alloc_struct(mem, cos_object_t, &st_cos_object, cname);
+
+ cos_object_init(pco, pdev, &cos_generic_procs);
+ return pco;
+}
+
+cos_object_t *
+cos_reference_alloc(gx_device_pdf *pdev, client_name_t cname)
+{
+ gs_memory_t *mem = pdev->pdf_memory;
+ cos_object_t *pco =
+ gs_alloc_struct(mem, cos_object_t, &st_cos_object, cname);
+
+ cos_object_init(pco, pdev, &cos_reference_procs);
+ return pco;
+}
+
+static void
+cos_reference_release(cos_object_t *pco, client_name_t cname)
+{
+ /* Do nothing. */
+}
+
+static void
+cos_generic_release(cos_object_t *pco, client_name_t cname)
+{
+ /* Do nothing. */
+}
+
+static int
+cos_generic_write(const cos_object_t *pco, gx_device_pdf *pdev, gs_id object_id)
+{
+ return_error(gs_error_Fatal);
+}
+
+static int
+cos_generic_equal(const cos_object_t *pco0, const cos_object_t *pco1, gx_device_pdf *pdev)
+{
+ return_error(gs_error_Fatal);
+}
+
+static int
+cos_generic_hash(const cos_object_t *pco0, gs_md5_state_t *md5, gs_md5_byte_t *hash, gx_device_pdf *pdev)
+{
+ return_error(gs_error_Fatal);
+}
+
+/* ------ Arrays ------ */
+
+static cos_proc_release(cos_array_release);
+static cos_proc_write(cos_array_write);
+static cos_proc_equal(cos_array_equal);
+static cos_proc_hash(cos_array_hash);
+const cos_object_procs_t cos_array_procs = {
+ cos_array_release, cos_array_write, cos_array_equal, cos_array_hash
+};
+
+cos_array_t *
+cos_array_alloc(gx_device_pdf *pdev, client_name_t cname)
+{
+ gs_memory_t *mem = pdev->pdf_memory;
+ cos_array_t *pca =
+ gs_alloc_struct(mem, cos_array_t, &st_cos_object, cname);
+
+ cos_object_init((cos_object_t *)pca, pdev, &cos_array_procs);
+ return pca;
+}
+
+cos_array_t *
+cos_array_from_floats(gx_device_pdf *pdev, const float *pf, uint size,
+ client_name_t cname)
+{
+ cos_array_t *pca = cos_array_alloc(pdev, cname);
+ uint i;
+
+ if (pca == 0)
+ return 0;
+ for (i = 0; i < size; ++i) {
+ int code = cos_array_add_real(pca, pf[i]);
+
+ if (code < 0) {
+ COS_FREE(pca, cname);
+ return 0;
+ }
+ }
+ return pca;
+}
+
+static void
+cos_array_release(cos_object_t *pco, client_name_t cname)
+{
+ cos_array_t *const pca = (cos_array_t *)pco;
+ cos_array_element_t *cur;
+ cos_array_element_t *next;
+
+ for (cur = pca->elements; cur; cur = next) {
+ next = cur->next;
+ cos_value_free(&cur->value, pco, cname);
+ gs_free_object(cos_object_memory(pco), cur, cname);
+ }
+ pca->elements = 0;
+}
+
+static cos_array_element_t *cos_array_reorder(const cos_array_t *pca,
+ cos_array_element_t *first);
+static int
+cos_array_write(const cos_object_t *pco, gx_device_pdf *pdev, gs_id object_id)
+{
+ stream *s = pdev->strm;
+ const cos_array_t *const pca = (const cos_array_t *)pco;
+ cos_array_element_t *first = cos_array_reorder(pca, NULL);
+ cos_array_element_t *pcae;
+ uint last_index = 0, Element_Count = 0;
+
+ stream_puts(s, "[");
+ for (pcae = first; pcae; ++last_index, pcae = pcae->next) {
+ Element_Count++;
+
+ if(pdev->PDFA != 0 && Element_Count > 8191) {
+ switch (pdev->PDFACompatibilityPolicy) {
+ case 0:
+ emprintf(pdev->memory,
+ "Too many entries in array,\n max 8191 in PDF/A, reverting to normal PDF output\n");
+ pdev->AbortPDFAX = true;
+ pdev->PDFA = 0;
+ break;
+ case 1:
+ emprintf(pdev->memory,
+ "Too many entries in array,\n max 8191 in PDF/A. Cannot simply elide dictionary, reverting to normal output\n");
+ pdev->AbortPDFAX = true;
+ pdev->PDFA = 0;
+ break;
+ case 2:
+ emprintf(pdev->memory,
+ "Too many entries in array,\n max 8191 in PDF/A. aborting conversion\n");
+ /* Careful here, only certain errors will bubble up
+ * through the text processing.
+ */
+ return gs_error_limitcheck;
+ break;
+ default:
+ emprintf(pdev->memory,
+ "Too many entries in array,\n max 8191 in PDF/A. Unrecognised PDFACompatibilityLevel,\nreverting to normal PDF output\n");
+ pdev->AbortPDFAX = true;
+ pdev->PDFA = 0;
+ break;
+ }
+ }
+ if (pcae != first)
+ stream_putc(s, '\n');
+ for (; pcae->index > last_index; ++last_index)
+ stream_puts(s, "null\n");
+ cos_value_write_spaced(&pcae->value, pdev, false, object_id);
+ }
+ DISCARD(cos_array_reorder(pca, first));
+ stream_puts(s, "]");
+ if (pdev->PDFA != 0)
+ stream_puts(s, "\n");
+ return 0;
+}
+
+static int
+cos_array_equal(const cos_object_t *pco0, const cos_object_t *pco1, gx_device_pdf *pdev)
+{
+ const cos_array_t *const pca0 = (const cos_array_t *)pco0;
+ const cos_array_t *const pca1 = (const cos_array_t *)pco1;
+ int code;
+
+ if (!pca0->md5_valid) {
+ gs_md5_init((gs_md5_state_t *)&pca0->md5);
+ code = cos_array_hash(pco0, (gs_md5_state_t *)&pca0->md5, (gs_md5_byte_t *)pca0->hash, pdev);
+ if (code < 0)
+ return code;
+ gs_md5_finish((gs_md5_state_t *)&pca0->md5, (gs_md5_byte_t *)pca0->hash);
+ ((cos_object_t *)pca0)->md5_valid = true;
+ }
+ if (!pca1->md5_valid) {
+ gs_md5_init((gs_md5_state_t *)&pca1->md5);
+ code = cos_array_hash(pco1, (gs_md5_state_t *)&pca1->md5, (gs_md5_byte_t *)pca1->hash, pdev);
+ if (code < 0)
+ return code;
+ gs_md5_finish((gs_md5_state_t *)&pca1->md5, (gs_md5_byte_t *)pca1->hash);
+ ((cos_object_t *)pca1)->md5_valid = true;
+ }
+ if (memcmp(&pca0->hash, &pca1->hash, 16) != 0)
+ return false;
+
+ return true;
+}
+
+static int
+cos_array_hash(const cos_object_t *pco0, gs_md5_state_t *md5, gs_md5_byte_t *hash, gx_device_pdf *pdev)
+{
+ const cos_array_t *const pca0 = (const cos_array_t *)pco0;
+ cos_array_element_t *first0 = pca0->elements;
+ cos_array_element_t *pcae0;
+ int code;
+
+ for (pcae0 = first0; pcae0;pcae0 = pcae0->next) {
+ code = cos_value_hash(&pcae0->value, md5, hash, pdev);
+ if (code < 0)
+ return code;
+ }
+ return 0;
+}
+
+/* Put/add an element in/to an array. */
+int
+cos_array_put(cos_array_t *pca, long index, const cos_value_t *pvalue)
+{
+ gs_memory_t *mem = COS_OBJECT_MEMORY(pca);
+ cos_value_t value;
+ int code = cos_copy_element_value(&value, mem, pvalue, true);
+
+ if (code >= 0) {
+ code = cos_array_put_no_copy(pca, index, &value);
+ if (code < 0)
+ cos_uncopy_element_value(&value, mem, true);
+ }
+ pca->md5_valid = false;
+ return code;
+}
+int
+cos_array_put_no_copy(cos_array_t *pca, long index, const cos_value_t *pvalue)
+{
+ gs_memory_t *mem = COS_OBJECT_MEMORY(pca);
+ cos_array_element_t **ppcae = &pca->elements;
+ cos_array_element_t *pcae;
+ cos_array_element_t *next;
+
+ while ((next = *ppcae) != 0 && next->index > index)
+ ppcae = &next->next;
+ if (next && next->index == index) {
+ /* We're replacing an existing element. */
+ cos_value_free(&next->value, COS_OBJECT(pca),
+ "cos_array_put(old value)");
+ pcae = next;
+ } else {
+ /* Create a new element. */
+ pcae = gs_alloc_struct(mem, cos_array_element_t, &st_cos_array_element,
+ "cos_array_put(element)");
+ if (pcae == 0)
+ return_error(gs_error_VMerror);
+ pcae->index = index;
+ pcae->next = next;
+ *ppcae = pcae;
+ }
+ pcae->value = *pvalue;
+ pca->md5_valid = false;
+ return 0;
+}
+static long
+cos_array_next_index(const cos_array_t *pca)
+{
+ return (pca->elements ? pca->elements->index + 1 : 0L);
+}
+int
+cos_array_add(cos_array_t *pca, const cos_value_t *pvalue)
+{
+ pca->md5_valid = false;
+ return cos_array_put(pca, cos_array_next_index(pca), pvalue);
+}
+int
+cos_array_add_no_copy(cos_array_t *pca, const cos_value_t *pvalue)
+{
+ pca->md5_valid = false;
+ return cos_array_put_no_copy(pca, cos_array_next_index(pca), pvalue);
+}
+int
+cos_array_add_c_string(cos_array_t *pca, const char *str)
+{
+ cos_value_t value;
+
+ return cos_array_add(pca, cos_c_string_value(&value, str));
+}
+int
+cos_array_add_int(cos_array_t *pca, int i)
+{
+ char str[sizeof(int) * 8 / 3 + 3]; /* sign, rounding, 0 terminator */
+ cos_value_t v;
+
+ gs_sprintf(str, "%d", i);
+ return cos_array_add(pca, cos_string_value(&v, (byte *)str, strlen(str)));
+}
+int
+cos_array_add_real(cos_array_t *pca, double r)
+{
+ byte str[50]; /****** ADHOC ******/
+ stream s;
+ cos_value_t v;
+
+ s_init(&s, NULL);
+ swrite_string(&s, str, sizeof(str));
+ pprintg1(&s, "%g", r);
+ return cos_array_add(pca, cos_string_value(&v, str, stell(&s)));
+}
+int
+cos_array_add_object(cos_array_t *pca, cos_object_t *pco)
+{
+ cos_value_t value;
+
+ value.contents.chars.size = 0; /* Quiet a warning appeared with MSVC6 inline optimization. */
+ return cos_array_add(pca, cos_object_value(&value, pco));
+}
+
+/*
+ * Remove and return the last element of an array. Since this is intended
+ * specifically for arrays used as stacks, it gives an error if there is a
+ * gap in indices between the last element and the element before it.
+ */
+int
+cos_array_unadd(cos_array_t *pca, cos_value_t *pvalue)
+{
+ cos_array_element_t *pcae = pca->elements;
+
+ if (pcae == 0 ||
+ pcae->index != (pcae->next == 0 ? 0 : pcae->next->index + 1)
+ )
+ return_error(gs_error_rangecheck);
+ *pvalue = pcae->value;
+ pca->elements = pcae->next;
+ gs_free_object(COS_OBJECT_MEMORY(pca), pcae, "cos_array_unadd");
+ pca->md5_valid = false;
+ return 0;
+}
+
+/* Get the first / next element for enumerating an array. */
+const cos_array_element_t *
+cos_array_element_first(const cos_array_t *pca)
+{
+ return pca->elements;
+}
+const cos_array_element_t *
+cos_array_element_next(const cos_array_element_t *pca, long *pindex,
+ const cos_value_t **ppvalue)
+{
+ *pindex = pca->index;
+ *ppvalue = &pca->value;
+ return pca->next;
+}
+
+/*
+ * Reorder the elements of an array for writing or after writing. Usage:
+ * first_element = cos_array_reorder(pca, NULL);
+ * ...
+ * cos_array_reorder(pca, first_element);
+ */
+static cos_array_element_t *
+cos_array_reorder(const cos_array_t *pca, cos_array_element_t *first)
+{
+ cos_array_element_t *last;
+ cos_array_element_t *next;
+ cos_array_element_t *pcae;
+
+ for (pcae = (first ? first : pca->elements), last = NULL; pcae;
+ pcae = next)
+ next = pcae->next, pcae->next = last, last = pcae;
+ return last;
+}
+
+/* ------ Dictionaries ------ */
+
+static cos_proc_release(cos_dict_release);
+static cos_proc_write(cos_dict_write);
+static cos_proc_equal(cos_dict_equal);
+static cos_proc_hash(cos_dict_hash);
+const cos_object_procs_t cos_dict_procs = {
+ cos_dict_release, cos_dict_write, cos_dict_equal, cos_dict_hash
+};
+
+cos_dict_t *
+cos_dict_alloc(gx_device_pdf *pdev, client_name_t cname)
+{
+ gs_memory_t *mem = pdev->pdf_memory;
+ cos_dict_t *pcd =
+ gs_alloc_struct(mem, cos_dict_t, &st_cos_object, cname);
+
+ cos_object_init((cos_object_t *)pcd, pdev, &cos_dict_procs);
+ return pcd;
+}
+
+static void
+cos_dict_element_free(cos_dict_t *pcd, cos_dict_element_t *pcde,
+ client_name_t cname)
+{
+ gs_memory_t *mem = COS_OBJECT_MEMORY(pcd);
+
+ cos_value_free(&pcde->value, COS_OBJECT(pcd), cname);
+ if (pcde->owns_key)
+ gs_free_string(mem, pcde->key.data, pcde->key.size, cname);
+ gs_free_object(mem, pcde, cname);
+}
+
+static int
+cos_dict_delete(cos_dict_t *pcd, const byte *key_data, uint key_size)
+{
+ cos_dict_element_t *pcde = pcd->elements, *prev = 0;
+
+ for (; pcde; pcde = pcde->next) {
+ if (!bytes_compare(key_data, key_size, pcde->key.data, pcde->key.size)) {
+ if (prev != 0)
+ prev->next = pcde->next;
+ else
+ pcd->elements = pcde->next;
+ cos_dict_element_free(pcd, pcde, "cos_dict_delete");
+ return 0;
+ }
+ prev = pcde;
+ }
+ return -1;
+}
+int
+cos_dict_delete_c_key(cos_dict_t *pcd, const char *key)
+{
+ return cos_dict_delete(pcd, (const byte *)key, strlen(key));
+}
+
+static void
+cos_dict_release(cos_object_t *pco, client_name_t cname)
+{
+ cos_dict_t *const pcd = (cos_dict_t *)pco;
+ cos_dict_element_t *cur;
+ cos_dict_element_t *next;
+
+ for (cur = pcd->elements; cur; cur = next) {
+ next = cur->next;
+ cos_dict_element_free(pcd, cur, cname);
+ }
+ pcd->elements = 0;
+}
+
+/* Write the elements of a dictionary. */
+static int
+cos_elements_write(stream *s, const cos_dict_element_t *pcde,
+ gx_device_pdf *pdev, bool do_space, gs_id object_id)
+{
+ int Element_Count = 0;
+
+ if (pcde) {
+ /* Temporarily replace the output stream in pdev. */
+ stream *save = pdev->strm;
+
+ pdev->strm = s;
+ for (;;) {
+ gs_id object_id1 = (pdev->NoEncrypt.size == 0 ||
+ bytes_compare(pdev->NoEncrypt.data, pdev->NoEncrypt.size,
+ pcde->key.data, pcde->key.size)
+ ? object_id : (gs_id)-1);
+
+ Element_Count++;
+
+ if(pdev->PDFA != 0 && Element_Count > 4095) {
+ switch (pdev->PDFACompatibilityPolicy) {
+ case 0:
+ emprintf(pdev->memory,
+ "Too many entries in dictionary,\n max 4095 in PDF/A, reverting to normal PDF output\n");
+ pdev->AbortPDFAX = true;
+ pdev->PDFA = 0;
+ break;
+ case 1:
+ emprintf(pdev->memory,
+ "Too many entries in dictionary,\n max 4095 in PDF/A. Cannot simply elide dictionary, reverting to normal output\n");
+ pdev->AbortPDFAX = true;
+ pdev->PDFA = 0;
+ break;
+ case 2:
+ emprintf(pdev->memory,
+ "Too many entries in dictionary,\n max 4095 in PDF/A. aborting conversion\n");
+ /* Careful here, only certain errors will bubble up
+ * through the text processing.
+ */
+ return gs_error_limitcheck;
+ break;
+ default:
+ emprintf(pdev->memory,
+ "Too many entries in dictionary,\n max 4095 in PDF/A. Unrecognised PDFACompatibilityLevel,\nreverting to normal PDF output\n");
+ pdev->AbortPDFAX = true;
+ pdev->PDFA = 0;
+ break;
+ }
+ }
+
+ pdf_write_value(pdev, pcde->key.data, pcde->key.size, object_id1);
+ cos_value_write_spaced(&pcde->value, pdev, true, object_id1);
+ pcde = pcde->next;
+ if (pcde || do_space)
+ stream_putc(s, '\n');
+ if (!pcde)
+ break;
+ }
+ pdev->strm = save;
+ }
+ return 0;
+}
+int
+cos_dict_elements_write(const cos_dict_t *pcd, gx_device_pdf *pdev)
+{
+ return cos_elements_write(pdev->strm, pcd->elements, pdev, true, pcd->id);
+}
+
+static int
+cos_dict_write(const cos_object_t *pco, gx_device_pdf *pdev, gs_id object_id)
+{
+ stream *s = pdev->strm;
+
+ stream_puts(s, "<<");
+ cos_elements_write(s, ((const cos_dict_t *)pco)->elements, pdev, false, object_id);
+ stream_puts(s, ">>");
+ if (pdev->PDFA != 0)
+ stream_puts(s, "\n");
+ return 0;
+}
+
+static int find_first_dict_entry(const cos_dict_t *d, const cos_dict_element_t **element)
+{
+ const cos_dict_element_t *pcde = d->elements, *First;
+ int code, length, length1, length2, offset1 = 0, offset2 = 0, i;
+
+ *element = 0L;
+
+ First = pcde;
+
+ /*
+ * If the name has any 'unusual' characters, it is 'escaped' by starting with NULLs
+ * I suspect we no longer need that, but here we remove the escaping NULLs
+ */
+ for (i = 0;First->key.data[i] == 0x00; i++)
+ ;
+
+ length1 = First->key.size - i;
+ offset1 = i;
+
+ if (First->key.data[offset1] == '/') {
+ length1 -= 1;
+ offset1 += 1;
+ } else {
+ if (First->key.data[offset1] == '(') {
+ length1 = First->key.size - 2;
+ offset1 = 1;
+ } else {
+ return_error(gs_error_typecheck);
+ }
+ }
+
+ pcde = pcde->next;
+ while (pcde){
+ for (i = 0;pcde->key.data[i] == 0x00; i++)
+ ;
+ length2 = pcde->key.size - i;
+ offset2 = i;
+
+ if (pcde->key.data[offset2] == '/') {
+ length2 -= 1;
+ offset2 += 1;
+ } else {
+ if (pcde->key.data[offset2] == '(') {
+ length2 = pcde->key.size - 2;
+ offset2 = 1;
+ } else {
+ return_error(gs_error_typecheck);
+ }
+ }
+
+ if (length2 < length1)
+ length = length2;
+ else
+ length = length1;
+ code = strncmp((const char *)&pcde->key.data[offset2], (const char *)&First->key.data[offset1], length);
+ if (code == 0) {
+ if (length2 < length1) {
+ First = pcde;
+ length1 = length2;
+ }
+ } else if (code < 0) {
+ First = pcde;
+ length1 = length2;
+ }
+ pcde = pcde->next;
+ }
+ *element = First;
+ return 0;
+}
+
+static int find_next_dict_entry(const cos_dict_t *d, const cos_dict_element_t **element)
+{
+ const cos_dict_element_t *pcde = d->elements, *Current = *element, *Next = 0L;
+ int code, length, length1, length2, length3, offset1 = 0, offset2 = 0, offset3 = 0, i;
+
+ /*
+ * If the name has any 'unusual' characters, it is 'escaped' by starting with NULLs
+ * I suspect we no longer need that, but here we remove the escaping NULLs
+ */
+ for (i = 0;Current->key.data[i] == 0x00; i++)
+ ;
+ length1 = Current->key.size - i;
+ offset1 = i;
+ if (Current->key.data[offset1] == '/') {
+ length1 -= 1;
+ offset1 += 1;
+ } else {
+ if (Current->key.data[0] == '(') {
+ length1 -= 2;
+ offset1 = 1;
+ } else {
+ return_error(gs_error_typecheck);
+ }
+ }
+
+ while (pcde) {
+ for (i = 0;pcde->key.data[i] == 0x00; i++)
+ ;
+ length2 = pcde->key.size - i;
+ offset2 = i;
+ if (pcde->key.data[offset2] == '/') {
+ length2 -= 1;
+ offset2 += 1;
+ } else {
+ if (pcde->key.data[0] == '(') {
+ length2 -= 2;
+ offset2 = 1;
+ } else {
+ return_error(gs_error_typecheck);
+ }
+ }
+
+ if (length2 < length1)
+ length = length2;
+ else
+ length = length1;
+ code = strncmp((const char *)&pcde->key.data[offset2], (const char *)&Current->key.data[offset1], length);
+ if (code > 0 || (code == 0 && length2 > length1)) {
+ if (Next) {
+ if (length3 < length2)
+ length = length3;
+ else
+ length = length2;
+ code = strncmp((const char *)&pcde->key.data[offset2], (const char *)&Next->key.data[offset3], length);
+ if (code < 0 || (code == 0 && length3 > length2)) {
+ Next = pcde;
+ for (i = 0;Next->key.data[i] == 0x00; i++)
+ ;
+ length3 = Next->key.size - i;
+ offset3 = i;
+ if (Next->key.data[offset3] == '/') {
+ length3 -= 1;
+ offset3 += 1;
+ } else {
+ if (Next->key.data[0] == '(') {
+ length3 -= 2;
+ offset3 = 1;
+ } else {
+ return_error(gs_error_typecheck);
+ }
+ }
+ }
+ } else {
+ Next = pcde;
+ for (i = 0;Next->key.data[i] == 0x00; i++)
+ ;
+ length3 = Next->key.size - i;
+ offset3 = i;
+ if (Next->key.data[offset3] == '/') {
+ length3 -= 1;
+ offset3 = 1;
+ } else {
+ if (Next->key.data[0] == '(') {
+ length3 -= 2;
+ offset3 = 1;
+ } else {
+ return_error(gs_error_typecheck);
+ }
+ }
+ }
+ }
+ pcde = pcde->next;
+ }
+ *element = Next;
+ return 0;
+}
+
+static int find_last_dict_entry(const cos_dict_t *d, const cos_dict_element_t **element)
+{
+ const cos_dict_element_t *pcde = d->elements, *Last, *Next;
+
+ *element = 0L;
+
+ Next = Last = pcde;
+
+ do {
+ Last = Next;
+ find_next_dict_entry(d, &Next);
+ } while (Next);
+
+ *element = Last;
+
+ return 0;
+}
+static int write_key_as_string_encrypted(const gx_device_pdf *pdev, const byte *str, uint size, gs_id object_id)
+{
+ stream sinp, sout;
+ stream_PSSD_state st;
+ stream_state so;
+ byte bufo[100], *buffer;
+ stream_arcfour_state sarc4;
+
+ buffer = gs_alloc_bytes(pdev->pdf_memory, size, "encryption buffer");
+ if (buffer == 0L)
+ return 0;
+
+ if (pdf_encrypt_init(pdev, object_id, &sarc4) < 0) {
+ gs_free_object(pdev->pdf_memory, buffer, "Free encryption buffer");
+ /* The interface can't pass an error. */
+ stream_write(pdev->strm, str, size);
+ return size;
+ }
+ s_init_state((stream_state *)&st, &s_PSSD_template, NULL);
+ s_init(&sout, NULL);
+ s_init_state(&so, &s_PSSE_template, NULL);
+ s_init_filter(&sout, &so, bufo, sizeof(bufo), pdev->strm);
+ stream_putc(pdev->strm, '(');
+ memcpy(buffer, str, size);
+ s_arcfour_process_buffer(&sarc4, buffer, size);
+ stream_write(&sout, buffer, size);
+ sclose(&sout); /* Writes ')'. */
+ gs_free_object(pdev->pdf_memory, buffer, "Free encryption buffer");
+ return (int)stell(&sinp) + 1;
+}
+
+static int write_key_as_string(const gx_device_pdf *pdev, stream *s, const cos_dict_element_t *element, gs_id object_id)
+{
+ int i, length, offset;
+
+ /*
+ * If the name has any 'unusual' characters, it is 'escaped' by starting with NULLs
+ * I suspect we no longer need that, but here we remove the escaping NULLs
+ */
+ if (element->key.data[0] == 0x00) {
+ for (i = 0;element->key.data[i] == 0x00; i++)
+ ;
+ length = element->key.size - (i + 1);
+ offset = i;
+ } else {
+ offset = 0;
+ length = element->key.size;
+ }
+
+ if (element->key.data[offset] == '/') {
+ offset++;
+ length--;
+ if (!pdev->KeyLength || object_id == (gs_id)-1) {
+ spputc(s, '(');
+ stream_write(s, &element->key.data[offset], length);
+ spputc(s, ')');
+ }
+ else
+ write_key_as_string_encrypted(pdev, &element->key.data[offset], length, object_id);
+ } else {
+ if (!pdev->KeyLength || object_id == (gs_id)-1)
+ stream_write(s, element->key.data, element->key.size);
+ else
+ write_key_as_string_encrypted(pdev, &element->key.data[1], element->key.size - 2, object_id);
+ }
+ return 0;
+}
+
+int
+cos_write_dict_as_ordered_array(cos_object_t *pco, gx_device_pdf *pdev, pdf_resource_type_t type)
+{
+ stream *s;
+ int code;
+ const cos_dict_t *d;
+ const cos_dict_element_t *pcde, *First, *Last;
+
+ if (cos_type(pco) != cos_type_dict)
+ return_error(gs_error_typecheck);
+
+ d = (const cos_dict_t *)pco;
+
+ if (pco->id == 0 || pco->written)
+ return_error(gs_error_Fatal);
+ pdf_open_separate(pdev, pco->id, type);
+
+ s = pdev->strm;
+ pcde = d->elements;
+ if (!pcde){
+ stream_puts(s, "<<>>\n");
+ pdf_end_separate(pdev, type);
+ return 0;
+ }
+
+ code = find_first_dict_entry(d, &First);
+ if (code < 0) {
+ pdf_end_separate(pdev, type);
+ return code;
+ }
+
+ code = find_last_dict_entry(d, &Last);
+ if (code < 0) {
+ pdf_end_separate(pdev, type);
+ return code;
+ }
+
+ stream_puts(s, "<<\n/Limits [\n");
+ write_key_as_string(pdev, s, First, pco->id);
+ stream_puts(s, "\n");
+ write_key_as_string(pdev, s, Last, pco->id);
+ stream_puts(s, "\n]\n");
+ stream_puts(s, "/Names [");
+ do {
+ stream_puts(s, "\n");
+ write_key_as_string(pdev, s, First, pco->id);
+ cos_value_write_spaced(&First->value, pdev, true, -1);
+ find_next_dict_entry(d, &First);
+ } while (First);
+ stream_puts(s, "]\n>>\n");
+
+ pdf_end_separate(pdev, type);
+ pco->written = true;
+ return code;
+}
+
+/* Write/delete definitions of named objects. */
+/* This is a special-purpose facility for pdf_close. */
+int
+cos_dict_objects_write(const cos_dict_t *pcd, gx_device_pdf *pdev)
+{
+ const cos_dict_element_t *pcde = pcd->elements;
+
+ for (; pcde; pcde = pcde->next)
+ if (COS_VALUE_IS_OBJECT(&pcde->value) &&
+ pcde->value.contents.object->id &&
+ !pcde->value.contents.object->written /* ForOPDFRead only. */)
+ cos_write_object(pcde->value.contents.object, pdev, resourceOther);
+ return 0;
+}
+int
+cos_dict_objects_delete(cos_dict_t *pcd)
+{
+ cos_dict_element_t *pcde = pcd->elements;
+
+ /*
+ * Delete duplicate references to prevent a dual object freeing.
+ * Delete the objects' IDs so that freeing the dictionary will
+ * free them.
+ */
+ for (; pcde; pcde = pcde->next) {
+ if (pcde->value.contents.object) {
+ cos_dict_element_t *pcde1 = pcde->next;
+
+ for (; pcde1; pcde1 = pcde1->next)
+ if (pcde->value.contents.object == pcde1->value.contents.object)
+ pcde1->value.contents.object = NULL;
+ pcde->value.contents.object->id = 0;
+ }
+ }
+ return 0;
+}
+
+/* Put an element in a dictionary. */
+#define DICT_COPY_KEY 1
+#define DICT_COPY_VALUE 2
+#define DICT_FREE_KEY 4
+#define DICT_COPY_ALL (DICT_COPY_KEY | DICT_COPY_VALUE | DICT_FREE_KEY)
+static int
+cos_dict_put_copy(cos_dict_t *pcd, const byte *key_data, uint key_size,
+ const cos_value_t *pvalue, int flags)
+{
+ gs_memory_t *mem = COS_OBJECT_MEMORY(pcd);
+ cos_dict_element_t **ppcde = &pcd->elements;
+ cos_dict_element_t *pcde;
+ cos_dict_element_t *next;
+ cos_value_t value;
+ int code;
+
+ while ((next = *ppcde) != 0 &&
+ bytes_compare(next->key.data, next->key.size, key_data, key_size)
+ )
+ ppcde = &next->next;
+ if (next) {
+ /* We're replacing an existing element. */
+ if ((pvalue->value_type == COS_VALUE_SCALAR ||
+ pvalue->value_type == COS_VALUE_CONST) &&
+ pvalue->value_type == next->value.value_type &&
+ !bytes_compare(pvalue->contents.chars.data, pvalue->contents.chars.size,
+ next->value.contents.chars.data, next->value.contents.chars.size))
+ return 0; /* Same as old value. */
+ if ((pvalue->value_type == COS_VALUE_OBJECT ||
+ pvalue->value_type == COS_VALUE_RESOURCE) &&
+ pvalue->value_type == next->value.value_type &&
+ pvalue->contents.object == next->value.contents.object)
+ return 0; /* Same as old value. */
+ code = cos_copy_element_value(&value, mem, pvalue,
+ (flags & DICT_COPY_VALUE) != 0);
+ if (code < 0)
+ return code;
+ if (flags & DICT_FREE_KEY)
+ gs_free_const_string(mem, key_data, key_size,
+ "cos_dict_put(new key)");
+ cos_value_free(&next->value, COS_OBJECT(pcd),
+ "cos_dict_put(old value)");
+ pcde = next;
+ } else {
+ /* Create a new element. */
+ byte *copied_key_data;
+
+ if (flags & DICT_COPY_KEY) {
+ copied_key_data = gs_alloc_string(mem, key_size,
+ "cos_dict_put(key)");
+ if (copied_key_data == 0)
+ return_error(gs_error_VMerror);
+ memcpy(copied_key_data, key_data, key_size);
+ } else
+ copied_key_data = (byte *)key_data; /* OK to break const */
+ pcde = gs_alloc_struct(mem, cos_dict_element_t, &st_cos_dict_element,
+ "cos_dict_put(element)");
+ code = cos_copy_element_value(&value, mem, pvalue,
+ (flags & DICT_COPY_VALUE) != 0);
+ if (pcde == 0 || code < 0) {
+ if (code >= 0)
+ cos_uncopy_element_value(&value, mem,
+ (flags & DICT_COPY_VALUE) != 0);
+ gs_free_object(mem, pcde, "cos_dict_put(element)");
+ if (flags & DICT_COPY_KEY)
+ gs_free_string(mem, copied_key_data, key_size,
+ "cos_dict_put(key)");
+ return (code < 0 ? code : gs_note_error(gs_error_VMerror));
+ }
+ pcde->key.data = copied_key_data;
+ pcde->key.size = key_size;
+ pcde->owns_key = (flags & DICT_FREE_KEY) != 0;
+ pcde->next = next;
+ *ppcde = pcde;
+ }
+ pcde->value = value;
+ pcd->md5_valid = false;
+ return 0;
+}
+int
+cos_dict_put(cos_dict_t *pcd, const byte *key_data, uint key_size,
+ const cos_value_t *pvalue)
+{
+ return cos_dict_put_copy(pcd, key_data, key_size, pvalue, DICT_COPY_ALL);
+}
+int
+cos_dict_put_no_copy(cos_dict_t *pcd, const byte *key_data, uint key_size,
+ const cos_value_t *pvalue)
+{
+ return cos_dict_put_copy(pcd, key_data, key_size, pvalue,
+ DICT_COPY_KEY | DICT_FREE_KEY);
+}
+int
+cos_dict_put_c_key(cos_dict_t *pcd, const char *key, const cos_value_t *pvalue)
+{
+ return cos_dict_put_copy(pcd, (const byte *)key, strlen(key), pvalue,
+ DICT_COPY_VALUE);
+}
+int
+cos_dict_put_c_key_string(cos_dict_t *pcd, const char *key,
+ const byte *data, uint size)
+{
+ cos_value_t value;
+
+ cos_string_value(&value, data, size);
+ return cos_dict_put_c_key(pcd, key, &value);
+}
+int
+cos_dict_put_c_key_int(cos_dict_t *pcd, const char *key, int value)
+{
+ char str[sizeof(int) * 8 / 3 + 3]; /* sign, rounding, 0 terminator */
+
+ gs_sprintf(str, "%d", value);
+ return cos_dict_put_c_key_string(pcd, key, (byte *)str, strlen(str));
+}
+int
+cos_dict_put_c_key_bool(cos_dict_t *pcd, const char *key, bool value)
+{
+ return cos_dict_put_c_key_string(pcd, key,
+ (const byte *)(value ? "true" : "false"),
+ (value ? 4 : 5));
+}
+int
+cos_dict_put_c_key_real(cos_dict_t *pcd, const char *key, double value)
+{
+ byte str[50]; /****** ADHOC ******/
+ stream s;
+
+ s_init(&s, NULL);
+ swrite_string(&s, str, sizeof(str));
+ pprintg1(&s, "%g", value);
+ return cos_dict_put_c_key_string(pcd, key, str, stell(&s));
+}
+int
+cos_dict_put_c_key_floats(gx_device_pdf *pdev, cos_dict_t *pcd, const char *key, const float *pf,
+ uint size)
+{
+ cos_array_t *pca = cos_array_from_floats(pdev, pf, size,
+ "cos_dict_put_c_key_floats");
+ int code;
+
+ if (pca == 0)
+ return_error(gs_error_VMerror);
+ code = cos_dict_put_c_key_object(pcd, key, COS_OBJECT(pca));
+ if (code < 0)
+ COS_FREE(pca, "cos_dict_put_c_key_floats");
+ return code;
+}
+int
+cos_dict_put_c_key_object(cos_dict_t *pcd, const char *key, cos_object_t *pco)
+{
+ cos_value_t value;
+
+ return cos_dict_put_c_key(pcd, key, cos_object_value(&value, pco));
+}
+int
+cos_dict_put_string(cos_dict_t *pcd, const byte *key_data, uint key_size,
+ const byte *value_data, uint value_size)
+{
+ cos_value_t cvalue;
+
+ return cos_dict_put(pcd, key_data, key_size,
+ cos_string_value(&cvalue, value_data, value_size));
+}
+int
+cos_dict_put_string_copy(cos_dict_t *pcd, const char *key, const char *value)
+{
+ return cos_dict_put_c_key_string(pcd, key, (byte *)value, strlen(value));
+}
+int
+cos_dict_put_c_strings(cos_dict_t *pcd, const char *key, const char *value)
+{
+ cos_value_t cvalue;
+
+ return cos_dict_put_c_key(pcd, key, cos_c_string_value(&cvalue, value));
+}
+
+/* Move all the elements from one dict to another. */
+int
+cos_dict_move_all(cos_dict_t *pcdto, cos_dict_t *pcdfrom)
+{
+ cos_dict_element_t *pcde = pcdfrom->elements;
+ cos_dict_element_t *head = pcdto->elements;
+
+ while (pcde) {
+ cos_dict_element_t *next = pcde->next;
+
+ if (cos_dict_find(pcdto, pcde->key.data, pcde->key.size)) {
+ /* Free the element, which has been superseded. */
+ cos_dict_element_free(pcdfrom, pcde, "cos_dict_move_all_from");
+ } else {
+ /* Move the element. */
+ pcde->next = head;
+ head = pcde;
+ }
+ pcde = next;
+ }
+ pcdto->elements = head;
+ pcdfrom->elements = 0;
+ pcdto->md5_valid = false;
+ return 0;
+}
+
+/* Look up a key in a dictionary. */
+const cos_value_t *
+cos_dict_find(const cos_dict_t *pcd, const byte *key_data, uint key_size)
+{
+ cos_dict_element_t *pcde = pcd->elements;
+
+ for (; pcde; pcde = pcde->next)
+ if (!bytes_compare(key_data, key_size, pcde->key.data, pcde->key.size))
+ return &pcde->value;
+ return 0;
+}
+const cos_value_t *
+cos_dict_find_c_key(const cos_dict_t *pcd, const char *key)
+{
+ return cos_dict_find(pcd, (const byte *)key, strlen(key));
+}
+
+int cos_dict_hash(const cos_object_t *pco0, gs_md5_state_t *md5, gs_md5_byte_t *hash, gx_device_pdf *pdev)
+{
+ cos_dict_t *dict = (cos_dict_t *) pco0;
+ cos_dict_element_t *pcde0 = dict->elements;
+
+ for (; pcde0; pcde0 = pcde0->next) {
+ gs_md5_append(md5, pcde0->key.data, pcde0->key.size);
+ cos_value_hash(&pcde0->value, md5, hash, pdev);
+ }
+ return 0;
+}
+
+/* Compare two dictionaries. */
+int
+cos_dict_equal(const cos_object_t *pco0, const cos_object_t *pco1, gx_device_pdf *pdev)
+{
+ int code = 0;
+
+ if (!pco0->md5_valid) {
+ gs_md5_init((gs_md5_state_t *)&pco0->md5);
+ code = cos_dict_hash(pco0, (gs_md5_state_t *)&pco0->md5, (gs_md5_byte_t *)pco0->hash, pdev);
+ if (code < 0)
+ return code;
+ gs_md5_finish((gs_md5_state_t *)&pco0->md5, (gs_md5_byte_t *)pco0->hash);
+ ((cos_object_t *)pco0)->md5_valid = true;
+ }
+ if (!pco1->md5_valid) {
+ gs_md5_init((gs_md5_state_t *)&pco1->md5);
+ code = cos_dict_hash(pco1, (gs_md5_state_t *)&pco1->md5, (gs_md5_byte_t *)pco1->hash, pdev);
+ if (code < 0)
+ return code;
+ gs_md5_finish((gs_md5_state_t *)&pco1->md5, (gs_md5_byte_t *)pco1->hash);
+ ((cos_object_t *)pco1)->md5_valid = true;
+ }
+ if (memcmp(&pco0->hash, &pco1->hash, 16) != 0)
+ return false;
+
+ return true;
+}
+
+/* Process all entries in a dictionary. */
+int
+cos_dict_forall(const cos_dict_t *pcd, void *client_data,
+ int (*proc)(void *client_data, const byte *key_data, uint key_size, const cos_value_t *v))
+{
+ cos_dict_element_t *pcde = pcd->elements;
+
+ for (; pcde; pcde = pcde->next) {
+ int code = proc(client_data, pcde->key.data, pcde->key.size, &pcde->value);
+
+ if (code != 0)
+ return code;
+ }
+ return 0;
+}
+
+/* Set up a parameter list that writes into a Cos dictionary. */
+
+/* We'll implement the other printers later if we have to. */
+static param_proc_xmit_typed(cos_param_put_typed);
+static const gs_param_list_procs cos_param_list_writer_procs = {
+ cos_param_put_typed,
+ NULL /* begin_collection */ ,
+ NULL /* end_collection */ ,
+ NULL /* get_next_key */ ,
+ gs_param_request_default,
+ gs_param_requested_default
+};
+static int
+cos_param_put_typed(gs_param_list * plist, gs_param_name pkey,
+ gs_param_typed_value * pvalue)
+{
+ cos_param_list_writer_t *const pclist =
+ (cos_param_list_writer_t *)plist;
+ gx_device_pdf *pdev = pclist->pdev;
+ gs_memory_t *mem = pclist->memory;
+ cos_value_t value;
+ cos_array_t *pca;
+ int key_len = strlen(pkey);
+ byte key_chars[100]; /****** ADHOC ******/
+ int code;
+
+ while(pdev->child)
+ pdev = (gx_device_pdf *)pdev->child;
+
+ if (key_len > sizeof(key_chars) - 1)
+ return_error(gs_error_limitcheck);
+ switch (pvalue->type) {
+ default: {
+ param_printer_params_t ppp;
+ printer_param_list_t pplist;
+ stream s;
+ int len, skip;
+ byte *str;
+
+ s_init(&s, NULL);
+ ppp = param_printer_params_default;
+ ppp.prefix = ppp.suffix = ppp.item_prefix = ppp.item_suffix = 0;
+ ppp.print_ok = pclist->print_ok;
+ s_init_param_printer(&pplist, &ppp, &s);
+ swrite_position_only(&s);
+ param_write_typed((gs_param_list *)&pplist, "", pvalue);
+ len = stell(&s);
+ str = gs_alloc_string(mem, len, "cos_param_put(string)");
+ if (str == 0)
+ return_error(gs_error_VMerror);
+ swrite_string(&s, str, len);
+ param_write_typed((gs_param_list *)&pplist, "", pvalue);
+ /*
+ * The string starts with an initial / or /<space>, which
+ * we need to remove.
+ */
+ skip = (str[1] == ' ' ? 2 : 1);
+ memmove(str, str + skip, len - skip);
+ str = gs_resize_string(mem, str, len, len - skip,
+ "cos_param_put(string)");
+ cos_string_value(&value, str, len - skip);
+ }
+ break;
+ case gs_param_type_int_array: {
+ uint i;
+
+ pca = cos_array_alloc(pdev, "cos_param_put(array)");
+ if (pca == 0)
+ return_error(gs_error_VMerror);
+ for (i = 0; i < pvalue->value.ia.size; ++i)
+ CHECK(cos_array_add_int(pca, pvalue->value.ia.data[i]));
+ }
+ av:
+ cos_object_value(&value, COS_OBJECT(pca));
+ break;
+ case gs_param_type_float_array: {
+ uint i;
+
+ pca = cos_array_alloc(pdev, "cos_param_put(array)");
+ if (pca == 0)
+ return_error(gs_error_VMerror);
+ for (i = 0; i < pvalue->value.ia.size; ++i)
+ CHECK(cos_array_add_real(pca, pvalue->value.fa.data[i]));
+ }
+ goto av;
+ case gs_param_type_string_array:
+ case gs_param_type_name_array:
+ /****** NYI ******/
+ return_error(gs_error_typecheck);
+ }
+ memcpy(key_chars + 1, pkey, key_len);
+ key_chars[0] = '/';
+ return cos_dict_put_no_copy(pclist->pcd, key_chars, key_len + 1, &value);
+}
+
+int
+cos_param_list_writer_init(gx_device_pdf *pdev, cos_param_list_writer_t *pclist, cos_dict_t *pcd,
+ int print_ok)
+{
+ gs_param_list_init((gs_param_list *)pclist, &cos_param_list_writer_procs,
+ COS_OBJECT_MEMORY(pcd));
+ pclist->pcd = pcd;
+ pclist->pdev = pdev;
+ pclist->print_ok = print_ok;
+ return 0;
+}
+
+/* ------ Streams ------ */
+
+static cos_proc_release(cos_stream_release);
+static cos_proc_write(cos_stream_write);
+static cos_proc_equal(cos_stream_equal);
+static cos_proc_hash(cos_stream_hash);
+const cos_object_procs_t cos_stream_procs = {
+ cos_stream_release, cos_stream_write, cos_stream_equal, cos_stream_hash
+};
+
+cos_stream_t *
+cos_stream_alloc(gx_device_pdf *pdev, client_name_t cname)
+{
+ gs_memory_t *mem = pdev->pdf_memory;
+ cos_stream_t *pcs =
+ gs_alloc_struct(mem, cos_stream_t, &st_cos_object, cname);
+
+ cos_object_init((cos_object_t *)pcs, pdev, &cos_stream_procs);
+ return pcs;
+}
+
+static void
+cos_stream_release(cos_object_t *pco, client_name_t cname)
+{
+ cos_stream_t *const pcs = (cos_stream_t *)pco;
+ cos_stream_piece_t *cur;
+ cos_stream_piece_t *next;
+
+ for (cur = pcs->pieces; cur; cur = next) {
+ next = cur->next;
+ gs_free_object(cos_object_memory(pco), cur, cname);
+ }
+ pcs->pieces = 0;
+ cos_dict_release(pco, cname);
+}
+
+static int hash_cos_stream(const cos_object_t *pco0, gs_md5_state_t *md5, gs_md5_byte_t *hash, gx_device_pdf *pdev)
+{
+ const cos_stream_t *pcs = (const cos_stream_t *)pco0;
+ cos_stream_piece_t *pcsp = pcs->pieces;
+ FILE *sfile = pdev->streams.file;
+ byte *ptr;
+ int64_t position_save;
+ int result;
+
+ sflush(pdev->strm);
+ sflush(pdev->streams.strm);
+ position_save = gp_ftell_64(sfile);
+
+ if (!pcsp)
+ return -1;
+
+ gs_md5_init(md5);
+ while(pcsp) {
+ ptr = gs_malloc(pdev->memory, sizeof (byte), pcsp->size, "hash_cos_stream");
+ if (ptr == 0L) {
+ result = gs_note_error(gs_error_VMerror);
+ return result;
+ }
+ if (gp_fseek_64(sfile, pcsp->position, SEEK_SET) != 0)
+ return gs_error_ioerror;
+
+ if (fread(ptr, 1, pcsp->size, sfile) != pcsp->size) {
+ gs_free(pdev->memory, ptr, sizeof (byte), pcsp->size, "hash_cos_stream");
+ result = gs_note_error(gs_error_ioerror);
+ return result;
+ }
+ gs_md5_append(md5, ptr, pcsp->size);
+ gs_free(pdev->memory, ptr, sizeof (byte), pcsp->size, "hash_cos_stream");
+ pcsp = pcsp->next;
+ }
+ gs_md5_finish(md5, (gs_md5_byte_t *)hash);
+ if (gp_fseek_64(sfile, position_save, SEEK_SET) != 0)
+ return gs_error_ioerror;
+
+ return 0;
+}
+
+static int cos_stream_hash(const cos_object_t *pco0, gs_md5_state_t *md5, gs_md5_byte_t *hash, gx_device_pdf *pdev)
+{
+ cos_stream_t *pcs0 = (cos_stream_t *)pco0;
+ int code=0;
+ if (!pco0->stream_md5_valid) {
+ code = hash_cos_stream(pco0, (gs_md5_state_t *)&pco0->md5, (gs_md5_byte_t *)&pco0->stream_hash, pdev);
+ if (code < 0)
+ return code;
+ pcs0->stream_md5_valid = 1;
+ }
+ gs_md5_append(md5, (byte *)&pco0->stream_hash, sizeof(pco0->stream_hash));
+ if (!pco0->md5_valid) {
+ gs_md5_init((gs_md5_state_t *)&pco0->md5);
+ code = cos_dict_hash(pco0, (gs_md5_state_t *)&pco0->md5, (gs_md5_byte_t *)pco0->hash, pdev);
+ if (code < 0)
+ return code;
+ gs_md5_finish((gs_md5_state_t *)&pco0->md5, (gs_md5_byte_t *)pco0->hash);
+ pcs0->md5_valid = 1;
+ }
+ gs_md5_append(md5, (byte *)&pco0->hash, sizeof(pco0->hash));
+ return code;
+}
+
+static int
+cos_stream_equal(const cos_object_t *pco0, const cos_object_t *pco1, gx_device_pdf *pdev)
+{
+ cos_stream_t *pcs0 = (cos_stream_t *)pco0;
+ cos_stream_t *pcs1 = (cos_stream_t *)pco1;
+ int code;
+ gs_md5_state_t md5;
+ gs_md5_byte_t hash[16];
+
+ gs_md5_init(&md5);
+
+ if (!pco0->stream_md5_valid) {
+ code = cos_stream_hash(pco0, &md5, (gs_md5_byte_t *)hash, pdev);
+ if (code < 0)
+ return false;
+ }
+ if (!pco1->stream_md5_valid) {
+ code = cos_stream_hash(pco1, &md5, (gs_md5_byte_t *)hash, pdev);
+ if (code < 0)
+ return false;
+ }
+ if (memcmp(&pcs0->stream_hash, &pcs1->stream_hash, 16) != 0)
+ return false;
+ code = cos_dict_equal(pco0, pco1, pdev);
+ if (code < 0)
+ return false;
+ if (!code)
+ return false;
+ return true;
+}
+
+/* Find the total length of a stream. */
+long
+cos_stream_length(const cos_stream_t *pcs)
+{
+ return pcs->length;
+}
+
+/* Write the (dictionary) elements of a stream. */
+/* (This procedure is exported.) */
+int
+cos_stream_elements_write(const cos_stream_t *pcs, gx_device_pdf *pdev)
+{
+ return cos_elements_write(pdev->strm, pcs->elements, pdev, true, pcs->id);
+}
+
+/* Write the contents of a stream. (This procedure is exported.) */
+int
+cos_stream_contents_write(const cos_stream_t *pcs, gx_device_pdf *pdev)
+{
+ stream *s = pdev->strm;
+ cos_stream_piece_t *pcsp;
+ cos_stream_piece_t *last;
+ cos_stream_piece_t *next;
+ FILE *sfile = pdev->streams.file;
+ int64_t end_pos;
+ bool same_file = (pdev->sbstack_depth > 0);
+ int code;
+ stream_arcfour_state sarc4, *ss = NULL;
+
+ if (pdev->KeyLength) {
+ code = pdf_encrypt_init(pdev, pcs->id, &sarc4);
+ if (code < 0)
+ return code;
+ ss = &sarc4;
+ }
+ sflush(s);
+ sflush(pdev->streams.strm);
+
+ /* Reverse the elements temporarily. */
+ for (pcsp = pcs->pieces, last = NULL; pcsp; pcsp = next)
+ next = pcsp->next, pcsp->next = last, last = pcsp;
+ for (pcsp = last, code = 0; pcsp && code >= 0; pcsp = pcsp->next) {
+ if (same_file)
+ pdf_copy_data_safe(s, sfile, pcsp->position, pcsp->size);
+ else {
+ end_pos = gp_ftell_64(sfile);
+ if (gp_fseek_64(sfile, pcsp->position, SEEK_SET) != 0)
+ return gs_error_ioerror;
+ pdf_copy_data(s, sfile, pcsp->size, ss);
+ if (gp_fseek_64(sfile, end_pos, SEEK_SET) != 0)
+ return gs_error_ioerror;
+ }
+ }
+ /* Reverse the elements back. */
+ for (pcsp = last, last = NULL; pcsp; pcsp = next)
+ next = pcsp->next, pcsp->next = last, last = pcsp;
+
+ return code;
+}
+
+static int
+cos_stream_write(const cos_object_t *pco, gx_device_pdf *pdev, gs_id object_id)
+{
+ stream *s = pdev->strm;
+ const cos_stream_t *const pcs = (const cos_stream_t *)pco;
+ int code;
+
+ if (pcs->input_strm != NULL) {
+ stream *s = pco->input_strm;
+ int status = s_close_filters(&s, NULL);
+
+ if (status < 0)
+ return_error(gs_error_ioerror);
+ /* We have to break const here to clear the input_strm. */
+ ((cos_object_t *)pco)->input_strm = 0;
+ }
+ stream_puts(s, "<<");
+ cos_elements_write(s, pcs->elements, pdev, false, object_id);
+ pprintld1(s, "/Length %ld>>stream\n", cos_stream_length(pcs));
+ code = cos_stream_contents_write(pcs, pdev);
+ stream_puts(s, "\nendstream\n");
+
+ return code;
+}
+
+/* Return a stream's dictionary (just a cast). */
+cos_dict_t *
+cos_stream_dict(cos_stream_t *pcs)
+{
+ return (cos_dict_t *)pcs;
+}
+
+/* Add a contents piece to a stream object: size bytes just written on */
+/* streams.strm. */
+int
+cos_stream_add(gx_device_pdf *pdev, cos_stream_t *pcs, uint size)
+{
+ stream *s;
+ gs_offset_t position;
+ cos_stream_piece_t *prev = pcs->pieces;
+
+ /* Beware of subclassed devices. This is mad IMO, we should store
+ * 's' in the stream state somewhere.
+ */
+ while (pdev->child)
+ pdev = (gx_device_pdf *)pdev->child;
+
+ s = pdev->streams.strm;
+ position = stell(s);
+
+ /* Check for consecutive writing -- just an optimization. */
+ if (prev != 0 && prev->position + prev->size + size == position) {
+ prev->size += size;
+ } else {
+ gs_memory_t *mem = pdev->pdf_memory;
+ cos_stream_piece_t *pcsp =
+ gs_alloc_struct(mem, cos_stream_piece_t, &st_cos_stream_piece,
+ "cos_stream_add");
+
+ if (pcsp == 0)
+ return_error(gs_error_VMerror);
+ pcsp->position = position - size;
+ pcsp->size = size;
+ pcsp->next = pcs->pieces;
+ pcs->pieces = pcsp;
+ }
+ pcs->length += size;
+ return 0;
+}
+
+/* Add bytes to a stream object. */
+int
+cos_stream_add_bytes(gx_device_pdf *pdev, cos_stream_t *pcs, const byte *data, uint size)
+{
+ stream_write(pdev->streams.strm, data, size);
+ return cos_stream_add(pdev, pcs, size);
+}
+
+/* Add the contents of a stream to a stream object. */
+int
+cos_stream_add_stream_contents(gx_device_pdf *pdev, cos_stream_t *pcs, stream *s)
+{
+ int code = 0;
+ byte sbuff[200]; /* arbitrary */
+ uint cnt;
+ int status = sseek(s, 0);
+
+ if (status < 0)
+ return_error(gs_error_ioerror);
+ do {
+ status = sgets(s, sbuff, sizeof(sbuff), &cnt);
+
+ if (cnt == 0) {
+ if (status == EOFC)
+ break;
+ return_error(gs_error_ioerror);
+ }
+ } while ((code = cos_stream_add_bytes(pdev, pcs, sbuff, cnt)) >= 0);
+ return code;
+}
+
+/* Release the last contents piece of a stream object. */
+/* Warning : this function can't release pieces if another stream is written after them. */
+int
+cos_stream_release_pieces(gx_device_pdf *pdev, cos_stream_t *pcs)
+{
+ stream *s = pdev->streams.strm;
+ gs_offset_t position = stell(s), position0 = position;
+
+ while (pcs->pieces != NULL &&
+ position == pcs->pieces->position + pcs->pieces->size) {
+ cos_stream_piece_t *p = pcs->pieces;
+
+ position -= p->size;
+ pcs->pieces = p->next;
+ gs_free_object(cos_object_memory((cos_object_t *)pcs), p, "cos_stream_release_pieces");
+ }
+ if (position0 != position)
+ if (sseek(s, position) < 0)
+ return_error(gs_error_ioerror);
+ return 0;
+}
+
+/* Create a stream that writes into a Cos stream. */
+/* Closing the stream will free it. */
+/* Note that this is not a filter. */
+typedef struct cos_write_stream_state_s {
+ stream_state_common;
+ cos_stream_t *pcs;
+ gx_device_pdf *pdev;
+ stream *s; /* pointer back to stream */
+ stream *target; /* use this instead of strm */
+} cos_write_stream_state_t;
+gs_private_st_suffix_add4(st_cos_write_stream_state, cos_write_stream_state_t,
+ "cos_write_stream_state_t",
+ cos_ws_state_enum_ptrs, cos_ws_state_reloc_ptrs,
+ st_stream_state, pcs, pdev, s, target);
+
+static int
+cos_write_stream_process(stream_state * st, stream_cursor_read * pr,
+ stream_cursor_write * ignore_pw, bool last)
+{
+ uint count = pr->limit - pr->ptr;
+ cos_write_stream_state_t *ss = (cos_write_stream_state_t *)st;
+ stream *target = ss->target;
+ gx_device_pdf *pdev = ss->pdev;
+ gs_offset_t start_pos = stell(pdev->streams.strm);
+ int code;
+
+ stream_write(target, pr->ptr + 1, count);
+ gs_md5_append(&ss->pcs->md5, pr->ptr + 1, count);
+ pr->ptr = pr->limit;
+ sflush(target);
+ code = cos_stream_add(pdev, ss->pcs, (uint)(stell(pdev->streams.strm) - start_pos));
+ return (code < 0 ? ERRC : 0);
+}
+static int
+cos_write_stream_close(stream *s)
+{
+ cos_write_stream_state_t *ss = (cos_write_stream_state_t *)s->state;
+ int status;
+
+ sflush(s);
+ status = s_close_filters(&ss->target, ss->pdev->streams.strm);
+ gs_md5_finish(&ss->pcs->md5, (gs_md5_byte_t *)ss->pcs->stream_hash);
+ ss->pcs->stream_md5_valid = 1;
+ return (status < 0 ? status : s_std_close(s));
+}
+
+static const stream_procs cos_s_procs = {
+ s_std_noavailable, s_std_noseek, s_std_write_reset,
+ s_std_write_flush, cos_write_stream_close, cos_write_stream_process
+};
+static const stream_template cos_write_stream_template = {
+ &st_cos_write_stream_state, 0, cos_write_stream_process, 1, 1
+};
+stream *
+cos_write_stream_alloc(cos_stream_t *pcs, gx_device_pdf *pdev,
+ client_name_t cname)
+{
+ gs_memory_t *mem = pdev->pdf_memory;
+ stream *s = s_alloc(mem, cname);
+ cos_write_stream_state_t *ss = (cos_write_stream_state_t *)
+ s_alloc_state(mem, &st_cos_write_stream_state, cname);
+#define CWS_BUF_SIZE 512 /* arbitrary */
+ byte *buf = gs_alloc_bytes(mem, CWS_BUF_SIZE, cname);
+
+ if (s == 0 || ss == 0 || buf == 0)
+ goto fail;
+ ss->templat = &cos_write_stream_template;
+ ss->pcs = pcs;
+ ss->pcs->stream_md5_valid = 0;
+ gs_md5_init(&ss->pcs->md5);
+ memset(&ss->pcs->hash, 0x00, 16);
+ ss->pdev = pdev;
+ ss->s = s;
+ ss->target = pdev->streams.strm; /* not s->strm */
+ s_std_init(s, buf, CWS_BUF_SIZE, &cos_s_procs, s_mode_write);
+ s->state = (stream_state *)ss;
+ return s;
+#undef CWS_BUF_SIZE
+ fail:
+ gs_free_object(mem, buf, cname);
+ gs_free_object(mem, ss, cname);
+ gs_free_object(mem, s, cname);
+ return 0;
+}
+
+/* Get cos stream from pipeline. */
+cos_stream_t *
+cos_stream_from_pipeline(stream *s)
+{
+ cos_write_stream_state_t *ss;
+
+ while(s->procs.process != cos_s_procs.process)
+ s = s->strm;
+ ss = (cos_write_stream_state_t *)s->state;
+ return ss->pcs;
+}
+
+/* Get cos write stream from pipeline. */
+stream *
+cos_write_stream_from_pipeline(stream *s)
+{
+ while(s->procs.process != cos_s_procs.process)
+ s = s->strm;
+ return s;
+}
diff --git a/devices/vector/gdevpdfo.h b/devices/vector/gdevpdfo.h
new file mode 100644
index 000000000..6237a8580
--- /dev/null
+++ b/devices/vector/gdevpdfo.h
@@ -0,0 +1,355 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Internal definitions for "objects" for pdfwrite driver. */
+
+#ifndef gdevpdfo_INCLUDED
+# define gdevpdfo_INCLUDED
+
+/*
+ * This file defines the structures and support procedures for what Adobe
+ * calls "Cos objects". (We don't know what "Cos" stands for, but in our
+ * case, it's a good acronym for "Collection and stream".) We only
+ * support arrays, dictionaries, and streams: we merge all other types into a
+ * single string type, which holds the printed representation that will be
+ * written on the output file, and we only support that type as an element
+ * of the collection types.
+ *
+ * Note that Cos objects are *not* reference-counted. Objects without
+ * an ID are assumed to be referenced from only one place; objects with
+ * IDs are managed manually. We also assume that strings (the keys of
+ * dictionaries, and non-object values in arrays and dictionaries) are
+ * "owned" by their referencing object.
+ */
+
+#include "gsparam.h"
+#include "smd5.h"
+
+/* Define some needed abstract types. */
+#ifndef gx_device_pdf_DEFINED
+# define gx_device_pdf_DEFINED
+typedef struct gx_device_pdf_s gx_device_pdf;
+#endif
+
+/* ---------------- Structures ---------------- */
+
+/* Define the abstract types if they aren't defined already (in gdevpdfx.h). */
+#ifndef cos_types_DEFINED
+# define cos_types_DEFINED
+typedef struct cos_object_s cos_object_t;
+typedef struct cos_stream_s cos_stream_t;
+typedef struct cos_dict_s cos_dict_t;
+typedef struct cos_array_s cos_array_t;
+typedef struct cos_value_s cos_value_t;
+typedef struct cos_object_procs_s cos_object_procs_t;
+typedef const cos_object_procs_t *cos_type_t;
+#endif
+
+/* Abstract types (defined concretely in gdevpdfo.c) */
+typedef struct cos_element_s cos_element_t;
+typedef struct cos_stream_piece_s cos_stream_piece_t;
+
+/*
+ * Define the object procedures for Cos objects.
+ */
+/*typedef struct cos_object_s cos_object_t;*/
+/*typedef*/ struct cos_object_procs_s {
+
+#define cos_proc_release(proc)\
+ void proc(cos_object_t *pco, client_name_t cname)
+ cos_proc_release((*release));
+
+#define cos_proc_write(proc)\
+ int proc(const cos_object_t *pco, gx_device_pdf *pdev, gs_id object_id)
+ cos_proc_write((*write));
+
+#define cos_proc_equal(proc)\
+ int proc(const cos_object_t *pco0, const cos_object_t *pco1, gx_device_pdf *pdev)
+ cos_proc_equal((*equal));
+
+#define cos_proc_hash(proc)\
+ int proc(const cos_object_t *pco0, gs_md5_state_t *md5, gs_md5_byte_t *hash, gx_device_pdf *pdev)
+ cos_proc_hash((*hash));
+
+} /*cos_object_procs_t*/;
+/*typedef const cos_object_procs_t *cos_type_t;*/
+#define cos_type(pco) ((pco)->cos_procs)
+
+/*
+ * Define the generic structure for Cos objects. Note that all Cos
+ * objects have the same structure and type, aside from their elements.
+ * This allows us to "mutate" a forward-referenced object into its
+ * proper type in place.
+ *
+ * Note that we distinguish elements from contents. Arrays and
+ * dictionaries have only elements; streams have both elements
+ * (additional elements of the stream dictionary) and contents pieces.
+ *
+ * The is_open member currently has no function other than error checking.
+ * It is set to true when the object is created, to false by the CLOSE
+ * and EP pdfmarks, and is checked by CLOSE, PUT, and SP.
+ *
+ * The is_graphics member exists only to ensure that the stream argument
+ * of SP was created by BP/EP.
+ *
+ * The written member records whether the object has been written (copied)
+ * into the contents or resource file.
+ */
+#define cos_object_struct(otype_s, etype)\
+struct otype_s {\
+ const cos_object_procs_t *cos_procs; /* must be first */\
+ long id;\
+ etype *elements;\
+ cos_stream_piece_t *pieces;\
+ gs_memory_t *mem;\
+ pdf_resource_t *pres; /* only for BP/EP XObjects */\
+ byte is_open; /* see above */\
+ byte is_graphics; /* see above */\
+ byte written; /* see above */\
+ long length; /* only for stream objects */\
+ stream *input_strm; /* only for stream objects */\
+ gs_md5_state_t md5; /* used to create MD5 hash to test equality */\
+ int md5_valid; /* 1 if hash created, 0 otherwise */\
+ byte hash[16]; /* MD5 hash value */\
+ int stream_md5_valid; /* (for streams) 1 if hash created, 0 otherwise */\
+ byte stream_hash[16]; /* MD5 hash value (if stream) */\
+ /* input_strm is introduced recently for pdfmark. */\
+ /* Using this field, psdf_binary_writer_s may be simplified. */\
+}
+cos_object_struct(cos_object_s, cos_element_t);
+#define private_st_cos_object() /* in gdevpdfo.c */\
+ gs_private_st_ptrs4(st_cos_object, cos_object_t, "cos_object_t",\
+ cos_object_enum_ptrs, cos_object_reloc_ptrs, elements, pieces,\
+ pres, input_strm)
+extern const cos_object_procs_t cos_generic_procs;
+#define cos_type_generic (&cos_generic_procs)
+
+extern const cos_object_procs_t cos_reference_procs;
+#define cos_type_reference (&cos_reference_procs)
+
+/*
+ * Define the macro for casting any cos object to type cos_object_t.
+ * Using cos_procs ensures that the argument is, in fact, a cos object.
+ */
+#define COS_OBJECT(pc) ((cos_object_t *)&((pc)->cos_procs))
+#define CONST_COS_OBJECT(pc) ((const cos_object_t *)&((pc)->cos_procs))
+
+/*
+ * Define the structure for the value of an array or dictionary element.
+ * This is where we create the union of composite and scalar types.
+ */
+/*typedef struct cos_value_s cos_value_t;*/
+typedef enum {
+ COS_VALUE_SCALAR = 0, /* heap-allocated string */
+ COS_VALUE_CONST, /* shared (constant) string */
+ COS_VALUE_OBJECT, /* object referenced by # # R */
+ COS_VALUE_RESOURCE /* object referenced by /R# */
+} cos_value_type_t;
+struct cos_value_s {
+ cos_value_type_t value_type;
+ union vc_ {
+ gs_string chars; /* SCALAR, CONST */
+ cos_object_t *object; /* OBJECT, RESOURCE */
+ } contents;
+};
+#define private_st_cos_value() /* in gdevpdfo.c */\
+ gs_private_st_composite(st_cos_value, cos_value_t,\
+ "cos_value_t", cos_value_enum_ptrs, cos_value_reloc_ptrs)
+
+/*
+ * Define Cos arrays, dictionaries, and streams.
+ *
+ * The elements of arrays are stored sorted in decreasing index order.
+ * The elements of dictionaries/streams are not sorted.
+ * The contents pieces of streams are stored in reverse order.
+ */
+ /* array */
+typedef struct cos_array_element_s cos_array_element_t;
+cos_object_struct(cos_array_s, cos_array_element_t);
+extern const cos_object_procs_t cos_array_procs;
+#define cos_type_array (&cos_array_procs)
+ /* dict */
+typedef struct cos_dict_element_s cos_dict_element_t;
+cos_object_struct(cos_dict_s, cos_dict_element_t);
+extern const cos_object_procs_t cos_dict_procs;
+#define cos_type_dict (&cos_dict_procs)
+ /* stream */
+cos_object_struct(cos_stream_s, cos_dict_element_t);
+extern const cos_object_procs_t cos_stream_procs;
+#define cos_type_stream (&cos_stream_procs)
+
+/* ---------------- Procedures ---------------- */
+
+/*
+ * NOTE: Procedures that include "_c_" in their name do not copy their C
+ * string argument(s). To copy the argument, use the procedure that takes
+ * a byte pointer and a length.
+ */
+
+/* Create a Cos object. */
+cos_object_t *cos_object_alloc(gx_device_pdf *, client_name_t);
+cos_array_t *cos_array_alloc(gx_device_pdf *, client_name_t);
+cos_array_t *cos_array_from_floats(gx_device_pdf *, const float *, uint,
+ client_name_t);
+cos_dict_t *cos_dict_alloc(gx_device_pdf *, client_name_t);
+cos_stream_t *cos_stream_alloc(gx_device_pdf *, client_name_t);
+
+/* A 'dummy object, used only to create a reference in a dict when writing */
+cos_object_t *cos_reference_alloc(gx_device_pdf *, client_name_t);
+
+/* Get the allocator for a Cos object. */
+gs_memory_t *cos_object_memory(const cos_object_t *);
+#define COS_OBJECT_MEMORY(pc) cos_object_memory(CONST_COS_OBJECT(pc))
+
+/* Set the type of a generic Cos object. */
+int cos_become(cos_object_t *, cos_type_t);
+
+/* Define wrappers for calling the object procedures. */
+cos_proc_release(cos_release);
+#define COS_RELEASE(pc, cname) cos_release(COS_OBJECT(pc), cname)
+cos_proc_write(cos_write);
+int cos_write_dict_as_ordered_array(cos_object_t *pco, gx_device_pdf *pdev, pdf_resource_type_t type);
+#define COS_WRITE(pc, pdev) cos_write(CONST_COS_OBJECT(pc), pdev, (pc)->id)
+
+/* Make a value to store into a composite object. */
+const cos_value_t *cos_string_value(cos_value_t *, const byte *, uint);
+const cos_value_t *cos_c_string_value(cos_value_t *, const char *);
+const cos_value_t *cos_object_value(cos_value_t *, cos_object_t *);
+#define COS_OBJECT_VALUE(pcv, pc) cos_object_value(pcv, COS_OBJECT(pc))
+/* A resource value is an object value referenced as /R#, not # # R. */
+const cos_value_t *cos_resource_value(cos_value_t *, cos_object_t *);
+#define COS_RESOURCE_VALUE(pcv, pc) cos_resource_value(pcv, COS_OBJECT(pc))
+
+/* Test whether a value is an object */
+#define COS_VALUE_IS_OBJECT(pv) ((pv)->value_type >= COS_VALUE_OBJECT)
+
+/*
+ * Put an element in / add an element to a Cos object. The _no_copy
+ * procedures assume that the value, if not an object, is a newly allocated,
+ * unshared string, allocated by the same allocator as the collection
+ * itself, that should not be copied.
+ */
+ /* array */
+int cos_array_put(cos_array_t *, long, const cos_value_t *);
+int cos_array_put_no_copy(cos_array_t *, long, const cos_value_t *);
+int cos_array_add(cos_array_t *, const cos_value_t *);
+int cos_array_add_no_copy(cos_array_t *, const cos_value_t *);
+int cos_array_add_c_string(cos_array_t *, const char *);
+int cos_array_add_int(cos_array_t *, int);
+int cos_array_add_real(cos_array_t *, double);
+int cos_array_add_object(cos_array_t *, cos_object_t *);
+/* add adds at the end, unadd removes the last element */
+int cos_array_unadd(cos_array_t *, cos_value_t *);
+ /* dict */
+int cos_dict_put(cos_dict_t *, const byte *, uint, const cos_value_t *);
+int cos_dict_put_no_copy(cos_dict_t *, const byte *, uint,
+ const cos_value_t *);
+int cos_dict_put_c_key(cos_dict_t *, const char *, const cos_value_t *);
+int cos_dict_put_c_key_string(cos_dict_t *, const char *, const byte *, uint);
+int cos_dict_put_c_key_int(cos_dict_t *, const char *, int);
+int cos_dict_put_c_key_bool(cos_dict_t *pcd, const char *key, bool value);
+int cos_dict_put_c_key_real(cos_dict_t *, const char *, double);
+int cos_dict_put_c_key_floats(gx_device_pdf *pdev, cos_dict_t *, const char *, const float *, uint);
+int cos_dict_put_c_key_object(cos_dict_t *, const char *, cos_object_t *);
+int cos_dict_put_string(cos_dict_t *, const byte *, uint, const byte *, uint);
+int cos_dict_put_string_copy(cos_dict_t *pcd, const char *key, const char *value);
+int cos_dict_put_c_strings(cos_dict_t *, const char *, const char *);
+/* move all the elements from one dict to another */
+int cos_dict_move_all(cos_dict_t *, cos_dict_t *);
+ /* stream */
+int cos_stream_add(gx_device_pdf *pdev, cos_stream_t *, uint);
+int cos_stream_add_bytes(gx_device_pdf *pdev, cos_stream_t *, const byte *, uint);
+int cos_stream_add_stream_contents(gx_device_pdf *pdev, cos_stream_t *, stream *);
+int cos_stream_release_pieces(gx_device_pdf *pdev, cos_stream_t *pcs);
+cos_dict_t *cos_stream_dict(cos_stream_t *);
+
+/* Remove an element from a cos_dict */
+int cos_dict_delete_c_key(cos_dict_t *pcd, const char *key);
+
+/*
+ * Get the first / next element for enumerating an array. Usage:
+ * const cos_array_element_t *elt = cos_array_element_first(pca);
+ * while (elt) {
+ * long idx;
+ * const cos_value_t *pvalue;
+ * elt = cos_array_element_next(elt, &idx, &pvalue);
+ * ...
+ * }
+ * The order in which the elements are returned is not defined.
+ * If the client adds elements to the array during the enumeration,
+ * they may or may not be included in the enumeration.
+ */
+const cos_array_element_t *
+ cos_array_element_first(const cos_array_t *);
+const cos_array_element_t *
+ cos_array_element_next(const cos_array_element_t *, long *,
+ const cos_value_t **);
+
+/* Look up a key in a dictionary. */
+const cos_value_t *cos_dict_find(const cos_dict_t *, const byte *, uint);
+const cos_value_t *cos_dict_find_c_key(const cos_dict_t *, const char *);
+/* Process all entries in a dictionary. */
+int cos_dict_forall(const cos_dict_t *pcd, void *client_data,
+ int (*proc)(void *client_data, const byte *key_data, uint key_size, const cos_value_t *v));
+
+/* Set up a parameter list that writes into a Cos dictionary. */
+typedef struct cos_param_list_writer_s {
+ gs_param_list_common;
+ cos_dict_t *pcd;
+ int print_ok;
+ gx_device_pdf *pdev;
+} cos_param_list_writer_t;
+int cos_param_list_writer_init(gx_device_pdf *pdev, cos_param_list_writer_t *, cos_dict_t *,
+ int print_ok);
+
+/* Create a stream that writes into a Cos stream. */
+/* Closing the stream will free it. */
+stream *cos_write_stream_alloc(cos_stream_t *pcs, gx_device_pdf *pdev,
+ client_name_t cname);
+
+/* Get cos stream from pipeline. */
+cos_stream_t * cos_stream_from_pipeline(stream *s);
+/* Get cos write stream from pipeline. */
+stream * cos_write_stream_from_pipeline(stream *s);
+
+/* Write a Cos value on the output. */
+int cos_value_write(const cos_value_t *, gx_device_pdf *);
+
+/* Write the elements of a dictionary/stream on the output. */
+int cos_dict_elements_write(const cos_dict_t *, gx_device_pdf *);
+int cos_stream_elements_write(const cos_stream_t *, gx_device_pdf *); /* = dict_elements_write */
+int cos_stream_contents_write(const cos_stream_t *, gx_device_pdf *);
+
+/* Find the total length of a stream. */
+long cos_stream_length(const cos_stream_t *pcs);
+
+/* Write/delete definitions of named objects. */
+/* This is a special-purpose facility for pdf_close. */
+int cos_dict_objects_write(const cos_dict_t *, gx_device_pdf *);
+int cos_dict_objects_delete(cos_dict_t *);
+
+/* Write a cos object as a PDF object. */
+int cos_write_object(cos_object_t *pco, gx_device_pdf *pdev, pdf_resource_type_t type);
+#define COS_WRITE_OBJECT(pc, pdev, type) cos_write_object(COS_OBJECT(pc), pdev, type)
+
+/* Free a Cos value owned by a Cos object. */
+void cos_value_free(const cos_value_t *, const cos_object_t *, client_name_t);
+
+/* Free a cos object. */
+void cos_free(cos_object_t *pco, client_name_t cname);
+#define COS_FREE(pc, cname) cos_free(COS_OBJECT(pc), cname)
+
+#endif /* gdevpdfo_INCLUDED */
diff --git a/devices/vector/gdevpdfp.c b/devices/vector/gdevpdfp.c
new file mode 100644
index 000000000..3924ee929
--- /dev/null
+++ b/devices/vector/gdevpdfp.c
@@ -0,0 +1,940 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Get/put parameters for PDF-writing driver */
+#include "memory_.h"
+#include "gx.h"
+#include "gserrors.h"
+#include "gdevpdfx.h"
+#include "gdevpdfo.h"
+#include "gdevpdfg.h"
+#include "gdevpsdf.h"
+#include "gsparamx.h"
+#include "gsicc_manage.h"
+
+/*
+ * The pdfwrite device supports the following "real" parameters:
+ * OutputFile <string>
+ * all the Distiller parameters (also see gdevpsdp.c)
+ * Only some of the Distiller parameters actually have any effect.
+ *
+ * The device also supports the following write-only pseudo-parameters that
+ * serve only to communicate other information from the PostScript file.
+ * Their "value" is an array of strings, some of which may be the result
+ * of converting arbitrary PostScript objects to string form.
+ * pdfmark - see gdevpdfm.c
+ * DSC - processed in this file
+ */
+static int pdf_dsc_process(gx_device_pdf * pdev,
+ const gs_param_string_array * pma);
+
+static const int CoreDistVersion = 5000; /* Distiller 5.0 */
+static const gs_param_item_t pdf_param_items[] = {
+#define pi(key, type, memb) { key, type, offset_of(gx_device_pdf, memb) }
+
+ /* Acrobat Distiller 4 parameters */
+
+ /*
+ * EndPage and StartPage are renamed because EndPage collides with
+ * a page device parameter.
+ */
+ pi("PDFEndPage", gs_param_type_int, EndPage),
+ pi("PDFStartPage", gs_param_type_int, StartPage),
+ pi("Optimize", gs_param_type_bool, Optimize),
+ pi("ParseDSCCommentsForDocInfo", gs_param_type_bool,
+ ParseDSCCommentsForDocInfo),
+ pi("ParseDSCComments", gs_param_type_bool, ParseDSCComments),
+ pi("EmitDSCWarnings", gs_param_type_bool, EmitDSCWarnings),
+ pi("CreateJobTicket", gs_param_type_bool, CreateJobTicket),
+ pi("PreserveEPSInfo", gs_param_type_bool, PreserveEPSInfo),
+ pi("AutoPositionEPSFiles", gs_param_type_bool, AutoPositionEPSFiles),
+ pi("PreserveCopyPage", gs_param_type_bool, PreserveCopyPage),
+ pi("UsePrologue", gs_param_type_bool, UsePrologue),
+
+ /* Acrobat Distiller 5 parameters */
+
+ pi("OffOptimizations", gs_param_type_int, OffOptimizations),
+
+ /* Ghostscript-specific parameters */
+
+ pi("ReAssignCharacters", gs_param_type_bool, ReAssignCharacters),
+ pi("ReEncodeCharacters", gs_param_type_bool, ReEncodeCharacters),
+ pi("FirstObjectNumber", gs_param_type_long, FirstObjectNumber),
+ pi("CompressFonts", gs_param_type_bool, CompressFonts),
+ pi("PrintStatistics", gs_param_type_bool, PrintStatistics),
+ pi("MaxInlineImageSize", gs_param_type_long, MaxInlineImageSize),
+ pi("DSCEncodingToUnicode", gs_param_type_int_array, DSCEncodingToUnicode),
+
+ /* PDF Encryption */
+ pi("OwnerPassword", gs_param_type_string, OwnerPassword),
+ pi("UserPassword", gs_param_type_string, UserPassword),
+ pi("KeyLength", gs_param_type_int, KeyLength),
+ pi("Permissions", gs_param_type_int, Permissions),
+ pi("EncryptionR", gs_param_type_int, EncryptionR),
+ pi("NoEncrypt", gs_param_type_string, NoEncrypt),
+
+ /* Target viewer capabilities (Ghostscript-specific) */
+ /* pi("ForOPDFRead", gs_param_type_bool, ForOPDFRead), pdfwrite-only */
+ pi("ProduceDSC", gs_param_type_bool, ProduceDSC),
+ pi("PatternImagemask", gs_param_type_bool, PatternImagemask),
+ pi("MaxClipPathSize", gs_param_type_int, MaxClipPathSize),
+ pi("MaxShadingBitmapSize", gs_param_type_int, MaxShadingBitmapSize),
+ pi("HaveTrueTypes", gs_param_type_bool, HaveTrueTypes),
+ pi("HaveCIDSystem", gs_param_type_bool, HaveCIDSystem),
+ pi("HaveTransparency", gs_param_type_bool, HaveTransparency),
+ pi("CompressEntireFile", gs_param_type_bool, CompressEntireFile),
+ pi("PDFX", gs_param_type_bool, PDFX),
+ pi("PDFA", gs_param_type_int, PDFA),
+ pi("DocumentUUID", gs_param_type_string, DocumentUUID),
+ pi("InstanceUUID", gs_param_type_string, InstanceUUID),
+ pi("DocumentTimeSeq", gs_param_type_int, DocumentTimeSeq),
+
+ /* PDF/X parameters */
+ pi("PDFXTrimBoxToMediaBoxOffset", gs_param_type_float_array, PDFXTrimBoxToMediaBoxOffset),
+ pi("PDFXSetBleedBoxToMediaBox", gs_param_type_bool, PDFXSetBleedBoxToMediaBox),
+ pi("PDFXBleedBoxToTrimBoxOffset", gs_param_type_float_array, PDFXBleedBoxToTrimBoxOffset),
+
+ /* media selection parameters */
+ pi("SetPageSize", gs_param_type_bool, SetPageSize),
+ pi("RotatePages", gs_param_type_bool, RotatePages),
+ pi("FitPages", gs_param_type_bool, FitPages),
+ pi("CenterPages", gs_param_type_bool, CenterPages),
+ pi("DoNumCopies", gs_param_type_bool, DoNumCopies),
+ pi("PreserveSeparation", gs_param_type_bool, PreserveSeparation),
+ pi("PreserveDeviceN", gs_param_type_bool, PreserveDeviceN),
+ pi("PDFACompatibilityPolicy", gs_param_type_int, PDFACompatibilityPolicy),
+ pi("DetectDuplicateImages", gs_param_type_bool, DetectDuplicateImages),
+ pi("AllowIncrementalCFF", gs_param_type_bool, AllowIncrementalCFF),
+ pi("WantsToUnicode", gs_param_type_bool, WantsToUnicode),
+ pi("AllowPSRepeatFunctions", gs_param_type_bool, AllowPSRepeatFunctions),
+ pi("IsDistiller", gs_param_type_bool, IsDistiller),
+ pi("PreserveSMask", gs_param_type_bool, PreserveSMask),
+ pi("PreserveTrMode", gs_param_type_bool, PreserveTrMode),
+ pi("NoT3CCITT", gs_param_type_bool, NoT3CCITT),
+ pi("PDFUseOldCMS", gs_param_type_bool, UseOldColor),
+ pi("FastWebView", gs_param_type_bool, Linearise),
+ pi("NoOutputFonts", gs_param_type_bool, FlattenFonts),
+#undef pi
+ gs_param_item_end
+};
+
+/*
+ Notes on implementing the remaining Distiller functionality
+ ===========================================================
+
+ Architectural issues
+ --------------------
+
+ Must optionally disable application of TR, BG, UCR similarly. Affects:
+ PreserveHalftoneInfo
+ PreserveOverprintSettings
+ TransferFunctionInfo
+ UCRandBGInfo
+
+ Current limitations
+ -------------------
+
+ Non-primary elements in HalftoneType 5 are not written correctly
+
+ Acrobat Distiller 3
+ -------------------
+
+ ---- Image parameters ----
+
+ AntiAlias{Color,Gray,Mono}Images
+
+ ---- Other parameters ----
+
+ CompressPages
+ Compress things other than page contents
+ * PreserveHalftoneInfo
+ PreserveOPIComments
+ ? see OPI spec?
+ * PreserveOverprintSettings
+ * TransferFunctionInfo
+ * UCRandBGInfo
+ ColorConversionStrategy
+ Select color space for drawing commands
+ ConvertImagesToIndexed
+ Postprocess image data *after* downsampling (requires an extra pass)
+
+ Acrobat Distiller 4
+ -------------------
+
+ ---- Other functionality ----
+
+ Document structure pdfmarks
+
+ ---- Parameters ----
+
+ xxxDownsampleType = /Bicubic
+ Add new filter (or use siscale?) & to setup (gdevpsdi.c)
+ DetectBlends
+ Idiom recognition? PatternType 2 patterns / shfill? (see AD4)
+ DoThumbnails
+ Also output to memory device -- resolution issue
+
+ ---- Job-level control ----
+
+ EmitDSCWarnings
+ Require DSC parser / interceptor
+ CreateJobTicket
+ ?
+ AutoPositionEPSFiles
+ Require DSC parsing
+ PreserveCopyPage
+ Concatenate Contents streams
+ UsePrologue
+ Needs hack in top-level control?
+
+*/
+
+/* Transfer a collection of parameters. */
+static const byte xfer_item_sizes[] = {
+ GS_PARAM_TYPE_SIZES(0)
+};
+int
+gdev_pdf_get_param(gx_device *dev, char *Param, void *list)
+{
+ gx_device_pdf *pdev = (gx_device_pdf *)dev;
+ const gs_param_item_t *pi;
+ gs_param_list * plist = (gs_param_list *)list;
+ int code = 0;
+
+ for (pi = pdf_param_items; pi->key != 0; ++pi) {
+ if (strcmp(pi->key, Param) == 0) {
+ const char *key = pi->key;
+ const void *pvalue = (const void *)((const char *)pdev + pi->offset);
+ int size = xfer_item_sizes[pi->type];
+ gs_param_typed_value typed;
+
+ memcpy(&typed.value, pvalue, size);
+ typed.type = pi->type;
+ code = (*plist->procs->xmit_typed) (plist, key, &typed);
+ return code;
+ }
+ }
+ if (strcmp(Param, "CoreDistVersion") == 0) {
+ return(param_write_int(plist, "CoreDistVersion", &CoreDistVersion));
+ }
+ if (strcmp(Param, "CompatibilityLevel") == 0) {
+ float f = pdev->CompatibilityLevel;
+ return(param_write_float(plist, "CompatibilityLevel", &f));
+ }
+ if (strcmp(Param, "ForOPDFRead") == 0) {
+ return(param_write_bool(plist, "ForOPDFRead", &pdev->ForOPDFRead));
+ }
+ if (!pdev->is_ps2write) {
+ if (strcmp(Param, "pdfmark") == 0){
+ return(param_write_null(plist, "pdfmark"));
+ }
+ if (strcmp(Param, "DSC") == 0){
+ return(param_write_null(plist, "DSC"));
+ }
+ }
+ return gdev_psdf_get_param(dev, Param, list);
+}
+
+/* ---------------- Get parameters ---------------- */
+
+/* Get parameters. */
+int
+gdev_pdf_get_params(gx_device * dev, gs_param_list * plist)
+{
+ gx_device_pdf *pdev = (gx_device_pdf *) dev;
+ float cl = (float)pdev->CompatibilityLevel;
+ int code;
+ int cdv = CoreDistVersion;
+
+ pdev->ParamCompatibilityLevel = cl;
+ code = gdev_psdf_get_params(dev, plist);
+ if (code < 0 ||
+ (code = param_write_int(plist, "CoreDistVersion", &cdv)) < 0 ||
+ (code = param_write_float(plist, "CompatibilityLevel", &cl)) < 0 ||
+ (!pdev->is_ps2write && (code = param_write_bool(plist, "ForOPDFRead", &pdev->ForOPDFRead)) < 0) ||
+ /* Indicate that we can process pdfmark and DSC. */
+ (param_requested(plist, "pdfmark") > 0 &&
+ (code = param_write_null(plist, "pdfmark")) < 0) ||
+ (param_requested(plist, "DSC") > 0 &&
+ (code = param_write_null(plist, "DSC")) < 0) ||
+ (code = gs_param_write_items(plist, pdev, NULL, pdf_param_items)) < 0
+ )
+ {}
+ return code;
+}
+
+/* ---------------- Put parameters ---------------- */
+
+/* Put parameters, implementation */
+static int
+gdev_pdf_put_params_impl(gx_device * dev, const gx_device_pdf * save_dev, gs_param_list * plist)
+{
+ int ecode, code;
+ gx_device_pdf *pdev = (gx_device_pdf *) dev;
+ float cl = (float)pdev->CompatibilityLevel;
+ bool locked = pdev->params.LockDistillerParams, ForOPDFRead;
+ gs_param_name param_name;
+ enum psdf_color_conversion_strategy save_ccs = pdev->params.ColorConversionStrategy;
+
+ pdev->pdf_memory = gs_memory_stable(pdev->memory);
+ /*
+ * If this is a pseudo-parameter (pdfmark or DSC),
+ * don't bother checking for any real ones.
+ */
+
+ {
+ gs_param_string_array ppa;
+ gs_param_string pps;
+
+ code = param_read_string_array(plist, (param_name = "pdfmark"), &ppa);
+ switch (code) {
+ case 0:
+ code = pdfwrite_pdf_open_document(pdev);
+ if (code < 0)
+ return code;
+ code = pdfmark_process(pdev, &ppa);
+ if (code >= 0)
+ return code;
+ /* falls through for errors */
+ default:
+ param_signal_error(plist, param_name, code);
+ return code;
+ case 1:
+ break;
+ }
+
+ code = param_read_string_array(plist, (param_name = "DSC"), &ppa);
+ switch (code) {
+ case 0:
+ code = pdfwrite_pdf_open_document(pdev);
+ if (code < 0)
+ return code;
+ code = pdf_dsc_process(pdev, &ppa);
+ if (code >= 0)
+ return code;
+ /* falls through for errors */
+ default:
+ param_signal_error(plist, param_name, code);
+ return code;
+ case 1:
+ break;
+ }
+
+ code = param_read_string(plist, (param_name = "pdfpagelabels"), &pps);
+ switch (code) {
+ case 0:
+ {
+ if (!pdev->ForOPDFRead) {
+ cos_dict_t *const pcd = pdev->Catalog;
+ code = pdfwrite_pdf_open_document(pdev);
+ if (code < 0)
+ return code;
+ code = cos_dict_put_string(pcd, (const byte *)"/PageLabels", 11,
+ pps.data, pps.size);
+ if (code >= 0)
+ return code;
+ } else
+ return 0;
+ }
+ /* falls through for errors */
+ default:
+ param_signal_error(plist, param_name, code);
+ return code;
+ case 1:
+ break;
+ }
+ }
+
+ /*
+ * Check for LockDistillerParams before doing anything else.
+ * If LockDistillerParams is true and is not being set to false,
+ * ignore all resettings of PDF-specific parameters. Note that
+ * LockDistillerParams is read again, and reset if necessary, in
+ * psdf_put_params.
+ */
+ ecode = param_read_bool(plist, "LockDistillerParams", &locked);
+ if (ecode < 0)
+ param_signal_error(plist, param_name, ecode);
+
+ /* General parameters. */
+
+ {
+ int efo = 1;
+
+ ecode = param_put_int(plist, (param_name = ".EmbedFontObjects"), &efo, ecode);
+ if (ecode < 0)
+ param_signal_error(plist, param_name, ecode);
+ if (efo != 1)
+ param_signal_error(plist, param_name, ecode = gs_error_rangecheck);
+ }
+ {
+ int cdv = CoreDistVersion;
+
+ ecode = param_put_int(plist, (param_name = "CoreDistVersion"), &cdv, ecode);
+ if (ecode < 0)
+ return gs_note_error(ecode);
+ if (cdv != CoreDistVersion)
+ param_signal_error(plist, param_name, ecode = gs_error_rangecheck);
+ }
+
+ switch (code = param_read_float(plist, (param_name = "CompatibilityLevel"), &cl)) {
+ default:
+ ecode = code;
+ param_signal_error(plist, param_name, ecode);
+ break;
+ case 0:
+ if (!(locked && pdev->params.LockDistillerParams)) {
+ /*
+ * Must be 1.2, 1.3, 1.4, or 1.5. Per Adobe documentation, substitute
+ * the nearest achievable value.
+ */
+ if (cl < (float)1.15)
+ cl = (float)1.1;
+ else if (cl < (float)1.25)
+ cl = (float)1.2;
+ else if (cl < (float)1.35)
+ cl = (float)1.3;
+ else if (cl < (float)1.45)
+ cl = (float)1.4;
+ else if (cl < (float)1.55)
+ cl = (float)1.5;
+ else if (cl < (float)1.65)
+ cl = (float)1.6;
+ else
+ cl = (float)1.7;
+ }
+ case 1:
+ break;
+ }
+ { /* HACK : gs_param_list_s::memory is documented in gsparam.h as
+ "for allocating coerced arrays". Not sure why zputdeviceparams
+ sets it to the current memory space, while the device
+ assumes to store them in the device's memory space.
+ As a hackish workaround we temporary replace it here.
+ Doing so because we don't want to change the global code now
+ because we're unable to test it with all devices.
+ Bug 688531 "Segmentation fault running pdfwrite from 219-01.ps".
+
+ This solution to be reconsidered after fixing
+ the bug 688533 "zputdeviceparams specifies a wrong memory space.".
+ */
+ gs_memory_t *mem = plist->memory;
+
+ plist->memory = pdev->pdf_memory;
+ code = gs_param_read_items(plist, pdev, pdf_param_items);
+ if (code < 0 || (code = param_read_bool(plist, "ForOPDFRead", &ForOPDFRead)) < 0)
+ {
+ }
+ if (code == 0 && !pdev->is_ps2write && !(locked && pdev->params.LockDistillerParams))
+ pdev->ForOPDFRead = ForOPDFRead;
+ plist->memory = mem;
+ }
+ if (code < 0)
+ ecode = code;
+ {
+ /*
+ * Setting FirstObjectNumber is only legal if the file
+ * has just been opened and nothing has been written,
+ * or if we are setting it to the same value.
+ */
+ long fon = pdev->FirstObjectNumber;
+
+ if (fon != save_dev->FirstObjectNumber) {
+ if (fon <= 0 || fon > 0x7fff0000 ||
+ (pdev->next_id != 0 &&
+ pdev->next_id !=
+ save_dev->FirstObjectNumber + pdf_num_initial_ids)
+ ) {
+ ecode = gs_error_rangecheck;
+ param_signal_error(plist, "FirstObjectNumber", ecode);
+ }
+ }
+ }
+ {
+ /*
+ * Set ProcessColorModel now, because gx_default_put_params checks
+ * it.
+ */
+ static const char *const pcm_names[] = {
+ "DeviceGray", "DeviceRGB", "DeviceCMYK", "DeviceN", 0
+ };
+ int pcm = -1;
+
+ ecode = param_put_enum(plist, "ProcessColorModel", &pcm,
+ pcm_names, ecode);
+ if (pcm >= 0) {
+ pdf_set_process_color_model(pdev, pcm);
+ pdf_set_initial_color(pdev, &pdev->saved_fill_color, &pdev->saved_stroke_color,
+ &pdev->fill_used_process_color, &pdev->stroke_used_process_color);
+ }
+ }
+ if (ecode < 0)
+ goto fail;
+
+ if (pdev->is_ps2write && (code = param_read_bool(plist, "ProduceDSC", &pdev->ProduceDSC)) < 0) {
+ param_signal_error(plist, param_name, code);
+ }
+
+ /* PDFA and PDFX are stored in the page device dictionary and therefore
+ * set on every setpagedevice. However, if we have encountered a file which
+ * can't be made this way, and the PDFACompatibilityPolicy is 1, we want to
+ * continue producing the file, but not as a PDF/A or PDF/X file. Its more
+ * or less impossible to alter the setting in the (potentially saved) page
+ * device dictionary, so we use this rather clunky method.
+ */
+ if (pdev->PDFA < 0 || pdev->PDFA > 2){
+ ecode = gs_note_error(gs_error_rangecheck);
+ param_signal_error(plist, "PDFA", ecode);
+ goto fail;
+ }
+ if(pdev->PDFA != 0 && pdev->AbortPDFAX)
+ pdev->PDFA = 0;
+ if(pdev->PDFX && pdev->AbortPDFAX)
+ pdev->PDFX = 0;
+ if (pdev->PDFX && pdev->PDFA != 0) {
+ ecode = gs_note_error(gs_error_rangecheck);
+ param_signal_error(plist, "PDFA", ecode);
+ goto fail;
+ }
+ if (pdev->PDFX && pdev->ForOPDFRead) {
+ ecode = gs_note_error(gs_error_rangecheck);
+ param_signal_error(plist, "PDFX", ecode);
+ goto fail;
+ }
+ if (pdev->PDFA != 0 && pdev->ForOPDFRead) {
+ ecode = gs_note_error(gs_error_rangecheck);
+ param_signal_error(plist, "PDFA", ecode);
+ goto fail;
+ }
+ if (pdev->PDFA == 1 || pdev->PDFX || pdev->CompatibilityLevel < 1.4) {
+ pdev->HaveTransparency = false;
+ pdev->PreserveSMask = false;
+ }
+
+ /*
+ * We have to set version to the new value, because the set of
+ * legal parameter values for psdf_put_params varies according to
+ * the version.
+ */
+ if (pdev->PDFX)
+ cl = (float)1.3; /* Instead pdev->CompatibilityLevel = 1.2; - see below. */
+ if (pdev->PDFA != 0 && cl < 1.4)
+ cl = (float)1.4;
+ pdev->version = (cl < 1.2 ? psdf_version_level2 : psdf_version_ll3);
+ if (pdev->ForOPDFRead) {
+ pdev->ResourcesBeforeUsage = true;
+ pdev->HaveCFF = false;
+ pdev->HavePDFWidths = false;
+ pdev->HaveStrokeColor = false;
+ cl = (float)1.2; /* Instead pdev->CompatibilityLevel = 1.2; - see below. */
+ pdev->MaxInlineImageSize = max_long; /* Save printer's RAM from saving temporary image data.
+ Immediate images doen't need buffering. */
+ pdev->version = psdf_version_level2;
+ } else {
+ pdev->ResourcesBeforeUsage = false;
+ pdev->HaveCFF = true;
+ pdev->HavePDFWidths = true;
+ pdev->HaveStrokeColor = true;
+ }
+ pdev->ParamCompatibilityLevel = cl;
+ if (cl < 1.2) {
+ pdev->HaveCFF = false;
+ }
+ ecode = gdev_psdf_put_params(dev, plist);
+ if (ecode < 0)
+ goto fail;
+ if (!pdev->UseOldColor) {
+ if (pdev->params.ConvertCMYKImagesToRGB) {
+ if (pdev->params.ColorConversionStrategy == ccs_CMYK) {
+ emprintf(pdev->memory, "ConvertCMYKImagesToRGB is not compatible with ColorConversionStrategy of CMYK\n");
+ } else {
+ if (pdev->params.ColorConversionStrategy == ccs_Gray) {
+ emprintf(pdev->memory, "ConvertCMYKImagesToRGB is not compatible with ColorConversionStrategy of Gray\n");
+ } else {
+ if (pdev->icc_struct)
+ rc_decrement(pdev->icc_struct,
+ "reset default profile\n");
+ pdf_set_process_color_model(pdev,1);
+ ecode = gsicc_init_device_profile_struct((gx_device *)pdev, NULL, 0);
+ if (ecode < 0)
+ goto fail;
+ }
+ }
+ }
+ switch (pdev->params.ColorConversionStrategy) {
+ case ccs_LeaveColorUnchanged:
+ case ccs_UseDeviceDependentColor:
+ case ccs_UseDeviceIndependentColor:
+ case ccs_UseDeviceIndependentColorForImages:
+ case ccs_ByObjectType:
+ break;
+ case ccs_CMYK:
+ if (pdev->icc_struct)
+ rc_decrement(pdev->icc_struct,
+ "reset default profile\n");
+ pdf_set_process_color_model(pdev, 2);
+ ecode = gsicc_init_device_profile_struct((gx_device *)pdev, NULL, 0);
+ if (ecode < 0)
+ goto fail;
+ break;
+ case ccs_Gray:
+ if (pdev->icc_struct)
+ rc_decrement(pdev->icc_struct,
+ "reset default profile\n");
+ pdf_set_process_color_model(pdev,0);
+ ecode = gsicc_init_device_profile_struct((gx_device *)pdev, NULL, 0);
+ if (ecode < 0)
+ goto fail;
+ break;
+ case ccs_sRGB:
+ case ccs_RGB:
+ /* Only bother to do this if we didn't handle it above */
+ if (!pdev->params.ConvertCMYKImagesToRGB) {
+ if (pdev->icc_struct)
+ rc_decrement(pdev->icc_struct,
+ "reset default profile\n");
+ pdf_set_process_color_model(pdev,1);
+ ecode = gsicc_init_device_profile_struct((gx_device *)pdev, NULL, 0);
+ if (ecode < 0)
+ goto fail;
+ }
+ break;
+ default:
+ break;
+ }
+ } else {
+ if ((pdev->params.ColorConversionStrategy == ccs_CMYK &&
+ strcmp(pdev->color_info.cm_name, "DeviceCMYK")) ||
+ ((pdev->params.ColorConversionStrategy == ccs_RGB ||
+ pdev->params.ColorConversionStrategy == ccs_sRGB) &&
+ strcmp(pdev->color_info.cm_name, "DeviceRGB")) ||
+ (pdev->params.ColorConversionStrategy == ccs_Gray &&
+ strcmp(pdev->color_info.cm_name, "DeviceGray"))) {
+ emprintf(pdev->memory,
+ "ColorConversionStrategy is incompatible to ProcessColorModel.\n");
+ ecode = gs_note_error(gs_error_rangecheck);
+ pdev->params.ColorConversionStrategy = save_ccs;
+ }
+ if (pdev->params.ColorConversionStrategy == ccs_UseDeviceIndependentColor) {
+ if (!pdev->UseCIEColor) {
+ emprintf(pdev->memory,
+ "Set UseCIEColor for UseDeviceIndependentColor to work properly.\n");
+ ecode = gs_note_error(gs_error_rangecheck);
+ pdev->UseCIEColor = true;
+ }
+ }
+ if (pdev->params.ColorConversionStrategy == ccs_UseDeviceIndependentColorForImages) {
+ if (!pdev->UseCIEColor) {
+ emprintf(pdev->memory,
+ "UseDeviceDependentColorForImages is not supported. Use UseDeviceIndependentColor.\n");
+ pdev->params.ColorConversionStrategy = ccs_UseDeviceIndependentColor;
+ if (!pdev->UseCIEColor) {
+ emprintf(pdev->memory,
+ "Set UseCIEColor for UseDeviceIndependentColor to work properly.\n");
+ ecode = gs_note_error(gs_error_rangecheck);
+ pdev->UseCIEColor = true;
+ }
+ }
+ }
+ if (pdev->params.ColorConversionStrategy == ccs_UseDeviceDependentColor) {
+ if (!strcmp(pdev->color_info.cm_name, "DeviceCMYK")) {
+ emprintf(pdev->memory,
+ "Replacing the deprecated device parameter value UseDeviceDependentColor with CMYK.\n");
+ pdev->params.ColorConversionStrategy = ccs_CMYK;
+ } else if (!strcmp(pdev->color_info.cm_name, "DeviceRGB")) {
+ emprintf(pdev->memory,
+ "Replacing the deprecated device parameter value UseDeviceDependentColor with sRGB.\n");
+ pdev->params.ColorConversionStrategy = ccs_sRGB;
+ } else {
+ emprintf(pdev->memory,
+ "Replacing the deprecated device parameter value UseDeviceDependentColor with Gray.\n");
+ pdev->params.ColorConversionStrategy = ccs_Gray;
+ }
+ }
+ }
+ if (cl < 1.5f && pdev->params.ColorImage.Filter != NULL &&
+ !strcmp(pdev->params.ColorImage.Filter, "JPXEncode")) {
+ emprintf(pdev->memory,
+ "JPXEncode requires CompatibilityLevel >= 1.5 .\n");
+ ecode = gs_note_error(gs_error_rangecheck);
+ }
+ if (cl < 1.5f && pdev->params.GrayImage.Filter != NULL &&
+ !strcmp(pdev->params.GrayImage.Filter, "JPXEncode")) {
+ emprintf(pdev->memory,
+ "JPXEncode requires CompatibilityLevel >= 1.5 .\n");
+ ecode = gs_note_error(gs_error_rangecheck);
+ }
+ if (cl < 1.4f && pdev->params.MonoImage.Filter != NULL &&
+ !strcmp(pdev->params.MonoImage.Filter, "JBIG2Encode")) {
+ emprintf(pdev->memory,
+ "JBIG2Encode requires CompatibilityLevel >= 1.4 .\n");
+ ecode = gs_note_error(gs_error_rangecheck);
+ }
+ if (pdev->HaveTrueTypes && pdev->version == psdf_version_level2) {
+ pdev->version = psdf_version_level2_with_TT ;
+ }
+ if (ecode < 0)
+ goto fail;
+ /*
+ * Acrobat Reader doesn't handle user-space coordinates larger than
+ * MAX_USER_COORD. To compensate for this, reduce the resolution so
+ * that the page size in device space (which we equate to user space) is
+ * significantly less than MAX_USER_COORD. Note that this still does
+ * not protect us against input files that use coordinates far outside
+ * the page boundaries.
+ */
+#define MAX_EXTENT ((int)(MAX_USER_COORD * 0.9))
+ /* Changing resolution or page size requires closing the device, */
+ if (dev->height > MAX_EXTENT || dev->width > MAX_EXTENT) {
+ double factor =
+ max(dev->height / (double)MAX_EXTENT,
+ dev->width / (double)MAX_EXTENT);
+
+ gx_device_set_resolution(dev, dev->HWResolution[0] / factor,
+ dev->HWResolution[1] / factor);
+ }
+#undef MAX_EXTENT
+ if (pdev->FirstObjectNumber != save_dev->FirstObjectNumber) {
+ if (pdev->xref.file != 0) {
+ gp_fseek_64(pdev->xref.file, 0L, SEEK_SET);
+ pdf_initialize_ids(pdev);
+ }
+ }
+ /* Handle the float/double mismatch. */
+ pdev->CompatibilityLevel = (int)(cl * 10 + 0.5) / 10.0;
+ if(pdev->OwnerPassword.size != save_dev->OwnerPassword.size ||
+ (pdev->OwnerPassword.size != 0 &&
+ memcmp(pdev->OwnerPassword.data, save_dev->OwnerPassword.data,
+ pdev->OwnerPassword.size) != 0)) {
+ if (pdev->is_open) {
+ if (pdev->PageCount == 0) {
+ gs_closedevice((gx_device *)save_dev);
+ return 0;
+ }
+ else
+ emprintf(pdev->memory, "Owner Password changed mid-job, ignoring.\n");
+ }
+ }
+
+ if (pdev->Linearise && pdev->is_ps2write) {
+ emprintf(pdev->memory, "Can't linearise PostScript output, ignoring\n");
+ pdev->Linearise = false;
+ }
+
+ if (pdev->FlattenFonts)
+ pdev->PreserveTrMode = false;
+ return 0;
+ fail:
+ /* Restore all the parameters to their original state. */
+ pdev->version = save_dev->version;
+ pdf_set_process_color_model(pdev, save_dev->pcm_color_info_index);
+ pdev->saved_fill_color = save_dev->saved_fill_color;
+ pdev->saved_stroke_color = save_dev->saved_fill_color;
+ {
+ const gs_param_item_t *ppi = pdf_param_items;
+
+ for (; ppi->key; ++ppi)
+ memcpy((char *)pdev + ppi->offset,
+ (char *)save_dev + ppi->offset,
+ gs_param_type_sizes[ppi->type]);
+ pdev->ForOPDFRead = save_dev->ForOPDFRead;
+ }
+ return ecode;
+}
+
+/* Put parameters */
+int
+gdev_pdf_put_params(gx_device * dev, gs_param_list * plist)
+{
+ int code;
+ gx_device_pdf *pdev = (gx_device_pdf *) dev;
+ gs_memory_t *mem = gs_memory_stable(pdev->memory);
+ gx_device_pdf *save_dev = gs_malloc(mem, sizeof(gx_device_pdf), 1,
+ "saved gx_device_pdf");
+
+ if (!save_dev)
+ return_error(gs_error_VMerror);
+ memcpy(save_dev, pdev, sizeof(gx_device_pdf));
+ code = gdev_pdf_put_params_impl(dev, save_dev, plist);
+ gs_free(mem, save_dev, sizeof(gx_device_pdf), 1, "saved gx_device_pdf");
+ return code;
+}
+
+/* ---------------- Process DSC comments ---------------- */
+
+/* Bug #695850 DSC comments are not actually encoded, nor are they PostScript strings
+ * they are simply a sequence of bytes. SO it would seem we should just preserve that
+ * byte sequence. Bizarrely, Distiller treats the comment as 'almost' a PostScript
+ * string. In particular it converts octal codes into an equivalent binary byte. It
+ * also converts (eg) '\n' and '\r' into 'n' and 'r' and invalid octal escapes
+ * (eg \11) simply have the '\' turned into a '?'.
+ * We think this is nuts and have no intention of trying to mimic it precisely. This
+ * routine will find octal escapes and convert them into binary. The way this works is
+ * a little obscure. The DSC parser does convert the comment into a PostScript string
+ * and so has to escape any unusual characters. This means our octal escaped values in
+ * the original DSC comment have had the escape character ('\') escaped to become '\\'.
+ * All we need to do is remove the escape of the escape and we will end up with a
+ * properly escaped PostScript string.
+ */
+static int unescape_octals(gx_device_pdf * pdev, char *src, int size)
+{
+ char *start, *dest;
+
+ start = src;
+ dest = src;
+
+ while(size) {
+ if (size > 4 && src[0] == '\\' && src[1] == '\\' &&
+ src[2] > 0x29 && src[2] < 0x35 &&
+ src[3] > 0x29 &&src[3] < 0x38 &&
+ src[4] > 0x29 && src[4] < 0x38) {
+ src++;
+ size--;
+ } else {
+ *dest++ = *src++;
+ size--;
+ }
+ }
+ return (dest - start);
+}
+
+static int
+pdf_dsc_process(gx_device_pdf * pdev, const gs_param_string_array * pma)
+{
+ /*
+ * The Adobe "Distiller Parameters" documentation says that Distiller
+ * looks at DSC comments, but it doesn't say which ones. We look at
+ * the ones that we see how to map directly to obvious PDF constructs.
+ */
+ int code = 0;
+ uint i;
+
+ /*
+ * If ParseDSCComments is false, all DSC comments are ignored, even if
+ * ParseDSCComentsForDocInfo or PreserveEPSInfo is true.
+ */
+ if (!pdev->ParseDSCComments)
+ return 0;
+
+ for (i = 0; i + 1 < pma->size && code >= 0; i += 2) {
+ const gs_param_string *pkey = &pma->data[i];
+ gs_param_string *pvalue = (gs_param_string *)&pma->data[i + 1];
+ const char *key;
+ int newsize;
+
+ /*
+ * %%For, %%Creator, and %%Title are recognized only if either
+ * ParseDSCCommentsForDocInfo or PreserveEPSInfo is true.
+ * The other DSC comments are always recognized.
+ *
+ * Acrobat Distiller sets CreationDate and ModDate to the current
+ * time, not the value of %%CreationDate. We think this is wrong,
+ * but we do the same -- we ignore %%CreationDate here.
+ */
+
+ if (pdf_key_eq(pkey, "Creator")) {
+ key = "/Creator";
+ newsize = unescape_octals(pdev, (char *)pvalue->data, pvalue->size);
+ code = cos_dict_put_c_key_string(pdev->Info, key,
+ pvalue->data, newsize);
+ continue;
+ } else if (pdf_key_eq(pkey, "Title")) {
+ key = "/Title";
+ newsize = unescape_octals(pdev, (char *)pvalue->data, pvalue->size);
+ code = cos_dict_put_c_key_string(pdev->Info, key,
+ pvalue->data, newsize);
+ continue;
+ } else if (pdf_key_eq(pkey, "For")) {
+ key = "/Author";
+ newsize = unescape_octals(pdev, (char *)pvalue->data, pvalue->size);
+ code = cos_dict_put_c_key_string(pdev->Info, key,
+ pvalue->data, newsize);
+ continue;
+ } else {
+ pdf_page_dsc_info_t *ppdi;
+ char scan_buf[200]; /* arbitrary */
+
+ if ((ppdi = &pdev->doc_dsc_info,
+ pdf_key_eq(pkey, "Orientation")) ||
+ (ppdi = &pdev->page_dsc_info,
+ pdf_key_eq(pkey, "PageOrientation"))
+ ) {
+ if (pvalue->size == 1 && pvalue->data[0] >= '0' &&
+ pvalue->data[0] <= '3'
+ )
+ ppdi->orientation = pvalue->data[0] - '0';
+ else
+ ppdi->orientation = -1;
+ } else if ((ppdi = &pdev->doc_dsc_info,
+ pdf_key_eq(pkey, "ViewingOrientation")) ||
+ (ppdi = &pdev->page_dsc_info,
+ pdf_key_eq(pkey, "PageViewingOrientation"))
+ ) {
+ gs_matrix mat;
+ int orient;
+
+ if(pvalue->size >= sizeof(scan_buf) - 1)
+ continue; /* error */
+ memcpy(scan_buf, pvalue->data, pvalue->size);
+ scan_buf[pvalue->size] = 0;
+ if (sscanf(scan_buf, "[%g %g %g %g]",
+ &mat.xx, &mat.xy, &mat.yx, &mat.yy) != 4
+ )
+ continue; /* error */
+ for (orient = 0; orient < 4; ++orient) {
+ if (mat.xx == 1 && mat.xy == 0 && mat.yx == 0 && mat.yy == 1)
+ break;
+ gs_matrix_rotate(&mat, -90.0, &mat);
+ }
+ if (orient == 4) /* error */
+ orient = -1;
+ ppdi->viewing_orientation = orient;
+ } else {
+ gs_rect box;
+
+ if (pdf_key_eq(pkey, "EPSF")) {
+ pdev->is_EPS = (pvalue->size >= 1 && pvalue->data[0] != '0');
+ continue;
+ }
+ /*
+ * We only parse the BoundingBox for the sake of
+ * AutoPositionEPSFiles.
+ */
+ if (pdf_key_eq(pkey, "BoundingBox"))
+ ppdi = &pdev->doc_dsc_info;
+ else if (pdf_key_eq(pkey, "PageBoundingBox"))
+ ppdi = &pdev->page_dsc_info;
+ else
+ continue;
+ if(pvalue->size >= sizeof(scan_buf) - 1)
+ continue; /* error */
+ memcpy(scan_buf, pvalue->data, pvalue->size);
+ scan_buf[pvalue->size] = 0;
+ if (sscanf(scan_buf, "[%lg %lg %lg %lg]",
+ &box.p.x, &box.p.y, &box.q.x, &box.q.y) != 4
+ )
+ continue; /* error */
+ ppdi->bounding_box = box;
+ }
+ continue;
+ }
+
+ if (pdev->ParseDSCCommentsForDocInfo || pdev->PreserveEPSInfo)
+ code = cos_dict_put_c_key_string(pdev->Info, key,
+ pvalue->data, pvalue->size);
+ }
+ return code;
+}
diff --git a/devices/vector/gdevpdfr.c b/devices/vector/gdevpdfr.c
new file mode 100644
index 000000000..a5b9bfe06
--- /dev/null
+++ b/devices/vector/gdevpdfr.c
@@ -0,0 +1,501 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Named object pdfmark processing */
+#include "memory_.h"
+#include "gx.h"
+#include "gserrors.h"
+#include "gsutil.h" /* for bytes_compare */
+#include "gdevpdfx.h"
+#include "gdevpdfo.h"
+#include "scanchar.h"
+#include "strimpl.h"
+#include "sstring.h"
+
+#ifndef gs_error_syntaxerror
+# define gs_error_syntaxerror gs_error_rangecheck
+#endif
+
+/* Test whether an object name has valid syntax, {name}. */
+bool
+pdf_objname_is_valid(const byte *data, uint size)
+{
+ return (size >= 2 && data[0] == '{' &&
+ (const byte *)memchr(data, '}', size) == data + size - 1);
+}
+
+/*
+ * Look up a named object. Return e_rangecheck if the syntax is invalid.
+ * If the object is missing, return e_undefined.
+ */
+int
+pdf_find_named(gx_device_pdf * pdev, const gs_param_string * pname,
+ cos_object_t **ppco)
+{
+ const cos_value_t *pvalue;
+
+ if (!pdf_objname_is_valid(pname->data, pname->size))
+ return_error(gs_error_rangecheck);
+ if ((pvalue = cos_dict_find(pdev->local_named_objects, pname->data,
+ pname->size)) != 0 ||
+ (pvalue = cos_dict_find(pdev->global_named_objects, pname->data,
+ pname->size)) != 0
+ ) {
+ *ppco = pvalue->contents.object;
+ return 0;
+ }
+ return_error(gs_error_undefined);
+}
+
+/*
+ * Create a (local) named object. id = -1L means do not assign an id.
+ * pname = 0 means just create the object, do not name it. Note that
+ * during initialization, local_named_objects == global_named_objects.
+ */
+int
+pdf_create_named(gx_device_pdf *pdev, const gs_param_string *pname,
+ cos_type_t cotype, cos_object_t **ppco, long id)
+{
+ cos_object_t *pco;
+ cos_value_t value;
+
+ *ppco = pco = cos_object_alloc(pdev, "pdf_create_named");
+ if (pco == 0)
+ return_error(gs_error_VMerror);
+ pco->id =
+ (id == -1 ? 0L : id == 0 ? pdf_obj_ref(pdev) : id);
+ if (pname) {
+ int code = cos_dict_put(pdev->local_named_objects, pname->data,
+ pname->size, cos_object_value(&value, pco));
+
+ if (code < 0)
+ return code;
+ }
+ if (cotype != cos_type_generic)
+ cos_become(pco, cotype);
+ *ppco = pco;
+ return 0;
+}
+int
+pdf_create_named_dict(gx_device_pdf *pdev, const gs_param_string *pname,
+ cos_dict_t **ppcd, long id)
+{
+ cos_object_t *pco;
+ int code = pdf_create_named(pdev, pname, cos_type_dict, &pco, id);
+
+ *ppcd = (cos_dict_t *)pco;
+ return code;
+}
+
+/*
+ * Look up a named object as for pdf_find_named. If the object does not
+ * exist, create it (as a dictionary if it is one of the predefined names
+ * {ThisPage}, {NextPage}, {PrevPage}, or {Page<#>}, otherwise as a
+ * generic object) and return 1.
+ */
+int
+pdf_refer_named(gx_device_pdf * pdev, const gs_param_string * pname_orig,
+ cos_object_t **ppco)
+{
+ const gs_param_string *pname = pname_orig;
+ int code = pdf_find_named(pdev, pname, ppco);
+ char page_name_chars[6 + 10 + 2]; /* {Page<n>}, enough for an int */
+ gs_param_string pnstr;
+ int page_number;
+
+ if (code != gs_error_undefined)
+ return code;
+ /*
+ * Check for a predefined name. Map ThisPage, PrevPage, and NextPage
+ * to the appropriate Page<#> name.
+ */
+ if (pname->size >= 7 && pname->size < sizeof(page_name_chars)) {
+ memcpy(page_name_chars, pname->data, pname->size);
+ page_name_chars[pname->size] = 0;
+ if (sscanf(page_name_chars, "{Page%d}", &page_number) == 1)
+ goto cpage;
+ }
+ if (pdf_key_eq(pname, "{ThisPage}"))
+ page_number = pdev->next_page + 1;
+ else if (pdf_key_eq(pname, "{NextPage}"))
+ page_number = pdev->next_page + 2;
+ else if (pdf_key_eq(pname, "{PrevPage}"))
+ page_number = pdev->next_page;
+ else {
+ code = pdf_create_named(pdev, pname, cos_type_generic, ppco, 0L);
+ return (code < 0 ? code : 1);
+ }
+ if (page_number <= 0)
+ return code;
+ gs_sprintf(page_name_chars, "{Page%d}", page_number);
+ param_string_from_string(pnstr, page_name_chars);
+ pname = &pnstr;
+ code = pdf_find_named(pdev, pname, ppco);
+ if (code != gs_error_undefined)
+ return code;
+ cpage:
+ if (pdf_page_id(pdev, page_number) <= 0)
+ return_error(gs_error_rangecheck);
+ *ppco = COS_OBJECT(pdev->pages[page_number - 1].Page);
+ return 0;
+}
+
+/*
+ * Look up a named object as for pdf_refer_named. If the object already
+ * exists and is not simply a forward reference, return e_rangecheck;
+ * if it exists as a forward reference, set its type and return 0;
+ * otherwise, create the object with the given type and return 1.
+ */
+int
+pdf_make_named(gx_device_pdf * pdev, const gs_param_string * pname,
+ cos_type_t cotype, cos_object_t **ppco, bool assign_id)
+{
+ if (pname) {
+ int code = pdf_refer_named(pdev, pname, ppco);
+ cos_object_t *pco = *ppco;
+
+ if (code < 0)
+ return code;
+ if (cos_type(pco) != cos_type_generic)
+ return_error(gs_error_rangecheck);
+ if (assign_id && pco->id == 0)
+ pco->id = pdf_obj_ref(pdev);
+ cos_become(pco, cotype);
+ return code;
+ } else {
+ int code = pdf_create_named(pdev, pname, cotype, ppco,
+ (assign_id ? 0L : -1L));
+
+ return (code < 0 ? code : 1);
+ }
+}
+int
+pdf_make_named_dict(gx_device_pdf * pdev, const gs_param_string * pname,
+ cos_dict_t **ppcd, bool assign_id)
+{
+ cos_object_t *pco;
+ int code = pdf_make_named(pdev, pname, cos_type_dict, &pco, assign_id);
+
+ *ppcd = (cos_dict_t *)pco;
+ return code;
+}
+
+/*
+ * Look up a named object as for pdf_refer_named. If the object does not
+ * exist, return e_undefined; if the object exists but has the wrong type,
+ * return e_typecheck.
+ */
+int
+pdf_get_named(gx_device_pdf * pdev, const gs_param_string * pname,
+ cos_type_t cotype, cos_object_t **ppco)
+{
+ int code = pdf_refer_named(pdev, pname, ppco);
+
+ if (code < 0)
+ return code;
+ if (cos_type(*ppco) != cotype)
+ return_error(gs_error_typecheck);
+ return code;
+}
+
+/*
+ * Push the current local namespace onto the namespace stack, and reset it
+ * to an empty namespace.
+ */
+int
+pdf_push_namespace(gx_device_pdf *pdev)
+{
+ int code = cos_array_add_object(pdev->Namespace_stack,
+ COS_OBJECT(pdev->local_named_objects));
+ cos_dict_t *pcd =
+ cos_dict_alloc(pdev, "pdf_push_namespace(local_named_objects)");
+ cos_array_t *pca =
+ cos_array_alloc(pdev, "pdf_push_namespace(NI_stack)");
+
+ if (code < 0 ||
+ (code = cos_array_add_object(pdev->Namespace_stack,
+ COS_OBJECT(pdev->NI_stack))) < 0
+ )
+ return code;
+ if (pcd == 0 || pca == 0)
+ return_error(gs_error_VMerror);
+ pdev->local_named_objects = pcd;
+ pdev->NI_stack = pca;
+ return 0;
+}
+
+/*
+ * Pop the top local namespace from the namespace stack. Return an error if
+ * the stack is empty.
+ */
+int
+pdf_pop_namespace(gx_device_pdf *pdev)
+{
+ cos_value_t nis_value, lno_value;
+ int code = cos_array_unadd(pdev->Namespace_stack, &nis_value);
+
+ if (code < 0 ||
+ (code = cos_array_unadd(pdev->Namespace_stack, &lno_value)) < 0
+ )
+ return code;
+ COS_FREE(pdev->local_named_objects,
+ "pdf_pop_namespace(local_named_objects)");
+ pdev->local_named_objects = (cos_dict_t *)lno_value.contents.object;
+ COS_FREE(pdev->NI_stack, "pdf_pop_namespace(NI_stack)");
+ pdev->NI_stack = (cos_array_t *)nis_value.contents.object;
+ return 0;
+}
+
+/*
+ * Scan a token from a string. <<, >>, [, and ] are treated as tokens.
+ * Return 1 if a token was scanned, 0 if we reached the end of the string,
+ * or an error. On a successful return, the token extends from *ptoken up
+ * to but not including *pscan.
+ *
+ * Note that this scanner expects a subset of PostScript syntax, not PDF
+ * syntax. In particular, it doesn't understand ASCII85 strings,
+ * doesn't process the PDF #-escape syntax within names, and does only
+ * minimal syntax checking. It also recognizes one extension to PostScript
+ * syntax, to allow gs_pdfwr.ps to pass names that include non-regular
+ * characters: If a name is immediately preceded by two null characters,
+ * the name includes everything up to a following null character. The only
+ * place that currently generates this convention is the PostScript code
+ * that pre-processes the arguments for pdfmarks, in lib/gs_pdfwr.ps.
+ */
+int
+pdf_scan_token(const byte **pscan, const byte * end, const byte **ptoken)
+{
+ const byte *p = *pscan;
+
+ while (p < end && scan_char_decoder[*p] == ctype_space) {
+ ++p;
+ if (p[-1] == 0 && p + 1 < end && p + 2 < end && *p == 0 && p[1] == 0 && p[2] == '/') {
+ /* Special handling for names delimited by a triple start and double end null character. */
+ *ptoken = p + 2;
+ while (*p != 0 || p[1] != 0)
+ if (++p >= end || p + 1 >= end)
+ return_error(gs_error_syntaxerror); /* no terminator */
+ *pscan = p + 1;
+ return 1;
+ } else {
+ if (p[-1] == 0 && p + 1 < end && *p == 0 && p[2] == '/') {
+ /* Special handling for names delimited by a double start and single end null character. */
+ *ptoken = ++p;
+ while (*p != 0)
+ if (++p >= end)
+ return_error(gs_error_syntaxerror); /* no terminator */
+ *pscan = p;
+ return 1;
+ }
+ }
+ }
+ *ptoken = p;
+ if (p >= end) {
+ *pscan = p;
+ return 0;
+ }
+ switch (*p) {
+ case '%':
+ case ')':
+ return_error(gs_error_syntaxerror);
+ case '(': {
+ /* Skip over the string. */
+ byte buf[50]; /* size is arbitrary */
+ stream_cursor_read r;
+ stream_cursor_write w;
+ stream_PSSD_state ss;
+ int status;
+
+ s_PSSD_init((stream_state *)&ss);
+ r.ptr = p; /* skip the '(' */
+ r.limit = end - 1;
+ w.limit = buf + sizeof(buf) - 1;
+ do {
+ /* One picky compiler complains if we initialize to buf - 1. */
+ w.ptr = buf; w.ptr--;
+ status = (*s_PSSD_template.process)
+ ((stream_state *) & ss, &r, &w, true);
+ }
+ while (status == 1);
+ *pscan = r.ptr + 1;
+ return 1;
+ }
+ case '<':
+ if (end - p < 2)
+ return_error(gs_error_syntaxerror);
+ if (p[1] != '<') {
+ /*
+ * We need the cast because some compilers declare memchar as
+ * returning a char * rather than a void *.
+ */
+ p = (const byte *)memchr(p + 1, '>', end - p - 1);
+ if (p == 0)
+ return_error(gs_error_syntaxerror);
+ }
+ goto m2;
+ case '>':
+ if (end - p < 2 || p[1] != '>')
+ return_error(gs_error_syntaxerror);
+m2: *pscan = p + 2;
+ return 1;
+ case '[': case ']': case '{': case '}':
+ *pscan = p + 1;
+ return 1;
+ case '/':
+ ++p;
+ default:
+ break;
+ }
+ while (p < end && scan_char_decoder[*p] <= ctype_name)
+ ++p;
+ *pscan = p;
+ if (p == *ptoken) /* no chars scanned, i.e., not ctype_name */
+ return_error(gs_error_syntaxerror);
+ return 1;
+}
+/*
+ * Scan a possibly composite token: arrays and dictionaries are treated as
+ * single tokens.
+ */
+int
+pdf_scan_token_composite(const byte **pscan, const byte * end,
+ const byte **ptoken_orig)
+{
+ int level = 0;
+ const byte *ignore_token;
+ const byte **ptoken = ptoken_orig;
+ int code;
+
+ do {
+ code = pdf_scan_token(pscan, end, ptoken);
+ if (code <= 0)
+ return (code < 0 || level == 0 ? code :
+ gs_note_error(gs_error_syntaxerror));
+ switch (**ptoken) {
+ case '<': case '[': case '{':
+ ++level; break;
+ case '>': case ']': case '}':
+ if (level == 0)
+ return_error(gs_error_syntaxerror);
+ --level; break;
+ }
+ ptoken = &ignore_token;
+ } while (level);
+ return code;
+}
+
+/* Replace object names with object references in a (parameter) string. */
+static const byte *
+pdfmark_next_object(const byte * scan, const byte * end, const byte **pname,
+ cos_object_t **ppco, gx_device_pdf * pdev)
+{
+ /*
+ * Starting at scan, find the next object reference, set *pname
+ * to point to it in the string, store the object at *ppco,
+ * and return a pointer to the first character beyond the
+ * reference. If there are no more object references, set
+ * *pname = end, *ppco = 0, and return end.
+ */
+ int code;
+
+ while ((code = pdf_scan_token(&scan, end, pname)) != 0) {
+ gs_param_string sname;
+
+ if (code < 0) {
+ ++scan;
+ continue;
+ }
+ if (**pname != '{')
+ continue;
+ /* Back up over the { and rescan as a single token. */
+ scan = *pname;
+ code = pdf_scan_token_composite(&scan, end, pname);
+ if (code < 0) {
+ ++scan;
+ continue;
+ }
+ sname.data = *pname;
+ sname.size = scan - sname.data;
+ /*
+ * Forward references are allowed. If there is an error,
+ * simply retain the name as a literal string.
+ */
+ code = pdf_refer_named(pdev, &sname, ppco);
+ if (code < 0)
+ continue;
+ return scan;
+ }
+ *ppco = 0;
+ return end;
+}
+int
+pdf_replace_names(gx_device_pdf * pdev, const gs_param_string * from,
+ gs_param_string * to)
+{
+ const byte *start = from->data;
+ const byte *end = start + from->size;
+ const byte *scan;
+ uint size = 0;
+ cos_object_t *pco;
+ bool any = false;
+ byte *sto;
+ char ref[1 + 10 + 5 + 1]; /* max obj number is 10 digits */
+
+ /* Do a first pass to compute the length of the result. */
+ for (scan = start; scan < end;) {
+ const byte *sname;
+ const byte *next =
+ pdfmark_next_object(scan, end, &sname, &pco, pdev);
+
+ size += sname - scan;
+ if (pco) {
+ gs_sprintf(ref, " %ld 0 R ", pco->id);
+ size += strlen(ref);
+ }
+ scan = next;
+ any |= next != sname;
+ }
+ to->persistent = true; /* ??? */
+ if (!any) {
+ to->data = start;
+ to->size = size;
+ return 0;
+ }
+ sto = gs_alloc_bytes(pdev->pdf_memory, size, "pdf_replace_names");
+ if (sto == 0)
+ return_error(gs_error_VMerror);
+ to->data = sto;
+ to->size = size;
+ /* Do a second pass to do the actual substitutions. */
+ for (scan = start; scan < end;) {
+ const byte *sname;
+ const byte *next =
+ pdfmark_next_object(scan, end, &sname, &pco, pdev);
+ uint copy = sname - scan;
+ int rlen;
+
+ memcpy(sto, scan, copy);
+ sto += copy;
+ if (pco) {
+ gs_sprintf(ref, " %ld 0 R ", pco->id);
+ rlen = strlen(ref);
+ memcpy(sto, ref, rlen);
+ sto += rlen;
+ }
+ scan = next;
+ }
+ return 0;
+}
diff --git a/devices/vector/gdevpdft.c b/devices/vector/gdevpdft.c
new file mode 100644
index 000000000..fddd53cb9
--- /dev/null
+++ b/devices/vector/gdevpdft.c
@@ -0,0 +1,470 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* transparency processing for PDF-writing driver */
+#include "gx.h"
+#include "string_.h"
+#include "gserrors.h"
+#include "gstrans.h"
+#include "gscolor2.h"
+#include "gzstate.h"
+#include "gdevpdfx.h"
+#include "gdevpdfg.h"
+#include "gdevpdfo.h"
+#include "gsccolor.h"
+
+static int
+pdf_make_soft_mask_dict(gx_device_pdf * pdev, const gs_pdf14trans_params_t * pparams)
+{
+ pdf_resource_t *pres_soft_mask_dict = 0;
+ cos_dict_t *soft_mask_dict;
+ int code;
+
+ /* Fixme : merge redundant objects. */
+ code = pdf_alloc_resource(pdev, resourceSoftMaskDict, gs_no_id, &pres_soft_mask_dict, -1);
+ if (code < 0)
+ return code;
+ cos_become(pres_soft_mask_dict->object, cos_type_dict);
+ pdev->pres_soft_mask_dict = pres_soft_mask_dict;
+ soft_mask_dict = (cos_dict_t *)pres_soft_mask_dict->object;
+ code = cos_dict_put_c_key_string(soft_mask_dict, "/S",
+ pparams->subtype == TRANSPARENCY_MASK_Alpha ? (byte *)"/Alpha" : (byte *)"/Luminosity",
+ pparams->subtype == TRANSPARENCY_MASK_Alpha ? 6 : 11);
+ if (code < 0)
+ return code;
+ if (pparams->Background_components) {
+ cos_array_t *Background;
+
+ Background = cos_array_from_floats(pdev, pparams->Background,
+ pparams->Background_components, "pdf_write_soft_mask_dict");
+ if (Background == NULL)
+ return_error(gs_error_VMerror);
+ code = cos_dict_put_c_key_object(soft_mask_dict, "/BC", (cos_object_t *)Background);
+ if (code < 0)
+ return code;
+ }
+ if (pparams->transfer_function != NULL) {
+ long id;
+ char buf[20];
+
+ code = pdf_write_function(pdev, pparams->transfer_function, &id);
+ if (code < 0)
+ return code;
+ gs_sprintf(buf, " %ld 0 R", id);
+ code = cos_dict_put_c_key_string(soft_mask_dict, "/TR", (const byte *)buf, strlen(buf));
+ if (code < 0)
+ return code;
+ }
+ return 0;
+
+}
+
+static int
+pdf_make_group_dict(gx_device_pdf * pdev, const gs_pdf14trans_params_t * pparams,
+ const gs_imager_state * pis, cos_dict_t **pdict)
+{
+ pdf_resource_t *pres_group;
+ cos_dict_t *group_dict;
+ int code;
+ const gs_state *gstate = gx_hld_get_gstate_ptr(pis);
+ cos_value_t cs_value;
+
+ code = pdf_alloc_resource(pdev, resourceGroup, gs_no_id, &pres_group, -1);
+ if (code < 0)
+ return code;
+ cos_become(pres_group->object, cos_type_dict);
+ group_dict = (cos_dict_t *)pres_group->object;
+ code = cos_dict_put_c_key_string(group_dict, "/Type", (const byte *)"/Group", 6);
+ if (code < 0)
+ return code;
+ code = cos_dict_put_c_key_string(group_dict, "/S", (const byte *)"/Transparency", 13);
+ if (code < 0)
+ return code;
+ if (pparams->Isolated) {
+ code = cos_dict_put_c_key_bool(group_dict, "/I", true);
+ if (code < 0)
+ return code;
+ }
+ if (pparams->Knockout) {
+ code = cos_dict_put_c_key_bool(group_dict, "/K", true);
+ if (code < 0)
+ return code;
+ }
+ /* Note that we should not add in the graphic state
+ color space for the group color if there was not
+ a group color specified.
+ In this case, the parent group is inherited from
+ the previous group or the device color space */
+ if (gstate != NULL && pparams->group_color != UNKNOWN) {
+ const gs_color_space *cs = gs_currentcolorspace_inline(gstate);
+
+ code = pdf_color_space_named(pdev, pis, &cs_value, NULL, cs,
+ &pdf_color_space_names, false, NULL, 0, false);
+ if (code < 0)
+ return code;
+ code = cos_dict_put_c_key(group_dict, "/CS", &cs_value);
+ if (code < 0)
+ return code;
+ }
+ group_dict = NULL; /* The next line invalidates it. */
+ code = pdf_substitute_resource(pdev, &pres_group, resourceGroup, NULL, false);
+ if (code < 0)
+ return code;
+ pres_group->where_used |= pdev->used_mask;
+ *pdict = (cos_dict_t *)pres_group->object;
+ return 0;
+}
+
+static int
+pdf_make_form_dict(gx_device_pdf * pdev, const gs_pdf14trans_params_t * pparams,
+ const gs_imager_state * pis,
+ const cos_dict_t *group_dict, cos_dict_t *form_dict)
+{
+ cos_array_t *bbox_array;
+ float bbox[4];
+ gs_rect bbox_rect;
+ int code;
+
+ code = gs_bbox_transform(&pparams->bbox, &ctm_only(pis), &bbox_rect);
+ if (code < 0)
+ return code;
+ bbox[0] = bbox_rect.p.x;
+ bbox[1] = bbox_rect.p.y;
+ bbox[2] = bbox_rect.q.x;
+ bbox[3] = bbox_rect.q.y;
+ code = cos_dict_put_c_key_string(form_dict, "/Type", (const byte *)"/XObject", 8);
+ if (code < 0)
+ return code;
+ code = cos_dict_put_c_key_string(form_dict, "/Subtype", (const byte *)"/Form", 5);
+ if (code < 0)
+ return code;
+ code = cos_dict_put_c_key_int(form_dict, "/FormType", 1);
+ if (code < 0)
+ return code;
+ code = cos_dict_put_c_key_string(form_dict, "/Matrix", (const byte *)"[1 0 0 1 0 0]", 13);
+ if (code < 0)
+ return code;
+ bbox_array = cos_array_from_floats(pdev, bbox, 4, "pdf_begin_transparency_group");
+ if (bbox_array == NULL)
+ return_error(gs_error_VMerror);
+ code = cos_dict_put_c_key_object(form_dict, "/BBox", (cos_object_t *)bbox_array);
+ if (code < 0)
+ return code;
+ return cos_dict_put_c_key_object(form_dict, "/Group", (cos_object_t *)group_dict);
+}
+
+static int
+pdf_begin_transparency_group(gs_imager_state * pis, gx_device_pdf * pdev,
+ const gs_pdf14trans_params_t * pparams)
+{
+ cos_dict_t *group_dict;
+ bool in_page = is_in_page(pdev);
+ const gs_state *gstate = gx_hld_get_gstate_ptr(pis);
+ int code;
+
+ if (gstate == NULL)
+ return_error(gs_error_unregistered); /* Must not happen. */
+ code = pdf_make_group_dict(pdev, pparams, pis, &group_dict);
+ if (code < 0)
+ return code;
+ code = pdf_open_page(pdev, PDF_IN_STREAM);
+ if (code < 0)
+ return code;
+ if (pdf_must_put_clip_path(pdev, gstate->clip_path)) {
+ code = pdf_put_clip_path(pdev, gstate->clip_path);
+ if (code < 0)
+ return code;
+ }
+ if (!in_page)
+ pdev->pages[pdev->next_page].group_id = group_dict->id;
+ else if (pparams->image_with_SMask) {
+ /* An internal group for the image implementation.
+ See doimagesmask in gs/lib/pdf_draw.ps .
+ Just set a flag for skipping pdf_end_transparency_group. */
+ pdev->image_with_SMask |= 1 << ++pdev->FormDepth;
+ } else {
+ pdf_resource_t *pres, *pres_gstate = NULL;
+ cos_dict_t *pcd = NULL, *pcd_Resources = NULL;
+
+ code = pdf_prepare_drawing(pdev, pis, &pres_gstate);
+ if (code < 0)
+ return code;
+ code = pdf_end_gstate(pdev, pres_gstate);
+ if (code < 0)
+ return code;
+ code = pdf_enter_substream(pdev, resourceXObject,
+ gs_no_id, &pres, false, pdev->params.CompressPages);
+ if (code < 0)
+ return code;
+ pdev->FormDepth++;
+ code = pdf_make_form_dict(pdev, pparams, pis, group_dict, (cos_dict_t *)pres->object);
+ if (code < 0)
+ return code;
+
+ /* Create a Resources dictionary and add it to the form dictionary */
+ pcd = cos_stream_dict((cos_stream_t *)pres->object);
+ pcd_Resources = cos_dict_alloc(pdev, "pdf_group(Resources)");
+ if (pcd == NULL || pcd_Resources == NULL)
+ return_error(gs_error_VMerror);
+ code = cos_dict_put_c_key_object(pcd, "/Resources", COS_OBJECT(pcd_Resources));
+ pdev->substream_Resources = pcd_Resources;
+ return code;
+ }
+ return 0;
+}
+
+static int
+pdf_end_transparency_group(gs_imager_state * pis, gx_device_pdf * pdev)
+{
+ int bottom = (pdev->ResourcesBeforeUsage ? 1 : 0);
+
+ if (!is_in_page(pdev))
+ return 0; /* corresponds to check in pdf_begin_transparency_group */
+ if (pdev->image_with_SMask & (1 << pdev->FormDepth)) {
+ /* An internal group for the image implementation.
+ See pdf_begin_transparency_group. */
+ pdev->image_with_SMask &= ~(1 << pdev->FormDepth--);
+ return 0;
+ } else if (pdev->sbstack_depth == bottom) {
+ /* We're closing the page group. */
+ if (pdev->pages[pdev->next_page].group_id == 0)
+ return_error(gs_error_unregistered); /* Must not happen. */
+ return 0;
+ } else {
+ pdf_resource_t *pres = pdev->accumulating_substream_resource;
+ int code;
+ uint ignore;
+
+ pdev->FormDepth--;
+ code = pdf_exit_substream(pdev);
+ if (code < 0)
+ return code;
+ code = pdf_substitute_resource(pdev, &pres, resourceXObject, NULL, false);
+ if (code < 0)
+ return code;
+ /* We need to update the 'where_used' field, in case we substituted a resource */
+ pres->where_used |= pdev->used_mask;
+ sputc(pdev->strm,'/');
+ sputs(pdev->strm, (const byte *)pres->rname, strlen(pres->rname), &ignore);
+ sputs(pdev->strm, (const byte *)" Do\n", 4, &ignore);
+ code = pdf_add_resource(pdev, pdev->substream_Resources, "/XObject", pres);
+ return code;
+ }
+}
+
+static int
+pdf_begin_transparency_mask(gs_imager_state * pis, gx_device_pdf * pdev,
+ const gs_pdf14trans_params_t * pparams)
+{
+ if (pparams->subtype == TRANSPARENCY_MASK_None) {
+ int code, id = pis->soft_mask_id;
+ pdf_resource_t *pres = 0L;
+
+ /* reset the soft mask ID. Apparently this is only used by pdfwrite, if we don't
+ * reset it, then the pdf_prepare_drawing code doesn't know that the SMask has
+ * changed, and so doesn't write out the GState
+ */
+ pis->soft_mask_id = 0;
+ code = pdf_prepare_drawing(pdev, pis, &pres);
+ if (code == gs_error_interrupt) {
+ /* Not in an appropriate context, ignore it but restore
+ * the old soft_mask_id. Not sure this is correct, but it works for now.
+ */
+ pis->soft_mask_id = id;
+ /* ignore return code, we don't care about this graphics state as we aren't
+ * emitting it anyway
+ */
+ pdf_end_gstate(pdev, pres);
+ return 0;
+ }
+ if (code < 0)
+ return code;
+ code = pdf_end_gstate(pdev, pres);
+ if (code < 0)
+ return code;
+ return 0;
+ }
+ if (pparams->mask_is_image) {
+ /* HACK :
+ The control comes here when
+ the PDF interpreter will make the PS interpreter
+ to interprete the mask for filling the transparency buffer
+ with an SMask image.
+ Since we handle Type 3 images as a high level objects,
+ we don't install the transparency buffer here
+ and need to skip the image enumeration for the SMask.
+ However we have no right method for skipping
+ an image enumeration due to possible side effect
+ of the image data proc in Postscript language.
+ Therefore we do enumerate the image mask and accumulate
+ it as a PDF stream, but don't create a reference to it.
+ Later it will be enumerated once again as a part of SMask-ed image,
+ and the pdfwrite image handler will recognize duplicated images
+ and won't create the second stream for same image.
+
+ We could make a special workaround for
+ skipping mask images either in the graphics library or
+ in the PS code of the PDF interpreter,
+ but we don't want to complicate things now.
+ The performance leak for the second enumeration
+ shouldn't be harmful.
+
+ So now just set a flag for pdf_end_and_do_image.
+ */
+ pdev->image_mask_skip = true;
+ return 0;
+ } else {
+ int code;
+
+ code = pdf_make_soft_mask_dict(pdev, pparams);
+ if (code < 0)
+ return code;
+ code = pdf_open_page(pdev, PDF_IN_STREAM);
+ if (code < 0)
+ return code;
+ return pdf_begin_transparency_group(pis, pdev, pparams);
+ }
+}
+
+static int
+pdf_end_transparency_mask(gs_imager_state * pis, gx_device_pdf * pdev,
+ const gs_pdf14trans_params_t * pparams)
+{
+ if (pdev->image_mask_skip)
+ pdev->image_mask_skip = false;
+ else {
+ pdf_resource_t *pres = pdev->accumulating_substream_resource;
+ int code;
+ char buf[20];
+
+ code = pdf_exit_substream(pdev);
+ if (code < 0)
+ return code;
+ code = pdf_substitute_resource(pdev, &pres, resourceXObject, NULL, false);
+ if (code < 0)
+ return 0;
+ /* We need to update the 'where_used' field, in case we substituted a resource */
+ pres->where_used |= pdev->used_mask;
+ gs_sprintf(buf, "%ld 0 R", pdf_resource_id(pres));
+ code = cos_dict_put_c_key_string((cos_dict_t *)pdev->pres_soft_mask_dict->object,
+ "/G", (const byte *)buf, strlen(buf));
+ if (code < 0)
+ return code;
+ code = pdf_substitute_resource(pdev, &pdev->pres_soft_mask_dict,
+ resourceSoftMaskDict, NULL, false);
+ if (code < 0)
+ return code;
+ pdev->pres_soft_mask_dict->where_used |= pdev->used_mask;
+ pis->soft_mask_id = pdev->pres_soft_mask_dict->object->id;
+ pdev->pres_soft_mask_dict = NULL;
+ /* We called pdf_start_trnasparency_group (see pdf_begin_transparency_mask
+ * above) but we don't call pdf_end_transparency_group, so we must reduce
+ * the FormDepth ourselves.
+ */
+ pdev->FormDepth--;
+ }
+ return 0;
+}
+
+static int
+pdf_set_blend_params(gs_imager_state * pis, gx_device_pdf * dev,
+ const gs_pdf14trans_params_t * pparams)
+{
+ return 0;
+}
+
+int
+gdev_pdf_create_compositor(gx_device *dev,
+ gx_device **pcdev, const gs_composite_t *pct,
+ gs_imager_state *pis, gs_memory_t *memory, gx_device *cdev)
+{
+ gx_device_pdf *pdev = (gx_device_pdf *)dev;
+
+ if (pdev->HaveTransparency && pdev->CompatibilityLevel >= 1.4 &&
+ pct->type->comp_id == GX_COMPOSITOR_PDF14_TRANS &&
+ pdev->PDFA != 1) {
+ gs_pdf14trans_t *pcte = (gs_pdf14trans_t *)pct;
+ gs_pdf14trans_params_t *params = &pcte->params;
+
+ *pcdev = dev;
+ switch(params->pdf14_op) {
+ case PDF14_PUSH_DEVICE:
+ return 0;
+ case PDF14_POP_DEVICE:
+ return 0;
+ case PDF14_ABORT_DEVICE:
+ return 0;
+ case PDF14_BEGIN_TRANS_GROUP:
+ return pdf_begin_transparency_group(pis, pdev, params);
+ case PDF14_END_TRANS_GROUP:
+ return pdf_end_transparency_group(pis, pdev);
+ case PDF14_BEGIN_TRANS_MASK:
+ return pdf_begin_transparency_mask(pis, pdev, params);
+ case PDF14_END_TRANS_MASK:
+ return pdf_end_transparency_mask(pis, pdev, params);
+ case PDF14_SET_BLEND_PARAMS:
+ return pdf_set_blend_params(pis, pdev, params);
+ case PDF14_PUSH_TRANS_STATE:
+ return 0;
+ case PDF14_POP_TRANS_STATE:
+ return 0;
+ case PDF14_PUSH_SMASK_COLOR:
+ return 0;
+ case PDF14_POP_SMASK_COLOR:
+ return 0;
+
+ default :
+ return_error(gs_error_unregistered); /* Must not happen. */
+ }
+ return 0;
+ }
+ return psdf_create_compositor(dev, pcdev, pct, pis, memory, cdev);
+}
+
+/* We're not sure why the folllowing device methods are never called.
+ Stub them for a while. */
+
+int
+gdev_pdf_begin_transparency_group(gx_device *dev,
+ const gs_transparency_group_params_t *ptgp,
+ const gs_rect *pbbox,
+ gs_imager_state *pis, gs_memory_t *mem)
+{
+ return 0;
+}
+
+int
+gdev_pdf_end_transparency_group(gx_device *dev,
+ gs_imager_state *pis)
+{
+ return 0;
+}
+
+int
+gdev_pdf_begin_transparency_mask(gx_device *dev,
+ const gx_transparency_mask_params_t *ptmp,
+ const gs_rect *pbbox,
+ gs_imager_state *pis, gs_memory_t *mem)
+{
+ return 0;
+}
+
+int
+gdev_pdf_end_transparency_mask(gx_device *dev,
+ gs_imager_state *pis)
+{
+ return 0;
+}
diff --git a/devices/vector/gdevpdfu.c b/devices/vector/gdevpdfu.c
new file mode 100644
index 000000000..a577d08be
--- /dev/null
+++ b/devices/vector/gdevpdfu.c
@@ -0,0 +1,2664 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Output utilities for PDF-writing driver */
+#include "memory_.h"
+#include "jpeglib_.h" /* for sdct.h */
+#include "gx.h"
+#include "gserrors.h"
+#include "gscdefs.h"
+#include "gsdsrc.h"
+#include "gsfunc.h"
+#include "gsfunc3.h"
+#include "gdevpdfx.h"
+#include "gdevpdfo.h"
+#include "gdevpdfg.h"
+#include "gdevpdtd.h"
+#include "strimpl.h"
+#include "sa85x.h"
+#include "scfx.h"
+#include "sdct.h"
+#include "slzwx.h"
+#include "spngpx.h"
+#include "srlx.h"
+#include "sarc4.h"
+#include "smd5.h"
+#include "sstring.h"
+#include "strmio.h"
+#include "szlibx.h"
+#ifdef USE_LDF_JB2
+#include "sjbig2_luratech.h"
+#endif
+#ifdef USE_LWF_JP2
+#include "sjpx_luratech.h"
+#endif
+
+#include "opdfread.h"
+#include "gdevagl.h"
+#include "gs_mgl_e.h"
+#include "gs_mro_e.h"
+
+extern single_glyph_list_t *SingleGlyphList;
+
+ /* Define the size of internal stream buffers. */
+/* (This is not a limitation, it only affects performance.) */
+#define sbuf_size 512
+
+/* Optionally substitute other filters for FlateEncode for debugging. */
+#if 1
+# define compression_filter_name "FlateDecode"
+# define compression_filter_template s_zlibE_template
+# define compression_filter_state stream_zlib_state
+#else
+# define compression_filter_name "LZWDecode"
+# define compression_filter_template s_LZWE_template
+# define compression_filter_state stream_LZW_state
+#endif
+
+/* Import procedures for writing filter parameters. */
+extern stream_state_proc_get_params(s_DCTE_get_params, stream_DCT_state);
+extern stream_state_proc_get_params(s_CF_get_params, stream_CF_state);
+
+#define CHECK(expr)\
+ BEGIN if ((code = (expr)) < 0) return code; END
+
+/* GC descriptors */
+extern_st(st_pdf_color_space);
+extern_st(st_pdf_font_resource);
+extern_st(st_pdf_char_proc);
+extern_st(st_pdf_font_descriptor);
+public_st_pdf_resource();
+private_st_pdf_x_object();
+private_st_pdf_pattern();
+
+/* ---------------- Utilities ---------------- */
+
+#ifdef PS2WRITE_USES_ROMFS
+/*
+ * Strip whitespace and comments from a line of PostScript code as possible.
+ * Return a pointer to any string that remains, or NULL if none.
+ * Note that this may store into the string.
+ */
+/* This function copied from geninit.c . */
+static char *
+doit(char *line, bool intact)
+{
+ char *str = line;
+ char *from;
+ char *to;
+ int in_string = 0;
+
+ if (intact)
+ return str;
+ while (*str == ' ' || *str == '\t') /* strip leading whitespace */
+ ++str;
+ if (*str == 0) /* all whitespace */
+ return NULL;
+ if (!strncmp(str, "%END", 4)) /* keep these for .skipeof */
+ return str;
+ if (str[0] == '%') /* comment line */
+ return NULL;
+ /*
+ * Copy the string over itself removing:
+ * - All comments not within string literals;
+ * - Whitespace adjacent to '[' ']' '{' '}';
+ * - Whitespace before '/' '(' '<';
+ * - Whitespace after ')' '>'.
+ */
+ for (to = from = str; (*to = *from) != 0; ++from, ++to) {
+ switch (*from) {
+ case '%':
+ if (!in_string)
+ break;
+ continue;
+ case ' ':
+ case '\t':
+ if (to > str && !in_string && strchr(" \t>[]{})", to[-1]))
+ --to;
+ continue;
+ case '(':
+ case '<':
+ case '/':
+ case '[':
+ case ']':
+ case '{':
+ case '}':
+ if (to > str && !in_string && strchr(" \t", to[-1]))
+ *--to = *from;
+ if (*from == '(')
+ ++in_string;
+ continue;
+ case ')':
+ --in_string;
+ continue;
+ case '\\':
+ if (from[1] == '\\' || from[1] == '(' || from[1] == ')')
+ *++to = *++from;
+ continue;
+ default:
+ continue;
+ }
+ break;
+ }
+ /* Strip trailing whitespace. */
+ while (to > str && (to[-1] == ' ' || to[-1] == '\t'))
+ --to;
+ *to = 0;
+ return str;
+}
+
+static int
+copy_ps_file_stripping_all(stream *s, const char *fname, bool HaveTrueTypes)
+{
+ stream *f;
+ char buf[1024], *p, *q = buf;
+ int n, l = 0, m = sizeof(buf) - 1, outl = 0;
+ bool skipping = false;
+
+ f = sfopen(fname, "rb", s->memory);
+ if (f == NULL)
+ return_error(gs_error_undefinedfilename);
+ n = sfread(buf, 1, m, f);
+ buf[n] = 0;
+ do {
+ if (*q == '\r' || *q == '\n') {
+ q++;
+ continue;
+ }
+ p = strchr(q, '\r');
+ if (p == NULL)
+ p = strchr(q, '\n');
+ if (p == NULL) {
+ if (n < m)
+ p = buf + n;
+ else {
+ strcpy(buf, q);
+ l = strlen(buf);
+ m = sizeof(buf) - 1 - l;
+ if (!m) {
+ sfclose(f);
+ emprintf1(s->memory,
+ "The procset %s contains a too long line.",
+ fname);
+ return_error(gs_error_ioerror);
+ }
+ n = sfread(buf + l, 1, m, f);
+ n += l;
+ m += l;
+ buf[n] = 0;
+ q = buf;
+ continue;
+ }
+ }
+ *p = 0;
+ if (q[0] == '%')
+ l = 0;
+ else {
+ q = doit(q, false);
+ if (q == NULL)
+ l = 0;
+ else
+ l = strlen(q);
+ }
+ if (l) {
+ if (!HaveTrueTypes && !strcmp("%%beg TrueType", q))
+ skipping = true;
+ if (!skipping) {
+ outl += l + 1;
+ if (outl > 100) {
+ q[l] = '\r';
+ outl = 0;
+ } else
+ q[l] = ' ';
+ stream_write(s, q, l + 1);
+ }
+ if (!HaveTrueTypes && !strcmp("%%end TrueType", q))
+ skipping = false;
+ }
+ q = p + 1;
+ } while (n == m || q < buf + n);
+ if (outl)
+ stream_write(s, "\r", 1);
+ sfclose(f);
+ return 0;
+}
+
+static int
+copy_ps_file_strip_comments(stream *s, const char *fname, bool HaveTrueTypes)
+{
+ stream *f;
+ char buf[1024], *p, *q = buf;
+ int n, l = 0, m = sizeof(buf) - 1, outl = 0;
+ bool skipping = false;
+
+ f = sfopen(fname, "rb", s->memory);
+ if (f == NULL)
+ return_error(gs_error_undefinedfilename);
+ n = sfread(buf, 1, m, f);
+ buf[n] = 0;
+ do {
+ if (*q == '\r' || *q == '\n') {
+ q++;
+ continue;
+ }
+ p = strchr(q, '\r');
+ if (p == NULL)
+ p = strchr(q, '\n');
+ if (p == NULL) {
+ if (n < m)
+ p = buf + n;
+ else {
+ strcpy(buf, q);
+ l = strlen(buf);
+ m = sizeof(buf) - 1 - l;
+ if (!m) {
+ sfclose(f);
+ emprintf1(s->memory,
+ "The procset %s contains a too long line.",
+ fname);
+ return_error(gs_error_ioerror);
+ }
+ n = sfread(buf + l, 1, m, f);
+ n += l;
+ m += l;
+ buf[n] = 0;
+ q = buf;
+ continue;
+ }
+ }
+ *p = 0;
+ if (q[0] == '%')
+ l = 0;
+ else {
+ q = doit(q, false);
+ if (q == NULL)
+ l = 0;
+ else
+ l = strlen(q);
+ }
+ if (l) {
+ if (!HaveTrueTypes && !strcmp("%%beg TrueType", q))
+ skipping = true;
+ if (!skipping) {
+ outl += l + 1;
+ if (outl > 100) {
+ q[l] = '\n';
+ outl = 0;
+ } else
+ q[l] = '\n';
+ stream_write(s, q, l + 1);
+ }
+ if (!HaveTrueTypes && !strcmp("%%end TrueType", q))
+ skipping = false;
+ }
+ q = p + 1;
+ } while (n == m || q < buf + n);
+ if (outl)
+ stream_write(s, "\r", 1);
+ sfclose(f);
+ return 0;
+}
+#endif
+
+static int write_opdfread(stream *s)
+{
+ int index = 0;
+
+ do {
+ if (opdfread_ps[index] == 0x00)
+ break;
+ stream_write(s, opdfread_ps[index], strlen(opdfread_ps[index]));
+ index++;
+ } while (1);
+ return 0;
+}
+
+static int write_tt_encodings(stream *s, bool HaveTrueTypes)
+{
+ int index = 0;
+
+ do {
+ if (gs_mro_e_ps[index] == 0x00)
+ break;
+ stream_write(s, gs_mro_e_ps[index], strlen(gs_mro_e_ps[index]));
+ index++;
+ } while (1);
+
+ if (HaveTrueTypes) {
+ char Buffer[256];
+ single_glyph_list_t *entry = (single_glyph_list_t *)&SingleGlyphList;
+
+ gs_sprintf(Buffer, "/AdobeGlyphList mark\n");
+ stream_write(s, Buffer, strlen(Buffer));
+ while (entry->Glyph) {
+ gs_sprintf(Buffer, "/%s 16#%04x\n", entry->Glyph, entry->Unicode);
+ stream_write(s, Buffer, strlen(Buffer));
+ entry++;
+ };
+ gs_sprintf(Buffer, ".dicttomark readonly def\n");
+ stream_write(s, Buffer, strlen(Buffer));
+
+ index = 0;
+ do {
+ if (gs_mgl_e_ps[index] == 0x00)
+ break;
+ stream_write(s, gs_mgl_e_ps[index], strlen(gs_mgl_e_ps[index]));
+ index++;
+ } while (1);
+ }
+ return 0;
+}
+
+static int
+copy_procsets(stream *s, bool HaveTrueTypes, bool stripping)
+{
+ int code;
+
+ code = write_opdfread(s);
+ if (code < 0)
+ return code;
+
+ code = write_tt_encodings(s, HaveTrueTypes);
+ return code;
+
+}
+
+static int
+encode(stream **s, const stream_template *t, gs_memory_t *mem)
+{
+ stream_state *st = s_alloc_state(mem, t->stype, "pdfwrite_pdf_open_document.encode");
+
+ if (st == 0)
+ return_error(gs_error_VMerror);
+ if (t->set_defaults)
+ t->set_defaults(st);
+ if (s_add_filter(s, t, st, mem) == 0) {
+ gs_free_object(mem, st, "pdfwrite_pdf_open_document.encode");
+ return_error(gs_error_VMerror);
+ }
+ return 0;
+}
+
+/* ------ Document ------ */
+
+int ps2write_dsc_header(gx_device_pdf * pdev, int pages)
+{
+ stream *s = pdev->strm;
+
+ if (pdev->ForOPDFRead) {
+ char cre_date_time[41];
+ int code, status, cre_date_time_len;
+ char BBox[256];
+
+ if (pdev->Eps2Write)
+ stream_write(s, (byte *)"%!PS-Adobe-3.0 EPSF-3.0\n", 24);
+ else
+ stream_write(s, (byte *)"%!PS-Adobe-3.0\n", 15);
+ /* We need to calculate the document BoundingBox which is a 'high water'
+ * mark derived from the BoundingBox of all the individual pages.
+ */
+ {
+ int pagecount = 1, j;
+ double urx=0, ury=0;
+
+ for (j = 0; j < NUM_RESOURCE_CHAINS; ++j) {
+ pdf_resource_t *pres = pdev->resources[resourcePage].chains[j];
+
+ for (; pres != 0; pres = pres->next)
+ if ((!pres->named || pdev->ForOPDFRead)
+ && !pres->object->written) {
+ pdf_page_t *page = &pdev->pages[pagecount - 1];
+ if (ceil(page->MediaBox.x) > urx)
+ urx = ceil(page->MediaBox.x);
+ if (ceil(page->MediaBox.y) > ury)
+ ury = ceil(page->MediaBox.y);
+ pagecount++;
+ }
+ }
+ if (!pdev->Eps2Write || pdev->BBox.p.x > pdev->BBox.q.x || pdev->BBox.p.y > pdev->BBox.q.y)
+ gs_sprintf(BBox, "%%%%BoundingBox: 0 0 %d %d\n", (int)urx, (int)ury);
+ else
+ gs_sprintf(BBox, "%%%%BoundingBox: %d %d %d %d\n", (int)floor(pdev->BBox.p.x), (int)floor(pdev->BBox.p.y), (int)ceil(pdev->BBox.q.x), (int)ceil(pdev->BBox.q.y));
+ stream_write(s, (byte *)BBox, strlen(BBox));
+ if (!pdev->Eps2Write || pdev->BBox.p.x > pdev->BBox.q.x || pdev->BBox.p.y > pdev->BBox.q.y)
+ gs_sprintf(BBox, "%%%%HiResBoundingBox: 0 0 %.2f %.2f\n", urx, ury);
+ else
+ gs_sprintf(BBox, "%%%%HiResBoundingBox: %.2f %.2f %.2f %.2f\n", pdev->BBox.p.x, pdev->BBox.p.y, pdev->BBox.q.x, pdev->BBox.q.y);
+ stream_write(s, (byte *)BBox, strlen(BBox));
+ }
+ cre_date_time_len = pdf_get_docinfo_item(pdev, "/CreationDate", cre_date_time, sizeof(cre_date_time) - 1);
+ cre_date_time[cre_date_time_len] = 0;
+ gs_sprintf(BBox, "%%%%Creator: %s %d (%s)\n", gs_product, (int)gs_revision,
+ pdev->dname);
+ stream_write(s, (byte *)BBox, strlen(BBox));
+ stream_puts(s, "%%LanguageLevel: 2\n");
+ gs_sprintf(BBox, "%%%%CreationDate: %s\n", cre_date_time);
+ stream_write(s, (byte *)BBox, strlen(BBox));
+ gs_sprintf(BBox, "%%%%Pages: %d\n", pages);
+ stream_write(s, (byte *)BBox, strlen(BBox));
+ gs_sprintf(BBox, "%%%%EndComments\n");
+ stream_write(s, (byte *)BBox, strlen(BBox));
+ gs_sprintf(BBox, "%%%%BeginProlog\n");
+ stream_write(s, (byte *)BBox, strlen(BBox));
+ if (pdev->params.CompressPages) {
+ /* When CompressEntireFile is true and ASCII85EncodePages is false,
+ the ASCII85Encode filter is applied, rather one may expect the opposite.
+ Keeping it so due to no demand for this mode.
+ A right implementation should compute the length of the compressed procset,
+ write out an invocation of SubFileDecode filter, and write the length to
+ there assuming the output file is positionable. */
+ stream_write(s, (byte *)"currentfile /ASCII85Decode filter /LZWDecode filter cvx exec\n", 61);
+ code = encode(&s, &s_A85E_template, pdev->pdf_memory);
+ if (code < 0)
+ return code;
+ code = encode(&s, &s_LZWE_template, pdev->pdf_memory);
+ if (code < 0)
+ return code;
+ }
+ stream_puts(s, "/DSC_OPDFREAD true def\n");
+ if (pdev->Eps2Write) {
+ stream_puts(s, "/SetPageSize false def\n");
+ stream_puts(s, "/EPS2Write true def\n");
+ } else {
+ if (pdev->SetPageSize)
+ stream_puts(s, "/SetPageSize true def\n");
+ stream_puts(s, "/EPS2Write false def\n");
+ }
+
+ code = copy_procsets(s, pdev->HaveTrueTypes, false);
+ if (code < 0)
+ return code;
+ status = s_close_filters(&s, pdev->strm);
+ if (status < 0)
+ return_error(gs_error_ioerror);
+ stream_puts(s, "\n");
+ pdev->OPDFRead_procset_length = (int)stell(s);
+ }
+ return 0;
+}
+
+/* Open the document if necessary. */
+int
+pdfwrite_pdf_open_document(gx_device_pdf * pdev)
+{
+ if (!is_in_page(pdev) && pdf_stell(pdev) == 0) {
+ stream *s = pdev->strm;
+ int level = (int)(pdev->CompatibilityLevel * 10 + 0.5);
+
+ pdev->binary_ok = !pdev->params.ASCII85EncodePages;
+ if (pdev->ForOPDFRead) {
+ int code, status;
+ char BBox[256];
+ int width = (int)(pdev->width * 72.0 / pdev->HWResolution[0] + 0.5);
+ int height = (int)(pdev->height * 72.0 / pdev->HWResolution[1] + 0.5);
+
+ if (pdev->ProduceDSC)
+ pdev->CompressEntireFile = 0;
+ else {
+ stream_write(s, (byte *)"%!\r", 3);
+ gs_sprintf(BBox, "%%%%BoundingBox: 0 0 %d %d\n", width, height);
+ stream_write(s, (byte *)BBox, strlen(BBox));
+ if (pdev->params.CompressPages || pdev->CompressEntireFile) {
+ /* When CompressEntireFile is true and ASCII85EncodePages is false,
+ the ASCII85Encode filter is applied, rather one may expect the opposite.
+ Keeping it so due to no demand for this mode.
+ A right implementation should compute the length of the compressed procset,
+ write out an invocation of SubFileDecode filter, and write the length to
+ there assuming the output file is positionable. */
+ stream_write(s, (byte *)"currentfile /ASCII85Decode filter /LZWDecode filter cvx exec\n", 61);
+ code = encode(&s, &s_A85E_template, pdev->pdf_memory);
+ if (code < 0)
+ return code;
+ code = encode(&s, &s_LZWE_template, pdev->pdf_memory);
+ if (code < 0)
+ return code;
+ }
+ stream_puts(s, "/DSC_OPDFREAD false def\n");
+ code = copy_procsets(s, pdev->HaveTrueTypes, true);
+ if (code < 0)
+ return code;
+ if (!pdev->CompressEntireFile) {
+ status = s_close_filters(&s, pdev->strm);
+ if (status < 0)
+ return_error(gs_error_ioerror);
+ } else
+ pdev->strm = s;
+ if (!pdev->Eps2Write)
+ stream_puts(s, "/EPS2Write false def\n");
+ if(pdev->SetPageSize)
+ stream_puts(s, "/SetPageSize true def\n");
+ if(pdev->RotatePages)
+ stream_puts(s, "/RotatePages true def\n");
+ if(pdev->FitPages)
+ stream_puts(s, "/FitPages true def\n");
+ if(pdev->CenterPages)
+ stream_puts(s, "/CenterPages true def\n");
+ pdev->OPDFRead_procset_length = stell(s);
+ }
+ }
+ if (!(pdev->ForOPDFRead)) {
+ pprintd2(s, "%%PDF-%d.%d\n", level / 10, level % 10);
+ if (pdev->binary_ok)
+ stream_puts(s, "%\307\354\217\242\n");
+ }
+ }
+ /*
+ * Determine the compression method. Currently this does nothing.
+ * It also isn't clear whether the compression method can now be
+ * changed in the course of the document.
+ *
+ * Flate compression is available starting in PDF 1.2. Since we no
+ * longer support any older PDF versions, we ignore UseFlateCompression
+ * and always use Flate compression.
+ */
+ if (!pdev->params.CompressPages)
+ pdev->compression = pdf_compress_none;
+ else
+ pdev->compression = pdf_compress_Flate;
+ return 0;
+}
+
+/* ------ Objects ------ */
+
+/* Allocate an object ID. */
+static long
+pdf_next_id(gx_device_pdf * pdev)
+{
+ return (pdev->next_id)++;
+}
+
+/*
+ * Return the current position in the output. Note that this may be in the
+ * main output file, the asides file, or the pictures file. If the current
+ * file is the pictures file, positions returned by pdf_stell must only be
+ * used locally (for computing lengths or patching), since there is no way
+ * to map them later to the eventual position in the output file.
+ */
+gs_offset_t
+pdf_stell(gx_device_pdf * pdev)
+{
+ stream *s = pdev->strm;
+ gs_offset_t pos = stell(s);
+
+ if (s == pdev->asides.strm)
+ pos += ASIDES_BASE_POSITION;
+ return pos;
+}
+
+/* Allocate an ID for a future object.
+ * pdf_obj_ref below allocates an object and assigns it a position assuming
+ * it will be written at the current location in the PDF file. But we want
+ * some way to notice when writing the PDF file if some kinds of objects have
+ * never been written out (eg pages allocated for /Dest targets). Setting the
+ * position to 0 is good, because we always write the header, which is 15
+ * bytes. However, pdf_obj_ref is so wisely used its no longer possible to
+ * tell whether writing the object out has been deferred (in which case the
+ * pos is updated by pdf_open_obj) or not. Adding this function to allow us
+ * to create an object whose writing is definitely deferred, in which case
+ * we know it will be updated later. This allows setting the pos to 0,
+ * and we can detect that when writing the xref, and set the object to
+ * 'unused'.
+ */
+long pdf_obj_forward_ref(gx_device_pdf * pdev)
+{
+ long id = pdf_next_id(pdev);
+ gs_offset_t pos = 0;
+
+ fwrite(&pos, sizeof(pos), 1, pdev->xref.file);
+ return id;
+}
+
+/* Allocate an ID for a future object. */
+long
+pdf_obj_ref(gx_device_pdf * pdev)
+{
+ long id = pdf_next_id(pdev);
+ gs_offset_t pos = pdf_stell(pdev);
+
+ fwrite(&pos, sizeof(pos), 1, pdev->xref.file);
+ return id;
+}
+
+/* Set the offset in the xref table for an object to zero. This
+ * means that whenwritingthe xref we will mark the object as 'unused'.
+ * This is primarily of use when we encounter an error writing an object,
+ * we want to elide the entry from the xref in order to not write a
+ * broken PDF file. Of course, the missing object may still produce
+ * a broken PDF file (more subtly broken), but because the PDF interpreter
+ * generally doesn't stop if we signal an error, we try to avoid grossly
+ * broken PDF files this way.
+ */
+long
+pdf_obj_mark_unused(gx_device_pdf *pdev, long id)
+{
+ FILE *tfile = pdev->xref.file;
+ int64_t tpos = gp_ftell_64(tfile);
+ gs_offset_t pos = 0;
+
+ if (gp_fseek_64 (tfile, ((int64_t)(id - pdev->FirstObjectNumber)) * sizeof(pos),
+ SEEK_SET) != 0)
+ return gs_error_ioerror;
+ fwrite(&pos, sizeof(pos), 1, tfile);
+ if (gp_fseek_64(tfile, tpos, SEEK_SET) != 0)
+ return gs_error_ioerror;
+ return 0;
+}
+
+/* Begin an object, optionally allocating an ID. */
+long
+pdf_open_obj(gx_device_pdf * pdev, long id, pdf_resource_type_t type)
+{
+ stream *s = pdev->strm;
+
+ if (id <= 0) {
+ id = pdf_obj_ref(pdev);
+ } else {
+ gs_offset_t pos = pdf_stell(pdev);
+ FILE *tfile = pdev->xref.file;
+ int64_t tpos = gp_ftell_64(tfile);
+
+ if (gp_fseek_64 (tfile, ((int64_t)(id - pdev->FirstObjectNumber)) * sizeof(pos),
+ SEEK_SET) != 0)
+ return gs_error_ioerror;
+ fwrite(&pos, sizeof(pos), 1, tfile);
+ if (gp_fseek_64(tfile, tpos, SEEK_SET) != 0)
+ return gs_error_ioerror;
+ }
+ if (pdev->ForOPDFRead && pdev->ProduceDSC) {
+ switch(type) {
+ case resourceNone:
+ /* Used when outputting usage of a previously defined resource
+ * Does not want comments around its use
+ */
+ break;
+ case resourcePage:
+ /* We *don't* want resource comments around pages */
+ break;
+ case resourceColorSpace:
+ pprintld1(s, "%%%%BeginResource: file (PDF Color Space obj_%ld)\n", id);
+ break;
+ case resourceExtGState:
+ pprintld1(s, "%%%%BeginResource: file (PDF Extended Graphics State obj_%ld)\n", id);
+ break;
+ case resourcePattern:
+ pprintld1(s, "%%%%BeginResource: pattern (PDF Pattern obj_%ld)\n", id);
+ break;
+ case resourceShading:
+ pprintld1(s, "%%%%BeginResource: file (PDF Shading obj_%ld)\n", id);
+ break;
+ case resourceCIDFont:
+ case resourceFont:
+ /* Ought to write the font name here */
+ pprintld1(s, "%%%%BeginResource: font (PDF Font obj_%ld)\n", id);
+ break;
+ case resourceCharProc:
+ pprintld1(s, "%%%%BeginResource: file (PDF CharProc obj_%ld)\n", id);
+ break;
+ case resourceCMap:
+ pprintld1(s, "%%%%BeginResource: file (PDF CMap obj_%ld)\n", id);
+ break;
+ case resourceFontDescriptor:
+ pprintld1(s, "%%%%BeginResource: file (PDF FontDescriptor obj_%ld)\n", id);
+ break;
+ case resourceGroup:
+ pprintld1(s, "%%%%BeginResource: file (PDF Group obj_%ld)\n", id);
+ break;
+ case resourceFunction:
+ pprintld1(s, "%%%%BeginResource: file (PDF Function obj_%ld)\n", id);
+ break;
+ case resourceEncoding:
+ pprintld1(s, "%%%%BeginResource: encoding (PDF Encoding obj_%ld)\n", id);
+ break;
+ case resourceCIDSystemInfo:
+ pprintld1(s, "%%%%BeginResource: file (PDF CIDSystemInfo obj_%ld)\n", id);
+ break;
+ case resourceHalftone:
+ pprintld1(s, "%%%%BeginResource: file (PDF Halftone obj_%ld)\n", id);
+ break;
+ case resourceLength:
+ pprintld1(s, "%%%%BeginResource: file (PDF Length obj_%ld)\n", id);
+ break;
+ case resourceSoftMaskDict:
+ /* This should not be possible, not valid in PostScript */
+ pprintld1(s, "%%%%BeginResource: file (PDF SoftMask obj_%ld)\n", id);
+ break;
+ case resourceXObject:
+ /* This should not be possible, we write these inline */
+ pprintld1(s, "%%%%BeginResource: file (PDF XObject obj_%ld)\n", id);
+ break;
+ case resourceStream:
+ /* Possibly we should not add comments to this type */
+ pprintld1(s, "%%%%BeginResource: file (PDF stream obj_%ld)\n", id);
+ break;
+ case resourceOutline:
+ /* This should not be possible, not valid in PostScript */
+ pprintld1(s, "%%%%BeginResource: file (PDF Outline obj_%ld)\n", id);
+ break;
+ case resourceArticle:
+ /* This should not be possible, not valid in PostScript */
+ pprintld1(s, "%%%%BeginResource: file (PDF Article obj_%ld)\n", id);
+ break;
+ case resourceDests:
+ /* This should not be possible, not valid in PostScript */
+ pprintld1(s, "%%%%BeginResource: file (PDF Dests obj_%ld)\n", id);
+ break;
+ case resourceEmbeddedFiles:
+ /* This should not be possible, not valid in PostScript */
+ pprintld1(s, "%%%%BeginResource: file (PDF EmbeddedFiles obj_%ld)\n", id);
+ break;
+ case resourceLabels:
+ /* This should not be possible, not valid in PostScript */
+ pprintld1(s, "%%%%BeginResource: file (PDF Page Labels obj_%ld)\n", id);
+ break;
+ case resourceThread:
+ /* This should not be possible, not valid in PostScript */
+ pprintld1(s, "%%%%BeginResource: file (PDF Thread obj_%ld)\n", id);
+ break;
+ case resourceCatalog:
+ /* This should not be possible, not valid in PostScript */
+ pprintld1(s, "%%%%BeginResource: file (PDF Catalog obj_%ld)\n", id);
+ break;
+ case resourceEncrypt:
+ /* This should not be possible, not valid in PostScript */
+ pprintld1(s, "%%%%BeginResource: file (PDF Encryption obj_%ld)\n", id);
+ break;
+ case resourcePagesTree:
+ /* This should not be possible, not valid in PostScript */
+ pprintld1(s, "%%%%BeginResource: file (PDF Pages Tree obj_%ld)\n", id);
+ break;
+ case resourceMetadata:
+ /* This should not be possible, not valid in PostScript */
+ pprintld1(s, "%%%%BeginResource: file (PDF Metadata obj_%ld)\n", id);
+ break;
+ case resourceICC:
+ /* This should not be possible, not valid in PostScript */
+ pprintld1(s, "%%%%BeginResource: file (PDF ICC Profile obj_%ld)\n", id);
+ break;
+ case resourceAnnotation:
+ /* This should not be possible, not valid in PostScript */
+ pprintld1(s, "%%%%BeginResource: file (PDF Annotation obj_%ld)\n", id);
+ break;
+ case resourceFontFile:
+ pprintld1(s, "%%%%BeginResource: file (PDF FontFile obj_%ld)\n", id);
+ break;
+ default:
+ pprintld1(s, "%%%%BeginResource: file (PDF object obj_%ld)\n", id);
+ break;
+ }
+ }
+ pprintld1(s, "%ld 0 obj\n", id);
+ return id;
+}
+long
+pdf_begin_obj(gx_device_pdf * pdev, pdf_resource_type_t type)
+{
+ return pdf_open_obj(pdev, 0L, type);
+}
+
+/* End an object. */
+int
+pdf_end_obj(gx_device_pdf * pdev, pdf_resource_type_t type)
+{
+ stream_puts(pdev->strm, "endobj\n");
+ if (pdev->ForOPDFRead && pdev->ProduceDSC) {
+ switch(type) {
+ case resourcePage:
+ break;
+ default:
+ stream_puts(pdev->strm, "%%EndResource\n");
+ break;
+ }
+ }
+ return 0;
+}
+
+/* ------ Page contents ------ */
+
+/* Handle transitions between contexts. */
+static int
+ none_to_stream(gx_device_pdf *), stream_to_text(gx_device_pdf *),
+ string_to_text(gx_device_pdf *), text_to_stream(gx_device_pdf *),
+ stream_to_none(gx_device_pdf *);
+typedef int (*context_proc) (gx_device_pdf *);
+static const context_proc context_procs[4][4] =
+{
+ {0, none_to_stream, none_to_stream, none_to_stream},
+ {stream_to_none, 0, stream_to_text, stream_to_text},
+ {text_to_stream, text_to_stream, 0, 0},
+ {string_to_text, string_to_text, string_to_text, 0}
+};
+
+/* Compute an object encryption key. */
+static int
+pdf_object_key(const gx_device_pdf * pdev, gs_id object_id, byte key[16])
+{
+ gs_md5_state_t md5;
+ gs_md5_byte_t zero[2] = {0, 0}, t;
+ int KeySize = pdev->KeyLength / 8;
+
+ gs_md5_init(&md5);
+ gs_md5_append(&md5, pdev->EncryptionKey, KeySize);
+ t = (byte)(object_id >> 0); gs_md5_append(&md5, &t, 1);
+ t = (byte)(object_id >> 8); gs_md5_append(&md5, &t, 1);
+ t = (byte)(object_id >> 16); gs_md5_append(&md5, &t, 1);
+ gs_md5_append(&md5, zero, 2);
+ gs_md5_finish(&md5, key);
+ return min(KeySize + 5, 16);
+}
+
+/* Initialize encryption. */
+int
+pdf_encrypt_init(const gx_device_pdf * pdev, gs_id object_id, stream_arcfour_state *psarc4)
+{
+ byte key[16];
+
+ return s_arcfour_set_key(psarc4, key, pdf_object_key(pdev, object_id, key));
+}
+
+/* Add the encryption filter. */
+int
+pdf_begin_encrypt(gx_device_pdf * pdev, stream **s, gs_id object_id)
+{
+ gs_memory_t *mem = pdev->v_memory;
+ stream_arcfour_state *ss;
+ gs_md5_byte_t key[16];
+ int code, keylength;
+
+ if (!pdev->KeyLength)
+ return 0;
+ keylength = pdf_object_key(pdev, object_id, key);
+ ss = gs_alloc_struct(mem, stream_arcfour_state,
+ s_arcfour_template.stype, "psdf_encrypt");
+ if (ss == NULL)
+ return_error(gs_error_VMerror);
+ code = s_arcfour_set_key(ss, key, keylength);
+ if (code < 0)
+ return code;
+ if (s_add_filter(s, &s_arcfour_template, (stream_state *)ss, mem) == 0)
+ return_error(gs_error_VMerror);
+ return 0;
+ /* IMPORTANT NOTE :
+ We don't encrypt streams written into temporary files,
+ because they can be used for comparizon
+ (for example, for merging equal images).
+ Instead that the encryption is applied in pdf_copy_data,
+ when the stream is copied to the output file.
+ */
+}
+
+/* Remove the encryption filter. */
+void
+pdf_end_encrypt(gx_device_pdf * pdev)
+{
+ if (pdev->KeyLength) {
+ stream *s = pdev->strm;
+ stream *fs = s->strm;
+
+ sclose(s);
+ gs_free_object(pdev->pdf_memory, s->cbuf, "encrypt buffer");
+ gs_free_object(pdev->pdf_memory, s, "encrypt stream");
+ pdev->strm = fs;
+ }
+}
+
+/* Enter stream context. */
+static int
+none_to_stream(gx_device_pdf * pdev)
+{
+ stream *s;
+ int code;
+
+ if (pdev->contents_id != 0)
+ return_error(gs_error_Fatal); /* only 1 contents per page */
+ pdev->compression_at_page_start = pdev->compression;
+ if (pdev->ResourcesBeforeUsage) {
+ pdf_resource_t *pres;
+
+ code = pdf_enter_substream(pdev, resourcePage, gs_no_id, &pres,
+ true, pdev->params.CompressPages);
+ if (code < 0)
+ return code;
+ pdev->contents_id = pres->object->id;
+ pdev->contents_length_id = gs_no_id; /* inapplicable */
+ pdev->contents_pos = -1; /* inapplicable */
+ s = pdev->strm;
+ } else {
+ pdev->contents_id = pdf_begin_obj(pdev, resourceStream);
+ pdev->contents_length_id = pdf_obj_ref(pdev);
+ s = pdev->strm;
+ pprintld1(s, "<</Length %ld 0 R", pdev->contents_length_id);
+ if (pdev->compression == pdf_compress_Flate) {
+ if (pdev->binary_ok)
+ pprints1(s, "/Filter /%s", compression_filter_name);
+ else
+ pprints1(s, "/Filter [/ASCII85Decode /%s]", compression_filter_name);
+ }
+ stream_puts(s, ">>\nstream\n");
+ pdev->contents_pos = pdf_stell(pdev);
+ code = pdf_begin_encrypt(pdev, &s, pdev->contents_id);
+ if (code < 0)
+ return code;
+ pdev->strm = s;
+ if (pdev->compression == pdf_compress_Flate) { /* Set up the Flate filter. */
+ const stream_template *templat;
+ stream *es;
+ byte *buf;
+ compression_filter_state *st;
+
+ if (!pdev->binary_ok) { /* Set up the A85 filter */
+ const stream_template *templat2 = &s_A85E_template;
+ stream *as = s_alloc(pdev->pdf_memory, "PDF contents stream");
+ byte *buf = gs_alloc_bytes(pdev->pdf_memory, sbuf_size,
+ "PDF contents buffer");
+ stream_A85E_state *ast = gs_alloc_struct(pdev->pdf_memory, stream_A85E_state,
+ templat2->stype, "PDF contents state");
+ if (as == 0 || ast == 0 || buf == 0)
+ return_error(gs_error_VMerror);
+ s_std_init(as, buf, sbuf_size, &s_filter_write_procs,
+ s_mode_write);
+ ast->memory = pdev->pdf_memory;
+ ast->templat = templat2;
+ as->state = (stream_state *) ast;
+ as->procs.process = templat2->process;
+ as->strm = s;
+ (*templat2->init) ((stream_state *) ast);
+ pdev->strm = s = as;
+ }
+ templat = &compression_filter_template;
+ es = s_alloc(pdev->pdf_memory, "PDF compression stream");
+ buf = gs_alloc_bytes(pdev->pdf_memory, sbuf_size,
+ "PDF compression buffer");
+ st = gs_alloc_struct(pdev->pdf_memory, compression_filter_state,
+ templat->stype, "PDF compression state");
+ if (es == 0 || st == 0 || buf == 0)
+ return_error(gs_error_VMerror);
+ s_std_init(es, buf, sbuf_size, &s_filter_write_procs,
+ s_mode_write);
+ st->memory = pdev->pdf_memory;
+ st->templat = templat;
+ es->state = (stream_state *) st;
+ es->procs.process = templat->process;
+ es->strm = s;
+ (*templat->set_defaults) ((stream_state *) st);
+ (*templat->init) ((stream_state *) st);
+ pdev->strm = s = es;
+ }
+ }
+ /*
+ * Scale the coordinate system. Use an extra level of q/Q for the
+ * sake of poorly designed PDF tools that assume that the contents
+ * stream restores the CTM.
+ */
+ pprintg2(s, "q %g 0 0 %g 0 0 cm\n",
+ 72.0 / pdev->HWResolution[0], 72.0 / pdev->HWResolution[1]);
+ if (pdev->CompatibilityLevel >= 1.3) {
+ /* Set the default rendering intent. */
+ if (pdev->params.DefaultRenderingIntent != ri_Default) {
+ static const char *const ri_names[] = { psdf_ri_names };
+
+ pprints1(s, "/%s ri\n",
+ ri_names[(int)pdev->params.DefaultRenderingIntent]);
+ }
+ }
+ pdev->AR4_save_bug = false;
+ return PDF_IN_STREAM;
+}
+/* Enter text context from stream context. */
+static int
+stream_to_text(gx_device_pdf * pdev)
+{
+ int code;
+
+ /*
+ * Bizarrely enough, Acrobat Reader cares how the final font size is
+ * obtained -- the CTM (cm), text matrix (Tm), and font size (Tf)
+ * are *not* all equivalent. In particular, it seems to use the
+ * product of the text matrix and font size to decide how to
+ * anti-alias characters. Therefore, we have to temporarily patch
+ * the CTM so that the scale factors are unity. What a nuisance!
+ */
+ code = pdf_save_viewer_state(pdev, pdev->strm);
+ if (code < 0)
+ return 0;
+ pprintg2(pdev->strm, "%g 0 0 %g 0 0 cm BT\n",
+ pdev->HWResolution[0] / 72.0, pdev->HWResolution[1] / 72.0);
+ pdev->procsets |= Text;
+ code = pdf_from_stream_to_text(pdev);
+ return (code < 0 ? code : PDF_IN_TEXT);
+}
+/* Exit string context to text context. */
+static int
+string_to_text(gx_device_pdf * pdev)
+{
+ int code = pdf_from_string_to_text(pdev);
+
+ return (code < 0 ? code : PDF_IN_TEXT);
+}
+/* Exit text context to stream context. */
+static int
+text_to_stream(gx_device_pdf * pdev)
+{
+ int code;
+
+ stream_puts(pdev->strm, "ET\n");
+ code = pdf_restore_viewer_state(pdev, pdev->strm);
+ if (code < 0)
+ return code;
+ pdf_reset_text(pdev); /* because of Q */
+ return PDF_IN_STREAM;
+}
+/* Exit stream context. */
+static int
+stream_to_none(gx_device_pdf * pdev)
+{
+ stream *s = pdev->strm;
+ gs_offset_t length;
+ int code;
+
+ if (pdev->ResourcesBeforeUsage) {
+ int code = pdf_exit_substream(pdev);
+
+ if (code < 0)
+ return code;
+ } else {
+ if (pdev->vgstack_depth) {
+ code = pdf_restore_viewer_state(pdev, s);
+ if (code < 0)
+ return code;
+ }
+ if (pdev->compression_at_page_start == pdf_compress_Flate) { /* Terminate the filters. */
+ stream *fs = s->strm;
+
+ if (!pdev->binary_ok) {
+ sclose(s); /* Terminate the ASCII85 filter. */
+ gs_free_object(pdev->pdf_memory, s->cbuf, "A85E contents buffer");
+ gs_free_object(pdev->pdf_memory, s, "A85E contents stream");
+ pdev->strm = s = fs;
+ fs = s->strm;
+ }
+ sclose(s); /* Next terminate the compression filter */
+ gs_free_object(pdev->pdf_memory, s->cbuf, "zlib buffer");
+ gs_free_object(pdev->pdf_memory, s, "zlib stream");
+ pdev->strm = fs;
+ }
+ pdf_end_encrypt(pdev);
+ s = pdev->strm;
+ length = pdf_stell(pdev) - pdev->contents_pos;
+ if (pdev->PDFA != 0)
+ stream_puts(s, "\n");
+ stream_puts(s, "endstream\n");
+ pdf_end_obj(pdev, resourceStream);
+ pdf_open_obj(pdev, pdev->contents_length_id, resourceLength);
+ pprintld1(s, "%ld\n", (long)length);
+ pdf_end_obj(pdev, resourceLength);
+ }
+ return PDF_IN_NONE;
+}
+
+/* Begin a page contents part. */
+int
+pdf_open_contents(gx_device_pdf * pdev, pdf_context_t context)
+{
+ int (*proc) (gx_device_pdf *);
+
+ while ((proc = context_procs[pdev->context][context]) != 0) {
+ int code = (*proc) (pdev);
+
+ if (code < 0)
+ return code;
+ pdev->context = (pdf_context_t) code;
+ }
+ pdev->context = context;
+ return 0;
+}
+
+/* Close the current contents part if we are in one. */
+int
+pdf_close_contents(gx_device_pdf * pdev, bool last)
+{
+ if (pdev->context == PDF_IN_NONE)
+ return 0;
+ if (last) { /* Exit from the clipping path gsave. */
+ int code = pdf_open_contents(pdev, PDF_IN_STREAM);
+
+ if (code < 0)
+ return code;
+ stream_puts(pdev->strm, "Q\n"); /* See none_to_stream. */
+ pdf_close_text_contents(pdev);
+ }
+ return pdf_open_contents(pdev, PDF_IN_NONE);
+}
+
+/* ------ Resources et al ------ */
+
+/* Define the allocator descriptors for the resource types. */
+const char *const pdf_resource_type_names[] = {
+ PDF_RESOURCE_TYPE_NAMES
+};
+const gs_memory_struct_type_t *const pdf_resource_type_structs[] = {
+ PDF_RESOURCE_TYPE_STRUCTS
+};
+
+/* Cancel a resource (do not write it into PDF). */
+int
+pdf_cancel_resource(gx_device_pdf * pdev, pdf_resource_t *pres, pdf_resource_type_t rtype)
+{
+ /* fixme : Remove *pres from resource chain. */
+ pres->where_used = 0;
+ if (pres->object) {
+ pres->object->written = true;
+ if (rtype == resourceXObject || rtype == resourceCharProc || rtype == resourceOther
+ || rtype >= NUM_RESOURCE_TYPES) {
+ int code = cos_stream_release_pieces(pdev, (cos_stream_t *)pres->object);
+
+ if (code < 0)
+ return code;
+ }
+ cos_release(pres->object, "pdf_cancel_resource");
+ gs_free_object(pdev->pdf_memory, pres->object, "pdf_cancel_resources");
+ pres->object = 0;
+ }
+ return 0;
+}
+
+/* Remove a resource. */
+void
+pdf_forget_resource(gx_device_pdf * pdev, pdf_resource_t *pres1, pdf_resource_type_t rtype)
+{ /* fixme : optimize. */
+ pdf_resource_t **pchain = pdev->resources[rtype].chains;
+ pdf_resource_t *pres;
+ pdf_resource_t **pprev = &pdev->last_resource;
+ int i;
+
+ /* since we're about to free the resource, we can just set
+ any of these references to null
+ */
+ for (i = 0; i < pdev->sbstack_size; i++) {
+ if (pres1 == pdev->sbstack[i].font3) {
+ pdev->sbstack[i].font3 = NULL;
+ }
+ else if (pres1 == pdev->sbstack[i].accumulating_substream_resource) {
+ pdev->sbstack[i].accumulating_substream_resource = NULL;
+ }
+ else if (pres1 == pdev->sbstack[i].pres_soft_mask_dict) {
+ pdev->sbstack[i].pres_soft_mask_dict = NULL;
+ }
+ }
+
+ for (; (pres = *pprev) != 0; pprev = &pres->prev)
+ if (pres == pres1) {
+ *pprev = pres->prev;
+ break;
+ }
+
+ for (i = (gs_id_hash(pres1->rid) % NUM_RESOURCE_CHAINS); i < NUM_RESOURCE_CHAINS; i++) {
+ pprev = pchain + i;
+ for (; (pres = *pprev) != 0; pprev = &pres->next)
+ if (pres == pres1) {
+ *pprev = pres->next;
+ if (pres->object) {
+ COS_RELEASE(pres->object, "pdf_forget_resource");
+ gs_free_object(pdev->pdf_memory, pres->object, "pdf_forget_resource");
+ pres->object = 0;
+ }
+ gs_free_object(pdev->pdf_memory, pres, "pdf_forget_resource");
+ return;
+ }
+ }
+}
+
+static int
+nocheck(gx_device_pdf * pdev, pdf_resource_t *pres0, pdf_resource_t *pres1)
+{
+ return 1;
+}
+
+/* Substitute a resource with a same one. */
+/* NB we cannot substitute resources which have already had an
+ id assigned to them, because they already have an entry in the
+ xref table. If we want to substiute a resource then it should
+ have been allocated with an initial id of -1.
+ (see pdf_alloc_resource)
+*/
+int
+pdf_substitute_resource(gx_device_pdf *pdev, pdf_resource_t **ppres,
+ pdf_resource_type_t rtype,
+ int (*eq)(gx_device_pdf * pdev, pdf_resource_t *pres0, pdf_resource_t *pres1),
+ bool write)
+{
+ pdf_resource_t *pres1 = *ppres;
+ int code;
+
+ code = pdf_find_same_resource(pdev, rtype, ppres, (eq ? eq : nocheck));
+ if (code < 0)
+ return code;
+ if (code != 0) {
+ code = pdf_cancel_resource(pdev, (pdf_resource_t *)pres1, rtype);
+ if (code < 0)
+ return code;
+ pdf_forget_resource(pdev, pres1, rtype);
+ return 0;
+ } else {
+ if (pres1->object->id < 0)
+ pdf_reserve_object_id(pdev, pres1, gs_no_id);
+ if (write) {
+ code = cos_write_object(pres1->object, pdev, rtype);
+ if (code < 0)
+ return code;
+ pres1->object->written = 1;
+ }
+ return 1;
+ }
+}
+
+/* Find a resource of a given type by gs_id. */
+pdf_resource_t *
+pdf_find_resource_by_gs_id(gx_device_pdf * pdev, pdf_resource_type_t rtype,
+ gs_id rid)
+{
+ pdf_resource_t **pchain = PDF_RESOURCE_CHAIN(pdev, rtype, rid);
+ pdf_resource_t **pprev = pchain;
+ pdf_resource_t *pres;
+
+ for (; (pres = *pprev) != 0; pprev = &pres->next)
+ if (pres->rid == rid) {
+ if (pprev != pchain) {
+ *pprev = pres->next;
+ pres->next = *pchain;
+ *pchain = pres;
+ }
+ return pres;
+ }
+ return 0;
+}
+
+/* Find resource by resource id. */
+pdf_resource_t *
+pdf_find_resource_by_resource_id(gx_device_pdf * pdev, pdf_resource_type_t rtype, gs_id id)
+{
+ pdf_resource_t **pchain = pdev->resources[rtype].chains;
+ pdf_resource_t *pres;
+ int i;
+
+ for (i = 0; i < NUM_RESOURCE_CHAINS; i++) {
+ for (pres = pchain[i]; pres != 0; pres = pres->next) {
+ if (pres->object->id == id)
+ return pres;
+ }
+ }
+ return 0;
+}
+
+/* Find same resource. */
+int
+pdf_find_same_resource(gx_device_pdf * pdev, pdf_resource_type_t rtype, pdf_resource_t **ppres,
+ int (*eq)(gx_device_pdf * pdev, pdf_resource_t *pres0, pdf_resource_t *pres1))
+{
+ pdf_resource_t **pchain = pdev->resources[rtype].chains;
+ pdf_resource_t *pres;
+ cos_object_t *pco0 = (*ppres)->object;
+ int i;
+
+ for (i = 0; i < NUM_RESOURCE_CHAINS; i++) {
+ for (pres = pchain[i]; pres != 0; pres = pres->next) {
+ if (*ppres != pres) {
+ int code;
+ cos_object_t *pco1 = pres->object;
+
+ if (pco1 == NULL || cos_type(pco0) != cos_type(pco1))
+ continue; /* don't compare different types */
+ code = pco0->cos_procs->equal(pco0, pco1, pdev);
+ if (code < 0)
+ return code;
+ if (code > 0) {
+ code = eq(pdev, *ppres, pres);
+ if (code < 0)
+ return code;
+ if (code > 0) {
+ *ppres = pres;
+ return 1;
+ }
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+/* Drop resources by a condition. */
+void
+pdf_drop_resources(gx_device_pdf * pdev, pdf_resource_type_t rtype,
+ int (*cond)(gx_device_pdf * pdev, pdf_resource_t *pres))
+{
+ pdf_resource_t **pchain = pdev->resources[rtype].chains;
+ pdf_resource_t **pprev;
+ pdf_resource_t *pres;
+ int i;
+
+ for (i = 0; i < NUM_RESOURCE_CHAINS; i++) {
+ pprev = pchain + i;
+ for (; (pres = *pprev) != 0; ) {
+ if (cond(pdev, pres)) {
+ *pprev = pres->next;
+ pres->next = pres; /* A temporary mark - see below */
+ } else
+ pprev = &pres->next;
+ }
+ }
+ pprev = &pdev->last_resource;
+ for (; (pres = *pprev) != 0; )
+ if (pres->next == pres) {
+ *pprev = pres->prev;
+ if (pres->object) {
+ COS_RELEASE(pres->object, "pdf_drop_resources");
+ gs_free_object(pdev->pdf_memory, pres->object, "pdf_drop_resources");
+ pres->object = 0;
+ }
+ gs_free_object(pdev->pdf_memory, pres, "pdf_drop_resources");
+ } else
+ pprev = &pres->prev;
+}
+
+/* Print resource statistics. */
+void
+pdf_print_resource_statistics(gx_device_pdf * pdev)
+{
+
+ int rtype;
+
+ for (rtype = 0; rtype < NUM_RESOURCE_TYPES; rtype++) {
+ pdf_resource_t **pchain = pdev->resources[rtype].chains;
+ pdf_resource_t *pres;
+ const char *name = pdf_resource_type_names[rtype];
+ int i, n = 0;
+
+ for (i = 0; i < NUM_RESOURCE_CHAINS; i++) {
+ for (pres = pchain[i]; pres != 0; pres = pres->next, n++);
+ }
+ dmprintf3(pdev->pdf_memory, "Resource type %d (%s) has %d instances.\n", rtype,
+ (name ? name : ""), n);
+ }
+}
+
+/* Begin an object logically separate from the contents. */
+long
+pdf_open_separate(gx_device_pdf * pdev, long id, pdf_resource_type_t type)
+{
+ int code;
+ code = pdfwrite_pdf_open_document(pdev);
+ if (code < 0)
+ return code;
+ pdev->asides.save_strm = pdev->strm;
+ pdev->strm = pdev->asides.strm;
+ return pdf_open_obj(pdev, id, type);
+}
+long
+pdf_begin_separate(gx_device_pdf * pdev, pdf_resource_type_t type)
+{
+ return pdf_open_separate(pdev, 0L, type);
+}
+
+void
+pdf_reserve_object_id(gx_device_pdf * pdev, pdf_resource_t *pres, long id)
+{
+ pres->object->id = (id == 0 ? pdf_obj_ref(pdev) : id);
+ gs_sprintf(pres->rname, "R%ld", pres->object->id);
+}
+
+/* Begin an aside (resource, annotation, ...). */
+int
+pdf_alloc_aside(gx_device_pdf * pdev, pdf_resource_t ** plist,
+ const gs_memory_struct_type_t * pst, pdf_resource_t **ppres,
+ long id)
+{
+ pdf_resource_t *pres;
+ cos_object_t *object;
+
+ if (pst == NULL)
+ pst = &st_pdf_resource;
+ pres = gs_alloc_struct(pdev->pdf_memory, pdf_resource_t, pst,
+ "pdf_alloc_aside(resource)");
+ if (pres == 0)
+ return_error(gs_error_VMerror);
+ object = cos_object_alloc(pdev, "pdf_alloc_aside(object)");
+ if (object == 0)
+ return_error(gs_error_VMerror);
+ memset(pres + 1, 0, pst->ssize - sizeof(*pres));
+ pres->object = object;
+ if (id < 0) {
+ object->id = -1L;
+ pres->rname[0] = 0;
+ } else
+ pdf_reserve_object_id(pdev, pres, id);
+ pres->next = *plist;
+ pres->rid = 0;
+ *plist = pres;
+ pres->prev = pdev->last_resource;
+ pdev->last_resource = pres;
+ pres->named = false;
+ pres->global = false;
+ pres->where_used = pdev->used_mask;
+ *ppres = pres;
+ return 0;
+}
+int
+pdf_begin_aside(gx_device_pdf * pdev, pdf_resource_t ** plist,
+ const gs_memory_struct_type_t * pst, pdf_resource_t ** ppres,
+ pdf_resource_type_t type)
+{
+ long id = pdf_begin_separate(pdev, type);
+
+ if (id < 0)
+ return (int)id;
+ return pdf_alloc_aside(pdev, plist, pst, ppres, id);
+}
+
+/* Begin a resource of a given type. */
+int
+pdf_begin_resource_body(gx_device_pdf * pdev, pdf_resource_type_t rtype,
+ gs_id rid, pdf_resource_t ** ppres)
+{
+ int code;
+
+ if (rtype >= NUM_RESOURCE_TYPES)
+ rtype = resourceOther;
+
+ code = pdf_begin_aside(pdev, PDF_RESOURCE_CHAIN(pdev, rtype, rid),
+ pdf_resource_type_structs[rtype], ppres, rtype);
+
+ if (code >= 0)
+ (*ppres)->rid = rid;
+ return code;
+}
+int
+pdf_begin_resource(gx_device_pdf * pdev, pdf_resource_type_t rtype, gs_id rid,
+ pdf_resource_t ** ppres)
+{
+ int code;
+
+ if (rtype >= NUM_RESOURCE_TYPES)
+ rtype = resourceOther;
+
+ code = pdf_begin_resource_body(pdev, rtype, rid, ppres);
+
+ if (code >= 0 && pdf_resource_type_names[rtype] != 0) {
+ stream *s = pdev->strm;
+
+ pprints1(s, "<</Type%s", pdf_resource_type_names[rtype]);
+ pprintld1(s, "/Name/R%ld", (*ppres)->object->id);
+ }
+ return code;
+}
+
+/* Allocate a resource, but don't open the stream. */
+/* If the passed in id 'id' is -1 then in pdf_alloc_aside
+ We *don't* reserve an object id (if its 0 or more we do).
+ This has important consequences; once an id is created we
+ can't 'cancel' it, it will always be written to the xref.
+ So if we want to not write duplicates we should create
+ the object with an 'id' of -1, and when we finish writing it
+ we should call 'pdf_substitute_resource'. If that finds a
+ duplicate then it will throw away the new one ands use the old.
+ If it doesn't find a duplicate then it will create an object
+ id for the new resource.
+*/
+int
+pdf_alloc_resource(gx_device_pdf * pdev, pdf_resource_type_t rtype, gs_id rid,
+ pdf_resource_t ** ppres, long id)
+{
+ int code;
+
+ if (rtype >= NUM_RESOURCE_TYPES)
+ rtype = resourceOther;
+
+ code = pdf_alloc_aside(pdev, PDF_RESOURCE_CHAIN(pdev, rtype, rid),
+ pdf_resource_type_structs[rtype], ppres, id);
+
+ if (code >= 0)
+ (*ppres)->rid = rid;
+ return code;
+}
+
+/* Get the object id of a resource. */
+long
+pdf_resource_id(const pdf_resource_t *pres)
+{
+ return pres->object->id;
+}
+
+/* End an aside or other separate object. */
+int
+pdf_end_separate(gx_device_pdf * pdev, pdf_resource_type_t type)
+{
+ int code = pdf_end_obj(pdev, type);
+
+ pdev->strm = pdev->asides.save_strm;
+ pdev->asides.save_strm = 0;
+ return code;
+}
+int
+pdf_end_aside(gx_device_pdf * pdev, pdf_resource_type_t type)
+{
+ return pdf_end_separate(pdev, type);
+}
+
+/* End a resource. */
+int
+pdf_end_resource(gx_device_pdf * pdev, pdf_resource_type_t type)
+{
+ return pdf_end_aside(pdev, type);
+}
+
+/*
+ * Write the Cos objects for resources local to a content stream. Formerly,
+ * this procedure also freed such objects, but this doesn't work, because
+ * resources of one type might refer to resources of another type.
+ */
+int
+pdf_write_resource_objects(gx_device_pdf *pdev, pdf_resource_type_t rtype)
+{
+ int j, code = 0;
+
+ for (j = 0; j < NUM_RESOURCE_CHAINS && code >= 0; ++j) {
+ pdf_resource_t *pres = pdev->resources[rtype].chains[j];
+
+ for (; pres != 0; pres = pres->next)
+ if ((!pres->named || pdev->ForOPDFRead)
+ && pres->object && !pres->object->written) {
+ code = cos_write_object(pres->object, pdev, rtype);
+ }
+ }
+ return code;
+}
+
+/*
+ * Reverse resource chains.
+ * ps2write uses it with page resources.
+ * Assuming only the 0th chain contauns something.
+ */
+void
+pdf_reverse_resource_chain(gx_device_pdf *pdev, pdf_resource_type_t rtype)
+{
+ pdf_resource_t *pres = pdev->resources[rtype].chains[0];
+ pdf_resource_t *pres1, *pres0 = pres, *pres2;
+
+ if (pres == NULL)
+ return;
+ pres1 = pres->next;
+ for (;;) {
+ if (pres1 == NULL)
+ break;
+ pres2 = pres1->next;
+ pres1->next = pres;
+ pres = pres1;
+ pres1 = pres2;
+ }
+ pres0->next = NULL;
+ pdev->resources[rtype].chains[0] = pres;
+}
+
+/*
+ * Free unnamed Cos objects for resources local to a content stream,
+ * since they can't be used again.
+ */
+int
+pdf_free_resource_objects(gx_device_pdf *pdev, pdf_resource_type_t rtype)
+{
+ int j;
+
+ for (j = 0; j < NUM_RESOURCE_CHAINS; ++j) {
+ pdf_resource_t **prev = &pdev->resources[rtype].chains[j];
+ pdf_resource_t *pres;
+
+ while ((pres = *prev) != 0) {
+ if (pres->named) { /* named, don't free */
+ prev = &pres->next;
+ } else {
+ if (pres->object) {
+ cos_free(pres->object, "pdf_free_resource_objects");
+ pres->object = 0;
+ }
+ *prev = pres->next;
+ }
+ }
+ }
+ return 0;
+}
+
+/*
+ * Store the resource sets for a content stream (page or XObject).
+ * Sets page->{procsets, resource_ids[]}.
+ */
+int
+pdf_store_page_resources(gx_device_pdf *pdev, pdf_page_t *page, bool clear_usage)
+{
+ int i;
+
+ /* Write any resource dictionaries. */
+
+ for (i = 0; i <= resourceFont; ++i) {
+ stream *s = 0;
+ int j;
+
+ if (i == resourceOther || i >= NUM_RESOURCE_TYPES)
+ continue;
+ page->resource_ids[i] = 0;
+ for (j = 0; j < NUM_RESOURCE_CHAINS; ++j) {
+ pdf_resource_t *pres = pdev->resources[i].chains[j];
+
+ for (; pres != 0; pres = pres->next) {
+ if (pres->where_used & pdev->used_mask) {
+ long id = pdf_resource_id(pres);
+
+ if (id == -1L)
+ continue;
+ if (s == 0) {
+ page->resource_ids[i] = pdf_begin_separate(pdev, i);
+ pdf_record_usage(pdev, page->resource_ids[i], pdev->next_page);
+ s = pdev->strm;
+ stream_puts(s, "<<");
+ }
+ pprints1(s, "/%s\n", pres->rname);
+ pprintld1(s, "%ld 0 R", id);
+ pdf_record_usage(pdev, id, pdev->next_page);
+ if (clear_usage)
+ pres->where_used -= pdev->used_mask;
+ }
+ }
+ }
+ if (s) {
+ stream_puts(s, ">>\n");
+ pdf_end_separate(pdev, i);
+ }
+ /* If an object isn't used, we still need to emit it :-( This is because
+ * we reserved an object number for it, and the xref will have an entry
+ * for it. If we don't actually emit it then the xref will be invalid.
+ * An alternative would be to modify the xref to mark the object as unused.
+ */
+ if (i != resourceFont && i != resourceProperties)
+ pdf_write_resource_objects(pdev, i);
+ }
+ page->procsets = pdev->procsets;
+ return 0;
+}
+
+/* Copy data from a temporary file to a stream. */
+void
+pdf_copy_data(stream *s, FILE *file, gs_offset_t count, stream_arcfour_state *ss)
+{
+ gs_offset_t r, left = count;
+ byte buf[sbuf_size];
+
+ while (left > 0) {
+ uint copy = min(left, sbuf_size);
+
+ r = fread(buf, 1, copy, file);
+ if (r < 1) {
+ gs_note_error(gs_error_ioerror);
+ return;
+ }
+ if (ss)
+ s_arcfour_process_buffer(ss, buf, copy);
+ stream_write(s, buf, copy);
+ left -= copy;
+ }
+}
+
+/* Copy data from a temporary file to a stream,
+ which may be targetted to the same file. */
+void
+pdf_copy_data_safe(stream *s, FILE *file, gs_offset_t position, long count)
+{
+ long r, left = count;
+
+ while (left > 0) {
+ byte buf[sbuf_size];
+ long copy = min(left, (long)sbuf_size);
+ int64_t end_pos = gp_ftell_64(file);
+
+ if (gp_fseek_64(file, position + count - left, SEEK_SET) != 0) {
+ gs_note_error(gs_error_ioerror);
+ return;
+ }
+ r = fread(buf, 1, copy, file);
+ if (r < 1) {
+ gs_note_error(gs_error_ioerror);
+ return;
+ }
+ if (gp_fseek_64(file, end_pos, SEEK_SET) != 0) {
+ gs_note_error(gs_error_ioerror);
+ return;
+ }
+ stream_write(s, buf, copy);
+ sflush(s);
+ left -= copy;
+ }
+}
+
+/* ------ Pages ------ */
+
+/* Get or assign the ID for a page. */
+/* Returns 0 if the page number is out of range. */
+long
+pdf_page_id(gx_device_pdf * pdev, int page_num)
+{
+ cos_dict_t *Page;
+
+ if (page_num < 1)
+ return 0;
+ if (page_num >= pdev->num_pages) { /* Grow the pages array. */
+ uint new_num_pages;
+ pdf_page_t *new_pages;
+
+ /* Maximum page in PDF is 2^31 - 1. Clamp to that limit here */
+ if (page_num > (1L << 31) - 11)
+ page_num = (1L << 31) - 11;
+ new_num_pages = max(page_num + 10, pdev->num_pages << 1);
+
+ new_pages = gs_resize_object(pdev->pdf_memory, pdev->pages, new_num_pages,
+ "pdf_page_id(resize pages)");
+
+ if (new_pages == 0)
+ return 0;
+ memset(&new_pages[pdev->num_pages], 0,
+ (new_num_pages - pdev->num_pages) * sizeof(pdf_page_t));
+ pdev->pages = new_pages;
+ pdev->num_pages = new_num_pages;
+ }
+ if ((Page = pdev->pages[page_num - 1].Page) == 0) {
+ pdev->pages[page_num - 1].Page = Page =
+ cos_dict_alloc(pdev, "pdf_page_id");
+ Page->id = pdf_obj_forward_ref(pdev);
+ }
+ return Page->id;
+}
+
+/* Get the page structure for the current page. */
+pdf_page_t *
+pdf_current_page(gx_device_pdf *pdev)
+{
+ return &pdev->pages[pdev->next_page];
+}
+
+/* Get the dictionary object for the current page. */
+cos_dict_t *
+pdf_current_page_dict(gx_device_pdf *pdev)
+{
+ if (pdf_page_id(pdev, pdev->next_page + 1) <= 0)
+ return 0;
+ return pdev->pages[pdev->next_page].Page;
+}
+
+/* Write saved page- or document-level information. */
+int
+pdf_write_saved_string(gx_device_pdf * pdev, gs_string * pstr)
+{
+ if (pstr->data != 0) {
+ stream_write(pdev->strm, pstr->data, pstr->size);
+ gs_free_string(pdev->pdf_memory, pstr->data, pstr->size,
+ "pdf_write_saved_string");
+ pstr->data = 0;
+ }
+ return 0;
+}
+
+/* Open a page for writing. */
+int
+pdf_open_page(gx_device_pdf * pdev, pdf_context_t context)
+{
+ if (!is_in_page(pdev)) {
+ int code;
+
+ if (pdf_page_id(pdev, pdev->next_page + 1) == 0)
+ return_error(gs_error_VMerror);
+ code = pdfwrite_pdf_open_document(pdev);
+ if (code < 0)
+ return code;
+ }
+ /* Note that context may be PDF_IN_NONE here. */
+ return pdf_open_contents(pdev, context);
+}
+
+/* Go to the unclipped stream context. */
+int
+pdf_unclip(gx_device_pdf * pdev)
+{
+ const int bottom = (pdev->ResourcesBeforeUsage ? 1 : 0);
+ /* When ResourcesBeforeUsage != 0, one sbstack element
+ appears from the page contents stream. */
+
+ if (pdev->sbstack_depth <= bottom) {
+ int code = pdf_open_page(pdev, PDF_IN_STREAM);
+
+ if (code < 0)
+ return code;
+ }
+ if (pdev->context > PDF_IN_STREAM) {
+ int code = pdf_open_contents(pdev, PDF_IN_STREAM);
+
+ if (code < 0)
+ return code;
+ }
+ if (pdev->vgstack_depth > pdev->vgstack_bottom) {
+ int code = pdf_restore_viewer_state(pdev, pdev->strm);
+
+ if (code < 0)
+ return code;
+ code = pdf_remember_clip_path(pdev, NULL);
+ if (code < 0)
+ return code;
+ pdev->clip_path_id = pdev->no_clip_path_id;
+ }
+ return 0;
+}
+
+/* ------ Miscellaneous output ------ */
+
+/* Generate the default Producer string. */
+void
+pdf_store_default_Producer(char buf[PDF_MAX_PRODUCER])
+{
+ if ((gs_revision % 100) == 0)
+ gs_sprintf(buf, "(%s %1.1f)", gs_product, gs_revision / 100.0);
+ else
+ gs_sprintf(buf, "(%s %1.2f)", gs_product, gs_revision / 100.0);
+}
+
+/* Write matrix values. */
+void
+pdf_put_matrix(gx_device_pdf * pdev, const char *before,
+ const gs_matrix * pmat, const char *after)
+{
+ stream *s = pdev->strm;
+
+ if (before)
+ stream_puts(s, before);
+ pprintg6(s, "%g %g %g %g %g %g ",
+ pmat->xx, pmat->xy, pmat->yx, pmat->yy, pmat->tx, pmat->ty);
+ if (after)
+ stream_puts(s, after);
+}
+
+/*
+ * Write a name, with escapes for unusual characters. Since we only support
+ * PDF 1.2 and above, we can use an escape sequence for anything except a
+ * null <00>, and the machinery for selecting the put_name_chars procedure
+ * depending on CompatibilityLevel is no longer needed.
+ */
+static int
+pdf_put_name_chars_1_2(stream *s, const byte *nstr, uint size)
+{
+ uint i;
+
+ for (i = 0; i < size; ++i) {
+ uint c = nstr[i];
+ char hex[4];
+
+ switch (c) {
+ default:
+ if (c >= 0x21 && c <= 0x7e) {
+ stream_putc(s, (byte)c);
+ break;
+ }
+ /* falls through */
+ case '#':
+ case '%':
+ case '(': case ')':
+ case '<': case '>':
+ case '[': case ']':
+ case '{': case '}':
+ case '/':
+ gs_sprintf(hex, "#%02x", c);
+ stream_puts(s, hex);
+ break;
+ case 0:
+ stream_puts(s, "BnZr"); /* arbitrary */
+ }
+ }
+ return 0;
+}
+pdf_put_name_chars_proc_t
+pdf_put_name_chars_proc(const gx_device_pdf *pdev)
+{
+ return &pdf_put_name_chars_1_2;
+}
+int
+pdf_put_name_chars(const gx_device_pdf *pdev, const byte *nstr, uint size)
+{
+ return pdf_put_name_chars_proc(pdev)(pdev->strm, nstr, size);
+}
+int
+pdf_put_name(const gx_device_pdf *pdev, const byte *nstr, uint size)
+{
+ stream_putc(pdev->strm, '/');
+ return pdf_put_name_chars(pdev, nstr, size);
+}
+
+/* Write an encoded string with encryption. */
+static int
+pdf_encrypt_encoded_string(const gx_device_pdf *pdev, const byte *str, uint size, gs_id object_id)
+{
+ stream sinp, sstr, sout;
+ stream_PSSD_state st;
+ stream_state so;
+ byte buf[100], bufo[100];
+ stream_arcfour_state sarc4;
+
+ if (pdf_encrypt_init(pdev, object_id, &sarc4) < 0) {
+ /* The interface can't pass an error. */
+ stream_write(pdev->strm, str, size);
+ return size;
+ }
+ s_init(&sinp, NULL);
+ sread_string(&sinp, str + 1, size);
+ s_init(&sstr, NULL);
+ sstr.close_at_eod = false;
+ s_init_state((stream_state *)&st, &s_PSSD_template, NULL);
+ s_init_filter(&sstr, (stream_state *)&st, buf, sizeof(buf), &sinp);
+ s_init(&sout, NULL);
+ s_init_state(&so, &s_PSSE_template, NULL);
+ s_init_filter(&sout, &so, bufo, sizeof(bufo), pdev->strm);
+ stream_putc(pdev->strm, '(');
+ for (;;) {
+ uint n;
+ int code = sgets(&sstr, buf, sizeof(buf), &n);
+
+ if (n > 0) {
+ s_arcfour_process_buffer(&sarc4, buf, n);
+ stream_write(&sout, buf, n);
+ }
+ if (code == EOFC)
+ break;
+ if (code < 0 || n < sizeof(buf)) {
+ /* The interface can't pass an error. */
+ break;
+ }
+ }
+ sclose(&sout); /* Writes ')'. */
+ return (int)stell(&sinp) + 1;
+}
+
+/* Write an encoded string with possible encryption. */
+static int
+pdf_put_encoded_string(const gx_device_pdf *pdev, const byte *str, uint size, gs_id object_id)
+{
+ if (!pdev->KeyLength || object_id == (gs_id)-1) {
+ stream_write(pdev->strm, str, size);
+ return 0;
+ } else
+ return pdf_encrypt_encoded_string(pdev, str, size, object_id);
+}
+/* Write an encoded string with possible encryption. */
+static int
+pdf_put_encoded_string_as_hex(const gx_device_pdf *pdev, const byte *str, uint size, gs_id object_id)
+{
+ if (!pdev->KeyLength || object_id == (gs_id)-1) {
+ int i, oct, width = 0;
+ char hex[16] = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
+
+ if (pdev->ForOPDFRead && pdev->ProduceDSC)
+ stream_write(pdev->strm, "\n", 1);
+ stream_write(pdev->strm, "<", 1);
+ width++;
+ for (i = 1; i < size - 1; i++) {
+ if (str[i] == '\\') {
+ if (str[i + 1] >= '0' && str[i + 1] <= '9') {
+ oct = (str[i+1] - 0x30) * 64;
+ oct += (str[i+2] - 0x30) *8;
+ oct += str[i+3] - 0x30;
+ i+=3;
+ } else {
+ switch (str[++i]) {
+ case 'b' :
+ oct = 8;
+ break;
+ case 't' :
+ oct = 9;
+ break;
+ case 'n' :
+ oct = 10;
+ break;
+ case 'f' :
+ oct = 12;
+ break;
+ case 'r' :
+ oct = 13;
+ break;
+ default:
+ oct = str[i];
+ break;
+ }
+ }
+ if (width > 252 && pdev->ForOPDFRead && pdev->ProduceDSC) {
+ stream_write(pdev->strm, "\n", 1);
+ width = 0;
+ }
+ stream_write(pdev->strm, &hex[(oct & 0xf0) >> 4], 1);
+ stream_write(pdev->strm, &hex[oct & 0x0f], 1);
+ width += 2;
+ } else {
+ if (width > 252 && pdev->ForOPDFRead && pdev->ProduceDSC) {
+ stream_write(pdev->strm, "\n", 1);
+ width = 0;
+ }
+ stream_write(pdev->strm, &hex[(str[i] & 0xf0) >> 4], 1);
+ stream_write(pdev->strm, &hex[str[i] & 0x0f], 1);
+ width += 2;
+ }
+ }
+ stream_write(pdev->strm, ">", 1);
+ if (pdev->ForOPDFRead && pdev->ProduceDSC)
+ stream_write(pdev->strm, "\n", 1);
+ return 0;
+ } else
+ return pdf_encrypt_encoded_string(pdev, str, size, object_id);
+}
+
+/* Write an encoded hexadecimal string with possible encryption. */
+static int
+pdf_put_encoded_hex_string(const gx_device_pdf *pdev, const byte *str, uint size, gs_id object_id)
+{
+ emprintf(pdev->memory,
+ "Unimplemented function : pdf_put_encoded_hex_string\n");
+ stream_write(pdev->strm, str, size);
+ return_error(gs_error_unregistered);
+}
+/* Scan an item in a serialized array or dictionary.
+ This is a very simplified Postscript lexical scanner.
+ It assumes the serialization with pdf===only defined in gs/lib/gs_pdfwr.ps .
+ We only need to select strings and encrypt them.
+ Other items are passed identically.
+ Note we don't reconstruct the nesting of arrays|dictionaries.
+*/
+static int
+pdf_scan_item(const gx_device_pdf * pdev, const byte * p, uint l, gs_id object_id)
+{
+ const byte *q = p;
+ int n = l;
+
+ if (*q == ' ' || *q == 't' || *q == '\r' || *q == '\n')
+ return (l > 0 ? 1 : 0);
+ for (q++, n--; n; q++, n--) {
+ if (*q == ' ' || *q == 't' || *q == '\r' || *q == '\n')
+ return q - p;
+ if (*q == '/' || *q == '[' || *q == ']' || *q == '{' || *q == '}' || *q == '(' || *q == '<')
+ return q - p;
+ /* Note : immediate names are not allowed in PDF. */
+ }
+ return l;
+}
+
+/* Write a serialized array or dictionary with possible encryption. */
+static int
+pdf_put_composite(const gx_device_pdf * pdev, const byte * vstr, uint size, gs_id object_id)
+{
+ if (!pdev->KeyLength || object_id == (gs_id)-1) {
+ if (pdev->ForOPDFRead && pdev->ProduceDSC) {
+ stream_putc(pdev->strm, (byte)'\n');
+ if (size > 255) {
+ const byte *start, *p, *end, *save;
+ int width = 0;
+
+ end = vstr + size;
+ start = p = vstr;
+ while (p < end) {
+ if(*p == '\r' || *p == '\n') {
+ width = 0;
+ p++;
+ continue;
+ }
+ if (width > 254) {
+ save = p;
+ /* search backwards for a point to split */
+ while (p > start) {
+ if (*p == '/' || *p == '[' || *p == '{' || *p == '(' || *p == ' ') {
+ stream_write(pdev->strm, start, p - start);
+ stream_putc(pdev->strm, (byte)'\n');
+ start = p;
+ }
+ else p--;
+ }
+ if (p == start) {
+ stream_write(pdev->strm, start, save - start);
+ stream_putc(pdev->strm, (byte)'\n');
+ start = save;
+ }
+ } else {
+ width++;
+ p++;
+ }
+ }
+ } else {
+ stream_write(pdev->strm, vstr, size);
+ }
+ } else {
+ stream_write(pdev->strm, vstr, size);
+ }
+ } else {
+ const byte *p = vstr;
+ int l = size, n;
+
+ for (;l > 0 ;) {
+ if (*p == '(')
+ n = pdf_encrypt_encoded_string(pdev, p, l, object_id);
+ else {
+ n = pdf_scan_item(pdev, p, l, object_id);
+ stream_write(pdev->strm, p, n);
+ }
+ l -= n;
+ p += n;
+ }
+ }
+ return 0;
+}
+
+/*
+ * Write a string in its shortest form ( () or <> ). Note that
+ * this form is different depending on whether binary data are allowed.
+ * We wish PDF supported ASCII85 strings ( <~ ~> ), but it doesn't.
+ */
+int
+pdf_put_string(const gx_device_pdf * pdev, const byte * str, uint size)
+{
+ psdf_write_string(pdev->strm, str, size,
+ (pdev->binary_ok ? PRINT_BINARY_OK : 0));
+ return 0;
+}
+
+/* Write a value, treating names specially. */
+int
+pdf_write_value(const gx_device_pdf * pdev, const byte * vstr, uint size, gs_id object_id)
+{
+ if (size > 0 && vstr[0] == '/')
+ return pdf_put_name(pdev, vstr + 1, size - 1);
+ else if (size > 5 && vstr[0] == 0 && vstr[1] == 0 && vstr[2] == 0 && vstr[size - 1] == 0 && vstr[size - 2] == 0)
+ return pdf_put_name(pdev, vstr + 4, size - 5);
+ else if (size > 3 && vstr[0] == 0 && vstr[1] == 0 && vstr[size - 1] == 0)
+ return pdf_put_name(pdev, vstr + 3, size - 4);
+ else if (size > 1 && (vstr[0] == '[' || vstr[0] == '{'))
+ return pdf_put_composite(pdev, vstr, size, object_id);
+ else if (size > 2 && vstr[0] == '<' && vstr[1] == '<')
+ return pdf_put_composite(pdev, vstr, size, object_id);
+ else if (size > 1 && vstr[0] == '(') {
+ if (pdev->ForOPDFRead)
+ return pdf_put_encoded_string_as_hex(pdev, vstr, size, object_id);
+ else
+ return pdf_put_encoded_string(pdev, vstr, size, object_id);
+ }
+ else if (size > 1 && vstr[0] == '<')
+ return pdf_put_encoded_hex_string(pdev, vstr, size, object_id);
+ stream_write(pdev->strm, vstr, size);
+ return 0;
+}
+
+/* Store filters for a stream. */
+/* Currently this only saves parameters for CCITTFaxDecode. */
+int
+pdf_put_filters(cos_dict_t *pcd, gx_device_pdf *pdev, stream *s,
+ const pdf_filter_names_t *pfn)
+{
+ const char *filter_name = 0;
+ bool binary_ok = true;
+ stream *fs = s;
+ cos_dict_t *decode_parms = 0;
+ int code;
+
+ for (; fs != 0; fs = fs->strm) {
+ const stream_state *st = fs->state;
+ const stream_template *templat = st->templat;
+
+#define TEMPLATE_IS(atemp)\
+ (templat->process == (atemp).process)
+ if (TEMPLATE_IS(s_A85E_template))
+ binary_ok = false;
+ else if (TEMPLATE_IS(s_CFE_template)) {
+ cos_param_list_writer_t writer;
+ stream_CF_state cfs;
+
+ decode_parms =
+ cos_dict_alloc(pdev, "pdf_put_image_filters(decode_parms)");
+ if (decode_parms == 0)
+ return_error(gs_error_VMerror);
+ CHECK(cos_param_list_writer_init(pdev, &writer, decode_parms, 0));
+ /*
+ * If EndOfBlock is true, we mustn't write a Rows value.
+ * This is a hack....
+ */
+ cfs = *(const stream_CF_state *)st;
+ if (cfs.EndOfBlock)
+ cfs.Rows = 0;
+ CHECK(s_CF_get_params((gs_param_list *)&writer, &cfs, false));
+ filter_name = pfn->CCITTFaxDecode;
+ } else if (TEMPLATE_IS(s_DCTE_template))
+ filter_name = pfn->DCTDecode;
+ else if (TEMPLATE_IS(s_zlibE_template))
+ filter_name = pfn->FlateDecode;
+ else if (TEMPLATE_IS(s_LZWE_template))
+ filter_name = pfn->LZWDecode;
+#ifdef USE_LDF_JB2
+ else if (TEMPLATE_IS(s_jbig2encode_template))
+ filter_name = pfn->JBIG2Decode;
+#endif
+#ifdef USE_LWF_JP2
+ else if (TEMPLATE_IS(s_jpxe_template))
+ filter_name = pfn->JPXDecode;
+#endif
+ else if (TEMPLATE_IS(s_PNGPE_template)) {
+ /* This is a predictor for FlateDecode or LZWEncode. */
+ const stream_PNGP_state *const ss =
+ (const stream_PNGP_state *)st;
+
+ decode_parms =
+ cos_dict_alloc(pdev, "pdf_put_image_filters(decode_parms)");
+ if (decode_parms == 0)
+ return_error(gs_error_VMerror);
+ CHECK(cos_dict_put_c_key_int(decode_parms, "/Predictor",
+ ss->Predictor));
+ CHECK(cos_dict_put_c_key_int(decode_parms, "/Columns",
+ ss->Columns));
+ if (ss->Colors != 1)
+ CHECK(cos_dict_put_c_key_int(decode_parms, "/Colors",
+ ss->Colors));
+ if (ss->BitsPerComponent != 8)
+ CHECK(cos_dict_put_c_key_int(decode_parms,
+ "/BitsPerComponent",
+ ss->BitsPerComponent));
+ } else if (TEMPLATE_IS(s_RLE_template))
+ filter_name = pfn->RunLengthDecode;
+#undef TEMPLATE_IS
+ }
+ if (filter_name) {
+ if (binary_ok) {
+ CHECK(cos_dict_put_c_strings(pcd, pfn->Filter, filter_name));
+ if (decode_parms)
+ CHECK(cos_dict_put_c_key_object(pcd, pfn->DecodeParms,
+ COS_OBJECT(decode_parms)));
+ } else {
+ cos_array_t *pca =
+ cos_array_alloc(pdev, "pdf_put_image_filters(Filters)");
+
+ if (pca == 0)
+ return_error(gs_error_VMerror);
+ CHECK(cos_array_add_c_string(pca, pfn->ASCII85Decode));
+ CHECK(cos_array_add_c_string(pca, filter_name));
+ CHECK(cos_dict_put_c_key_object(pcd, pfn->Filter,
+ COS_OBJECT(pca)));
+ if (decode_parms) {
+ pca = cos_array_alloc(pdev,
+ "pdf_put_image_filters(DecodeParms)");
+ if (pca == 0)
+ return_error(gs_error_VMerror);
+ CHECK(cos_array_add_c_string(pca, "null"));
+ CHECK(cos_array_add_object(pca, COS_OBJECT(decode_parms)));
+ CHECK(cos_dict_put_c_key_object(pcd, pfn->DecodeParms,
+ COS_OBJECT(pca)));
+ }
+ }
+ } else if (!binary_ok)
+ CHECK(cos_dict_put_c_strings(pcd, pfn->Filter, pfn->ASCII85Decode));
+ return 0;
+}
+
+/* Add a Flate compression filter to a binary writer. */
+static int
+pdf_flate_binary(gx_device_pdf *pdev, psdf_binary_writer *pbw)
+{
+ const stream_template *templat = (pdev->CompatibilityLevel < 1.3 ?
+ &s_LZWE_template : &s_zlibE_template);
+ stream_state *st = s_alloc_state(pdev->pdf_memory, templat->stype,
+ "pdf_write_function");
+
+ if (st == 0)
+ return_error(gs_error_VMerror);
+ if (templat->set_defaults)
+ templat->set_defaults(st);
+ return psdf_encode_binary(pbw, templat, st);
+}
+
+/*
+ * Begin a data stream. The client has opened the object and written
+ * the << and any desired dictionary keys.
+ */
+int
+pdf_begin_data(gx_device_pdf *pdev, pdf_data_writer_t *pdw)
+{
+ return pdf_begin_data_stream(pdev, pdw,
+ DATA_STREAM_BINARY | DATA_STREAM_COMPRESS, 0);
+}
+
+int
+pdf_append_data_stream_filters(gx_device_pdf *pdev, pdf_data_writer_t *pdw,
+ int orig_options, gs_id object_id)
+{
+ stream *s = pdev->strm;
+ int options = orig_options;
+#define USE_ASCII85 1
+#define USE_FLATE 2
+ static const char *const fnames[4] = {
+ "", "/Filter/ASCII85Decode", "/Filter/FlateDecode",
+ "/Filter[/ASCII85Decode/FlateDecode]"
+ };
+ static const char *const fnames1_2[4] = {
+ "", "/Filter/ASCII85Decode", "/Filter/LZWDecode",
+ "/Filter[/ASCII85Decode/LZWDecode]"
+ };
+ int filters = 0;
+ int code;
+
+ if (options & DATA_STREAM_COMPRESS) {
+ filters |= USE_FLATE;
+ options |= DATA_STREAM_BINARY;
+ }
+ if ((options & DATA_STREAM_BINARY) && !pdev->binary_ok)
+ filters |= USE_ASCII85;
+ if (!(options & DATA_STREAM_NOLENGTH)) {
+ stream_puts(s, (pdev->CompatibilityLevel < 1.3 ?
+ fnames1_2[filters] : fnames[filters]));
+ if (pdev->ResourcesBeforeUsage) {
+ pdw->length_pos = stell(s) + 8;
+ stream_puts(s, "/Length >>stream\n");
+ pdw->length_id = -1;
+ } else {
+ pdw->length_pos = -1;
+ pdw->length_id = pdf_obj_ref(pdev);
+ pprintld1(s, "/Length %ld 0 R>>stream\n", pdw->length_id);
+ }
+ }
+ if (options & DATA_STREAM_ENCRYPT) {
+ code = pdf_begin_encrypt(pdev, &s, object_id);
+ if (code < 0)
+ return code;
+ pdev->strm = s;
+ pdw->encrypted = true;
+ } else
+ pdw->encrypted = false;
+ if (options & DATA_STREAM_BINARY) {
+ code = psdf_begin_binary((gx_device_psdf *)pdev, &pdw->binary);
+ if (code < 0)
+ return code;
+ } else {
+ code = 0;
+ pdw->binary.target = pdev->strm;
+ pdw->binary.dev = (gx_device_psdf *)pdev;
+ pdw->binary.strm = pdev->strm;
+ }
+ pdw->start = stell(s);
+ if (filters & USE_FLATE)
+ code = pdf_flate_binary(pdev, &pdw->binary);
+ return code;
+#undef USE_ASCII85
+#undef USE_FLATE
+}
+
+int
+pdf_begin_data_stream(gx_device_pdf *pdev, pdf_data_writer_t *pdw,
+ int options, gs_id object_id)
+{ int code;
+ /* object_id is an unused rudiment from the old code,
+ when the encription was applied when creating the stream.
+ The new code encrypts than copying stream from the temporary file. */
+ pdw->pdev = pdev; /* temporary for backward compatibility of pdf_end_data prototype. */
+ pdw->binary.target = pdev->strm;
+ pdw->binary.dev = (gx_device_psdf *)pdev;
+ pdw->binary.strm = 0; /* for GC in case of failure */
+ code = pdf_open_aside(pdev, resourceNone, gs_no_id, &pdw->pres, !object_id,
+ options);
+ if (object_id != 0)
+ pdf_reserve_object_id(pdev, pdw->pres, object_id);
+ pdw->binary.strm = pdev->strm;
+ return code;
+}
+
+/* End a data stream. */
+int
+pdf_end_data(pdf_data_writer_t *pdw)
+{ int code;
+
+ code = pdf_close_aside(pdw->pdev);
+ if (code < 0)
+ return code;
+ code = COS_WRITE_OBJECT(pdw->pres->object, pdw->pdev, resourceNone);
+ if (code < 0)
+ return code;
+ return 0;
+}
+
+/* Create a Function object. */
+static int pdf_function_array(gx_device_pdf *pdev, cos_array_t *pca,
+ const gs_function_info_t *pinfo);
+int
+pdf_function_scaled(gx_device_pdf *pdev, const gs_function_t *pfn,
+ const gs_range_t *pranges, cos_value_t *pvalue)
+{
+ if (pranges == NULL)
+ return pdf_function(pdev, pfn, pvalue);
+ {
+ /*
+ * Create a temporary scaled function. Note that the ranges
+ * represent the inverse scaling from what gs_function_make_scaled
+ * expects.
+ */
+ gs_memory_t *mem = pdev->pdf_memory;
+ gs_function_t *psfn;
+ gs_range_t *ranges = (gs_range_t *)
+ gs_alloc_byte_array(mem, pfn->params.n, sizeof(gs_range_t),
+ "pdf_function_scaled");
+ int i, code;
+
+ if (ranges == 0)
+ return_error(gs_error_VMerror);
+ for (i = 0; i < pfn->params.n; ++i) {
+ double rbase = pranges[i].rmin;
+ double rdiff = pranges[i].rmax - rbase;
+ double invbase = -rbase / rdiff;
+
+ ranges[i].rmin = invbase;
+ ranges[i].rmax = invbase + 1.0 / rdiff;
+ }
+ code = gs_function_make_scaled(pfn, &psfn, ranges, mem);
+ if (code >= 0) {
+ code = pdf_function(pdev, psfn, pvalue);
+ gs_function_free(psfn, true, mem);
+ }
+ gs_free_object(mem, ranges, "pdf_function_scaled");
+ return code;
+ }
+}
+static int
+pdf_function_aux(gx_device_pdf *pdev, const gs_function_t *pfn,
+ pdf_resource_t **ppres)
+{
+ gs_function_info_t info;
+ cos_param_list_writer_t rlist;
+ pdf_resource_t *pres;
+ cos_object_t *pcfn;
+ cos_dict_t *pcd;
+ int code = pdf_alloc_resource(pdev, resourceFunction, gs_no_id, &pres, -1);
+
+ if (code < 0) {
+ *ppres = 0;
+ return code;
+ }
+ *ppres = pres;
+ pcfn = pres->object;
+ gs_function_get_info(pfn, &info);
+ if (FunctionType(pfn) == function_type_ArrayedOutput) {
+ /*
+ * Arrayed Output Functions are used internally to represent
+ * Shading Function entries that are arrays of Functions.
+ * They require special handling.
+ */
+ cos_array_t *pca;
+
+ cos_become(pcfn, cos_type_array);
+ pca = (cos_array_t *)pcfn;
+ return pdf_function_array(pdev, pca, &info);
+ }
+ if (info.DataSource != 0) {
+ psdf_binary_writer writer;
+ stream *save = pdev->strm;
+ cos_stream_t *pcos;
+ stream *s;
+
+ cos_become(pcfn, cos_type_stream);
+ pcos = (cos_stream_t *)pcfn;
+ pcd = cos_stream_dict(pcos);
+ s = cos_write_stream_alloc(pcos, pdev, "pdf_function");
+ if (s == 0)
+ return_error(gs_error_VMerror);
+ pdev->strm = s;
+ code = psdf_begin_binary((gx_device_psdf *)pdev, &writer);
+ if (code >= 0 && info.data_size > 30 /* 30 is arbitrary */
+ )
+ code = pdf_flate_binary(pdev, &writer);
+ if (code >= 0) {
+ static const pdf_filter_names_t fnames = {
+ PDF_FILTER_NAMES
+ };
+
+ code = pdf_put_filters(pcd, pdev, writer.strm, &fnames);
+ }
+ if (code >= 0) {
+ byte buf[100]; /* arbitrary */
+ ulong pos;
+ uint count;
+ const byte *ptr;
+
+ for (pos = 0; pos < info.data_size; pos += count) {
+ count = min(sizeof(buf), info.data_size - pos);
+ data_source_access_only(info.DataSource, pos, count, buf,
+ &ptr);
+ stream_write(writer.strm, ptr, count);
+ }
+ code = psdf_end_binary(&writer);
+ sclose(s);
+ }
+ pdev->strm = save;
+ if (code < 0)
+ return code;
+ } else {
+ cos_become(pcfn, cos_type_dict);
+ pcd = (cos_dict_t *)pcfn;
+ }
+ if (info.Functions != 0) {
+ cos_array_t *functions =
+ cos_array_alloc(pdev, "pdf_function(Functions)");
+ cos_value_t v;
+
+ if (functions == 0)
+ return_error(gs_error_VMerror);
+ if ((code = pdf_function_array(pdev, functions, &info)) < 0 ||
+ (code = cos_dict_put_c_key(pcd, "/Functions",
+ COS_OBJECT_VALUE(&v, functions))) < 0
+ ) {
+ COS_FREE(functions, "pdf_function(Functions)");
+ return code;
+ }
+ }
+ code = cos_param_list_writer_init(pdev, &rlist, pcd, PRINT_BINARY_OK);
+ if (code < 0)
+ return code;
+ return gs_function_get_params(pfn, (gs_param_list *)&rlist);
+}
+static int
+functions_equal(gx_device_pdf * pdev, pdf_resource_t *pres0, pdf_resource_t *pres1)
+{
+ return true;
+}
+int
+pdf_function(gx_device_pdf *pdev, const gs_function_t *pfn, cos_value_t *pvalue)
+{
+ pdf_resource_t *pres;
+ int code = pdf_function_aux(pdev, pfn, &pres);
+
+ if (code < 0)
+ return code;
+ if (pres->object->md5_valid)
+ pres->object->md5_valid = 0;
+
+ code = pdf_substitute_resource(pdev, &pres, resourceFunction, functions_equal, false);
+ if (code < 0)
+ return code;
+ pres->where_used |= pdev->used_mask;
+ COS_OBJECT_VALUE(pvalue, pres->object);
+ return 0;
+}
+static int pdf_function_array(gx_device_pdf *pdev, cos_array_t *pca,
+ const gs_function_info_t *pinfo)
+{
+ int i, code = 0;
+ cos_value_t v;
+
+ for (i = 0; i < pinfo->num_Functions; ++i) {
+ if ((code = pdf_function(pdev, pinfo->Functions[i], &v)) < 0 ||
+ (code = cos_array_add(pca, &v)) < 0
+ ) {
+ break;
+ }
+ }
+ return code;
+}
+
+/* Write a Function object. */
+int
+pdf_write_function(gx_device_pdf *pdev, const gs_function_t *pfn, long *pid)
+{
+ cos_value_t value;
+ int code = pdf_function(pdev, pfn, &value);
+
+ if (code < 0)
+ return code;
+ *pid = value.contents.object->id;
+ return 0;
+}
+
+/* Write a FontBBox dictionary element. */
+int
+pdf_write_font_bbox(gx_device_pdf *pdev, const gs_int_rect *pbox)
+{
+ stream *s = pdev->strm;
+ /*
+ * AR 4 doesn't like fonts with empty FontBBox, which
+ * happens when the font contains only space characters.
+ * Small bbox causes AR 4 to display a hairline. So we use
+ * the full BBox.
+ */
+ int x = pbox->q.x + ((pbox->p.x == pbox->q.x) ? 1000 : 0);
+ int y = pbox->q.y + ((pbox->p.y == pbox->q.y) ? 1000 : 0);
+
+ pprintd4(s, "/FontBBox[%d %d %d %d]",
+ pbox->p.x, pbox->p.y, x, y);
+ return 0;
+}
+
+/* Write a FontBBox dictionary element using floats for the values. */
+int
+pdf_write_font_bbox_float(gx_device_pdf *pdev, const gs_rect *pbox)
+{
+ stream *s = pdev->strm;
+ /*
+ * AR 4 doesn't like fonts with empty FontBBox, which
+ * happens when the font contains only space characters.
+ * Small bbox causes AR 4 to display a hairline. So we use
+ * the full BBox.
+ */
+ float x = pbox->q.x + ((pbox->p.x == pbox->q.x) ? 1000 : 0);
+ float y = pbox->q.y + ((pbox->p.y == pbox->q.y) ? 1000 : 0);
+
+ pprintg4(s, "/FontBBox[%g %g %g %g]",
+ pbox->p.x, pbox->p.y, x, y);
+ return 0;
+}
diff --git a/devices/vector/gdevpdfv.c b/devices/vector/gdevpdfv.c
new file mode 100644
index 000000000..bbe28f397
--- /dev/null
+++ b/devices/vector/gdevpdfv.c
@@ -0,0 +1,1036 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Color value writing for pdfwrite driver */
+#include "math_.h"
+#include "string_.h"
+#include "gx.h"
+#include "gscindex.h"
+#include "gserrors.h"
+#include "gsiparm3.h" /* for pattern colors */
+#include "gsmatrix.h" /* for gspcolor.h */
+#include "gscoord.h" /* for gs_currentmatrix, requires gsmatrix.h */
+#include "gsptype2.h"
+#include "gxcolor2.h" /* for gxpcolor.h */
+#include "gxdcolor.h" /* for gxpcolor.h */
+#include "gxpcolor.h" /* for pattern device color types */
+#include "gxshade.h"
+#include "gdevpdfx.h"
+#include "gdevpdfg.h"
+#include "gdevpdfo.h"
+
+/* Import the PatternType 2 Pattern device color type. */
+extern const gx_device_color_type_t gx_dc_pattern2;
+
+/*
+ * Define the scaling and range of values written for mesh shadings.
+ * BitsPerCoordinate is always 24; BitsPerComponent (for colors) is
+ * always 16.
+ */
+#define ENCODE_VALUE(v, emax, vmin, vmax)\
+ ( ((v) - (vmin)) * ((double)(emax) / ((vmax) - (vmin))) )
+/*
+ * Because of the Acrobat Reader limitation noted in gdevpdfx.h,
+ * we must limit coordinate values to 14 bits.
+ */
+#define MIN_MESH_COORDINATE (-0x400000 / 256.0)
+#define MAX_MESH_COORDINATE ( 0x3fffff / 256.0)
+#define ENCODE_MESH_COORDINATE(v)\
+ ENCODE_VALUE(v, 0xffffff, MIN_MESH_COORDINATE, MAX_MESH_COORDINATE)
+
+#define MIN_MESH_COLOR_INDEX 0
+#define MAX_MESH_COLOR_INDEX 0xffff
+#define ENCODE_MESH_COLOR_INDEX(v) ((v) + MIN_MESH_COLOR_INDEX)
+
+#define ENCODE_MESH_COMPONENT(v, vmin, vmax)\
+ ENCODE_VALUE(v, 0xffff, vmin, vmax)
+
+/* ---------------- Utilities ---------------- */
+
+/* Write a matrix parameter. */
+static int
+cos_dict_put_matrix(gx_device_pdf *pdev, cos_dict_t *pscd, const char *key, const gs_matrix *pmat)
+{
+ float matrix[6];
+
+ matrix[0] = pmat->xx;
+ matrix[1] = pmat->xy;
+ matrix[2] = pmat->yx;
+ matrix[3] = pmat->yy;
+ matrix[4] = pmat->tx;
+ matrix[5] = pmat->ty;
+ return cos_dict_put_c_key_floats(pdev, pscd, key, matrix, 6);
+}
+
+/* ---------------- PatternType 1 colors ---------------- */
+
+/*
+ * Create a Pattern resource referencing an image (currently only an XObject).
+ * p_tile is NULL for uncolored patterns or the NULL pattern.
+ * m_tile is NULL for colored patterns that fill their bounding box,
+ * including the NULL pattern.
+ ****** WE DON'T HANDLE NULL PATTERNS YET ******
+ */
+static uint
+tile_size(const gx_strip_bitmap *tile, int depth)
+{
+ return (tile->rep_width * depth + 7) / 8 * tile->rep_height;
+}
+static bool
+tile_size_ok(const gx_device_pdf *pdev, const gx_color_tile *p_tile,
+ const gx_color_tile *m_tile)
+{
+ /*
+ * Acrobat Reader can't handle image Patterns with more than
+ * 64K of data. :-(
+ */
+ uint p_size =
+ (p_tile == 0 ? 0 : tile_size(&p_tile->tbits, p_tile->depth));
+ uint m_size =
+ (m_tile == 0 ? 0 : tile_size(&m_tile->tmask, 1));
+ /* The image limit only applies to Acrobat versions less than 5
+ * (PDF 1.4).
+ */
+ if (pdev->CompatibilityLevel < 1.4)
+ return (max(p_size, m_size) <= 65500);
+ else
+ return 1;
+}
+
+static int
+pdf_pattern(gx_device_pdf *pdev, const gx_drawing_color *pdc,
+ const gx_color_tile *p_tile, const gx_color_tile *m_tile,
+ cos_stream_t *pcs_image, pdf_resource_t **ppres)
+{
+ pdf_resource_t *pres;
+ int code = pdf_alloc_resource(pdev, resourcePattern, pdc->mask.id, ppres,
+ 0L);
+ cos_stream_t *pcos;
+ cos_dict_t *pcd;
+ cos_dict_t *pcd_Resources = cos_dict_alloc(pdev, "pdf_pattern(Resources)");
+ const gx_color_tile *tile = (p_tile ? p_tile : m_tile);
+ const gx_strip_bitmap *btile = (p_tile ? &p_tile->tbits : &m_tile->tmask);
+ bool mask = p_tile == 0;
+ gs_point step;
+ gs_matrix smat;
+
+ if (code < 0)
+ return code;
+ if (!tile_size_ok(pdev, p_tile, m_tile))
+ return_error(gs_error_limitcheck);
+ /*
+ * We currently can't handle Patterns whose X/Y step isn't parallel
+ * to the coordinate axes.
+ */
+ if (is_xxyy(&tile->step_matrix))
+ step.x = tile->step_matrix.xx, step.y = tile->step_matrix.yy;
+ else if (is_xyyx(&tile->step_matrix))
+ step.x = tile->step_matrix.yx, step.y = tile->step_matrix.xy;
+ else
+ return_error(gs_error_rangecheck);
+ if (pcd_Resources == 0)
+ return_error(gs_error_VMerror);
+ gs_make_identity(&smat);
+ smat.xx = btile->rep_width / (pdev->HWResolution[0] / 72.0);
+ smat.yy = btile->rep_height / (pdev->HWResolution[1] / 72.0);
+ smat.tx = tile->step_matrix.tx / (pdev->HWResolution[0] / 72.0);
+ smat.ty = tile->step_matrix.ty / (pdev->HWResolution[1] / 72.0);
+ pres = *ppres;
+ {
+ cos_dict_t *pcd_XObject = cos_dict_alloc(pdev, "pdf_pattern(XObject)");
+ char key[MAX_REF_CHARS + 3];
+ cos_value_t v;
+ cos_object_t *object;
+
+ if (pcd_XObject == 0)
+ return_error(gs_error_VMerror);
+ gs_sprintf(key, "/R%ld", pcs_image->id);
+ /* This is non-obvious code. Previously we would put the image object (pcs_image)
+ * into the Resources dit. When we come to write out the Resources dict
+ * that code writes a reference (index 0 R) using the ID from the object.
+ * However that means we have two pointers to the XObject. One in the chain
+ * of resoruces (which we need in order to write teh XObject) and one from
+ * the pattern here. That seriously messes up memory handling. So instead
+ * we now make a new object, and copy the id from the pcs_image. Since that's
+ * all that the writing code will use, we cna avoid the double pointers.
+ */
+ object = cos_reference_alloc(pdev, "pdf_pattern(reference copy of XObject)");
+ object->id = pcs_image->id;
+ COS_OBJECT_VALUE(&v, object);
+ if ((code = cos_dict_put(pcd_XObject, (byte *)key, strlen(key), &v)) < 0 ||
+ (code = cos_dict_put_c_key_object(pcd_Resources, "/XObject",
+ COS_OBJECT(pcd_XObject))) < 0
+ )
+ return code;
+ }
+ if ((code = cos_dict_put_c_strings(pcd_Resources, "/ProcSet",
+ (mask ? "[/PDF/ImageB]" :
+ "[/PDF/ImageC]"))) < 0)
+ return code;
+ cos_become(pres->object, cos_type_stream);
+ pcos = (cos_stream_t *)pres->object;
+ pcd = cos_stream_dict(pcos);
+ if ((code = cos_dict_put_c_key_int(pcd, "/PatternType", 1)) < 0 ||
+ (code = cos_dict_put_c_key_int(pcd, "/PaintType",
+ (mask ? 2 : 1))) < 0 ||
+ (code = cos_dict_put_c_key_int(pcd, "/TilingType",
+ tile->tiling_type)) < 0 ||
+ (code = cos_dict_put_c_key_object(pcd, "/Resources",
+ COS_OBJECT(pcd_Resources))) < 0 ||
+ (code = cos_dict_put_c_strings(pcd, "/BBox", "[0 0 1 1]")) < 0 ||
+ (code = cos_dict_put_matrix(pdev, pcd, "/Matrix", &smat)) < 0 ||
+ (code = cos_dict_put_c_key_real(pcd, "/XStep", step.x / btile->rep_width)) < 0 ||
+ (code = cos_dict_put_c_key_real(pcd, "/YStep", step.y / btile->rep_height)) < 0
+ ) {
+ return code;
+ }
+
+ {
+ char buf[MAX_REF_CHARS + 6 + 1]; /* +6 for /R# Do\n */
+
+ gs_sprintf(buf, "/R%ld Do\n", pcs_image->id);
+ cos_stream_add_bytes(pdev, pcos, (const byte *)buf, strlen(buf));
+ }
+
+ return 0;
+}
+
+/* Store pattern 1 parameters to cos dictionary. */
+int
+pdf_store_pattern1_params(gx_device_pdf *pdev, pdf_resource_t *pres,
+ gs_pattern1_instance_t *pinst)
+{
+ gs_pattern1_template_t *t = &pinst->templat;
+ gs_matrix smat2 = ctm_only((gs_imager_state *)pinst->saved), smat;
+ double scale_x = pdev->HWResolution[0] / 72.0;
+ double scale_y = pdev->HWResolution[1] / 72.0;
+ cos_dict_t *pcd = cos_stream_dict((cos_stream_t *)pres->object);
+ cos_dict_t *pcd_Resources = cos_dict_alloc(pdev, "pdf_pattern(Resources)");
+ float bbox[4];
+ int code;
+
+ if (pcd == NULL || pcd_Resources == NULL)
+ return_error(gs_error_VMerror);
+ pdev->substream_Resources = pcd_Resources;
+ bbox[0] = t->BBox.p.x;
+ bbox[1] = t->BBox.p.y;
+ bbox[2] = t->BBox.q.x;
+ bbox[3] = t->BBox.q.y;
+ /* The graphics library assumes a shifted origin to provide
+ positive bitmap pixel indices. Compensate it now. */
+ smat2.tx += pinst->step_matrix.tx;
+ smat2.ty += pinst->step_matrix.ty;
+ /*
+ * In PDF, the Matrix is the transformation from the pattern space to
+ * the *default* user coordinate space, not the current space.
+ * NB. For a form the default space is the parent. This means that when a
+ * form is nested inside a form, the default space is the space of the
+ * first form, and therefore we do *not* remove the resolution scaling.
+ */
+ if (pdev->FormDepth == 0) {
+ gs_matrix scaled;
+
+ gs_make_scaling(1 / scale_x, 1 / scale_y, &scaled);
+ gs_matrix_multiply(&smat2, &scaled, &smat);
+ } else {
+ smat = smat2;
+ }
+ if (pdev->ForOPDFRead) {
+ if (pdev->PatternDepth) {
+ gs_matrix_multiply(&smat, &pdev->AccumulatedPatternMatrix, &smat2);
+ gs_matrix_multiply(&pdev->AccumulatedPatternMatrix, &smat, &pdev->AccumulatedPatternMatrix);
+ smat = smat2;
+ } else {
+ gs_make_identity(&pdev->AccumulatedPatternMatrix);
+ gs_matrix_multiply(&pdev->AccumulatedPatternMatrix, &smat, &pdev->AccumulatedPatternMatrix);
+ }
+ }
+ if (any_abs(smat.tx) < 0.0001) /* Noise. */
+ smat.tx = 0;
+ if (any_abs(smat.ty) < 0.0001)
+ smat.ty = 0;
+ code = cos_dict_put_c_strings(pcd, "/Type", "/Pattern");
+ if (code >= 0)
+ code = cos_dict_put_c_key_int(pcd, "/PatternType", 1);
+ if (code >= 0)
+ code = cos_dict_put_c_key_int(pcd, "/PaintType", t->PaintType);
+ if (code >= 0)
+ code = cos_dict_put_c_key_int(pcd, "/TilingType", t->TilingType);
+ if (code >= 0)
+ code = cos_dict_put_c_key_floats(pdev, pcd, "/BBox", bbox, 4);
+ if (code >= 0)
+ code = cos_dict_put_matrix(pdev, pcd, "/Matrix", &smat);
+ if (code >= 0)
+ code = cos_dict_put_c_key_real(pcd, "/XStep", t->XStep);
+ if (code >= 0)
+ code = cos_dict_put_c_key_real(pcd, "/YStep", t->YStep);
+ if (code >= 0)
+ code = cos_dict_put_c_key_object(pcd, "/Resources", COS_OBJECT(pcd_Resources));
+ pdev->skip_colors = (t->PaintType == 2);
+ return code;
+}
+
+/* Set the ImageMatrix, Width, and Height for a Pattern image. */
+static void
+pdf_set_pattern_image(gs_data_image_t *pic, const gx_strip_bitmap *tile)
+{
+ pic->ImageMatrix.xx = (float)(pic->Width = tile->rep_width);
+ pic->ImageMatrix.yy = (float)(pic->Height = tile->rep_height);
+}
+
+/* Write the mask for a Pattern (colored or uncolored). */
+static int
+pdf_put_pattern_mask(gx_device_pdf *pdev, const gx_color_tile *m_tile,
+ cos_stream_t **ppcs_mask)
+{
+ int w = m_tile->tmask.rep_width, h = m_tile->tmask.rep_height;
+ gs_image1_t image;
+ pdf_image_writer writer;
+ int code;
+
+ gs_image_t_init_mask_adjust(&image, true, false);
+ pdf_set_pattern_image((gs_data_image_t *)&image, &m_tile->tmask);
+ pdf_image_writer_init(&writer);
+ if ((code = pdf_begin_write_image(pdev, &writer, gs_no_id, w, h, NULL, false)) < 0 ||
+ (pdev->params.MonoImage.Encode &&
+ (code = psdf_CFE_binary(&writer.binary[0], w, h, true)) < 0) ||
+ (code = pdf_begin_image_data(pdev, &writer, (const gs_pixel_image_t *)&image, NULL, 0)) < 0
+ )
+ return code;
+ /* Pattern masks are specified in device coordinates, so invert Y. */
+ if ((code = pdf_copy_mask_bits(writer.binary[0].strm, m_tile->tmask.data + (h - 1) * m_tile->tmask.raster, 0, -m_tile->tmask.raster, w, h, 0)) < 0 ||
+ (code = pdf_end_image_binary(pdev, &writer, h)) < 0 ||
+ (code = pdf_end_write_image(pdev, &writer)) < 0
+ )
+ return code;
+ *ppcs_mask = (cos_stream_t *)writer.pres->object;
+ return 0;
+}
+
+/* Write an uncolored Pattern color. */
+int
+pdf_put_uncolored_pattern(gx_device_pdf *pdev, const gx_drawing_color *pdc,
+ const gs_color_space *pcs,
+ const psdf_set_color_commands_t *ppscc,
+ const gs_imager_state * pis, pdf_resource_t **ppres)
+{
+ const gx_color_tile *m_tile = pdc->mask.m_tile;
+ gx_drawing_color dc_pure;
+
+ if (!pis->have_pattern_streams && m_tile == 0) {
+ /*
+ * If m_tile == 0, this uncolored Pattern is all 1's,
+ * equivalent to a pure color.
+ */
+ *ppres = 0;
+ set_nonclient_dev_color(&dc_pure, gx_dc_pure_color(pdc));
+ return psdf_set_color((gx_device_vector *)pdev, &dc_pure, ppscc, pdev->UseOldColor);
+ } else {
+ cos_value_t v;
+ stream *s = pdev->strm;
+ int code;
+ cos_stream_t *pcs_image;
+ static const psdf_set_color_commands_t no_scc = {0, 0, 0};
+
+ if (!tile_size_ok(pdev, NULL, m_tile))
+ return_error(gs_error_limitcheck);
+ if (!pis->have_pattern_streams) {
+ if ((code = pdf_cs_Pattern_uncolored(pdev, &v)) < 0 ||
+ (code = pdf_put_pattern_mask(pdev, m_tile, &pcs_image)) < 0 ||
+ (code = pdf_pattern(pdev, pdc, NULL, m_tile, pcs_image, ppres)) < 0
+ )
+ return code;
+ } else {
+ code = pdf_cs_Pattern_uncolored_hl(pdev, pcs, &v, pis);
+ if (code < 0)
+ return code;
+ *ppres = pdf_find_resource_by_gs_id(pdev, resourcePattern, pdc->mask.id);
+ *ppres = pdf_substitute_pattern(*ppres);
+ if (!pdev->AR4_save_bug && pdev->CompatibilityLevel <= 1.3) {
+ /* We reconnized AR4 behavior as reserving "q Q" stack elements
+ * on demand. It looks as processing a pattern stream
+ * with PaintType 1 AR4 replaces the topmost stack element
+ * instead allocating a new one, if it was not previousely allocated.
+ * AR 5 doesn't have this bug. Working around the AR4 bug here.
+ */
+ stream_puts(pdev->strm, "q q Q Q\n");
+ pdev->AR4_save_bug = true;
+ }
+ (*ppres)->where_used |= pdev->used_mask;
+ }
+ cos_value_write(&v, pdev);
+ pprints1(s, " %s ", ppscc->setcolorspace);
+ if (pis->have_pattern_streams)
+ return 0;
+ set_nonclient_dev_color(&dc_pure, gx_dc_pure_color(pdc));
+ return psdf_set_color((gx_device_vector *)pdev, &dc_pure, &no_scc, pdev->UseOldColor);
+ }
+}
+
+int
+pdf_put_colored_pattern(gx_device_pdf *pdev, const gx_drawing_color *pdc,
+ const gs_color_space *pcs,
+ const psdf_set_color_commands_t *ppscc,
+ const gs_imager_state * pis, pdf_resource_t **ppres)
+{
+ const gx_color_tile *p_tile = pdc->colors.pattern.p_tile;
+ gs_color_space *pcs_Device;
+ cos_value_t cs_value;
+ cos_value_t v;
+ int code;
+ gs_image1_t image;
+ const gx_color_tile *m_tile = NULL;
+ pdf_image_writer writer;
+ int w = 0, h = 0;
+
+ if (p_tile) {
+ w = p_tile->tbits.rep_width;
+ h = p_tile->tbits.rep_height;
+ }
+
+ if (!pis->have_pattern_streams) {
+ /*
+ * NOTE: We assume here that the color space of the cached Pattern
+ * is the same as the native color space of the device. This will
+ * have to change in the future!
+ */
+ /*
+ * Check whether this colored pattern is actually a masked pure color,
+ * by testing whether all the colored pixels have the same color.
+ */
+ m_tile = pdc->mask.m_tile;
+ if (m_tile) {
+ if (p_tile && !(p_tile->depth & 7) && p_tile->depth <= arch_sizeof_color_index * 8) {
+ int depth_bytes = p_tile->depth >> 3;
+ int width = p_tile->tbits.rep_width;
+ int skip = p_tile->tbits.raster -
+ p_tile->tbits.rep_width * depth_bytes;
+ const byte *bp;
+ const byte *mp;
+ int i, j, k;
+ gx_color_index color = 0; /* init is arbitrary if not empty */
+ bool first = true;
+
+ for (i = 0, bp = p_tile->tbits.data, mp = p_tile->tmask.data;
+ i < p_tile->tbits.rep_height;
+ ++i, bp += skip, mp += p_tile->tmask.raster) {
+
+ for (j = 0; j < width; ++j) {
+ if (mp[j >> 3] & (0x80 >> (j & 7))) {
+ gx_color_index ci = 0;
+
+ for (k = 0; k < depth_bytes; ++k)
+ ci = (ci << 8) + *bp++;
+ if (first)
+ color = ci, first = false;
+ else if (ci != color)
+ goto not_pure;
+ } else
+ bp += depth_bytes;
+ }
+ }
+ {
+ /* Set the color, then handle as an uncolored pattern. */
+ gx_drawing_color dcolor;
+
+ dcolor = *pdc;
+ dcolor.colors.pure = color;
+ return pdf_put_uncolored_pattern(pdev, &dcolor, pcs, ppscc,
+ pis, ppres);
+ }
+ not_pure:
+ DO_NOTHING; /* required by MSVC */
+ }
+ if (pdev->CompatibilityLevel < 1.3) {
+ /* Masked images are only supported starting in PDF 1.3. */
+ return_error(gs_error_rangecheck);
+ }
+ }
+ /* Acrobat Reader has a size limit for image Patterns. */
+ if (!tile_size_ok(pdev, p_tile, m_tile))
+ return_error(gs_error_limitcheck);
+ }
+ code = pdf_cs_Pattern_colored(pdev, &v);
+ if (code < 0)
+ return code;
+ pdf_cspace_init_Device(pdev->memory, &pcs_Device, pdev->color_info.num_components);
+ /*
+ * We don't have to worry about color space scaling: the color
+ * space is always a Device space.
+ */
+ code = pdf_color_space_named(pdev, NULL, &cs_value, NULL, pcs_Device,
+ &pdf_color_space_names, true, NULL, 0, false);
+ if (code < 0)
+ return code;
+ if (!pis->have_pattern_streams) {
+ cos_stream_t *pcs_mask = 0;
+ cos_stream_t *pcs_image;
+
+ gs_image_t_init_adjust(&image, pcs_Device, false);
+ image.BitsPerComponent = 8;
+ pdf_set_pattern_image((gs_data_image_t *)&image, &p_tile->tbits);
+ if (m_tile) {
+ if ((code = pdf_put_pattern_mask(pdev, m_tile, &pcs_mask)) < 0)
+ return code;
+ }
+ pdf_image_writer_init(&writer);
+ pdev->ParamCompatibilityLevel = pdev->CompatibilityLevel;
+ if ((code = pdf_begin_write_image(pdev, &writer, gs_no_id, w, h, NULL, false)) < 0 ||
+ (code = psdf_setup_lossless_filters((gx_device_psdf *)pdev,
+ &writer.binary[0],
+ (gs_pixel_image_t *)&image, false)) < 0 ||
+ (code = pdf_begin_image_data(pdev, &writer, (const gs_pixel_image_t *)&image, &cs_value, 0)) < 0
+ )
+ return code;
+ /* Pattern masks are specified in device coordinates, so invert Y. */
+ if ((code = pdf_copy_color_bits(writer.binary[0].strm, p_tile->tbits.data + (h - 1) * p_tile->tbits.raster, 0, -p_tile->tbits.raster, w, h, pdev->color_info.depth >> 3)) < 0 ||
+ (code = pdf_end_image_binary(pdev, &writer, h)) < 0
+ )
+ return code;
+ pcs_image = (cos_stream_t *)writer.pres->object;
+ if ((pcs_mask != 0 &&
+ (code = cos_dict_put_c_key_object(cos_stream_dict(pcs_image), "/Mask",
+ COS_OBJECT(pcs_mask))) < 0) ||
+ (code = pdf_end_write_image(pdev, &writer)) < 0
+ )
+ return code;
+ pcs_image = (cos_stream_t *)writer.pres->object; /* pdf_end_write_image may change it. */
+ code = pdf_pattern(pdev, pdc, p_tile, m_tile, pcs_image, ppres);
+ if (code < 0)
+ return code;
+ } else {
+ *ppres = pdf_find_resource_by_gs_id(pdev, resourcePattern, p_tile->id);
+ *ppres = pdf_substitute_pattern(*ppres);
+ (*ppres)->where_used |= pdev->used_mask;
+ }
+ /* pcs_Device will leak (picked up by GC in PS) on error, but we'll
+ tolerate that for now. */
+ rc_decrement_cs(pcs_Device, "pdf_put_colored_pattern");
+ cos_value_write(&v, pdev);
+ pprints1(pdev->strm, " %s", ppscc->setcolorspace);
+ return 0;
+}
+
+/* ---------------- PatternType 2 colors ---------------- */
+
+/* Write parameters common to all Shadings. */
+static int
+pdf_put_shading_common(gx_device_pdf *pdev, cos_dict_t *pscd, const gs_imager_state * pis, const gs_shading_t *psh,
+ bool shfill, const gs_range_t **ppranges)
+{
+ gs_shading_type_t type = ShadingType(psh);
+ const gs_color_space *pcs = psh->params.ColorSpace;
+ int code = cos_dict_put_c_key_int(pscd, "/ShadingType", (int)type);
+ cos_value_t cs_value;
+
+ if (code < 0 ||
+ (psh->params.AntiAlias &&
+ (code = cos_dict_put_c_strings(pscd, "/AntiAlias", "true")) < 0) ||
+ (code = pdf_color_space_named(pdev, pis, &cs_value, ppranges, pcs,
+ &pdf_color_space_names, false, NULL, 0, false)) < 0 ||
+ (code = cos_dict_put_c_key(pscd, "/ColorSpace", &cs_value)) < 0
+ )
+ return code;
+ if (psh->params.Background && !shfill) {
+ /****** SCALE Background ******/
+ code = cos_dict_put_c_key_floats(pdev, pscd, "/Background",
+ psh->params.Background->paint.values,
+ gs_color_space_num_components(pcs));
+ if (code < 0)
+ return code;
+ }
+ if (psh->params.have_BBox) {
+ float bbox[4];
+
+ bbox[0] = psh->params.BBox.p.x;
+ bbox[1] = psh->params.BBox.p.y;
+ bbox[2] = psh->params.BBox.q.x;
+ bbox[3] = psh->params.BBox.q.y;
+ code = cos_dict_put_c_key_floats(pdev, pscd, "/BBox", bbox, 4);
+ if (code < 0)
+ return code;
+ }
+ return 0;
+}
+
+/* Write an optional Function parameter. */
+static int
+pdf_put_shading_Function(gx_device_pdf *pdev, cos_dict_t *pscd, const gs_function_t *pfn,
+ const gs_range_t *pranges)
+{
+ int code = 0;
+
+ if (pfn != 0) {
+ cos_value_t fn_value;
+
+ if ((code = pdf_function_scaled(pdev, pfn, pranges, &fn_value)) >= 0)
+ code = cos_dict_put_c_key(pscd, "/Function", &fn_value);
+ }
+ return code;
+}
+
+/* Write a linear (Axial / Radial) Shading. */
+static int
+pdf_put_linear_shading(gx_device_pdf *pdev, cos_dict_t *pscd, const float *Coords,
+ int num_coords, const float *Domain /*[2]*/,
+ const gs_function_t *Function,
+ const bool *Extend /*[2]*/,
+ const gs_range_t *pranges)
+{
+ int code = cos_dict_put_c_key_floats(pdev, pscd, "/Coords", Coords, num_coords);
+
+ if (code < 0 ||
+ ((Domain[0] != 0 || Domain[1] != 1) &&
+ (code = cos_dict_put_c_key_floats(pdev, pscd, "/Domain", Domain, 2)) < 0) ||
+ (code = pdf_put_shading_Function(pdev, pscd, Function, pranges)) < 0
+ )
+ return code;
+ if (Extend[0] | Extend[1]) {
+ char extend_str[1 + 5 + 1 + 5 + 1 + 1]; /* [bool bool] */
+
+ gs_sprintf(extend_str, "[%s %s]",
+ (Extend[0] ? "true" : "false"),
+ (Extend[1] ? "true" : "false"));
+ code = cos_dict_put_c_key_string(pscd, "/Extend",
+ (const byte *)extend_str,
+ strlen(extend_str));
+ }
+ return code;
+}
+
+/* Write a scalar (non-mesh) Shading. */
+/* (Single-use procedure for readability.) */
+static int
+pdf_put_scalar_shading(gx_device_pdf *pdev, cos_dict_t *pscd, const gs_shading_t *psh,
+ const gs_range_t *pranges)
+{
+ int code;
+
+ switch (ShadingType(psh)) {
+ case shading_type_Function_based: {
+ const gs_shading_Fb_params_t *const params =
+ (const gs_shading_Fb_params_t *)&psh->params;
+
+ if ((code = cos_dict_put_c_key_floats(pdev, pscd, "/Domain", params->Domain, 4)) < 0 ||
+ (code = pdf_put_shading_Function(pdev, pscd, params->Function, pranges)) < 0 ||
+ (code = cos_dict_put_matrix(pdev, pscd, "/Matrix", &params->Matrix)) < 0
+ )
+ return code;
+ return 0;
+ }
+ case shading_type_Axial: {
+ const gs_shading_A_params_t *const params =
+ (const gs_shading_A_params_t *)&psh->params;
+
+ return pdf_put_linear_shading(pdev, pscd, params->Coords, 4,
+ params->Domain, params->Function,
+ params->Extend, pranges);
+ }
+ case shading_type_Radial: {
+ const gs_shading_R_params_t *const params =
+ (const gs_shading_R_params_t *)&psh->params;
+
+ return pdf_put_linear_shading(pdev, pscd, params->Coords, 6,
+ params->Domain, params->Function,
+ params->Extend, pranges);
+ }
+ default:
+ return_error(gs_error_rangecheck);
+ }
+}
+
+/* Add a floating point range to an array. */
+static int
+pdf_array_add_real2(cos_array_t *pca, double lower, double upper)
+{
+ int code = cos_array_add_real(pca, lower);
+
+ if (code >= 0)
+ code = cos_array_add_real(pca, upper);
+ return code;
+}
+
+/* Define a parameter structure for mesh data. */
+typedef struct pdf_mesh_data_params_s {
+ int num_points;
+ int num_components;
+ bool is_indexed;
+ const float *Domain; /* iff Function */
+ const gs_range_t *ranges;
+} pdf_mesh_data_params_t;
+
+/* Put a clamped value into a data stream. num_bytes < sizeof(int). */
+static void
+put_clamped(byte *p, double v, int num_bytes)
+{
+ int limit = 1 << (num_bytes * 8);
+ int i, shift;
+
+ if (v <= -limit)
+ i = -limit + 1;
+ else if (v >= limit)
+ i = limit - 1;
+ else
+ i = (int)v;
+ for (shift = (num_bytes - 1) * 8; shift >= 0; shift -= 8)
+ *p++ = (byte)(i >> shift);
+}
+static inline void
+put_clamped_coord(byte *p, double v, int num_bytes)
+{
+ put_clamped(p, ENCODE_MESH_COORDINATE(v), num_bytes);
+}
+
+/* Convert floating-point mesh data to packed binary. */
+/* BitsPerFlag = 8, BitsPerCoordinate = 24, BitsPerComponent = 16, */
+/* scaling is as defined below. */
+static int
+put_float_mesh_data(gx_device_pdf *pdev, cos_stream_t *pscs, shade_coord_stream_t *cs,
+ int flag, const pdf_mesh_data_params_t *pmdp)
+{
+ int num_points = pmdp->num_points;
+ byte b[1 + (3 + 3) * 16]; /* flag + x + y or c */
+ gs_fixed_point pts[16];
+ const float *domain = pmdp->Domain;
+ const gs_range_t *pranges = pmdp->ranges;
+ int i, code;
+
+ b[0] = (byte)flag; /* may be -1 */
+ if ((code = shade_next_coords(cs, pts, num_points)) < 0)
+ return code;
+ for (i = 0; i < num_points; ++i) {
+ put_clamped_coord(b + 1 + i * 6, fixed2float(pts[i].x), 3);
+ put_clamped_coord(b + 4 + i * 6, fixed2float(pts[i].y), 3);
+ }
+ if ((code = cos_stream_add_bytes(pdev, pscs, b + (flag < 0),
+ (flag >= 0) + num_points * 6)) < 0)
+ return code;
+ for (i = 0; i < pmdp->num_components; ++i) {
+ float c;
+ double v;
+
+ cs->get_decoded(cs, 0, NULL, &c);
+ if (pmdp->is_indexed)
+ v = ENCODE_MESH_COLOR_INDEX(c);
+ else {
+ /*
+ * We don't rescale stream data values, only the Decode ranges.
+ * (We do have to rescale data values from an array, unless
+ * they are the input parameter for a Function.)
+ * This makes everything come out as it should.
+ */
+ double vmin, vmax;
+
+ if (domain)
+ vmin = domain[2 * i], vmax = domain[2 * i + 1];
+ else
+ vmin = 0.0, vmax = 1.0;
+ if (pranges) {
+ double base = pranges[i].rmin, factor = pranges[i].rmax - base;
+
+ vmin = vmin * factor + base;
+ vmax = vmax * factor + base;
+ }
+ v = ENCODE_MESH_COMPONENT(c, vmin, vmax);
+ }
+ put_clamped(b, v, 2);
+ if ((code = cos_stream_add_bytes(pdev, pscs, b, 2)) < 0)
+ return code;
+ }
+ return 0;
+}
+
+/* Write a mesh Shading. */
+static int
+pdf_put_mesh_shading(gx_device_pdf *pdev, cos_stream_t *pscs, const gs_shading_t *psh,
+ const gs_range_t *pranges)
+{
+ cos_dict_t *const pscd = cos_stream_dict(pscs);
+ gs_color_space *pcs = psh->params.ColorSpace;
+ const gs_shading_mesh_params_t *const pmp =
+ (const gs_shading_mesh_params_t *)&psh->params;
+ int code, code1;
+ int bits_per_coordinate, bits_per_component, bits_per_flag;
+ int num_comp;
+ bool from_array = data_source_is_array(pmp->DataSource);
+ pdf_mesh_data_params_t data_params;
+ shade_coord_stream_t cs;
+ gs_matrix_fixed ctm_ident;
+ int flag;
+
+ if (pmp->Function) {
+ data_params.Domain = 0;
+ num_comp = 1;
+ } else {
+ data_params.Domain = (pmp->Decode != 0 ? pmp->Decode + 4 : NULL);
+ num_comp = gs_color_space_num_components(pcs);
+ }
+ data_params.ranges = pranges;
+
+ /* Write parameters common to all mesh Shadings. */
+ shade_next_init(&cs, pmp, NULL);
+ if (from_array) {
+ cos_array_t *pca = cos_array_alloc(pdev, "pdf_put_mesh_shading");
+ int i;
+
+ if (pca == 0)
+ return_error(gs_error_VMerror);
+ for (i = 0; i < 2; ++i)
+ if ((code = pdf_array_add_real2(pca, MIN_MESH_COORDINATE,
+ MAX_MESH_COORDINATE)) < 0)
+ return code;
+ data_params.is_indexed = false;
+ if (gs_color_space_get_index(pcs) == gs_color_space_index_Indexed) {
+ data_params.is_indexed = true;
+ if ((code = pdf_array_add_real2(pca, MIN_MESH_COLOR_INDEX,
+ MAX_MESH_COLOR_INDEX)) < 0)
+ return code;
+ } else {
+ for (i = 0; i < num_comp; ++i) {
+ double rmin, rmax;
+
+ if (pmp->Function || pranges || data_params.Domain == 0) {
+ if (pmp->Function && pmp->Function->params.Domain != 0) {
+ rmin = pmp->Function->params.Domain[0], rmax = pmp->Function->params.Domain[1];
+ } else {
+ rmin = 0.0, rmax = 1.0;
+ }
+ }
+ else
+ rmin = data_params.Domain[2 * i],
+ rmax = data_params.Domain[2 * i + 1];
+ if ((code =
+ pdf_array_add_real2(pca, rmin, rmax)) < 0)
+ return code;
+ }
+ }
+ code = cos_dict_put_c_key_object(pscd, "/Decode", COS_OBJECT(pca));
+ if (code < 0)
+ return code;
+ bits_per_coordinate = 24;
+ bits_per_component = 16;
+ bits_per_flag = 8;
+ gs_make_identity((gs_matrix *)&ctm_ident);
+ ctm_ident.tx_fixed = ctm_ident.ty_fixed = 0;
+ ctm_ident.txy_fixed_valid = true;
+ cs.pctm = &ctm_ident;
+ if (pmp->Function)
+ data_params.ranges = 0; /* don't scale function parameter */
+ } else {
+ /****** SCALE Decode ******/
+ code = cos_dict_put_c_key_floats(pdev, pscd, "/Decode", pmp->Decode,
+ 4 + num_comp * 2);
+ if (code >= 0)
+ code = cos_stream_add_stream_contents(pdev, pscs, cs.s);
+ bits_per_coordinate = pmp->BitsPerCoordinate;
+ bits_per_component = pmp->BitsPerComponent;
+ bits_per_flag = -1;
+ }
+ if (code < 0 ||
+ (code = pdf_put_shading_Function(pdev, pscd, pmp->Function, pranges)) < 0 ||
+ (code = cos_dict_put_c_key_int(pscd, "/BitsPerCoordinate",
+ bits_per_coordinate)) < 0 ||
+ (code = cos_dict_put_c_key_int(pscd, "/BitsPerComponent",
+ bits_per_component)) < 0
+ )
+ return code;
+
+ switch (ShadingType(psh)) {
+ case shading_type_Free_form_Gouraud_triangle: {
+ const gs_shading_FfGt_params_t *const params =
+ (const gs_shading_FfGt_params_t *)pmp;
+
+ data_params.num_points = 1;
+ data_params.num_components = num_comp;
+ if (from_array) {
+ while ((flag = shade_next_flag(&cs, 0)) >= 0)
+ if ((code = put_float_mesh_data(pdev, pscs, &cs, flag,
+ &data_params)) < 0)
+ return code;
+ if (!seofp(cs.s))
+ code = gs_note_error(gs_error_rangecheck);
+ }
+ if (bits_per_flag < 0)
+ bits_per_flag = params->BitsPerFlag;
+ break;
+ }
+ case shading_type_Lattice_form_Gouraud_triangle: {
+ const gs_shading_LfGt_params_t *const params =
+ (const gs_shading_LfGt_params_t *)pmp;
+
+ data_params.num_points = 1;
+ data_params.num_components = num_comp;
+ if (from_array)
+ while (!seofp(cs.s))
+ if ((code = put_float_mesh_data(pdev, pscs, &cs, -1,
+ &data_params)) < 0)
+ return code;
+ code = cos_dict_put_c_key_int(pscd, "/VerticesPerRow",
+ params->VerticesPerRow);
+ return code;
+ }
+ case shading_type_Coons_patch: {
+ const gs_shading_Cp_params_t *const params =
+ (const gs_shading_Cp_params_t *)pmp;
+
+ if (from_array) {
+ while ((flag = shade_next_flag(&cs, 0)) >= 0) {
+ data_params.num_points = (flag == 0 ? 12 : 8);
+ data_params.num_components = num_comp * (flag == 0 ? 4 : 2);
+ if ((code = put_float_mesh_data(pdev, pscs, &cs, flag,
+ &data_params)) < 0)
+ return code;
+ }
+ if (!seofp(cs.s))
+ code = gs_note_error(gs_error_rangecheck);
+ }
+ if (bits_per_flag < 0)
+ bits_per_flag = params->BitsPerFlag;
+ break;
+ }
+ case shading_type_Tensor_product_patch: {
+ const gs_shading_Tpp_params_t *const params =
+ (const gs_shading_Tpp_params_t *)pmp;
+
+ if (from_array) {
+ while ((flag = shade_next_flag(&cs, 0)) >= 0) {
+ data_params.num_points = (flag == 0 ? 16 : 12);
+ data_params.num_components = num_comp * (flag == 0 ? 4 : 2);
+ if ((code = put_float_mesh_data(pdev, pscs, &cs, flag,
+ &data_params)) < 0)
+ return code;
+ }
+ if (!seofp(cs.s))
+ code = gs_note_error(gs_error_rangecheck);
+ }
+ if (bits_per_flag < 0)
+ bits_per_flag = params->BitsPerFlag;
+ break;
+ }
+ default:
+ return_error(gs_error_rangecheck);
+ }
+ code1 = cos_dict_put_c_key_int(pscd, "/BitsPerFlag", bits_per_flag);
+ if (code1 < 0)
+ return code;
+ return code;
+}
+
+/* Write a PatternType 2 (shading pattern) color. */
+int
+pdf_put_pattern2(gx_device_pdf *pdev, const gs_imager_state * pis, const gx_drawing_color *pdc,
+ const psdf_set_color_commands_t *ppscc,
+ pdf_resource_t **ppres)
+{
+ const gs_pattern2_instance_t *pinst =
+ (gs_pattern2_instance_t *)pdc->ccolor.pattern;
+ const gs_shading_t *psh = pinst->templat.Shading;
+ cos_value_t v;
+ pdf_resource_t *pres;
+ pdf_resource_t *psres;
+ cos_dict_t *pcd;
+ cos_object_t *psco;
+ const gs_range_t *pranges;
+ int code = pdf_cs_Pattern_colored(pdev, &v);
+ int code1 = 0;
+ gs_matrix smat;
+
+ if (code < 0)
+ return code;
+ code = pdf_alloc_resource(pdev, resourcePattern, gs_no_id, ppres, -1);
+ if (code < 0)
+ return code;
+ pres = *ppres;
+ cos_become(pres->object, cos_type_dict);
+ pcd = (cos_dict_t *)pres->object;
+ code = pdf_alloc_resource(pdev, resourceShading, gs_no_id, &psres, -1);
+ if (code < 0)
+ return code;
+ psco = psres->object;
+ if (ShadingType(psh) >= 4) {
+ /* Shading has an associated data stream. */
+ cos_become(psco, cos_type_stream);
+ code = pdf_put_shading_common(pdev, cos_stream_dict((cos_stream_t *)psco), pis,
+ psh, pinst->shfill, &pranges);
+ if (code >= 0)
+ code1 = pdf_put_mesh_shading(pdev, (cos_stream_t *)psco, psh, pranges);
+ else
+ /* We won't use this shading, we fall back because we couldn't write it */
+ psres->where_used = 0;
+ } else {
+ cos_become(psco, cos_type_dict);
+ code = pdf_put_shading_common(pdev, (cos_dict_t *)psco, pis, psh, pinst->shfill, &pranges);
+ if (code >= 0)
+ code = pdf_put_scalar_shading(pdev, (cos_dict_t *)psco, psh, pranges);
+ else
+ /* We won't use this shading, we fall back because we couldn't write it */
+ psres->where_used = 0;
+ }
+ if (psres->where_used) {
+ code = pdf_substitute_resource(pdev, &psres, resourceShading, NULL, false);
+ if (code < 0)
+ return code;
+ psco = psres->object;
+ psres->where_used |= pdev->used_mask;
+ }
+ /*
+ * In PDF, the Matrix is the transformation from the pattern space to
+ * the *default* user coordinate space, not the current space.
+ * NB. For a form the default space is the parent. This means that when a
+ * form is nested inside a form, the default space is the space of the
+ * first form, and therefore we do *not* remove the resolution scaling.
+ */
+ gs_currentmatrix(pinst->saved, &smat);
+ {
+ double xscale = 1.0, yscale = 1.0;
+ if (pdev->FormDepth == 0) {
+ xscale = 72.0 / pdev->HWResolution[0];
+ yscale = 72.0 / pdev->HWResolution[1];
+ }
+
+ smat.xx *= xscale, smat.yx *= xscale, smat.tx *= xscale;
+ smat.xy *= yscale, smat.yy *= yscale, smat.ty *= yscale;
+ }
+ if (code < 0 ||
+ (code = cos_dict_put_c_key_int(pcd, "/PatternType", 2)) < 0 ||
+ (code = cos_dict_put_c_key_object(pcd, "/Shading", psco)) < 0 ||
+ (code = cos_dict_put_matrix(pdev, pcd, "/Matrix", &smat)) < 0
+ /****** ExtGState ******/
+ )
+ return code;
+ code = pdf_substitute_resource(pdev, &pres, resourcePattern, NULL, false);
+ if (code < 0)
+ return code;
+ pres->where_used |= pdev->used_mask;
+ *ppres = pres;
+
+ cos_value_write(&v, pdev);
+ pprints1(pdev->strm, " %s\n", ppscc->setcolorspace);
+ return code1;
+}
+
+/*
+ Include color space.
+ */
+int
+gdev_pdf_include_color_space(gx_device *dev, gs_color_space *cspace, const byte *res_name, int name_length)
+{
+ gx_device_pdf * pdev = (gx_device_pdf *)dev;
+ cos_value_t cs_value;
+
+ return pdf_color_space_named(pdev, NULL, &cs_value, NULL, cspace,
+ &pdf_color_space_names, true, res_name, name_length, false);
+}
diff --git a/devices/vector/gdevpdfx.h b/devices/vector/gdevpdfx.h
new file mode 100644
index 000000000..694d04fbe
--- /dev/null
+++ b/devices/vector/gdevpdfx.h
@@ -0,0 +1,1489 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Internal definitions for PDF-writing driver. */
+
+#ifndef gdevpdfx_INCLUDED
+# define gdevpdfx_INCLUDED
+
+#include "gsparam.h"
+#include "gsuid.h"
+#include "gxdevice.h"
+#include "gxfont.h"
+#include "gxline.h"
+#include "stream.h"
+#include "spprint.h"
+#include "gdevpsdf.h"
+#include "gxdevmem.h"
+#include "sarc4.h"
+
+#define FINE_GLYPH_USAGE 1 /* Old code = 0, new code = 1 */
+
+/* ---------------- Acrobat limitations ---------------- */
+
+/*
+ * The PDF reference manual, 2nd ed., claims that the limit for coordinates
+ * is +/- 32767. However, testing indicates that Acrobat Reader 4 for
+ * Windows and Linux fail with coordinates outside +/- 16383. Hence, we
+ * limit coordinates to 16k, with a little slop.
+ */
+#define MAX_USER_COORD 16300
+
+/* ---------------- Statically allocated sizes ---------------- */
+/* These should really be dynamic.... */
+
+/* Define the maximum depth of an outline tree. */
+/* Note that there is no limit on the breadth of the tree. */
+#define INITIAL_MAX_OUTLINE_DEPTH 32
+
+/* Define the maximum size of a destination array string. */
+#define MAX_DEST_STRING 80
+
+/* ================ Types and structures ================ */
+
+/* Define the possible contexts for the output stream. */
+typedef enum {
+ PDF_IN_NONE,
+ PDF_IN_STREAM,
+ PDF_IN_TEXT,
+ PDF_IN_STRING
+} pdf_context_t;
+
+/* ---------------- Cos objects ---------------- */
+
+/*
+ * These are abstract types for cos objects. The concrete types are in
+ * gdevpdfo.h.
+ */
+typedef struct cos_object_s cos_object_t;
+typedef struct cos_stream_s cos_stream_t;
+typedef struct cos_dict_s cos_dict_t;
+typedef struct cos_array_s cos_array_t;
+typedef struct cos_value_s cos_value_t;
+typedef struct cos_object_procs_s cos_object_procs_t;
+typedef const cos_object_procs_t *cos_type_t;
+#define cos_types_DEFINED
+
+#ifndef pdf_text_state_DEFINED
+# define pdf_text_state_DEFINED
+typedef struct pdf_text_state_s pdf_text_state_t;
+#endif
+
+#ifndef pdf_char_glyph_pairs_DEFINED
+# define pdf_char_glyph_pairs_DEFINED
+typedef struct pdf_char_glyph_pairs_s pdf_char_glyph_pairs_t;
+#endif
+
+/* ---------------- Resources ---------------- */
+
+typedef enum {
+ /*
+ * Standard PDF resources. Font must be last, because resources
+ * up to but not including Font are written page-by-page.
+ */
+ resourceColorSpace,
+ resourceExtGState,
+ resourcePattern,
+ resourceShading,
+ resourceXObject,
+ resourceProperties,
+ resourceOther, /* Anything else that needs to be stored for a time.
+ * Can be any of the types defined below NUM_RESOURCE_TYPES
+ * but this is the type used to identify the object.
+ */
+ resourceFont,
+ /*
+ * Internally used (pseudo-)resources.
+ */
+ resourceCharProc,
+ resourceCIDFont,
+ resourceCMap,
+ resourceFontDescriptor,
+ resourceGroup,
+ resourceSoftMaskDict,
+ resourceFunction,
+ resourcePage,
+ NUM_RESOURCE_TYPES,
+ /* These resource types were 'resourceOther', but we want to track them
+ * for ps2write. They are not stored in the pdf device structure, unlike
+ * the reource types above.
+ */
+ resourceEncoding,
+ resourceCIDSystemInfo,
+ resourceHalftone,
+ resourceLength,
+ resourceStream,
+ resourceOutline,
+ resourceArticle,
+ resourceDests,
+ resourceLabels,
+ resourceThread,
+ resourceCatalog,
+ resourceEncrypt,
+ resourcePagesTree,
+ resourceMetadata,
+ resourceICC,
+ resourceAnnotation,
+ resourceEmbeddedFiles,
+ resourceFontFile,
+ resourceNone /* Special, used when this isn't a resource at all
+ * eg when we execute a resource we've just written, such as
+ * a Pattern.
+ */
+} pdf_resource_type_t;
+
+#define PDF_RESOURCE_TYPE_NAMES\
+ "/ColorSpace", "/ExtGState", "/Pattern", "/Shading", "/XObject", "/Properties", 0, "/Font",\
+ 0, "/Font", "/CMap", "/FontDescriptor", "/Group", "/Mask", 0, 0, 0, 0, 0,\
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+#define PDF_RESOURCE_TYPE_STRUCTS\
+ &st_pdf_color_space, /* gdevpdfg.h / gdevpdfc.c */\
+ &st_pdf_resource, /* see below */\
+ &st_pdf_pattern,\
+ &st_pdf_resource,\
+ &st_pdf_x_object, /* see below */\
+ &st_pdf_resource,\
+ &st_pdf_resource,\
+ &st_pdf_font_resource, /* gdevpdff.h / gdevpdff.c */\
+ &st_pdf_char_proc, /* gdevpdff.h / gdevpdff.c */\
+ &st_pdf_font_resource, /* gdevpdff.h / gdevpdff.c */\
+ &st_pdf_resource,\
+ &st_pdf_font_descriptor, /* gdevpdff.h / gdevpdff.c */\
+ &st_pdf_resource,\
+ &st_pdf_resource,\
+ &st_pdf_resource,\
+ &st_pdf_resource
+
+/*
+ * rname is currently R<id> for all resources other than synthesized fonts;
+ * for synthesized fonts, rname is A, B, .... The string is null-terminated.
+ */
+
+ /* WARNING WILL ROBINSON!
+ * these 2 pointers may *look* like a double linked list but in fact these are pointers
+ * to two *different* single-linked lists. 'next' points to the next resource
+ * of this type, which is stored in the resource chains pointed to by the array
+ * 'resources' in the device structure. 'prev' is a pointer
+ * to the list of stored resources of all types, pointed to by the
+ * 'last_resource' member of the device structure.
+ */
+#define pdf_resource_common(typ)\
+ typ *next; /* next resource of this type */\
+ pdf_resource_t *prev; /* previously allocated resource */\
+ gs_id rid; /* optional ID key */\
+ bool named;\
+ bool global; /* ps2write only */\
+ char rname[1/*R*/ + (sizeof(long) * 8 / 3 + 1) + 1/*\0*/];\
+ ulong where_used; /* 1 bit per level of content stream */\
+ cos_object_t *object
+typedef struct pdf_resource_s pdf_resource_t;
+struct pdf_resource_s {
+ pdf_resource_common(pdf_resource_t);
+};
+
+/* The descriptor is public for subclassing. */
+extern_st(st_pdf_resource);
+#define public_st_pdf_resource() /* in gdevpdfu.c */\
+ gs_public_st_ptrs3(st_pdf_resource, pdf_resource_t, "pdf_resource_t",\
+ pdf_resource_enum_ptrs, pdf_resource_reloc_ptrs, next, prev, object)
+
+/*
+ * We define XObject resources here because they are used for Image,
+ * Form, and PS XObjects, which are implemented in different files.
+ */
+typedef struct pdf_x_object_s pdf_x_object_t;
+struct pdf_x_object_s {
+ pdf_resource_common(pdf_x_object_t);
+ int width, height; /* specified width and height for images */
+ int data_height; /* actual data height for images */
+};
+#define private_st_pdf_x_object() /* in gdevpdfu.c */\
+ gs_private_st_suffix_add0(st_pdf_x_object, pdf_x_object_t,\
+ "pdf_x_object_t", pdf_x_object_enum_ptrs, pdf_x_object_reloc_ptrs,\
+ st_pdf_resource)
+
+/* Define the mask for which procsets have been used on a page. */
+typedef enum {
+ NoMarks = 0,
+ ImageB = 1,
+ ImageC = 2,
+ ImageI = 4,
+ Text = 8
+} pdf_procset_t;
+
+/* ------ Fonts ------ */
+
+/* Define abstract types. */
+typedef struct pdf_char_proc_s pdf_char_proc_t; /* gdevpdff.h */
+typedef struct pdf_char_proc_ownership_s pdf_char_proc_ownership_t; /* gdevpdff.h */
+typedef struct pdf_font_s pdf_font_t; /* gdevpdff.h */
+typedef struct pdf_text_data_s pdf_text_data_t; /* gdevpdft.h */
+
+/* ---------------- Other auxiliary structures ---------------- */
+
+/* Outline nodes and levels */
+typedef struct pdf_outline_node_s {
+ long id, parent_id, prev_id, first_id, last_id;
+ int count;
+ cos_dict_t *action;
+} pdf_outline_node_t;
+typedef struct pdf_outline_level_s {
+ pdf_outline_node_t first;
+ pdf_outline_node_t last;
+ int left;
+} pdf_outline_level_t;
+/*
+ * The GC descriptor is implicit, since outline levels occur only in an
+ * embedded array in the gx_device_pdf structure.
+ */
+
+/* Articles */
+typedef struct pdf_bead_s {
+ long id, article_id, prev_id, next_id, page_id;
+ gs_rect rect;
+} pdf_bead_t;
+typedef struct pdf_article_s pdf_article_t;
+struct pdf_article_s {
+ pdf_article_t *next;
+ cos_dict_t *contents;
+ pdf_bead_t first;
+ pdf_bead_t last;
+};
+
+#define private_st_pdf_article()\
+ gs_private_st_ptrs2(st_pdf_article, pdf_article_t,\
+ "pdf_article_t", pdf_article_enum_ptrs, pdf_article_reloc_ptrs,\
+ next, contents)
+
+/* ---------------- The device structure ---------------- */
+
+/* Resource lists */
+/* We seem to split the resources up into 'NUM_RESOURCE_CHAINS' linked
+ * lists purely as a performance boost. Presumably it save searching very
+ * long lists.
+ */
+#define NUM_RESOURCE_CHAINS 16
+typedef struct pdf_resource_list_s {
+ pdf_resource_t *chains[NUM_RESOURCE_CHAINS];
+} pdf_resource_list_t;
+
+/* Define the hash function for gs_ids. */
+#define gs_id_hash(rid) ((rid) + ((rid) / NUM_RESOURCE_CHAINS))
+/* Define the accessor for the proper hash chain. */
+#define PDF_RESOURCE_CHAIN(pdev, type, rid)\
+ (&(pdev)->resources[type].chains[gs_id_hash(rid) % NUM_RESOURCE_CHAINS])
+
+/* Define the bookkeeping for an open stream. */
+typedef struct pdf_stream_position_s {
+ long length_id;
+ gs_offset_t start_pos;
+} pdf_stream_position_t;
+
+/*
+ * Define the structure for keeping track of text rotation.
+ * There is one for the current page (for AutoRotate /PageByPage)
+ * and one for the whole document (for AutoRotate /All).
+ */
+typedef struct pdf_text_rotation_s {
+ long counts[5]; /* 0, 90, 180, 270, other */
+ int Rotate; /* computed rotation, -1 means none */
+} pdf_text_rotation_t;
+#define pdf_text_rotation_angle_values 0, 90, 180, 270, -1
+
+/*
+ * Define document and page information derived from DSC comments.
+ */
+typedef struct pdf_page_dsc_info_s {
+ int orientation; /* -1 .. 3 */
+ int viewing_orientation; /* -1 .. 3 */
+ gs_rect bounding_box;
+} pdf_page_dsc_info_t;
+
+/*
+ * Define the stored information for a page. Because pdfmarks may add
+ * information to any page anywhere in the document, we have to wait
+ * until the end to write the page dictionaries.
+ */
+typedef struct pdf_page_s {
+ cos_dict_t *Page;
+ gs_point MediaBox;
+ pdf_procset_t procsets;
+ long contents_id;
+ long resource_ids[resourceFont + 1]; /* resources thru Font, see above */
+ long group_id;
+ cos_array_t *Annots;
+ pdf_text_rotation_t text_rotation;
+ pdf_page_dsc_info_t dsc_info;
+ bool NumCopies_set; /* ps2write only. */
+ int NumCopies; /* ps2write only. */
+} pdf_page_t;
+#define private_st_pdf_page() /* in gdevpdf.c */\
+ gs_private_st_ptrs2(st_pdf_page, pdf_page_t, "pdf_page_t",\
+ pdf_page_enum_ptrs, pdf_page_reloc_ptrs, Page, Annots)
+
+/*
+ * Define the structure for the temporary files used while writing.
+ * There are 4 of these, described below.
+ */
+typedef struct pdf_temp_file_s {
+ char file_name[gp_file_name_sizeof];
+ FILE *file;
+ stream *strm;
+ byte *strm_buf;
+ stream *save_strm; /* save pdev->strm while writing here */
+} pdf_temp_file_t;
+
+#ifndef gx_device_pdf_DEFINED
+# define gx_device_pdf_DEFINED
+typedef struct gx_device_pdf_s gx_device_pdf;
+#endif
+
+/* Structures and definitions for linearisation */
+typedef struct linearisation_record_s {
+ int PageUsage;
+ int NumPagesUsing;
+ int *PageList;
+ uint NewObjectNumber;
+ gs_offset_t OriginalOffset;
+ gs_offset_t LinearisedOffset;
+ gs_offset_t Length;
+} pdf_linearisation_record_t;
+
+#define private_st_pdf_linearisation_record()\
+ gs_private_st_ptrs1(st_pdf_linearisation_record, pdf_linearisation_record_t,\
+ "pdf_linearisation_record_t", pdf_linearisation_record_enum_ptrs, pdf_linearisation_record_reloc_ptrs,\
+ PageList)
+
+typedef struct page_hint_stream_header_s {
+ unsigned int LeastObjectsPerPage; /* Including the page object */
+ /* Item 2 is already stored elsewhere */
+ unsigned int MostObjectsPerPage;
+ unsigned short ObjectNumBits;
+ unsigned int LeastPageLength; /* From beginning of page object to end of last object used by page */
+ unsigned int MostPageLength;
+ unsigned short PageLengthNumBits;
+ unsigned int LeastPageOffset;
+ unsigned int MostPageOffset;
+ unsigned short PageOffsetNumBits;
+ unsigned int LeastContentLength;
+ unsigned int MostContentLength;
+ unsigned short ContentLengthNumBits;
+ unsigned int MostSharedObjects;
+ unsigned int LargestSharedObject;
+ unsigned short SharedObjectNumBits;
+} page_hint_stream_header_t;
+
+typedef struct page_hint_stream_s {
+ unsigned int NumUniqueObjects; /* biased by the least number of objects on any page */
+ unsigned int PageLength; /* biased by the least page length*/
+ unsigned int NumSharedObjects;
+ unsigned int *SharedObjectRef; /* one for each shaed object on the page */
+ /* Item 5 we invent */
+ gs_offset_t ContentOffset; /* biased by the least offset to the conent stream for any page */
+ gs_offset_t ContentLength;/* biased by the least content stream length */
+} page_hint_stream_t;
+
+typedef struct shared_hint_stream_header_s {
+ unsigned int FirstSharedObject;
+ gs_offset_t FirstObjectOffset;
+ unsigned int FirstPageEntries;
+ unsigned int NumSharedObjects;
+ /* Item 5 is always 1 as far as we are concerned */
+ unsigned int LeastObjectLength;
+ unsigned int MostObjectLength;
+ unsigned short LengthNumBits;
+} shared_hint_stream_header_t;
+
+typedef struct share_hint_stream_s {
+ unsigned int ObjectNumber;
+ gs_offset_t ObjectOffset;
+ unsigned int ObjectLength; /* biased by the LeastObjectLength */
+ /* item 2 is always 0 */
+ /* Which means that item 3 is never present */
+ /* Finally item 4 is always 0 (1 less than the number of objects in the group, which is always 1) */
+} shared_hint_stream_t;
+
+typedef struct pdf_linearisation_s {
+ FILE *sfile;
+ pdf_temp_file_t Lin_File;
+ char HintBuffer[256];
+ unsigned char HintBits;
+ unsigned char HintByte;
+ long Catalog_id;
+ long Info_id;
+ long Pages_id;
+ long NumPage1Resources;
+ long NumPart1StructureResources;
+ long NumSharedResources;
+ long NumUniquePageResources;
+ long NumPart9Resources;
+ long NumNonPageResources;
+ long LastResource;
+ long MainFileEnd;
+ gs_offset_t *Offsets;
+ gs_offset_t xref;
+ gs_offset_t FirstxrefOffset;
+ gs_offset_t FirsttrailerOffset;
+ gs_offset_t LDictOffset;
+ gs_offset_t FileLength;
+ gs_offset_t T;
+ gs_offset_t E;
+ page_hint_stream_header_t PageHintHeader;
+ int NumPageHints;
+ page_hint_stream_t *PageHints;
+ shared_hint_stream_header_t SharedHintHeader;
+ int NumSharedHints;
+ shared_hint_stream_t *SharedHints;
+} pdf_linearisation_t;
+
+/* These are the values for 'PageUsage' above, values > 0 indicate the page number that uses the resource */
+#define resource_usage_not_referenced 0
+#define resource_usage_page_shared -1
+/* Thses need to be lower than the shared value */
+#define resource_usage_part1_structure -2
+#define resource_usage_part9_structure -3
+#define resource_usage_written -4
+
+/*
+ * Define the structure for PDF font cache element.
+ */
+typedef struct pdf_font_cache_elem_s pdf_font_cache_elem_t;
+struct pdf_font_cache_elem_s {
+ pdf_font_cache_elem_t *next;
+ gs_id font_id;
+ int num_chars; /* safety purpose only */
+ int num_widths; /* safety purpose only */
+ struct pdf_font_resource_s *pdfont;
+ byte *glyph_usage;
+ double *real_widths; /* [count] (not used for Type 0) */
+};
+
+#define private_st_pdf_font_cache_elem()\
+ gs_private_st_ptrs4(st_pdf_font_cache_elem, pdf_font_cache_elem_t,\
+ "pdf_font_cache_elem_t", pdf_font_cache_elem_enum,\
+ pdf_font_cache_elem_reloc, next, pdfont,\
+ glyph_usage, real_widths)
+
+/*
+ * pdf_viewer_state tracks the graphic state of a viewer,
+ * which would interpret the generated PDF file
+ * immediately when it is generated.
+ */
+typedef struct pdf_viewer_state_s {
+ int transfer_not_identity; /* bitmask */
+ gs_id transfer_ids[4];
+ float opacity_alpha; /* state.opacity.alpha */
+ float shape_alpha; /* state.shape.alpha */
+ gs_blend_mode_t blend_mode; /* state.blend_mode */
+ gs_id halftone_id;
+ gs_id black_generation_id;
+ gs_id undercolor_removal_id;
+ int overprint_mode;
+ float smoothness; /* state.smoothness */
+ float flatness;
+ bool text_knockout; /* state.text_knockout */
+ bool fill_overprint;
+ bool stroke_overprint;
+ bool stroke_adjust; /* state.stroke_adjust */
+ bool fill_used_process_color;
+ bool stroke_used_process_color;
+ gx_hl_saved_color saved_fill_color;
+ gx_hl_saved_color saved_stroke_color;
+ gx_line_params line_params;
+ float *dash_pattern;
+ uint dash_pattern_size;
+ gs_id soft_mask_id;
+} pdf_viewer_state;
+
+/*
+ * Define a structure for saving context when entering
+ * a contents stream accumulation mode (charproc, Type 1 pattern).
+ */
+typedef struct pdf_substream_save_s {
+ pdf_context_t context;
+ pdf_text_state_t *text_state;
+ gx_path *clip_path;
+ gs_id clip_path_id;
+ int vgstack_bottom;
+ stream *strm;
+ cos_dict_t *substream_Resources;
+ pdf_procset_t procsets;
+ bool skip_colors;
+ pdf_resource_t *font3;
+ pdf_resource_t *accumulating_substream_resource;
+ bool charproc_just_accumulated;
+ bool accumulating_a_global_object;
+ pdf_resource_t *pres_soft_mask_dict;
+ gs_const_string objname;
+ int last_charpath_op;
+} pdf_substream_save;
+
+#define private_st_pdf_substream_save()\
+ gs_private_st_strings1_ptrs7(st_pdf_substream_save, pdf_substream_save,\
+ "pdf_substream_save", pdf_substream_save_enum,\
+ pdf_substream_save_reloc, objname, text_state, clip_path, strm, \
+ substream_Resources, font3, accumulating_substream_resource, pres_soft_mask_dict)
+#define private_st_pdf_substream_save_element()\
+ gs_private_st_element(st_pdf_substream_save_element, pdf_substream_save,\
+ "pdf_substream_save[]", pdf_substream_save_elt_enum_ptrs,\
+ pdf_substream_save_elt_reloc_ptrs, st_pdf_substream_save)
+
+typedef enum {
+ pdf_compress_none,
+ pdf_compress_LZW, /* not currently used, thanks to Unisys */
+ pdf_compress_Flate
+} pdf_compression_type;
+
+/* Define the device structure. */
+struct gx_device_pdf_s {
+ gx_device_psdf_common;
+ gs_font_dir *pdf_font_dir; /* Our own font cache, so that PCL can free its own ones, we set our font copeis to use this */
+ /* see 'pdf_free_pdf_font_cache' in gdevpdf.c for more details. */
+ bool is_ps2write; /* ps2write (true) versus pdfwrite (false); never changed */
+ /* PDF-specific distiller parameters */
+ double CompatibilityLevel;
+ int EndPage;
+ int StartPage;
+ bool Optimize;
+ bool ParseDSCCommentsForDocInfo;
+ bool ParseDSCComments;
+ bool EmitDSCWarnings;
+ bool CreateJobTicket;
+ bool PreserveEPSInfo;
+ bool AutoPositionEPSFiles;
+ bool PreserveCopyPage;
+ bool UsePrologue;
+ int OffOptimizations;
+ /* End of distiller parameters */
+ /* PDF/X parameters */
+ gs_param_float_array PDFXTrimBoxToMediaBoxOffset;
+ gs_param_float_array PDFXBleedBoxToTrimBoxOffset;
+ bool PDFXSetBleedBoxToMediaBox;
+ /* Other parameters */
+ bool ReAssignCharacters;
+ bool ReEncodeCharacters;
+ long FirstObjectNumber;
+ bool CompressFonts;
+ bool PrintStatistics;
+ gs_param_string DocumentUUID;
+ gs_param_string InstanceUUID;
+ int DocumentTimeSeq;
+ bool ForOPDFRead; /* PS2WRITE only. */
+ bool Eps2Write; /* EPS2WRITE only */
+ bool CompressEntireFile; /* PS2WRITE only. */
+ bool ResourcesBeforeUsage; /* PS2WRITE only. */
+ bool HavePDFWidths; /* PS2WRITE only. */
+ bool HaveStrokeColor; /* PS2WRITE only. */
+ bool ProduceDSC; /* PS2WRITE only. */
+ bool HaveTransparency;
+ bool PatternImagemask; /* The target viewer|printer handles imagemask
+ with pattern color. */
+ bool PDFX; /* Generate PDF/X */
+ int PDFA; /* Generate PDF/A 0 = don't produce, otherwise level of PDF/A */
+ bool AbortPDFAX; /* Abort generation of PDFA or X, produce regular PDF */
+ long MaxClipPathSize; /* The maximal number of elements of a clipping path
+ that the target viewer|printer can handle. */
+ long MaxShadingBitmapSize; /* The maximal number of bytes in
+ a bitmap representation of a shading.
+ (Bigger shadings to be downsampled). */
+ long MaxInlineImageSize;
+ gs_param_int_array DSCEncodingToUnicode;
+ /* Encryption parameters */
+ gs_param_string OwnerPassword;
+ gs_param_string UserPassword;
+ uint KeyLength;
+ uint Permissions;
+ uint EncryptionR;
+ gs_param_string NoEncrypt;
+ bool EncryptMetadata;
+ /* End of parameters */
+ bool ComputeDocumentDigest; /* Developer needs only; Always true in production. */
+ /* Encryption data */
+ byte EncryptionO[32];
+ byte EncryptionU[32];
+ byte EncryptionKey[16];
+ uint EncryptionV;
+ /* Values derived from DSC comments */
+ bool is_EPS;
+ int AccumulatingBBox;
+ gs_rect BBox;
+ pdf_page_dsc_info_t doc_dsc_info; /* document default */
+ pdf_page_dsc_info_t page_dsc_info; /* current page */
+ /* Additional graphics state */
+ bool fill_overprint, stroke_overprint;
+ bool remap_fill_color, remap_stroke_color;
+ int overprint_mode;
+ gs_id halftone_id;
+ gs_id transfer_ids[4];
+ int transfer_not_identity; /* bitmask */
+ gs_id black_generation_id, undercolor_removal_id;
+ /* Following are set when device is opened. */
+ pdf_compression_type compression;
+ pdf_compression_type compression_at_page_start;
+ /* pdf_memory is 'stable' memory, it is not subject to save and restore
+ * and is the allocator which should be used for pretty much ewverything
+ */
+#define pdf_memory v_memory
+ /*
+ * The xref temporary file is logically an array of longs.
+ * xref[id - FirstObjectNumber] is the position in the output file
+ * of the object with the given id.
+ *
+ * Note that xref, unlike the other temporary files, does not have
+ * an associated stream or stream buffer.
+ */
+ pdf_temp_file_t xref;
+ /*
+ * asides holds resources and other "aside" objects. It is
+ * copied verbatim to the output file at the end of the document.
+ */
+ pdf_temp_file_t asides;
+ /*
+ * streams holds data for stream-type Cos objects. The data is
+ * copied to the output file at the end of the document.
+ *
+ * Note that streams.save_strm is not used, since we don't interrupt
+ * normal output when saving stream data.
+ */
+ pdf_temp_file_t streams;
+ /*
+ * pictures holds graphic objects being accumulated between BP and EP.
+ * The object is moved to streams when the EP is reached: since BP and
+ * EP nest, we delete the object from the pictures file at that time.
+ */
+ pdf_temp_file_t pictures;
+ /* ................ */
+ long next_id;
+ /* The following 3 objects, and only these, are allocated */
+ /* when the file is opened. */
+ cos_dict_t *Catalog;
+ cos_dict_t *Info;
+ cos_dict_t *Pages;
+#define pdf_num_initial_ids 3
+ long outlines_id;
+ int next_page;
+ int max_referred_page;
+ long contents_id;
+ pdf_context_t context;
+ long contents_length_id;
+ gs_offset_t contents_pos;
+ pdf_procset_t procsets; /* used on this page */
+ pdf_text_data_t *text;
+ pdf_text_rotation_t text_rotation;
+#define initial_num_pages 50
+ pdf_page_t *pages;
+ int num_pages;
+ ulong used_mask; /* for where_used: page level = 1 */
+ pdf_resource_list_t resources[NUM_RESOURCE_TYPES];
+ /* cs_Patterns[0] is colored; 1,3,4 are uncolored + Gray,RGB,CMYK */
+ pdf_resource_t *cs_Patterns[5];
+ pdf_resource_t *Identity_ToUnicode_CMaps[2]; /* WMode = 0,1 */
+ pdf_resource_t *last_resource;
+ pdf_resource_t *OneByteIdentityH;
+ gs_id IdentityCIDSystemInfo_id;
+ pdf_outline_level_t *outline_levels;
+ int outline_depth;
+ int max_outline_depth;
+ int closed_outline_depth;
+ int outlines_open;
+ pdf_article_t *articles;
+ cos_dict_t *Dests;
+ cos_dict_t *EmbeddedFiles;
+ byte fileID[16];
+ /* Use a single time moment for all UUIDs to minimize an indeterminizm. */
+ long uuid_time[2];
+ /*
+ * global_named_objects holds named objects that are independent of
+ * the current namespace: {Catalog}, {DocInfo}, {Page#}, {ThisPage},
+ * {PrevPage}, {NextPage}.
+ */
+ cos_dict_t *global_named_objects;
+ /*
+ * local_named_objects holds named objects in the current namespace.
+ */
+ cos_dict_t *local_named_objects;
+ /*
+ * NI_stack is a last-in, first-out stack in which each element is a
+ * (named) cos_stream_t object that eventually becomes the object of an
+ * image XObject resource.
+ */
+ cos_array_t *NI_stack;
+ /*
+ * Namespace_stack is a last-in, first-out stack in which each pair of
+ * elements is, respectively, a saved value of local_named_objects and
+ * a saved value of NI_stack. (The latter is not documented by Adobe,
+ * but it was confirmed by them.)
+ */
+ cos_array_t *Namespace_stack;
+ pdf_font_cache_elem_t *font_cache;
+ /*
+ * char_width is used by pdf_text_set_cache to communicate
+ * with assign_char_code around gdev_pdf_fill_mask.
+ */
+ gs_point char_width;
+ /*
+ * We need a stable copy of clipping path to prevent writing
+ * redundant clipping paths when PS document generates such ones.
+ */
+ gx_path *clip_path;
+ /*
+ * Page labels.
+ */
+ cos_array_t *PageLabels;
+ int PageLabels_current_page;
+ cos_dict_t *PageLabels_current_label;
+ /*
+ * The following is a dangerous pointer, which pdf_text_process
+ * uses to communicate with assign_char_code.
+ * It is a pointer from global memory to local memory.
+ * The garbager must not proceess this pointer, and it must
+ * not be listed in st_device_pdfwrite.
+ * It's life time terminates on garbager invocation.
+ */
+ gs_text_enum_t *pte;
+ /*
+ * The viewer's graphic state stack.
+ */
+ pdf_viewer_state *vgstack;
+ int vgstack_size;
+ int vgstack_depth;
+ int vgstack_bottom; /* Stack bottom for the current substream. */
+ pdf_viewer_state vg_initial; /* Initial values for viewer's graphic state */
+ bool vg_initial_set;
+
+ /* The substream context stack. */
+ int sbstack_size;
+ int sbstack_depth;
+ pdf_substream_save *sbstack;
+
+ /* Temporary workaround. The only way to get forms out of pdfwrite at present
+ * is via a transparency group or mask operation. Ordinarily we don't care
+ * much about forms, but Patterns within forms need to be scaled to the
+ * CTM of the Pattern, not the default page co-ordinate system. We use
+ * this value to know if we are nested inside a form or not. If we are
+ * we don't undo the resolution co-ordinate transform.
+ */
+ int FormDepth;
+
+ /* Determine if we have a high level form. We want to do things differently
+ * sometimes, if we are capturing a form
+ */
+ int HighLevelForm;
+
+ /* Nasty hack. OPDFread.ps resets the graphics state to the identity before
+ * replaying the Pattern PaintProc, but if the Pattern is nested inside a
+ * previous pattern, this doesn't work. We use this to keep track of whether
+ * we are nested, and if we are (and are ps2write, not pdfwrite) we track the
+ * pattern CTM below.
+ */
+ int PatternDepth;
+ gs_matrix AccumulatedPatternMatrix;
+
+ /* Accessories */
+ cos_dict_t *substream_Resources; /* Substream resources */
+ gs_color_space_index pcm_color_info_index; /* Index of the ProcessColorModel space. */
+ bool skip_colors; /* Skip colors while a pattern/charproc accumulation. */
+ bool AR4_save_bug; /* See pdf_put_uncolored_pattern */
+ pdf_resource_t *font3; /* The owner of the accumulated charstring. */
+ pdf_resource_t *accumulating_substream_resource;
+ gs_matrix_fixed charproc_ctm;
+ bool accumulating_charproc;
+ gs_rect charproc_BBox;
+ bool charproc_just_accumulated; /* A flag for controlling
+ the glyph variation recognition.
+ Used only with uncached charprocs. */
+ bool PS_accumulator; /* A flag to determine whether a given
+ accumulator is for a PostScript type 3 font or not. */
+ bool accumulating_a_global_object; /* ps2write only.
+ Accumulating a global object (such as a named Form,
+ so that resources used in it must also be global.
+ Important for viewers with small memory,
+ which drops resources per page. */
+ const pdf_char_glyph_pairs_t *cgp; /* A temporary pointer
+ for pdf_is_same_charproc1.
+ Must be NULL when the garbager is invoked,
+ because it points from global to local memory. */
+ int substituted_pattern_count;
+ int substituted_pattern_drop_page;
+ /* Temporary data for use_image_as_pattern,
+ They pass an information about a mask of a masked image,
+ (which is being converted into a pattern)
+ between 2 consecutive calls to pdf_image_end_image_data. */
+ gs_id image_mask_id;
+ bool image_mask_is_SMask;
+ bool image_mask_skip; /* A flag for pdf_begin_transparency_mask */
+ uint image_with_SMask; /* A flag for pdf_begin_transparency_group. In order to
+ * deal with nested groups we set/test the bit according
+ * to the FormDepth
+ */
+ gs_matrix converting_image_matrix;
+ double image_mask_scale;
+ /* Temporary data for soft mask form. */
+ pdf_resource_t *pres_soft_mask_dict;
+ /* Temporary data for pdfmark_BP. */
+ gs_const_string objname;
+ int OPDFRead_procset_length; /* PS2WRITE only. */
+ void *find_resource_param; /* WARNING : not visible for garbager. */
+ int last_charpath_op; /* true or false state of last charpath */
+ bool type3charpath;
+ bool SetPageSize;
+ bool RotatePages;
+ bool FitPages;
+ bool CenterPages;
+ bool DoNumCopies;
+ bool PreserveSeparation;
+ bool PreserveDeviceN;
+ int PDFACompatibilityPolicy;
+ bool DetectDuplicateImages;
+ bool AllowIncrementalCFF;
+ bool WantsToUnicode;
+ bool AllowPSRepeatFunctions;
+ bool IsDistiller;
+ bool PreserveSMask;
+ bool PreserveTrMode;
+ bool NoT3CCITT; /* A bug in Brother printers causes CCITTFaxDecode
+ * to fail, especially with small amounts of data.
+ * This parameter is present only to allow
+ * ps2write output to work on those pritners.
+ */
+ bool UseOldColor; /* Use the old pdfwrite colour conversions instead of the CMS
+ * temporary variable
+ */
+ bool Linearise; /* Whether to Linearizse the file, the next 2 parameter
+ * are only used if this is true.
+ */
+ pdf_linearisation_record_t
+ *ResourceUsage; /* An array, one per resource defined to date, which
+ * contains either -2 (shared on multiple pages), -1
+ * (structure object, eg catalog), 0 (not used on a page
+ * or the page number. This does limit us to a mere 2^31
+ * pages
+ */
+ int ResourceUsageSize; /* Size of the above array, currently */
+ bool InOutputPage; /* Used when closing the file, if this is true then we were
+ * called from output_page and should emit a page even if there
+ * are no marks. If false, then we probably were called from
+ * closedevice and, if there are no marks, we should delete
+ * the last file *if* we are emitting one file per page.
+ */
+ bool FlattenFonts;
+ int LastFormID;
+};
+
+#define is_in_page(pdev)\
+ ((pdev)->contents_id != 0)
+
+/* Enumerate the individual pointers in a gx_device_pdf.
+ I disliked this macro and so its been removed, the pointers are
+ enumerated/relocated in gdevpdf.c now. We still need the gx_device_pdf_num_ptrs
+ though, so I'm maintaining this comment just to keep track of the number
+ of pointers.
+ */
+/*#define gx_device_pdf_do_ptrs(m)\
+ m(0,asides.strm) m(1,asides.strm_buf) m(2,asides.save_strm)\
+ m(3,streams.strm) m(4,streams.strm_buf)\
+ m(5,pictures.strm) m(6,pictures.strm_buf) m(7,pictures.save_strm)\
+ m(8,Catalog) m(9,Info) m(10,Pages)\
+ m(11,text) m(12,pages)\
+ m(13,cs_Patterns[0])\
+ m(14,cs_Patterns[1]) m(15,cs_Patterns[3]) m(16,cs_Patterns[4])\
+ m(17,last_resource)\
+ m(18,articles) m(19,Dests) m(20,global_named_objects)\
+ m(21, local_named_objects) m(22,NI_stack) m(23,Namespace_stack)\
+ m(24,font_cache) m(25,clip_path)\
+ m(26,PageLabels) m(27,PageLabels_current_label)\
+ m(28,sbstack) m(29,substream_Resources) m(30,font3)\
+ m(31,accumulating_substream_resource) \
+ m(32,pres_soft_mask_dict) m(33,PDFXTrimBoxToMediaBoxOffset.data)\
+ m(34,PDFXBleedBoxToTrimBoxOffset.data) m(35, DSCEncodingToUnicode.data)\
+ m(36,Identity_ToUnicode_CMaps[0]) m(37,Identity_ToUnicode_CMaps[1])\
+ m(38,ResourceUsage) m(39,vgstack)\
+ m(40, outline_levels) m(41, EmbeddedFiles)
+ m(41, gx_device_pdf, EmbeddedFiles);
+ m(42, gx_device_pdf, pdf_font_dir);*/
+#define gx_device_pdf_num_ptrs 43
+#define gx_device_pdf_do_param_strings(m)\
+ m(0, OwnerPassword) m(1, UserPassword) m(2, NoEncrypt)\
+ m(3, DocumentUUID) m(4, InstanceUUID)
+#define gx_device_pdf_num_param_strings 5
+#define gx_device_pdf_do_const_strings(m)\
+ m(0, objname)
+#define gx_device_pdf_num_const_strings 1
+
+#define private_st_device_pdfwrite() /* in gdevpdf.c */\
+ gs_private_st_composite_final(st_device_pdfwrite, gx_device_pdf,\
+ "gx_device_pdf", device_pdfwrite_enum_ptrs, device_pdfwrite_reloc_ptrs,\
+ device_pdfwrite_finalize)
+
+/* ================ Driver procedures ================ */
+
+ /* In gdevpdfb.c */
+dev_proc_copy_mono(gdev_pdf_copy_mono);
+dev_proc_copy_color(gdev_pdf_copy_color);
+dev_proc_fill_mask(gdev_pdf_fill_mask);
+dev_proc_strip_tile_rectangle(gdev_pdf_strip_tile_rectangle);
+ /* In gdevpdfd.c */
+extern const gx_device_vector_procs pdf_vector_procs;
+dev_proc_fill_rectangle(gdev_pdf_fill_rectangle);
+dev_proc_fill_path(gdev_pdf_fill_path);
+dev_proc_stroke_path(gdev_pdf_stroke_path);
+dev_proc_fillpage(gdev_pdf_fillpage);
+ /* In gdevpdfi.c */
+dev_proc_begin_typed_image(gdev_pdf_begin_typed_image);
+ /* In gdevpdfp.c */
+dev_proc_get_params(gdev_pdf_get_params);
+dev_proc_put_params(gdev_pdf_put_params);
+ /* In gdevpdft.c */
+dev_proc_text_begin(gdev_pdf_text_begin);
+dev_proc_fill_rectangle_hl_color(gdev_pdf_fill_rectangle_hl_color);
+ /* In gdevpdfv.c */
+dev_proc_include_color_space(gdev_pdf_include_color_space);
+ /* In gdevpdft.c */
+dev_proc_create_compositor(gdev_pdf_create_compositor);
+dev_proc_begin_transparency_group(gdev_pdf_begin_transparency_group);
+dev_proc_end_transparency_group(gdev_pdf_end_transparency_group);
+dev_proc_begin_transparency_mask(gdev_pdf_begin_transparency_mask);
+dev_proc_end_transparency_mask(gdev_pdf_end_transparency_mask);
+dev_proc_dev_spec_op(gdev_pdf_dev_spec_op);
+
+/* ================ Utility procedures ================ */
+
+/* ---------------- Exported by gdevpdf.c ---------------- */
+
+/* Initialize the IDs allocated at startup. */
+void pdf_initialize_ids(gx_device_pdf * pdev);
+
+/* Update the color mapping procedures after setting ProcessColorModel. */
+void pdf_set_process_color_model(gx_device_pdf * pdev, int index);
+
+/* Reset the text state parameters to initial values. */
+void pdf_reset_text(gx_device_pdf *pdev);
+
+/* ---------------- Exported by gdevpdfu.c ---------------- */
+
+/* ------ Document ------ */
+
+/* Write a DSC compliant header to the file */
+int ps2write_dsc_header(gx_device_pdf * pdev, int pages);
+
+/* Open the document if necessary. */
+int pdfwrite_pdf_open_document(gx_device_pdf * pdev);
+
+/* ------ Objects ------ */
+
+/* Allocate an ID for a future object, set its pos=0 so we can tell if it is used */
+long pdf_obj_forward_ref(gx_device_pdf * pdev);
+
+/* Allocate an ID for a future object. */
+long pdf_obj_ref(gx_device_pdf * pdev);
+
+/* Remove an object from the xref table (mark as unused) */
+long pdf_obj_mark_unused(gx_device_pdf *pdev, long id);
+
+/* Read the current position in the output stream. */
+gs_offset_t pdf_stell(gx_device_pdf * pdev);
+
+/* Begin an object, optionally allocating an ID. */
+long pdf_open_obj(gx_device_pdf * pdev, long id, pdf_resource_type_t type);
+long pdf_begin_obj(gx_device_pdf * pdev, pdf_resource_type_t type);
+
+/* End an object. */
+int pdf_end_obj(gx_device_pdf * pdev, pdf_resource_type_t type);
+
+/* ------ Page contents ------ */
+
+/* Open a page contents part. */
+/* Return an error if the page has too many contents parts. */
+int pdf_open_contents(gx_device_pdf * pdev, pdf_context_t context);
+
+/* Close the current contents part if we are in one. */
+int pdf_close_contents(gx_device_pdf * pdev, bool last);
+
+/* ------ Resources et al ------ */
+
+extern const char *const pdf_resource_type_names[];
+extern const gs_memory_struct_type_t *const pdf_resource_type_structs[];
+
+/* Record usage of resoruces by pages */
+int pdf_record_usage(gx_device_pdf *const pdev, long resource_id, int page_num);
+int pdf_record_usage_by_parent(gx_device_pdf *const pdev, long resource_id, long parent);
+
+/*
+ * Define the offset that indicates that a file position is in the
+ * asides file rather than the main (contents) file.
+ * Must be a power of 2, and larger than the largest possible output file.
+ */
+#define ASIDES_BASE_POSITION min_int64_t
+
+/* Begin an object logically separate from the contents. */
+/* (I.e., an object in the resource file.) */
+long pdf_open_separate(gx_device_pdf * pdev, long id, pdf_resource_type_t type);
+long pdf_begin_separate(gx_device_pdf * pdev, pdf_resource_type_t type);
+
+/* Reserve object id. */
+void pdf_reserve_object_id(gx_device_pdf * pdev, pdf_resource_t *ppres, long id);
+
+/* Begin an aside (resource, annotation, ...). */
+int pdf_alloc_aside(gx_device_pdf * pdev, pdf_resource_t ** plist,
+ const gs_memory_struct_type_t * pst, pdf_resource_t **ppres,
+ long id);
+/* Begin an aside (resource, annotation, ...). */
+int pdf_begin_aside(gx_device_pdf * pdev, pdf_resource_t **plist,
+ const gs_memory_struct_type_t * pst,
+ pdf_resource_t **ppres, pdf_resource_type_t type);
+
+/* Begin a resource of a given type. */
+int pdf_begin_resource(gx_device_pdf * pdev, pdf_resource_type_t rtype,
+ gs_id rid, pdf_resource_t **ppres);
+
+/* Begin a resource body of a given type. */
+int pdf_begin_resource_body(gx_device_pdf * pdev, pdf_resource_type_t rtype,
+ gs_id rid, pdf_resource_t **ppres);
+
+/* Allocate a resource, but don't open the stream. */
+int pdf_alloc_resource(gx_device_pdf * pdev, pdf_resource_type_t rtype,
+ gs_id rid, pdf_resource_t **ppres, long id);
+
+/* Find same resource. */
+int pdf_find_same_resource(gx_device_pdf * pdev,
+ pdf_resource_type_t rtype, pdf_resource_t **ppres,
+ int (*eq)(gx_device_pdf * pdev, pdf_resource_t *pres0, pdf_resource_t *pres1));
+
+/* Find resource by resource id. */
+pdf_resource_t *pdf_find_resource_by_resource_id(gx_device_pdf * pdev,
+ pdf_resource_type_t rtype, gs_id id);
+
+/* Find a resource of a given type by gs_id. */
+pdf_resource_t *pdf_find_resource_by_gs_id(gx_device_pdf * pdev,
+ pdf_resource_type_t rtype,
+ gs_id rid);
+
+void pdf_drop_resources(gx_device_pdf * pdev, pdf_resource_type_t rtype,
+ int (*cond)(gx_device_pdf * pdev, pdf_resource_t *pres));
+
+/* Print resource statistics. */
+void pdf_print_resource_statistics(gx_device_pdf * pdev);
+
+/* Cancel a resource (do not write it into PDF). */
+int pdf_cancel_resource(gx_device_pdf * pdev, pdf_resource_t *pres,
+ pdf_resource_type_t rtype);
+
+/* Remove a resource. */
+void pdf_forget_resource(gx_device_pdf * pdev, pdf_resource_t *pres1,
+ pdf_resource_type_t rtype);
+
+/* Substitute a resource with a same one. */
+int pdf_substitute_resource(gx_device_pdf *pdev, pdf_resource_t **ppres,
+ pdf_resource_type_t rtype,
+ int (*eq)(gx_device_pdf *pdev, pdf_resource_t *pres0, pdf_resource_t *pres1),
+ bool write);
+
+/* Get the object id of a resource. */
+long pdf_resource_id(const pdf_resource_t *pres);
+
+/* End a separate object. */
+int pdf_end_separate(gx_device_pdf * pdev, pdf_resource_type_t type);
+
+/* End an aside. */
+int pdf_end_aside(gx_device_pdf * pdev, pdf_resource_type_t type);
+
+/* End a resource. */
+int pdf_end_resource(gx_device_pdf * pdev, pdf_resource_type_t type);
+
+/*
+ * Write the Cos objects for resources local to a content stream.
+ */
+int pdf_write_resource_objects(gx_device_pdf *pdev, pdf_resource_type_t rtype);
+
+/*
+ * Reverse resource chains.
+ * ps2write uses it with page resources.
+ * Assuming only the 0th chain contauns something.
+ */
+void pdf_reverse_resource_chain(gx_device_pdf *pdev, pdf_resource_type_t rtype);
+
+/*
+ * Free unnamed Cos objects for resources local to a content stream.
+ */
+int pdf_free_resource_objects(gx_device_pdf *pdev, pdf_resource_type_t rtype);
+
+/*
+ * Store the resource sets for a content stream (page or XObject).
+ * Sets page->{procsets, resource_ids[], fonts_id}.
+ */
+int pdf_store_page_resources(gx_device_pdf *pdev, pdf_page_t *page, bool clear_usage);
+
+/* Copy data from a temporary file to a stream. */
+void pdf_copy_data(stream *s, FILE *file, gs_offset_t count, stream_arcfour_state *ss);
+void pdf_copy_data_safe(stream *s, FILE *file, gs_offset_t position, long count);
+
+/* Add the encryption filter. */
+int pdf_begin_encrypt(gx_device_pdf * pdev, stream **s, gs_id object_id);
+/* Remove the encryption filter. */
+void pdf_end_encrypt(gx_device_pdf * pdev);
+/* Initialize encryption. */
+int pdf_encrypt_init(const gx_device_pdf * pdev, gs_id object_id, stream_arcfour_state *psarc4);
+
+/* ------ Pages ------ */
+
+/* Get or assign the ID for a page. */
+/* Returns 0 if the page number is out of range. */
+long pdf_page_id(gx_device_pdf * pdev, int page_num);
+
+/* Get the page structure for the current page. */
+pdf_page_t *pdf_current_page(gx_device_pdf *pdev);
+
+/* Get the dictionary object for the current page. */
+cos_dict_t *pdf_current_page_dict(gx_device_pdf *pdev);
+
+/* Open a page for writing. */
+int pdf_open_page(gx_device_pdf * pdev, pdf_context_t context);
+
+/* Go to the unclipped stream context. */
+int pdf_unclip(gx_device_pdf * pdev);
+
+/* Write saved page- or document-level information. */
+int pdf_write_saved_string(gx_device_pdf * pdev, gs_string * pstr);
+
+/* ------ Path drawing ------ */
+
+/* Store a copy of clipping path. */
+int pdf_remember_clip_path(gx_device_pdf * pdev, const gx_clip_path * pcpath);
+
+/* Test whether the clip path needs updating. */
+bool pdf_must_put_clip_path(gx_device_pdf * pdev, const gx_clip_path * pcpath);
+
+/* Write and update the clip path. */
+int pdf_put_clip_path(gx_device_pdf * pdev, const gx_clip_path * pcpath);
+
+/* ------ Masked image convertion ------ */
+
+typedef struct pdf_lcvd_s {
+ gx_device_memory mdev;
+ gx_device_memory *mask;
+ gx_device_pdf *pdev;
+ dev_t_proc_copy_color((*std_copy_color), gx_device);
+ dev_t_proc_fill_rectangle((*std_fill_rectangle), gx_device);
+ dev_t_proc_close_device((*std_close_device), gx_device);
+ dev_t_proc_get_clipping_box((*std_get_clipping_box), gx_device);
+ bool mask_is_empty;
+ bool path_is_empty;
+ bool mask_is_clean;
+ bool write_matrix;
+ bool has_background;
+ gs_matrix m;
+ gs_point path_offset;
+} pdf_lcvd_t;
+
+#define public_st_pdf_lcvd_t()\
+ gs_public_st_suffix_add2(st_pdf_lcvd_t, pdf_lcvd_t,\
+ "pdf_lcvd_t", pdf_lcvd_t_enum_ptrs,\
+ pdf_lcvd_t_reloc_ptrs, st_device_memory, mask, pdev)
+#define pdf_lcvd_t_max_ptrs (gx_device_memory_max_ptrs + 2)
+
+int pdf_setup_masked_image_converter(gx_device_pdf *pdev, gs_memory_t *mem, const gs_matrix *m, pdf_lcvd_t **pcvd,
+ bool need_mask, int x, int y, int w, int h, bool write_on_close);
+int pdf_dump_converted_image(gx_device_pdf *pdev, pdf_lcvd_t *cvd);
+void pdf_remove_masked_image_converter(gx_device_pdf *pdev, pdf_lcvd_t *cvd, bool need_mask);
+
+/* ------ Miscellaneous output ------ */
+
+#define PDF_MAX_PRODUCER 200 /* adhoc */
+/* Generate the default Producer string. */
+void pdf_store_default_Producer(char buf[PDF_MAX_PRODUCER]);
+
+/* Define the strings for filter names and parameters. */
+typedef struct pdf_filter_names_s {
+ const char *ASCII85Decode;
+ const char *ASCIIHexDecode;
+ const char *CCITTFaxDecode;
+ const char *DCTDecode;
+ const char *DecodeParms;
+ const char *Filter;
+ const char *FlateDecode;
+ const char *LZWDecode;
+ const char *RunLengthDecode;
+ const char *JBIG2Decode;
+ const char *JPXDecode;
+} pdf_filter_names_t;
+#define PDF_FILTER_NAMES\
+ "/ASCII85Decode", "/ASCIIHexDecode", "/CCITTFaxDecode",\
+ "/DCTDecode", "/DecodeParms", "/Filter", "/FlateDecode",\
+ "/LZWDecode", "/RunLengthDecode", "/JBIG2Decode", "/JPXDecode"
+#define PDF_FILTER_NAMES_SHORT\
+ "/A85", "/AHx", "/CCF", "/DCT", "/DP", "/F", "/Fl", "/LZW", "/RL", "/???", "/???"
+
+/* Write matrix values. */
+void pdf_put_matrix(gx_device_pdf *pdev, const char *before,
+ const gs_matrix *pmat, const char *after);
+
+/* Write a name, with escapes for unusual characters. */
+typedef int (*pdf_put_name_chars_proc_t)(stream *, const byte *, uint);
+pdf_put_name_chars_proc_t
+ pdf_put_name_chars_proc(const gx_device_pdf *pdev);
+int pdf_put_name_chars(const gx_device_pdf *pdev, const byte *nstr,
+ uint size);
+int pdf_put_name(const gx_device_pdf *pdev, const byte *nstr, uint size);
+
+/* Write a string in its shortest form ( () or <> ). */
+int pdf_put_string(const gx_device_pdf *pdev, const byte *str, uint size);
+
+/* Write a value, treating names specially. */
+int pdf_write_value(const gx_device_pdf *pdev, const byte *vstr, uint size, gs_id object_id);
+
+/* Store filters for a stream. */
+int pdf_put_filters(cos_dict_t *pcd, gx_device_pdf *pdev, stream *s,
+ const pdf_filter_names_t *pfn);
+
+/* Define a possibly encoded and compressed data stream. */
+typedef struct pdf_data_writer_s {
+ psdf_binary_writer binary;
+ gs_offset_t start;
+ gs_offset_t length_pos;
+ pdf_resource_t *pres;
+ gx_device_pdf *pdev; /* temporary for backward compatibility of pdf_end_data prototype. */
+ long length_id;
+ bool encrypted;
+} pdf_data_writer_t;
+/*
+ * Begin a data stream. The client has opened the object and written
+ * the << and any desired dictionary keys.
+ */
+#define DATA_STREAM_NOT_BINARY 0 /* data are text, not binary */
+#define DATA_STREAM_BINARY 1 /* data are binary */
+#define DATA_STREAM_COMPRESS 2 /* OK to compress data */
+#define DATA_STREAM_NOLENGTH 4 /* Skip the length reference and filter names writing. */
+#define DATA_STREAM_ENCRYPT 8 /* Encrypt data. */
+int pdf_begin_data_stream(gx_device_pdf *pdev, pdf_data_writer_t *pdw,
+ int options, gs_id object_id);
+int pdf_append_data_stream_filters(gx_device_pdf *pdev, pdf_data_writer_t *pdw,
+ int orig_options, gs_id object_id);
+/* begin_data = begin_data_binary with both options = true. */
+int pdf_begin_data(gx_device_pdf *pdev, pdf_data_writer_t *pdw);
+
+/* End a data stream. */
+int pdf_end_data(pdf_data_writer_t *pdw);
+
+/* ------ Functions ------ */
+
+/* Define the maximum size of a Function reference. */
+#define MAX_REF_CHARS ((sizeof(long) * 8 + 2) / 3)
+
+/*
+ * Create a Function object with or without range scaling. Scaling means
+ * that if x[i] is the i'th output value from the original Function,
+ * the i'th output value from the Function object will be (x[i] -
+ * ranges[i].rmin) / (ranges[i].rmax - ranges[i].rmin). Note that this is
+ * the inverse of the scaling convention for Functions per se.
+ */
+#ifndef gs_function_DEFINED
+typedef struct gs_function_s gs_function_t;
+# define gs_function_DEFINED
+#endif
+int pdf_function(gx_device_pdf *pdev, const gs_function_t *pfn,
+ cos_value_t *pvalue);
+int pdf_function_scaled(gx_device_pdf *pdev, const gs_function_t *pfn,
+ const gs_range_t *pranges, cos_value_t *pvalue);
+
+/* Write a Function object, returning its object ID. */
+int pdf_write_function(gx_device_pdf *pdev, const gs_function_t *pfn,
+ long *pid);
+
+/* ------ Fonts ------ */
+
+/* Write a FontBBox dictionary element. */
+int pdf_write_font_bbox(gx_device_pdf *pdev, const gs_int_rect *pbox);
+int pdf_write_font_bbox_float(gx_device_pdf *pdev, const gs_rect *pbox);
+
+/* ---------------- Exported by gdevpdfm.c ---------------- */
+
+/*
+ * Define the type for a pdfmark-processing procedure.
+ * If nameable is false, the objname argument is always NULL.
+ */
+#define pdfmark_proc(proc)\
+ int proc(gx_device_pdf *pdev, gs_param_string *pairs, uint count,\
+ const gs_matrix *pctm, const gs_param_string *objname)
+
+/* Compare a C string and a gs_param_string. */
+bool pdf_key_eq(const gs_param_string * pcs, const char *str);
+
+/* Scan an integer out of a parameter string. */
+int pdfmark_scan_int(const gs_param_string * pstr, int *pvalue);
+
+/* Process a pdfmark (called from pdf_put_params). */
+int pdfmark_process(gx_device_pdf * pdev, const gs_param_string_array * pma);
+
+/* Close the current level of the outline tree. */
+int pdfmark_close_outline(gx_device_pdf * pdev);
+
+/* Close the pagelabel numtree. */
+int pdfmark_end_pagelabels(gx_device_pdf * pdev);
+
+/* Finish writing an article. */
+int pdfmark_write_article(gx_device_pdf * pdev, const pdf_article_t * part);
+
+/* ---------------- Exported by gdevpdfr.c ---------------- */
+
+/* Test whether an object name has valid syntax, {name}. */
+bool pdf_objname_is_valid(const byte *data, uint size);
+
+/*
+ * Look up a named object. Return e_rangecheck if the syntax is invalid,
+ * e_undefined if no object by that name exists.
+ */
+int pdf_find_named(gx_device_pdf * pdev, const gs_param_string * pname,
+ cos_object_t **ppco);
+
+/*
+ * Create a named object. id = -1L means do not assign an id. pname = 0
+ * means just create the object, do not name it.
+ */
+int pdf_create_named(gx_device_pdf *pdev, const gs_param_string *pname,
+ cos_type_t cotype, cos_object_t **ppco, long id);
+int pdf_create_named_dict(gx_device_pdf *pdev, const gs_param_string *pname,
+ cos_dict_t **ppcd, long id);
+
+/*
+ * Look up a named object as for pdf_find_named. If the object does not
+ * exist, create it (as a dictionary if it is one of the predefined names
+ * {ThisPage}, {NextPage}, {PrevPage}, or {Page<#>}, otherwise as a
+ * generic object) and return 1.
+ */
+int pdf_refer_named(gx_device_pdf *pdev, const gs_param_string *pname,
+ cos_object_t **ppco);
+
+/*
+ * Look up a named object as for pdf_refer_named. If the object already
+ * exists and is not simply a forward reference, return e_rangecheck;
+ * if it exists as a forward reference, set its type and return 0;
+ * otherwise, create the object with the given type and return 1.
+ * pname = 0 is allowed: in this case, simply create the object.
+ */
+int pdf_make_named(gx_device_pdf * pdev, const gs_param_string * pname,
+ cos_type_t cotype, cos_object_t **ppco, bool assign_id);
+int pdf_make_named_dict(gx_device_pdf * pdev, const gs_param_string * pname,
+ cos_dict_t **ppcd, bool assign_id);
+
+/*
+ * Look up a named object as for pdf_refer_named. If the object does not
+ * exist, or is a forward reference, return e_undefined; if the object
+ * exists has the wrong type, return e_typecheck.
+ */
+int pdf_get_named(gx_device_pdf * pdev, const gs_param_string * pname,
+ cos_type_t cotype, cos_object_t **ppco);
+
+/*
+ * Push the current local namespace onto the namespace stack, and reset it
+ * to an empty namespace.
+ */
+int pdf_push_namespace(gx_device_pdf *pdev);
+
+/*
+ * Pop the top local namespace from the namespace stack. Return an error if
+ * the stack is empty.
+ */
+int pdf_pop_namespace(gx_device_pdf *pdev);
+
+/*
+ * Scan a string for a token. <<, >>, [, and ] are treated as tokens.
+ * Return 1 if a token was scanned, 0 if we reached the end of the string,
+ * or an error. On a successful return, the token extends from *ptoken up
+ * to but not including *pscan.
+ */
+int pdf_scan_token(const byte **pscan, const byte * end, const byte **ptoken);
+
+/*
+ * Scan a possibly composite token: arrays and dictionaries are treated as
+ * single tokens.
+ */
+int pdf_scan_token_composite(const byte **pscan, const byte * end,
+ const byte **ptoken);
+
+/* Replace object names with object references in a (parameter) string. */
+int pdf_replace_names(gx_device_pdf *pdev, const gs_param_string *from,
+ gs_param_string *to);
+
+/* ================ Text module procedures ================ */
+
+/* ---------------- Exported by gdevpdfw.c ---------------- */
+
+/* For gdevpdf.c */
+
+int write_font_resources(gx_device_pdf *pdev, pdf_resource_list_t *prlist);
+
+/* ---------------- Exported by gdevpdft.c ---------------- */
+
+/* For gdevpdf.c */
+
+pdf_text_data_t *pdf_text_data_alloc(gs_memory_t *mem);
+void pdf_set_text_state_default(pdf_text_state_t *pts);
+void pdf_text_state_copy(pdf_text_state_t *pts_to, pdf_text_state_t *pts_from);
+void pdf_reset_text_page(pdf_text_data_t *ptd);
+void pdf_reset_text_state(pdf_text_data_t *ptd);
+void pdf_close_text_page(gx_device_pdf *pdev);
+int pdf_get_stoted_text_size(pdf_text_state_t *state);
+
+/* For gdevpdfb.c */
+
+int pdf_char_image_y_offset(const gx_device_pdf *pdev, int x, int y, int h);
+
+/* Begin a CharProc for an embedded (bitmap) font. */
+int pdf_begin_char_proc(gx_device_pdf * pdev, int w, int h, int x_width,
+ int y_offset, int x_offset, gs_id id, pdf_char_proc_t **ppcp,
+ pdf_stream_position_t * ppos);
+
+/* End a CharProc. */
+int pdf_end_char_proc(gx_device_pdf * pdev, pdf_stream_position_t * ppos);
+
+/* Put out a reference to an image as a character in an embedded font. */
+int pdf_do_char_image(gx_device_pdf * pdev, const pdf_char_proc_t * pcp,
+ const gs_matrix * pimat);
+
+/* Start charproc accumulation for a Type 3 font. */
+int pdf_start_charproc_accum(gx_device_pdf *pdev);
+/* Install charproc accumulator for a Type 3 font. */
+int pdf_set_charproc_attrs(gx_device_pdf *pdev, gs_font *font, double *pw, int narg,
+ gs_text_cache_control_t control, gs_char ch, bool scale_100);
+/* Complete charproc accumulation for aType 3 font. */
+int pdf_end_charproc_accum(gx_device_pdf *pdev, gs_font *font, const pdf_char_glyph_pairs_t *cgp,
+ gs_glyph glyph, gs_char output_char_code, const gs_const_string *gnstr);
+/* Open a stream object in the temporary file. */
+int pdf_open_aside(gx_device_pdf *pdev, pdf_resource_type_t rtype,
+ gs_id id, pdf_resource_t **ppres, bool reserve_object_id, int options);
+
+/* Close a stream object in the temporary file. */
+int pdf_close_aside(gx_device_pdf *pdev);
+
+/* Enter the substream accumulation mode. */
+int pdf_enter_substream(gx_device_pdf *pdev, pdf_resource_type_t rtype,
+ gs_id id, pdf_resource_t **ppres, bool reserve_object_id, bool compress);
+
+/* Exit the substream accumulation mode. */
+int pdf_exit_substream(gx_device_pdf *pdev);
+/* Add procsets to substream Resources. */
+int pdf_add_procsets(cos_dict_t *pcd, pdf_procset_t procsets);
+/* Add a resource to substream Resources. */
+int pdf_add_resource(gx_device_pdf *pdev, cos_dict_t *pcd, const char *key, pdf_resource_t *pres);
+
+/* For gdevpdfu.c */
+
+int pdf_from_stream_to_text(gx_device_pdf *pdev);
+int pdf_from_string_to_text(gx_device_pdf *pdev);
+void pdf_close_text_contents(gx_device_pdf *pdev);
+
+int gdev_pdf_get_param(gx_device *dev, char *Param, void *list);
+#endif /* gdevpdfx_INCLUDED */
diff --git a/devices/vector/gdevpdt.c b/devices/vector/gdevpdt.c
new file mode 100644
index 000000000..e3e557173
--- /dev/null
+++ b/devices/vector/gdevpdt.c
@@ -0,0 +1,66 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Miscellaneous external entry points for pdfwrite text */
+#include "gx.h"
+#include "memory_.h"
+#include "gdevpdfx.h"
+#include "gdevpdtx.h"
+#include "gdevpdtf.h"
+#include "gdevpdti.h"
+
+/* GC descriptors */
+private_st_pdf_text_data();
+
+/* ---------------- Initialization ---------------- */
+
+/*
+ * Allocate and initialize the text data structure.
+ */
+pdf_text_data_t *
+pdf_text_data_alloc(gs_memory_t *mem)
+{
+ pdf_text_data_t *ptd =
+ gs_alloc_struct(mem, pdf_text_data_t, &st_pdf_text_data,
+ "pdf_text_data_alloc");
+ pdf_outline_fonts_t *pofs = pdf_outline_fonts_alloc(mem);
+ pdf_bitmap_fonts_t *pbfs = pdf_bitmap_fonts_alloc(mem);
+ pdf_text_state_t *pts = pdf_text_state_alloc(mem);
+
+ if (pts == 0 || pbfs == 0 || pofs == 0 || ptd == 0) {
+ gs_free_object(mem, pts, "pdf_text_data_alloc");
+ gs_free_object(mem, pbfs, "pdf_text_data_alloc");
+ gs_free_object(mem, pofs, "pdf_text_data_alloc");
+ gs_free_object(mem, ptd, "pdf_text_data_alloc");
+ return 0;
+ }
+ memset(ptd, 0, sizeof(*ptd));
+ ptd->outline_fonts = pofs;
+ ptd->bitmap_fonts = pbfs;
+ ptd->text_state = pts;
+ return ptd;
+}
+
+int text_data_free(gs_memory_t *mem, pdf_text_data_t *ptd)
+{
+ gs_free_object(mem, ptd->outline_fonts->standard_fonts, "Free text Outline standard fonts");;
+ gs_free_object(mem, ptd->outline_fonts, "Free text Outline fonts");;
+ gs_free_object(mem, ptd->bitmap_fonts, "Free text Bitmap fotns");;
+ gs_free_object(mem, ptd->text_state, "Free text state");;
+ gs_free_object(mem, ptd, "Free text");
+
+ return 0;
+}
diff --git a/devices/vector/gdevpdt.h b/devices/vector/gdevpdt.h
new file mode 100644
index 000000000..8694e36d3
--- /dev/null
+++ b/devices/vector/gdevpdt.h
@@ -0,0 +1,99 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Interface for pdfwrite text and fonts */
+
+#ifndef gdevpdt_INCLUDED
+# define gdevpdt_INCLUDED
+
+/*
+ * This file defines a largely opaque interface to the text and font
+ * handling code for pdfwrite. This is the only file that pdfwrite code
+ * outside the text and font handling subsystem (pdftext.dev) should
+ * #include.
+ *
+ * The declarations in this file deliberately duplicate declarations in
+ * various other header files of the pdfwrite text/font subsystem.
+ * This allows the compiler to check them for consistency.
+ */
+
+/* ================ Procedures ================ */
+
+/* ---------------- Utility (for gdevpdf.c) ---------------- */
+
+/*
+ * Allocate and initialize text state bookkeeping.
+ */
+pdf_text_state_t *pdf_text_state_alloc(gs_memory_t *mem);
+
+/*
+ * Allocate and initialize the text data structure.
+ */
+pdf_text_data_t *pdf_text_data_alloc(gs_memory_t *mem); /* gdevpdts.h */
+
+int text_data_free(gs_memory_t *mem, pdf_text_data_t *ptd);
+
+/*
+ * Reset the text state at the beginning of the page.
+ */
+void pdf_reset_text_page(pdf_text_data_t *ptd); /* gdevpdts.h */
+
+/*
+ * Reset the text state after a grestore.
+ */
+void pdf_reset_text_state(pdf_text_data_t *ptd); /* gdevpdts.h */
+
+/*
+ * Update text state at the end of a page.
+ */
+void pdf_close_text_page(gx_device_pdf *pdev); /* gdevpdti.h */
+
+/* ---------------- Contents state (for gdevpdfu.c) ---------------- */
+
+/*
+ * Transition from stream context to text context.
+ */
+int pdf_from_stream_to_text(gx_device_pdf *pdev); /* gdevpdts.h */
+
+/*
+ * Transition from string context to text context.
+ */
+int pdf_from_string_to_text(gx_device_pdf *pdev); /* gdevpdts.h */
+
+/*
+ * Close the text aspect of the current contents part.
+ */
+void pdf_close_text_contents(gx_device_pdf *pdev); /* gdevpdts.h */
+
+/* ---------------- Bitmap fonts (for gdevpdfb.c) ---------------- */
+
+/* Return the Y offset for a bitmap character image. */
+int pdf_char_image_y_offset(const gx_device_pdf *pdev, int x, int y, int h);/* gdevpdti.h */
+
+/* Begin a CharProc for an embedded (bitmap) font. */
+int pdf_begin_char_proc(gx_device_pdf * pdev, int w, int h, int x_width,
+ int y_offset, int x_offset, gs_id id, pdf_char_proc_t **ppcp,
+ pdf_stream_position_t * ppos); /* gdevpdti.h */
+
+/* End a CharProc. */
+int pdf_end_char_proc(gx_device_pdf * pdev,
+ pdf_stream_position_t * ppos); /* gdevpdti.h */
+
+/* Put out a reference to an image as a character in an embedded font. */
+int pdf_do_char_image(gx_device_pdf * pdev, const pdf_char_proc_t * pcp,
+ const gs_matrix * pimat); /* gdevpdti.h */
+
+#endif /* gdevpdt_INCLUDED */
diff --git a/devices/vector/gdevpdtb.c b/devices/vector/gdevpdtb.c
new file mode 100644
index 000000000..fdb304001
--- /dev/null
+++ b/devices/vector/gdevpdtb.c
@@ -0,0 +1,824 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* BaseFont implementation for pdfwrite */
+#include "memory_.h"
+#include "ctype_.h"
+#include "gx.h"
+#include "gserrors.h"
+#include "gsutil.h" /* for bytes_compare */
+#include "gxfcid.h"
+#include "gxfcopy.h"
+#include "gxfont.h" /* for gxfont42.h */
+#include "gxfont42.h"
+#include "gdevpsf.h"
+#include "gdevpdfx.h"
+#include "gdevpdfo.h"
+#include "gdevpdtb.h"
+#include "gdevpdtf.h"
+#include "smd5.h"
+#include "gxfcache.h" /* for gs_purge_font_from_char_caches_completely */
+/*
+ * Adobe's Distiller Parameters documentation for Acrobat Distiller 5
+ * says that all fonts other than Type 1 are always subsetted; the
+ * documentation for Distiller 4 says that fonts other than Type 1 and
+ * TrueType are subsetted. We do the latter, except that we always
+ * subset large TrueType fonts.
+ */
+#define MAX_NO_SUBSET_GLYPHS 4096 /* arbitrary */
+
+/* ---------------- Private ---------------- */
+
+private_st_pdf_base_font();
+gs_private_st_basic(st_pdf_base_font, pdf_base_font_t, "pdf_base_font_t",\
+ pdf_base_font_ptrs, pdf_base_font_data);
+
+#define SUBSET_PREFIX_SIZE 7 /* XXXXXX+ */
+
+typedef struct pdf_base14_font_info_s {
+ const char *urwname;
+ const char *stdname;
+} pdf_base14_font_info_t;
+
+static const pdf_base14_font_info_t base14_font_info[] = {
+ /* Standard mapping of URW fonts */
+ {"NimbusMonL-Regu", "Courier" },
+ {"NimbusMonL-Bold", "Courier-Bold" },
+ {"NimbusMonL-ReguObli", "Courier-Oblique" },
+ {"NimbusMonL-BoldObli", "Courier-BoldOblique" },
+ {"NimbusSanL-Regu", "Helvetica" },
+ {"NimbusSanL-Bold", "Helvetica-Bold" },
+ {"NimbusSanL-ReguItal", "Helvetica-Oblique" },
+ {"NimbusSanL-BoldItal", "Helvetica-BoldOblique"},
+ {"StandardSymL", "Symbol" },
+ {"NimbusRomNo9L-Regu", "Times-Roman" },
+ {"NimbusRomNo9L-Medi", "Times-Bold" },
+ {"NimbusRomNo9L-ReguItal","Times-Italic" },
+ {"NimbusRomNo9L-MediItal","Times-BoldItalic" },
+ {"Dingbats", "ZapfDingbats" },
+ /* A few other mappings of URW fonts */
+ {"NimbusMono-Reg", "Courier" },
+ {"NimbusMono-Bol", "Courier-Bold" },
+ {"NimbusMono-Ita", "Courier-Oblique" },
+ {"NimbusMono-BolIta", "Courier-BoldOblique" },
+ {"NimbusSan-Reg", "Helvetica" },
+ {"NimbusSan-Bol", "Helvetica-Bold" },
+ {"NimbusSan-Ita", "Helvetica-Oblique" },
+ {"NimbusSan-BolIta", "Helvetica-BoldOblique"},
+ {"A030-Reg", "Helvetica" },
+ {"A030-Bol", "Helvetica-Bold" },
+ {"A030-Ita", "Helvetica-Oblique" },
+ {"A030-BolIta", "Helvetica-BoldOblique"},
+ {"NimbusSanNo2-Reg", "Helvetica" },
+ {"NimbusSanNo2-Bol", "Helvetica-Bold" },
+ {"NimbusSanNo2-Ita", "Helvetica-Oblique" },
+ {"NimbusSanNo2-BolIta", "Helvetica-BoldOblique"},
+ {"NimbusRomanNo4-Lig", "Times-Roman" },
+ {"NimbusRomanNo4-Bol", "Times-Bold" },
+ {"NimbusRomanNo4-LigIta", "Times-Italic" },
+ {"NimbusRomanNo4-BolIta", "Times-BoldItalic" },
+ {"NimbusRomanNo9-Reg", "Times-Roman" },
+ {"NimbusRomanNo9-Med", "Times-Bold" },
+ {"NimbusRomanNo9-Ita", "Times-Italic" },
+ {"NimbusRomanNo9-MedIta", "Times-BoldItalic" },
+ {"NimbusRom-Reg", "Times-Roman" },
+ {"NimbusRom-Med", "Times-Bold" },
+ {"NimbusRom-Ita", "Times-Italic" },
+ {"NimbusRom-MedIta", "Times-BoldItalic" },
+ {"NimbusRomNo9-Reg", "Times-Roman" },
+ {"NimbusRomNo9-Bol", "Times-Bold" },
+ {"NimbusRomNo9-Ita", "Times-Italic" },
+ {"NimbusRomNo9-MedIta", "Times-BoldItalic" },
+ {0}
+};
+
+/* Given a pointer to a font name, return a pointer to an
+ * equivalent base 14 font name, of NULL if there is no
+ * equivalent.
+ */
+const char *pdf_find_base14_name(const byte *str, uint size)
+{
+ const pdf_base14_font_info_t *ppsf;
+
+ for (ppsf = base14_font_info; ppsf->urwname; ++ppsf)
+ if (!memcmp(ppsf->urwname, (const char *)str, size))
+ return ppsf->stdname;
+ return NULL;
+}
+
+
+/*
+ * Determine whether a font is a subset font by examining the name.
+ */
+bool
+pdf_has_subset_prefix(const byte *str, uint size)
+{
+ int i;
+
+ if (size < SUBSET_PREFIX_SIZE || str[SUBSET_PREFIX_SIZE - 1] != '+')
+ return false;
+ for (i = 0; i < SUBSET_PREFIX_SIZE - 1; ++i)
+ if ((uint)(str[i] - 'A') >= 26)
+ return false;
+ return true;
+}
+
+static inline ulong
+hash(ulong v, int index, ushort w)
+{
+ return v * 3141592653u + w;
+}
+
+/*
+ * Add the XXXXXX+ prefix for a subset font.
+ */
+int
+pdf_add_subset_prefix(const gx_device_pdf *pdev, gs_string *pstr, byte *used, int count, char *md5_hash)
+{
+ uint size = pstr->size;
+ byte *data = gs_resize_string(pdev->pdf_memory, pstr->data, size,
+ size + SUBSET_PREFIX_SIZE,
+ "pdf_add_subset_prefix");
+ int len = (count + 7) / 8;
+ int len0 = len & ~(sizeof(ushort) - 1);
+ ulong v = 0;
+ int i;
+
+ if (data == 0)
+ return_error(gs_error_VMerror);
+
+ if (md5_hash) {
+ for (i = 0; i < 8; i += sizeof(ushort)) {
+ v = hash(v, i, (*(md5_hash + i) | *(md5_hash + i + 1) << 8));
+ }
+ }
+
+ /* Hash the 'used' array. */
+ for (i = 0; i < len0; i += sizeof(ushort))
+ v = hash(v, i, *(ushort *)(used + i));
+ for (; i < len; i++)
+ v = hash(v, i, used[i]);
+
+ memmove(data + SUBSET_PREFIX_SIZE, data, size);
+ for (i = 0; i < SUBSET_PREFIX_SIZE - 1; ++i, v /= 26)
+ data[i] = 'A' + (v % 26);
+ data[SUBSET_PREFIX_SIZE - 1] = '+';
+ pstr->data = data;
+ pstr->size = size + SUBSET_PREFIX_SIZE;
+ return 0;
+}
+
+/* Finish writing FontFile* data. */
+static int
+pdf_end_fontfile(gx_device_pdf *pdev, pdf_data_writer_t *pdw)
+{
+ /* We would like to call pdf_end_data,
+ but we don't want to write the object to the output file now. */
+ return pdf_close_aside(pdw->pdev);
+}
+
+static int copied_font_notify(void *proc_data, void *event_data)
+{
+ return gs_purge_font_from_char_caches_completely((gs_font *)proc_data);
+}
+
+/* ---------------- Public ---------------- */
+/*
+ * Allocate and initialize a base font structure, making the required
+ * stable copy/ies of the gs_font. Note that this removes any XXXXXX+
+ * font name prefix from the copy. If is_standard is true, the copy is
+ * a complete one, and adding glyphs or Encoding entries is not allowed.
+ */
+int
+pdf_base_font_alloc(gx_device_pdf *pdev, pdf_base_font_t **ppbfont,
+ gs_font_base *font, const gs_matrix *orig_matrix,
+ bool is_standard)
+{
+ gs_memory_t *mem = pdev->pdf_memory;
+ gs_font *copied;
+ gs_font *complete;
+ pdf_base_font_t *pbfont =
+ gs_alloc_struct(mem, pdf_base_font_t,
+ &st_pdf_base_font, "pdf_base_font_alloc");
+ const gs_font_name *pfname = &font->font_name;
+ gs_const_string font_name;
+ char fnbuf[2*sizeof(long) + 3]; /* .F########\0 */
+ int code, reserve_glyphs = -1;
+
+ if (pbfont == 0)
+ return_error(gs_error_VMerror);
+ memset(pbfont, 0, sizeof(*pbfont));
+ switch (font->FontType) {
+ case ft_encrypted:
+ case ft_encrypted2:
+ {
+ int index, count;
+ gs_glyph glyph;
+
+ for (index = 0, count = 0;
+ (font->procs.enumerate_glyph((gs_font *)font, &index,
+ GLYPH_SPACE_NAME, &glyph),
+ index != 0);
+ )
+ ++count;
+ pbfont->num_glyphs = count;
+ pbfont->do_subset = (is_standard ? DO_SUBSET_NO : DO_SUBSET_UNKNOWN);
+ }
+ /* If we find an excessively large type 1 font we won't be able to emit
+ * a complete copy. Instead we will emit multiple subsets. Detect that here
+ * and only reserve enough space in the font copy for the maximum subset
+ * glyphs, 257.
+ * This also prevents us making a 'complete' copy of the font below. NB the
+ * value 2048 is merely a guess, intended to prevent copying very large fonts.
+ */
+ if(pbfont->num_glyphs > 2048 && !is_standard) {
+ reserve_glyphs = 257;
+ if(pbfont->do_subset != DO_SUBSET_NO){
+ char buf[gs_font_name_max + 1];
+ int l = min(font->font_name.size, sizeof(buf) - 1);
+
+ memcpy(buf, font->font_name.chars, l);
+ buf[l] = 0;
+ emprintf1(pdev->memory,
+ "Can't embed the complete font %s as it is too large, embedding a subset.\n",
+ buf);
+ }
+ }
+ break;
+ case ft_TrueType:
+ pbfont->num_glyphs = ((gs_font_type42 *)font)->data.trueNumGlyphs;
+ pbfont->do_subset =
+ (pbfont->num_glyphs <= MAX_NO_SUBSET_GLYPHS ?
+ DO_SUBSET_UNKNOWN : DO_SUBSET_YES);
+ break;
+ case ft_CID_encrypted:
+ pbfont->num_glyphs = ((gs_font_cid0 *)font)->cidata.common.CIDCount;
+ goto cid;
+ case ft_CID_TrueType:
+ pbfont->num_glyphs = ((gs_font_cid2 *)font)->cidata.common.CIDCount;
+ cid:
+ pbfont->do_subset = DO_SUBSET_YES;
+ pbfont->CIDSet =
+ gs_alloc_bytes(mem, (pbfont->num_glyphs + 7) / 8,
+ "pdf_base_font_alloc(CIDSet)");
+ if (pbfont->CIDSet == 0) {
+ code = gs_note_error(gs_error_VMerror);
+ goto fail;
+ }
+ pbfont->CIDSetLength = (pbfont->num_glyphs + 7) / 8;
+ memset(pbfont->CIDSet, 0, (pbfont->num_glyphs + 7) / 8);
+ break;
+ default:
+ code = gs_note_error(gs_error_rangecheck);
+ goto fail;
+ }
+
+ code = gs_copy_font((gs_font *)font, orig_matrix, mem, &copied, reserve_glyphs);
+ if (code < 0)
+ goto fail;
+ gs_notify_register(&copied->notify_list, copied_font_notify, copied);
+ {
+ /*
+ * Adobe Technical Note # 5012 "The Type 42 Font Format Specification" says :
+ *
+ * There is a known bug in the TrueType rasterizer included in versions of the
+ * PostScript interpreter previous to version 2013. The problem is that the
+ * translation components of the FontMatrix, as used as an argument to the
+ * definefont or makefont operators, are ignored. Translation of user space is
+ * not affected by this bug.
+ *
+ * Besides that, we found that Adobe Acrobat Reader 4 and 5 ignore
+ * FontMatrix.ty .
+ */
+ copied->FontMatrix.tx = copied->FontMatrix.ty = 0;
+ }
+
+ if (pbfont->do_subset != DO_SUBSET_YES && reserve_glyphs == -1) {
+ /* The only possibly non-subsetted fonts are Type 1/2 and Type 42. */
+ if (is_standard)
+ complete = copied;
+ else {
+ code = gs_copy_font((gs_font *)font, &font->FontMatrix, mem, &complete, -1);
+ if (code < 0)
+ goto fail;
+ }
+ code = gs_copy_font_complete((gs_font *)font, complete);
+ if (code < 0 && pbfont->do_subset == DO_SUBSET_NO) {
+ char buf[gs_font_name_max + 1];
+ int l = min(copied->font_name.size, sizeof(buf) - 1);
+
+ memcpy(buf, copied->font_name.chars, l);
+ buf[l] = 0;
+ emprintf1(pdev->memory,
+ "Can't embed the complete font %s due to font error.\n",
+ buf);
+ goto fail;
+ }
+ if (code < 0) {
+ /* A font error happened, but it may be caused by a glyph,
+ which is not used in the document. Continue with subsetting the font.
+ If the failed glyph will be used in the document,
+ another error will hgappen when the glyph is used.
+ */
+ gs_free_copied_font(complete);
+ complete = copied;
+ }
+ } else
+ complete = copied;
+ pbfont->copied = (gs_font_base *)copied;
+ pbfont->complete = (gs_font_base *)complete;
+
+ /* replace the font cache of the copied fonts with our own font cache
+ * this is required for PCL, see 'pdf_free_pdf_font_cache' in gdevpdf.c
+ * for further details.
+ */
+ pbfont->copied->dir = pbfont->complete->dir = pdev->pdf_font_dir;
+ pbfont->is_standard = is_standard;
+ if (pfname->size > 0) {
+ font_name.data = pfname->chars;
+ font_name.size = pfname->size;
+ while (pdf_has_subset_prefix(font_name.data, font_name.size)) {
+ /* Strip off an existing subset prefix. */
+ font_name.data += SUBSET_PREFIX_SIZE;
+ font_name.size -= SUBSET_PREFIX_SIZE;
+ }
+ } else {
+ gs_sprintf(fnbuf, ".F%lx", (ulong)copied);
+ font_name.data = (byte *)fnbuf;
+ font_name.size = strlen(fnbuf);
+ }
+ pbfont->font_name.data =
+ gs_alloc_string(mem, font_name.size, "pdf_base_font_alloc(font_name)");
+ if (pbfont->font_name.data == 0)
+ goto fail;
+ memcpy(pbfont->font_name.data, font_name.data, font_name.size);
+ pbfont->font_name.size = font_name.size;
+ *ppbfont = pbfont;
+ return 0;
+ fail:
+ gs_free_object(mem, pbfont, "pdf_base_font_alloc");
+ return code;
+}
+
+/*
+ * Return a reference to the name of a base font. This name is guaranteed
+ * not to have a XXXXXX+ prefix. The client may change the name at will,
+ * but must not add a XXXXXX+ prefix.
+ */
+gs_string *
+pdf_base_font_name(pdf_base_font_t *pbfont)
+{
+ return &pbfont->font_name;
+}
+
+/*
+ * Return the (copied, subset) font associated with a base font.
+ * This procedure probably shouldn't exist....
+ */
+gs_font_base *
+pdf_base_font_font(const pdf_base_font_t *pbfont, bool complete)
+{
+ return (complete ? pbfont->complete : pbfont->copied);
+}
+
+/*
+ * Check for subset font.
+ */
+bool
+pdf_base_font_is_subset(const pdf_base_font_t *pbfont)
+{
+ return pbfont->do_subset == DO_SUBSET_YES;
+}
+
+/*
+ * Drop the copied complete font associated with a base font.
+ */
+void
+pdf_base_font_drop_complete(pdf_base_font_t *pbfont)
+{
+ pbfont->complete = NULL;
+}
+
+/*
+ * Copy a glyph (presumably one that was just used) into a saved base
+ * font. Note that it is the client's responsibility to determine that
+ * the source font is compatible with the target font. (Normally they
+ * will be the same.)
+ */
+int
+pdf_base_font_copy_glyph(pdf_base_font_t *pbfont, gs_glyph glyph,
+ gs_font_base *font)
+{
+ int code =
+ gs_copy_glyph_options((gs_font *)font, glyph,
+ (gs_font *)pbfont->copied,
+ (pbfont->is_standard ? COPY_GLYPH_NO_NEW : 0));
+
+ if (code < 0)
+ return code;
+ if (pbfont->CIDSet != 0 &&
+ (uint)(glyph - GS_MIN_CID_GLYPH) < pbfont->num_glyphs
+ ) {
+ uint cid = glyph - GS_MIN_CID_GLYPH;
+
+ pbfont->CIDSet[cid >> 3] |= 0x80 >> (cid & 7);
+ }
+ return 0;
+}
+
+/*
+ * Determine whether a font should be subsetted.
+ */
+bool
+pdf_do_subset_font(gx_device_pdf *pdev, pdf_base_font_t *pbfont, gs_id rid)
+{
+ gs_font_base *copied = pbfont->copied;
+
+ /*
+ * If the decision has not been made already, determine whether or not
+ * to subset the font.
+ */
+ if (pbfont->do_subset == DO_SUBSET_UNKNOWN) {
+ int max_pct = pdev->params.MaxSubsetPct;
+ bool do_subset = pdev->params.SubsetFonts && max_pct > 0;
+
+ if (do_subset && max_pct < 100) {
+ /* We want to subset iff used <= total * MaxSubsetPct / 100. */
+ do_subset = false;
+ if (max_pct > 0) {
+ int max_subset_used = pbfont->num_glyphs * max_pct / 100;
+ int used, index;
+ gs_glyph ignore_glyph;
+
+ do_subset = true;
+ for (index = 0, used = 0;
+ (copied->procs.enumerate_glyph((gs_font *)copied,
+ &index, GLYPH_SPACE_INDEX,
+ &ignore_glyph), index != 0);
+ )
+ if (++used > max_subset_used) {
+ do_subset = false;
+ break;
+ }
+ }
+ }
+ pbfont->do_subset = (do_subset ? DO_SUBSET_YES : DO_SUBSET_NO);
+ }
+ return (pbfont->do_subset == DO_SUBSET_YES);
+}
+
+/*
+ * Write the FontFile entry for an embedded font, /FontFile<n> # # R.
+ */
+int
+pdf_write_FontFile_entry(gx_device_pdf *pdev, pdf_base_font_t *pbfont)
+{
+ stream *s = pdev->strm;
+ const char *FontFile_key;
+
+ switch (pbfont->copied->FontType) {
+ case ft_TrueType:
+ case ft_CID_TrueType:
+ FontFile_key = "/FontFile2";
+ break;
+ default: /* Type 1/2, CIDFontType 0 */
+ if (pdev->ResourcesBeforeUsage)
+ FontFile_key = "/FontFile";
+ else
+ FontFile_key = "/FontFile3";
+ }
+ stream_puts(s, FontFile_key);
+ pprintld1(s, " %ld 0 R", pbfont->FontFile->id);
+ return 0;
+}
+
+/*
+ * Adjust font name for Acrobat Reader 3.
+ */
+static int
+pdf_adjust_font_name(gx_device_pdf *pdev, long id, pdf_base_font_t *pbfont)
+{
+ /*
+ * In contradiction with previous version of pdfwrite,
+ * this always adds a suffix. We don't check the original name
+ * for uniquity bacause the layered architecture
+ * (see gdevpdtx.h) doesn't provide an easy access for
+ * related information.
+ */
+ int i;
+ byte *chars = (byte *)pbfont->font_name.data; /* break 'const' */
+ byte *data;
+ uint size = pbfont->font_name.size;
+ char suffix[sizeof(long) * 2 + 2];
+ uint suffix_size;
+
+#define SUFFIX_CHAR '~'
+ /*
+ * If the name looks as though it has one of our unique suffixes,
+ * remove the suffix.
+ */
+ for (i = size;
+ i > 0 && isxdigit(chars[i - 1]);
+ --i)
+ DO_NOTHING;
+ if (i < size && i > 0 && chars[i - 1] == SUFFIX_CHAR) {
+ do {
+ --i;
+ } while (i > 0 && chars[i - 1] == SUFFIX_CHAR);
+ size = i + 1;
+ }
+ /* Create a unique name. */
+ gs_sprintf(suffix, "%c%lx", SUFFIX_CHAR, id);
+ suffix_size = strlen(suffix);
+ data = gs_resize_string(pdev->pdf_memory, chars, size,
+ size + suffix_size,
+ "pdf_adjust_font_name");
+ if (data == 0)
+ return_error(gs_error_VMerror);
+ memcpy(data + size, (const byte *)suffix, suffix_size);
+ pbfont->font_name.data = data;
+ pbfont->font_name.size = size + suffix_size;
+#undef SUFFIX_CHAR
+ return 0;
+}
+
+/*
+ * Write an embedded font.
+ */
+int
+pdf_write_embedded_font(gx_device_pdf *pdev, pdf_base_font_t *pbfont, font_type FontType,
+ gs_int_rect *FontBBox, gs_id rid, cos_dict_t **ppcd)
+{
+ bool do_subset = pdf_do_subset_font(pdev, pbfont, rid);
+ gs_font_base *out_font =
+ (do_subset || pbfont->complete == NULL ? pbfont->copied : pbfont->complete);
+ gs_const_string fnstr;
+ pdf_data_writer_t writer;
+ byte digest[6] = {0,0,0,0,0,0};
+ int code = 0;
+ int options=0;
+
+ if (pbfont->written)
+ return 0; /* already written */
+ code = copied_order_font((gs_font *)out_font);
+ if (code < 0)
+ return code;
+ /* Since we now always ASCIIHex encode the eexec encrypted portion of a
+ * Type 1 font, such a font cannot contain any binary data, if its not being
+ * compressed then there is no reason to ASCII encode it (which will happen
+ * we set DATA_STREAM_BINARY and the device does not permit binary output)
+ * NB if HaveCFF is true then we convert type 1 to CFF which is a binary
+ * format, so we still need to set DATA_STREAM_BINARY.
+ */
+ if (pdev->CompressFonts)
+ options = DATA_STREAM_BINARY | DATA_STREAM_COMPRESS;
+ else
+ if (FontType != ft_encrypted || pdev->HaveCFF)
+ options = DATA_STREAM_BINARY;
+ /* Don't set DATA_STREAM_ENCRYPT since we write to a temporary file.
+ * See comment in pdf_begin_encrypt.
+ */
+ code = pdf_begin_data_stream(pdev, &writer, options, 0);
+ if (code < 0)
+ return code;
+ if (pdev->PDFA != 0) {
+ stream *s = s_MD5C_make_stream(pdev->pdf_memory, writer.binary.strm);
+
+ if (s == NULL)
+ return_error(gs_error_VMerror);
+ writer.binary.strm = s;
+ }
+ if (pdev->CompatibilityLevel == 1.2 &&
+ !do_subset && !pbfont->is_standard ) {
+ /*
+ * Due to a bug in Acrobat Reader 3, we need to generate
+ * unique font names, except base 14 fonts being not embedded.
+ * To recognize base 14 fonts here we used the knowledge
+ * that pbfont->is_standard is true for base 14 fonts only.
+ * Note that subsetted fonts already have an unique name
+ * due to subset prefix.
+ */
+ int code = pdf_adjust_font_name(pdev, writer.pres->object->id, pbfont);
+ if (code < 0)
+ return code;
+ }
+ fnstr.data = pbfont->font_name.data;
+ fnstr.size = pbfont->font_name.size;
+ /* Now write the font (or subset). */
+ switch (FontType) {
+
+ case ft_composite:
+ /* Nothing to embed -- the descendant fonts do it all. */
+ code = 0;
+ break;
+
+ case ft_encrypted2:
+ if (!pdev->HaveCFF) {
+ /* Must convert to Type 1 charstrings. */
+ return_error(gs_error_unregistered); /* Not implemented yet. */
+ }
+ case ft_encrypted:
+ if (pdev->HavePDFWidths) {
+ code = copied_drop_extension_glyphs((gs_font *)out_font);
+ if (code < 0)
+ return code;
+ }
+ if (!pdev->HaveCFF) {
+ /* Write the type 1 font with no converting to CFF. */
+ int lengths[3];
+
+ code = psf_write_type1_font(writer.binary.strm,
+ (gs_font_type1 *)out_font,
+ WRITE_TYPE1_WITH_LENIV | WRITE_TYPE1_EEXEC |
+ WRITE_TYPE1_EEXEC_PAD | WRITE_TYPE1_ASCIIHEX,
+ NULL, 0, &fnstr, lengths);
+ if (lengths[0] > 0) {
+ if (code < 0)
+ goto finish;
+ code = cos_dict_put_c_key_int((cos_dict_t *)writer.pres->object,
+ "/Length1", lengths[0]);
+ }
+ if (lengths[1] > 0) {
+ if (code < 0)
+ goto finish;
+ code = cos_dict_put_c_key_int((cos_dict_t *)writer.pres->object,
+ "/Length2", lengths[1]);
+ if (code < 0)
+ return code;
+ code = cos_dict_put_c_key_int((cos_dict_t *)writer.pres->object,
+ "/Length3", lengths[2]);
+ }
+ } else {
+ /*
+ * Since we only support PDF 1.2 and later, always write Type 1
+ * fonts as Type1C (Type 2). Acrobat Reader apparently doesn't
+ * accept CFF fonts with Type 1 CharStrings, so we need to convert
+ * them. Also remove lenIV, so Type 2 fonts will compress better.
+ */
+#define TYPE2_OPTIONS (WRITE_TYPE2_NO_LENIV | WRITE_TYPE2_CHARSTRINGS)
+ code = cos_dict_put_string_copy((cos_dict_t *)writer.pres->object, "/Subtype", "/Type1C");
+ if (code < 0)
+ return code;
+ code = psf_write_type2_font(writer.binary.strm,
+ (gs_font_type1 *)out_font,
+ TYPE2_OPTIONS |
+ (pdev->CompatibilityLevel < 1.3 ? WRITE_TYPE2_AR3 : 0),
+ NULL, 0, &fnstr, FontBBox);
+ }
+ goto finish;
+
+ case ft_TrueType: {
+ gs_font_type42 *const pfont = (gs_font_type42 *)out_font;
+#define TRUETYPE_OPTIONS (WRITE_TRUETYPE_NAME | WRITE_TRUETYPE_HVMTX)
+ /* Acrobat Reader 3 doesn't handle cmap format 6 correctly. */
+ const int options = TRUETYPE_OPTIONS |
+ (pdev->PDFA != 0 ? WRITE_TRUETYPE_UNICODE_CMAP : 0) |
+ (pdev->CompatibilityLevel <= 1.2 ?
+ WRITE_TRUETYPE_NO_TRIMMED_TABLE : 0) |
+ /* Generate a cmap only for incrementally downloaded fonts
+ and for subsetted fonts. */
+ (pfont->data.numGlyphs != pfont->data.trueNumGlyphs ||
+ pbfont->do_subset == DO_SUBSET_YES ?
+ WRITE_TRUETYPE_CMAP : 0);
+ stream poss;
+
+ if (pdev->HavePDFWidths) {
+ code = copied_drop_extension_glyphs((gs_font *)out_font);
+ if (code < 0)
+ return code;
+ }
+ s_init(&poss, pdev->memory);
+ swrite_position_only(&poss);
+ code = psf_write_truetype_font(&poss, pfont, options, NULL, 0, &fnstr);
+ if (code < 0)
+ return code;
+ code = cos_dict_put_c_key_int((cos_dict_t *)writer.pres->object, "/Length1", stell(&poss));
+ if (code < 0)
+ return code;
+ code = psf_write_truetype_font(writer.binary.strm, pfont,
+ options, NULL, 0, &fnstr);
+ goto finish;
+ }
+
+ case ft_CID_encrypted:
+ code = cos_dict_put_string_copy((cos_dict_t *)writer.pres->object, "/Subtype", "/CIDFontType0C");
+ if (code < 0)
+ return code;
+ code = psf_write_cid0_font(writer.binary.strm,
+ (gs_font_cid0 *)out_font, TYPE2_OPTIONS,
+ NULL, 0, &fnstr);
+ goto finish;
+
+ case ft_CID_TrueType:
+ /* CIDFontType 2 fonts don't use cmap, name, OS/2, or post. */
+#define CID2_OPTIONS WRITE_TRUETYPE_HVMTX
+ code = psf_write_cid2_font(writer.binary.strm,
+ (gs_font_cid2 *)out_font,
+ CID2_OPTIONS, NULL, 0, &fnstr);
+ finish:
+ if (pdev->PDFA != 0) {
+ sflush(writer.binary.strm);
+ s_MD5C_get_digest(writer.binary.strm, digest, sizeof(digest));
+ }
+ *ppcd = (cos_dict_t *)writer.pres->object;
+ if (code < 0) {
+ pdf_end_fontfile(pdev, &writer);
+ pdf_obj_mark_unused(pdev, writer.pres->object->id);
+ return code;
+ }
+ code = pdf_end_fontfile(pdev, &writer);
+ break;
+
+ default:
+ code = gs_note_error(gs_error_rangecheck);
+ }
+
+ pbfont->written = true;
+ return code;
+}
+
+/*
+ * Write the CharSet for a subsetted font, as a PDF string.
+ */
+int
+pdf_write_CharSet(gx_device_pdf *pdev, pdf_base_font_t *pbfont)
+{
+ stream *s = pdev->strm;
+ gs_font_base *font = pbfont->copied;
+ int index;
+ gs_glyph glyph;
+
+ stream_puts(s, "(");
+ for (index = 0;
+ (font->procs.enumerate_glyph((gs_font *)font, &index,
+ GLYPH_SPACE_NAME, &glyph),
+ index != 0);
+ ) {
+ gs_const_string gstr;
+ int code = font->procs.glyph_name((gs_font *)font, glyph, &gstr);
+
+ /* Don't include .notdef. */
+ if (code >= 0 &&
+ bytes_compare(gstr.data, gstr.size, (const byte *)".notdef", 7)
+ )
+ pdf_put_name(pdev, gstr.data, gstr.size);
+ }
+ stream_puts(s, ")");
+ return 0;
+}
+
+/*
+ * Write the CIDSet object for a subsetted CIDFont.
+ */
+int
+pdf_write_CIDSet(gx_device_pdf *pdev, pdf_base_font_t *pbfont,
+ long *pcidset_id)
+{
+ pdf_data_writer_t writer;
+ int code;
+
+ code = pdf_begin_data_stream(pdev, &writer,
+ DATA_STREAM_BINARY |
+ (pdev->CompressFonts ? DATA_STREAM_COMPRESS : 0),
+ gs_no_id);
+ if (code < 0)
+ return code;
+ stream_write(writer.binary.strm, pbfont->CIDSet,
+ pbfont->CIDSetLength);
+ code = pdf_end_data(&writer);
+ if (code < 0)
+ return code;
+ *pcidset_id = pdf_resource_id(writer.pres);
+ return 0;
+}
+/*
+ * Check whether a base font is standard.
+ */
+bool
+pdf_is_standard_font(pdf_base_font_t *bfont)
+{ return bfont->is_standard;
+}
+
+void
+pdf_set_FontFile_object(pdf_base_font_t *bfont, cos_dict_t *pcd)
+{
+ bfont->FontFile = pcd;
+}
+const cos_dict_t *
+pdf_get_FontFile_object(pdf_base_font_t *bfont)
+{
+ return bfont->FontFile;
+}
diff --git a/devices/vector/gdevpdtb.h b/devices/vector/gdevpdtb.h
new file mode 100644
index 000000000..200c02193
--- /dev/null
+++ b/devices/vector/gdevpdtb.h
@@ -0,0 +1,160 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* BaseFont structure and API for pdfwrite */
+
+#ifndef gdevpdtb_INCLUDED
+# define gdevpdtb_INCLUDED
+
+#include "gdevpdtx.h"
+
+/* ================ Types and structures ================ */
+
+/*
+ * A "base font" pdf_base_font_t contains a stable copy of a gs_font. The
+ * only supported font types are Type 1/2, TrueType / Type 42, CIDFontType
+ * 0, and CIDFontType 2.
+ *
+ * At the time a base font is created, we copy the fixed elements of the
+ * gs_font (that is, everything except the data for individual glyphs) into
+ * stable memory; we then copy individual glyphs as they are used. If
+ * subsetting is not mandatory (that is, if the entire font might be
+ * embedded or its Widths written), we also save a separate copy of the
+ * entire font, since the subsetting decision cannot be made until the font
+ * is written out (normally at the end of the document).
+ *
+ * In an earlier design, we deferred making the complete copy until the font
+ * was about to be freed. We decided that the substantial extra complexity
+ * of this approach did not justify the space that would be saved in the
+ * usual (subsetted) case.
+ *
+ * The term "base font" is used, confusingly, for at least 3 different
+ * concepts in Ghostscript. However, the above meaning is used consistently
+ * within the PDF text code (gdevpdt*.[ch]).
+ */
+/*
+ * Font names in PDF files have caused an enormous amount of trouble, so we
+ * document specifically how they are handled in each structure.
+ *
+ * The PDF Reference doesn't place any constraints on the [CID]FontName of
+ * base fonts, although it does say that the BaseFont name in the font
+ * resource is "usually" derived from the [CID]FontName of the base font.
+ * The code in this module allows setting the font name. It initializes
+ * the name to the key_name of the base font, or to the font_name if the
+ * base font has no key_name, minus any XXXXXX+ subset prefix; the
+ * pdf_do_subset_font procedure adds the XXXXXX+ prefix if the font will
+ * be subsetted.
+ */
+
+#ifndef pdf_base_font_DEFINED
+# define pdf_base_font_DEFINED
+typedef struct pdf_base_font_s pdf_base_font_t;
+#endif
+
+/* ================ Procedures ================ */
+
+/*
+ * Allocate and initialize a base font structure, making the required
+ * stable copy/ies of the gs_font. Note that this removes any XXXXXX+
+ * font name prefix from the copy. If complete is true, the copy is
+ * a complete one, and adding glyphs or Encoding entries is not allowed.
+ */
+int pdf_base_font_alloc(gx_device_pdf *pdev, pdf_base_font_t **ppbfont,
+ gs_font_base *font, const gs_matrix *orig_matrix,
+ bool is_standard);
+
+/*
+ * Return a reference to the name of a base font. This name is guaranteed
+ * not to have a XXXXXX+ prefix. The client may change the name at will,
+ * but must not add a XXXXXX+ prefix.
+ */
+gs_string *pdf_base_font_name(pdf_base_font_t *pbfont);
+
+/*
+ * Return the (copied, subset or complete) font associated with a base font.
+ * This procedure probably shouldn't exist....
+ */
+gs_font_base *pdf_base_font_font(const pdf_base_font_t *pbfont, bool complete);
+
+/*
+ * Check for subset font.
+ */
+bool pdf_base_font_is_subset(const pdf_base_font_t *pbfont);
+
+/*
+ * Drop the copied complete font associated with a base font.
+ */
+void pdf_base_font_drop_complete(pdf_base_font_t *pbfont);
+
+/*
+ * Copy a glyph (presumably one that was just used) into a saved base
+ * font. Note that it is the client's responsibility to determine that
+ * the source font is compatible with the target font. (Normally they
+ * will be the same.)
+ */
+int pdf_base_font_copy_glyph(pdf_base_font_t *pbfont, gs_glyph glyph,
+ gs_font_base *font);
+
+/*
+ * Determine whether a font is a subset font by examining the name.
+ */
+bool pdf_has_subset_prefix(const byte *str, uint size);
+
+/*
+ * Add the XXXXXX+ prefix for a subset font.
+ */
+int pdf_add_subset_prefix(const gx_device_pdf *pdev, gs_string *pstr,
+ byte *used, int count, char *md5_hash);
+
+/*
+ * Determine whether a copied font should be subsetted.
+ */
+bool pdf_do_subset_font(gx_device_pdf *pdev, pdf_base_font_t *pbfont,
+ gs_id rid);
+
+/*
+ * Write the FontFile entry for an embedded font, /FontFile<n> # # R.
+ */
+int pdf_write_FontFile_entry(gx_device_pdf *pdev, pdf_base_font_t *pbfont);
+
+/*
+ * Write an embedded font, possibly subsetted.
+ */
+int pdf_write_embedded_font(gx_device_pdf *pdev, pdf_base_font_t *pbfont, font_type FontType,
+ gs_int_rect *FontBBox, gs_id rid, cos_dict_t **ppcd);
+
+/*
+ * Write the CharSet data for a subsetted font, as a PDF string.
+ */
+int pdf_write_CharSet(gx_device_pdf *pdev, pdf_base_font_t *pbfont);
+
+/*
+ * Write the CIDSet object for a subsetted CIDFont.
+ */
+int pdf_write_CIDSet(gx_device_pdf *pdev, pdf_base_font_t *pbfont,
+ long *pcidset_id);
+
+/*
+ * Check whether a base font is standard.
+ */
+bool pdf_is_standard_font(pdf_base_font_t *bfont);
+
+void pdf_set_FontFile_object(pdf_base_font_t *bfont, cos_dict_t *pcd);
+const cos_dict_t * pdf_get_FontFile_object(pdf_base_font_t *bfont);
+
+const char *pdf_find_base14_name(const byte *str, uint size);
+
+#endif /* gdevpdtb_INCLUDED */
diff --git a/devices/vector/gdevpdtc.c b/devices/vector/gdevpdtc.c
new file mode 100644
index 000000000..18cb06eda
--- /dev/null
+++ b/devices/vector/gdevpdtc.c
@@ -0,0 +1,983 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Composite and CID-based text processing for pdfwrite. */
+#include "memory_.h"
+#include "gx.h"
+#include "gserrors.h"
+#include "gxfcmap.h"
+#include "gxfont.h"
+#include "gxfont0.h"
+#include "gxfont0c.h"
+#include "gzpath.h"
+#include "gxchar.h"
+#include "gdevpsf.h"
+#include "gdevpdfx.h"
+#include "gdevpdtx.h"
+#include "gdevpdtd.h"
+#include "gdevpdtf.h"
+#include "gdevpdts.h"
+#include "gdevpdtt.h"
+
+#include "gximage.h"
+#include "gxcpath.h"
+/* ---------------- Non-CMap-based composite font ---------------- */
+
+/*
+ * Process a text string in a composite font with FMapType != 9 (CMap).
+ */
+int
+process_composite_text(gs_text_enum_t *pte, void *vbuf, uint bsize)
+{
+ byte *const buf = vbuf;
+ pdf_text_enum_t *const penum = (pdf_text_enum_t *)pte;
+ int code = 0;
+ gs_string str;
+ pdf_text_process_state_t text_state;
+ pdf_text_enum_t curr, prev, out;
+ gs_point total_width;
+ const gs_matrix *psmat = 0;
+ gs_font *prev_font = 0;
+ gs_char chr, char_code = 0x0badf00d, space_char = GS_NO_CHAR;
+ int buf_index = 0;
+ bool return_width = (penum->text.operation & TEXT_RETURN_WIDTH);
+
+ str.data = buf;
+ if (return_width) {
+ code = gx_path_current_point(penum->path, &penum->origin);
+ if (code < 0)
+ return code;
+ }
+ if (pte->text.operation &
+ (TEXT_FROM_ANY - (TEXT_FROM_STRING | TEXT_FROM_BYTES))
+ )
+ return_error(gs_error_rangecheck);
+ if (pte->text.operation & TEXT_INTERVENE) {
+ /* Not implemented. (PostScript doesn't even allow this case.) */
+ return_error(gs_error_rangecheck);
+ }
+ total_width.x = total_width.y = 0;
+ curr = *penum;
+ prev = curr;
+ out = curr;
+ out.current_font = 0;
+ /* Scan runs of characters in the same leaf font. */
+ for ( ; ; ) {
+ int font_code;
+ gs_font *new_font = 0;
+
+ gs_text_enum_copy_dynamic((gs_text_enum_t *)&out,
+ (gs_text_enum_t *)&curr, false);
+ for (;;) {
+ gs_glyph glyph;
+
+ gs_text_enum_copy_dynamic((gs_text_enum_t *)&prev,
+ (gs_text_enum_t *)&curr, false);
+ font_code = pte->orig_font->procs.next_char_glyph
+ ((gs_text_enum_t *)&curr, &chr, &glyph);
+ /*
+ * We check for a font change by comparing the current
+ * font, rather than testing the return code, because
+ * it makes the control structure a little simpler.
+ */
+ switch (font_code) {
+ case 0: /* no font change */
+ case 1: /* font change */
+ curr.returned.current_char = chr;
+ char_code = gx_current_char((gs_text_enum_t *)&curr);
+ new_font = curr.fstack.items[curr.fstack.depth].font;
+ if (new_font != prev_font)
+ break;
+ if (chr != (byte)chr) /* probably can't happen */
+ return_error(gs_error_rangecheck);
+ if (buf_index >= bsize)
+ return_error(gs_error_unregistered); /* Must not happen. */
+ buf[buf_index] = (byte)chr;
+ buf_index++;
+ prev_font = new_font;
+ psmat = &curr.fstack.items[curr.fstack.depth - 1].font->FontMatrix;
+ if ((pte->text.operation & TEXT_ADD_TO_SPACE_WIDTH) &&
+ pte->text.space.s_char == char_code)
+ space_char = chr;
+ continue;
+ case 2: /* end of string */
+ break;
+ default: /* error */
+ return font_code;
+ }
+ break;
+ }
+ str.size = buf_index;
+ if (buf_index) {
+ /* buf_index == 0 is only possible the very first time. */
+ /*
+ * The FontMatrix of leaf descendant fonts is not updated
+ * by scalefont. Compute the effective FontMatrix now.
+ */
+ gs_matrix fmat;
+
+ /* set up the base font : */
+ out.fstack.depth = 0;
+ out.fstack.items[out.fstack.depth].font = out.current_font = prev_font;
+ pte->current_font = prev_font;
+
+ /* Provide the decoded space character : */
+ out.text.space.s_char = space_char;
+
+ gs_matrix_multiply(&prev_font->FontMatrix, psmat, &fmat);
+ out.index = 0; /* Note : we don't reset out.xy_index here. */
+ code = pdf_process_string_aux(&out, &str, NULL, &fmat, &text_state);
+ if (code < 0) {
+ if (code == gs_error_undefined && new_font && new_font->FontType == ft_encrypted2)
+ /* Caused by trying to make a CFF font resource for ps2write, which doesn't support CFF, abort now! */
+ return gs_error_invalidfont;
+ return code;
+ }
+ curr.xy_index = out.xy_index; /* pdf_encode_process_string advanced it. */
+ if (out.index < str.size) {
+ gs_glyph glyph;
+
+ /* Advance *pte exactly for out.index chars,
+ because above we stored bytes into buf. */
+ while (out.index--)
+ pte->orig_font->procs.next_char_glyph(pte, &chr, &glyph);
+ font_code = 2; /* force exiting the loop */
+ } else {
+ /* advance *pte past the current substring */
+ gs_text_enum_copy_dynamic(pte, (gs_text_enum_t *)&prev, true);
+ }
+ pte->xy_index = out.xy_index;
+ if (return_width) {
+ /* This is silly, but its a consequence of the way pdf_process_string
+ * works. When we have TEXT_DO_NONE (stringwidth) we add the width of the
+ * glyph(s) to the enumerator 'returned.total_width' so we keep track
+ * of the total width as we go. However when we are returning the width
+ * but its NOT for a stringwidth, we set the enumerator 'retuerned'
+ * value to just the width of the glyph(s) processed. So when we are *not*
+ * handling a stringwidth we need to keep track of the total width
+ * ourselves. I'd have preferred to alter pdf_process_string, but that
+ * is used in many other places, and those places rely on this behaviour.
+ */
+ if (pte->text.operation & TEXT_DO_NONE) {
+ pte->returned.total_width.x = total_width.x = out.returned.total_width.x;
+ pte->returned.total_width.y = total_width.y = out.returned.total_width.y;
+ } else {
+ pte->returned.total_width.x = total_width.x +=
+ out.returned.total_width.x;
+ pte->returned.total_width.y = total_width.y +=
+ out.returned.total_width.y;
+ }
+ }
+ pdf_text_release_cgp(penum);
+ }
+ if (font_code == 2)
+ break;
+ buf[0] = (byte)chr;
+ buf_index = 1;
+ space_char = ((pte->text.operation & TEXT_ADD_TO_SPACE_WIDTH) &&
+ pte->text.space.s_char == char_code ? chr : ~0);
+ psmat = &curr.fstack.items[curr.fstack.depth - 1].font->FontMatrix;
+ prev_font = new_font;
+ }
+ if (!return_width)
+ return 0;
+ return pdf_shift_text_currentpoint(penum, &total_width);
+}
+
+/* ---------------- CMap-based composite font ---------------- */
+
+/*
+ * Process a text string in a composite font with FMapType == 9 (CMap).
+ */
+static const char *const standard_cmap_names[] = {
+ /* The following were added in PDF 1.5. */
+
+ "UniGB-UTF16-H", "UniGB-UTF16-V",
+
+ "GBKp-EUC-H", "GBKp-EUC-V",
+ "HKscs-B5-H", "HKscs-B5-V",
+ "UniCNS-UTF16-H", "UniCNS-UTF16-V",
+ "UniJIS-UTF16-H", "UniJIS-UTF16-V",
+ "UniKS-UTF16-H", "UniKS-UTF16-V",
+#define END_PDF15_CMAP_NAMES_INDEX 12
+ /* The following were added in PDF 1.4. */
+ "GBKp-EUC-H", "GBKp-EUC-V",
+ "GBK2K-H", "GBK2K-V",
+ "HKscs-B5-H", "HKscs-B5-V",
+#define END_PDF14_CMAP_NAMES_INDEX 18
+ /* The following were added in PDF 1.3. */
+
+ "GBpc-EUC-V",
+ "GBK-EUC-H", "GBK-EUC-V",
+ "UniGB-UCS2-H", "UniGB-UCS2-V",
+
+ "ETenms-B5-H", "ETenms-B5-V",
+
+ "UniCNS-UCS2-H", "UniCNS-UCS2-V",
+
+ "90msp-RKSJ-H", "90msp-RKSJ-V",
+ "EUC-H", "EUC-V",
+ "UniJIS-UCS2-H", "UniJIS-UCS2-V",
+ "UniJIS-UCS2-HW-H", "UniJIS-UCS2-HW-V",
+
+ "KSCms-UHC-HW-H", "KSCms-UHC-HW-V",
+ "UniKS-UCS2-H", "UniKS-UCS2-V",
+
+#define END_PDF13_CMAP_NAMES_INDEX 39
+ /* The following were added in PDF 1.2. */
+
+ "GB-EUC-H", "GB-EUC-V",
+ "GBpc-EUC-H",
+
+ "B5pc-H", "B5pc-V",
+ "ETen-B5-H", "ETen-B5-V",
+ "CNS-EUC-H", "CNS-EUC-V",
+
+ "83pv-RKSJ-H",
+ "90ms-RKSJ-H", "90ms-RKSJ-V",
+ "90pv-RKSJ-H",
+ "Add-RKSJ-H", "Add-RKSJ-V",
+ "Ext-RKSJ-H", "Ext-RKSJ-V",
+ "H", "V",
+
+ "KSC-EUC-H", "KSC-EUC-V",
+ "KSCms-UHC-H", "KSCms-UHC-V",
+ "KSCpc-EUC-H",
+
+ "Identity-H", "Identity-V",
+
+ 0
+};
+
+static int
+attach_cmap_resource(gx_device_pdf *pdev, pdf_font_resource_t *pdfont,
+ const gs_cmap_t *pcmap, int font_index_only)
+{
+ const char *const *pcmn =
+ standard_cmap_names +
+ (pdev->CompatibilityLevel < 1.3 ? END_PDF13_CMAP_NAMES_INDEX :
+ pdev->CompatibilityLevel < 1.4 ? END_PDF14_CMAP_NAMES_INDEX :
+ pdev->CompatibilityLevel < 1.5 ? END_PDF15_CMAP_NAMES_INDEX : 0);
+ bool is_identity = false;
+ pdf_resource_t *pcmres = 0; /* CMap */
+ int code;
+
+ /* Make sure cmap names is properly initialised. Silences Coverity warning */
+ if (!pcmn)
+ return_error(gs_error_unknownerror);
+
+ /*
+ * If the CMap isn't standard, write it out if necessary.
+ */
+ for (; *pcmn != 0; ++pcmn)
+ if (pcmap->CMapName.size == strlen(*pcmn) &&
+ !memcmp(*pcmn, pcmap->CMapName.data, pcmap->CMapName.size))
+ break;
+ if (*pcmn == 0) {
+ /*
+ * PScript5.dll Version 5.2 creates identity CMaps with
+ * instandard name. Check this specially here
+ * and later replace with a standard name.
+ * This is a temporary fix for SF bug #615994 "CMAP is corrupt".
+ */
+ is_identity = gs_cmap_is_identity(pcmap, font_index_only);
+ }
+ if (*pcmn == 0 && !is_identity) { /* not standard */
+ pcmres = pdf_find_resource_by_gs_id(pdev, resourceCMap, pcmap->id + font_index_only);
+ if (pcmres == 0) {
+ /* Create and write the CMap object. */
+ code = pdf_cmap_alloc(pdev, pcmap, &pcmres, font_index_only);
+ if (code < 0)
+ return code;
+ }
+ }
+ if (pcmap->from_Unicode) {
+ gs_cmap_ranges_enum_t renum;
+
+ gs_cmap_ranges_enum_init(pcmap, &renum);
+ if (gs_cmap_enum_next_range(&renum) == 0 && renum.range.size == 2 &&
+ gs_cmap_enum_next_range(&renum) == 1) {
+ /*
+ * Exactly one code space range, of size 2. Add an identity
+ * ToUnicode CMap.
+ */
+ if (!pdev->Identity_ToUnicode_CMaps[pcmap->WMode]) {
+ /* Create and write an identity ToUnicode CMap now. */
+ gs_cmap_t *pidcmap;
+
+ code = gs_cmap_create_char_identity(&pidcmap, 2, pcmap->WMode,
+ pdev->memory);
+ if (code < 0)
+ return code;
+ pidcmap->CMapType = 2; /* per PDF Reference */
+ pidcmap->ToUnicode = true;
+ code = pdf_cmap_alloc(pdev, pidcmap,
+ &pdev->Identity_ToUnicode_CMaps[pcmap->WMode], -1);
+ if (code < 0)
+ return code;
+ }
+ pdfont->res_ToUnicode = pdev->Identity_ToUnicode_CMaps[pcmap->WMode];
+ }
+ }
+ if (pcmres || is_identity) {
+ uint size = pcmap->CMapName.size;
+ byte *chars = gs_alloc_string(pdev->pdf_memory, size,
+ "pdf_font_resource_t(CMapName)");
+
+ if (chars == 0)
+ return_error(gs_error_VMerror);
+ memcpy(chars, pcmap->CMapName.data, size);
+ if (is_identity)
+ strcpy(pdfont->u.type0.Encoding_name,
+ (pcmap->WMode ? "/Identity-V" : "/Identity-H"));
+ else
+ gs_sprintf(pdfont->u.type0.Encoding_name, "%ld 0 R",
+ pdf_resource_id(pcmres));
+ pdfont->u.type0.CMapName.data = chars;
+ pdfont->u.type0.CMapName.size = size;
+ } else {
+ if (!*pcmn)
+ /* Should not be possible, if *pcmn is NULL then either
+ * is_identity is true or we create pcmres.
+ */
+ return_error(gs_error_invalidfont);
+
+ gs_sprintf(pdfont->u.type0.Encoding_name, "/%s", *pcmn);
+ pdfont->u.type0.CMapName.data = (const byte *)*pcmn;
+ pdfont->u.type0.CMapName.size = strlen(*pcmn);
+ pdfont->u.type0.cmap_is_standard = true;
+ }
+ pdfont->u.type0.WMode = pcmap->WMode;
+ return 0;
+}
+
+static int estimate_fontbbox(pdf_text_enum_t *pte, gs_font_base *font,
+ const gs_matrix *pfmat,
+ gs_rect *text_bbox)
+{
+ gs_matrix m;
+ gs_point p0, p1, p2, p3;
+
+ if (font->FontBBox.p.x == font->FontBBox.q.x ||
+ font->FontBBox.p.y == font->FontBBox.q.y)
+ return_error(gs_error_undefined);
+ if (pfmat == 0)
+ pfmat = &font->FontMatrix;
+ m = ctm_only(pte->pis);
+ m.tx = fixed2float(pte->origin.x);
+ m.ty = fixed2float(pte->origin.y);
+ gs_matrix_multiply(pfmat, &m, &m);
+
+ gs_point_transform(font->FontBBox.p.x, font->FontBBox.p.y, &m, &p0);
+ gs_point_transform(font->FontBBox.p.x, font->FontBBox.q.y, &m, &p1);
+ gs_point_transform(font->FontBBox.q.x, font->FontBBox.p.y, &m, &p2);
+ gs_point_transform(font->FontBBox.q.x, font->FontBBox.q.y, &m, &p3);
+ text_bbox->p.x = min(min(p0.x, p1.x), min(p1.x, p2.x));
+ text_bbox->p.y = min(min(p0.y, p1.y), min(p1.y, p2.y));
+ text_bbox->q.x = max(max(p0.x, p1.x), max(p1.x, p2.x));
+ text_bbox->q.y = max(max(p0.y, p1.y), max(p1.y, p2.y));
+
+ return 0;
+}
+
+/* Record widths and CID => GID mappings. */
+static int
+scan_cmap_text(pdf_text_enum_t *pte, void *vbuf)
+{
+ gx_device_pdf *pdev = (gx_device_pdf *)pte->dev;
+ /* gs_font_type0 *const font = (gs_font_type0 *)pte->current_font;*/ /* Type 0, fmap_CMap */
+ gs_font_type0 *const font = (gs_font_type0 *)pte->orig_font; /* Type 0, fmap_CMap */
+ /* Not sure. Changed for CDevProc callout. Was pte->current_font */
+ gs_text_enum_t scan = *(gs_text_enum_t *)pte;
+ int wmode = font->WMode, code, rcode = 0;
+ pdf_font_resource_t *pdsubf0 = NULL;
+ gs_font *subfont0 = NULL, *saved_subfont = NULL;
+ uint index = scan.index, xy_index = scan.xy_index, start_index = index;
+ uint font_index0 = 0x7badf00d;
+ bool done = false;
+ pdf_char_glyph_pairs_t p;
+ gs_glyph *type1_glyphs = (gs_glyph *)vbuf;
+ int num_type1_glyphs = 0;
+
+ p.num_all_chars = 1;
+ p.num_unused_chars = 1;
+ p.unused_offset = 0;
+ pte->returned.total_width.x = pte->returned.total_width.y = 0;;
+ for (;;) {
+ uint break_index, break_xy_index;
+ uint font_index = 0x7badf00d;
+ gs_const_string str;
+ pdf_text_process_state_t text_state;
+ pdf_font_resource_t *pdsubf;
+ gs_font *subfont = NULL;
+ gs_point wxy;
+ bool font_change = 0;
+
+ code = gx_path_current_point(pte->path, &pte->origin);
+ if (code < 0)
+ return code;
+ do {
+ gs_char chr;
+ gs_glyph glyph;
+ pdf_font_descriptor_t *pfd;
+ byte *glyph_usage;
+ double *real_widths, *w, *v, *w0;
+ int char_cache_size, width_cache_size;
+ gs_char cid;
+
+ break_index = scan.index;
+ break_xy_index = scan.xy_index;
+ code = font->procs.next_char_glyph(&scan, &chr, &glyph);
+ if (code == 2) { /* end of string */
+ done = true;
+ break;
+ }
+ if (code < 0)
+ return code;
+ subfont = scan.fstack.items[scan.fstack.depth].font;
+ font_index = scan.fstack.items[scan.fstack.depth - 1].index;
+ scan.xy_index++;
+ if (glyph == GS_NO_GLYPH)
+ glyph = GS_MIN_CID_GLYPH;
+ cid = glyph - GS_MIN_CID_GLYPH;
+ switch (subfont->FontType) {
+ case ft_encrypted:
+ case ft_encrypted2:{
+ if (glyph == GS_MIN_CID_GLYPH) {
+ glyph = subfont->procs.encode_char(subfont, chr, GLYPH_SPACE_NAME);
+ }
+ type1_glyphs[num_type1_glyphs] = glyph;
+ num_type1_glyphs++;
+ break;
+ }
+ case ft_CID_encrypted:
+ case ft_CID_TrueType: {
+ p.s[0].glyph = glyph;
+ p.s[0].chr = cid;
+ code = pdf_obtain_cidfont_resource(pdev, subfont, &pdsubf, &p);
+ if (code < 0)
+ return code;
+ break;
+ }
+ case ft_user_defined: {
+ gs_string str1;
+
+ str1.data = NULL;
+ str1.size = 0;
+ pte->current_font = subfont;
+ code = pdf_obtain_font_resource(pte, &str1, &pdsubf);
+ if (code < 0)
+ return code;
+ cid = pdf_find_glyph(pdsubf, glyph);
+ if (cid == GS_NO_CHAR) {
+ code = pdf_make_font3_resource(pdev, subfont, &pdsubf);
+ if (code < 0)
+ return code;
+ code = pdf_attach_font_resource(pdev, subfont, pdsubf);
+ if (code < 0)
+ return code;
+ cid = 0;
+ }
+ break;
+ }
+ default:
+ /* An unsupported case, fall back to default implementation. */
+ return_error(gs_error_rangecheck);
+ }
+ code = pdf_attached_font_resource(pdev, (gs_font *)subfont, &pdsubf,
+ &glyph_usage, &real_widths, &char_cache_size, &width_cache_size);
+ if (code < 0)
+ return code;
+ if (break_index > start_index && pdev->charproc_just_accumulated)
+ break;
+ if (subfont->FontType == ft_user_defined &&
+ (break_index > start_index || !pdev->charproc_just_accumulated) &&
+ !(pdsubf->u.simple.s.type3.cached[cid >> 3] & (0x80 >> (cid & 7)))) {
+ if (subfont0 && subfont0->FontType != ft_user_defined)
+ /* This is hacky. By pretending to be in a type 3 font doing a charpath we force
+ * text handling to fall right back to bitmap glyphs. This is because we can't handle
+ * CIDFonts with mixed type 1/3 descendants. Ugly but it produces correct output for
+ * what is after all a dumb setup.
+ */
+ pdev->type3charpath = 1;
+ pte->current_font = subfont;
+ return gs_error_undefined;
+ }
+ if (subfont->FontType == ft_encrypted || subfont->FontType == ft_encrypted2) {
+ font_change = (subfont != subfont0 && subfont0 != NULL);
+ if (font_change) {
+ saved_subfont = subfont;
+ subfont = subfont0;
+ num_type1_glyphs--;
+ }
+ } else
+ font_change = (pdsubf != pdsubf0 && pdsubf0 != NULL);
+ if (!font_change) {
+ pdsubf0 = pdsubf;
+ font_index0 = font_index;
+ subfont0 = subfont;
+ }
+ if (subfont->FontType != ft_encrypted && subfont->FontType != ft_encrypted2) {
+ pfd = pdsubf->FontDescriptor;
+ code = pdf_resize_resource_arrays(pdev, pdsubf, cid + 1);
+ if (code < 0)
+ return code;
+ if (subfont->FontType == ft_CID_encrypted || subfont->FontType == ft_CID_TrueType) {
+ if (cid >=width_cache_size) {
+ /* fixme: we add the CID=0 glyph as CID=cid glyph to the output font.
+ Really it must not add and leave the CID undefined. */
+ cid = 0; /* notdef. */
+ }
+ }
+ if (cid >= char_cache_size || cid >= width_cache_size)
+ return_error(gs_error_unregistered); /* Must not happen */
+ if (pdsubf->FontType == ft_user_defined || pdsubf->FontType == ft_encrypted ||
+ pdsubf->FontType == ft_encrypted2) {
+ } else {
+ pdf_font_resource_t *pdfont;
+ bool notdef_subst = false;
+
+ code = pdf_obtain_cidfont_widths_arrays(pdev, pdsubf, wmode, &w, &w0, &v);
+ if (code < 0)
+ return code;
+ code = pdf_obtain_parent_type0_font_resource(pdev, pdsubf, font_index,
+ &font->data.CMap->CMapName, &pdfont);
+ if (code < 0)
+ return code;
+ if (pdf_is_CID_font(subfont)) {
+ /* Some Pscript5 output has non-identity mappings between character code and CID
+ * and the GlyphNames2Unicode dictionary uses character codes, not glyph names. So
+ * if we detect ths condition we cheat and claim not to be a CIDFont, so that the
+ * decode_glyph procedure can use the character code to look up the GlyphNames2Unicode
+ * dictionary. See bugs #696021, #688768 and #687954 for examples of the various ways
+ * this code can be exercised.
+ */
+ if (chr == glyph - GS_MIN_CID_GLYPH)
+ code = subfont->procs.decode_glyph((gs_font *)subfont, glyph, -1);
+ else
+ code = subfont->procs.decode_glyph((gs_font *)subfont, glyph, chr);
+ if (code != GS_NO_CHAR)
+ /* Since PScript5.dll creates GlyphNames2Unicode with character codes
+ instead CIDs, and with the WinCharSetFFFF-H2 CMap
+ character codes appears different than CIDs (Bug 687954),
+ pass the character code intead the CID. */
+ code = pdf_add_ToUnicode(pdev, subfont, pdfont,
+ chr + GS_MIN_CID_GLYPH, chr, NULL);
+ else {
+ /* If we interpret a PDF document, ToUnicode
+ CMap may be attached to the Type 0 font. */
+ code = pdf_add_ToUnicode(pdev, pte->orig_font, pdfont,
+ chr + GS_MIN_CID_GLYPH, chr, NULL);
+ }
+ }
+ else
+ code = pdf_add_ToUnicode(pdev, subfont, pdfont, glyph, cid, NULL);
+ if (code < 0)
+ return code;
+ /* We can't check pdsubf->used[cid >> 3] here,
+ because it mixed data for different values of WMode.
+ Perhaps pdf_font_used_glyph returns fast with reused glyphs.
+ */
+ code = pdf_font_used_glyph(pfd, glyph, (gs_font_base *)subfont);
+ if (code == gs_error_rangecheck) {
+ if (!(pdsubf->used[cid >> 3] & (0x80 >> (cid & 7)))) {
+ char buf[gs_font_name_max + 1];
+ int l = min(sizeof(buf) - 1, subfont->font_name.size);
+
+ memcpy(buf, subfont->font_name.chars, l);
+ buf[l] = 0;
+ emprintf3(pdev->memory,
+ "Missing glyph CID=%d, glyph=%04x in the font %s . The output PDF may fail with some viewers.\n",
+ (int)cid,
+ (unsigned int)(glyph - GS_MIN_CID_GLYPH),
+ buf);
+ pdsubf->used[cid >> 3] |= 0x80 >> (cid & 7);
+ }
+ cid = 0, code = 1; /* undefined glyph. */
+ notdef_subst = true;
+ /* If this is the first use of CID=0, get its width */
+ if (pdsubf->Widths[cid] == 0) {
+ pdf_glyph_widths_t widths;
+
+ code = pdf_glyph_widths(pdsubf, wmode, glyph, (gs_font *)subfont, &widths,
+ pte->cdevproc_callout ? pte->cdevproc_result : NULL);
+ }
+ } else if (code < 0)
+ return code;
+ if ((code == 0 /* just copied */ || pdsubf->Widths[cid] == 0) && !notdef_subst) {
+ pdf_glyph_widths_t widths;
+
+ code = pdf_glyph_widths(pdsubf, wmode, glyph, (gs_font *)subfont, &widths,
+ pte->cdevproc_callout ? pte->cdevproc_result : NULL);
+ if (code < 0)
+ return code;
+ if (code == TEXT_PROCESS_CDEVPROC) {
+ pte->returned.current_glyph = glyph;
+ pte->current_font = subfont;
+ rcode = TEXT_PROCESS_CDEVPROC;
+ break;
+ }
+ if (code >= 0) {
+ if (cid > pdsubf->count)
+ return_error(gs_error_unregistered); /* Must not happen. */
+ w[cid] = widths.Width.w;
+ if (v != NULL) {
+ v[cid * 2 + 0] = widths.Width.v.x;
+ v[cid * 2 + 1] = widths.Width.v.y;
+ }
+ real_widths[cid] = widths.real_width.w;
+ }
+ if (wmode) {
+ /* Since AR5 use W or DW to compute the x-coordinate of
+ v-vector, comupte and store the glyph width for WMode 0. */
+ /* fixme : skip computing real_width here. */
+ code = pdf_glyph_widths(pdsubf, 0, glyph, (gs_font *)subfont, &widths,
+ pte->cdevproc_callout ? pte->cdevproc_result : NULL);
+ if (code < 0)
+ return code;
+ w0[cid] = widths.Width.w;
+ }
+ if (pdsubf->u.cidfont.CIDToGIDMap != 0) {
+ gs_font_cid2 *subfont2 = (gs_font_cid2 *)subfont;
+
+ pdsubf->u.cidfont.CIDToGIDMap[cid] =
+ subfont2->cidata.CIDMap_proc(subfont2, glyph);
+ }
+ }
+ if (wmode)
+ pdsubf->u.cidfont.used2[cid >> 3] |= 0x80 >> (cid & 7);
+ }
+ pdsubf->used[cid >> 3] |= 0x80 >> (cid & 7);
+ }
+ if (pte->cdevproc_callout) {
+ /* Only handle a single character because its width is stored
+ into pte->cdevproc_result, and process_text_modify_width neds it.
+ fixme: next time take from w, v, real_widths. */
+ break_index = scan.index;
+ break_xy_index = scan.xy_index;
+ break;
+ }
+ } while (!font_change);
+ if (break_index > index) {
+ pdf_font_resource_t *pdfont;
+ gs_matrix m3;
+ int xy_index_step = (!(pte->text.operation & TEXT_REPLACE_WIDTHS) ? 0 :
+ pte->text.x_widths == pte->text.y_widths ? 2 : 1);
+ gs_text_params_t save_text;
+
+ if (!subfont && num_type1_glyphs != 0)
+ subfont = subfont0;
+ if (subfont && (subfont->FontType == ft_encrypted || subfont->FontType == ft_encrypted2)) {
+ int save_op = pte->text.operation;
+ gs_font *save_font = pte->current_font;
+ const gs_glyph *save_data = pte->text.data.glyphs;
+
+ pte->current_font = subfont;
+ pte->text.operation |= TEXT_FROM_GLYPHS;
+ pte->text.data.glyphs = type1_glyphs;
+ str.data = ((const byte *)vbuf) + ((pte->text.size - pte->index) * sizeof(gs_glyph));
+ str.size = num_type1_glyphs;
+ code = pdf_obtain_font_resource_unencoded(pte, (const gs_string *)&str, &pdsubf0,
+ type1_glyphs);
+ if (code < 0) {
+ /* Replace the modified values, fall back to default implementation
+ * (type 3 bitmap image font)
+ */
+ pte->current_font = save_font;
+ pte->text.operation |= save_op;
+ pte->text.data.glyphs = save_data;
+ return(code);
+ }
+ memcpy((void *)scan.text.data.bytes, (void *)str.data, str.size);
+ str.data = scan.text.data.bytes;
+ pdsubf = pdsubf0;
+ pte->text.operation = save_op;
+ }
+ pte->current_font = subfont0;
+ if (!subfont0 || !pdsubf0)
+ /* This should be impossible */
+ return_error(gs_error_invalidfont);
+
+ code = gs_matrix_multiply(&subfont0->FontMatrix, &font->FontMatrix, &m3);
+ /* We thought that it should be gs_matrix_multiply(&font->FontMatrix, &subfont0->FontMatrix, &m3); */
+ if (code < 0)
+ return code;
+ if (pdsubf0->FontType == ft_user_defined || pdsubf->FontType == ft_encrypted ||
+ pdsubf->FontType == ft_encrypted2)
+ pdfont = pdsubf0;
+ else {
+ code = pdf_obtain_parent_type0_font_resource(pdev, pdsubf0, font_index0,
+ &font->data.CMap->CMapName, &pdfont);
+ if (code < 0)
+ return code;
+ if (!pdfont->u.type0.Encoding_name[0]) {
+ /*
+ * If pdfont->u.type0.Encoding_name is set,
+ * a CMap resource is already attached.
+ * See attach_cmap_resource.
+ */
+ code = attach_cmap_resource(pdev, pdfont, font->data.CMap, font_index0);
+ if (code < 0)
+ return code;
+ }
+ }
+ pdf_set_text_wmode(pdev, font->WMode);
+ code = pdf_update_text_state(&text_state, (pdf_text_enum_t *)pte, pdfont, &m3);
+ if (code < 0)
+ return code;
+ /* process_text_modify_width breaks text parameters.
+ We would like to improve it someday.
+ Now save them locally and restore after the call. */
+ save_text = pte->text;
+ if (subfont && (subfont->FontType != ft_encrypted &&
+ subfont->FontType != ft_encrypted2)) {
+ /* If we are a type 1 descendant, we already sorted this out above */
+ str.data = scan.text.data.bytes + index;
+ str.size = break_index - index;
+ }
+ if (pte->text.operation & TEXT_REPLACE_WIDTHS) {
+ if (pte->text.x_widths != NULL)
+ pte->text.x_widths += xy_index * xy_index_step;
+ if (pte->text.y_widths != NULL)
+ pte->text.y_widths += xy_index * xy_index_step;
+ }
+ pte->xy_index = 0;
+ if (subfont && (subfont->FontType == ft_encrypted ||
+ subfont->FontType == ft_encrypted2)) {
+ gs_font *f = pte->orig_font;
+
+ adjust_first_last_char(pdfont, (byte *)str.data, str.size);
+
+ /* Make sure we use the descendant font, not the original type 0 ! */
+ pte->orig_font = subfont;
+ code = process_text_modify_width((pdf_text_enum_t *)pte,
+ (gs_font *)subfont, &text_state, &str, &wxy, type1_glyphs, false, scan.index - index);
+ if (code < 0)
+ return(code);
+ if(font_change) {
+ type1_glyphs[0] = type1_glyphs[num_type1_glyphs];
+ num_type1_glyphs = 1;
+ subfont = saved_subfont;
+ } else {
+ num_type1_glyphs = 0;
+ }
+ pte->orig_font = f;
+ } else {
+ code = process_text_modify_width((pdf_text_enum_t *)pte, (gs_font *)font,
+ &text_state, &str, &wxy, NULL, true, scan.index - index);
+ }
+ if (pte->text.operation & TEXT_REPLACE_WIDTHS) {
+ if (pte->text.x_widths != NULL)
+ pte->text.x_widths -= xy_index * xy_index_step;
+ if (pte->text.y_widths != NULL)
+ pte->text.y_widths -= xy_index * xy_index_step;
+ }
+ pte->text = save_text;
+ pte->cdevproc_callout = false;
+ if (code < 0) {
+ pte->index = index;
+ pte->xy_index = xy_index;
+ return code;
+ }
+ pte->index = break_index;
+ pte->xy_index = break_xy_index;
+ if (pdev->Eps2Write) {
+ gs_rect text_bbox;
+ gx_device_clip cdev;
+ gx_drawing_color devc;
+ fixed x0, y0, bx2, by2;
+
+ text_bbox.q.x = text_bbox.p.y = text_bbox.q.y = 0;
+ estimate_fontbbox(pte, (gs_font_base *)font, NULL, &text_bbox);
+ text_bbox.p.x = fixed2float(pte->origin.x);
+ text_bbox.q.x = text_bbox.p.x + wxy.x;
+
+ x0 = float2fixed(text_bbox.p.x);
+ y0 = float2fixed(text_bbox.p.y);
+ bx2 = float2fixed(text_bbox.q.x) - x0;
+ by2 = float2fixed(text_bbox.q.y) - y0;
+
+ pdev->AccumulatingBBox++;
+ gx_make_clip_device_on_stack(&cdev, pte->pcpath, (gx_device *)pdev);
+ set_nonclient_dev_color(&devc, gx_device_black((gx_device *)pdev)); /* any non-white color will do */
+ gx_default_fill_triangle((gx_device *) pdev, x0, y0,
+ float2fixed(text_bbox.p.x) - x0,
+ float2fixed(text_bbox.q.y) - y0,
+ bx2, by2, &devc, lop_default);
+ gx_default_fill_triangle((gx_device *) & cdev, x0, y0,
+ float2fixed(text_bbox.q.x) - x0,
+ float2fixed(text_bbox.p.y) - y0,
+ bx2, by2, &devc, lop_default);
+ pdev->AccumulatingBBox--;
+ }
+ code = pdf_shift_text_currentpoint(pte, &wxy);
+ if (code < 0)
+ return code;
+ }
+ pdf_text_release_cgp(pte);
+ index = break_index;
+ xy_index = break_xy_index;
+ if (done || rcode != 0)
+ break;
+ pdsubf0 = pdsubf;
+ font_index0 = font_index;
+ subfont0 = subfont;
+ }
+ pte->index = index;
+ pte->xy_index = xy_index;
+ return rcode;
+}
+
+int
+process_cmap_text(gs_text_enum_t *penum, void *vbuf, uint bsize)
+{
+ int code;
+ pdf_text_enum_t *pte = (pdf_text_enum_t *)penum;
+ byte *save;
+
+ if (pte->text.operation &
+ (TEXT_FROM_ANY - (TEXT_FROM_STRING | TEXT_FROM_BYTES))
+ )
+ return_error(gs_error_rangecheck);
+ if (pte->text.operation & TEXT_INTERVENE) {
+ /* Not implemented. (PostScript doesn't allow TEXT_INTERVENE.) */
+ return_error(gs_error_rangecheck);
+ }
+ /* scan_cmap_text has the unfortunate side effect of meddling with the
+ * text data in the enumerator. In general that's OK but in the case where
+ * the string is (eg) in a bound procedure, and we run that procedure more
+ * than once, the string is corrupted on the first use and then produces
+ * incorrect output for the subsequent use(s).
+ * The routine is, sadly, extremely convoluted so instead of trying to fix
+ * it so that it doesn't corrupt the string (which looks likely to be impossible
+ * without copying the string at some point) I've chosen to take a copy of the
+ * string here, and restore it after the call to scan_cmap_text.
+ * See bug #695322 and test file Bug691680.ps
+ */
+ save = (byte *)pte->text.data.bytes;
+ pte->text.data.bytes = gs_alloc_string(pte->memory, pte->text.size, "pdf_text_process");
+ memcpy((byte *)pte->text.data.bytes, save, pte->text.size);
+ code = scan_cmap_text(pte, vbuf);
+ gs_free_string(pte->memory, (byte *)pte->text.data.bytes, pte->text.size, "pdf_text_process");
+ pte->text.data.bytes = save;
+
+ if (code == TEXT_PROCESS_CDEVPROC)
+ pte->cdevproc_callout = true;
+ else
+ pte->cdevproc_callout = false;
+ return code;
+}
+
+/* ---------------- CIDFont ---------------- */
+
+/*
+ * Process a text string in a CIDFont. (Only glyphshow is supported.)
+ */
+int
+process_cid_text(gs_text_enum_t *pte, void *vbuf, uint bsize)
+{
+ pdf_text_enum_t *penum = (pdf_text_enum_t *)pte;
+ uint operation = pte->text.operation;
+ gs_text_enum_t save;
+ gs_font *scaled_font = pte->current_font; /* CIDFont */
+ gs_font *font; /* unscaled font (CIDFont) */
+ const gs_glyph *glyphs;
+ gs_matrix scale_matrix;
+ pdf_font_resource_t *pdsubf; /* CIDFont */
+ gs_font_type0 *font0 = NULL;
+ uint size;
+ int code;
+
+ if (operation & TEXT_FROM_GLYPHS) {
+ glyphs = pte->text.data.glyphs;
+ size = pte->text.size - pte->index;
+ } else if (operation & TEXT_FROM_SINGLE_GLYPH) {
+ glyphs = &pte->text.data.d_glyph;
+ size = 1;
+ } else if (operation & TEXT_FROM_STRING) {
+ glyphs = &pte->outer_CID;
+ size = 1;
+ } else
+ return_error(gs_error_rangecheck);
+
+ /*
+ * PDF doesn't support glyphshow directly: we need to create a Type 0
+ * font with an Identity CMap. Make sure all the glyph numbers fit
+ * into 16 bits. (Eventually we should support wider glyphs too,
+ * but this would require a different CMap.)
+ */
+ if (bsize < size * 2)
+ return_error(gs_error_unregistered); /* Must not happen. */
+ {
+ int i;
+ byte *pchars = vbuf;
+
+ for (i = 0; i < size; ++i) {
+ ulong gnum = glyphs[i] - GS_MIN_CID_GLYPH;
+
+ if (gnum & ~0xffffL)
+ return_error(gs_error_rangecheck);
+ *pchars++ = (byte)(gnum >> 8);
+ *pchars++ = (byte)gnum;
+ }
+ }
+
+ /* Find the original (unscaled) version of this font. */
+
+ for (font = scaled_font; font->base != font; )
+ font = font->base;
+ /* Compute the scaling matrix. */
+ gs_matrix_invert(&font->FontMatrix, &scale_matrix);
+ gs_matrix_multiply(&scale_matrix, &scaled_font->FontMatrix, &scale_matrix);
+
+ /* Find or create the CIDFont resource. */
+
+ code = pdf_obtain_font_resource(penum, NULL, &pdsubf);
+ if (code < 0)
+ return code;
+
+ /* Create the CMap and Type 0 font if they don't exist already. */
+
+ if (pdsubf->u.cidfont.glyphshow_font_id != 0)
+ font0 = (gs_font_type0 *)gs_find_font_by_id(font->dir,
+ pdsubf->u.cidfont.glyphshow_font_id, &scaled_font->FontMatrix);
+ if (font0 == NULL) {
+ code = gs_font_type0_from_cidfont(&font0, font, font->WMode,
+ &scale_matrix, font->memory);
+ if (code < 0)
+ return code;
+ pdsubf->u.cidfont.glyphshow_font_id = font0->id;
+ }
+
+ /* Now handle the glyphshow as a show in the Type 0 font. */
+
+ save = *pte;
+ pte->current_font = pte->orig_font = (gs_font *)font0;
+ /* Patch the operation temporarily for init_fstack. */
+ pte->text.operation = (operation & ~TEXT_FROM_ANY) | TEXT_FROM_BYTES;
+ /* Patch the data for process_cmap_text. */
+ pte->text.data.bytes = vbuf;
+ pte->text.size = size * 2;
+ pte->index = 0;
+ gs_type0_init_fstack(pte, pte->current_font);
+ code = process_cmap_text(pte, vbuf, bsize);
+ pte->current_font = scaled_font;
+ pte->orig_font = save.orig_font;
+ pte->text = save.text;
+ pte->index = save.index + pte->index / 2;
+ pte->fstack = save.fstack;
+ return code;
+}
diff --git a/devices/vector/gdevpdtd.c b/devices/vector/gdevpdtd.c
new file mode 100644
index 000000000..200786f6a
--- /dev/null
+++ b/devices/vector/gdevpdtd.c
@@ -0,0 +1,869 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* FontDescriptor implementation for pdfwrite */
+#include "math_.h"
+#include "memory_.h"
+#include "gx.h"
+#include "gserrors.h"
+#include "gsrect.h" /* for rect_merge */
+#include "gscencs.h"
+#include "gxfcopy.h"
+#include "gdevpdfx.h"
+#include "gdevpdfo.h" /* for object->written */
+#include "gdevpdtb.h"
+#include "gdevpdtd.h"
+#include "gdevpdtf.h"
+
+/* ================ Types and structures ================ */
+
+/*
+ * There are many different flavors of FontDescriptor. The first division
+ * is between FontDescriptors for actual fonts, and character class entries
+ * in the FD dictionary of a CID-keyed font. The latter include metrics and
+ * a FontName but nothing else. We represent these with a different C
+ * structure (pdf_sub_font_descriptor_t).
+ *
+ * Descriptors for actual fonts have three major orthogonal properties:
+ *
+ * - Whether they represent a CID-keyed font or a name-keyed
+ * (non-CID-keyed) font. We distinguish this by the FontType
+ * of the saved font (see below).
+ *
+ * - Whether the font they represent is embedded.
+ *
+ * - Whether the font they represent is a complete font or a subset.
+ * We always track which glyphs in a font are used: client code
+ * decides whether to treat the font as a subset when the
+ * descriptor (and, if embedded, the font) is written.
+ *
+ * Note that non-embedded fonts in the base set of 14 do not have
+ * descriptors, nor do Type 0 or (synthetic bitmap) Type 3 fonts.
+ */
+/*
+ * Start by defining the elements common to font descriptors and sub-font
+ * (character class) descriptors.
+ */
+typedef struct pdf_font_descriptor_values_s {
+ /* Required elements */
+ int Ascent, CapHeight, Descent, ItalicAngle, StemV;
+ gs_int_rect FontBBox;
+ gs_string FontName;
+ uint Flags;
+ /* Optional elements (default to 0) */
+ int AvgWidth, Leading, MaxWidth, MissingWidth, StemH, XHeight;
+} pdf_font_descriptor_values_t;
+typedef struct pdf_font_descriptor_common_s pdf_font_descriptor_common_t;
+struct pdf_font_descriptor_common_s {
+ pdf_resource_common(pdf_font_descriptor_common_t);
+ pdf_font_descriptor_values_t values;
+};
+/* Flag bits */
+/*#define FONT_IS_FIXED_WIDTH (1<<0)*/ /* defined in gxfont.h */
+#define FONT_IS_SERIF (1<<1)
+#define FONT_IS_SYMBOLIC (1<<2)
+#define FONT_IS_SCRIPT (1<<3)
+/*
+ * There is confusion over the meaning of the 1<<5 bit. According to the
+ * published PDF documentation, in PDF 1.1, it meant "font uses
+ * StandardEncoding", and as of PDF 1.2, it means "font uses (a subset of)
+ * the Adobe standard Latin character set"; however, Acrobat Reader 3 and 4
+ * seem to use the former interpretation, even if the font is embedded and
+ * the file is identified as a PDF 1.2 file. We have to use the former
+ * interpretation in order to produce output that Acrobat will handle
+ * correctly.
+ */
+#define FONT_USES_STANDARD_ENCODING (1<<5) /* always used */
+#define FONT_IS_ADOBE_ROMAN (1<<5) /* never used */
+#define FONT_IS_ITALIC (1<<6)
+#define FONT_IS_ALL_CAPS (1<<16)
+#define FONT_IS_SMALL_CAPS (1<<17)
+#define FONT_IS_FORCE_BOLD (1<<18)
+
+/*
+ * Define a (top-level) FontDescriptor. CID-keyed vs. non-CID-keyed fonts
+ * are distinguished by their FontType.
+ */
+#ifndef pdf_base_font_DEFINED
+# define pdf_base_font_DEFINED
+typedef struct pdf_base_font_s pdf_base_font_t;
+#endif
+struct pdf_font_descriptor_s {
+ pdf_font_descriptor_common_t common;
+ pdf_base_font_t *base_font;
+ font_type FontType; /* (copied from base_font) */
+ bool embed;
+ struct cid_ { /* (CIDFonts only) */
+ cos_dict_t *Style;
+ char Lang[3]; /* 2 chars + \0 */
+ cos_dict_t *FD; /* value = COS_VALUE_RESOURCE */
+ } cid;
+};
+/*
+ * Define a sub-descriptor for a character class (FD dictionary element).
+ */
+typedef struct pdf_sub_font_descriptor_s {
+ pdf_font_descriptor_common_t common;
+} pdf_sub_font_descriptor_t;
+
+/*
+ * Font descriptors are pseudo-resources, so their GC descriptors
+ * must be public.
+ */
+BASIC_PTRS(pdf_font_descriptor_ptrs) {
+ GC_STRING_ELT(pdf_font_descriptor_t, common.values.FontName),
+ GC_OBJ_ELT(pdf_font_descriptor_t, base_font),
+ GC_OBJ_ELT(pdf_font_descriptor_t, cid.Style),
+ GC_OBJ_ELT(pdf_font_descriptor_t, cid.FD)
+};
+gs_public_st_basic_super(st_pdf_font_descriptor, pdf_font_descriptor_t,
+ "pdf_font_descriptor_t", pdf_font_descriptor_ptrs,
+ pdf_font_descriptor_data, &st_pdf_resource, 0);
+
+/*
+ * Sub-font descriptors are also pseudo-resources.
+ */
+BASIC_PTRS(pdf_sub_font_descriptor_ptrs) {
+ GC_STRING_ELT(pdf_sub_font_descriptor_t, common.values.FontName)
+};
+gs_public_st_basic_super(st_pdf_sub_font_descriptor,
+ pdf_sub_font_descriptor_t, "pdf_sub_font_descriptor_t",
+ pdf_sub_font_descriptor_ptrs, pdf_sub_font_descriptor_data,
+ &st_pdf_resource, 0);
+
+/* ================ Procedures ================ */
+
+/* ---------------- Private ---------------- */
+
+/* Get the ID of font descriptor metrics. */
+static inline long
+pdf_font_descriptor_common_id(const pdf_font_descriptor_common_t *pfdc)
+{
+ return pdf_resource_id((const pdf_resource_t *)pfdc);
+}
+
+/* Write the common part of a FontDescriptor, aside from the final >>. */
+static int
+write_FontDescriptor_common(gx_device_pdf *pdev,
+ const pdf_font_descriptor_common_t *pfd, bool embed)
+{
+ stream *s;
+ int code;
+ param_printer_params_t params;
+ printer_param_list_t rlist;
+ gs_param_list *const plist = (gs_param_list *)&rlist;
+ char *base14_name = NULL;
+
+ pdf_open_separate(pdev, pdf_font_descriptor_common_id(pfd), resourceFontDescriptor);
+ s = pdev->strm;
+ stream_puts(s, "<</Type/FontDescriptor/FontName");
+ if (!embed) {
+ base14_name = (char *)pdf_find_base14_name(pfd->values.FontName.data, pfd->values.FontName.size);
+ if(base14_name)
+ pdf_put_name(pdev, (byte *)base14_name, strlen(base14_name));
+ else
+ pdf_put_name(pdev, pfd->values.FontName.data, pfd->values.FontName.size);
+ } else
+ pdf_put_name(pdev, pfd->values.FontName.data, pfd->values.FontName.size);
+
+ pdf_write_font_bbox(pdev, &pfd->values.FontBBox);
+ params = param_printer_params_default;
+ code = s_init_param_printer(&rlist, &params, s);
+ if (code >= 0) {
+#define DESC_INT(str, memb)\
+ {str, gs_param_type_int, offset_of(pdf_font_descriptor_common_t, values.memb)}
+ static const gs_param_item_t required_items[] = {
+ DESC_INT("Ascent", Ascent),
+ DESC_INT("CapHeight", CapHeight),
+ DESC_INT("Descent", Descent),
+ DESC_INT("ItalicAngle", ItalicAngle),
+ DESC_INT("StemV", StemV),
+ gs_param_item_end
+ };
+ static const gs_param_item_t optional_items[] = {
+ DESC_INT("AvgWidth", AvgWidth),
+ DESC_INT("Leading", Leading),
+ DESC_INT("MaxWidth", MaxWidth),
+ DESC_INT("MissingWidth", MissingWidth),
+ DESC_INT("StemH", StemH),
+ DESC_INT("XHeight", XHeight),
+ gs_param_item_end
+ };
+#undef DESC_INT
+ int Flags = pfd->values.Flags;
+ pdf_font_descriptor_t defaults;
+
+ if (base14_name)
+ Flags |=FONT_USES_STANDARD_ENCODING;
+
+ code = param_write_int(plist, "Flags", &Flags);
+ if (code < 0)
+ return code;
+ code = gs_param_write_items(plist, pfd, NULL, required_items);
+ if (code < 0)
+ return code;
+ memset(&defaults, 0, sizeof(defaults));
+ code = gs_param_write_items(plist, pfd, &defaults, optional_items);
+ if (code < 0)
+ return code;
+ s_release_param_printer(&rlist);
+ }
+ return 0;
+}
+
+/* ---------------- Public ---------------- */
+
+/*
+ * Allocate a FontDescriptor, initializing the FontType and rid from the
+ * gs_font.
+ */
+int
+pdf_font_descriptor_alloc(gx_device_pdf *pdev, pdf_font_descriptor_t **ppfd,
+ gs_font_base *font, bool embed)
+{
+ pdf_font_descriptor_t *pfd;
+ pdf_base_font_t *pbfont;
+ int code = pdf_base_font_alloc(pdev, &pbfont, font,
+ (font->orig_FontMatrix.xx == 0 && font->orig_FontMatrix.xy == 0
+ ? &font->FontMatrix : &font->orig_FontMatrix), false);
+
+ if (code < 0)
+ return code;
+ code = pdf_alloc_resource(pdev, resourceFontDescriptor,
+ font->id, (pdf_resource_t **)&pfd, -1L);
+ if (code < 0) {
+ gs_free_object(pdev->pdf_memory, pbfont,
+ "pdf_font_descriptor_alloc(base_font)");
+ return code;
+ }
+ memset(&pfd->common.values, 0,
+ sizeof(*pfd) - offset_of(pdf_font_descriptor_t, common.values));
+ pfd->base_font = pbfont;
+ pfd->FontType = font->FontType;
+ pfd->embed = embed;
+ *ppfd = pfd;
+ return 0;
+}
+
+int pdf_font_descriptor_free(gx_device_pdf *pdev, pdf_resource_t *pres)
+{
+ pdf_font_descriptor_t *pfd = (pdf_font_descriptor_t *)pres;
+ pdf_base_font_t *pbfont = pfd->base_font;
+ gs_font *copied = (gs_font *)pbfont->copied, *complete = (gs_font *)pbfont->complete;
+
+ if (complete && copied != complete)
+ gs_free_copied_font(complete);
+
+ if (copied)
+ gs_free_copied_font(copied);
+
+ if (pbfont) {
+ if (pbfont->font_name.size) {
+ gs_free_string(pdev->pdf_memory, pbfont->font_name.data, pbfont->font_name.size, "Free BaseFont FontName string");
+ pbfont->font_name.data = (byte *)0L;
+ pbfont->font_name.size = 0;
+ }
+ gs_free_object(cos_object_memory(pres->object), pbfont, "Free base font from FontDescriptor)");
+ pfd->base_font = 0;
+ }
+ if (pres->object) {
+ gs_free_object(pdev->pdf_memory, pres->object, "free FontDescriptor object");
+ pres->object = NULL;
+ }
+ return 0;
+}
+
+/* Get the object ID of a FontDescriptor. */
+long
+pdf_font_descriptor_id(const pdf_font_descriptor_t *pfd)
+{
+ return pdf_resource_id((const pdf_resource_t *)pfd);
+}
+
+long pdf_set_font_descriptor_usage(gx_device_pdf *pdev, int parent_id, const pdf_font_descriptor_t *pfd)
+{
+ int id = pdf_resource_id((const pdf_resource_t *)pfd);
+
+ pdf_record_usage_by_parent(pdev, id, parent_id);
+ if (pfd->base_font->FontFile) {
+ id = pfd->base_font->FontFile->id;
+ pdf_record_usage_by_parent(pdev, id, parent_id);
+ }
+ return 0;
+}
+
+/*
+ * Get the FontType of a FontDescriptor.
+ */
+font_type
+pdf_font_descriptor_FontType(const pdf_font_descriptor_t *pfd)
+{
+ return pfd->FontType;
+}
+
+/*
+ * Get the embedding status of a FontDescriptor.
+ */
+bool
+pdf_font_descriptor_embedding(const pdf_font_descriptor_t *pfd)
+{
+ return pfd->embed;
+}
+
+/*
+ * Check for subset font.
+ */
+bool
+pdf_font_descriptor_is_subset(const pdf_font_descriptor_t *pfd)
+{
+ return pdf_base_font_is_subset(pfd->base_font);
+}
+
+/*
+ * Return a reference to the FontName of a FontDescriptor, similar to
+ * pdf_base_font_name.
+ */
+gs_string *pdf_font_descriptor_name(pdf_font_descriptor_t *pfd)
+{
+ return &pfd->common.values.FontName;
+}
+
+char *pdf_fontfile_hash(void *pfd)
+{
+ pdf_font_descriptor_t *fd = (pdf_font_descriptor_t *)pfd;
+ cos_dict_t *pcd;
+
+ if (fd->base_font && fd->base_font->FontFile) {
+ pcd = (cos_dict_t *)fd->base_font->FontFile;
+ if (pcd->stream_md5_valid)
+ return ((char *)pcd->stream_hash);
+ else
+ return 0;
+ } else
+ return 0;
+}
+
+/*
+ * Return the (copied, subset or complete) font associated with a FontDescriptor.
+ * This procedure probably shouldn't exist....
+ */
+gs_font_base *
+pdf_font_descriptor_font(const pdf_font_descriptor_t *pfd, bool complete)
+{
+ return pdf_base_font_font(pfd->base_font, complete);
+}
+
+/*
+ * Drop the copied complete font associated with a FontDescriptor.
+ */
+void
+pdf_font_descriptor_drop_complete_font(const pdf_font_descriptor_t *pfd)
+{
+ pdf_base_font_drop_complete(pfd->base_font);
+}
+
+/*
+ * Return a reference to the name of a FontDescriptor's base font, per
+ * pdf_base_font_name.
+ */
+gs_string *pdf_font_descriptor_base_name(const pdf_font_descriptor_t *pfd)
+{
+ return pdf_base_font_name(pfd->base_font);
+}
+
+/*
+ * Copy a glyph from a font to the stable copy. Return 0 if this is a
+ * new glyph, 1 if it was already copied.
+ */
+int
+pdf_font_used_glyph(pdf_font_descriptor_t *pfd, gs_glyph glyph,
+ gs_font_base *font)
+{
+ return pdf_base_font_copy_glyph(pfd->base_font, glyph, font);
+}
+
+/* Compute the FontDescriptor metrics for a font. */
+int
+pdf_compute_font_descriptor(gx_device_pdf *pdev, pdf_font_descriptor_t *pfd)
+{
+ gs_font_base *bfont = pdf_base_font_font(pfd->base_font, false);
+ gs_glyph glyph, notdef;
+ int index;
+ int wmode = bfont->WMode;
+ int members = (GLYPH_INFO_WIDTH0 << wmode) |
+ GLYPH_INFO_BBOX | GLYPH_INFO_NUM_PIECES;
+ pdf_font_descriptor_values_t desc;
+ gs_matrix smat;
+ gs_matrix *pmat = NULL;
+ int fixed_width = 0;
+ int small_descent = 0, small_height = 0;
+ bool small_present = false;
+ int x_height = 0;
+ int cap_height = 0;
+ gs_rect bbox_colon, bbox_period, bbox_I;
+ bool is_cid = (bfont->FontType == ft_CID_encrypted ||
+ bfont->FontType == ft_CID_TrueType);
+ bool have_colon = false, have_period = false, have_I = false;
+ int code;
+
+ memset(&bbox_colon, 0, sizeof(bbox_colon)); /* quiet gcc warnings. */
+ memset(&bbox_period, 0, sizeof(bbox_period)); /* quiet gcc warnings. */
+ memset(&bbox_I, 0, sizeof(bbox_I)); /* quiet gcc warnings. */
+ memset(&desc, 0, sizeof(desc));
+ if (is_cid && bfont->FontBBox.p.x != bfont->FontBBox.q.x &&
+ bfont->FontBBox.p.y != bfont->FontBBox.q.y) {
+ int scale = (bfont->FontType == ft_TrueType || bfont->FontType == ft_CID_TrueType ? 1000 : 1);
+
+ desc.FontBBox.p.x = (int)(bfont->FontBBox.p.x * scale);
+ desc.FontBBox.p.y = (int)(bfont->FontBBox.p.y * scale);
+ desc.FontBBox.q.x = (int)(bfont->FontBBox.q.x * scale);
+ desc.FontBBox.q.y = (int)(bfont->FontBBox.q.y * scale);
+ desc.Ascent = desc.FontBBox.q.y;
+ members &= ~GLYPH_INFO_BBOX;
+ } else {
+ desc.FontBBox.p.x = desc.FontBBox.p.y = max_int;
+ desc.FontBBox.q.x = desc.FontBBox.q.y = min_int;
+ }
+ /*
+ * Embedded TrueType fonts use a 1000-unit character space, but the
+ * font itself uses a 1-unit space. Compensate for this here.
+ */
+ switch (bfont->FontType) {
+ case ft_TrueType:
+ case ft_CID_TrueType:
+ gs_make_scaling(1000.0, 1000.0, &smat);
+ pmat = &smat;
+ /* Type 3 fonts may use a FontMatrix in PDF, so we don't
+ * need to deal with non-standard matrices
+ */
+ case ft_GL2_531:
+ case ft_PCL_user_defined:
+ case ft_GL2_stick_user_defined:
+ case ft_MicroType:
+ case ft_user_defined:
+ break;
+ /* Other font types may use a non-standard (not 1000x1000) design grid
+ * The FontMatrix is used to map to the unit square. However PDF files
+ * don't allow FontMatrix entries, all fonts are nominally 1000x1000.
+ * If we have a font with a non-standard matrix we must account for that
+ * here by scaling the font outline.
+ */
+ default:
+ gs_matrix_scale(&bfont->FontMatrix, 1000.0, 1000.0, &smat);
+ pmat = &smat;
+ break;
+ }
+
+ /*
+ * Scan the entire glyph space to compute Ascent, Descent, FontBBox, and
+ * the fixed width if any. For non-symbolic fonts, also note the
+ * bounding boxes for Latin letters and a couple of other characters,
+ * for computing the remaining descriptor values (CapHeight,
+ * ItalicAngle, StemV, XHeight, and flags SERIF, SCRIPT, ITALIC,
+ * ALL_CAPS, and SMALL_CAPS). (The algorithms are pretty crude.)
+ */
+ notdef = GS_NO_GLYPH;
+ for (index = 0;
+ (bfont->procs.enumerate_glyph((gs_font *)bfont, &index,
+ (is_cid ? GLYPH_SPACE_INDEX : GLYPH_SPACE_NAME), &glyph)) >= 0 &&
+ index != 0;
+ ) {
+ gs_glyph_info_t info;
+ gs_const_string gname;
+ gs_glyph glyph_known_enc;
+ gs_char position=0;
+
+ code = bfont->procs.glyph_info((gs_font *)bfont, glyph, pmat, members, &info);
+ if (code == gs_error_VMerror)
+ return code;
+ if (code < 0) {
+ /*
+ * Since this function may be indirtectly called from gx_device_finalize,
+ * we are unable to propagate error code to the interpreter.
+ * Therefore we skip it here hoping that few errors can be
+ * recovered by the integration through entire glyph set.
+ */
+ continue;
+ }
+ if (members & GLYPH_INFO_BBOX) {
+ /* rect_merge(desc.FontBBox, info.bbox); Expanding due to type cast :*/
+ if (info.bbox.p.x < desc.FontBBox.p.x) desc.FontBBox.p.x = (int)info.bbox.p.x;
+ if (info.bbox.q.x > desc.FontBBox.q.x) desc.FontBBox.q.x = (int)info.bbox.q.x;
+ if (info.bbox.p.y < desc.FontBBox.p.y) desc.FontBBox.p.y = (int)info.bbox.p.y;
+ if (info.bbox.q.y > desc.FontBBox.q.y) desc.FontBBox.q.y = (int)info.bbox.q.y;
+ if (!info.num_pieces)
+ desc.Ascent = max(desc.Ascent, (int)info.bbox.q.y);
+ }
+ if (notdef == GS_NO_GLYPH && gs_font_glyph_is_notdef(bfont, glyph)) {
+ notdef = glyph;
+ desc.MissingWidth = (int)info.width[wmode].x;
+ }
+ if (info.width[wmode].y != 0)
+ fixed_width = min_int;
+ else if (fixed_width == 0)
+ fixed_width = (int)info.width[wmode].x;
+ else if (info.width[wmode].x != fixed_width)
+ fixed_width = min_int;
+ if (desc.Flags & FONT_IS_SYMBOLIC)
+ continue; /* skip Roman-only computation */
+ if (is_cid)
+ continue;
+ code = bfont->procs.glyph_name((gs_font *)bfont, glyph, &gname);
+ if (code < 0) {
+ /* If we fail to get the glyph name, best assume this is a symbolic font */
+ desc.Flags |= FONT_IS_SYMBOLIC;
+ continue;
+ }
+ /* See if the glyph name is in any of the known encodings */
+ glyph_known_enc = gs_c_name_glyph(gname.data, gname.size);
+ if (glyph_known_enc == gs_no_glyph) {
+ desc.Flags |= FONT_IS_SYMBOLIC;
+ continue;
+ }
+ /* Finally check if the encoded glyph is in Standard Encoding */
+ /* gs_c_decode always fails to find .notdef, its always present so
+ * don't worry about it
+ */
+ if(strncmp(".notdef", (const char *)gname.data, gname.size)) {
+ position = gs_c_decode(glyph_known_enc, 0);
+ if (position == GS_NO_CHAR) {
+ desc.Flags |= FONT_IS_SYMBOLIC;
+ continue;
+ }
+ }
+ switch (gname.size) {
+ case 5:
+ if (!memcmp(gname.data, "colon", 5))
+ bbox_colon = info.bbox, have_colon = true;
+ continue;
+ case 6:
+ if (!memcmp(gname.data, "period", 6))
+ bbox_period = info.bbox, have_period = true;
+ continue;
+ case 1:
+ break;
+ default:
+ continue;
+ }
+
+ if (gname.data[0] >= 'A' && gname.data[0] <= 'Z') {
+ cap_height = max(cap_height, (int)info.bbox.q.y);
+ if (gname.data[0] == 'I')
+ bbox_I = info.bbox, have_I = true;
+ } else if (gname.data[0] >= 'a' && gname.data[0] <= 'z') {
+ int y0 = (int)(info.bbox.p.y), y1 = (int)(info.bbox.q.y);
+
+ small_present = true;
+ switch (gname.data[0]) {
+ case 'b': case 'd': case 'f': case 'h':
+ case 'k': case 'l': case 't': /* ascender */
+ small_height = max(small_height, y1);
+ case 'i': /* anomalous ascent */
+ break;
+ case 'j': /* descender with anomalous ascent */
+ small_descent = min(small_descent, y0);
+ break;
+ case 'g': case 'p': case 'q': case 'y': /* descender */
+ small_descent = min(small_descent, y0);
+ default: /* no ascender or descender */
+ x_height = max(x_height, y1);
+ }
+ }
+ }
+ if (!(desc.Flags & FONT_IS_SYMBOLIC)) {
+ desc.Flags |= FONT_IS_ADOBE_ROMAN; /* required if not symbolic */
+ desc.XHeight = (int)x_height;
+ if (!small_present && (!pdev->PDFA != 0 || bfont->FontType != ft_TrueType))
+ desc.Flags |= FONT_IS_ALL_CAPS;
+ desc.CapHeight = cap_height;
+ /*
+ * Look at various glyphs to determine ItalicAngle, StemV,
+ * SERIF, SCRIPT, and ITALIC.
+ */
+ if (have_colon && have_period) {
+ /* Calculate the dominant angle. */
+ int angle =
+ (int)(atan2((bbox_colon.q.y - bbox_colon.p.y) -
+ (bbox_period.q.y - bbox_period.p.y),
+ (bbox_colon.q.x - bbox_colon.p.x) -
+ (bbox_period.q.x - bbox_period.p.x)) *
+ radians_to_degrees) - 90;
+
+ /* Normalize to [-90..90]. */
+ while (angle > 90)
+ angle -= 180;
+ while (angle < -90)
+ angle += 180;
+ if (angle < -30)
+ angle = -30;
+ else if (angle > 30)
+ angle = 30;
+ /*
+ * For script or embellished fonts, we can get an angle that is
+ * slightly off from zero even for non-italic fonts.
+ * Compensate for this now.
+ */
+ if (angle <= 2 && angle >= -2)
+ angle = 0;
+ desc.ItalicAngle = angle;
+ }
+ if (desc.ItalicAngle)
+ desc.Flags |= FONT_IS_ITALIC;
+ if (have_I) {
+ double wdot = bbox_period.q.x - bbox_period.p.x;
+ double wcolon = bbox_I.q.x - bbox_I.p.x;
+ double wI = bbox_period.q.x - bbox_period.p.x;
+
+ desc.StemV = (int)wdot;
+ if (wI > wcolon * 2.5 || wI > (bbox_period.q.y - bbox_period.p.y) * 0.25)
+ desc.Flags |= FONT_IS_SERIF;
+ }
+ }
+ if (desc.Ascent == 0)
+ desc.Ascent = desc.FontBBox.q.y;
+ desc.Descent = desc.FontBBox.p.y;
+ if (!(desc.Flags & (FONT_IS_SYMBOLIC | FONT_IS_ALL_CAPS)) &&
+ (small_descent > desc.Descent / 3 || desc.XHeight > small_height * 0.9) &&
+ (!pdev->PDFA != 0 || bfont->FontType != ft_TrueType)
+ )
+ desc.Flags |= FONT_IS_SMALL_CAPS;
+ if (fixed_width > 0 && (!pdev->PDFA != 0 || bfont->FontType != ft_TrueType)) {
+ desc.Flags |= FONT_IS_FIXED_WIDTH;
+ desc.AvgWidth = desc.MaxWidth = desc.MissingWidth = fixed_width;
+ }
+ if (desc.CapHeight == 0)
+ desc.CapHeight = desc.Ascent;
+ if (desc.StemV == 0)
+ desc.StemV = (int)(desc.FontBBox.q.x * 0.15);
+ pfd->common.values = desc;
+ return 0;
+}
+
+/*
+ * Finish a FontDescriptor by computing the metric values, and then
+ * writing the associated embedded font if any.
+ */
+int
+pdf_finish_FontDescriptor(gx_device_pdf *pdev, pdf_resource_t *pres)
+{
+ pdf_font_descriptor_t *pfd = (pdf_font_descriptor_t *)pres;
+ int code = 0;
+ cos_dict_t *pcd = 0;
+ if (pfd->common.object->id == -1)
+ return 0;
+ if (!pfd->common.object->written &&
+ (code = pdf_compute_font_descriptor(pdev, pfd)) >= 0 &&
+ (!pfd->embed ||
+ (code = pdf_write_embedded_font(pdev, pfd->base_font,
+ pfd->FontType,
+ &pfd->common.values.FontBBox,
+ pfd->common.rid, &pcd)) >= 0)
+ ) {
+ pdf_set_FontFile_object(pfd->base_font, pcd);
+ }
+ return code;
+}
+
+/* Write a FontDescriptor. */
+int
+pdf_write_FontDescriptor(gx_device_pdf *pdev, pdf_resource_t *pres)
+{
+ pdf_font_descriptor_t *pfd = (pdf_font_descriptor_t *)pres;
+ font_type ftype = pfd->FontType;
+ long cidset_id = 0;
+ int code = 0;
+ stream *s;
+
+ if (pfd->common.object->written)
+ return 0;
+ if (pfd->common.object->id == -1)
+ return 0;
+
+ /* If this is a CIDFont subset, write the CIDSet now. */
+ switch (ftype) {
+ case ft_CID_encrypted:
+ case ft_CID_TrueType:
+ if (pdf_do_subset_font(pdev, pfd->base_font, pfd->common.rid)) {
+ if (pdev->PDFA < 2) {
+ code = pdf_write_CIDSet(pdev, pfd->base_font, &cidset_id);
+ if (code < 0)
+ return code;
+ }
+ }
+ default:
+ break;
+ }
+
+ {
+ /*
+ * Hack: make all embedded TrueType fonts "symbolic" to
+ * work around undocumented assumptions in Acrobat Reader.
+ */
+ /* See the comments in pdf_make_font_resource(). If we are embedding a font, its
+ * a TrueType font, we are not subsetting it, *and* the original font was not symbolic,
+ * then force the font to be non-symbolic. Otherwise, yes, force it symbolic.
+ */
+ pdf_font_descriptor_common_t fd;
+
+ fd = pfd->common;
+ if (pfd->embed && pfd->FontType == ft_TrueType) {
+ fd.values.Flags =
+ (fd.values.Flags & ~(FONT_IS_ADOBE_ROMAN)) | FONT_IS_SYMBOLIC;
+
+ if (pfd->base_font->do_subset == DO_SUBSET_NO && ((const gs_font_base *)pfd->base_font)->nearest_encoding_index != ENCODING_INDEX_UNKNOWN) {
+ fd.values.Flags =
+ (fd.values.Flags & ~(FONT_IS_SYMBOLIC)) | FONT_IS_ADOBE_ROMAN;
+ }
+ }
+ code = write_FontDescriptor_common(pdev, &fd, pfd->embed);
+ }
+ if (code < 0)
+ return code;
+ s = pdev->strm;
+ if (cidset_id != 0)
+ pprintld1(s, "/CIDSet %ld 0 R\n", cidset_id);
+ else if (pdf_do_subset_font(pdev, pfd->base_font, pfd->common.rid) &&
+ (ftype == ft_encrypted || ftype == ft_encrypted2)
+ ) {
+ stream_puts(s, "/CharSet");
+ code = pdf_write_CharSet(pdev, pfd->base_font);
+ if (code < 0)
+ return code;
+ }
+ if (pfd->embed && pfd->base_font->FontFile) {
+ code = pdf_write_FontFile_entry(pdev, pfd->base_font);
+ if (code < 0)
+ return code;
+ }
+ if (pfd->cid.Style) {
+ stream_puts(s, "/Style");
+ COS_WRITE(pfd->cid.Style, pdev);
+ }
+ if (pfd->cid.Lang[0]) {
+ pprints1(s, "/Lang(%s)", pfd->cid.Lang);
+ }
+ if (pfd->cid.FD) {
+ stream_puts(s, "/FD");
+ COS_WRITE(pfd->cid.FD, pdev);
+ }
+ stream_puts(s, ">>\n");
+ pdf_end_separate(pdev, resourceFontDescriptor);
+ pfd->common.object->written = true;
+ { const cos_object_t *pco = (const cos_object_t *)pdf_get_FontFile_object(pfd->base_font);
+ if (pco != NULL) {
+ code = COS_WRITE_OBJECT(pco, pdev, resourceFontFile);
+ if (code < 0)
+ return code;
+ }
+ }
+ return 0;
+}
+
+/*
+ * Release a FontDescriptor components.
+ */
+int
+pdf_release_FontDescriptor_components(gx_device_pdf *pdev, pdf_resource_t *pres)
+{
+ pdf_font_descriptor_t *pfd = (pdf_font_descriptor_t *) pres;
+
+ gs_free_object(pdev->pdf_memory, pfd->base_font, "pdf_release_FontDescriptor_components");
+ pfd->base_font = NULL;
+ /* fixme: underimplemented. */
+ return 0;
+}
+
+/*
+ * Mark a FontDescriptor used in a text.
+ */
+int
+pdf_mark_font_descriptor_used(gx_device_pdf *pdev, pdf_font_descriptor_t *pfd)
+{
+ if (pfd != NULL && pfd->common.object->id == -1)
+ pdf_reserve_object_id(pdev, (pdf_resource_t *)&pfd->common, 0);
+ return 0;
+}
+
+/*
+ * Convert True Type font descriptor into CID font descriptor for PDF/A.
+ */
+int
+pdf_convert_truetype_font_descriptor(gx_device_pdf *pdev, pdf_font_resource_t *pdfont)
+{
+ pdf_font_descriptor_t *pfd = pdfont->FontDescriptor;
+ pdf_base_font_t *pbfont = pfd->base_font;
+ gs_font *pfont = (gs_font *)pbfont->copied;
+ gs_char ch;
+ /* Save the simple font descriptor data because CID font data overlap them. */
+ int FirstChar = pdfont->u.simple.FirstChar, LastChar = pdfont->u.simple.LastChar;
+ pdf_encoding_element_t *Encoding = pdfont->u.simple.Encoding;
+ int length_CIDSet = (pbfont->num_glyphs > LastChar ? (pbfont->num_glyphs + 7) / 8 : ((LastChar + 1) + 7 / 8));
+ int length_CIDToGIDMap = (pbfont->num_glyphs > LastChar ? (pbfont->num_glyphs + 1) * sizeof(ushort) : (LastChar + 1) * sizeof(ushort));
+
+ pfd->FontType = ft_CID_TrueType;
+ pdfont->u.simple.Encoding = NULL; /* Drop due to overlapping against a garbager problem. */
+ pbfont->CIDSet = gs_alloc_bytes(pdev->pdf_memory, length_CIDSet,
+ "pdf_convert_truetype_font_descriptor");
+ if (pbfont->CIDSet == NULL)
+ return_error(gs_error_VMerror);
+ memset(pbfont->CIDSet, 0, length_CIDSet);
+ pdfont->u.cidfont.CIDToGIDMap = (ushort *)gs_alloc_bytes(pdev->pdf_memory,
+ length_CIDToGIDMap, "pdf_convert_truetype_font_descriptor");
+ if (pdfont->u.cidfont.CIDToGIDMap == NULL)
+ return_error(gs_error_VMerror);
+ memset(pdfont->u.cidfont.CIDToGIDMap, 0, length_CIDToGIDMap);
+ if(pdev->PDFA) {
+ for (ch = FirstChar; ch <= LastChar; ch++) {
+ if (Encoding[ch].glyph != GS_NO_GLYPH) {
+ gs_glyph glyph = pfont->procs.encode_char(pfont, ch, GLYPH_SPACE_INDEX);
+
+ pbfont->CIDSet[ch / 8] |= 0x80 >> (ch % 8);
+ pdfont->u.cidfont.CIDToGIDMap[ch] = glyph - GS_MIN_GLYPH_INDEX;
+ }
+ }
+ /* Set the CIDSet bit for CID 0 (the /.notdef) which must always be present */
+ pbfont->CIDSet[0] |= 0x80;
+ } else {
+ for (ch = 0; ch <= pbfont->num_glyphs; ch++) {
+ gs_glyph glyph = pfont->procs.encode_char(pfont, ch, GLYPH_SPACE_INDEX);
+
+ pbfont->CIDSet[ch / 8] |= 0x80 >> (ch % 8);
+ pdfont->u.cidfont.CIDToGIDMap[ch] = glyph - GS_MIN_GLYPH_INDEX;
+ }
+ }
+ pbfont->CIDSetLength = length_CIDSet;
+ pdfont->u.cidfont.CIDToGIDMapLength = length_CIDToGIDMap / sizeof(ushort);
+ pdfont->u.cidfont.Widths2 = NULL;
+ pdfont->u.cidfont.used2 = NULL;
+ pdfont->u.cidfont.v = NULL;
+ return 0;
+}
+
+int mark_font_descriptor_symbolic(const pdf_font_resource_t *pdfont)
+{
+ pdf_font_descriptor_values_t *desc;
+
+ if(!pdfont || !pdfont->FontDescriptor)
+ return 0;
+
+ desc = &pdfont->FontDescriptor->common.values;
+
+ if (!(desc->Flags & FONT_IS_SYMBOLIC)) {
+ desc->Flags |= FONT_IS_SYMBOLIC;
+ desc->Flags &= ~FONT_IS_ADOBE_ROMAN;
+ }
+ return 1;
+}
diff --git a/devices/vector/gdevpdtd.h b/devices/vector/gdevpdtd.h
new file mode 100644
index 000000000..0ad885f3f
--- /dev/null
+++ b/devices/vector/gdevpdtd.h
@@ -0,0 +1,169 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* FontDescriptor structure and API for pdfwrite */
+
+#ifndef gdevpdtd_INCLUDED
+# define gdevpdtd_INCLUDED
+
+#include "gdevpdtx.h"
+#include "gdevpdtb.h"
+
+/* ================ Types and structures ================ */
+
+/*
+ * FontDescriptors are handled as pseudo-resources. Multiple Font resources
+ * may share a descriptor. We don't need to use reference counting to keep
+ * track of this, since all descriptors persist until the device is closed.
+ * The CharSet entry in the FontDescriptor for a Type 1 subset font lists
+ * the glyphs that are included in the subset, so the FontDescriptor cannot
+ * be written until the font has been written, but we could use an indirect
+ * object for the CharSet and write the FontDescriptor itself early.
+ * However, we don't think that is worthwhile, since FontDescriptors are
+ * small objects compared to the fonts themselves, and it's simpler to keep
+ * all the FontDescriptors until the end.
+ *
+ * Note that FontDescriptors and BaseFonts correspond 1-to-1. While the PDF
+ * specification allows multiple FontDescriptors for a single BaseFont, this
+ * has no value: all the information in the FontDescriptor is derived from
+ * the BaseFont, so all the FontDescriptors for the same BaseFont must be
+ * the same.
+ */
+/*
+ * Font names in PDF files have caused an enormous amount of trouble, so we
+ * document specifically how they are handled in each structure.
+ *
+ * The PDF Reference says that the FontName in a font descriptor must be
+ * the same as the BaseFont in the font or CIDFont resource(s) that
+ * reference(s) it.
+ *
+ * We never create a font descriptor without also creating a font resource
+ * that references it, so we set the FontName at the same time as the
+ * BaseFont of the font resource. For more information, see gdevpdtf.h.
+ */
+
+#ifndef pdf_font_descriptor_DEFINED
+# define pdf_font_descriptor_DEFINED
+typedef struct pdf_font_descriptor_s pdf_font_descriptor_t;
+#endif
+
+/* ================ Procedures ================ */
+
+/*
+ * Allocate a FontDescriptor, initializing the FontType and rid from the
+ * gs_font.
+ */
+int pdf_font_descriptor_alloc(gx_device_pdf *pdev,
+ pdf_font_descriptor_t **ppfd,
+ gs_font_base *font, bool embed);
+
+int pdf_font_descriptor_free(gx_device_pdf *pdev, pdf_resource_t *pres);
+
+/*
+ * Get the object ID of a FontDescriptor.
+ */
+long pdf_font_descriptor_id(const pdf_font_descriptor_t *pfd);
+
+/*
+ * For Linearised PDF production, set the usage array of the FontDescriptor
+ * resoruce and the FontFile resource, based on teh usage of the font.
+ */
+long pdf_set_font_descriptor_usage(gx_device_pdf *pdev, int parent_id, const pdf_font_descriptor_t *pfd);
+
+/*
+ * Get the FontType of a FontDescriptor.
+ */
+font_type pdf_font_descriptor_FontType(const pdf_font_descriptor_t *pfd);
+
+/*
+ * Get the embedding status of a FontDescriptor.
+ */
+bool pdf_font_descriptor_embedding(const pdf_font_descriptor_t *pfd);
+
+/*
+ * Check for subset font.
+ */
+bool pdf_font_descriptor_is_subset(const pdf_font_descriptor_t *pfd);
+
+/*
+ * Return a reference to the FontName of a FontDescriptor, similar to
+ * pdf_base_font_name.
+ */
+gs_string *pdf_font_descriptor_name(pdf_font_descriptor_t *pfd);
+
+char *pdf_fontfile_hash(void *pfd);
+
+/*
+ * Return the (copied, subset or complete) font associated with a FontDescriptor.
+ * This procedure probably shouldn't exist....
+ */
+gs_font_base *pdf_font_descriptor_font(const pdf_font_descriptor_t *pfd, bool complete);
+
+/*
+ * Drop the copied complete font associated with a FontDescriptor.
+ */
+void pdf_font_descriptor_drop_complete_font(const pdf_font_descriptor_t *pfd);
+
+/*
+ * Return a reference to the name of a FontDescriptor's base font, per
+ * pdf_base_font_name.
+ */
+gs_string *pdf_font_descriptor_base_name(const pdf_font_descriptor_t *pfd);
+
+/*
+ * Copy a glyph from a font to the stable copy. Return 0 if this is a
+ * new glyph, 1 if it was already copied.
+ */
+int pdf_font_used_glyph(pdf_font_descriptor_t *pfd, gs_glyph glyph,
+ gs_font_base *font);
+
+/*
+ * Compute the FontDescriptor metrics for a font.
+ */
+int pdf_compute_font_descriptor(gx_device_pdf *pdev, pdf_font_descriptor_t *pfd);
+
+/*
+ * Finish a FontDescriptor by computing the metric values, and then
+ * writing the associated embedded font if any.
+ */
+int pdf_finish_FontDescriptor(gx_device_pdf *pdev,
+ pdf_resource_t *pfd);
+
+int pdf_finish_resources(gx_device_pdf *pdev, pdf_resource_type_t type,
+ int (*finish_proc)(gx_device_pdf *,
+ pdf_resource_t *));
+/*
+ * Write a FontDescriptor.
+ */
+int pdf_write_FontDescriptor(gx_device_pdf *pdev,
+ pdf_resource_t *pfd);
+
+/*
+ * Release a FontDescriptor components.
+ */
+int pdf_release_FontDescriptor_components(gx_device_pdf *pdev, pdf_resource_t *pfd);
+
+/*
+ * Mark a FontDescriptor used in a text.
+ */
+int pdf_mark_font_descriptor_used(gx_device_pdf *pdev, pdf_font_descriptor_t *pfd);
+
+/*
+ * Mark a FontDescriptor Flags value as symbolic
+ */
+int mark_font_descriptor_symbolic(const pdf_font_resource_t *pdfont);
+
+#endif /* gdevpdtd_INCLUDED */
diff --git a/devices/vector/gdevpdte.c b/devices/vector/gdevpdte.c
new file mode 100644
index 000000000..c8ecb71fc
--- /dev/null
+++ b/devices/vector/gdevpdte.c
@@ -0,0 +1,1481 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Encoding-based (Type 1/2/42) text processing for pdfwrite. */
+
+#include "math_.h"
+#include "memory_.h"
+#include "gx.h"
+#include "gserrors.h"
+#include "gsutil.h"
+#include "gxfcmap.h"
+#include "gxfcopy.h"
+#include "gxfont.h"
+#include "gxfont0.h"
+#include "gxfont0c.h"
+#include "gxpath.h" /* for getting current point */
+#include "gxchar.h" /* for gx_compute_text_oversampling & gx_lookup_cached_char */
+#include "gxfcache.h" /* for gx_lookup_fm_pair */
+#include "gdevpsf.h"
+#include "gdevpdfx.h"
+#include "gdevpdfg.h"
+#include "gdevpdfo.h"
+#include "gdevpdtx.h"
+#include "gdevpdtd.h"
+#include "gdevpdtf.h"
+#include "gdevpdts.h"
+#include "gdevpdtt.h"
+
+#include "gximage.h"
+#include "gxcpath.h"
+
+static int pdf_char_widths(gx_device_pdf *const pdev,
+ pdf_font_resource_t *pdfont, int ch,
+ gs_font_base *font,
+ pdf_glyph_widths_t *pwidths /* may be NULL */);
+static int pdf_process_string(pdf_text_enum_t *penum, gs_string *pstr,
+ const gs_matrix *pfmat,
+ pdf_text_process_state_t *ppts,
+ const gs_glyph *gdata);
+
+/*
+ * Process a string with a simple gs_font.
+ */
+int
+pdf_process_string_aux(pdf_text_enum_t *penum, gs_string *pstr,
+ const gs_glyph *gdata, const gs_matrix *pfmat,
+ pdf_text_process_state_t *ppts)
+{
+ gs_font_base *font = (gs_font_base *)penum->current_font;
+
+ switch (font->FontType) {
+ case ft_TrueType:
+ case ft_encrypted:
+ case ft_encrypted2:
+ case ft_user_defined:
+ case ft_PCL_user_defined:
+ case ft_GL2_stick_user_defined:
+ case ft_GL2_531:
+ case ft_MicroType:
+ break;
+ default:
+ return_error(gs_error_rangecheck);
+ }
+ return pdf_process_string(penum, pstr, pfmat, ppts, gdata);
+}
+
+/*
+ * Add char code pair to ToUnicode CMap,
+ * creating the CMap on neccessity.
+ */
+int
+pdf_add_ToUnicode(gx_device_pdf *pdev, gs_font *font, pdf_font_resource_t *pdfont,
+ gs_glyph glyph, gs_char ch, const gs_const_string *gnstr)
+{ int code;
+ gs_char unicode;
+
+ if (glyph == GS_NO_GLYPH)
+ return 0;
+ unicode = font->procs.decode_glyph((gs_font *)font, glyph, ch);
+ if (unicode == GS_NO_CHAR && gnstr != NULL && gnstr->size == 7) {
+ if (!memcmp(gnstr->data, "uni", 3)) {
+ static const char *hexdigits = "0123456789ABCDEF";
+ char *d0 = strchr(hexdigits, gnstr->data[3]);
+ char *d1 = strchr(hexdigits, gnstr->data[4]);
+ char *d2 = strchr(hexdigits, gnstr->data[5]);
+ char *d3 = strchr(hexdigits, gnstr->data[6]);
+
+ if (d0 != NULL && d1 != NULL && d2 != NULL && d3 != NULL)
+ unicode = ((d0 - hexdigits) << 12) + ((d1 - hexdigits) << 8) +
+ ((d2 - hexdigits) << 4 ) + (d3 - hexdigits);
+ }
+ }
+ if (unicode != GS_NO_CHAR) {
+ if (pdfont->cmap_ToUnicode == NULL) {
+ /* ToUnicode CMaps are always encoded with two byte keys. See
+ * Technical Note 5411, 'ToUnicode Mapping File Tutorial'
+ * page 3.
+ */
+ /* Unfortunately, the above is not true. See the PDF Reference (version 1.7
+ * p 472 'ToUnicode CMaps'. Even that documentation is incorrect as it
+ * describes codespaceranges, in fact for Acrobat this is irrelevant,
+ * but the bfranges must be one byte for simple fonts. By altering the
+ * key size for CID fonts we can write both consistently correct.
+ */
+ uint num_codes = 256, key_size = 1;
+
+ if (font->FontType == ft_CID_encrypted) {
+ gs_font_cid0 *pfcid = (gs_font_cid0 *)font;
+
+ num_codes = pfcid->cidata.common.CIDCount;
+ key_size = 2;
+ } else if (font->FontType == ft_CID_TrueType || font->FontType == ft_composite) {
+ key_size = 2;
+ /* Since PScript5.dll creates GlyphNames2Unicode with character codes
+ instead CIDs, and with the WinCharSetFFFF-H2 CMap
+ character codes appears from the range 0-0xFFFF (Bug 687954),
+ we must use the maximal character code value for the ToUnicode
+ code count. */
+ num_codes = 65536;
+ }
+ code = gs_cmap_ToUnicode_alloc(pdev->pdf_memory, pdfont->rid, num_codes, key_size,
+ &pdfont->cmap_ToUnicode);
+ if (code < 0)
+ return code;
+ }
+ if (pdfont->cmap_ToUnicode != NULL)
+ gs_cmap_ToUnicode_add_pair(pdfont->cmap_ToUnicode, ch, unicode);
+ }
+ return 0;
+}
+
+typedef struct {
+ gx_device_pdf *pdev;
+ pdf_resource_type_t rtype;
+} pdf_resource_enum_data_t;
+
+static int
+process_resources2(void *client_data, const byte *key_data, uint key_size, const cos_value_t *v)
+{
+ pdf_resource_enum_data_t *data = (pdf_resource_enum_data_t *)client_data;
+ pdf_resource_t *pres = pdf_find_resource_by_resource_id(data->pdev, data->rtype, v->contents.object->id);
+
+ if (pres == NULL)
+ return_error(gs_error_unregistered); /* Must not happen. */
+ pres->where_used |= data->pdev->used_mask;
+ return 0;
+}
+
+static int
+process_resources1(void *client_data, const byte *key_data, uint key_size, const cos_value_t *v)
+{
+ pdf_resource_enum_data_t *data = (pdf_resource_enum_data_t *)client_data;
+ static const char *rn[] = {PDF_RESOURCE_TYPE_NAMES};
+ int i;
+
+ for (i = 0; i < count_of(rn); i++) {
+ if (rn[i] != NULL && !bytes_compare((const byte *)rn[i], strlen(rn[i]), key_data, key_size))
+ break;
+ }
+ if (i >= count_of(rn))
+ return 0;
+ data->rtype = i;
+ return cos_dict_forall((cos_dict_t *)v->contents.object, data, process_resources2);
+}
+
+/*
+ * Register charproc fonts with the page or substream.
+ */
+int
+pdf_used_charproc_resources(gx_device_pdf *pdev, pdf_font_resource_t *pdfont)
+{
+ if (pdfont->where_used & pdev->used_mask)
+ return 0;
+ pdfont->where_used |= pdev->used_mask;
+ if (pdev->CompatibilityLevel >= 1.2)
+ return 0;
+ if (pdfont->FontType == ft_user_defined ||
+ pdfont->FontType == ft_PCL_user_defined ||
+ pdfont->FontType == ft_MicroType ||
+ pdfont->FontType == ft_GL2_stick_user_defined ||
+ pdfont->FontType == ft_GL2_531) {
+ pdf_resource_enum_data_t data;
+
+ data.pdev = pdev;
+ return cos_dict_forall(pdfont->u.simple.s.type3.Resources, &data, process_resources1);
+ }
+ return 0;
+}
+
+/*
+ * Given a text string and a simple gs_font, return a font resource suitable
+ * for the text string, possibly re-encoding the string. This
+ * may involve creating a font resource and/or adding glyphs and/or Encoding
+ * entries to it.
+ *
+ * Sets *ppdfont.
+ */
+static int
+pdf_encode_string_element(gx_device_pdf *pdev, gs_font *font, pdf_font_resource_t *pdfont,
+ gs_char ch, const gs_glyph *gdata)
+{
+ gs_font_base *cfont, *ccfont;
+ int code;
+ gs_glyph copied_glyph;
+ gs_const_string gnstr;
+ pdf_encoding_element_t *pet;
+ gs_glyph glyph;
+
+ /*
+ * In contradiction with pre-7.20 versions of pdfwrite,
+ * we never re-encode texts due to possible encoding conflict while font merging.
+ */
+ cfont = pdf_font_resource_font(pdfont, false);
+ ccfont = pdf_font_resource_font(pdfont, true);
+ pet = &pdfont->u.simple.Encoding[ch];
+ glyph = (gdata == NULL ? font->procs.encode_char(font, ch, GLYPH_SPACE_NAME)
+ : *gdata);
+ if (glyph == GS_NO_GLYPH || glyph == pet->glyph)
+ return 0;
+ if (pet->glyph != GS_NO_GLYPH) { /* encoding conflict */
+ return_error(gs_error_rangecheck);
+ /* Must not happen because pdf_obtain_font_resource
+ * checks for encoding compatibility.
+ */
+ }
+ code = font->procs.glyph_name(font, glyph, &gnstr);
+ if (code < 0)
+ return code; /* can't get name of glyph */
+ if (font->FontType != ft_user_defined &&
+ font->FontType != ft_PCL_user_defined &&
+ font->FontType != ft_MicroType &&
+ font->FontType != ft_GL2_stick_user_defined &&
+ font->FontType != ft_GL2_531) {
+ /* The standard 14 fonts don't have a FontDescriptor. */
+ code = (pdfont->base_font != 0 ?
+ pdf_base_font_copy_glyph(pdfont->base_font, glyph, (gs_font_base *)font) :
+ pdf_font_used_glyph(pdfont->FontDescriptor, glyph, (gs_font_base *)font));
+ if (code < 0 && code != gs_error_undefined)
+ return code;
+ if (code == gs_error_undefined) {
+ if (pdev->PDFA != 0 || pdev->PDFX) {
+ switch (pdev->PDFACompatibilityPolicy) {
+ case 0:
+ emprintf(pdev->memory,
+ "Requested glyph not present in source font,\n not permitted in PDF/A, reverting to normal PDF output\n");
+ pdev->AbortPDFAX = true;
+ pdev->PDFA = 0;
+ break;
+ case 1:
+ emprintf(pdev->memory,
+ "Requested glyph not present in source font,\n not permitted in PDF/A, glyph will not be present in output file\n\n");
+ /* Returning an error causees text processing to try and
+ * handle the glyph by rendering to a bitmap instead of
+ * as a glyph in a font. This will eliminate the problem
+ * and the fiel should appear the same as the original.
+ */
+ return gs_error_unknownerror;
+ break;
+ case 2:
+ emprintf(pdev->memory,
+ "Requested glyph not present in source font,\n not permitted in PDF/A, aborting conversion\n");
+ /* Careful here, only certain errors will bubble up
+ * through the text processing.
+ */
+ return gs_error_invalidfont;
+ break;
+ default:
+ emprintf(pdev->memory,
+ "Requested glyph not present in source font,\n not permitted in PDF/A, unrecognised PDFACompatibilityLevel,\nreverting to normal PDF output\n");
+ pdev->AbortPDFAX = true;
+ pdev->PDFA = 0;
+ break;
+ }
+ }
+ /* PS font has no such glyph. */
+ if (bytes_compare(gnstr.data, gnstr.size, (const byte *)".notdef", 7)) {
+ pet->glyph = glyph;
+ pet->str = gnstr;
+ pet->is_difference = true;
+ }
+ } else if (pdfont->base_font == NULL && ccfont != NULL &&
+ (gs_copy_glyph_options(font, glyph, (gs_font *)ccfont, COPY_GLYPH_NO_NEW) != 1 ||
+ gs_copied_font_add_encoding((gs_font *)ccfont, ch, glyph) < 0)) {
+ /*
+ * The "complete" copy of the font appears incomplete
+ * due to incrementally added glyphs. Drop the "complete"
+ * copy now and continue with subset font only.
+ *
+ * Note that we need to add the glyph to the encoding of the
+ * "complete" font, because "PPI-ProPag 2.6.1.4 (archivePg)"
+ * creates multiple font copies with reduced encodings
+ * (we believe it is poorly designed),
+ * and we can merge the copies back to a single font (see Bug 686875).
+ * We also check whether the encoding is compatible.
+ * It must be compatible here due to the pdf_obtain_font_resource
+ * and ccfont logics, but we want to ensure for safety reason.
+ */
+ ccfont = NULL;
+ pdf_font_descriptor_drop_complete_font(pdfont->FontDescriptor);
+ }
+ /*
+ * We arbitrarily allow the first encoded character in a given
+ * position to determine the encoding associated with the copied
+ * font.
+ */
+ copied_glyph = cfont->procs.encode_char((gs_font *)cfont, ch,
+ GLYPH_SPACE_NAME);
+ if (glyph != copied_glyph &&
+ gs_copied_font_add_encoding((gs_font *)cfont, ch, glyph) < 0
+ )
+ pet->is_difference = true;
+ pdfont->used[ch >> 3] |= 0x80 >> (ch & 7);
+ }
+ /*
+ * We always generate ToUnicode for simple fonts, because
+ * we can't detemine in advance, which glyphs the font actually uses.
+ * The decision about writing it out is deferred until pdf_write_font_resource.
+ */
+ code = pdf_add_ToUnicode(pdev, font, pdfont, glyph, ch, &gnstr);
+ if (code < 0)
+ return code;
+ pet->glyph = glyph;
+ pet->str = gnstr;
+ return 0;
+}
+
+/*
+ * Estimate text bbox.
+ */
+static int
+process_text_estimate_bbox(pdf_text_enum_t *pte, gs_font_base *font,
+ const gs_const_string *pstr,
+ const gs_matrix *pfmat,
+ gs_rect *text_bbox, gs_point *pdpt)
+{
+ int i;
+ int space_char =
+ (pte->text.operation & TEXT_ADD_TO_SPACE_WIDTH ?
+ pte->text.space.s_char : -1);
+ int WMode = font->WMode;
+ int code = 0;
+ gs_point total = {0, 0};
+ gs_fixed_point origin;
+ gs_matrix m;
+ int xy_index = pte->xy_index;
+
+ if (font->FontBBox.p.x == font->FontBBox.q.x ||
+ font->FontBBox.p.y == font->FontBBox.q.y)
+ return_error(gs_error_undefined);
+ code = gx_path_current_point(pte->path, &origin);
+ if (code < 0)
+ return code;
+ m = ctm_only(pte->pis);
+ m.tx = fixed2float(origin.x);
+ m.ty = fixed2float(origin.y);
+ gs_matrix_multiply(pfmat, &m, &m);
+ for (i = 0; i < pstr->size; ++i) {
+ byte c = pstr->data[i];
+ gs_rect bbox;
+ gs_point wanted, tpt, p0, p1, p2, p3;
+ gs_glyph glyph = font->procs.encode_char((gs_font *)font, c,
+ GLYPH_SPACE_NAME);
+ gs_glyph_info_t info;
+ int code;
+
+ if (glyph == gs_no_glyph)
+ return_error (gs_error_invalidfont);
+
+ code = font->procs.glyph_info((gs_font *)font, glyph, NULL,
+ GLYPH_INFO_WIDTH0 << WMode,
+ &info);
+
+ /* If we got an undefined error, and its a type 1/CFF font, try to
+ * find the /.notdef glyph and use its width instead (as this is the
+ * glyph which will be rendered). We don't do this for other font types
+ * as it seems Acrobat/Distiller may not do so either.
+ */
+ /* The GL/2 stick font does not supply the enumerate_glyphs method,
+ * *and* leaves it uninitialised. But it should not be possible to
+ * get an undefiend error with this font anyway.
+ */
+ if (code < 0) {
+ if ((font->FontType == ft_encrypted ||
+ font->FontType == ft_encrypted2)) {
+ int index;
+
+ for (index = 0;
+ (font->procs.enumerate_glyph((gs_font *)font, &index,
+ (GLYPH_SPACE_NAME), &glyph)) >= 0 &&
+ index != 0;) {
+
+ if (gs_font_glyph_is_notdef(font, glyph)) {
+ code = font->procs.glyph_info((gs_font *)font, glyph, NULL,
+ GLYPH_INFO_WIDTH0 << WMode,
+ &info);
+
+ if (code < 0)
+ return code;
+ }
+ break;
+ }
+ }
+ if (code < 0)
+ return code;
+ }
+ gs_point_transform(font->FontBBox.p.x, font->FontBBox.p.y, &m, &p0);
+ gs_point_transform(font->FontBBox.p.x, font->FontBBox.q.y, &m, &p1);
+ gs_point_transform(font->FontBBox.q.x, font->FontBBox.p.y, &m, &p2);
+ gs_point_transform(font->FontBBox.q.x, font->FontBBox.q.y, &m, &p3);
+ bbox.p.x = min(min(p0.x, p1.x), min(p1.x, p2.x)) + total.x;
+ bbox.p.y = min(min(p0.y, p1.y), min(p1.y, p2.y)) + total.y;
+ bbox.q.x = max(max(p0.x, p1.x), max(p1.x, p2.x)) + total.x;
+ bbox.q.y = max(max(p0.y, p1.y), max(p1.y, p2.y)) + total.y;
+ if (i == 0)
+ *text_bbox = bbox;
+ else
+ rect_merge(*text_bbox, bbox);
+ if (pte->text.operation & TEXT_REPLACE_WIDTHS) {
+ gs_text_replaced_width(&pte->text, xy_index++, &tpt);
+ gs_distance_transform(tpt.x, tpt.y, &ctm_only(pte->pis), &wanted);
+ } else {
+ gs_distance_transform(info.width[WMode].x,
+ info.width[WMode].y,
+ &m, &wanted);
+ if (pte->text.operation & TEXT_ADD_TO_ALL_WIDTHS) {
+ gs_distance_transform(pte->text.delta_all.x,
+ pte->text.delta_all.y,
+ &ctm_only(pte->pis), &tpt);
+ wanted.x += tpt.x;
+ wanted.y += tpt.y;
+ }
+ if (pstr->data[i] == space_char && pte->text.operation & TEXT_ADD_TO_SPACE_WIDTH) {
+ gs_distance_transform(pte->text.delta_space.x,
+ pte->text.delta_space.y,
+ &ctm_only(pte->pis), &tpt);
+ wanted.x += tpt.x;
+ wanted.y += tpt.y;
+ }
+ }
+ total.x += wanted.x;
+ total.y += wanted.y;
+ }
+ *pdpt = total;
+ return 0;
+}
+
+void
+adjust_first_last_char(pdf_font_resource_t *pdfont, byte *str, int size)
+{
+ int i;
+
+ for (i = 0; i < size; ++i) {
+ int chr = str[i];
+
+ if (chr < pdfont->u.simple.FirstChar)
+ pdfont->u.simple.FirstChar = chr;
+ if (chr > pdfont->u.simple.LastChar)
+ pdfont->u.simple.LastChar = chr;
+ }
+}
+
+int
+pdf_shift_text_currentpoint(pdf_text_enum_t *penum, gs_point *wpt)
+{
+ gs_state *pgs;
+ extern_st(st_gs_state);
+
+ if (gs_object_type(penum->dev->memory, penum->pis) != &st_gs_state) {
+ /* Probably never happens. Not sure though. */
+ return_error(gs_error_unregistered);
+ }
+ pgs = (gs_state *)penum->pis;
+ return gs_moveto_aux(penum->pis, gx_current_path(pgs),
+ fixed2float(penum->origin.x) + wpt->x,
+ fixed2float(penum->origin.y) + wpt->y);
+}
+
+/*
+ * Internal procedure to process a string in a non-composite font.
+ * Doesn't use or set pte->{data,size,index}; may use/set pte->xy_index;
+ * may set penum->returned.total_width. Sets ppts->values.
+ *
+ * Note that the caller is responsible for re-encoding the string, if
+ * necessary; for adding Encoding entries in pdfont; and for copying any
+ * necessary glyphs. penum->current_font provides the gs_font for getting
+ * glyph metrics, but this font's Encoding is not used.
+ */
+static int process_text_return_width(const pdf_text_enum_t *pte,
+ gs_font_base *font,
+ pdf_text_process_state_t *ppts,
+ const gs_const_string *pstr, const gs_glyph *gdata,
+ gs_point *pdpt, int *accepted, gs_rect *bbox);
+static int
+pdf_process_string(pdf_text_enum_t *penum, gs_string *pstr,
+ const gs_matrix *pfmat,
+ pdf_text_process_state_t *ppts, const gs_glyph *gdata)
+{
+ gx_device_pdf *const pdev = (gx_device_pdf *)penum->dev;
+ gs_font_base *font = (gs_font_base *)penum->current_font;
+ pdf_font_resource_t *pdfont;
+ const gs_text_params_t *text = &penum->text;
+ int code = 0, mask;
+ gs_point width_pt;
+ int accepted;
+ gs_rect text_bbox = {{0, 0}, {0, 0}}, glyphs_bbox = {{10000,10000}, {0,0}};
+
+ code = pdf_obtain_font_resource(penum, pstr, &pdfont);
+ if (code < 0)
+ return code;
+ if (pfmat == 0)
+ pfmat = &font->FontMatrix;
+ if (text->operation & TEXT_RETURN_WIDTH) {
+ code = gx_path_current_point(penum->path, &penum->origin);
+ if (code < 0)
+ return code;
+ }
+ if (text->size == 0)
+ return 0;
+ if (penum->pis->text_rendering_mode != 3 && !(text->operation & TEXT_DO_NONE)) {
+ /*
+ * Acrobat Reader can't handle text with huge coordinates,
+ * so skip the text if it is outside the clip bbox
+ * (Note : it ever fails with type 3 fonts).
+ */
+
+ code = process_text_estimate_bbox(penum, font, (gs_const_string *)pstr, pfmat,
+ &text_bbox, &width_pt);
+ if (code == 0) {
+ gs_fixed_rect clip_bbox;
+ gs_rect rect;
+
+ gx_cpath_outer_box(penum->pcpath, &clip_bbox);
+ rect.p.x = fixed2float(clip_bbox.p.x);
+ rect.p.y = fixed2float(clip_bbox.p.y);
+ rect.q.x = fixed2float(clip_bbox.q.x);
+ rect.q.y = fixed2float(clip_bbox.q.y);
+ rect_intersect(rect, text_bbox);
+ if (rect.p.x > rect.q.x || rect.p.y > rect.q.y) {
+ penum->index += pstr->size;
+ goto finish;
+ }
+ } else {
+ gs_matrix m;
+ gs_fixed_point origin;
+ gs_point p0, p1, p2, p3;
+
+ code = gx_path_current_point(penum->path, &origin);
+ m = ctm_only(penum->pis);
+ m.tx = fixed2float(origin.x);
+ m.ty = fixed2float(origin.y);
+ gs_matrix_multiply(pfmat, &m, &m);
+
+ if (font->FontBBox.p.x != font->FontBBox.q.x) {
+ text_bbox.p.x = font->FontBBox.p.x;
+ text_bbox.q.x = font->FontBBox.q.x;
+ } else {
+ text_bbox.p.x = 0;
+ text_bbox.q.x = 1000;
+ }
+ if (font->FontBBox.p.y != font->FontBBox.q.y) {
+ text_bbox.p.y = font->FontBBox.p.y;
+ text_bbox.q.y = font->FontBBox.q.y;
+ } else {
+ text_bbox.p.y = 0;
+ text_bbox.q.y = 1000;
+ }
+ gs_point_transform(text_bbox.p.x, text_bbox.p.y, &m, &p0);
+ gs_point_transform(text_bbox.p.x, text_bbox.q.y, &m, &p1);
+ gs_point_transform(text_bbox.q.x, text_bbox.p.y, &m, &p2);
+ gs_point_transform(text_bbox.q.x, text_bbox.q.y, &m, &p3);
+ text_bbox.p.x = min(min(p0.x, p1.x), min(p1.x, p2.x));
+ text_bbox.p.y = min(min(p0.y, p1.y), min(p1.y, p2.y));
+ text_bbox.q.x = max(max(p0.x, p1.x), max(p1.x, p2.x));
+ text_bbox.q.y = max(max(p0.y, p1.y), max(p1.y, p2.y));
+ }
+ } else {
+ /* We have no penum->pcpath. */
+ }
+
+ /*
+ * Note that pdf_update_text_state sets all the members of ppts->values
+ * to their current values.
+ */
+ code = pdf_update_text_state(ppts, penum, pdfont, pfmat);
+ if (code > 0) {
+ /* Try not to emulate ADD_TO_WIDTH if we don't have to. */
+ if (code & TEXT_ADD_TO_SPACE_WIDTH) {
+ if (!memchr(pstr->data, penum->text.space.s_char, pstr->size))
+ code &= ~TEXT_ADD_TO_SPACE_WIDTH;
+ }
+ }
+ if (code < 0)
+ return code;
+ mask = code;
+
+ if (text->operation & TEXT_REPLACE_WIDTHS)
+ mask |= TEXT_REPLACE_WIDTHS;
+
+ /*
+ * The only operations left to handle are TEXT_DO_DRAW and
+ * TEXT_RETURN_WIDTH.
+ */
+ if (mask == 0) {
+ /*
+ * If any character has real_width != Width, we have to process
+ * the string character-by-character. process_text_return_width
+ * will tell us what we need to know.
+ */
+ if (!(text->operation & (TEXT_DO_DRAW | TEXT_RETURN_WIDTH)))
+ return 0;
+ code = process_text_return_width(penum, font, ppts,
+ (gs_const_string *)pstr, gdata,
+ &width_pt, &accepted, &glyphs_bbox);
+ if (code < 0)
+ return code;
+ if (code == 0) {
+ /* No characters with redefined widths -- the fast case. */
+ if (text->operation & TEXT_DO_DRAW || penum->pis->text_rendering_mode == 3) {
+ code = pdf_append_chars(pdev, pstr->data, accepted,
+ width_pt.x, width_pt.y, false);
+ if (code < 0)
+ return code;
+ adjust_first_last_char(pdfont, pstr->data, accepted);
+ penum->index += accepted;
+ } else if (text->operation & TEXT_DO_NONE)
+ penum->index += accepted;
+ } else {
+ /* Use the slow case. Set mask to any non-zero value. */
+ mask = TEXT_RETURN_WIDTH;
+ }
+ }
+ if (mask) {
+ /* process_text_modify_width destroys text parameters, save them now. */
+ int index0 = penum->index, xy_index = penum->xy_index;
+ gs_text_params_t text = penum->text;
+ int xy_index_step = (!(penum->text.operation & TEXT_REPLACE_WIDTHS) ? 0 :
+ penum->text.x_widths == penum->text.y_widths ? 2 : 1);
+ /* A glyphshow takes a shortcut by storing the single glyph directly into
+ * penum->text.data.d_glyph. However, process_text_modify_width
+ * replaces pte->text.data.bytes (these two are part of a union) with
+ * pstr->data, which is not valid for a glyphshow because it alters
+ * the glyph value store there. If we make a copy of the single glyph,
+ * it all works correctly.then
+ */
+ gs_glyph gdata_i, *gdata_p = (gs_glyph *)gdata;
+ if (penum->text.operation & TEXT_FROM_SINGLE_GLYPH) {
+ gdata_i = *gdata;
+ gdata_p = &gdata_i;
+ }
+
+ if (penum->text.operation & TEXT_REPLACE_WIDTHS) {
+ if (penum->text.x_widths != NULL)
+ penum->text.x_widths += xy_index * xy_index_step;
+ if (penum->text.y_widths != NULL)
+ penum->text.y_widths += xy_index * xy_index_step;
+ }
+ penum->xy_index = 0;
+ code = process_text_modify_width(penum, (gs_font *)font, ppts,
+ (gs_const_string *)pstr,
+ &width_pt, (const gs_glyph *)gdata_p, false, 1);
+ if (penum->text.operation & TEXT_REPLACE_WIDTHS) {
+ if (penum->text.x_widths != NULL)
+ penum->text.x_widths -= xy_index * xy_index_step;
+ if (penum->text.y_widths != NULL)
+ penum->text.y_widths -= xy_index * xy_index_step;
+ }
+ penum->xy_index += xy_index;
+ adjust_first_last_char(pdfont, pstr->data, penum->index);
+ penum->text = text;
+ penum->index += index0;
+ if (code < 0)
+ return code;
+ }
+
+finish:
+ /* Finally, return the total width if requested. */
+ if (pdev->Eps2Write) {
+ gx_device_clip cdev;
+ gx_drawing_color devc;
+ fixed x0, y0, bx2, by2;
+
+ if (glyphs_bbox.p.x != 10000 && glyphs_bbox.q.x != 0){
+ gs_matrix m;
+ gs_fixed_point origin;
+ gs_point p0, p1, p2, p3;
+
+ code = gx_path_current_point(penum->path, &origin);
+ m = ctm_only(penum->pis);
+ m.tx = fixed2float(origin.x);
+ m.ty = fixed2float(origin.y);
+ gs_matrix_multiply(pfmat, &m, &m);
+
+ gs_point_transform(glyphs_bbox.p.x, glyphs_bbox.p.y, &m, &p0);
+ gs_point_transform(glyphs_bbox.p.x, glyphs_bbox.q.y, &m, &p1);
+ gs_point_transform(glyphs_bbox.q.x, glyphs_bbox.p.y, &m, &p2);
+ gs_point_transform(glyphs_bbox.q.x, glyphs_bbox.q.y, &m, &p3);
+ glyphs_bbox.p.x = min(min(p0.x, p1.x), min(p1.x, p2.x));
+ glyphs_bbox.p.y = min(min(p0.y, p1.y), min(p1.y, p2.y));
+ glyphs_bbox.q.x = max(max(p0.x, p1.x), max(p1.x, p2.x));
+ glyphs_bbox.q.y = max(max(p0.y, p1.y), max(p1.y, p2.y));
+ if (glyphs_bbox.p.y > text_bbox.p.y)
+ text_bbox.p.y = glyphs_bbox.p.y;
+ if (glyphs_bbox.q.y < text_bbox.q.y)
+ text_bbox.q.y = glyphs_bbox.q.y;
+ }
+ /* removed this section for bug #695671, where the rotated text
+ * doesn't contribute the 'height' of the text to the x dimension
+ * of the bounding box if this code is present. I can't see why
+ * this clamping was done, if it turns out to be required then
+ * we will need to revisit this and bug #695671.
+ */
+#if 0
+ text_bbox.p.x = fixed2float(penum->origin.x);
+ text_bbox.q.x = text_bbox.p.x + width_pt.x;
+#endif
+ x0 = float2fixed(text_bbox.p.x);
+ y0 = float2fixed(text_bbox.p.y);
+ bx2 = float2fixed(text_bbox.q.x) - x0;
+ by2 = float2fixed(text_bbox.q.y) - y0;
+
+ pdev->AccumulatingBBox++;
+ gx_make_clip_device_on_stack(&cdev, penum->pcpath, (gx_device *)pdev);
+ set_nonclient_dev_color(&devc, gx_device_black((gx_device *)pdev)); /* any non-white color will do */
+ gx_default_fill_triangle((gx_device *) pdev, x0, y0,
+ float2fixed(text_bbox.p.x) - x0,
+ float2fixed(text_bbox.q.y) - y0,
+ bx2, by2, &devc, lop_default);
+ gx_default_fill_triangle((gx_device *) & cdev, x0, y0,
+ float2fixed(text_bbox.q.x) - x0,
+ float2fixed(text_bbox.p.y) - y0,
+ bx2, by2, &devc, lop_default);
+ pdev->AccumulatingBBox--;
+ }
+ if (!(text->operation & TEXT_RETURN_WIDTH))
+ return 0;
+ if (text->operation & TEXT_DO_NONE) {
+ /* stringwidth needs to transform to user space. */
+ gs_point p;
+
+ gs_distance_transform_inverse(width_pt.x, width_pt.y, &ctm_only(penum->pis), &p);
+ penum->returned.total_width.x += p.x;
+ penum->returned.total_width.y += p.y;
+ } else
+ penum->returned.total_width = width_pt;
+ return pdf_shift_text_currentpoint(penum, &width_pt);
+}
+
+/*
+ * Get the widths (unmodified and possibly modified) of a given character
+ * in a simple font. May add the widths to the widths cache (pdfont->Widths
+ * and pdf_font_cache_elem::real_widths). Return 1 if the widths were not cached.
+ */
+static int
+pdf_char_widths(gx_device_pdf *const pdev,
+ pdf_font_resource_t *pdfont, int ch, gs_font_base *font,
+ pdf_glyph_widths_t *pwidths /* may be NULL */)
+{
+ pdf_glyph_widths_t widths;
+ int code;
+ byte *glyph_usage;
+ double *real_widths;
+ int char_cache_size, width_cache_size;
+ pdf_font_resource_t *pdfont1;
+
+ code = pdf_attached_font_resource(pdev, (gs_font *)font, &pdfont1,
+ &glyph_usage, &real_widths, &char_cache_size, &width_cache_size);
+ if (code < 0)
+ return code;
+ if (pdfont1 != pdfont)
+ return_error(gs_error_unregistered); /* Must not happen. */
+ if (ch < 0 || ch > 255)
+ return_error(gs_error_rangecheck);
+ if (ch >= width_cache_size)
+ return_error(gs_error_unregistered); /* Must not happen. */
+ if (pwidths == 0)
+ pwidths = &widths;
+ if ((font->FontType != ft_user_defined &&
+ font->FontType != ft_PCL_user_defined &&
+ font->FontType != ft_MicroType &&
+ font->FontType != ft_GL2_stick_user_defined &&
+ font->FontType != ft_GL2_531) && real_widths[ch] == 0) {
+ /* Might be an unused char, or just not cached. */
+ gs_glyph glyph = pdfont->u.simple.Encoding[ch].glyph;
+
+ code = pdf_glyph_widths(pdfont, font->WMode, glyph, (gs_font *)font, pwidths, NULL);
+ if (code < 0)
+ return code;
+ pwidths->BBox.p.x = pwidths->BBox.p.y = pwidths->BBox.q.x = pwidths->BBox.q.y = 0;
+ if (font->WMode != 0 && code > 0 && !pwidths->replaced_v) {
+ /*
+ * The font has no Metrics2, so it must write
+ * horizontally due to PS spec.
+ * Therefore we need to fill the Widths array,
+ * which is required by PDF spec.
+ * Take it from WMode==0.
+ */
+ code = pdf_glyph_widths(pdfont, 0, glyph, (gs_font *)font, pwidths, NULL);
+ }
+ if (pwidths->replaced_v) {
+ pdfont->u.simple.v[ch].x = pwidths->real_width.v.x - pwidths->Width.v.x;
+ pdfont->u.simple.v[ch].y = pwidths->real_width.v.y - pwidths->Width.v.y;
+ } else
+ pdfont->u.simple.v[ch].x = pdfont->u.simple.v[ch].y = 0;
+ if (code == 0) {
+ pdfont->Widths[ch] = pwidths->Width.w;
+ real_widths[ch] = pwidths->real_width.w;
+ } else {
+ if ((font->WMode == 0 || pwidths->ignore_wmode) && !pwidths->replaced_v)
+ pdfont->Widths[ch] = pwidths->real_width.w;
+ }
+ } else {
+ if (font->FontType == ft_user_defined || font->FontType == ft_PCL_user_defined ||
+ font->FontType == ft_MicroType || font->FontType == ft_GL2_stick_user_defined ||
+ font->FontType == ft_GL2_531) {
+ if (!(pdfont->used[ch >> 3] & 0x80 >> (ch & 7)))
+ return gs_error_undefined; /* The charproc was not accumulated. */
+ if (!pdev->charproc_just_accumulated &&
+ !(pdfont->u.simple.s.type3.cached[ch >> 3] & 0x80 >> (ch & 7))) {
+ /* The charproc uses setcharwidth.
+ Need to accumulate again to check for a glyph variation. */
+ return gs_error_undefined;
+ }
+ }
+ if (pdev->charproc_just_accumulated && font->FontType == ft_user_defined) {
+ pwidths->BBox.p.x = pdev->charproc_BBox.p.x;
+ pwidths->BBox.p.y = pdev->charproc_BBox.p.y;
+ pwidths->BBox.q.x = pdev->charproc_BBox.q.x;
+ pwidths->BBox.q.y = pdev->charproc_BBox.q.y;
+ }
+ pwidths->Width.w = pdfont->Widths[ch];
+ pwidths->Width.v = pdfont->u.simple.v[ch];
+ pwidths->real_width.v.x = pwidths->real_width.v.y = 0;
+ pwidths->ignore_wmode = false;
+ if (font->FontType == ft_user_defined || font->FontType == ft_PCL_user_defined ||
+ font->FontType == ft_MicroType || font->FontType == ft_GL2_stick_user_defined ||
+ font->FontType == ft_GL2_531) {
+ pwidths->real_width.w = real_widths[ch * 2];
+ pwidths->Width.xy.x = pwidths->Width.w;
+ pwidths->Width.xy.y = 0;
+ pwidths->real_width.xy.x = real_widths[ch * 2 + 0];
+ pwidths->real_width.xy.y = real_widths[ch * 2 + 1];
+ pwidths->replaced_v = 0;
+ } else if (font->WMode) {
+ pwidths->real_width.w = real_widths[ch];
+ pwidths->Width.xy.x = 0;
+ pwidths->Width.xy.y = pwidths->Width.w;
+ pwidths->real_width.xy.x = 0;
+ pwidths->real_width.xy.y = pwidths->real_width.w;
+ } else {
+ pwidths->real_width.w = real_widths[ch];
+ pwidths->Width.xy.x = pwidths->Width.w;
+ pwidths->Width.xy.y = 0;
+ pwidths->real_width.xy.x = pwidths->real_width.w;
+ pwidths->real_width.xy.y = 0;
+ }
+ code = 0;
+ }
+ return code;
+}
+
+/*
+ * Convert glyph widths (.Width.xy and .real_widths.xy) from design to PDF text space
+ * Zero-out one of Width.xy.x/y per PDF Ref 5.3.3 "Text Space Details"
+ */
+static void
+pdf_char_widths_to_uts(pdf_font_resource_t *pdfont /* may be NULL for non-Type3 */,
+ pdf_glyph_widths_t *pwidths)
+{
+ if (pdfont && (pdfont->FontType == ft_user_defined ||
+ pdfont->FontType == ft_PCL_user_defined ||
+ pdfont->FontType == ft_MicroType ||
+ pdfont->FontType == ft_GL2_stick_user_defined ||
+ pdfont->FontType == ft_GL2_531)) {
+ gs_matrix *pmat = &pdfont->u.simple.s.type3.FontMatrix;
+
+ pwidths->Width.xy.x *= pmat->xx; /* formula simplified based on wy in glyph space == 0 */
+ pwidths->Width.xy.y = 0.0; /* WMode == 0 for PDF Type 3 fonts */
+ gs_distance_transform(pwidths->real_width.xy.x, pwidths->real_width.xy.y, pmat, &pwidths->real_width.xy);
+ } else {
+ /*
+ * For other font types:
+ * - PDF design->text space is a simple scaling by 0.001.
+ * - The Width.xy.x/y that should be zeroed-out per 5.3.3 "Text Space Details" is already 0.
+ */
+ pwidths->Width.xy.x /= 1000.0;
+ pwidths->Width.xy.y /= 1000.0;
+ pwidths->real_width.xy.x /= 1000.0;
+ pwidths->real_width.xy.y /= 1000.0;
+ }
+}
+
+/*
+ * Compute the total text width (in user space). Return 1 if any
+ * character had real_width != Width, otherwise 0.
+ */
+static int
+process_text_return_width(const pdf_text_enum_t *pte, gs_font_base *font,
+ pdf_text_process_state_t *ppts,
+ const gs_const_string *pstr, const gs_glyph *gdata,
+ gs_point *pdpt, int *accepted, gs_rect *bbox)
+{
+ int i;
+ gs_point w;
+ gs_point dpt;
+ int num_spaces = 0;
+ int space_char =
+ (pte->text.operation & TEXT_ADD_TO_SPACE_WIDTH ?
+ pte->text.space.s_char : -1);
+ int widths_differ = 0, code;
+ gx_device_pdf *pdev = (gx_device_pdf *)pte->dev;
+ pdf_font_resource_t *pdfont;
+
+ code = pdf_attached_font_resource(pdev, (gs_font *)font, &pdfont, NULL, NULL, NULL, NULL);
+ if (code < 0)
+ return code;
+ for (i = 0, w.x = w.y = 0; i < pstr->size; ++i) {
+ pdf_glyph_widths_t cw; /* in PDF text space */
+ gs_char ch = pstr->data[i];
+
+ /* Initialise some variables */
+ cw.real_width.xy.x = cw.real_width.xy.y = cw.Width.xy.x = cw.Width.xy.y = 0;
+ cw.BBox.p.x = cw.BBox.p.y = cw.BBox.q.x = cw.BBox.q.y = 0;
+
+ { const gs_glyph *gdata_i = (gdata != NULL ? gdata + i : 0);
+
+ code = pdf_encode_string_element(pdev, (gs_font *)font, pdfont, ch, gdata_i);
+ if (code < 0)
+ return code;
+ }
+ if ((font->FontType == ft_user_defined ||
+ font->FontType == ft_PCL_user_defined ||
+ font->FontType == ft_GL2_stick_user_defined ||
+ font->FontType == ft_MicroType ||
+ font->FontType == ft_GL2_531) &&
+ (i > 0 || !pdev->charproc_just_accumulated) &&
+ !(pdfont->u.simple.s.type3.cached[ch >> 3] & (0x80 >> (ch & 7)))){
+ code = gs_error_undefined;
+ }
+ else {
+ if (font->FontType == ft_PCL_user_defined) {
+ /* Check the cache, if the glyph has been flushed, assume that
+ * it has been redefined, and do not use the current glyph.
+ * Additional code in pdf_text_process will also spot this
+ * condition and will not capture the glyph in this font.
+ */
+ /* Cache checking code copied from gxchar.c, show_proceed,
+ * case 0, 'plain char'.
+ */
+ gs_font *rfont = (pte->fstack.depth < 0 ? pte->current_font : pte->fstack.items[0].font);
+ gs_font *pfont = (pte->fstack.depth < 0 ? pte->current_font :
+ pte->fstack.items[pte->fstack.depth].font);
+ int wmode = rfont->WMode;
+ gs_log2_scale_point log2_scale = {0,0};
+ gs_fixed_point subpix_origin = {0,0};
+ cached_fm_pair *pair;
+
+ code = gx_lookup_fm_pair(pfont, &ctm_only(pte->pis), &log2_scale,
+ false, &pair);
+ if (code < 0)
+ return code;
+ if (gx_lookup_cached_char(pfont, pair, ch, wmode,
+ 1, &subpix_origin) == 0) {
+ /* Character is not in cache, must have been redefined. */
+ code = gs_error_undefined;
+ }
+ else {
+ /* Character is in cache, go ahead and use it */
+ code = pdf_char_widths((gx_device_pdf *)pte->dev,
+ ppts->values.pdfont, ch, font, &cw);
+ }
+ } else
+ /* Not a PCL bitmap font, we don't need to worry about redefined glyphs */
+ code = pdf_char_widths((gx_device_pdf *)pte->dev,
+ ppts->values.pdfont, ch, font, &cw);
+ }
+ if (code < 0) {
+ if (i)
+ break;
+ *accepted = 0;
+ return code;
+ }
+ pdf_char_widths_to_uts(pdfont, &cw);
+ w.x += cw.real_width.xy.x;
+ w.y += cw.real_width.xy.y;
+ if (cw.real_width.xy.x != cw.Width.xy.x ||
+ cw.real_width.xy.y != cw.Width.xy.y
+ )
+ widths_differ = 1;
+ if (pstr->data[i] == space_char)
+ ++num_spaces;
+ if (cw.BBox.p.x != 0 && cw.BBox.q.x != 0){
+ if (cw.BBox.p.x < bbox->p.x)
+ bbox->p.x = cw.BBox.p.x;
+ if (cw.BBox.p.y < bbox->p.y)
+ bbox->p.y = cw.BBox.p.y;
+ if (cw.BBox.q.x > bbox->q.x)
+ bbox->q.x = cw.BBox.q.x;
+ if (cw.BBox.q.y > bbox->q.y)
+ bbox->q.y = cw.BBox.q.y;
+ }
+ }
+ *accepted = i;
+ gs_distance_transform(w.x * ppts->values.size, w.y * ppts->values.size,
+ &ppts->values.matrix, &dpt);
+ if (pte->text.operation & TEXT_ADD_TO_ALL_WIDTHS) {
+ int num_chars = *accepted;
+ gs_point tpt;
+
+ gs_distance_transform(pte->text.delta_all.x, pte->text.delta_all.y,
+ &ctm_only(pte->pis), &tpt);
+ dpt.x += tpt.x * num_chars;
+ dpt.y += tpt.y * num_chars;
+ }
+ if (pte->text.operation & TEXT_ADD_TO_SPACE_WIDTH) {
+ gs_point tpt;
+
+ gs_distance_transform(pte->text.delta_space.x, pte->text.delta_space.y,
+ &ctm_only(pte->pis), &tpt);
+ dpt.x += tpt.x * num_spaces;
+ dpt.y += tpt.y * num_spaces;
+ }
+ *pdpt = dpt;
+
+ return widths_differ;
+}
+
+/*
+ * Emulate TEXT_ADD_TO_ALL_WIDTHS and/or TEXT_ADD_TO_SPACE_WIDTH,
+ * and implement TEXT_REPLACE_WIDTHS if requested.
+ * Uses and updates ppts->values.matrix; uses ppts->values.pdfont.
+ *
+ * Destroys the text parameters in *pte.
+ * The caller must restore them.
+ */
+int
+process_text_modify_width(pdf_text_enum_t *pte, gs_font *font,
+ pdf_text_process_state_t *ppts,
+ const gs_const_string *pstr,
+ gs_point *pdpt, const gs_glyph *gdata, bool composite, int decoded_bytes)
+{
+ gx_device_pdf *const pdev = (gx_device_pdf *)pte->dev;
+ int space_char =
+ (pte->text.operation & TEXT_ADD_TO_SPACE_WIDTH ?
+ pte->text.space.s_char : -1);
+ gs_point start, total;
+ pdf_font_resource_t *pdfont3 = NULL;
+ int code;
+
+ if (font->FontType == ft_user_defined ||
+ font->FontType == ft_PCL_user_defined ||
+ font->FontType == ft_MicroType ||
+ font->FontType == ft_GL2_stick_user_defined ||
+ font->FontType == ft_GL2_531) {
+ code = pdf_attached_font_resource(pdev, font, &pdfont3, NULL, NULL, NULL, NULL);
+ if (code < 0)
+ return code;
+
+ }
+ pte->text.data.bytes = pstr->data;
+ pte->text.size = pstr->size;
+ pte->index = 0;
+ pte->text.operation &= ~TEXT_FROM_ANY;
+ pte->text.operation |= TEXT_FROM_STRING;
+ start.x = ppts->values.matrix.tx;
+ start.y = ppts->values.matrix.ty;
+ total.x = total.y = 0; /* user space */
+ /*
+ * Note that character widths are in design space, but text.delta_*
+ * values and the width value returned in *pdpt are in user space,
+ * and the width values for pdf_append_chars are in device space.
+ */
+ for (;;) {
+ pdf_glyph_widths_t cw; /* design space, then converted to PDF text space */
+ gs_point did, wanted, tpt; /* user space */
+ gs_point v = {0, 0}; /* design space */
+ gs_char chr;
+ gs_glyph glyph;
+ int index = pte->index;
+ gs_text_enum_t pte1 = *(gs_text_enum_t *)pte;
+ int FontType;
+ bool use_cached_v = true;
+ byte composite_type3_text[1];
+
+ code = pte1.orig_font->procs.next_char_glyph(&pte1, &chr, &glyph);
+ if (code == 2) { /* end of string */
+ gs_text_enum_copy_dynamic((gs_text_enum_t *)pte, &pte1, true);
+ break;
+ }
+ if (code < 0)
+ return code;
+ if (composite) { /* from process_cmap_text */
+ gs_font *subfont = pte1.fstack.items[pte1.fstack.depth].font;
+
+ if (subfont->FontType == ft_user_defined) {
+ pdf_font_resource_t *pdfont;
+
+ FontType = subfont->FontType;
+ code = pdf_attached_font_resource(pdev, subfont,
+ &pdfont, NULL, NULL, NULL, NULL);
+ if (code < 0)
+ return code;
+ chr = pdf_find_glyph(pdfont, glyph);
+ composite_type3_text[0] = (byte)chr;
+ code = pdf_char_widths((gx_device_pdf *)pte->dev,
+ ppts->values.pdfont, chr, (gs_font_base *)subfont,
+ &cw);
+ } else {
+ pdf_font_resource_t *pdsubf = ppts->values.pdfont->u.type0.DescendantFont;
+
+ FontType = pdsubf->FontType;
+ code = pdf_glyph_widths(pdsubf, font->WMode, glyph, subfont, &cw,
+ pte->cdevproc_callout ? pte->cdevproc_result : NULL);
+ }
+ } else {/* must be a base font */
+ const gs_glyph *gdata_i = (gdata != NULL ? gdata + pte->index : 0);
+
+ /* gdata is NULL when composite == true, or the text isn't a single byte. */
+ code = pdf_encode_string_element(pdev, font, ppts->values.pdfont, chr, gdata_i);
+ FontType = font->FontType;
+ if (code >= 0) {
+ if (chr == GS_NO_CHAR && glyph != GS_NO_GLYPH) {
+ /* glyphshow, we have no char code. Bug 686988.*/
+ code = pdf_glyph_widths(ppts->values.pdfont, font->WMode, glyph, font, &cw, NULL);
+ use_cached_v = false; /* Since we have no chr and don't call pdf_char_widths. */
+ } else {
+ code = pdf_char_widths((gx_device_pdf *)pte->dev,
+ ppts->values.pdfont, chr, (gs_font_base *)font,
+ &cw);
+ if (code == 0 && font->FontType == ft_PCL_user_defined) {
+ /* Check the cache, if the glyph has been flushed, assume that
+ * it has been redefined, and do not use the current glyph.
+ * Additional code in pdf_text_process will also spot this
+ * condition and will not capture the glyph in this font.
+ */
+ /* Cache checking code copied from gxchar.c, show_proceed,
+ * case 0, 'plain char'.
+ */
+ gs_font *rfont = (pte->fstack.depth < 0 ? pte->current_font : pte->fstack.items[0].font);
+ gs_font *pfont = (pte->fstack.depth < 0 ? pte->current_font :
+ pte->fstack.items[pte->fstack.depth].font);
+ int wmode = rfont->WMode;
+ gs_log2_scale_point log2_scale = {0,0};
+ gs_fixed_point subpix_origin = {0,0};
+ cached_fm_pair *pair;
+
+ code = gx_lookup_fm_pair(pfont, &ctm_only(pte->pis), &log2_scale,
+ false, &pair);
+ if (code < 0)
+ return code;
+ if (gx_lookup_cached_char(pfont, pair, chr, wmode,
+ 1, &subpix_origin) == 0) {
+ /* Character is not in cache, must have been redefined. */
+ code = gs_error_undefined;
+ }
+ }
+ }
+ }
+ }
+ if (code < 0) {
+ if (index > 0)
+ break;
+ return code;
+ }
+ /* TrueType design grid is 2048x2048 against the nominal PS/PDF grid of
+ * 1000x1000. This can lead to rounding errors when converting to text space
+ * and comparing against any entries in /W or /Widths arrays. We fix the
+ * TrueType widths to the nearest integer here to avoid this.
+ * See Bug #693825
+ */
+ if (FontType == ft_CID_TrueType || FontType == ft_TrueType) {
+ cw.Width.w = floor(cw.Width.w + 0.5);
+ cw.Width.xy.x = floor(cw.Width.xy.x + 0.5);
+ cw.Width.xy.y = floor(cw.Width.xy.y + 0.5);
+ cw.Width.v.x = floor(cw.Width.v.x + 0.5);
+ cw.Width.v.y = floor(cw.Width.v.y + 0.5);
+ cw.real_width.w = floor(cw.real_width.w + 0.5);
+ cw.real_width.xy.x = floor(cw.real_width.xy.x + 0.5);
+ cw.real_width.xy.y = floor(cw.real_width.xy.y + 0.5);
+ cw.real_width.v.x = floor(cw.real_width.v.x + 0.5);
+ cw.real_width.v.y = floor(cw.real_width.v.y + 0.5);
+ }
+
+ gs_text_enum_copy_dynamic((gs_text_enum_t *)pte, &pte1, true);
+ if (composite || !use_cached_v) {
+ if (cw.replaced_v) {
+ v.x = cw.real_width.v.x - cw.Width.v.x;
+ v.y = cw.real_width.v.y - cw.Width.v.y;
+ }
+ } else
+ v = ppts->values.pdfont->u.simple.v[chr];
+ if (font->WMode && !cw.ignore_wmode) {
+ /* With WMode 1 v-vector is (WMode 1 origin) - (WMode 0 origin).
+ The glyph shifts in the opposite direction. */
+ v.x = - v.x;
+ v.y = - v.y;
+ } else {
+ /* With WMode 0 v-vector is (Metrics sb) - (native sb).
+ The glyph shifts in same direction. */
+ }
+ /* pdf_glyph_origin is not longer used. */
+ if (v.x != 0 || v.y != 0) {
+ gs_point glyph_origin_shift;
+ double scale0;
+
+ if (FontType == ft_TrueType || FontType == ft_CID_TrueType)
+ scale0 = (float)0.001;
+ else
+ scale0 = 1;
+ glyph_origin_shift.x = v.x * scale0;
+ glyph_origin_shift.y = v.y * scale0;
+ if (composite) {
+ gs_font *subfont = pte->fstack.items[pte->fstack.depth].font;
+
+ gs_distance_transform(glyph_origin_shift.x, glyph_origin_shift.y,
+ &subfont->FontMatrix, &glyph_origin_shift);
+ }
+ gs_distance_transform(glyph_origin_shift.x, glyph_origin_shift.y,
+ &font->FontMatrix, &glyph_origin_shift);
+ gs_distance_transform(glyph_origin_shift.x, glyph_origin_shift.y,
+ &ctm_only(pte->pis), &glyph_origin_shift);
+ if (glyph_origin_shift.x != 0 || glyph_origin_shift.y != 0) {
+ ppts->values.matrix.tx = start.x + total.x + glyph_origin_shift.x;
+ ppts->values.matrix.ty = start.y + total.y + glyph_origin_shift.y;
+ code = pdf_set_text_state_values(pdev, &ppts->values);
+ if (code < 0)
+ break;
+ }
+ }
+ pdf_char_widths_to_uts(pdfont3, &cw); /* convert design->text space */
+ if (pte->text.operation & (TEXT_DO_DRAW | TEXT_RENDER_MODE_3)) {
+ gs_distance_transform(cw.Width.xy.x * ppts->values.size,
+ cw.Width.xy.y * ppts->values.size,
+ &ppts->values.matrix, &did);
+ gs_distance_transform(((font->WMode && !cw.ignore_wmode) ? 0 : ppts->values.character_spacing),
+ ((font->WMode && !cw.ignore_wmode) ? ppts->values.character_spacing : 0),
+ &ppts->values.matrix, &tpt);
+ did.x += tpt.x;
+ did.y += tpt.y;
+ /* If pte->single_byte_space == 0 then we had a widthshow or awidthshow from
+ * PostScript, so we apply the PostScript rules. Otherwise it was from PDF
+ * in which case if the number of bytes in the character code was 1 we apply
+ * word spacing. If it was PDF and we had a multi-byte decode, do not apply
+ * word spacing (how ugly!). Note tht its important this is applied the same to
+ * both the 'did' and 'wanted' calculations (see below).
+ */
+ if (chr == space_char && (!pte->single_byte_space || decoded_bytes == 1)) {
+ gs_distance_transform(((font->WMode && !cw.ignore_wmode)? 0 : ppts->values.word_spacing),
+ ((font->WMode && !cw.ignore_wmode) ? ppts->values.word_spacing : 0),
+ &ppts->values.matrix, &tpt);
+ did.x += tpt.x;
+ did.y += tpt.y;
+ }
+ if (composite && FontType == ft_user_defined)
+ code = pdf_append_chars(pdev, composite_type3_text, 1, did.x, did.y, composite);
+ else
+ code = pdf_append_chars(pdev, pstr->data + index, pte->index - index, did.x, did.y, composite);
+ if (code < 0)
+ break;
+ } else
+ did.x = did.y = 0;
+ if (pte->text.operation & TEXT_REPLACE_WIDTHS) {
+ gs_point dpt;
+
+ code = gs_text_replaced_width(&pte->text, pte->xy_index++, &dpt);
+ if (code < 0)
+ return_error(gs_error_unregistered);
+ gs_distance_transform(dpt.x, dpt.y, &ctm_only(pte->pis), &wanted);
+ } else {
+ gs_distance_transform(cw.real_width.xy.x * ppts->values.size,
+ cw.real_width.xy.y * ppts->values.size,
+ &ppts->values.matrix, &wanted);
+ if (pte->text.operation & TEXT_ADD_TO_ALL_WIDTHS) {
+ gs_distance_transform(pte->text.delta_all.x,
+ pte->text.delta_all.y,
+ &ctm_only(pte->pis), &tpt);
+ wanted.x += tpt.x;
+ wanted.y += tpt.y;
+ }
+ /* See comment above for 'did' calculations, the application of word spacing must
+ * be the same for did and wanted.
+ */
+ if (chr == space_char && (!pte->single_byte_space || decoded_bytes == 1)) {
+ gs_distance_transform(pte->text.delta_space.x,
+ pte->text.delta_space.y,
+ &ctm_only(pte->pis), &tpt);
+ wanted.x += tpt.x;
+ wanted.y += tpt.y;
+ }
+ }
+ total.x += wanted.x;
+ total.y += wanted.y;
+ if (wanted.x != did.x || wanted.y != did.y) {
+ ppts->values.matrix.tx = start.x + total.x;
+ ppts->values.matrix.ty = start.y + total.y;
+ code = pdf_set_text_state_values(pdev, &ppts->values);
+ if (code < 0)
+ break;
+ }
+ pdev->charproc_just_accumulated = false;
+ }
+ *pdpt = total;
+ return 0;
+}
+
+/*
+ * Get character code from a glyph code.
+ * An usage of this function is very undesirable,
+ * because a glyph may be unlisted in Encoding.
+ */
+int
+pdf_encode_glyph(gs_font_base *bfont, gs_glyph glyph0,
+ byte *buf, int buf_size, int *char_code_length)
+{
+ gs_char c;
+
+ *char_code_length = 1;
+ if (*char_code_length > buf_size)
+ return_error(gs_error_rangecheck); /* Must not happen. */
+ for (c = 0; c < 255; c++) {
+ gs_glyph glyph1 = bfont->procs.encode_char((gs_font *)bfont, c,
+ GLYPH_SPACE_NAME);
+ if (glyph1 == glyph0) {
+ buf[0] = (byte)c;
+ return 0;
+ }
+ }
+ return_error(gs_error_rangecheck); /* Can't encode. */
+}
+
+/* ---------------- Type 1 or TrueType font ---------------- */
+
+/*
+ * Process a text string in a simple font.
+ */
+int
+process_plain_text(gs_text_enum_t *pte, void *vbuf, uint bsize)
+{
+ byte *const buf = vbuf;
+ uint count;
+ uint operation = pte->text.operation;
+ pdf_text_enum_t *penum = (pdf_text_enum_t *)pte;
+ int code;
+ gs_string str;
+ pdf_text_process_state_t text_state;
+ const gs_glyph *gdata = NULL;
+
+ if (operation & (TEXT_FROM_STRING | TEXT_FROM_BYTES)) {
+ count = pte->text.size - pte->index;
+ if (bsize < count)
+ return_error(gs_error_unregistered); /* Must not happen. */
+ memcpy(buf, (const byte *)pte->text.data.bytes + pte->index, count);
+ } else if (operation & (TEXT_FROM_CHARS | TEXT_FROM_SINGLE_CHAR)) {
+ /* Check that all chars fit in a single byte. */
+ const gs_char *cdata;
+ int i;
+
+ if (operation & TEXT_FROM_CHARS) {
+ cdata = pte->text.data.chars;
+ count = (pte->text.size - pte->index);
+ } else {
+ cdata = &pte->text.data.d_char;
+ count = 1;
+ }
+ if (bsize < count * sizeof(gs_char))
+ return_error(gs_error_unregistered); /* Must not happen. */
+ for (i = 0; i < count; ++i) {
+ gs_char chr = cdata[pte->index + i];
+
+ if (chr & ~0xff)
+ return_error(gs_error_rangecheck);
+ buf[i] = (byte)chr;
+ }
+ } else if (operation & (TEXT_FROM_GLYPHS | TEXT_FROM_SINGLE_GLYPH)) {
+ /*
+ * Since PDF has no analogue of 'glyphshow',
+ * we try to encode glyphs with the current
+ * font's encoding. If the current font has no encoding,
+ * or the encoding doesn't contain necessary glyphs,
+ * the text will be represented with a Type 3 font with
+ * bitmaps or outlines.
+ *
+ * When we fail with encoding (136-01.ps is an example),
+ * we could locate a PDF font resource or create a new one
+ * with same outlines and an appropriate encoding.
+ * Also we could change .notdef entries in the
+ * copied font (assuming that document designer didn't use
+ * .notdef for a meanful printing).
+ * fixme: Not implemented yet.
+ */
+ gs_font *font = pte->current_font;
+ uint size;
+ int i;
+
+ if (operation & TEXT_FROM_GLYPHS) {
+ gdata = pte->text.data.glyphs;
+ size = pte->text.size - pte->index;
+ } else {
+ gdata = &pte->text.data.d_glyph;
+ size = 1;
+ }
+ if (!pdf_is_simple_font(font))
+ return_error(gs_error_unregistered); /* Must not happen. */
+ count = 0;
+ for (i = 0; i < size; ++i) {
+ pdf_font_resource_t *pdfont;
+ gs_glyph glyph = gdata[pte->index + i];
+ int char_code_length;
+
+ code = pdf_encode_glyph((gs_font_base *)font, glyph,
+ buf + count, size - count, &char_code_length);
+ if (code < 0)
+ break;
+ /* Even if we already have a glyph encoded at this position in the font
+ * it may not be the *right* glyph. We effectively use the first byte of
+ * the glyph name as the index when using glyphshow which means that
+ * /o and /omicron would be encoded at the same index. So we need
+ * to check the actual glyph to see if they are the same. To do
+ * that we need the PDF font resource which is attached to the font (if any).
+ * cf bugs #695259 and #695168
+ */
+ code = pdf_attached_font_resource((gx_device_pdf *)penum->dev, font,
+ &pdfont, NULL, NULL, NULL, NULL);
+ if (code >= 0 && pdfont && pdfont->u.simple.Encoding[*(buf + count)].glyph != glyph)
+ /* the glyph doesn't match the glyph already encoded at this position.
+ * Breaking out here will start a new PDF font resource in the code below.
+ */
+ break;
+ count += char_code_length;
+ if (operation & TEXT_INTERVENE)
+ break; /* Just do one character. */
+ }
+ if (i < size) {
+ pdf_font_resource_t *pdfont;
+
+ str.data = buf;
+ str.size = size;
+ code = pdf_obtain_font_resource_unencoded(penum, &str, &pdfont, gdata);
+ if (code < 0) {
+ /*
+ * pdf_text_process will fall back
+ * to default implementation.
+ */
+ return code;
+ }
+ count = size;
+ }
+ /* So far we will use TEXT_FROM_STRING instead
+ TEXT_FROM_*_GLYPH*. Since we used a single
+ byte encoding, the character index appears invariant
+ during this substitution.
+ */
+ } else
+ return_error(gs_error_rangecheck);
+ str.data = buf;
+ if (count > 1 && (operation & TEXT_INTERVENE)) {
+ /* Just do one character. */
+ str.size = 1;
+ code = pdf_process_string_aux(penum, &str, gdata, NULL, &text_state);
+ if (code >= 0) {
+ pte->returned.current_char = buf[0];
+ code = TEXT_PROCESS_INTERVENE;
+ }
+ } else {
+ str.size = count;
+ code = pdf_process_string_aux(penum, &str, gdata, NULL, &text_state);
+ }
+ return code;
+}
diff --git a/devices/vector/gdevpdtf.c b/devices/vector/gdevpdtf.c
new file mode 100644
index 000000000..d2145aa29
--- /dev/null
+++ b/devices/vector/gdevpdtf.c
@@ -0,0 +1,1210 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Font and CMap resource implementation for pdfwrite text */
+#include "memory_.h"
+#include "gx.h"
+#include "gserrors.h"
+#include "gsutil.h" /* for bytes_compare */
+#include "gxfcache.h" /* for orig_fonts list */
+#include "gxfcid.h"
+#include "gxfcmap.h"
+#include "gxfcopy.h"
+#include "gxfont.h"
+#include "gxfont1.h"
+#include "gdevpsf.h"
+#include "gdevpdfx.h"
+#include "gdevpdtb.h"
+#include "gdevpdtd.h"
+#include "gdevpdtf.h"
+#include "gdevpdtw.h"
+#include "gdevpdti.h"
+#include "whitelst.h" /* Checks whether protected fonta cna be embedded */
+
+/* GC descriptors */
+public_st_pdf_font_resource();
+private_st_pdf_encoding1();
+private_st_pdf_encoding_element();
+private_st_pdf_standard_font();
+private_st_pdf_standard_font_element();
+private_st_pdf_outline_fonts();
+
+static
+ENUM_PTRS_WITH(pdf_font_resource_enum_ptrs, pdf_font_resource_t *pdfont)
+ENUM_PREFIX(st_pdf_resource, 12);
+case 0: return ENUM_STRING(&pdfont->BaseFont);
+case 1: ENUM_RETURN(pdfont->FontDescriptor);
+case 2: ENUM_RETURN(pdfont->base_font);
+case 3: ENUM_RETURN(pdfont->Widths);
+case 4: ENUM_RETURN(pdfont->used);
+case 5: ENUM_RETURN(pdfont->res_ToUnicode);
+case 6: ENUM_RETURN(pdfont->cmap_ToUnicode);
+case 7: switch (pdfont->FontType) {
+ case ft_composite:
+ ENUM_RETURN(pdfont->u.type0.DescendantFont);
+ case ft_CID_encrypted:
+ case ft_CID_TrueType:
+ ENUM_RETURN(pdfont->u.cidfont.Widths2);
+ default:
+ pdf_mark_glyph_names(pdfont, mem);
+ ENUM_RETURN(pdfont->u.simple.Encoding);
+}
+case 8: switch (pdfont->FontType) {
+ case ft_composite:
+ return (pdfont->u.type0.cmap_is_standard ? ENUM_OBJ(0) :
+ ENUM_CONST_STRING(&pdfont->u.type0.CMapName));
+ case ft_encrypted:
+ case ft_encrypted2:
+ case ft_TrueType:
+ case ft_PCL_user_defined:
+ case ft_MicroType:
+ case ft_GL2_stick_user_defined:
+ case ft_user_defined:
+ case ft_GL2_531:
+ ENUM_RETURN(pdfont->u.simple.v);
+ case ft_CID_encrypted:
+ case ft_CID_TrueType:
+ ENUM_RETURN(pdfont->u.cidfont.v);
+ default:
+ ENUM_RETURN(0);
+}
+case 9: switch (pdfont->FontType) {
+ case ft_PCL_user_defined:
+ case ft_MicroType:
+ case ft_GL2_stick_user_defined:
+ case ft_user_defined:
+ case ft_GL2_531:
+ ENUM_RETURN(pdfont->u.simple.s.type3.char_procs);
+ case ft_CID_encrypted:
+ case ft_CID_TrueType:
+ ENUM_RETURN(pdfont->u.cidfont.CIDToGIDMap);
+ default:
+ ENUM_RETURN(0);
+}
+case 10: switch (pdfont->FontType) {
+ case ft_PCL_user_defined:
+ case ft_MicroType:
+ case ft_GL2_stick_user_defined:
+ case ft_user_defined:
+ case ft_GL2_531:
+ ENUM_RETURN(pdfont->u.simple.s.type3.cached);
+ case ft_CID_encrypted:
+ case ft_CID_TrueType:
+ ENUM_RETURN(pdfont->u.cidfont.parent);
+ default:
+ ENUM_RETURN(0);
+}
+case 11: switch (pdfont->FontType) {
+ case ft_PCL_user_defined:
+ case ft_MicroType:
+ case ft_GL2_stick_user_defined:
+ case ft_user_defined:
+ case ft_GL2_531:
+ ENUM_RETURN(pdfont->u.simple.s.type3.Resources);
+ case ft_CID_encrypted:
+ case ft_CID_TrueType:
+ ENUM_RETURN(pdfont->u.cidfont.used2);
+ default:
+ ENUM_RETURN(0);
+}
+ENUM_PTRS_END
+static
+RELOC_PTRS_WITH(pdf_font_resource_reloc_ptrs, pdf_font_resource_t *pdfont)
+{
+ RELOC_PREFIX(st_pdf_resource);
+ RELOC_STRING_VAR(pdfont->BaseFont);
+ RELOC_VAR(pdfont->FontDescriptor);
+ RELOC_VAR(pdfont->base_font);
+ RELOC_VAR(pdfont->Widths);
+ RELOC_VAR(pdfont->used);
+ RELOC_VAR(pdfont->res_ToUnicode);
+ RELOC_VAR(pdfont->cmap_ToUnicode);
+ switch (pdfont->FontType) {
+ case ft_composite:
+ if (!pdfont->u.type0.cmap_is_standard)
+ RELOC_CONST_STRING_VAR(pdfont->u.type0.CMapName);
+ RELOC_VAR(pdfont->u.type0.DescendantFont);
+ break;
+ case ft_PCL_user_defined:
+ case ft_MicroType:
+ case ft_GL2_stick_user_defined:
+ case ft_user_defined:
+ case ft_GL2_531:
+ RELOC_VAR(pdfont->u.simple.Encoding);
+ RELOC_VAR(pdfont->u.simple.v);
+ RELOC_VAR(pdfont->u.simple.s.type3.char_procs);
+ RELOC_VAR(pdfont->u.simple.s.type3.cached);
+ RELOC_VAR(pdfont->u.simple.s.type3.Resources);
+ break;
+ case ft_CID_encrypted:
+ case ft_CID_TrueType:
+ RELOC_VAR(pdfont->u.cidfont.Widths2);
+ RELOC_VAR(pdfont->u.cidfont.v);
+ RELOC_VAR(pdfont->u.cidfont.CIDToGIDMap);
+ RELOC_VAR(pdfont->u.cidfont.parent);
+ RELOC_VAR(pdfont->u.cidfont.used2);
+ break;
+ default:
+ RELOC_VAR(pdfont->u.simple.Encoding);
+ RELOC_VAR(pdfont->u.simple.v);
+ break;
+ }
+}
+RELOC_PTRS_END
+
+/* ---------------- Standard fonts ---------------- */
+
+/* ------ Private ------ */
+
+/* Define the 14 standard built-in fonts. */
+#define PDF_NUM_STANDARD_FONTS 14
+typedef struct pdf_standard_font_info_s {
+ const char *fname;
+ int size;
+ gs_encoding_index_t base_encoding;
+} pdf_standard_font_info_t;
+static const pdf_standard_font_info_t standard_font_info[] = {
+ {"Courier", 7, ENCODING_INDEX_STANDARD},
+ {"Courier-Bold", 12, ENCODING_INDEX_STANDARD},
+ {"Courier-Oblique", 15, ENCODING_INDEX_STANDARD},
+ {"Courier-BoldOblique", 19, ENCODING_INDEX_STANDARD},
+ {"Helvetica", 9, ENCODING_INDEX_STANDARD},
+ {"Helvetica-Bold", 14, ENCODING_INDEX_STANDARD},
+ {"Helvetica-Oblique", 17, ENCODING_INDEX_STANDARD},
+ {"Helvetica-BoldOblique", 21, ENCODING_INDEX_STANDARD},
+ {"Symbol", 6, ENCODING_INDEX_SYMBOL},
+ {"Times-Roman", 11, ENCODING_INDEX_STANDARD},
+ {"Times-Bold", 10, ENCODING_INDEX_STANDARD},
+ {"Times-Italic", 12, ENCODING_INDEX_STANDARD},
+ {"Times-BoldItalic", 16, ENCODING_INDEX_STANDARD},
+ {"ZapfDingbats", 12, ENCODING_INDEX_DINGBATS},
+ {0}
+};
+
+/* Return the index of a standard font name, or -1 if missing. */
+static int
+pdf_find_standard_font_name(const byte *str, uint size)
+{
+ const pdf_standard_font_info_t *ppsf;
+
+ for (ppsf = standard_font_info; ppsf->fname; ++ppsf)
+ if (ppsf->size == size &&
+ !memcmp(ppsf->fname, (const char *)str, size)
+ )
+ return ppsf - standard_font_info;
+ return -1;
+}
+
+/*
+ * If there is a standard font with the same appearance (CharStrings,
+ * Private, WeightVector) as the given font, set *psame to the mask of
+ * identical properties, and return the standard-font index; otherwise,
+ * set *psame to 0 and return -1.
+ */
+static int
+find_std_appearance(const gx_device_pdf *pdev, gs_font_base *bfont,
+ int mask, pdf_char_glyph_pair_t *pairs, int num_glyphs)
+{
+ bool has_uid = uid_is_UniqueID(&bfont->UID) && bfont->UID.id != 0;
+ const pdf_standard_font_t *psf = pdf_standard_fonts(pdev);
+ int i;
+
+ switch (bfont->FontType) {
+ case ft_encrypted:
+ case ft_encrypted2:
+ case ft_TrueType:
+ break;
+ default:
+ return -1;
+ }
+
+ for (i = 0; i < PDF_NUM_STANDARD_FONTS; ++psf, ++i) {
+ gs_font_base *cfont;
+ int code;
+
+ if (!psf->pdfont)
+ continue;
+ cfont = pdf_font_resource_font(psf->pdfont, false);
+ if (has_uid) {
+ /*
+ * Require the UIDs to match. The PostScript spec says this
+ * is the case iff the outlines are the same.
+ */
+ if (!uid_equal(&bfont->UID, &cfont->UID))
+ continue;
+ }
+ /*
+ * Require the actual outlines to match (within the given subset).
+ */
+ code = gs_copied_can_copy_glyphs((const gs_font *)cfont,
+ (const gs_font *)bfont,
+ &pairs[0].glyph, num_glyphs,
+ sizeof(pdf_char_glyph_pair_t), true);
+ if (code == gs_error_unregistered) /* Debug purpose only. */
+ return code;
+ /* Note: code < 0 means an error. Skip it here. */
+ if (code > 0)
+ return i;
+ }
+ return -1;
+}
+
+/*
+ * Scan a font directory for standard fonts. Return true if any new ones
+ * were found. A font is recognized as standard if it was loaded as a
+ * resource, it has a UniqueId, and it has a standard name.
+ */
+static bool
+scan_for_standard_fonts(gx_device_pdf *pdev, const gs_font_dir *dir)
+{
+ bool found = false;
+ gs_font *orig = dir->orig_fonts;
+
+ for (; orig; orig = orig->next) {
+ gs_font_base *obfont;
+
+ if (orig->FontType == ft_composite || !orig->is_resource)
+ continue;
+ obfont = (gs_font_base *)orig;
+ if (uid_is_UniqueID(&obfont->UID)) {
+ /* Is it one of the standard fonts? */
+ int i = pdf_find_standard_font_name(orig->key_name.chars,
+ orig->key_name.size);
+
+ if (i >= 0 && pdf_standard_fonts(pdev)[i].pdfont == 0) {
+ pdf_font_resource_t *pdfont;
+ int code = pdf_font_std_alloc(pdev, &pdfont, true, orig->id, obfont,
+ i);
+
+ if (code < 0)
+ continue;
+ found = true;
+ }
+ }
+ }
+ return found;
+}
+
+/* ---------------- Initialization ---------------- */
+
+/*
+ * Allocate and initialize bookkeeping for outline fonts.
+ */
+pdf_outline_fonts_t *
+pdf_outline_fonts_alloc(gs_memory_t *mem)
+{
+ pdf_outline_fonts_t *pofs =
+ gs_alloc_struct(mem, pdf_outline_fonts_t, &st_pdf_outline_fonts,
+ "pdf_outline_fonts_alloc(outline_fonts)");
+ pdf_standard_font_t *ppsf =
+ gs_alloc_struct_array(mem, PDF_NUM_STANDARD_FONTS,
+ pdf_standard_font_t,
+ &st_pdf_standard_font_element,
+ "pdf_outline_fonts_alloc(standard_fonts)");
+
+ if (pofs == 0 || ppsf == 0)
+ return 0;
+ memset(ppsf, 0, PDF_NUM_STANDARD_FONTS * sizeof(*ppsf));
+ memset(pofs, 0, sizeof(*pofs));
+ pofs->standard_fonts = ppsf;
+ return pofs;
+}
+
+/*
+ * Return the standard fonts array.
+ */
+pdf_standard_font_t *
+pdf_standard_fonts(const gx_device_pdf *pdev)
+{
+ return pdev->text->outline_fonts->standard_fonts;
+}
+
+/*
+ * Clean the standard fonts array.
+ */
+void
+pdf_clean_standard_fonts(const gx_device_pdf *pdev)
+{
+ pdf_standard_font_t *ppsf = pdf_standard_fonts(pdev);
+
+ memset(ppsf, 0, PDF_NUM_STANDARD_FONTS * sizeof(*ppsf));
+}
+
+/* ---------------- Font resources ---------------- */
+
+/* ------ Private ------ */
+
+static int
+pdf_resize_array(gs_memory_t *mem, void **p, int elem_size, int old_size, int new_size)
+{
+ void *q = gs_alloc_byte_array(mem, new_size, elem_size, "pdf_resize_array");
+
+ if (q == NULL)
+ return_error(gs_error_VMerror);
+ memset((char *)q + elem_size * old_size, 0, elem_size * (new_size - old_size));
+ memcpy(q, *p, elem_size * old_size);
+ gs_free_object(mem, *p, "pdf_resize_array");
+ *p = q;
+ return 0;
+}
+
+/*
+ * Allocate and (minimally) initialize a font resource.
+ */
+static int
+font_resource_alloc(gx_device_pdf *pdev, pdf_font_resource_t **ppfres,
+ pdf_resource_type_t rtype, gs_id rid, font_type ftype,
+ int chars_count,
+ pdf_font_write_contents_proc_t write_contents)
+{
+ gs_memory_t *mem = pdev->pdf_memory;
+ pdf_font_resource_t *pfres;
+ double *widths = 0;
+ byte *used = 0;
+ int code;
+ bool is_CID_font = (ftype == ft_CID_encrypted || ftype == ft_CID_TrueType);
+
+ if (chars_count != 0) {
+ uint size = (chars_count + 7) / 8;
+
+ if (!is_CID_font) {
+ widths = (void *)gs_alloc_byte_array(mem, chars_count, sizeof(*widths),
+ "font_resource_alloc(Widths)");
+ } else {
+ /* Delay allocation because we don't know which WMode will be used. */
+ }
+ used = gs_alloc_bytes(mem, size, "font_resource_alloc(used)");
+ if ((!is_CID_font && widths == 0) || used == 0) {
+ code = gs_note_error(gs_error_VMerror);
+ goto fail;
+ }
+ if (!is_CID_font)
+ memset(widths, 0, chars_count * sizeof(*widths));
+ memset(used, 0, size);
+ }
+ code = pdf_alloc_resource(pdev, rtype, rid, (pdf_resource_t **)&pfres, -1L);
+ if (code < 0)
+ goto fail;
+ memset((byte *)pfres + sizeof(pdf_resource_t), 0,
+ sizeof(*pfres) - sizeof(pdf_resource_t));
+ pfres->FontType = ftype;
+ pfres->count = chars_count;
+ pfres->Widths = widths;
+ pfres->used = used;
+ pfres->write_contents = write_contents;
+ pfres->res_ToUnicode = NULL;
+ pfres->cmap_ToUnicode = NULL;
+ pfres->mark_glyph = 0;
+ pfres->mark_glyph_data = 0;
+ *ppfres = pfres;
+ return 0;
+ fail:
+ gs_free_object(mem, used, "font_resource_alloc(used)");
+ gs_free_object(mem, widths, "font_resource_alloc(Widths)");
+ return code;
+}
+
+int font_resource_free(gx_device_pdf *pdev, pdf_font_resource_t *pdfont)
+{
+ if(pdfont->BaseFont.size
+ && (pdfont->base_font == NULL || !pdfont->base_font->is_standard)) {
+
+ gs_free_string(pdev->pdf_memory, pdfont->BaseFont.data, pdfont->BaseFont.size, "Free BaseFont string");
+ pdfont->BaseFont.data = (byte *)0L;
+ pdfont->BaseFont.size = 0;
+ }
+ if(pdfont->Widths) {
+ gs_free_object(pdev->pdf_memory, pdfont->Widths, "Free Widths array");
+ pdfont->Widths = 0;
+ }
+ if(pdfont->used) {
+ gs_free_object(pdev->pdf_memory, pdfont->used, "Free used array");
+ pdfont->used = 0;
+ }
+ if(pdfont->res_ToUnicode) {
+ /* ToUnicode resources are tracked amd released separately */
+ pdfont->res_ToUnicode = 0;
+ }
+ if(pdfont->cmap_ToUnicode) {
+ gs_cmap_ToUnicode_free(pdev->pdf_memory, pdfont->cmap_ToUnicode);
+ pdfont->cmap_ToUnicode = 0;
+ }
+ switch(pdfont->FontType) {
+ case ft_composite:
+ break;
+ case ft_PCL_user_defined:
+ case ft_MicroType:
+ case ft_GL2_stick_user_defined:
+ case ft_user_defined:
+ case ft_GL2_531:
+ if(pdfont->u.simple.Encoding) {
+ gs_free_object(pdev->pdf_memory, pdfont->u.simple.Encoding, "Free simple Encoding");
+ pdfont->u.simple.Encoding = 0;
+ }
+ if(pdfont->u.simple.v) {
+ gs_free_object(pdev->pdf_memory, pdfont->u.simple.v, "Free simple v");
+ pdfont->u.simple.v = 0;
+ }
+ if (pdfont->u.simple.s.type3.char_procs) {
+ pdf_free_charproc_ownership(pdev, (pdf_resource_t *)pdfont->u.simple.s.type3.char_procs);
+ pdfont->u.simple.s.type3.char_procs = 0;
+ }
+ break;
+ case ft_CID_encrypted:
+ case ft_CID_TrueType:
+ if(pdfont->u.cidfont.used2) {
+ gs_free_object(pdev->pdf_memory, pdfont->u.cidfont.used2, "Free CIDFont used2");
+ pdfont->u.cidfont.used2 = 0;
+ }
+ if(pdfont->u.cidfont.CIDToGIDMap) {
+ gs_free_object(pdev->pdf_memory, pdfont->u.cidfont.CIDToGIDMap, "Free CIDToGID map");
+ pdfont->u.cidfont.CIDToGIDMap = 0;
+ }
+ break;
+ default:
+ if(pdfont->u.simple.Encoding) {
+ gs_free_object(pdev->pdf_memory, pdfont->u.simple.Encoding, "Free simple Encoding");
+ pdfont->u.simple.Encoding = 0;
+ }
+ if(pdfont->u.simple.v) {
+ gs_free_object(pdev->pdf_memory, pdfont->u.simple.v, "Free simple v");
+ pdfont->u.simple.v = 0;
+ }
+ break;
+ }
+ if (pdfont->object) {
+ gs_free_object(pdev->pdf_memory, pdfont->object, "Free font resource object");
+ pdfont->object = 0;
+ }
+ /* We free FontDescriptor resources separately */
+ if(pdfont->FontDescriptor)
+ pdfont->FontDescriptor = NULL;
+ else {
+ if (pdfont->base_font) {
+ /* Normally we free the 'base font' when we free the Font Descriptor,
+ * but if we have no font descriptor (we are not embedding the font),
+ * we won't free the copies of the font, or any other associated memory,
+ * so do it now.
+ */
+ pdf_base_font_t *pbfont = pdfont->base_font;
+ gs_font *copied = (gs_font *)pbfont->copied, *complete = (gs_font *)pbfont->complete;
+
+ if (copied)
+ gs_free_copied_font(copied);
+ if (complete && copied != complete) {
+ gs_free_copied_font(complete);
+ pbfont->complete = 0;
+ }
+ pbfont->copied = 0;
+ if (pbfont && pbfont->font_name.size) {
+ gs_free_string(pdev->pdf_memory, pbfont->font_name.data, pbfont->font_name.size, "Free BaseFont FontName string");
+ pbfont->font_name.data = (byte *)0L;
+ pbfont->font_name.size = 0;
+ }
+ if (pbfont) {
+ gs_free_object(pdev->pdf_memory, pbfont, "Free base font from FontDescriptor)");
+ pdfont->base_font = 0;
+ }
+ }
+ }
+ return 0;
+}
+
+int
+pdf_assign_font_object_id(gx_device_pdf *pdev, pdf_font_resource_t *pdfont)
+{
+ if (pdf_resource_id((pdf_resource_t *)pdfont) == -1) {
+ int code;
+
+ pdf_reserve_object_id(pdev, (pdf_resource_t *)pdfont, 0);
+ code = pdf_mark_font_descriptor_used(pdev, pdfont->FontDescriptor);
+ if (code < 0)
+ return code;
+ if (pdfont->FontType == 0) {
+ pdf_font_resource_t *pdfont1 = pdfont->u.type0.DescendantFont;
+
+ if (pdf_font_id(pdfont1) == -1) {
+ pdf_reserve_object_id(pdev, (pdf_resource_t *)pdfont1, 0);
+ code = pdf_mark_font_descriptor_used(pdev, pdfont1->FontDescriptor);
+ if (code < 0)
+ return code;
+ }
+ }
+ }
+ return 0;
+}
+
+static int
+font_resource_simple_alloc(gx_device_pdf *pdev, pdf_font_resource_t **ppfres,
+ gs_id rid, font_type ftype, int chars_count,
+ pdf_font_write_contents_proc_t write_contents)
+{
+ pdf_font_resource_t *pfres;
+ int code = font_resource_alloc(pdev, &pfres, resourceFont, rid, ftype,
+ chars_count, write_contents);
+
+ if (code < 0)
+ return code;
+ pfres->u.simple.FirstChar = 256;
+ pfres->u.simple.LastChar = -1;
+ pfres->u.simple.BaseEncoding = -1;
+ pfres->u.simple.preferred_encoding_index = -1;
+ pfres->u.simple.last_reserved_char = -1;
+ *ppfres = pfres;
+ return 0;
+}
+int
+font_resource_encoded_alloc(gx_device_pdf *pdev, pdf_font_resource_t **ppfres,
+ gs_id rid, font_type ftype,
+ pdf_font_write_contents_proc_t write_contents)
+{
+ pdf_encoding_element_t *Encoding =
+ gs_alloc_struct_array(pdev->pdf_memory, 256, pdf_encoding_element_t,
+ &st_pdf_encoding_element,
+ "font_resource_encoded_alloc");
+ gs_point *v = (gs_point *)gs_alloc_byte_array(pdev->pdf_memory,
+ 256, sizeof(gs_point), "pdf_font_simple_alloc");
+ pdf_font_resource_t *pdfont;
+ int code, i;
+
+ if (v == 0 || Encoding == 0) {
+ gs_free_object(pdev->pdf_memory, Encoding,
+ "font_resource_encoded_alloc");
+ gs_free_object(pdev->pdf_memory, v,
+ "font_resource_encoded_alloc");
+ return_error(gs_error_VMerror);
+ }
+ code = font_resource_simple_alloc(pdev, &pdfont, rid, ftype,
+ 256, write_contents);
+ if (code < 0) {
+ gs_free_object(pdev->pdf_memory, Encoding,
+ "font_resource_encoded_alloc");
+ gs_free_object(pdev->pdf_memory, v,
+ "font_resource_encoded_alloc");
+ return_error(gs_error_VMerror);
+ }
+ memset(v, 0, 256 * sizeof(*v));
+ memset(Encoding, 0, 256 * sizeof(*Encoding));
+ for (i = 0; i < 256; ++i)
+ Encoding[i].glyph = GS_NO_GLYPH;
+ pdfont->u.simple.Encoding = Encoding;
+ pdfont->u.simple.v = v;
+ *ppfres = pdfont;
+ return 0;
+}
+
+/*
+ * Record whether a Type 1 or Type 2 font is a Multiple Master instance.
+ */
+static void
+set_is_MM_instance(pdf_font_resource_t *pdfont, const gs_font_base *pfont)
+{
+ switch (pfont->FontType) {
+ case ft_encrypted:
+ case ft_encrypted2:
+ pdfont->u.simple.s.type1.is_MM_instance =
+ ((const gs_font_type1 *)pfont)->data.WeightVector.count > 0;
+ default:
+ break;
+ }
+}
+
+/* ------ Generic public ------ */
+
+/* Resize font resource arrays. */
+int
+pdf_resize_resource_arrays(gx_device_pdf *pdev, pdf_font_resource_t *pfres, int chars_count)
+{
+ /* This function fixes CID fonts that provide a lesser CIDCount than
+ CIDs used in a document. Rather PS requires to print CID=0,
+ we need to provide a bigger CIDCount since we don't
+ re-encode the text. The text should look fine if the
+ viewer application substitutes the font. */
+ gs_memory_t *mem = pdev->pdf_memory;
+ int code;
+
+ if (chars_count < pfres->count)
+ return 0;
+ if (pfres->Widths != NULL) {
+ code = pdf_resize_array(mem, (void **)&pfres->Widths, sizeof(*pfres->Widths),
+ pfres->count, chars_count);
+ if (code < 0)
+ return code;
+ }
+ code = pdf_resize_array(mem, (void **)&pfres->used, sizeof(*pfres->used),
+ (pfres->count + 7) / 8, (chars_count + 7) / 8);
+ if (code < 0)
+ return code;
+ if (pfres->FontType == ft_CID_encrypted || pfres->FontType == ft_CID_TrueType) {
+ if (pfres->u.cidfont.v != NULL) {
+ code = pdf_resize_array(mem, (void **)&pfres->u.cidfont.v,
+ sizeof(*pfres->u.cidfont.v), pfres->count * 2, chars_count * 2);
+ if (code < 0)
+ return code;
+ }
+ if (pfres->u.cidfont.Widths2 != NULL) {
+ code = pdf_resize_array(mem, (void **)&pfres->u.cidfont.Widths2,
+ sizeof(*pfres->u.cidfont.Widths2), pfres->count, chars_count);
+ if (code < 0)
+ return code;
+ }
+ }
+ if (pfres->FontType == ft_CID_TrueType) {
+ if (pfres->u.cidfont.CIDToGIDMap != NULL) {
+ code = pdf_resize_array(mem, (void **)&pfres->u.cidfont.CIDToGIDMap,
+ sizeof(*pfres->u.cidfont.CIDToGIDMap), pfres->count, chars_count);
+ if (code < 0)
+ return code;
+ pfres->u.cidfont.CIDToGIDMapLength = chars_count;
+ }
+ }
+ if (pfres->FontType == ft_CID_encrypted || pfres->FontType == ft_CID_TrueType) {
+ if (pfres->u.cidfont.used2 != NULL) {
+ code = pdf_resize_array(mem, (void **)&pfres->u.cidfont.used2,
+ sizeof(*pfres->u.cidfont.used2),
+ (pfres->count + 7) / 8, (chars_count + 7) / 8);
+ if (code < 0)
+ return code;
+ }
+ }
+ pfres->count = chars_count;
+ return 0;
+}
+
+/* Get the object ID of a font resource. */
+long
+pdf_font_id(const pdf_font_resource_t *pdfont)
+{
+ return pdf_resource_id((const pdf_resource_t *)pdfont);
+}
+
+/*
+ * Return the (copied, subset) font associated with a font resource.
+ * If this font resource doesn't have one (Type 0 or Type 3), return 0.
+ */
+gs_font_base *
+pdf_font_resource_font(const pdf_font_resource_t *pdfont, bool complete)
+{
+ if (pdfont->base_font != NULL)
+ return pdf_base_font_font(pdfont->base_font, complete);
+ if (pdfont->FontDescriptor == 0)
+ return 0;
+ return pdf_font_descriptor_font(pdfont->FontDescriptor, complete);
+}
+
+/*
+ * Determine the embedding status of a font. If the font is in the base
+ * 14, store its index (0..13) in *pindex and its similarity to the base
+ * font (as determined by the font's same_font procedure) in *psame.
+ */
+static bool
+font_is_symbolic(const gs_font *font)
+{
+ if (font->FontType == ft_composite)
+ return true; /* arbitrary */
+ switch (((const gs_font_base *)font)->nearest_encoding_index) {
+ case ENCODING_INDEX_STANDARD:
+ case ENCODING_INDEX_ISOLATIN1:
+ case ENCODING_INDEX_WINANSI:
+ case ENCODING_INDEX_MACROMAN:
+ return false;
+ default:
+ return true;
+ }
+}
+static bool
+embed_list_includes(const gs_param_string_array *psa, const byte *chars,
+ uint size)
+{
+ uint i;
+
+ for (i = 0; i < psa->size; ++i)
+ if (!bytes_compare(psa->data[i].data, psa->data[i].size, chars, size))
+ return true;
+ return false;
+}
+static bool
+embed_as_standard(gx_device_pdf *pdev, gs_font *font, int index,
+ pdf_char_glyph_pair_t *pairs, int num_glyphs)
+{
+ if (font->is_resource) {
+ return true;
+ }
+ if (find_std_appearance(pdev, (gs_font_base *)font, -1,
+ pairs, num_glyphs) == index)
+ return true;
+ if (!scan_for_standard_fonts(pdev, font->dir))
+ return false;
+ return (find_std_appearance(pdev, (gs_font_base *)font, -1,
+ pairs, num_glyphs) == index);
+}
+static bool
+has_extension_glyphs(gs_font *pfont)
+{
+ psf_glyph_enum_t genum;
+ gs_glyph glyph;
+ gs_const_string str;
+ int code, j = 0, l;
+ const int sl = strlen(gx_extendeg_glyph_name_separator);
+
+ psf_enumerate_glyphs_begin(&genum, (gs_font *)pfont, NULL, 0, GLYPH_SPACE_NAME);
+ for (glyph = gs_no_glyph; (psf_enumerate_glyphs_next(&genum, &glyph)) != 1; ) {
+ code = pfont->procs.glyph_name(pfont, glyph, &str);
+ if (code < 0)
+ return code;
+ l = str.size - sl;
+ for (j = 0; j < l; j ++)
+ if (!memcmp(gx_extendeg_glyph_name_separator, str.data + j, sl))
+ return true;
+ }
+ psf_enumerate_glyphs_reset(&genum);
+ return false;
+}
+
+pdf_font_embed_t
+pdf_font_embed_status(gx_device_pdf *pdev, gs_font *font, int *pindex,
+ pdf_char_glyph_pair_t *pairs, int num_glyphs)
+{
+ const gs_font_name *fn = &font->font_name;
+ const byte *chars = fn->chars;
+ uint size = fn->size;
+ int index = pdf_find_standard_font_name(chars, size);
+ bool embed_as_standard_called = false;
+ bool do_embed_as_standard = false; /* Quiet compiler. */
+ int code;
+ gs_font_info_t info;
+
+ memset(&info, 0x00, sizeof(gs_font_info_t));
+ code = font->procs.font_info(font, NULL, FONT_INFO_EMBEDDING_RIGHTS, &info);
+ if (code == 0 && (info.members & FONT_INFO_EMBEDDING_RIGHTS)) {
+ if (((info.EmbeddingRights == 0x0002) || (info.EmbeddingRights & 0x0200))
+ && !IsInWhiteList ((const char *)chars, size)) {
+ /* See the OpenType specification, "The 'OS/2' and Windows Metrics Table" for details
+ of the fstype parameter. This is a bitfield, currently we forbid embedding of fonts
+ with these bits set:
+ bit 1 0x0002 Fonts that have only this bit set must not be modified, embedded or
+ exchanged in any manner.
+ bit 9 0x0200 Bitmap embedding only.
+
+ Note for Restricted License embedding (bit 1), this must be the only level of embedding
+ selected (see the OpenType spec).
+ */
+ char name[gs_font_name_max + 1];
+ int len;
+
+ len = min(gs_font_name_max, font->font_name.size);
+ memcpy(name, font->font_name.chars, len);
+ name[len] = 0;
+ emprintf1(pdev->pdf_memory,
+ "\nWarning: %s cannot be embedded because of licensing restrictions\n",
+ name);
+ return FONT_EMBED_NO;
+ }
+ }
+ /*
+ * The behavior of Acrobat Distiller changed between 3.0 (PDF 1.2),
+ * which will never embed the base 14 fonts, and 4.0 (PDF 1.3), which
+ * doesn't treat them any differently from any other fonts. However,
+ * if any of the base 14 fonts is not embedded, it still requires
+ * special treatment.
+ */
+ if (pindex)
+ *pindex = index;
+ if (pdev->PDFX || pdev->PDFA != 0)
+ return FONT_EMBED_YES;
+ if (pdev->CompatibilityLevel < 1.3) {
+ if (index >= 0 &&
+ (embed_as_standard_called = true,
+ do_embed_as_standard = embed_as_standard(pdev, font, index, pairs, num_glyphs))) {
+ if (pdev->ForOPDFRead && has_extension_glyphs(font))
+ return FONT_EMBED_YES;
+ return FONT_EMBED_STANDARD;
+ }
+ }
+ /* Check the Embed lists. */
+ if (!embed_list_includes(&pdev->params.NeverEmbed, chars, size) ||
+ (index >= 0 &&
+ !(embed_as_standard_called ? do_embed_as_standard :
+ (embed_as_standard_called = true,
+ (do_embed_as_standard = embed_as_standard(pdev, font, index, pairs, num_glyphs)))))
+ /* Ignore NeverEmbed for a non-standard font with a standard name */
+ ) {
+ if (pdev->params.EmbedAllFonts || font_is_symbolic(font) ||
+ embed_list_includes(&pdev->params.AlwaysEmbed, chars, size))
+ return FONT_EMBED_YES;
+ }
+ if (index >= 0 &&
+ (embed_as_standard_called ? do_embed_as_standard :
+ embed_as_standard(pdev, font, index, pairs, num_glyphs)))
+ return FONT_EMBED_STANDARD;
+ return FONT_EMBED_NO;
+}
+
+/*
+ * Compute the BaseFont of a font according to the algorithm described
+ * in gdevpdtf.h.
+ */
+int
+pdf_compute_BaseFont(gx_device_pdf *pdev, pdf_font_resource_t *pdfont, bool finish)
+{
+ pdf_font_resource_t *pdsubf = pdfont;
+ gs_string fname;
+ uint size;
+ byte *data;
+
+ if (pdfont->FontType == ft_composite) {
+ int code;
+
+ pdsubf = pdfont->u.type0.DescendantFont;
+ code = pdf_compute_BaseFont(pdev, pdsubf, finish);
+ if (code < 0)
+ return code;
+ fname = pdsubf->BaseFont;
+ }
+ else if (pdfont->FontDescriptor == 0) {
+ /* Type 3 font, or has its BaseFont computed in some other way. */
+ return 0;
+ } else
+ fname = *pdf_font_descriptor_base_name(pdsubf->FontDescriptor);
+ size = fname.size;
+ data = gs_alloc_string(pdev->pdf_memory, size,
+ "pdf_compute_BaseFont");
+ if (data == 0)
+ return_error(gs_error_VMerror);
+ memcpy(data, fname.data, size);
+ switch (pdfont->FontType) {
+ case ft_composite:
+ /* Previously we copied the name of the original CMap onto the font name
+ * but this doesn't make any sense.
+ */
+ break;
+ case ft_encrypted:
+ case ft_encrypted2:
+ if (pdfont->u.simple.s.type1.is_MM_instance &&
+ !pdf_font_descriptor_embedding(pdfont->FontDescriptor)
+ ) {
+ /* Replace spaces by underscores in the base name. */
+ uint i;
+
+ for (i = 0; i < size; ++i)
+ if (data[i] == ' ')
+ data[i] = '_';
+ }
+ break;
+ case ft_TrueType:
+ case ft_CID_TrueType: {
+ /* Remove spaces from the base name. */
+ uint i, j;
+
+ for (i = j = 0; i < size; ++i)
+ if (data[i] != ' ')
+ data[j++] = data[i];
+ data = gs_resize_string(pdev->pdf_memory, data, i, j,
+ "pdf_compute_BaseFont");
+ size = j;
+ break;
+ }
+ default:
+ break;
+ }
+ if (pdfont->BaseFont.size)
+ gs_free_string(pdev->pdf_memory, pdfont->BaseFont.data, pdfont->BaseFont.size, "Replacing BaseFont string");
+ pdfont->BaseFont.data = fname.data = data;
+ pdfont->BaseFont.size = fname.size = size;
+ /* Compute names for subset fonts. */
+ if (finish && pdfont->FontDescriptor != NULL &&
+ pdf_font_descriptor_is_subset(pdfont->FontDescriptor) &&
+ !pdf_has_subset_prefix(fname.data, fname.size) &&
+ pdf_font_descriptor_embedding(pdfont->FontDescriptor)
+ ) {
+ int code;
+
+ if (pdfont->FontDescriptor)
+ code = pdf_add_subset_prefix(pdev, &fname, pdfont->used, pdfont->count, pdf_fontfile_hash(pdfont->FontDescriptor));
+ else
+ code = pdf_add_subset_prefix(pdev, &fname, pdfont->used, pdfont->count, 0);
+
+ if (code < 0)
+ return code;
+ pdfont->BaseFont = fname;
+ /* Don't write a UID for subset fonts. */
+ uid_set_invalid(&pdf_font_resource_font(pdfont, false)->UID);
+ }
+ if (pdfont->FontType != ft_composite && pdsubf->FontDescriptor)
+ *pdf_font_descriptor_name(pdsubf->FontDescriptor) = fname;
+ return 0;
+}
+
+/* ------ Type 0 ------ */
+
+/* Allocate a Type 0 font resource. */
+int
+pdf_font_type0_alloc(gx_device_pdf *pdev, pdf_font_resource_t **ppfres,
+ gs_id rid, pdf_font_resource_t *DescendantFont,
+ const gs_const_string *CMapName)
+{
+ int code = font_resource_alloc(pdev, ppfres, resourceFont, rid,
+ ft_composite, 0, pdf_write_contents_type0);
+
+ if (code >= 0) {
+ (*ppfres)->u.type0.DescendantFont = DescendantFont;
+ (*ppfres)->u.type0.CMapName = *CMapName;
+ (*ppfres)->u.type0.font_index = 0;
+ code = pdf_compute_BaseFont(pdev, *ppfres, false);
+ }
+ return code;
+}
+
+/* ------ Type 3 ------ */
+
+/* Allocate a Type 3 font resource for sinthesyzed bitmap fonts. */
+int
+pdf_font_type3_alloc(gx_device_pdf *pdev, pdf_font_resource_t **ppfres,
+ pdf_font_write_contents_proc_t write_contents)
+{
+ return font_resource_simple_alloc(pdev, ppfres, gs_no_id, ft_user_defined,
+ 256, write_contents);
+}
+
+/* ------ Standard (base 14) Type 1 or TrueType ------ */
+
+/* Allocate a standard (base 14) font resource. */
+int
+pdf_font_std_alloc(gx_device_pdf *pdev, pdf_font_resource_t **ppfres,
+ bool is_original, gs_id rid, gs_font_base *pfont, int index)
+{
+ pdf_font_resource_t *pdfont;
+ int code = font_resource_encoded_alloc(pdev, &pdfont, rid, pfont->FontType,
+ pdf_write_contents_std);
+ const pdf_standard_font_info_t *psfi = &standard_font_info[index];
+ pdf_standard_font_t *psf = &pdf_standard_fonts(pdev)[index];
+ gs_matrix *orig_matrix = (is_original ? &pfont->FontMatrix : &psf->orig_matrix);
+
+ if (code < 0 ||
+ (code = pdf_base_font_alloc(pdev, &pdfont->base_font, pfont, orig_matrix, true)) < 0
+ )
+ return code;
+ pdfont->BaseFont.data = (byte *)psfi->fname; /* break const */
+ pdfont->BaseFont.size = strlen(psfi->fname);
+ pdfont->mark_glyph = pfont->dir->ccache.mark_glyph;
+ set_is_MM_instance(pdfont, pfont);
+ if (is_original) {
+ psf->pdfont = pdfont;
+ psf->orig_matrix = pfont->FontMatrix;
+ }
+ *ppfres = pdfont;
+ return 0;
+}
+
+/* ------ Other Type 1 or TrueType ------ */
+
+/* Allocate a Type 1 or TrueType font resource. */
+int
+pdf_font_simple_alloc(gx_device_pdf *pdev, pdf_font_resource_t **ppfres,
+ gs_id rid, pdf_font_descriptor_t *pfd)
+{
+ pdf_font_resource_t *pdfont;
+ int code;
+
+ code = font_resource_encoded_alloc(pdev, &pdfont, rid,
+ pdf_font_descriptor_FontType(pfd),
+ pdf_write_contents_simple);
+
+ if (code < 0)
+ return(gs_note_error(code));
+ pdfont->FontDescriptor = pfd;
+ set_is_MM_instance(pdfont, pdf_font_descriptor_font(pfd, false));
+ *ppfres = pdfont;
+ return pdf_compute_BaseFont(pdev, pdfont, false);
+}
+
+/* ------ CID-keyed ------ */
+
+/* Allocate a CIDFont resource. */
+int
+pdf_font_cidfont_alloc(gx_device_pdf *pdev, pdf_font_resource_t **ppfres,
+ gs_id rid, pdf_font_descriptor_t *pfd)
+{
+ font_type FontType = pdf_font_descriptor_FontType(pfd);
+ gs_font_base *font = pdf_font_descriptor_font(pfd, false);
+ int chars_count;
+ int code;
+ pdf_font_write_contents_proc_t write_contents;
+ const gs_cid_system_info_t *pcidsi;
+ ushort *map = 0;
+ pdf_font_resource_t *pdfont;
+
+ switch (FontType) {
+ case ft_CID_encrypted:
+ chars_count = ((const gs_font_cid0 *)font)->cidata.common.CIDCount;
+ pcidsi = &((const gs_font_cid0 *)font)->cidata.common.CIDSystemInfo;
+ write_contents = pdf_write_contents_cid0;
+ break;
+ case ft_CID_TrueType:
+ chars_count = ((const gs_font_cid2 *)font)->cidata.common.CIDCount;
+ pcidsi = &((const gs_font_cid2 *)font)->cidata.common.CIDSystemInfo;
+ map = (void *)gs_alloc_byte_array(pdev->pdf_memory, chars_count,
+ sizeof(*map), "CIDToGIDMap");
+ if (map == 0)
+ return_error(gs_error_VMerror);
+ memset(map, 0, chars_count * sizeof(*map));
+ write_contents = pdf_write_contents_cid2;
+ break;
+ default:
+ return_error(gs_error_rangecheck);
+ }
+ code = font_resource_alloc(pdev, &pdfont, resourceCIDFont, rid, FontType,
+ chars_count, write_contents);
+ if (code < 0)
+ return code;
+ pdfont->FontDescriptor = pfd;
+ pdfont->u.cidfont.CIDToGIDMap = map;
+ pdfont->u.cidfont.CIDToGIDMapLength = chars_count;
+ /* fixme : Likely pdfont->u.cidfont.CIDToGIDMap duplicates
+ pdfont->FontDescriptor->base_font->copied->client_data->CIDMap.
+ Only difference is 0xFFFF designates unmapped CIDs.
+ */
+ pdfont->u.cidfont.Widths2 = NULL;
+ pdfont->u.cidfont.v = NULL;
+ pdfont->u.cidfont.parent = NULL;
+ /* Don' know whether the font will use WMode 1,
+ so reserve it now. */
+ pdfont->u.cidfont.used2 = gs_alloc_bytes(pdev->pdf_memory,
+ (chars_count + 7) / 8, "pdf_font_cidfont_alloc");
+ if (pdfont->u.cidfont.used2 == NULL)
+ return_error(gs_error_VMerror);
+ memset(pdfont->u.cidfont.used2, 0, (chars_count + 7) / 8);
+ /*
+ * Write the CIDSystemInfo now, so we don't try to access it after
+ * the font may no longer be available.
+ */
+ code = pdf_write_cid_systemInfo_separate(pdev, pcidsi, &pdfont->u.cidfont.CIDSystemInfo_id);
+ if (code < 0)
+ return code;
+ *ppfres = pdfont;
+ return pdf_compute_BaseFont(pdev, pdfont, false);
+}
+
+int
+pdf_obtain_cidfont_widths_arrays(gx_device_pdf *pdev, pdf_font_resource_t *pdfont,
+ int wmode, double **w, double **w0, double **v)
+{
+ gs_memory_t *mem = pdev->pdf_memory;
+ double *ww, *vv = 0, *ww0 = 0;
+ int chars_count = pdfont->count;
+
+ *w0 = (wmode ? pdfont->Widths : NULL);
+ *v = (wmode ? pdfont->u.cidfont.v : NULL);
+ *w = (wmode ? pdfont->u.cidfont.Widths2 : pdfont->Widths);
+ if (*w == NULL) {
+ ww = (double *)gs_alloc_byte_array(mem, chars_count, sizeof(*ww),
+ "pdf_obtain_cidfont_widths_arrays");
+ if (wmode) {
+ vv = (double *)gs_alloc_byte_array(mem, chars_count, sizeof(*vv) * 2,
+ "pdf_obtain_cidfont_widths_arrays");
+ if (pdfont->Widths == 0) {
+ ww0 = (double *)gs_alloc_byte_array(mem, chars_count, sizeof(*ww0),
+ "pdf_obtain_cidfont_widths_arrays");
+ pdfont->Widths = *w0 = ww0;
+ if (ww0 != 0)
+ memset(ww0, 0, chars_count * sizeof(*ww));
+ } else
+ *w0 = ww0 = pdfont->Widths;
+ }
+ if (ww == 0 || (wmode && vv == 0) || (wmode && ww0 == 0)) {
+ gs_free_object(mem, ww, "pdf_obtain_cidfont_widths_arrays");
+ gs_free_object(mem, vv, "pdf_obtain_cidfont_widths_arrays");
+ gs_free_object(mem, ww0, "pdf_obtain_cidfont_widths_arrays");
+ return_error(gs_error_VMerror);
+ }
+ if (wmode)
+ memset(vv, 0, chars_count * 2 * sizeof(*vv));
+ memset(ww, 0, chars_count * sizeof(*ww));
+ if (wmode) {
+ pdfont->u.cidfont.Widths2 = *w = ww;
+ pdfont->u.cidfont.v = *v = vv;
+ } else {
+ pdfont->Widths = *w = ww;
+ *v = NULL;
+ }
+ }
+ return 0;
+}
+
+/*
+ * Convert True Type fonts into CID fonts for PDF/A.
+ */
+int
+pdf_convert_truetype_font(gx_device_pdf *pdev, pdf_resource_t *pres)
+{
+ if (pdev->PDFA == 0)
+ return 0;
+ else {
+ pdf_font_resource_t *pdfont = (pdf_font_resource_t *)pres;
+
+ if (pdfont->FontType != ft_TrueType)
+ return 0;
+ else if (pdf_resource_id(pres) == -1)
+ return 0; /* An unused font. */
+ else {
+ int code = pdf_different_encoding_index(pdfont, 0);
+
+ if (code < 0)
+ return code;
+ if (code == 256 && pdfont->u.simple.BaseEncoding != ENCODING_INDEX_UNKNOWN)
+ return 0;
+ { /* The encoding have a difference - do convert. */
+ pdf_font_resource_t *pdfont0;
+ gs_const_string CMapName = {(const byte *)"OneByteIdentityH", 16};
+
+ code = pdf_convert_truetype_font_descriptor(pdev, pdfont);
+ if (code < 0)
+ return code;
+ code = pdf_font_type0_alloc(pdev, &pdfont0, pres->rid + 1, pdfont, &CMapName);
+ if (code < 0)
+ return code;
+ /* Pass the font object ID to the type 0 font resource. */
+ pdf_reserve_object_id(pdev, (pdf_resource_t *)pdfont0, pdf_resource_id(pres));
+ pdf_reserve_object_id(pdev, (pdf_resource_t *)pdfont, gs_no_id);
+ /* Set Encoding_name because we won't call attach_cmap_resource for it : */
+ code = pdf_write_OneByteIdentityH(pdev);
+ if (code < 0)
+ return 0;
+ pdfont->u.cidfont.CIDSystemInfo_id = pdev->IdentityCIDSystemInfo_id;
+ gs_sprintf(pdfont0->u.type0.Encoding_name, "%ld 0 R", pdf_resource_id(pdev->OneByteIdentityH));
+ /* Move ToUnicode : */
+ pdfont0->res_ToUnicode = pdfont->res_ToUnicode; pdfont->res_ToUnicode = 0;
+ pdfont0->cmap_ToUnicode = pdfont->cmap_ToUnicode; pdfont->cmap_ToUnicode = 0;
+ /* Change the font type to CID font : */
+ pdfont->FontType = ft_CID_TrueType;
+ pdfont->write_contents = pdf_write_contents_cid2;
+ return 0;
+ }
+ }
+ }
+}
+
+/* ---------------- CMap resources ---------------- */
+
+/*
+ * Allocate a CMap resource.
+ */
+int
+pdf_cmap_alloc(gx_device_pdf *pdev, const gs_cmap_t *pcmap,
+ pdf_resource_t **ppres, int font_index_only)
+{
+ return pdf_write_cmap(pdev, pcmap, ppres, font_index_only);
+}
diff --git a/devices/vector/gdevpdtf.h b/devices/vector/gdevpdtf.h
new file mode 100644
index 000000000..89de1afca
--- /dev/null
+++ b/devices/vector/gdevpdtf.h
@@ -0,0 +1,450 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Font and CMap resource structure and API for pdfwrite */
+
+#ifndef gdevpdtf_INCLUDED
+# define gdevpdtf_INCLUDED
+
+#include "gdevpdtx.h"
+
+/* ================ Types and structures ================ */
+
+/* ---------------- Font resources ---------------- */
+
+/*
+ * pdfwrite manages several different flavors of font resources:
+ *
+ * Those that have neither a FontDescriptor nor a base_font:
+ * Type 0 (composite) fonts
+ * Those that have no FontDescriptor, but do have a base_font:
+ * Standard 14 fonts
+ * Those that have a FontDescriptor but no base_font:
+ * Type 3 bitmap fonts
+ * Those that have a FontDescriptor with a base_font:
+ * Type 1 / Type 2 fonts
+ * Type 42 (TrueType) fonts
+ * CIDFontType 0 (Type 1/2) CIDFonts
+ * CIDFontType 2 (TrueType) CIDFonts
+ */
+/*
+ * Font names in PDF files have caused an enormous amount of trouble, so we
+ * document specifically how they are handled in each structure.
+ *
+ * The PDF Reference specifies the BaseFont of a font resource as follows,
+ * depending on the font type:
+ *
+ * Type 0 - if the descendant font is CIDFontType 0, the descendant font
+ * name followed by a hyphen and the CMap name (the value of Encoding,
+ * if a name, otherwise the CMapName from the CMap); if the descendant
+ * font is CIDFontType 2, the descendant font name.
+ *
+ * Type 1 - "usually" the same as the FontName in the base font.
+ *
+ * MM Type 1 - if embedded, the same as Type 1; if not embedded, spaces
+ * in the font name are replaced with underscores.
+ *
+ * Type 3 - not used.
+ *
+ * TrueType - initially, the PostScript name from the 'name' table in
+ * the font; if none, the "name by which the font is known in the host
+ * operating system". Spaces are removed. Then, under circumstances
+ * not defined, the string ",Bold", ",Italic", or ",BoldItalic" is
+ * appended if the font has the corresponding style properties.
+ * [We do not do this: we simply use the key_name or font_name.]
+ *
+ * CIDFontType 0 - "usually" the same as the CIDFontName in the base font.
+ *
+ * CIDFontType 2 - the same as TrueType.
+ *
+ * In addition, the BaseFont has a XXXXXX+ prefix if the font is a subset
+ * (whether embedded or not).
+ *
+ * We would like to compute the BaseFont at the time that we create the font
+ * resource object. The font descriptor (which is needed to provide
+ * information about embedding) and the base font are both available at that
+ * time. Unfortunately, the information as to whether the font will be
+ * subsetted is not available. Therefore, we do compute the BaseFont from
+ * the base font name when the font resource is created, to allow checking
+ * for duplicate names and for standard font names, but we compute it again
+ * after writing out the base font.
+ */
+
+#ifndef gs_cmap_DEFINED
+# define gs_cmap_DEFINED
+typedef struct gs_cmap_s gs_cmap_t;
+#endif
+
+#ifndef gs_font_type0_DEFINED
+# define gs_font_type0_DEFINED
+typedef struct gs_font_type0_s gs_font_type0;
+#endif
+
+#ifndef pdf_base_font_DEFINED
+# define pdf_base_font_DEFINED
+typedef struct pdf_base_font_s pdf_base_font_t;
+#endif
+
+#ifndef pdf_font_descriptor_DEFINED
+# define pdf_font_descriptor_DEFINED
+typedef struct pdf_font_descriptor_s pdf_font_descriptor_t;
+#endif
+
+#ifndef pdf_char_glyph_pair_DEFINED
+# define pdf_char_glyph_pair_DEFINED
+typedef struct pdf_char_glyph_pair_s pdf_char_glyph_pair_t;
+#endif
+
+struct pdf_char_glyph_pair_s {
+ gs_char chr;
+ gs_glyph glyph;
+};
+
+/*
+ * The write_contents procedure is set by the implementation when the
+ * font resource is created. It is called after generic code has opened
+ * the resource object and written the Type, BaseFont (if any),
+ * FontDescriptor reference (if any), ToUnicode CMap reference (if any),
+ * and additional dictionary entries (if any).
+ * The write_contents procedure must write any remaining entries specific
+ * to the FontType, followed by the closing ">>", and then call
+ * pdf_end_separate. The reason for this division of function is to allow
+ * the write_contents procedure to write additional objects that the
+ * resource object references, after closing the resource object.
+ */
+typedef int (*pdf_font_write_contents_proc_t)
+ (gx_device_pdf *, pdf_font_resource_t *);
+
+/*
+ * Define an element of an Encoding. The element is unused if glyph ==
+ * GS_NO_GLYPH.
+ */
+typedef struct pdf_encoding_element_s {
+ gs_glyph glyph;
+ gs_const_string str;
+ bool is_difference; /* true if must be written in Differences */
+} pdf_encoding_element_t;
+#define private_st_pdf_encoding1() /* gdevpdtf.c */\
+ gs_private_st_const_strings1(st_pdf_encoding1,\
+ pdf_encoding_element_t, "pdf_encoding_element_t",\
+ pdf_encoding1_enum_ptrs, pdf_encoding1_reloc_ptrs, str)
+#define private_st_pdf_encoding_element() /* gdevpdtf.c */\
+ gs_private_st_element(st_pdf_encoding_element, pdf_encoding_element_t,\
+ "pdf_encoding_element_t[]", pdf_encoding_elt_enum_ptrs,\
+ pdf_encoding_elt_reloc_ptrs, st_pdf_encoding1)
+
+struct pdf_base_font_s {
+ /*
+ * For the standard 14 fonts, copied == complete is a complete copy
+ * of the font, and DO_SUBSET = NO.
+ *
+ * For fonts that MAY be subsetted, copied is a partial copy,
+ * complete is a complete copy, and DO_SUBSET = UNKNOWN until
+ * pdf_font_do_subset is called.
+ *
+ * For fonts that MUST be subsetted, copied == complete is a partial
+ * copy, and DO_SUBSET = YES.
+ */
+ gs_font_base *copied;
+ gs_font_base *complete;
+ enum {
+ DO_SUBSET_UNKNOWN = 0,
+ DO_SUBSET_NO,
+ DO_SUBSET_YES
+ } do_subset;
+ bool is_standard;
+ /*
+ * For CIDFonts, which are always subsetted, num_glyphs is CIDCount.
+ * For optionally subsetted fonts, num_glyphs is the count of glyphs
+ * in the font when originally copied. Note that if the font is
+ * downloaded incrementally, num_glyphs may be 0.
+ */
+ int num_glyphs;
+ byte *CIDSet; /* for CIDFonts */
+ int CIDSetLength;
+ gs_string font_name;
+ bool written;
+ cos_dict_t *FontFile;
+};
+#define private_st_pdf_base_font()\
+BASIC_PTRS(pdf_base_font_ptrs) {\
+ GC_OBJ_ELT(pdf_base_font_t, copied),\
+ GC_OBJ_ELT(pdf_base_font_t, complete),\
+ GC_OBJ_ELT(pdf_base_font_t, CIDSet),\
+ GC_OBJ_ELT(pdf_base_font_t, FontFile),\
+ GC_STRING_ELT(pdf_base_font_t, font_name)\
+}
+
+typedef struct {
+ gs_id id;
+ pdf_resource_type_t type;
+} pdf_resource_ref_t;
+
+/*
+ * Widths are the widths in the outlines: this is what PDF interpreters
+ * use, and what will be written in the PDF file. real_widths are the
+ * widths possibly modified by Metrics[2] and CDevProc: these define the
+ * actual advance widths of the characters in the PostScript text.
+ */
+struct pdf_font_resource_s {
+ pdf_resource_common(pdf_font_resource_t);
+ font_type FontType; /* copied from font, if any */
+ pdf_font_write_contents_proc_t write_contents;
+ gs_string BaseFont; /* (not used for Type 3) */
+ pdf_font_descriptor_t *FontDescriptor; /* (not used for Type 0, Type 3, */
+ /* or standard 14 fonts) */
+ /*
+ * The base_font member is only used for
+ * the standard 14 fonts, which do not have a FontDescriptor.
+ */
+ pdf_base_font_t *base_font; /* == FontDescriptor->base_font */
+ uint count; /* # of chars/CIDs */
+ double *Widths; /* [count] (not used for Type 0) */
+ byte *used; /* [ceil(count/8)] bitmap of chars/CIDs used */
+ /* (not used for Type 0 or Type 3) */
+ pdf_resource_t *res_ToUnicode; /* CMap (not used for CIDFonts) */
+ gs_cmap_t *cmap_ToUnicode; /* CMap (not used for CIDFonts) */
+ gs_glyph_mark_proc_t mark_glyph;
+ void *mark_glyph_data; /* closure data */
+ union {
+
+ struct /*type0*/ {
+
+ pdf_font_resource_t *DescendantFont; /* CIDFont */
+ /*
+ * The Encoding_name must be long enough to hold either the
+ * longest standard CMap name defined in the PDF Reference,
+ * or the longest reference to an embedded CMap (# 0 R).
+ */
+ char Encoding_name[max( /* standard name or <id> 0 R */
+ 17, /* /UniJIS-UCS2-HW-H */
+ sizeof(long) * 8 / 3 + 1 + 4 /* <id> 0 R */
+ ) + 1 /* \0 terminator */
+ ];
+ gs_const_string CMapName; /* copied from the original CMap, */
+ /* or references the table of standard names */
+ uint font_index; /* The index of the descendent font in the source CMap. */
+ bool cmap_is_standard;
+ int WMode; /* of CMap */
+
+ } type0;
+
+ struct /*cidfont*/ {
+
+ /* [D]W[2] is Widths. */
+ long CIDSystemInfo_id; /* (written when font is allocated) */
+ ushort *CIDToGIDMap; /* (CIDFontType 2 only) [count] */
+ unsigned int CIDToGIDMapLength;
+ gs_id glyphshow_font_id;
+ double *Widths2; /* [count * 2] (x, y) */
+ double *v; /* [count] */
+ byte *used2; /* [(count + 7) / 8] */
+ pdf_font_resource_t *parent;
+
+ } cidfont;
+
+ struct /*simple*/ {
+
+ int FirstChar, LastChar; /* 0 <= FirstChar <= LastChar <= 255 */
+ /*
+ * The BaseEncoding can only be ENCODING_INDEX_WINANSI,
+ * ENCODING_INDEX_MACROMAN, ENCODING_INDEX_MACEXPERT, or -1.
+ */
+ gs_encoding_index_t BaseEncoding;
+ gs_encoding_index_t preferred_encoding_index;
+ pdf_encoding_element_t *Encoding; /* [256], not for Type 3 */
+ gs_point *v; /* [256], glyph origin for WMode 1 */
+ int last_reserved_char; /* Except for synthesised Type 3,
+ which stores such data in LastChar */
+
+ union {
+
+ struct /*type1*/ {
+ bool is_MM_instance;
+ } type1;
+
+ struct /*truetype*/ {
+ /*
+ * No extra info needed, but the ANSI standard doesn't
+ * allow empty structs.
+ */
+ int _dummy;
+ } truetype;
+
+ struct /*type3*/ {
+ gs_rect FontBBox;
+ gs_matrix FontMatrix;
+ pdf_char_proc_ownership_t *char_procs;
+ int max_y_offset;
+ bool bitmap_font;
+ cos_dict_t *Resources;
+ byte *cached;
+ } type3;
+
+ } s;
+
+ } simple;
+
+ } u;
+};
+/* The GC descriptor for resource types must be public. */
+#define public_st_pdf_font_resource() /* gdevpdtf.c */\
+ gs_public_st_composite(st_pdf_font_resource, pdf_font_resource_t,\
+ "pdf_font_resource_t", pdf_font_resource_enum_ptrs,\
+ pdf_font_resource_reloc_ptrs)
+
+/*
+ * Define the possible embedding statuses of a font.
+ */
+typedef enum {
+ FONT_EMBED_STANDARD, /* 14 standard fonts */
+ FONT_EMBED_NO,
+ FONT_EMBED_YES
+} pdf_font_embed_t;
+
+/* ---------------- Global structures ---------------- */
+
+/*
+ * Define a structure for keeping track of the (unique) resource for
+ * each standard font. Note that standard fonts do not have descriptors:
+ * the base_font and copied_font members of the font resource provide the
+ * necessary information.
+ */
+typedef struct pdf_standard_font_s {
+ pdf_font_resource_t *pdfont;
+ gs_matrix orig_matrix; /* ****** do we need this? */
+} pdf_standard_font_t;
+#define private_st_pdf_standard_font() /* gdevpdtf.c */\
+ gs_private_st_ptrs1(st_pdf_standard_font, pdf_standard_font_t,\
+ "pdf_standard_font_t", pdf_std_font_enum_ptrs, pdf_std_font_reloc_ptrs,\
+ pdfont)
+#define private_st_pdf_standard_font_element() /* gdevpdtf.c */\
+ gs_private_st_element(st_pdf_standard_font_element, pdf_standard_font_t,\
+ "pdf_standard_font_t[]", pdf_std_font_elt_enum_ptrs,\
+ pdf_std_font_elt_reloc_ptrs, st_pdf_standard_font)
+
+/*
+ * There is a single instance (per device) of a structure that tracks global
+ * information about outline fonts. It is defined here, rather than
+ * opaquely in the implementation file, because the text processing code
+ * needs access to it.
+ */
+
+/*typedef struct pdf_outline_fonts_s pdf_outline_fonts_t;*/ /* gdevpdtx.h */
+struct pdf_outline_fonts_s {
+ pdf_standard_font_t *standard_fonts; /* [PDF_NUM_STANDARD_FONTS] */
+};
+#define private_st_pdf_outline_fonts() /* gdevpdtf.c */\
+ gs_private_st_ptrs1(st_pdf_outline_fonts, pdf_outline_fonts_t,\
+ "pdf_outline_fonts_t", pdf_outline_fonts_enum_ptrs,\
+ pdf_outline_fonts_reloc_ptrs, standard_fonts)
+
+/* ================ Procedures ================ */
+
+/* ---------------- Font resources ---------------- */
+
+/*
+ * Allocate and initialize bookkeeping for outline fonts.
+ */
+pdf_outline_fonts_t *pdf_outline_fonts_alloc(gs_memory_t *mem);
+
+/*
+ * Return the standard fonts array.
+ */
+pdf_standard_font_t *pdf_standard_fonts(const gx_device_pdf *pdev);
+
+/*
+ * Clean the standard fonts array.
+ */
+void pdf_clean_standard_fonts(const gx_device_pdf *pdev);
+
+/* Free font cache. */
+int pdf_free_font_cache(gx_device_pdf *pdev);
+
+/*
+ * Allocate specific types of font resource.
+ */
+int pdf_font_type0_alloc(gx_device_pdf *pdev, pdf_font_resource_t **ppfres,
+ gs_id rid, pdf_font_resource_t *DescendantFont,
+ const gs_const_string *CMapName);
+int pdf_font_type3_alloc(gx_device_pdf *pdev, pdf_font_resource_t **ppfres,
+ pdf_font_write_contents_proc_t write_contents);
+int pdf_font_std_alloc(gx_device_pdf *pdev, pdf_font_resource_t **ppfres,
+ bool is_original, gs_id rid, gs_font_base *pfont, int index);
+int pdf_font_simple_alloc(gx_device_pdf *pdev, pdf_font_resource_t **ppfres,
+ gs_id rid, pdf_font_descriptor_t *pfd);
+int pdf_font_cidfont_alloc(gx_device_pdf *pdev, pdf_font_resource_t **ppfres,
+ gs_id rid, pdf_font_descriptor_t *pfd);
+int pdf_obtain_cidfont_widths_arrays(gx_device_pdf *pdev, pdf_font_resource_t *pdfont,
+ int wmode, double **w, double **w0, double **v);
+int font_resource_encoded_alloc(gx_device_pdf *pdev, pdf_font_resource_t **ppfres,
+ gs_id rid, font_type ftype,
+ pdf_font_write_contents_proc_t write_contents);
+int pdf_assign_font_object_id(gx_device_pdf *pdev, pdf_font_resource_t *pdfont);
+
+int font_resource_free(gx_device_pdf *pdev, pdf_font_resource_t *pdfont);
+
+/* Resize font resource arrays. */
+int pdf_resize_resource_arrays(gx_device_pdf *pdev, pdf_font_resource_t *pfres,
+ int chars_count);
+
+/*
+ * Return the (copied, subset or complete) font associated with a font resource.
+ * If this font resource doesn't have a descriptor (Type 0, Type 3, or
+ * standard 14), return 0.
+ */
+gs_font_base *pdf_font_resource_font(const pdf_font_resource_t *pdfont, bool complete);
+
+/*
+ * Determine the embedding status of a font. If the font is in the base
+ * 14, store its index (0..13) in *pindex and its similarity to the base
+ * font (as determined by the font's same_font procedure) in *psame.
+ * (pindex and/or psame may be NULL.)
+ */
+pdf_font_embed_t pdf_font_embed_status(gx_device_pdf *pdev, gs_font *font,
+ int *pindex,
+ pdf_char_glyph_pair_t *pairs, int num_glyphs);
+
+/*
+ * Compute the BaseFont of a font according to the algorithm described
+ * above.
+ */
+int pdf_compute_BaseFont(gx_device_pdf *pdev, pdf_font_resource_t *pdfont, bool finish);
+
+/*
+ * Convert True Type fonts into CID fonts for PDF/A.
+ */
+int pdf_convert_truetype_font(gx_device_pdf *pdev, pdf_resource_t *pres);
+
+/*
+ * Convert True Type font descriptor into CID font descriptor for PDF/A.
+ */
+int pdf_convert_truetype_font_descriptor(gx_device_pdf *pdev, pdf_font_resource_t *pdfont);
+
+/* ---------------- CMap resources ---------------- */
+
+/*
+ * Allocate a CMap resource.
+ */
+int pdf_cmap_alloc(gx_device_pdf *pdev, const gs_cmap_t *pcmap,
+ pdf_resource_t **ppres /* CMap */, int font_index_only);
+
+/*
+ * Add a CID-to-GID mapping to a CIDFontType 2 font resource.
+ */
+int pdf_font_add_cid_to_gid(pdf_font_resource_t *pdfont, uint cid, uint gid);
+
+#endif /* gdevpdtf_INCLUDED */
diff --git a/devices/vector/gdevpdti.c b/devices/vector/gdevpdti.c
new file mode 100644
index 000000000..afa209426
--- /dev/null
+++ b/devices/vector/gdevpdti.c
@@ -0,0 +1,1212 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Bitmap font implementation for pdfwrite */
+#include "memory_.h"
+#include "gx.h"
+#include "gxpath.h"
+#include "gserrors.h"
+#include "gsutil.h"
+#include "gdevpdfx.h"
+#include "gdevpdfg.h"
+#include "gdevpdtf.h"
+#include "gdevpdti.h"
+#include "gdevpdts.h"
+#include "gdevpdtw.h"
+#include "gdevpdtt.h"
+#include "gdevpdfo.h"
+#include "gxchar.h" /* For gs_show_enum */
+
+/* ---------------- Private ---------------- */
+
+/* Define the structure for a CharProc pseudo-resource. */
+/*typedef struct pdf_char_proc_s pdf_char_proc_t;*/ /* gdevpdfx.h */
+struct pdf_char_proc_s {
+ pdf_resource_common(pdf_char_proc_t);
+ pdf_char_proc_ownership_t *owner_fonts; /* fonts using this charproc. */
+ int y_offset; /* of character (0,0) */
+ int x_offset; /* of character (0,0) */
+ gs_point real_width; /* Not used with synthesised bitmap fonts. */
+ gs_point v; /* Not used with synthesised bitmap fonts. */
+};
+
+/* The descriptor is public for pdf_resource_type_structs. */
+gs_public_st_suffix_add1(st_pdf_char_proc, pdf_char_proc_t,
+ "pdf_char_proc_t", pdf_char_proc_enum_ptrs, pdf_char_proc_reloc_ptrs,
+ st_pdf_resource, owner_fonts);
+
+struct pdf_char_proc_ownership_s {
+ pdf_char_proc_t *char_proc;
+ pdf_char_proc_ownership_t *font_next; /* next char_proc for same font */
+ pdf_char_proc_ownership_t *char_next; /* next char_proc for same charproc */
+ pdf_font_resource_t *font;
+ gs_char char_code; /* Character code in PDF font. */
+ gs_glyph glyph; /* Glyph id in Postscript font. */
+ gs_const_string char_name;
+ bool duplicate_char_name;
+};
+gs_private_st_strings1_ptrs4(st_pdf_char_proc_ownership, pdf_char_proc_ownership_t,
+ "pdf_char_proc_ownership_t", pdf_char_proc_ownership_enum_ptrs,
+ pdf_char_proc_ownership_reloc_ptrs, char_name, char_proc, char_next, font_next, font);
+
+gs_private_st_ptrs1(st_pdf_bitmap_fonts, pdf_bitmap_fonts_t,
+ "pdf_bitmap_fonts_t", pdf_bitmap_fonts_enum_ptrs,
+ pdf_bitmap_fonts_reloc_ptrs, open_font);
+
+static inline long
+pdf_char_proc_id(const pdf_char_proc_t *pcp)
+{
+ return pdf_resource_id((const pdf_resource_t *)pcp);
+}
+
+/* Assign a code for a char_proc. */
+static int
+assign_char_code(gx_device_pdf * pdev, gs_text_enum_t *pte)
+{
+ pdf_bitmap_fonts_t *pbfs = pdev->text->bitmap_fonts;
+ pdf_font_resource_t *pdfont = pbfs->open_font; /* Type 3 */
+ int i, c = 0, code;
+ uint operation = pte->text.operation;
+
+ if (pbfs->bitmap_encoding_id == 0)
+ pbfs->bitmap_encoding_id = pdf_obj_ref(pdev);
+ if (pdfont == 0 || pdfont->u.simple.LastChar == 255 ||
+ !pbfs->use_open_font
+ ) {
+ /* Start a new synthesized font. */
+ char *pc;
+
+ code = pdf_font_type3_alloc(pdev, &pdfont, pdf_write_contents_bitmap);
+ if (code < 0)
+ return code;
+ pdfont->u.simple.s.type3.bitmap_font = true;
+ if (pbfs->open_font == 0)
+ pdfont->rname[0] = 0;
+ else
+ strcpy(pdfont->rname, pbfs->open_font->rname);
+ pdfont->u.simple.s.type3.FontBBox.p.x = 0;
+ pdfont->u.simple.s.type3.FontBBox.p.y = 0;
+ pdfont->u.simple.s.type3.FontBBox.q.x = 0;
+ pdfont->u.simple.s.type3.FontBBox.q.y = 0;
+ pdfont->mark_glyph = NULL;
+ gs_make_identity(&pdfont->u.simple.s.type3.FontMatrix);
+ /*
+ * We "increment" the font name as a radix-26 "number".
+ * This cannot possibly overflow.
+ */
+ for (pc = pdfont->rname; *pc == 'Z'; ++pc)
+ *pc = '@';
+ if ((*pc)++ == 0)
+ *pc = 'A', pc[1] = 0;
+ pbfs->open_font = pdfont;
+ pbfs->use_open_font = true;
+ pdfont->u.simple.FirstChar = 255;
+ }
+ if ((operation & (TEXT_FROM_STRING | TEXT_FROM_BYTES)) ||
+ (operation & (TEXT_FROM_CHARS | TEXT_FROM_SINGLE_CHAR))) {
+ unsigned char p = *pte->text.data.bytes;
+ unsigned char index = p / 8, bit = 0x01 << (p % 8);
+
+ if (pdfont->used[index] & bit) {
+ for (i = 0;i < 256;i++) {
+ index = i / 8;
+ bit = 0x01 << (i % 8);
+ if (!(pdfont->used[index] & bit)) {
+ c = i;
+ break;
+ }
+ }
+ } else
+ c = p;
+ pdfont->used[index] |= bit;
+ if (c > pdfont->u.simple.LastChar)
+ pdfont->u.simple.LastChar = c;
+
+ } else {
+ unsigned char index, bit;
+ c = ++(pdfont->u.simple.LastChar);
+ index = c / 8;
+ bit = 0x01 << (c % 8);
+ pdfont->used[index] |= bit;
+ }
+ if (c < pdfont->u.simple.FirstChar)
+ pdfont->u.simple.FirstChar = c;
+
+ pdfont->Widths[c] = psdf_round(pdev->char_width.x, 100, 10); /* See
+ pdf_write_Widths about rounding. We need to provide
+ a compatible data for Tj. */
+ if (c > pbfs->max_embedded_code)
+ pbfs->max_embedded_code = c;
+
+ return c;
+}
+
+/* Write the contents of a Type 3 bitmap or vector font resource. */
+int
+pdf_write_contents_bitmap(gx_device_pdf *pdev, pdf_font_resource_t *pdfont)
+{
+ stream *s = pdev->strm;
+ const pdf_char_proc_ownership_t *pcpo;
+ long diff_id = 0;
+ int code;
+
+ if (pdfont->u.simple.s.type3.bitmap_font)
+ diff_id = pdev->text->bitmap_fonts->bitmap_encoding_id;
+ else {
+ /* See comment in pdf_write_encoding. */
+ diff_id = pdf_obj_ref(pdev);
+ }
+ code = pdf_write_encoding_ref(pdev, pdfont, diff_id);
+ if (code < 0)
+ return code;
+ stream_puts(s, "/CharProcs <<");
+ /* Write real characters. */
+ for (pcpo = pdfont->u.simple.s.type3.char_procs; pcpo;
+ pcpo = pcpo->char_next
+ ) {
+ if (pdfont->u.simple.s.type3.bitmap_font)
+ pprintld2(s, "/a%ld %ld 0 R\n", (long)pcpo->char_code,
+ pdf_char_proc_id(pcpo->char_proc));
+ else if (!pcpo-> duplicate_char_name) {
+ pdf_put_name(pdev, pcpo->char_name.data, pcpo->char_name.size);
+ pprintld1(s, " %ld 0 R\n", pdf_char_proc_id(pcpo->char_proc));
+ }
+ pdf_record_usage_by_parent(pdev, pdf_char_proc_id(pcpo->char_proc), pdfont->object->id);
+ }
+ stream_puts(s, ">>");
+ pprintg6(s, "/FontMatrix[%g %g %g %g %g %g]",
+ (float)pdfont->u.simple.s.type3.FontMatrix.xx,
+ (float)pdfont->u.simple.s.type3.FontMatrix.xy,
+ (float)pdfont->u.simple.s.type3.FontMatrix.yx,
+ (float)pdfont->u.simple.s.type3.FontMatrix.yy,
+ (float)pdfont->u.simple.s.type3.FontMatrix.tx,
+ (float)pdfont->u.simple.s.type3.FontMatrix.ty);
+ code = pdf_finish_write_contents_type3(pdev, pdfont);
+ if (code < 0)
+ return code;
+ if (!pdfont->u.simple.s.type3.bitmap_font && diff_id > 0) {
+ code = pdf_write_encoding(pdev, pdfont, diff_id, 0);
+ if (code < 0)
+ return code;
+ }
+ return 0;
+}
+
+/* ---------------- Public ---------------- */
+
+/*
+ * Allocate and initialize bookkeeping for bitmap fonts.
+ */
+pdf_bitmap_fonts_t *
+pdf_bitmap_fonts_alloc(gs_memory_t *mem)
+{
+ pdf_bitmap_fonts_t *pbfs =
+ gs_alloc_struct(mem, pdf_bitmap_fonts_t, &st_pdf_bitmap_fonts,
+ "pdf_bitmap_fonts_alloc");
+
+ if (pbfs == 0)
+ return 0;
+ memset(pbfs, 0, sizeof(*pbfs));
+ pbfs->max_embedded_code = -1;
+ return pbfs;
+}
+
+/*
+ * Update text state at the end of a page.
+ */
+void
+pdf_close_text_page(gx_device_pdf *pdev)
+{
+ /*
+ * When Acrobat Reader 3 prints a file containing a Type 3 font with a
+ * non-standard Encoding, it apparently only emits the subset of the
+ * font actually used on the page. Thus, if the "Download Fonts Once"
+ * option is selected, characters not used on the page where the font
+ * first appears will not be defined, and hence will print as blank if
+ * used on subsequent pages. Thus, we can't allow a Type 3 font to
+ * add additional characters on subsequent pages.
+ */
+ if (pdev->CompatibilityLevel <= 1.2)
+ pdev->text->bitmap_fonts->use_open_font = false;
+}
+
+int
+pdf_charproc_y_offset(pdf_char_proc_t *pcp)
+{
+ return pcp->y_offset;
+}
+
+int
+pdf_charproc_x_offset(pdf_char_proc_t *pcp)
+{
+ return pcp->x_offset;
+}
+
+/* Attach a CharProc to a font. */
+static int
+pdf_attach_charproc(gx_device_pdf * pdev, pdf_font_resource_t *pdfont, pdf_char_proc_t *pcp,
+ gs_glyph glyph, gs_char char_code, const gs_const_string *gnstr)
+{
+ pdf_char_proc_ownership_t *pcpo;
+ bool duplicate_char_name = false;
+
+ for (pcpo = pdfont->u.simple.s.type3.char_procs; pcpo != NULL; pcpo = pcpo->char_next) {
+ if (pcpo->glyph == glyph && pcpo->char_code == char_code)
+ return 0;
+ }
+ if (!pdfont->u.simple.s.type3.bitmap_font) {
+ for (pcpo = pdfont->u.simple.s.type3.char_procs; pcpo != NULL; pcpo = pcpo->char_next) {
+ if (!bytes_compare(pcpo->char_name.data, pcpo->char_name.size, gnstr->data, gnstr->size)) {
+ duplicate_char_name = true;
+ break;
+ }
+ }
+ }
+ pcpo = gs_alloc_struct(pdev->pdf_memory,
+ pdf_char_proc_ownership_t, &st_pdf_char_proc_ownership, "pdf_attach_charproc");
+
+ if (pcpo == NULL)
+ return_error(gs_error_VMerror);
+ pcpo->font = pdfont;
+ pcpo->char_next = pdfont->u.simple.s.type3.char_procs;
+ pdfont->u.simple.s.type3.char_procs = pcpo;
+ pcpo->char_proc = pcp;
+ pcpo->font_next = pcp->owner_fonts;
+ pcp->owner_fonts = pcpo;
+ pcpo->char_code = char_code;
+ pcpo->glyph = glyph;
+ if (gnstr == NULL) {
+ pcpo->char_name.data = 0;
+ pcpo->char_name.size = 0;
+ } else
+ pcpo->char_name = *gnstr;
+ pcpo->duplicate_char_name = duplicate_char_name;
+ return 0;
+}
+
+int
+pdf_free_charproc_ownership(gx_device_pdf * pdev, pdf_resource_t *pres)
+{
+ pdf_char_proc_ownership_t *next, *pcpo = (pdf_char_proc_ownership_t *)pres;
+
+ while (pcpo) {
+ next = pcpo->char_next;
+ if(pcpo->char_name.size != 0 && pcpo->char_name.data) {
+ /* This causes PCL some trouble, don't know why yet FIXME-MEMORY
+ gs_free_string(pdev->pdf_memory, (byte *)pcpo->char_name.data, pcpo->char_name.size, "Free CharProc name");*/
+ pcpo->char_name.data = (byte *)0L;
+ pcpo->char_name.size = 0;
+ }
+ gs_free_object(pdev->pdf_memory, pcpo, "Free CharProc");
+ pcpo = next;
+ }
+ return 0;
+}
+
+/* Begin a CharProc for a synthesized (bitmap) font. */
+int
+pdf_begin_char_proc(gx_device_pdf * pdev, int w, int h, int x_width,
+ int y_offset, int x_offset, gs_id id, pdf_char_proc_t ** ppcp,
+ pdf_stream_position_t * ppos)
+{
+ gs_char char_code = 0;
+ pdf_bitmap_fonts_t *const pbfs = pdev->text->bitmap_fonts;
+ pdf_font_resource_t *font;
+ pdf_resource_t *pres;
+ pdf_char_proc_t *pcp;
+ int code;
+ /* This code added to store PCL bitmap glyphs in type 3 fonts where possible */
+ gs_glyph glyph = GS_NO_GLYPH;
+ gs_const_string *str = NULL;
+ gs_show_enum *show_enum = (gs_show_enum *)pdev->pte;
+ pdf_encoding_element_t *pet = 0;
+ /* Since this is for text searching, its only useful if the character code
+ * lies in an ASCII range, so we only handle some kinds of text layout.
+ */
+ int allowed_op = (show_enum->text.operation &
+ (TEXT_FROM_STRING | TEXT_FROM_BYTES | TEXT_FROM_CHARS | TEXT_FROM_SINGLE_CHAR));
+
+ /* Check to see the current font is a type 3. We can get here if pdfwrite decides
+ * it can't handle a font type, and renders to a bitmap instead. If that's the
+ * case then we can't add the bitmap to the existing font (its not a type 3 font)
+ * and must fall back to holding it in our fallback type 3 font 'collection'.
+ */
+ /* Because the bitmaps are stored directly in the cache they already have any
+ * effects caused by non-identity FontMatrix entries applied. So if the type 3
+ * font we created has a non-identity FontMatrix we can't use it and must
+ * go back to collecting the bitmap into our fallback font.
+ */
+ if ((show_enum->current_font->FontType == ft_user_defined ||
+ show_enum->current_font->FontType == ft_PCL_user_defined ||
+ show_enum->current_font->FontType == ft_MicroType ||
+ show_enum->current_font->FontType == ft_GL2_stick_user_defined ||
+ show_enum->current_font->FontType == ft_GL2_531) && allowed_op &&
+ show_enum->current_font->FontMatrix.xx == 1 && show_enum->current_font->FontMatrix.xy == 0 &&
+ show_enum->current_font->FontMatrix.yx == 0 && show_enum->current_font->FontMatrix.yy == 1) {
+ pdf_char_proc_ownership_t *pcpo;
+
+ gs_font_base *base = (gs_font_base *)show_enum->current_font;
+ code = pdf_attached_font_resource(pdev, show_enum->current_font, &font, NULL, NULL, NULL, NULL);
+ if (code < 0)
+ return code;
+ /* The text processing will have run past the glyph, so we need to 'back up'
+ * by one and get it again in order to get the character code and glyph, and update
+ * the pointer correctly.
+ */
+ show_enum->index--;
+ code = gs_default_next_char_glyph((gs_text_enum_t *)show_enum, (gs_char *)&char_code, &glyph);
+ if (code < 0)
+ return code;
+
+ /* If the returned character code is outside the possible Encoding for
+ * a type 3 font, then set pet to NULL, this means we will fall back to
+ * the 'collection' font, as pet is checked below.
+ */
+ if (char_code >= 0 && char_code <= 255) {
+ pet = &font->u.simple.Encoding[char_code];
+ if (pet) {
+ /* Check to see if we *already* have this glyph in this font. If
+ * we do then we can't add it to this font. Setting pet to 0
+ * falls back to the collection method.
+ */
+ for (pcpo = font->u.simple.s.type3.char_procs; pcpo != NULL; pcpo = pcpo->char_next) {
+ if (pcpo->glyph == pet->glyph && pcpo->char_code == char_code) {
+ pet = 0x00;
+ break;
+ }
+ }
+ }
+ }
+ else
+ pet = 0x00;
+
+ /* We need a glyph name for the type 3 font's Encoding, if we haven't got one
+ * then we need to give up, something about the font or text is not acceptable
+ * (see various comments above).
+ */
+ if (pet && pet->glyph != GS_NO_GLYPH && !(pet->str.size == 7 &&
+ !strncmp((const char *)pet->str.data, ".notdef", 7))) {
+ if (char_code < font->u.simple.FirstChar)
+ font->u.simple.FirstChar = char_code;
+ if ((int)char_code > font->u.simple.LastChar)
+ font->u.simple.LastChar = char_code;
+ base->FontBBox.q.x = max(base->FontBBox.q.x, w);
+ base->FontBBox.q.y = max(base->FontBBox.q.y, y_offset + h);
+ str = &pet->str;
+ glyph = pet->glyph;
+ /* This is to work around a weird Acrobat bug. If the Encoding of a type 3
+ * (possibly other types) is simply a standard encoding (eg WinAnsiEncoding)
+ * then Acrobat 4 & 8 just ignore the glyphs altogether. This forces us to write
+ * all the used glyphs as /Differencess, and that makes it work <sigh>
+ */
+ pet->is_difference = 1;
+ font->Widths[char_code] = psdf_round(pdev->char_width.x, 100, 10); /* See
+ pdf_write_Widths about rounding. We need to provide
+ a compatible data for Tj. */
+ } else {
+ char_code = assign_char_code(pdev, pdev->pte);
+ font = pbfs->open_font; /* Type 3 */
+ }
+ } else {
+ char_code = assign_char_code(pdev, pdev->pte);
+ font = pbfs->open_font; /* Type 3 */
+ }
+
+ code = pdf_begin_resource(pdev, resourceCharProc, id, &pres);
+ if (code < 0)
+ return code;
+ pcp = (pdf_char_proc_t *) pres;
+ code = pdf_attach_charproc(pdev, font, pcp, glyph, char_code, str);
+ if (code < 0)
+ return code;
+ pres->object->written = true;
+ {
+ stream *s = pdev->strm;
+
+ /*
+ * The resource file is positionable, so rather than use an
+ * object reference for the length, we'll go back and fill it in
+ * at the end of the definition. Take 1M as the longest
+ * definition we can handle. (This used to be 10K, but there was
+ * a real file that exceeded this limit.)
+ */
+ stream_puts(s, "<</Length >>stream\n");
+ ppos->start_pos = stell(s);
+ }
+ code = pdf_begin_encrypt(pdev, &pdev->strm, pres->object->id);
+ if (code < 0)
+ return code;
+ pcp->y_offset = y_offset;
+ pcp->x_offset = x_offset;
+ font->u.simple.s.type3.FontBBox.q.x =
+ max(font->u.simple.s.type3.FontBBox.q.x, w);
+ font->u.simple.s.type3.FontBBox.q.y =
+ max(font->u.simple.s.type3.FontBBox.q.y, y_offset + h);
+ font->u.simple.s.type3.max_y_offset =
+ max(font->u.simple.s.type3.max_y_offset, h + (h >> 2));
+ pcp->real_width.x = w;
+ pcp->real_width.y = y_offset + h;
+ *ppcp = pcp;
+ return 0;
+}
+
+/* End a CharProc. */
+int
+pdf_end_char_proc(gx_device_pdf * pdev, pdf_stream_position_t * ppos)
+{
+ stream *s;
+ gs_offset_t start_pos, end_pos, length;
+
+ pdf_end_encrypt(pdev);
+ s = pdev->strm;
+ start_pos = ppos->start_pos;
+ end_pos = stell(s);
+ length = end_pos - start_pos;
+ if (length > 999999)
+ return_error(gs_error_limitcheck);
+ sseek(s, start_pos - 15);
+ pprintd1(s, "%d", length);
+ sseek(s, end_pos);
+ if (pdev->PDFA != 0)
+ stream_puts(s, "\n");
+ stream_puts(s, "endstream\n");
+ pdf_end_separate(pdev, resourceCharProc);
+ return 0;
+}
+
+/* Mark glyph names for garbager. */
+void
+pdf_mark_glyph_names(const pdf_font_resource_t *pdfont, const gs_memory_t *memory)
+{
+ if (pdfont->mark_glyph == NULL) {
+ /* Synthesised bitmap fonts pass here. */
+ return;
+ }
+ if (pdfont->u.simple.Encoding != NULL) {
+ int i;
+
+ for (i = 0; i < 256; i++)
+ if (pdfont->u.simple.Encoding[i].glyph != GS_NO_GLYPH)
+ pdfont->mark_glyph(memory, pdfont->u.simple.Encoding[i].glyph, pdfont->mark_glyph_data);
+ }
+ if (pdfont->FontType == ft_user_defined ||
+ pdfont->FontType == ft_PCL_user_defined ||
+ pdfont->FontType == ft_MicroType ||
+ pdfont->FontType == ft_GL2_stick_user_defined ||
+ pdfont->FontType == ft_GL2_531) {
+ const pdf_char_proc_ownership_t *pcpo = pdfont->u.simple.s.type3.char_procs;
+
+ for (; pcpo != NULL; pcpo = pcpo->font_next)
+ pdfont->mark_glyph(memory, pcpo->glyph, pdfont->mark_glyph_data);
+ }
+}
+
+/* Put out a reference to an image as a character in a synthesized font. */
+int
+pdf_do_char_image(gx_device_pdf * pdev, const pdf_char_proc_t * pcp,
+ const gs_matrix * pimat)
+{
+ /* We need to choose a font, which use the charproc.
+ In most cases it is the last font, which the charproc is attached to.
+ If the charproc is substituted, it causes a font change. */
+ const pdf_char_proc_ownership_t * pcpo = pcp->owner_fonts;
+ pdf_font_resource_t *pdfont = pcpo->font;
+ byte ch = pcpo->char_code;
+ pdf_text_state_values_t values;
+
+ values.character_spacing = 0;
+ values.pdfont = pdfont;
+ values.size = 1;
+ values.matrix = *pimat;
+ values.render_mode = pdev->pte->pis->text_rendering_mode;
+ values.word_spacing = 0;
+ pdf_set_text_state_values(pdev, &values);
+ pdf_bitmap_char_update_bbox(pdev, pcp->x_offset, pcp->y_offset, pcp->real_width.x, pcp->real_width.y);
+ pdf_append_chars(pdev, &ch, 1, pdfont->Widths[ch] * pimat->xx, 0.0, false);
+ return 0;
+}
+
+/*
+ * Write the Encoding for bitmap fonts, if needed.
+ */
+int
+pdf_write_bitmap_fonts_Encoding(gx_device_pdf *pdev)
+{
+ pdf_bitmap_fonts_t *pbfs = pdev->text->bitmap_fonts;
+
+ if (pbfs->bitmap_encoding_id) {
+ stream *s;
+ int i;
+
+ pdf_open_separate(pdev, pbfs->bitmap_encoding_id, resourceEncoding);
+ s = pdev->strm;
+ /*
+ * Even though the PDF reference documentation says that a
+ * BaseEncoding key is required unless the encoding is
+ * "based on the base font's encoding" (and there is no base
+ * font in this case), Acrobat 2.1 gives an error if the
+ * BaseEncoding key is present.
+ */
+ stream_puts(s, "<</Type/Encoding/Differences[0");
+ for (i = 0; i <= pbfs->max_embedded_code; ++i) {
+ if (!(i & 15))
+ stream_puts(s, "\n");
+ pprintd1(s, "/a%d", i);
+ }
+ stream_puts(s, "\n] >>\n");
+ pdf_end_separate(pdev, resourceEncoding);
+ pbfs->bitmap_encoding_id = 0;
+ }
+ return 0;
+}
+
+/*
+ * Start charproc accumulation for a Type 3 font.
+ */
+int
+pdf_start_charproc_accum(gx_device_pdf *pdev)
+{
+ pdf_char_proc_t *pcp;
+ pdf_resource_t *pres;
+ int id = gs_next_ids(pdev->memory, 1);
+ int code = pdf_enter_substream(pdev, resourceCharProc, id,
+ &pres, false, pdev->CompressFonts);
+
+ pres->rid = id;
+ if (code < 0)
+ return code;
+ pcp = (pdf_char_proc_t *)pres;
+ pcp->owner_fonts = NULL;
+ return 0;
+}
+
+/*
+ * Install charproc accumulator for a Type 3 font.
+ */
+int
+pdf_set_charproc_attrs(gx_device_pdf *pdev, gs_font *font, double *pw, int narg,
+ gs_text_cache_control_t control, gs_char ch, bool scale_100)
+{
+ pdf_font_resource_t *pdfont;
+ pdf_resource_t *pres = pdev->accumulating_substream_resource;
+ pdf_char_proc_t *pcp;
+ int code;
+
+ code = pdf_attached_font_resource(pdev, font, &pdfont, NULL, NULL, NULL, NULL);
+ if (code < 0)
+ return code;
+ pcp = (pdf_char_proc_t *)pres;
+ pcp->owner_fonts = NULL;
+ pcp->real_width.x = pw[font->WMode && narg > 6 ? 6 : 0];
+ pcp->real_width.y = pw[font->WMode && narg > 6 ? 7 : 1];
+ pcp->v.x = (narg > 8 ? pw[8] : 0);
+ pcp->v.y = (narg > 8 ? pw[9] : 0);
+ if (control == TEXT_SET_CHAR_WIDTH) {
+ /* PLRM 5.7.1 "BuildGlyph" reads : "Normally, it is unnecessary and
+ undesirable to initialize the current color parameter, because show
+ is defined to paint glyphs with the current color."
+ However comparefiles/Bug687044.ps doesn't follow that. */
+ pdev->skip_colors = false;
+ pprintg1(pdev->strm, "%g 0 d0\n", (float)pw[0]);
+ /* The colour change described above can't affect PCL fonts and we need
+ * all glyphs to be noted as cached in order for the bitmap font cache
+ * probing to work properly.
+ */
+ if (font->FontType == ft_PCL_user_defined || font->FontType == ft_GL2_stick_user_defined
+ || font->FontType == ft_GL2_531 || font->FontType == ft_MicroType)
+ pdfont->u.simple.s.type3.cached[ch >> 3] |= 0x80 >> (ch & 7);
+ } else {
+ double d;
+ pdev->skip_colors = true;
+ if (pw[4] < pw[2]) {
+ d = pw[2];
+ pw[2] = pw[4];
+ pw[4] = d;
+ }
+ if (pw[5] < pw[3]) {
+ d = pw[5];
+ pw[5] = pw[3];
+ pw[3] = d;
+ }
+ pprintg6(pdev->strm, "%g %g %g %g %g %g d1\n",
+ (float)pw[0], (float)0.0, (float)pw[2],
+ (float)pw[3], (float)pw[4], (float)pw[5]);
+ pdfont->u.simple.s.type3.cached[ch >> 3] |= 0x80 >> (ch & 7);
+ }
+ /* See comments in pdf_text_process regarding type 3 CharProc accumulation
+ * Initially this matrix was emitted there, at the start of the accumulator
+ * but if we do that then GS incorrectly applied the matrix to the 'd1'
+ * operator. We write the scale matrix here because this is *after* the
+ * 'd1' has been emitted above, and so does not affect it.
+ */
+ if (scale_100) {
+ code = stream_puts(pdev->strm, "0.01 0 0 0.01 0 0 cm\n");
+ if (code < 0)
+ return code;
+ }
+ return 0;
+}
+
+/*
+ * Open a stream object in the temporary file.
+ */
+
+int
+pdf_open_aside(gx_device_pdf *pdev, pdf_resource_type_t rtype,
+ gs_id id, pdf_resource_t **ppres, bool reserve_object_id, int options)
+{
+ int code;
+ pdf_resource_t *pres;
+ stream *s, *save_strm = pdev->strm;
+ pdf_data_writer_t writer;
+ static const pdf_filter_names_t fnames = {
+ PDF_FILTER_NAMES
+ };
+
+ pdev->streams.save_strm = pdev->strm;
+
+ if (rtype > NUM_RESOURCE_TYPES)
+ rtype = resourceOther;
+ code = pdf_alloc_aside(pdev, PDF_RESOURCE_CHAIN(pdev, rtype, id),
+ pdf_resource_type_structs[rtype], &pres, reserve_object_id ? 0 : -1);
+ if (code < 0)
+ return code;
+ cos_become(pres->object, cos_type_stream);
+ s = cos_write_stream_alloc((cos_stream_t *)pres->object, pdev, "pdf_enter_substream");
+ if (s == 0)
+ return_error(gs_error_VMerror);
+ pdev->strm = s;
+ code = pdf_append_data_stream_filters(pdev, &writer,
+ options | DATA_STREAM_NOLENGTH, pres->object->id);
+ if (code < 0) {
+ pdev->strm = save_strm;
+ return code;
+ }
+ code = pdf_put_filters((cos_dict_t *)pres->object, pdev, writer.binary.strm, &fnames);
+ if (code < 0) {
+ pdev->strm = save_strm;
+ return code;
+ }
+ pdev->strm = writer.binary.strm;
+ *ppres = pres;
+ return 0;
+}
+
+/*
+ * Close a stream object in the temporary file.
+ */
+int
+pdf_close_aside(gx_device_pdf *pdev)
+{
+ /* We should call pdf_end_data here, but we don't want to put pdf_data_writer_t
+ into pdf_substream_save stack to simplify garbager descriptors.
+ Use a lower level functions instead that. */
+ stream *s = pdev->strm;
+ cos_stream_t *pcs = cos_stream_from_pipeline(s);
+ int status = s_close_filters(&s, NULL);
+
+ pdev->strm = pdev->streams.save_strm;
+ if (status < 0)
+ return(gs_note_error(gs_error_ioerror));
+
+ pcs->is_open = false;
+ return 0;
+}
+
+/*
+ * Enter the substream accumulation mode.
+ */
+int
+pdf_enter_substream(gx_device_pdf *pdev, pdf_resource_type_t rtype,
+ gs_id id, pdf_resource_t **ppres, bool reserve_object_id, bool compress)
+{
+ int sbstack_ptr = pdev->sbstack_depth;
+ pdf_resource_t *pres;
+ stream *save_strm = pdev->strm;
+ int code;
+
+ if (pdev->sbstack_depth >= pdev->sbstack_size)
+ return_error(gs_error_unregistered); /* Must not happen. */
+ if (pdev->sbstack[sbstack_ptr].text_state == 0) {
+ pdev->sbstack[sbstack_ptr].text_state = pdf_text_state_alloc(pdev->pdf_memory);
+ if (pdev->sbstack[sbstack_ptr].text_state == 0)
+ return_error(gs_error_VMerror);
+ }
+ code = pdf_open_aside(pdev, rtype, id, &pres, reserve_object_id,
+ (compress ? DATA_STREAM_COMPRESS : 0));
+ if (code < 0)
+ return code;
+ code = pdf_save_viewer_state(pdev, NULL);
+ if (code < 0) {
+ pdev->strm = save_strm;
+ return code;
+ }
+ pdev->sbstack[sbstack_ptr].context = pdev->context;
+ pdf_text_state_copy(pdev->sbstack[sbstack_ptr].text_state, pdev->text->text_state);
+ pdf_set_text_state_default(pdev->text->text_state);
+ pdev->sbstack[sbstack_ptr].clip_path = pdev->clip_path;
+ pdev->clip_path = 0;
+ pdev->sbstack[sbstack_ptr].clip_path_id = pdev->clip_path_id;
+ pdev->clip_path_id = pdev->no_clip_path_id;
+ pdev->sbstack[sbstack_ptr].vgstack_bottom = pdev->vgstack_bottom;
+ pdev->vgstack_bottom = pdev->vgstack_depth;
+ pdev->sbstack[sbstack_ptr].strm = save_strm;
+ pdev->sbstack[sbstack_ptr].procsets = pdev->procsets;
+ pdev->sbstack[sbstack_ptr].substream_Resources = pdev->substream_Resources;
+ pdev->sbstack[sbstack_ptr].skip_colors = pdev->skip_colors;
+ pdev->sbstack[sbstack_ptr].font3 = pdev->font3;
+ pdev->sbstack[sbstack_ptr].accumulating_substream_resource = pdev->accumulating_substream_resource;
+ pdev->sbstack[sbstack_ptr].charproc_just_accumulated = pdev->charproc_just_accumulated;
+ pdev->sbstack[sbstack_ptr].accumulating_a_global_object = pdev->accumulating_a_global_object;
+ pdev->sbstack[sbstack_ptr].pres_soft_mask_dict = pdev->pres_soft_mask_dict;
+ pdev->sbstack[sbstack_ptr].objname = pdev->objname;
+ pdev->sbstack[sbstack_ptr].last_charpath_op = pdev->last_charpath_op;
+ pdev->skip_colors = false;
+ pdev->charproc_just_accumulated = false;
+ pdev->pres_soft_mask_dict = NULL;
+ pdev->objname.data = NULL;
+ pdev->objname.size = 0;
+ /* Do not reset pdev->accumulating_a_global_object - it inherits. */
+ pdev->sbstack_depth++;
+ pdev->procsets = 0;
+ pdev->font3 = 0;
+ pdev->context = PDF_IN_STREAM;
+ pdev->accumulating_substream_resource = pres;
+ pdev->last_charpath_op = 0;
+ /* Do not alter type3charpath, inherit the current value. We need to know if */
+ /* we are inside a charpath operation, and only reset this when the charpath */
+ /* is complete */
+ pdf_reset_graphics(pdev);
+ *ppres = pres;
+ return 0;
+}
+
+/*
+ * Exit the substream accumulation mode.
+ */
+int
+pdf_exit_substream(gx_device_pdf *pdev)
+{
+ int code, code1;
+ int sbstack_ptr;
+
+ if (pdev->sbstack_depth <= 0)
+ return_error(gs_error_unregistered); /* Must not happen. */
+ code = pdf_open_contents(pdev, PDF_IN_STREAM);
+ sbstack_ptr = pdev->sbstack_depth - 1;
+ while (pdev->vgstack_depth > pdev->vgstack_bottom) {
+ code1 = pdf_restore_viewer_state(pdev, pdev->strm);
+ if (code >= 0)
+ code = code1;
+ }
+ if (pdev->clip_path != 0)
+ gx_path_free(pdev->clip_path, "pdf_end_charproc_accum");
+ code1 = pdf_close_aside(pdev);
+ if (code1 < 0 && code >= 0)
+ code = code1;
+ pdev->context = pdev->sbstack[sbstack_ptr].context;
+ pdf_text_state_copy(pdev->text->text_state, pdev->sbstack[sbstack_ptr].text_state);
+ gs_free_object(pdev->pdf_memory, pdev->sbstack[sbstack_ptr].text_state, "free text state for stream");
+ pdev->sbstack[sbstack_ptr].text_state = 0;
+ pdev->clip_path = pdev->sbstack[sbstack_ptr].clip_path;
+ pdev->sbstack[sbstack_ptr].clip_path = 0;
+ pdev->clip_path_id = pdev->sbstack[sbstack_ptr].clip_path_id;
+ pdev->vgstack_bottom = pdev->sbstack[sbstack_ptr].vgstack_bottom;
+ pdev->strm = pdev->sbstack[sbstack_ptr].strm;
+ pdev->sbstack[sbstack_ptr].strm = 0;
+ pdev->procsets = pdev->sbstack[sbstack_ptr].procsets;
+ pdev->substream_Resources = pdev->sbstack[sbstack_ptr].substream_Resources;
+ pdev->sbstack[sbstack_ptr].substream_Resources = 0;
+ pdev->skip_colors = pdev->sbstack[sbstack_ptr].skip_colors;
+ pdev->font3 = pdev->sbstack[sbstack_ptr].font3;
+ pdev->sbstack[sbstack_ptr].font3 = 0;
+ pdev->accumulating_substream_resource = pdev->sbstack[sbstack_ptr].accumulating_substream_resource;
+ pdev->sbstack[sbstack_ptr].accumulating_substream_resource = 0;
+ pdev->charproc_just_accumulated = pdev->sbstack[sbstack_ptr].charproc_just_accumulated;
+ pdev->accumulating_a_global_object = pdev->sbstack[sbstack_ptr].accumulating_a_global_object;
+ pdev->pres_soft_mask_dict = pdev->sbstack[sbstack_ptr].pres_soft_mask_dict;
+ pdev->objname = pdev->sbstack[sbstack_ptr].objname;
+ pdev->last_charpath_op = pdev->sbstack[sbstack_ptr].last_charpath_op;
+ pdev->sbstack_depth = sbstack_ptr;
+ code1 = pdf_restore_viewer_state(pdev, NULL);
+ if (code1 < 0 && code >= 0)
+ code = code1;
+ return code;
+}
+
+static bool
+pdf_is_same_charproc_attrs1(gx_device_pdf *pdev, pdf_char_proc_t *pcp0, pdf_char_proc_t *pcp1)
+{
+ if (pcp0->real_width.x != pcp1->real_width.x)
+ return false;
+ if (pcp0->real_width.y != pcp1->real_width.y)
+ return false;
+ if (pcp0->v.x != pcp1->v.x)
+ return false;
+ if (pcp0->v.y != pcp1->v.y)
+ return false;
+ return true;
+}
+
+typedef struct charproc_compatibility_data_s {
+ const pdf_char_glyph_pairs_t *cgp;
+ pdf_font_resource_t *pdfont;
+ gs_char char_code;
+ gs_glyph glyph;
+ gs_font *font;
+} charproc_compatibility_data_t;
+
+static bool
+is_char_code_used(pdf_font_resource_t *pdfont, gs_char char_code)
+{
+ pdf_char_proc_ownership_t *pcpo;
+
+ for (pcpo = pdfont->u.simple.s.type3.char_procs; pcpo != NULL; pcpo = pcpo->char_next) {
+ if (pcpo->char_code == char_code) {
+ return true;
+ }
+ }
+ return false;
+}
+
+static int
+pdf_is_charproc_compatible(gx_device_pdf * pdev, pdf_resource_t *pres0, pdf_resource_t *pres1)
+{
+ charproc_compatibility_data_t *data = (charproc_compatibility_data_t *)pdev->find_resource_param;
+ pdf_char_proc_t *pcp0 = (pdf_char_proc_t *)pres0;
+ pdf_char_proc_t *pcp1 = (pdf_char_proc_t *)pres1;
+ pdf_font_resource_t *pdfont = data->pdfont;
+ pdf_char_proc_ownership_t *pcpo;
+ pdf_font_cache_elem_t **e;
+ bool can_add_to_current_font = false, computed_can_add_to_current_font = false;
+
+ /* Does it have same attributes ? */
+ if (!pdf_is_same_charproc_attrs1(pdev, pcp0, pcp1))
+ return 0;
+ /* Is it from same font ? */
+ for (pcpo = pcp1->owner_fonts; pcpo != NULL; pcpo = pcpo->char_next) {
+ if (pdfont == pcpo->font) {
+ /* Check for encoding conflict. */
+ if (pcpo->char_code == data->char_code && pcpo->glyph == data->glyph)
+ return 1; /* Same char code. */
+ if (!computed_can_add_to_current_font) {
+ can_add_to_current_font = !is_char_code_used(pdfont, data->char_code);
+ computed_can_add_to_current_font = true;
+ }
+ if (can_add_to_current_font)
+ return 1; /* No conflict. */
+ }
+ }
+ /* Look for another font with same encoding,
+ because we want to reduce the number of new fonts.
+ We also restrict with ones attached to same PS font,
+ otherwise it creates too mixed fonts and disturbs word breaks.
+ */
+ e = pdf_locate_font_cache_elem(pdev, data->font);
+ if (e != NULL) {
+ for (pcpo = pcp1->owner_fonts; pcpo != NULL; pcpo = pcpo->char_next) {
+ if (pcpo->char_code != data->char_code || pcpo->glyph != data->glyph)
+ continue; /* Need same Encoding to generate a proper ToUnicode. */
+ if (pdfont->u.simple.s.type3.bitmap_font != pcpo->font->u.simple.s.type3.bitmap_font)
+ continue;
+ if (gs_matrix_compare(&pdfont->u.simple.s.type3.FontMatrix, &pcpo->font->u.simple.s.type3.FontMatrix))
+ continue;
+ if (data->cgp != NULL) {
+ if (!pdf_check_encoding_compatibility(pcpo->font, data->cgp->s, data->cgp->num_all_chars))
+ continue;
+ }
+ if ((*e)->pdfont != pcpo->font)
+ continue;
+ data->pdfont = pcpo->font; /* Switch to the other font. */
+ return 1;
+ }
+ }
+ /* Check whether it can be added into the current font. */
+ if (!computed_can_add_to_current_font)
+ can_add_to_current_font = !is_char_code_used(pdfont, data->char_code);
+ if (!can_add_to_current_font) {
+ /* Can't substitute due to encoding conflict. */
+ return 0;
+ }
+ /* The current font will share it with another font. */
+ return 1;
+}
+
+static int
+pdf_find_same_charproc_aux(gx_device_pdf *pdev,
+ pdf_font_resource_t **ppdfont, pdf_char_proc_t **ppcp)
+{
+ pdf_char_proc_ownership_t *pcpo;
+ int code;
+
+ /* fixme: this passes parameters to pdf_is_charproc_compatible
+ through special gx_device_pdf field pdev->find_resource_param
+ due to prototype limitation of pdf_find_same_resource.
+ It would be better to change the client data argument type in there to void. */
+ for (pcpo = (*ppdfont)->u.simple.s.type3.char_procs; pcpo != NULL; pcpo = pcpo->char_next) {
+ pdf_char_proc_t *pcp = pcpo->char_proc;
+
+ if (*ppcp != pcp && pdf_is_same_charproc_attrs1(pdev, *ppcp, pcp)) {
+ cos_object_t *pco0 = pcp->object;
+ cos_object_t *pco1 = (*ppcp)->object;
+
+ code = pco0->cos_procs->equal(pco0, pco1, pdev);
+ if (code < 0) {
+ return code;
+ }
+ if (code) {
+ *ppcp = pcp;
+ return 1;
+ }
+ }
+ }
+ return pdf_find_same_resource(pdev, resourceCharProc, (pdf_resource_t **)ppcp, pdf_is_charproc_compatible);
+}
+static int
+pdf_find_same_charproc(gx_device_pdf *pdev,
+ pdf_font_resource_t **ppdfont, const pdf_char_glyph_pairs_t *cgp,
+ pdf_char_proc_t **ppcp, gs_glyph glyph, gs_char char_code,
+ gs_font *font)
+{
+ charproc_compatibility_data_t data;
+ int code;
+
+ data.cgp = cgp;
+ data.pdfont = *ppdfont;
+ data.char_code = char_code;
+ data.glyph = glyph;
+ data.font = font;
+ pdev->find_resource_param = &data;
+ code = pdf_find_same_charproc_aux(pdev, ppdfont, ppcp);
+ pdev->find_resource_param = NULL;
+ *ppdfont = data.pdfont;
+ return code;
+}
+
+static bool
+pdf_is_charproc_defined(gx_device_pdf *pdev, pdf_font_resource_t *pdfont, gs_char ch)
+{
+ pdf_char_proc_ownership_t *pcpo;
+
+ for (pcpo = pdfont->u.simple.s.type3.char_procs; pcpo != NULL; pcpo = pcpo->char_next) {
+ if (pcpo->char_code == ch)
+ return true;
+ }
+ return false;
+}
+
+static int
+complete_adding_char(gx_device_pdf *pdev, gs_font *font,
+ gs_glyph glyph, gs_char ch, pdf_char_proc_t *pcp,
+ const gs_const_string *gnstr)
+{
+ pdf_font_resource_t *pdfont;
+ double *real_widths;
+ byte *glyph_usage;
+ int char_cache_size, width_cache_size;
+ pdf_encoding_element_t *pet;
+ int code;
+
+ code = pdf_attached_font_resource(pdev, font, &pdfont,
+ &glyph_usage, &real_widths, &char_cache_size, &width_cache_size);
+ if (code < 0)
+ return code;
+ code = pdf_attach_charproc(pdev, pdfont, pcp, glyph, ch, gnstr);
+ if (code < 0)
+ return code;
+ if (ch >= char_cache_size || ch >= width_cache_size)
+ return_error(gs_error_unregistered); /* Must not happen. */
+ pet = &pdfont->u.simple.Encoding[ch];
+ pdfont->Widths[ch] = pcp->real_width.x;
+ real_widths[ch * 2 ] = pcp->real_width.x;
+ real_widths[ch * 2 + 1] = pcp->real_width.y;
+ glyph_usage[ch / 8] |= 0x80 >> (ch & 7);
+ pdfont->used[ch >> 3] |= 0x80 >> (ch & 7);
+ if (pdfont->u.simple.v != NULL && font->WMode) {
+ pdfont->u.simple.v[ch].x = pcp->v.x;
+ pdfont->u.simple.v[ch].y = pcp->v.x;
+ }
+ pet->glyph = glyph;
+ pet->str = *gnstr;
+ pet->is_difference = true;
+ if (pdfont->u.simple.LastChar < (int)ch)
+ pdfont->u.simple.LastChar = (int)ch;
+ if (pdfont->u.simple.FirstChar > (int)ch)
+ pdfont->u.simple.FirstChar = (int)ch;
+ return 0;
+}
+
+static int
+pdf_char_widths_from_charprocs(gx_device_pdf *pdev, gs_font *font)
+{
+ pdf_font_resource_t *pdfont;
+ double *real_widths;
+ byte *glyph_usage;
+ int char_cache_size, width_cache_size;
+ pdf_char_proc_ownership_t *pcpo;
+ int code;
+
+ code = pdf_attached_font_resource(pdev, font, &pdfont,
+ &glyph_usage, &real_widths, &char_cache_size, &width_cache_size);
+ if (code < 0)
+ return code;
+ for (pcpo = pdfont->u.simple.s.type3.char_procs; pcpo != NULL; pcpo = pcpo->char_next) {
+ pdf_char_proc_t *pcp = pcpo->char_proc;
+ gs_char ch = pcpo->char_code;
+
+ real_widths[ch * 2 ] = pcp->real_width.x;
+ real_widths[ch * 2 + 1] = pcp->real_width.y;
+ glyph_usage[ch / 8] |= 0x80 >> (ch & 7);
+ }
+ return 0;
+}
+
+/*
+ * Complete charproc accumulation for a Type 3 font.
+ */
+int
+pdf_end_charproc_accum(gx_device_pdf *pdev, gs_font *font, const pdf_char_glyph_pairs_t *cgp,
+ gs_glyph glyph, gs_char output_char_code, const gs_const_string *gnstr)
+{
+ int code;
+ pdf_resource_t *pres = (pdf_resource_t *)pdev->accumulating_substream_resource;
+ /* We could use pdfont->u.simple.s.type3.char_procs insted the thing above
+ unless the font is defined recursively.
+ But we don't want such assumption. */
+ pdf_char_proc_t *pcp = (pdf_char_proc_t *)pres;
+ pdf_font_resource_t *pdfont;
+ gs_char ch = output_char_code;
+ bool checking_glyph_variation = false;
+
+ if (ch == GS_NO_CHAR)
+ return_error(gs_error_unregistered); /* Must not happen. */
+ if (ch >= 256)
+ return_error(gs_error_unregistered); /* Must not happen. */
+ code = pdf_attached_font_resource(pdev, font, &pdfont, NULL, NULL, NULL, NULL);
+ if (code < 0)
+ return code;
+ if (pdfont != (pdf_font_resource_t *)pdev->font3)
+ return_error(gs_error_unregistered); /* Must not happen. */
+ code = pdf_exit_substream(pdev);
+ if (code < 0)
+ return code;
+ if (!(pdfont->used[ch >> 3] & (0x80 >> (ch & 7))) ||
+ !(pdfont->u.simple.s.type3.cached[ch >> 3] & (0x80 >> (ch & 7)))) {
+ /* First appearence or not cached - check for duplicates. */
+ pdf_font_resource_t *pdfont1 = pdfont;
+
+ checking_glyph_variation = true;
+ /* CAUTION : a possible font change. */
+ code = pdf_find_same_charproc(pdev, &pdfont, cgp, &pcp, glyph, ch, font);
+ if (code < 0)
+ return code;
+ if (code != 0) {
+ code = pdf_cancel_resource(pdev, pres, resourceCharProc);
+ if (code < 0)
+ return code;
+ pdf_forget_resource(pdev, pres, resourceCharProc);
+ if (pdfont1 != pdfont) {
+ code = pdf_attach_font_resource(pdev, font, pdfont);
+ if (code < 0)
+ return code;
+ code = pdf_char_widths_from_charprocs(pdev, font);
+ if (code < 0)
+ return code;
+ }
+ pdev->charproc_just_accumulated = true;
+ return complete_adding_char(pdev, font, glyph, ch, pcp, gnstr);
+ }
+ if (pdf_is_charproc_defined(pdev, pdfont, ch)) {
+ /* Encoding conflict after a font change. */
+ gs_font *base_font = font, *below;
+
+ while ((below = base_font->base) != base_font &&
+ base_font->procs.same_font(base_font, below, FONT_SAME_OUTLINES))
+ base_font = below;
+ code = pdf_make_font3_resource(pdev, base_font, &pdfont);
+ if (code < 0)
+ return code;
+ code = pdf_attach_font_resource(pdev, font, pdfont);
+ if (code < 0)
+ return code;
+ }
+ }
+ pdf_reserve_object_id(pdev, pres, 0);
+ if (checking_glyph_variation)
+ pdev->charproc_just_accumulated = true;
+ return complete_adding_char(pdev, font, glyph, ch, pcp, gnstr);
+}
+
+/* Add procsets to substream Resources. */
+int
+pdf_add_procsets(cos_dict_t *pcd, pdf_procset_t procsets)
+{
+ char str[5 + 7 + 7 + 7 + 5 + 2];
+ cos_value_t v;
+
+ strcpy(str, "[/PDF");
+ if (procsets & ImageB)
+ strcat(str, "/ImageB");
+ if (procsets & ImageC)
+ strcat(str, "/ImageC");
+ if (procsets & ImageI)
+ strcat(str, "/ImageI");
+ if (procsets & Text)
+ strcat(str, "/Text");
+ strcat(str, "]");
+ cos_string_value(&v, (byte *)str, strlen(str));
+ return cos_dict_put_c_key(pcd, "/ProcSet", &v);
+}
+
+/* Add a resource to substream Resources. */
+int
+pdf_add_resource(gx_device_pdf *pdev, cos_dict_t *pcd, const char *key, pdf_resource_t *pres)
+{
+ if (pcd != 0) {
+ const cos_value_t *v = cos_dict_find(pcd, (const byte *)key, strlen(key));
+ cos_dict_t *list;
+ int code;
+ char buf[10 + (sizeof(long) * 8 / 3 + 1)], buf1[sizeof(pres->rname) + 1];
+
+ if (pdev->ForOPDFRead && !pres->global && pdev->accumulating_a_global_object) {
+ pres->global = true;
+ code = cos_dict_put_c_key_bool((cos_dict_t *)pres->object, "/.Global", true);
+ if (code < 0)
+ return code;
+ }
+ gs_sprintf(buf, "%ld 0 R\n", pres->object->id);
+ if (v != NULL) {
+ if (v->value_type != COS_VALUE_OBJECT &&
+ v->value_type != COS_VALUE_RESOURCE)
+ return_error(gs_error_unregistered); /* Must not happen. */
+ list = (cos_dict_t *)v->contents.object;
+ if (list->cos_procs != &cos_dict_procs)
+ return_error(gs_error_unregistered); /* Must not happen. */
+ } else {
+ list = cos_dict_alloc(pdev, "pdf_add_resource");
+ if (list == NULL)
+ return_error(gs_error_VMerror);
+ code = cos_dict_put_c_key_object((cos_dict_t *)pcd, key, (cos_object_t *)list);
+ if (code < 0)
+ return code;
+ }
+ buf1[0] = '/';
+ strcpy(buf1 + 1, pres->rname);
+ return cos_dict_put_string(list, (const byte *)buf1, strlen(buf1),
+ (const byte *)buf, strlen(buf));
+ }
+ return 0;
+}
diff --git a/devices/vector/gdevpdti.h b/devices/vector/gdevpdti.h
new file mode 100644
index 000000000..de624a925
--- /dev/null
+++ b/devices/vector/gdevpdti.h
@@ -0,0 +1,95 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Bitmap font interface for pdfwrite */
+
+#ifndef gdevpdti_INCLUDED
+# define gdevpdti_INCLUDED
+
+#include "gdevpdt.h"
+
+/*
+ * Bitmap fonts are Type 3 fonts created internally by pdfwrite. Their
+ * CharProc consists of a single bitmap image at "device" resolution.
+ */
+
+/* ================ Types and structures ================ */
+
+/* Define the state structure for tracking bitmap fonts. */
+/*typedef struct pdf_bitmap_fonts_s pdf_bitmap_fonts_t;*/
+struct pdf_bitmap_fonts_s {
+ pdf_font_resource_t *open_font; /* current Type 3 synthesized font */
+ bool use_open_font; /* if false, start new open_font */
+ long bitmap_encoding_id;
+ int max_embedded_code; /* max Type 3 code used */
+};
+
+#ifndef pdf_bitmap_fonts_DEFINED
+# define pdf_bitmap_fonts_DEFINED
+typedef struct pdf_bitmap_fonts_s pdf_bitmap_fonts_t;
+#endif
+
+/* ================ Procedures ================ */
+
+/* Exported for gdevpdf.c */
+
+/*
+ * Update text state at the end of a page.
+ */
+void pdf_close_text_page(gx_device_pdf *pdev);
+
+/* Exported for gdevpdfb.c */
+
+/* Return the Y offset for a bitmap character image. */
+int pdf_char_image_y_offset(const gx_device_pdf *pdev, int x, int y, int h);
+
+/* Retrieve the x and y offsets for a charproc */
+int pdf_charproc_x_offset(pdf_char_proc_t *pcp);
+int pdf_charproc_y_offset(pdf_char_proc_t *pcp);
+
+/* Begin a CharProc for an embedded (bitmap) font. */
+int pdf_begin_char_proc(gx_device_pdf * pdev, int w, int h, int x_width,
+ int y_offset, int x_offset, gs_id id, pdf_char_proc_t **ppcp,
+ pdf_stream_position_t * ppos);
+
+/* End a CharProc. */
+int pdf_end_char_proc(gx_device_pdf * pdev, pdf_stream_position_t * ppos);
+
+int pdf_free_charproc_ownership(gx_device_pdf * pdev, pdf_resource_t *pres);
+
+/* Put out a reference to an image as a character in an embedded font. */
+int pdf_do_char_image(gx_device_pdf * pdev, const pdf_char_proc_t * pcp,
+ const gs_matrix * pimat);
+
+/* Only used within text code */
+
+/*
+ * Allocate and initialize bookkeeping for bitmap fonts.
+ */
+pdf_bitmap_fonts_t *pdf_bitmap_fonts_alloc(gs_memory_t *mem);
+
+/*
+ * Write the Encoding for bitmap fonts, if needed.
+ */
+int pdf_write_bitmap_fonts_Encoding(gx_device_pdf *pdev);
+
+/* Write the contents of a Type 3 bitmap font resource. */
+int pdf_write_contents_bitmap(gx_device_pdf *pdev, pdf_font_resource_t *pdfont);
+
+/* Mark glyph names for garbager. */
+void pdf_mark_glyph_names(const pdf_font_resource_t *pdfont, const gs_memory_t *memory);
+
+#endif /* gdevpdti_INCLUDED */
diff --git a/devices/vector/gdevpdts.c b/devices/vector/gdevpdts.c
new file mode 100644
index 000000000..edbae0ce6
--- /dev/null
+++ b/devices/vector/gdevpdts.c
@@ -0,0 +1,853 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Text state management for pdfwrite */
+#include "math_.h"
+#include "memory_.h"
+#include "gx.h"
+#include "gserrors.h"
+#include "gdevpdfx.h"
+#include "gdevpdfg.h"
+#include "gdevpdtx.h"
+#include "gdevpdtf.h" /* for pdfont->FontType */
+#include "gdevpdts.h"
+#include "gdevpdtt.h"
+#include "gdevpdti.h"
+
+/* ================ Types and structures ================ */
+
+/*
+ * We accumulate text, and possibly horizontal or vertical moves (depending
+ * on the font's writing direction), until forced to emit them. This
+ * happens when changing text state parameters, when the buffer is full, or
+ * when exiting text mode.
+ *
+ * Note that movement distances are measured in unscaled text space.
+ */
+typedef struct pdf_text_move_s {
+ int index; /* within buffer.chars */
+ float amount;
+} pdf_text_move_t;
+#define MAX_TEXT_BUFFER_CHARS 200 /* arbitrary, but overflow costs 5 chars */
+#define MAX_TEXT_BUFFER_MOVES 50 /* ibid. */
+typedef struct pdf_text_buffer_s {
+ /*
+ * Invariant:
+ * count_moves <= MAX_TEXT_BUFFER_MOVES
+ * count_chars <= MAX_TEXT_BUFFER_CHARS
+ * 0 < moves[0].index < moves[1].index < ... moves[count_moves-1].index
+ * <= count_chars
+ * moves[*].amount != 0
+ */
+ pdf_text_move_t moves[MAX_TEXT_BUFFER_MOVES + 1];
+ byte chars[MAX_TEXT_BUFFER_CHARS];
+ int count_moves;
+ int count_chars;
+} pdf_text_buffer_t;
+#define TEXT_BUFFER_DEFAULT\
+ { { 0, 0 } }, /* moves */\
+ { 0 }, /* chars */\
+ 0, /* count_moves */\
+ 0 /* count_chars */
+
+/*
+ * We maintain two sets of text state values (as defined in gdevpdts.h): the
+ * "in" set reflects the current state as seen by the client, while the
+ * "out" set reflects the current state as seen by an interpreter processing
+ * the content stream emitted so far. We emit commands to make "out" the
+ * same as "in" when necessary.
+ */
+/*typedef struct pdf_text_state_s pdf_text_state_t;*/ /* gdevpdts.h */
+struct pdf_text_state_s {
+ /* State as seen by client */
+ pdf_text_state_values_t in; /* see above */
+ gs_point start; /* in.txy as of start of buffer */
+ pdf_text_buffer_t buffer;
+ int wmode; /* WMode of in.font */
+ /* State relative to content stream */
+ pdf_text_state_values_t out; /* see above */
+ double leading; /* TL (not settable, only used internally) */
+ bool use_leading; /* if true, use T* or ' */
+ bool continue_line;
+ gs_point line_start;
+ gs_point out_pos; /* output position */
+ double PaintType0Width;
+};
+static const pdf_text_state_t ts_default = {
+ /* State as seen by client */
+ { TEXT_STATE_VALUES_DEFAULT }, /* in */
+ { 0, 0 }, /* start */
+ { TEXT_BUFFER_DEFAULT }, /* buffer */
+ 0, /* wmode */
+ /* State relative to content stream */
+ { TEXT_STATE_VALUES_DEFAULT }, /* out */
+ 0, /* leading */
+ 0 /*false*/, /* use_leading */
+ 0 /*false*/, /* continue_line */
+ { 0, 0 }, /* line_start */
+ { 0, 0 } /* output position */
+};
+/* GC descriptor */
+gs_private_st_ptrs2(st_pdf_text_state, pdf_text_state_t, "pdf_text_state_t",
+ pdf_text_state_enum_ptrs, pdf_text_state_reloc_ptrs,
+ in.pdfont, out.pdfont);
+
+/* ================ Procedures ================ */
+
+/* ---------------- Private ---------------- */
+
+/*
+ * Append a writing-direction movement to the text being accumulated. If
+ * the buffer is full, or the requested movement is not in writing
+ * direction, return <0 and do nothing. (This is different from
+ * pdf_append_chars.) Requires pts->buffer.count_chars > 0.
+ */
+static int
+append_text_move(pdf_text_state_t *pts, double dw)
+{
+ int count = pts->buffer.count_moves;
+ int pos = pts->buffer.count_chars;
+ double rounded;
+
+ if (count > 0 && pts->buffer.moves[count - 1].index == pos) {
+ /* Merge adjacent moves. */
+ dw += pts->buffer.moves[--count].amount;
+ }
+ /* Round dw if it's very close to an integer. */
+ rounded = floor(dw + 0.5);
+ if (fabs(dw - rounded) < 0.001)
+ dw = rounded;
+ if (dw < -MAX_USER_COORD) {
+ /* Acrobat reader 4.0c, 5.0 can't handle big offsets.
+ Adobe Reader 6 can. */
+ return -1;
+ }
+ if (dw != 0) {
+ if (count == MAX_TEXT_BUFFER_MOVES)
+ return -1;
+ pts->buffer.moves[count].index = pos;
+ pts->buffer.moves[count].amount = dw;
+ ++count;
+ }
+ pts->buffer.count_moves = count;
+ return 0;
+}
+
+/*
+ * Set *pdist to the distance (dx,dy), in the space defined by *pmat.
+ */
+static int
+set_text_distance(gs_point *pdist, double dx, double dy, const gs_matrix *pmat)
+{
+ int code = gs_distance_transform_inverse(dx, dy, pmat, pdist);
+ double rounded;
+
+ if (code == gs_error_undefinedresult) {
+ /* The CTM is degenerate.
+ Can't know the distance in user space.
+ Set zero because we believe it is not important for rendering.
+ We want to copy the text to PDF to make it searchable.
+ Bug 689006.
+ */
+ pdist->x = pdist->y = 0;
+ } else if (code < 0)
+ return code;
+ /* If the distance is very close to integers, round it. */
+ if (fabs(pdist->x - (rounded = floor(pdist->x + 0.5))) < 0.0005)
+ pdist->x = rounded;
+ if (fabs(pdist->y - (rounded = floor(pdist->y + 0.5))) < 0.0005)
+ pdist->y = rounded;
+ return 0;
+}
+
+/*
+ * Test whether the transformation parts of two matrices are compatible.
+ */
+static bool
+matrix_is_compatible(const gs_matrix *pmat1, const gs_matrix *pmat2)
+{
+ return (pmat2->xx == pmat1->xx && pmat2->xy == pmat1->xy &&
+ pmat2->yx == pmat1->yx && pmat2->yy == pmat1->yy);
+}
+
+/*
+ * Try to handle a change of text position with TJ or a space
+ * character. If successful, return >=0, if not, return <0.
+ */
+static int
+add_text_delta_move(gx_device_pdf *pdev, const gs_matrix *pmat)
+{
+ pdf_text_state_t *const pts = pdev->text->text_state;
+
+ if (matrix_is_compatible(pmat, &pts->in.matrix)) {
+ double dx = pmat->tx - pts->in.matrix.tx,
+ dy = pmat->ty - pts->in.matrix.ty;
+ gs_point dist;
+ double dw, dnotw, tdw;
+ int code;
+
+ code = set_text_distance(&dist, dx, dy, pmat);
+ if (code < 0)
+ return code;
+ if (pts->wmode)
+ dw = dist.y, dnotw = dist.x;
+ else
+ dw = dist.x, dnotw = dist.y;
+ if (dnotw == 0 && pts->buffer.count_chars > 0 &&
+ /*
+ * Acrobat Reader limits the magnitude of user-space
+ * coordinates. Also, AR apparently doesn't handle large
+ * positive movement values (negative X displacements), even
+ * though the PDF Reference says this bug was fixed in AR3.
+ *
+ * Old revisions used the upper threshold 1000 for tdw,
+ * but it appears too big when a font sets a too big
+ * character width in setcachedevice. Particularly this happens
+ * with a Type 3 font generated by Aldus Freehand 4.0
+ * to represent a texture - see bug #687051.
+ * The problem is that when the Widths is multiplied
+ * to the font size, the viewer represents the result
+ * with insufficient fraction bits to represent the precise width.
+ * We work around that problem here restricting tdw
+ * with a smaller threshold 990. Our intention is to
+ * disable Tj when the real glyph width appears smaller
+ * than 1% of the width specified in setcachedevice.
+ * A Td instruction will be generated instead.
+ * Note that the value 990 is arbitrary and may need a
+ * further adjustment.
+ */
+ /* Revised the above. It seems unreasonable to use a fixed
+ * value which is not based on the point size, when the problem is
+ * caused by a large point size being multiplied by the width. The
+ * original fix also caused bitmap fonts (from PCL and other sources)
+ * to fail to use kerning, as these fonts are scaled to 1 point and
+ * therefore use large kerning values. Instead we check the kerned value
+ * multiplied by the point size of the font.
+ */
+ (tdw = dw * -1000.0 / pts->in.size,
+ tdw >= -MAX_USER_COORD && (tdw * pts->in.size) < MAX_USER_COORD)
+ ) {
+ /* Use TJ. */
+ int code = append_text_move(pts, tdw);
+
+ if (code >= 0)
+ goto finish;
+ }
+ }
+ return -1;
+ finish:
+ pts->in.matrix = *pmat;
+ return 0;
+}
+
+/*
+ * Set the text matrix for writing text. The translation component of the
+ * matrix is the text origin. If the non-translation components of the
+ * matrix differ from the current ones, write a Tm command; if there is only
+ * a Y translation, set use_leading so the next text string will be written
+ * with ' rather than Tj; otherwise, write a Td command.
+ */
+static int
+pdf_set_text_matrix(gx_device_pdf * pdev)
+{
+ pdf_text_state_t *pts = pdev->text->text_state;
+ stream *s = pdev->strm;
+
+ pts->use_leading = false;
+ if (matrix_is_compatible(&pts->out.matrix, &pts->in.matrix)) {
+ gs_point dist;
+ int code;
+
+ code = set_text_distance(&dist, pts->start.x - pts->line_start.x,
+ pts->start.y - pts->line_start.y, &pts->in.matrix);
+ if (code < 0)
+ return code;
+ if (dist.x == 0 && dist.y < 0) {
+ /* Use TL, if needed, and T* or '. */
+ float dist_y = (float)-dist.y;
+
+ if (fabs(pts->leading - dist_y) > 0.0005) {
+ pprintg1(s, "%g TL\n", dist_y);
+ pts->leading = dist_y;
+ }
+ pts->use_leading = true;
+ } else {
+ /* Use Td. */
+ pprintg2(s, "%g %g Td\n", dist.x, dist.y);
+ }
+ } else { /* Use Tm. */
+ /*
+ * See stream_to_text in gdevpdfu.c for why we need the following
+ * matrix adjustments.
+ */
+ double sx = 72.0 / pdev->HWResolution[0],
+ sy = 72.0 / pdev->HWResolution[1];
+
+ pprintg6(s, "%g %g %g %g %g %g Tm\n",
+ pts->in.matrix.xx * sx, pts->in.matrix.xy * sy,
+ pts->in.matrix.yx * sx, pts->in.matrix.yy * sy,
+ pts->start.x * sx, pts->start.y * sy);
+ }
+ pts->line_start.x = pts->start.x;
+ pts->line_start.y = pts->start.y;
+ pts->out.matrix = pts->in.matrix;
+ return 0;
+}
+
+/* ---------------- Public ---------------- */
+
+/*
+ * Allocate and initialize text state bookkeeping.
+ */
+pdf_text_state_t *
+pdf_text_state_alloc(gs_memory_t *mem)
+{
+ pdf_text_state_t *pts =
+ gs_alloc_struct(mem, pdf_text_state_t, &st_pdf_text_state,
+ "pdf_text_state_alloc");
+
+ if (pts == 0)
+ return 0;
+ *pts = ts_default;
+ return pts;
+}
+
+/*
+ * Set the text state to default values.
+ */
+void
+pdf_set_text_state_default(pdf_text_state_t *pts)
+{
+ *pts = ts_default;
+}
+
+/*
+ * Copy the text state.
+ */
+void
+pdf_text_state_copy(pdf_text_state_t *pts_to, pdf_text_state_t *pts_from)
+{
+ *pts_to = *pts_from;
+}
+
+/*
+ * Reset the text state to its condition at the beginning of the page.
+ */
+void
+pdf_reset_text_page(pdf_text_data_t *ptd)
+{
+ pdf_set_text_state_default(ptd->text_state);
+}
+
+/*
+ * Reset the text state after a grestore.
+ */
+void
+pdf_reset_text_state(pdf_text_data_t *ptd)
+{
+ pdf_text_state_t *pts = ptd->text_state;
+
+ pts->out = ts_default.out;
+ pts->leading = 0;
+}
+
+/*
+ * Transition from stream context to text context.
+ */
+int
+pdf_from_stream_to_text(gx_device_pdf *pdev)
+{
+ pdf_text_state_t *pts = pdev->text->text_state;
+
+ gs_make_identity(&pts->out.matrix);
+ pts->line_start.x = pts->line_start.y = 0;
+ pts->continue_line = false; /* Not sure, probably doesn't matter. */
+ pts->buffer.count_chars = 0;
+ pts->buffer.count_moves = 0;
+ return 0;
+}
+
+int
+pdf_get_stoted_text_size(pdf_text_state_t *state)
+{
+ return state->buffer.count_chars;
+}
+
+/*
+ * Flush text from buffer.
+ */
+static int
+flush_text_buffer(gx_device_pdf *pdev)
+{
+ pdf_text_state_t *pts = pdev->text->text_state;
+ stream *s = pdev->strm;
+
+ if (pts->buffer.count_chars != 0) {
+ pdf_font_resource_t *pdfont = pts->in.pdfont;
+ int code = pdf_assign_font_object_id(pdev, pdfont);
+
+ if (code < 0)
+ return code;
+ code = pdf_add_resource(pdev, pdev->substream_Resources, "/Font", (pdf_resource_t *)pdfont);
+ if (code < 0)
+ return code;
+ }
+ if (pts->buffer.count_moves > 0) {
+ int i, cur = 0;
+
+ if (pts->use_leading)
+ stream_puts(s, "T*");
+ stream_puts(s, "[");
+ for (i = 0; i < pts->buffer.count_moves; ++i) {
+ int next = pts->buffer.moves[i].index;
+
+ pdf_put_string(pdev, pts->buffer.chars + cur, next - cur);
+ pprintg1(s, "%g", pts->buffer.moves[i].amount);
+ cur = next;
+ }
+ if (pts->buffer.count_chars > cur)
+ pdf_put_string(pdev, pts->buffer.chars + cur,
+ pts->buffer.count_chars - cur);
+ stream_puts(s, "]TJ\n");
+ } else {
+ pdf_put_string(pdev, pts->buffer.chars, pts->buffer.count_chars);
+ stream_puts(s, (pts->use_leading ? "'\n" : "Tj\n"));
+ }
+ pts->buffer.count_chars = 0;
+ pts->buffer.count_moves = 0;
+ pts->use_leading = false;
+ return 0;
+}
+
+/*
+ * Transition from string context to text context.
+ */
+int
+sync_text_state(gx_device_pdf *pdev)
+{
+ pdf_text_state_t *pts = pdev->text->text_state;
+ stream *s = pdev->strm;
+ int code;
+
+ if (pts->buffer.count_chars == 0)
+ return 0; /* nothing to output */
+
+ if (pts->continue_line)
+ return flush_text_buffer(pdev);
+
+ /* Bring text state parameters up to date. */
+
+ if (pts->out.character_spacing != pts->in.character_spacing) {
+ pprintg1(s, "%g Tc\n", pts->in.character_spacing);
+ pts->out.character_spacing = pts->in.character_spacing;
+ }
+
+ if (pts->out.pdfont != pts->in.pdfont || pts->out.size != pts->in.size) {
+ pdf_font_resource_t *pdfont = pts->in.pdfont;
+
+ code = pdf_assign_font_object_id(pdev, pdfont);
+ if (code < 0)
+ return code;
+ pprints1(s, "/%s ", pdfont->rname);
+ pprintg1(s, "%g Tf\n", pts->in.size);
+ pts->out.pdfont = pdfont;
+ pts->out.size = pts->in.size;
+ /*
+ * In PDF, the only place to specify WMode is in the CMap
+ * (a.k.a. Encoding) of a Type 0 font.
+ */
+ pts->wmode =
+ (pdfont->FontType == ft_composite ?
+ pdfont->u.type0.WMode : 0);
+ code = pdf_used_charproc_resources(pdev, pdfont);
+ if (code < 0)
+ return code;
+ }
+
+ if (gs_matrix_compare(&pts->in.matrix, &pts->out.matrix) ||
+ ((pts->start.x != pts->out_pos.x || pts->start.y != pts->out_pos.y) &&
+ (pts->buffer.count_chars != 0 || pts->buffer.count_moves != 0))) {
+ /* pdf_set_text_matrix sets out.matrix = in.matrix */
+ code = pdf_set_text_matrix(pdev);
+ if (code < 0)
+ return code;
+ }
+
+ if (pts->out.render_mode != pts->in.render_mode) {
+ pprintg1(s, "%g Tr\n", pts->in.render_mode);
+ pts->out.render_mode = pts->in.render_mode;
+ }
+
+ if (pts->out.word_spacing != pts->in.word_spacing) {
+ if (memchr(pts->buffer.chars, 32, pts->buffer.count_chars)) {
+ pprintg1(s, "%g Tw\n", pts->in.word_spacing);
+ pts->out.word_spacing = pts->in.word_spacing;
+ }
+ }
+
+ return flush_text_buffer(pdev);
+}
+
+int
+pdf_from_string_to_text(gx_device_pdf *pdev)
+{
+ return sync_text_state(pdev);
+}
+
+/*
+ * Close the text aspect of the current contents part.
+ */
+void
+pdf_close_text_contents(gx_device_pdf *pdev)
+{
+ /*
+ * Clear the font pointer. This is probably left over from old code,
+ * but it is appropriate in case we ever choose in the future to write
+ * out and free font resources before the end of the document.
+ */
+ pdf_text_state_t *pts = pdev->text->text_state;
+
+ pts->in.pdfont = pts->out.pdfont = 0;
+ pts->in.size = pts->out.size = 0;
+}
+
+/*
+ * Test whether a change in render_mode requires resetting the stroke
+ * parameters.
+ */
+bool
+pdf_render_mode_uses_stroke(const gx_device_pdf *pdev,
+ const pdf_text_state_values_t *ptsv)
+{
+ return ((ptsv->render_mode == 1 || ptsv->render_mode == 2 ||
+ ptsv->render_mode == 5 || ptsv->render_mode == 6));
+}
+
+/*
+ * Read the stored client view of text state values.
+ */
+void
+pdf_get_text_state_values(gx_device_pdf *pdev, pdf_text_state_values_t *ptsv)
+{
+ *ptsv = pdev->text->text_state->in;
+}
+
+/*
+ * Set wmode to text state.
+ */
+void
+pdf_set_text_wmode(gx_device_pdf *pdev, int wmode)
+{
+ pdf_text_state_t *pts = pdev->text->text_state;
+
+ pts->wmode = wmode;
+}
+
+/*
+ * Set the stored client view of text state values.
+ */
+int
+pdf_set_text_state_values(gx_device_pdf *pdev,
+ const pdf_text_state_values_t *ptsv)
+{
+ pdf_text_state_t *pts = pdev->text->text_state;
+
+ if (pts->buffer.count_chars > 0) {
+ int code;
+
+ if (pts->in.character_spacing == ptsv->character_spacing &&
+ pts->in.pdfont == ptsv->pdfont && pts->in.size == ptsv->size &&
+ pts->in.render_mode == ptsv->render_mode &&
+ pts->in.word_spacing == ptsv->word_spacing
+ ) {
+ if (!gs_matrix_compare(&pts->in.matrix, &ptsv->matrix))
+ return 0;
+ /* add_text_delta_move sets pts->in.matrix if successful */
+ code = add_text_delta_move(pdev, &ptsv->matrix);
+ if (code >= 0)
+ return 0;
+ }
+ code = sync_text_state(pdev);
+ if (code < 0)
+ return code;
+ }
+
+ pts->in = *ptsv;
+ pts->continue_line = false;
+ return 0;
+}
+
+/*
+ * Transform a distance from unscaled text space (text space ignoring the
+ * scaling implied by the font size) to device space.
+ */
+int
+pdf_text_distance_transform(double wx, double wy, const pdf_text_state_t *pts,
+ gs_point *ppt)
+{
+ return gs_distance_transform(wx, wy, &pts->in.matrix, ppt);
+}
+
+/*
+ * Return the current (x,y) text position as seen by the client, in
+ * unscaled text space.
+ */
+void
+pdf_text_position(const gx_device_pdf *pdev, gs_point *ppt)
+{
+ pdf_text_state_t *pts = pdev->text->text_state;
+
+ ppt->x = pts->in.matrix.tx;
+ ppt->y = pts->in.matrix.ty;
+}
+
+int pdf_bitmap_char_update_bbox(gx_device_pdf * pdev,int x_offset, int y_offset, double x, double y)
+{
+ pdf_text_state_t *pts = pdev->text->text_state;
+ gs_rect bbox;
+
+ bbox.p.x = (pts->in.matrix.tx + x_offset) / (pdev->HWResolution[0] / 72);
+ bbox.p.y = (pts->in.matrix.ty + y_offset) / (pdev->HWResolution[1] / 72);
+ bbox.q.x = bbox.p.x + (x / (pdev->HWResolution[0] / 72));
+ bbox.q.y = bbox.p.y + (y / (pdev->HWResolution[0] / 72));
+
+ if (bbox.p.x < pdev->BBox.p.x)
+ pdev->BBox.p.x = bbox.p.x;
+ if (bbox.p.y < pdev->BBox.p.y)
+ pdev->BBox.p.y = bbox.p.y;
+ if (bbox.q.x > pdev->BBox.q.x)
+ pdev->BBox.q.x = bbox.q.x;
+ if (bbox.q.y > pdev->BBox.q.y)
+ pdev->BBox.q.y = bbox.q.y;
+
+ return 0;
+}
+/*
+ * Append characters to text being accumulated, giving their advance width
+ * in device space.
+ */
+int
+pdf_append_chars(gx_device_pdf * pdev, const byte * str, uint size,
+ double wx, double wy, bool nobreak)
+{
+ pdf_text_state_t *pts = pdev->text->text_state;
+ const byte *p = str;
+ uint left = size;
+
+ if (pts->buffer.count_chars == 0 && pts->buffer.count_moves == 0) {
+ pts->out_pos.x = pts->start.x = pts->in.matrix.tx;
+ pts->out_pos.y = pts->start.y = pts->in.matrix.ty;
+ }
+ while (left)
+ if (pts->buffer.count_chars == MAX_TEXT_BUFFER_CHARS ||
+ (nobreak && pts->buffer.count_chars + left > MAX_TEXT_BUFFER_CHARS)) {
+ int code = sync_text_state(pdev);
+
+ if (code < 0)
+ return code;
+ /* We'll keep a continuation of this line in the buffer,
+ * but the current input parameters don't correspond to
+ * the current position, because the text was broken in a
+ * middle with unknown current point.
+ * Don't change the output text state parameters
+ * until input parameters are changed.
+ * pdf_set_text_state_values will reset the 'continue_line' flag
+ * at that time.
+ */
+ pts->continue_line = true;
+ } else {
+ int code = pdf_open_page(pdev, PDF_IN_STRING);
+ uint copy;
+
+ if (code < 0)
+ return code;
+ copy = min(MAX_TEXT_BUFFER_CHARS - pts->buffer.count_chars, left);
+ memcpy(pts->buffer.chars + pts->buffer.count_chars, p, copy);
+ pts->buffer.count_chars += copy;
+ p += copy;
+ left -= copy;
+ }
+ pts->in.matrix.tx += wx;
+ pts->in.matrix.ty += wy;
+ pts->out_pos.x += wx;
+ pts->out_pos.y += wy;
+ return 0;
+}
+
+/* Check a new piece of charpath text to see if its safe to combine
+ * with a previous text operation using text rendering modes.
+ */
+bool pdf_compare_text_state_for_charpath(pdf_text_state_t *pts, gx_device_pdf *pdev,
+ gs_imager_state *pis, gs_font *font,
+ const gs_text_params_t *text)
+{
+ int code;
+ float size;
+ gs_matrix smat, tmat;
+ struct pdf_font_resource_s *pdfont;
+
+ /* check to ensure the new text has the same length as the saved text */
+ if(text->size != pts->buffer.count_chars)
+ return(false);
+
+ if(font->FontType == ft_user_defined ||
+ font->FontType == ft_PCL_user_defined ||
+ font->FontType == ft_MicroType ||
+ font->FontType == ft_GL2_stick_user_defined ||
+ font->FontType == ft_GL2_531)
+ return(false);
+
+ /* check to ensure the new text has the same data as the saved text */
+ if(memcmp(text->data.bytes, &pts->buffer.chars, text->size))
+ return(false);
+
+ /* See if the same font is in use by checking the attahced pdfont resource for
+ * the currrent font and comparing with the saved text state
+ */
+ code = pdf_attached_font_resource(pdev, font, &pdfont, NULL, NULL, NULL, NULL);
+ if(code < 0)
+ return(false);
+
+ if(!pdfont || pdfont != pts->in.pdfont)
+ return(false);
+
+ /* Check to see the new text starts at the same point as the saved text.
+ * NB! only check 2 decimal places, allow some slack in the match. This
+ * still may prove to be too tight a requirement.
+ */
+ if(fabs(pts->start.x - pis->current_point.x) > 0.01 ||
+ fabs(pts->start.y - pis->current_point.y) > 0.01)
+ return(false);
+
+ size = pdf_calculate_text_size(pis, pdfont, &font->FontMatrix, &smat, &tmat, font, pdev);
+
+ /* Finally, check the calculated size against the size stored in
+ * the text state.
+ */
+ if(size != pts->in.size)
+ return(false);
+
+ return(true);
+}
+
+int pdf_get_text_render_mode(pdf_text_state_t *pts)
+{
+ return(pts->in.render_mode);
+}
+
+void pdf_set_text_render_mode(pdf_text_state_t *pts, int mode)
+{
+ pts->in.render_mode = mode;
+}
+
+/* Add a render mode to the rendering mode of the current text.
+ * mode 0 = fill
+ * mode 1 = stroke
+ * mode 2 = clip
+ * If the modes are not compatible returns 0. NB currently only
+ * a stroke rendering mode is supported.
+ */
+int pdf_modify_text_render_mode(pdf_text_state_t *pts, int render_mode)
+{
+ switch (pts->in.render_mode) {
+ case 0:
+ if (render_mode == 1) {
+ pts->in.render_mode = 2;
+ return(1);
+ }
+ break;
+ case 1:
+ if (render_mode == 1)
+ return(1);
+ break;
+ case 2:
+ if (render_mode == 1)
+ return(1);
+ break;
+ case 3:
+ if (render_mode == 1) {
+ pts->in.render_mode = 1;
+ return(1);
+ }
+ break;
+ case 4:
+ if (render_mode == 1) {
+ pts->in.render_mode = 6;
+ return(1);
+ }
+ break;
+ case 5:
+ if (render_mode == 1)
+ return(1);
+ break;
+ case 6:
+ if (render_mode == 1)
+ return(1);
+ break;
+ case 7:
+ if (render_mode == 1) {
+ pts->in.render_mode = 5;
+ return(1);
+ }
+ break;
+ default:
+ break;
+ }
+ return(0);
+}
+
+int pdf_set_PaintType0_params (gx_device_pdf *pdev, gs_imager_state *pis, float size,
+ double scaled_width, const pdf_text_state_values_t *ptsv)
+{
+ pdf_text_state_t *pts = pdev->text->text_state;
+ double saved_width = pis->line_params.half_width;
+ int code;
+
+ /* This routine is used to check if we have accumulated glyphs waiting for output
+ * if we do, and we are using a PaintType 0 font (stroke), which is the only way we
+ * can get here, then we check to see if the stroke width has changed. If so we want to
+ * flush the buffer, and set the new stroke width. This produces:
+ * <width> w
+ * (text) Tj
+ * <new width> w
+ * (new text) Tj
+ *
+ * instead of :
+ * <width> w
+ * <new width> w
+ * (text) Tj
+ * (new text) Tj
+ */
+ if (pts->buffer.count_chars > 0) {
+ if (pts->PaintType0Width != scaled_width) {
+ pis->line_params.half_width = scaled_width / 2;
+ code = pdf_set_text_state_values(pdev, ptsv);
+ if (code < 0)
+ return code;
+ if (pdev->text->text_state->in.render_mode == ptsv->render_mode){
+ code = pdf_prepare_stroke(pdev, pis);
+ if (code >= 0)
+ code = gdev_vector_prepare_stroke((gx_device_vector *)pdev,
+ pis, NULL, NULL, 1);
+ }
+ if (code < 0)
+ return code;
+ pis->line_params.half_width = saved_width;
+ pts->PaintType0Width = scaled_width;
+ }
+ }
+ return 0;
+}
diff --git a/devices/vector/gdevpdts.h b/devices/vector/gdevpdts.h
new file mode 100644
index 000000000..fdae7a353
--- /dev/null
+++ b/devices/vector/gdevpdts.h
@@ -0,0 +1,141 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Text state structure and API for pdfwrite */
+
+#ifndef gdevpdts_INCLUDED
+# define gdevpdts_INCLUDED
+
+#include "gsmatrix.h"
+
+/*
+ * See gdevpdtt.h for a discussion of the multiple coordinate systems that
+ * the text code must use.
+ */
+
+/* ================ Types and structures ================ */
+
+#ifndef pdf_text_state_DEFINED
+# define pdf_text_state_DEFINED
+typedef struct pdf_text_state_s pdf_text_state_t;
+#endif
+
+/*
+ * Clients pass in the text state values; the implementation decides when
+ * (and, in the case of text positioning, how) to emit PDF commands to
+ * set them in the output.
+ */
+typedef struct pdf_text_state_values_s {
+ float character_spacing; /* Tc */
+ pdf_font_resource_t *pdfont; /* for Tf */
+ double size; /* for Tf */
+ /*
+ * The matrix is the transformation from text space to user space, which
+ * in pdfwrite text output is the same as device space. Thus this
+ * matrix combines the effect of the PostScript CTM and the FontMatrix,
+ * scaled by the inverse of the font size value.
+ */
+ gs_matrix matrix; /* Tm et al */
+ int render_mode; /* Tr */
+ float word_spacing; /* Tw */
+} pdf_text_state_values_t;
+#define TEXT_STATE_VALUES_DEFAULT\
+ 0, /* character_spacing */\
+ NULL, /* font */\
+ 0, /* size */\
+ { identity_matrix_body }, /* matrix */\
+ 0, /* render_mode */\
+ 0 /* word_spacing */
+
+/* ================ Procedures ================ */
+
+/* ------ Exported for gdevpdfu.c ------ */
+
+/*
+ * Transition from stream context to text context.
+ */
+int pdf_from_stream_to_text(gx_device_pdf *pdev);
+
+/*
+ * Transition from string context to text context.
+ */
+int pdf_from_string_to_text(gx_device_pdf *pdev);
+
+/*
+ * Close the text aspect of the current contents part.
+ */
+void pdf_close_text_contents(gx_device_pdf *pdev); /* gdevpdts.h */
+
+/* ------ Used only within the text code ------ */
+
+/*
+ * Test whether a change in render_mode requires resetting the stroke
+ * parameters.
+ */
+bool pdf_render_mode_uses_stroke(const gx_device_pdf *pdev,
+ const pdf_text_state_values_t *ptsv);
+
+/*
+ * Read the stored client view of text state values.
+ */
+void pdf_get_text_state_values(gx_device_pdf *pdev,
+ pdf_text_state_values_t *ptsv);
+
+/*
+ * Set wmode to text state.
+ */
+void pdf_set_text_wmode(gx_device_pdf *pdev, int wmode);
+
+/*
+ * Set the stored client view of text state values.
+ */
+int pdf_set_text_state_values(gx_device_pdf *pdev,
+ const pdf_text_state_values_t *ptsv);
+
+/*
+ * Transform a distance from unscaled text space (text space ignoring the
+ * scaling implied by the font size) to device space.
+ */
+int pdf_text_distance_transform(double wx, double wy,
+ const pdf_text_state_t *pts,
+ gs_point *ppt);
+
+/*
+ * Return the current (x,y) text position as seen by the client, in
+ * unscaled text space.
+ */
+void pdf_text_position(const gx_device_pdf *pdev, gs_point *ppt);
+
+int pdf_bitmap_char_update_bbox(gx_device_pdf * pdev,int x_offset, int y_offset, double x, double y);
+/*
+ * Append characters to text being accumulated, giving their advance width
+ * in device space.
+ */
+int pdf_append_chars(gx_device_pdf * pdev, const byte * str, uint size,
+ double wx, double wy, bool nobreak);
+
+bool pdf_compare_text_state_for_charpath(pdf_text_state_t *pts, gx_device_pdf *pdev,
+ gs_imager_state *pis, gs_font *font,
+ const gs_text_params_t *text);
+
+int pdf_get_text_render_mode(pdf_text_state_t *pts);
+void pdf_set_text_render_mode(pdf_text_state_t *pts, int mode);
+int pdf_modify_text_render_mode(pdf_text_state_t *pts, int render_mode);
+int pdf_set_PaintType0_params (gx_device_pdf *pdev, gs_imager_state *pis,
+ float size, double scaled_width,
+ const pdf_text_state_values_t *ptsv);
+int sync_text_state(gx_device_pdf *pdev);
+#endif /* gdevpdts_INCLUDED */
diff --git a/devices/vector/gdevpdtt.c b/devices/vector/gdevpdtt.c
new file mode 100644
index 000000000..b6c4da6cf
--- /dev/null
+++ b/devices/vector/gdevpdtt.c
@@ -0,0 +1,3561 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Text processing for pdfwrite. */
+#include "math_.h"
+#include "string_.h"
+#include <stdlib.h> /* abs() */
+#include "gx.h"
+#include "gserrors.h"
+#include "gscencs.h"
+#include "gscedata.h"
+#include "gsmatrix.h"
+#include "gzstate.h"
+#include "gxfcache.h" /* for orig_fonts list */
+#include "gxfont.h"
+#include "gxfont0.h"
+#include "gxfcid.h"
+#include "gxfcopy.h"
+#include "gxfcmap.h"
+#include "gxpath.h" /* for getting current point */
+#include "gxchar.h"
+#include "gxstate.h"
+#include "gdevpdfx.h"
+#include "gdevpdfg.h"
+#include "gdevpdfo.h"
+#include "gdevpdtx.h"
+#include "gdevpdtd.h"
+#include "gdevpdtf.h"
+#include "gdevpdts.h"
+#include "gdevpdtt.h"
+#include "gdevpdti.h"
+#include "gxhldevc.h"
+#include "gzpath.h"
+
+/* ================ Text enumerator ================ */
+
+/* GC descriptor */
+private_st_pdf_text_enum();
+
+/* A static data for pdf_reserve_char_code_in_pdfont.
+ No, this isn't a reenterability error,
+ because all threads will set same value,
+ which actually depends on static data only. */
+static gs_glyph standard_glyph_code_for_notdef = GS_NO_GLYPH;
+
+/* Define the auxiliary procedures for text processing. */
+static int
+pdf_text_resync(gs_text_enum_t *pte, const gs_text_enum_t *pfrom)
+{
+ pdf_text_enum_t *const penum = (pdf_text_enum_t *)pte;
+
+ if ((pte->text.operation ^ pfrom->text.operation) & ~TEXT_FROM_ANY)
+ return_error(gs_error_rangecheck);
+ if (penum->pte_default) {
+ int code = gs_text_resync(penum->pte_default, pfrom);
+
+ if (code < 0)
+ return code;
+ }
+ pte->text = pfrom->text;
+ gs_text_enum_copy_dynamic(pte, pfrom, false);
+ return 0;
+}
+static bool
+pdf_text_is_width_only(const gs_text_enum_t *pte)
+{
+ const pdf_text_enum_t *const penum = (const pdf_text_enum_t *)pte;
+
+ if (penum->pte_default)
+ return gs_text_is_width_only(penum->pte_default);
+ return false;
+}
+static int
+pdf_text_current_width(const gs_text_enum_t *pte, gs_point *pwidth)
+{
+ const pdf_text_enum_t *const penum = (const pdf_text_enum_t *)pte;
+
+ if (penum->pte_default)
+ return gs_text_current_width(penum->pte_default, pwidth);
+ return_error(gs_error_rangecheck); /* can't happen */
+}
+static int
+pdf_text_set_cache(gs_text_enum_t *pte, const double *pw,
+ gs_text_cache_control_t control)
+{
+ pdf_text_enum_t *const penum = (pdf_text_enum_t *)pte;
+ gs_text_enum_t *pgste;
+ gx_device_pdf *pdev = (gx_device_pdf *)pte->dev;
+ gs_matrix m;
+
+ if (pdev->type3charpath)
+ return gs_text_set_cache(penum->pte_default, pw, control);
+
+ switch (control) {
+ case TEXT_SET_CHAR_WIDTH:
+ case TEXT_SET_CACHE_DEVICE:
+ /* See comments in pdf_text_process. We are using a 100x100 matrix
+ * NOT the identity, but we want the cache device values to be in
+ * font co-ordinate space, so we need to undo that scale here.
+ */
+ if (pdev->PS_accumulator){
+ gs_matrix_scale(&ctm_only(pte->pis), .01, .01, &m);
+ gs_distance_transform(pw[0], pw[1], &m, &pdev->char_width);
+ } else {
+ pdev->char_width.x = pw[0];
+ pdev->char_width.y = pw[1];
+ }
+ break;
+ case TEXT_SET_CACHE_DEVICE2:
+ /*
+ * pdev->char_width is used with synthesized Type 3 fonts only.
+ * Since they are simple fonts, we only need the horisontal
+ * width for Widths array. Therefore we don't check
+ * gs_rootfont(pgs)->WMode and don't use pw[6:7].
+ */
+ /* See comments in pdf_text_process. We are using a 100x100 matrix
+ * NOT the identity, but we want the cache device values to be in
+ * font co-ordinate space, so we need to undo that scale here.
+ */
+ if (pdev->PS_accumulator){
+ gs_matrix_scale(&ctm_only(pte->pis), .01, .01, &m);
+ gs_distance_transform(pw[0], pw[1], &m, &pdev->char_width);
+ } else {
+ pdev->char_width.x = pw[0];
+ pdev->char_width.y = pw[1];
+ }
+ if (penum->cdevproc_callout) {
+ memcpy(penum->cdevproc_result, pw, sizeof(penum->cdevproc_result));
+ return 0;
+ }
+ break;
+ default:
+ return_error(gs_error_rangecheck);
+ }
+ if (!pdev->PS_accumulator)
+ pgste = (gs_text_enum_t *)penum;
+ else
+ pgste = penum->pte_default;
+
+ if ((penum->current_font->FontType == ft_user_defined ||
+ penum->current_font->FontType == ft_PCL_user_defined ||
+ penum->current_font->FontType == ft_MicroType ||
+ penum->current_font->FontType == ft_GL2_stick_user_defined ||
+ penum->current_font->FontType == ft_GL2_531) &&
+ penum->outer_CID == GS_NO_GLYPH &&
+ !(pgste->text.operation & TEXT_DO_CHARWIDTH)) {
+ int code;
+ gs_glyph glyph;
+
+ glyph = penum->returned.current_glyph;
+ if ((glyph != GS_NO_GLYPH && penum->output_char_code != GS_NO_CHAR) || !pdev->PS_accumulator) {
+ gs_show_enum *penum_s;
+ extern_st(st_gs_show_enum);
+ gs_fixed_rect clip_box;
+ double pw1[10];
+ int narg = (control == TEXT_SET_CHAR_WIDTH ? 2 :
+ control == TEXT_SET_CACHE_DEVICE ? 6 : 10), i;
+
+ /* Check to verify the structure type is really gs_show_enum */
+ if (gs_object_type(pgste->memory, pgste) != &st_gs_show_enum) {
+ /* Must not happen with PS interpreter.
+ Other clients should conform. */
+ return_error(gs_error_unregistered);
+ }
+ penum_s = (gs_show_enum *)pgste;
+ /* BuildChar could change the scale before calling setcachedevice (Bug 687290).
+ We must scale the setcachedevice arguments because we assumed
+ identity scale before entering the charproc.
+ For now we only handle scaling matrices.
+ */
+ for (i = 0; i < narg; i += 2) {
+ gs_point p;
+
+ gs_point_transform(pw[i], pw[i + 1], &ctm_only(penum_s->pgs), &p);
+ pw1[i] = p.x;
+ pw1[i + 1] = p.y;
+ }
+ if (control != TEXT_SET_CHAR_WIDTH) {
+ clip_box.p.x = float2fixed(pw1[2]);
+ clip_box.p.y = float2fixed(pw1[3]);
+ clip_box.q.x = float2fixed(pw1[4]);
+ clip_box.q.y = float2fixed(pw1[5]);
+ } else {
+ /*
+ * We have no character bbox, but we need one to install the clipping
+ * to the graphic state of the PS interpreter. Since some fonts don't
+ * provide a proper FontBBox (Bug 687239 supplies a zero one),
+ * we set an "infinite" clipping here.
+ * We also detected that min_int, max_int don't work here with
+ * comparefiles/Bug687044.ps, therefore we divide them by 2.
+ */
+ clip_box.p.x = clip_box.p.y = min_int / 2;
+ clip_box.q.x = clip_box.q.y = max_int / 2;
+ }
+ code = gx_clip_to_rectangle(penum_s->pgs, &clip_box);
+ if (code < 0)
+ return code;
+
+ /* See comments in pdf_text_process. We are using a 100x100 matrix
+ * NOT the identity, but we want the cache device values to be in
+ * font co-ordinate space, so we need to undo that scale here. We
+ * can't do it above, where we take any scaling from the BuildChar
+ * into account, because that would get the clip path wrong, that
+ * needs to be in the 100x100 space so that it doesn't clip
+ * out marking operations.
+ */
+ if (pdev->PS_accumulator)
+ gs_matrix_scale(&ctm_only(penum_s->pgs), .01, .01, &m);
+ else
+ m = ctm_only(penum_s->pgs);
+ for (i = 0; i < narg; i += 2) {
+ gs_point p;
+
+ gs_point_transform(pw[i], pw[i + 1], &m, &p);
+ pw1[i] = p.x;
+ pw1[i + 1] = p.y;
+ }
+ if (!pdev->PS_accumulator)
+ code = pdf_set_charproc_attrs(pdev, pte->current_font,
+ pw1, narg, control, penum->returned.current_char, false);
+ else
+ code = pdf_set_charproc_attrs(pdev, pte->current_font,
+ pw1, narg, control, penum->output_char_code, true);
+ if (code < 0)
+ return code;
+ /* Prevent writing the clipping path to charproc.
+ See the comment above and bugs 687678, 688327.
+ Note that the clipping in the graphic state will be used while
+ fallbacks to default implementations of graphic objects.
+ Hopely such fallbacks are rare. */
+ pdev->clip_path_id = gx_get_clip_path_id(penum_s->pgs);
+ return code;
+ } else {
+ gs_matrix m;
+ pdf_resource_t *pres = pdev->accumulating_substream_resource;
+
+ /* pdf_text_process started a charproc stream accumulation,
+ but now we re-decided to go with the default implementation.
+ Cancel the stream now.
+ */
+ code = pdf_exit_substream(pdev);
+ if (code < 0)
+ return code;
+ code = pdf_cancel_resource(pdev, pres, resourceCharProc);
+ if (code < 0)
+ return code;
+ pdf_forget_resource(pdev, pres, resourceCharProc);
+ /* pdf_text_process had set an identity CTM for the
+ charproc stream accumulation, but now we re-decided
+ to go with the default implementation.
+ Need to restore the correct CTM and add
+ changes, which the charproc possibly did. */
+ /* See comments in pdf_text_process. We are using a 100x100 matrix
+ * NOT the identity, so we need to undo that scale here.
+ */
+ gs_matrix_scale(&ctm_only(penum->pis), .01, .01, (gs_matrix *)&ctm_only(penum->pis));
+ /* We also scaled the page height and width. Because we
+ * don't go through the accumulator 'close' in pdf_text_process
+ * we must also undo that scale.
+ */
+ pdev->width /= 100;
+ pdev->height /= 100;
+
+ gs_matrix_multiply((gs_matrix *)&pdev->charproc_ctm, (gs_matrix *)&penum->pis->ctm, &m);
+ gs_matrix_fixed_from_matrix(&penum->pis->ctm, &m);
+ penum->charproc_accum = false;
+ pdev->accumulating_charproc = false;
+ }
+ }
+ if (pdev->PS_accumulator && penum->pte_default) {
+ if (penum->pte_default->text.operation & TEXT_DO_CHARWIDTH /* See process_cmap_text.*/)
+ return gs_text_set_cache(penum->pte_default, pw, TEXT_SET_CHAR_WIDTH);
+ else
+ return gs_text_set_cache(penum->pte_default, pw, control);
+ }
+ return_error(gs_error_unregistered); /* can't happen */
+}
+
+static int
+pdf_text_retry(gs_text_enum_t *pte)
+{
+ pdf_text_enum_t *const penum = (pdf_text_enum_t *)pte;
+
+ if (penum->pte_default)
+ return gs_text_retry(penum->pte_default);
+ return_error(gs_error_rangecheck); /* can't happen */
+}
+static void
+pdf_text_release(gs_text_enum_t *pte, client_name_t cname)
+{
+ pdf_text_enum_t *const penum = (pdf_text_enum_t *)pte;
+
+ if (penum->pte_default) {
+ gs_text_release(penum->pte_default, cname);
+ penum->pte_default = 0;
+ }
+ pdf_text_release_cgp(penum);
+ gx_default_text_release(pte, cname);
+}
+void
+pdf_text_release_cgp(pdf_text_enum_t *penum)
+{
+ if (penum->cgp) {
+ gs_free_object(penum->memory, penum->cgp, "pdf_text_release");
+ penum->cgp = 0;
+ }
+}
+
+/* Begin processing text. */
+static text_enum_proc_process(pdf_text_process);
+static const gs_text_enum_procs_t pdf_text_procs = {
+ pdf_text_resync, pdf_text_process,
+ pdf_text_is_width_only, pdf_text_current_width,
+ pdf_text_set_cache, pdf_text_retry,
+ pdf_text_release
+};
+
+/* Ideally we would set the stroke and fill colours in pdf_prepare_text_drawing
+ * but this is called from pdf_process_text, not pdf_begin_text. The problem is
+ * that if we get a pattern colour we need to exit to the interpreter and run
+ * the PaintProc, we do this by retunring e_ReampColor. But the 'process' routines
+ * aren't set up to accept this, and just throw an error. Trying to rework the
+ * interpreter routines began turning into a gigantic task, so I chose instead
+ * to 'set' the colours here, which is called from text_begin, where the code
+ * allows us to return e_RemapColor. Attempting to write the colour to the PDF file
+ * in this routine as well caused trouble keeping the graphics states synchronised,
+ * so this functionality was left in pdf_prepare_text_drawing.
+ */
+static int
+pdf_prepare_text_color(gx_device_pdf *const pdev, gs_imager_state *pis, const gs_text_params_t *text, gs_font *font)
+{
+ int code=0;
+ if (text->operation & TEXT_DO_DRAW) {
+ gs_state *pgs = (gs_state *)pis;
+ if (!pdev->ForOPDFRead) {
+ if (pis->text_rendering_mode != 3 && pis->text_rendering_mode != 7) {
+ if (font->PaintType == 2) {
+ /* Bit awkward, if the PaintType is 2 then we want to set the
+ * current ie 'fill' colour, but as a stroke colour because we
+ * will later change the text rendering mode to 1 (stroke).
+ */
+ code = gx_set_dev_color(pgs);
+ if (code != 0)
+ return code;
+ code = pdf_set_drawing_color(pdev, pis, pgs->color[0].dev_color, &pdev->saved_stroke_color,
+ &pdev->stroke_used_process_color,
+ &psdf_set_stroke_color_commands);
+ if (code < 0)
+ return code;
+ } else {
+ if ((pis->text_rendering_mode == 0 || pis->text_rendering_mode == 2 ||
+ pis->text_rendering_mode == 4 || pis->text_rendering_mode == 6) &&
+ !pdev->remap_stroke_color) {
+ code = gx_set_dev_color(pgs);
+ if (code != 0)
+ return code;
+ }
+ if (pis->text_rendering_mode == 1 || pis->text_rendering_mode == 2 ||
+ pis->text_rendering_mode == 5 || pis->text_rendering_mode == 6) {
+ if (!pdev->remap_fill_color) {
+ if (pdev->remap_stroke_color) {
+ pdev->remap_stroke_color = false;
+ } else {
+ gs_swapcolors_quick(pgs);
+ code = gx_set_dev_color(pgs);
+ if (code == gs_error_Remap_Color)
+ pdev->remap_stroke_color = true;
+ if (code != 0)
+ return code;
+ }
+ } else
+ pdev->remap_fill_color = false;
+ gs_swapcolors_quick(pgs);
+ code = gx_set_dev_color(pgs);
+ if (code == gs_error_Remap_Color)
+ pdev->remap_fill_color = true;
+ if (code != 0)
+ return code;
+ }
+ }
+ }
+ }
+ }
+ return code;
+}
+
+static int
+pdf_prepare_text_drawing(gx_device_pdf *const pdev, gs_text_enum_t *pte)
+{
+ gs_imager_state * pis = pte->pis;
+ gs_state *pgs = (gs_state *)pis;
+ const gx_clip_path * pcpath = pte->pcpath;
+ const gs_text_params_t *text = &pte->text;
+ bool new_clip = false; /* Quiet compiler. */
+ int code;
+ gs_font *font = pte->current_font;
+
+ if (!(text->operation & TEXT_DO_NONE) || pis->text_rendering_mode == 3) {
+ new_clip = pdf_must_put_clip_path(pdev, pcpath);
+ if (new_clip)
+ code = pdf_unclip(pdev);
+ else if (pdev->context == PDF_IN_NONE)
+ code = pdf_open_page(pdev, PDF_IN_STREAM);
+ else
+ code = 0;
+ if (code < 0)
+ return code;
+ code = pdf_prepare_fill(pdev, pis);
+ if (code < 0)
+ return code;
+ }
+ if (text->operation & TEXT_DO_DRAW) {
+ /*
+ * Set the clipping path and drawing color. We set both the fill
+ * and stroke color, because we don't know whether the fonts will be
+ * filled or stroked, and we can't set a color while we are in text
+ * mode. (This is a consequence of the implementation, not a
+ * limitation of PDF.)
+ */
+
+ if (new_clip) {
+ code = pdf_put_clip_path(pdev, pcpath);
+ if (code < 0)
+ return code;
+ }
+
+ if (!pdev->ForOPDFRead) {
+ if (pis->text_rendering_mode != 3 && pis->text_rendering_mode != 7) {
+ if (font->PaintType == 2) {
+ /* Bit awkward, if the PaintType is 2 then we want to set the
+ * current ie 'fill' colour, but as a stroke colour because we
+ * will later change the text rendering mode to 1 (stroke).
+ */
+ code = gx_set_dev_color(pgs);
+ if (code != 0)
+ return code;
+ code = pdf_set_drawing_color(pdev, pis, pgs->color[0].dev_color, &pdev->saved_stroke_color,
+ &pdev->stroke_used_process_color,
+ &psdf_set_stroke_color_commands);
+ if (code < 0)
+ return code;
+ } else {
+ if (pis->text_rendering_mode == 0 || pis->text_rendering_mode == 2 ||
+ pis->text_rendering_mode == 4 || pis->text_rendering_mode == 6) {
+ code = gx_set_dev_color(pgs);
+ if (code != 0)
+ return code;
+ code = pdf_set_drawing_color(pdev, pis, pgs->color[0].dev_color, &pdev->saved_fill_color,
+ &pdev->fill_used_process_color,
+ &psdf_set_fill_color_commands);
+ if (code < 0)
+ return code;
+ }
+ if (pis->text_rendering_mode == 1 || pis->text_rendering_mode == 2 ||
+ pis->text_rendering_mode == 5 || pis->text_rendering_mode == 6) {
+ gs_swapcolors_quick(pgs);
+ code = gx_set_dev_color(pgs);
+ if (code != 0)
+ return code;
+ code = pdf_set_drawing_color(pdev, pis, pgs->color[0].dev_color, &pdev->saved_stroke_color,
+ &pdev->stroke_used_process_color,
+ &psdf_set_stroke_color_commands);
+ if (code < 0)
+ return code;
+
+ gs_swapcolors_quick(pgs);
+ }
+ }
+ }
+ } else {
+ code = gx_set_dev_color(pgs);
+ if (code != 0)
+ return code;
+
+ if ((code =
+ pdf_set_drawing_color(pdev, pis, pgs->color[0].dev_color, &pdev->saved_stroke_color,
+ &pdev->stroke_used_process_color,
+ &psdf_set_stroke_color_commands)) < 0 ||
+ (code =
+ pdf_set_drawing_color(pdev, pis, pgs->color[0].dev_color, &pdev->saved_fill_color,
+ &pdev->fill_used_process_color,
+ &psdf_set_fill_color_commands)) < 0
+ )
+ return code;
+ }
+ }
+ return 0;
+}
+
+int
+gdev_pdf_text_begin(gx_device * dev, gs_imager_state * pis,
+ const gs_text_params_t *text, gs_font * font,
+ gx_path * path0, const gx_device_color * pdcolor,
+ const gx_clip_path * pcpath,
+ gs_memory_t * mem, gs_text_enum_t ** ppte)
+{
+ gx_device_pdf *const pdev = (gx_device_pdf *)dev;
+ gx_path *path = path0;
+ pdf_text_enum_t *penum;
+ int code, user_defined = 0;
+
+ /* should we "flatten" the font to "normal" marking operations */
+ if (pdev->FlattenFonts) {
+ font->dir->ccache.upper = 0;
+ return gx_default_text_begin(dev, pis, text, font, path, pdcolor,
+ pcpath, mem, ppte);
+ }
+
+ /* Track the dominant text rotation. */
+ {
+ gs_matrix tmat;
+ gs_point p;
+ int i;
+
+ gs_matrix_multiply(&font->FontMatrix, &ctm_only(pis), &tmat);
+ gs_distance_transform(1, 0, &tmat, &p);
+ if (p.x > fabs(p.y))
+ i = 0;
+ else if (p.x < -fabs(p.y))
+ i = 2;
+ else if (p.y > fabs(p.x))
+ i = 1;
+ else if (p.y < -fabs(p.x))
+ i = 3;
+ else
+ i = 4;
+ pdf_current_page(pdev)->text_rotation.counts[i] += text->size;
+ }
+
+ pdev->last_charpath_op = 0;
+ if ((text->operation & TEXT_DO_ANY_CHARPATH) && !path0->first_subpath) {
+ if (pdf_compare_text_state_for_charpath(pdev->text->text_state, pdev, pis, font, text))
+ pdev->last_charpath_op = text->operation & TEXT_DO_ANY_CHARPATH;
+ }
+
+ if(font->FontType == ft_user_defined ||
+ font->FontType == ft_PCL_user_defined ||
+ font->FontType == ft_MicroType ||
+ font->FontType == ft_GL2_stick_user_defined ||
+ font->FontType == ft_GL2_531)
+ user_defined = 1;
+
+ /* We need to know whether any of the glyphs in a string using a composite font
+ * use a descendant font which is a type 3 (user-defined) so that we can properly
+ * skip the caching below.
+ */
+ if(font->FontType == ft_composite && ((gs_font_type0 *)font)->data.FMapType != fmap_CMap) {
+ int font_code;
+ gs_char chr;
+ gs_glyph glyph;
+
+ rc_alloc_struct_1(penum, pdf_text_enum_t, &st_pdf_text_enum, mem,
+ return_error(gs_error_VMerror), "gdev_pdf_text_begin");
+ penum->rc.free = rc_free_text_enum;
+ penum->pte_default = 0;
+ penum->charproc_accum = false;
+ pdev->accumulating_charproc = false;
+ penum->cdevproc_callout = false;
+ penum->returned.total_width.x = penum->returned.total_width.y = 0;
+ penum->cgp = NULL;
+ penum->output_char_code = GS_NO_CHAR;
+ code = gs_text_enum_init((gs_text_enum_t *)penum, &pdf_text_procs,
+ dev, pis, text, font, path, pdcolor, pcpath, mem);
+ if (code < 0) {
+ gs_free_object(mem, penum, "gdev_pdf_text_begin");
+ return code;
+ }
+ do {
+ font_code = penum->orig_font->procs.next_char_glyph
+ ((gs_text_enum_t *)penum, &chr, &glyph);
+ if (font_code == 1){
+ if (penum->fstack.items[penum->fstack.depth].font->FontType == 3) {
+ user_defined = 1;
+ break;
+ }
+ }
+ } while(font_code != 2 && font_code >= 0);
+ gs_text_release((gs_text_enum_t *)penum, "pdf_text_process");
+ }
+
+ if (!user_defined || !(text->operation & TEXT_DO_ANY_CHARPATH)) {
+ if (user_defined &&
+ (text->operation & TEXT_DO_NONE) && (text->operation & TEXT_RETURN_WIDTH)
+ && pis->text_rendering_mode != 3) {
+ /* This is stringwidth, see gx_default_text_begin.
+ * We need to prevent writing characters to PS cache,
+ * otherwise the font converts to bitmaps.
+ * So pass through even with stringwidth.
+ */
+ code = gx_hld_stringwidth_begin(pis, &path);
+ if (code < 0)
+ return code;
+ } else if ((!(text->operation & TEXT_DO_DRAW) && pis->text_rendering_mode != 3)
+ || path == 0 || !path_position_valid(path)
+ || pdev->type3charpath)
+ return gx_default_text_begin(dev, pis, text, font, path, pdcolor,
+ pcpath, mem, ppte);
+ else if (text->operation & TEXT_DO_ANY_CHARPATH)
+ return gx_default_text_begin(dev, pis, text, font, path, pdcolor,
+ pcpath, mem, ppte);
+ }
+
+ if (!pdev->ForOPDFRead) {
+ code = pdf_prepare_text_color(pdev, pis, text, font);
+ if (code != 0)
+ return code;
+ }
+
+ /* Allocate and initialize the enumerator. */
+
+ rc_alloc_struct_1(penum, pdf_text_enum_t, &st_pdf_text_enum, mem,
+ return_error(gs_error_VMerror), "gdev_pdf_text_begin");
+ penum->rc.free = rc_free_text_enum;
+ penum->pte_default = 0;
+ penum->charproc_accum = false;
+ pdev->accumulating_charproc = false;
+ penum->cdevproc_callout = false;
+ penum->returned.total_width.x = penum->returned.total_width.y = 0;
+ penum->cgp = NULL;
+ penum->output_char_code = GS_NO_CHAR;
+ code = gs_text_enum_init((gs_text_enum_t *)penum, &pdf_text_procs,
+ dev, pis, text, font, path, pdcolor, pcpath, mem);
+ if (code < 0) {
+ gs_free_object(mem, penum, "gdev_pdf_text_begin");
+ return code;
+ }
+ if (pdev->font3 != 0) {
+ /* A text operation happens while accumulating a charproc.
+ This is a case when source document uses a Type 3 font,
+ which's charproc uses another font.
+ Since the text operation is handled by the device,
+ the font isn't converting to a raster (i.e. to a bitmap font).
+ Disable the grid fitting for the convertion to get a proper outlines,
+ because the viewer resolution is not known during the accumulation.
+ Note we set identity CTM in pdf_text_set_cache for the accumilation,
+ and therefore the font may look too small while the source charproc
+ interpretation. The document tpc2.ps of the bug 687087 is an example.
+ */
+ penum->device_disabled_grid_fitting = true;
+ }
+
+ *ppte = (gs_text_enum_t *)penum;
+
+ return 0;
+}
+
+/* ================ Font cache element ================ */
+
+/* GC descriptor */
+private_st_pdf_font_cache_elem();
+
+/*
+ * Compute id for a font cache element.
+ */
+static ulong
+pdf_font_cache_elem_id(gs_font *font)
+{
+ return font->id;
+}
+
+pdf_font_cache_elem_t **
+pdf_locate_font_cache_elem(gx_device_pdf *pdev, gs_font *font)
+{
+ pdf_font_cache_elem_t **e = &pdev->font_cache;
+ long id = pdf_font_cache_elem_id(font);
+
+ for (; *e != 0; e = &(*e)->next)
+ if ((*e)->font_id == id) {
+ return e;
+ }
+ return 0;
+}
+
+static void
+pdf_remove_font_cache_elem(gx_device_pdf *pdev, pdf_font_cache_elem_t *e0)
+{
+ pdf_font_cache_elem_t **e = &pdev->font_cache;
+
+ for (; *e != 0; e = &(*e)->next)
+ if (*e == e0) {
+ *e = e0->next;
+ gs_free_object(pdev->pdf_memory, e0->glyph_usage,
+ "pdf_remove_font_cache_elem");
+ gs_free_object(pdev->pdf_memory, e0->real_widths,
+ "pdf_remove_font_cache_elem");
+ /* Clean pointers, because gs_free_object below may work idle
+ when this function is called by a garbager notification.
+ Leaving unclean pointers may cause
+ a further heap validation to fail
+ since the unfreed structure points to freed areas.
+ For instance, e0->next may point to an element,
+ which is really freed with a subsequent 'restore'.
+ Bug 688837. */
+ e0->next = 0;
+ e0->glyph_usage = 0;
+ e0->real_widths = 0;
+ gs_free_object(pdev->pdf_memory, e0,
+ "pdf_remove_font_cache_elem");
+ return;
+ }
+}
+
+static void
+font_cache_elem_array_sizes(gx_device_pdf *pdev, gs_font *font,
+ int *num_widths, int *num_chars)
+{
+ switch (font->FontType) {
+ case ft_composite:
+ *num_widths = 0; /* Unused for Type 0 */
+ *num_chars = 65536; /* No chance to determine, use max. */
+ break;
+ case ft_encrypted:
+ case ft_encrypted2:
+ case ft_user_defined:
+ case ft_GL2_stick_user_defined:
+ case ft_PCL_user_defined:
+ case ft_MicroType:
+ case ft_GL2_531:
+ case ft_disk_based:
+ case ft_Chameleon:
+ case ft_TrueType:
+ *num_widths = *num_chars = 256; /* Assuming access to glyph_usage by character codes */
+ break;
+ case ft_CID_encrypted:
+ *num_widths = *num_chars = ((gs_font_cid0 *)font)->cidata.common.MaxCID + 1;
+ break;
+ case ft_CID_TrueType:
+ *num_widths = *num_chars = ((gs_font_cid2 *)font)->cidata.common.CIDCount;
+ break;
+ default:
+ *num_widths = *num_chars = 65536; /* No chance to determine, use max. */
+ }
+}
+
+static int
+alloc_font_cache_elem_arrays(gx_device_pdf *pdev, pdf_font_cache_elem_t *e,
+ gs_font *font)
+{
+ int num_widths, num_chars, len;
+
+ font_cache_elem_array_sizes(pdev, font, &num_widths, &num_chars);
+ len = (num_chars + 7) / 8;
+ e->glyph_usage = gs_alloc_bytes(pdev->pdf_memory,
+ len, "alloc_font_cache_elem_arrays");
+
+ e->real_widths = (num_widths > 0 ? (double *)gs_alloc_bytes(pdev->pdf_memory,
+ num_widths * sizeof(*e->real_widths) *
+ ((font->FontType == ft_user_defined ||
+ font->FontType == ft_GL2_stick_user_defined ||
+ font->FontType == ft_PCL_user_defined ||
+ font->FontType == ft_MicroType ||
+ font->FontType == ft_GL2_531) ? 2 : 1),
+ "alloc_font_cache_elem_arrays") : NULL);
+ if (e->glyph_usage == NULL || (num_widths !=0 && e->real_widths == NULL)) {
+ gs_free_object(pdev->pdf_memory, e->glyph_usage,
+ "pdf_attach_font_resource");
+ gs_free_object(pdev->pdf_memory, e->real_widths,
+ "alloc_font_cache_elem_arrays");
+ return_error(gs_error_VMerror);
+ }
+ e->num_chars = num_chars;
+ e->num_widths = num_widths;
+ memset(e->glyph_usage, 0, len);
+ if (e->real_widths != NULL)
+ memset(e->real_widths, 0, num_widths * sizeof(*e->real_widths));
+ return 0;
+}
+
+int
+pdf_free_font_cache(gx_device_pdf *pdev)
+{
+ pdf_font_cache_elem_t *e = pdev->font_cache, *next;
+
+ /* fixed! fixme : release elements.
+ * We no longer track font_cache elements in the original font, which is where
+ * the memory used to be released. So now we must release it ourselves.
+ */
+
+ while (e != NULL) {
+ next = e->next;
+ pdf_remove_font_cache_elem(pdev, e);
+ e = next;
+ }
+ pdev->font_cache = NULL;
+ return 0;
+}
+
+/*
+ * Retrive font resource attached to a font,
+ * allocating glyph_usage and real_widths on request.
+ */
+int
+pdf_attached_font_resource(gx_device_pdf *pdev, gs_font *font,
+ pdf_font_resource_t **pdfont, byte **glyph_usage,
+ double **real_widths, int *num_chars, int *num_widths)
+{
+ pdf_font_cache_elem_t **e = pdf_locate_font_cache_elem(pdev, font);
+
+ if (e != NULL && (((*e)->glyph_usage == NULL && glyph_usage !=NULL) ||
+ ((*e)->real_widths == NULL && real_widths !=NULL))) {
+ int code = alloc_font_cache_elem_arrays(pdev, *e, font);
+
+ if (code < 0)
+ return code;
+ }
+ *pdfont = (e == NULL ? NULL : (*e)->pdfont);
+ if (glyph_usage != NULL)
+ *glyph_usage = (e == NULL ? NULL : (*e)->glyph_usage);
+ if (real_widths != NULL)
+ *real_widths = (e == NULL ? NULL : (*e)->real_widths);
+ if (num_chars != NULL)
+ *num_chars = (e == NULL ? 0 : (*e)->num_chars);
+ if (num_widths != NULL)
+ *num_widths = (e == NULL ? 0 : (*e)->num_widths);
+ return 0;
+}
+
+/*
+ * Attach font resource to a font.
+ */
+int
+pdf_attach_font_resource(gx_device_pdf *pdev, gs_font *font,
+ pdf_font_resource_t *pdfont)
+{
+ int num_chars, num_widths, len;
+ pdf_font_cache_elem_t *e, **pe = pdf_locate_font_cache_elem(pdev, font);
+
+ /* Allow the HPGL2 stick font to be attached to a type 3 font, it *is* a
+ * type 3 font, its just identified differently so that we can choose not
+ * to capture it elsewhere. Same for PCL bitmap fonts.
+ */
+ if (pdfont->FontType != font->FontType &&
+ (pdfont->FontType != ft_user_defined ||
+ (font->FontType != ft_PCL_user_defined &&
+ font->FontType != ft_MicroType &&
+ font->FontType != ft_GL2_stick_user_defined &&
+ font->FontType != ft_GL2_531)))
+ return_error(gs_error_unregistered); /* Must not happen. */
+ font_cache_elem_array_sizes(pdev, font, &num_widths, &num_chars);
+ len = (num_chars + 7) / 8;
+ if (pe != NULL) {
+ e = *pe;
+ if (e->pdfont == pdfont)
+ return 0;
+ e->pdfont = pdfont;
+ /* Reset glyph cache because e->pdfont had changed. */
+ memset(e->glyph_usage, 0, len);
+ memset(e->real_widths, 0, num_widths * sizeof(*e->real_widths));
+ } else {
+ e = (pdf_font_cache_elem_t *)gs_alloc_struct(pdev->pdf_memory,
+ pdf_font_cache_elem_t, &st_pdf_font_cache_elem,
+ "pdf_attach_font_resource");
+ if (e == NULL)
+ return_error(gs_error_VMerror);
+ e->pdfont = pdfont;
+ e->font_id = pdf_font_cache_elem_id(font);
+ e->num_chars = 0;
+ e->glyph_usage = NULL;
+ e->real_widths = NULL;
+ e->next = pdev->font_cache;
+ pdev->font_cache = e;
+ return 0;
+ }
+ return 0;
+}
+
+/* ================ Process text ================ */
+
+/* ---------------- Internal utilities ---------------- */
+
+/*
+ * Compute and return the orig_matrix of a font.
+ */
+int
+pdf_font_orig_matrix(const gs_font *font, gs_matrix *pmat)
+{
+ switch (font->FontType) {
+ case ft_composite: /* subfonts have their own FontMatrix */
+ case ft_TrueType:
+ case ft_CID_TrueType:
+ /* The TrueType FontMatrix is 1 unit per em, which is what we want. */
+ gs_make_identity(pmat);
+ return 0;
+ case ft_encrypted:
+ case ft_encrypted2:
+ case ft_CID_encrypted:
+ case ft_user_defined:
+ case ft_PCL_user_defined:
+ case ft_MicroType:
+ case ft_GL2_stick_user_defined:
+ case ft_GL2_531:
+ /*
+ * Type 1 fonts are supposed to use a standard FontMatrix of
+ * [0.001 0 0 0.001 0 0], with a 1000-unit cell. However,
+ * Windows NT 4.0 creates Type 1 fonts, apparently derived from
+ * TrueType fonts, that use a 2048-unit cell and corresponding
+ * FontMatrix. Also, some PS programs perform font scaling by
+ * replacing FontMatrix like this :
+ *
+ * /f12 /Times-Roman findfont
+ * copyfont % (remove FID)
+ * dup /FontMatrix [0.012 0 0 0.012 0 0] put
+ * definefont
+ * /f12 1 selectfont
+ *
+ * Such fonts are their own "base font", but the orig_matrix
+ * must still be set to 0.001, not 0.012 .
+ *
+ * The old code used a heuristic to detect and correct for this here.
+ * Unfortunately it doesn't work properly when it meets a font
+ * with FontMatrix like this :
+ *
+ * /FontMatrix [1 2288 div 0 0 1 2288 div 0 0 ] def
+ *
+ * (the bug 686970). Also comparefiles\455690.pdf appears to
+ * have similar problem. Therefore we added a support to lib/gs_fonts.ps,
+ * src/zbfont.c, src/gsfont.c that provides an acces to the original
+ * font via a special key OrigFont added to the font dictionary while definefont.
+ * Now we work through this access with PS interpreter,
+ * but keep the old heuristic for other clients.
+ */
+ {
+ const gs_font *base_font = font;
+
+ while (base_font->base != base_font)
+ base_font = base_font->base;
+ if (font->FontType == ft_user_defined ||
+ font->FontType == ft_PCL_user_defined ||
+ font->FontType == ft_MicroType ||
+ font->FontType == ft_GL2_stick_user_defined ||
+ font->FontType == ft_GL2_531)
+ *pmat = base_font->FontMatrix;
+ else if (base_font->orig_FontMatrix.xx != 0 || base_font->orig_FontMatrix.xy != 0 ||
+ base_font->orig_FontMatrix.yx != 0 || base_font->orig_FontMatrix.yy != 0)
+ *pmat = base_font->orig_FontMatrix;
+ else {
+ /* Must not happen with PS interpreter.
+ Provide a hewuristic for other clients.
+ */
+ if (base_font->FontMatrix.xx == 1.0/2048 &&
+ base_font->FontMatrix.xy == 0 &&
+ base_font->FontMatrix.yx == 0 &&
+ any_abs(base_font->FontMatrix.yy) == 1.0/2048
+ )
+ *pmat = base_font->FontMatrix;
+ else
+ gs_make_scaling(0.001, 0.001, pmat);
+ }
+ }
+ return 0;
+ default:
+ return_error(gs_error_rangecheck);
+ }
+}
+
+/*
+ * Special version of pdf_font_orig_matrix(), that cares FDArray font's FontMatrix too.
+ * Called only by pdf_glyph_width().
+ * 'cid' is only consulted if 'font' is a CIDFontType 0 CID font.
+ */
+static int
+glyph_orig_matrix(const gs_font *font, gs_glyph cid, gs_matrix *pmat)
+{
+ int code = pdf_font_orig_matrix(font, pmat);
+ if (code >= 0) {
+ if (font->FontType == ft_CID_encrypted) {
+ int fidx;
+
+ if (cid < GS_MIN_CID_GLYPH)
+ cid = GS_MIN_CID_GLYPH;
+ code = ((gs_font_cid0 *)font)->cidata.glyph_data((gs_font_base *)font,
+ cid, NULL, &fidx);
+ if (code < 0) {
+ code = ((gs_font_cid0 *)font)->cidata.glyph_data((gs_font_base *)font,
+ (gs_glyph)GS_MIN_CID_GLYPH, NULL, &fidx);
+ }
+ if (code >= 0) {
+ gs_matrix_multiply(&(gs_cid0_indexed_font(font, fidx)->FontMatrix),
+ pmat, pmat);
+ }
+ }
+ }
+ return code;
+}
+
+/*
+ * Check the Encoding compatibility
+ */
+bool
+pdf_check_encoding_compatibility(const pdf_font_resource_t *pdfont,
+ const pdf_char_glyph_pair_t *pairs, int num_chars)
+{
+ int i;
+
+ for (i = 0; i < num_chars; ++i) {
+ gs_char ch = pairs[i].chr;
+ pdf_encoding_element_t *pet = &pdfont->u.simple.Encoding[ch];
+
+ if (pairs[i].glyph == pet->glyph)
+ continue;
+ if (pet->glyph != GS_NO_GLYPH) /* encoding conflict */
+ return false;
+ }
+ return true;
+}
+
+/*
+ * Check whether the Encoding has listed glyphs.
+ */
+static bool
+pdf_check_encoding_has_glyphs(const pdf_font_resource_t *pdfont,
+ const pdf_char_glyph_pair_t *pairs, int num_chars)
+{
+ /* This function is pretty slow, but we can't find a better algorithm.
+ It works for the case of glyphshow with no proper encoding,
+ which we believe comes from poorly designed documents.
+ */
+ int i, ch;
+
+ for (i = 0; i < num_chars; ++i) {
+ for (ch = 0; ch < 256; ch++) {
+ pdf_encoding_element_t *pet = &pdfont->u.simple.Encoding[ch];
+
+ if (pairs[i].glyph == pet->glyph)
+ return true;
+ }
+ }
+ return false;
+}
+
+/*
+ * Check font resource for encoding compatibility.
+ */
+static bool
+pdf_is_compatible_encoding(gx_device_pdf *pdev, pdf_font_resource_t *pdfont,
+ gs_font *font, const pdf_char_glyph_pair_t *pairs, int num_chars)
+{
+ /*
+ * This crude version of the code ignores
+ * the possibility of re-encoding characters.
+ */
+ switch (pdfont->FontType) {
+ case ft_composite:
+ { /*
+ * We assume that source document don't redefine CMap
+ * resources and that incremental CMaps do not exist.
+ * Therefore we don't maintain stable CMap copies,
+ * but just compare CMap names for equality.
+ * A better implementation should compare the chars->glyphs
+ * translation against the stable copy of CMap,
+ * which to be handled with PDF CMap resource.
+ */
+ gs_font_type0 *pfont = (gs_font_type0 *)font;
+
+ if (pfont->data.FMapType == fmap_CMap) {
+ const gs_cmap_t *pcmap = pfont->data.CMap;
+ const gs_const_string *s0 = &pdfont->u.type0.CMapName;
+ const gs_const_string *s1 = &pcmap->CMapName;
+
+ return (s0->size == s1->size &&
+ !memcmp(s0->data, s1->data, s0->size));
+ }
+ }
+ return false;
+ case ft_user_defined:
+ case ft_PCL_user_defined:
+ case ft_MicroType:
+ case ft_GL2_stick_user_defined:
+ case ft_GL2_531:
+ if (pdfont->u.simple.Encoding == NULL)
+ return false; /* Not sure. Happens with 020-01.ps . */
+ /* fall through */
+ case ft_encrypted:
+ case ft_encrypted2:
+ case ft_TrueType:
+ return pdf_check_encoding_compatibility(pdfont, pairs, num_chars);
+ case ft_CID_encrypted:
+ case ft_CID_TrueType:
+ {
+ gs_font *font1 = (gs_font *)pdf_font_resource_font(pdfont, false);
+
+ return gs_is_CIDSystemInfo_compatible(
+ gs_font_cid_system_info(font),
+ gs_font_cid_system_info(font1));
+ }
+ default:
+ return false;
+ }
+}
+
+/*
+ * Check whethet the font resource has glyphs.
+ */
+static bool
+pdf_font_has_glyphs(gx_device_pdf *pdev, pdf_font_resource_t *pdfont,
+ gs_font *font, const pdf_char_glyph_pair_t *pairs, int num_chars)
+{
+ /*
+ * This crude version of the code ignores
+ * the possibility of re-encoding characters.
+ */
+ switch (pdfont->FontType) {
+ case ft_composite:
+ case ft_user_defined:
+ case ft_PCL_user_defined:
+ case ft_MicroType:
+ case ft_GL2_stick_user_defined:
+ case ft_GL2_531:
+ /* Unused case. */
+ return false;
+ case ft_encrypted:
+ case ft_encrypted2:
+ case ft_TrueType:
+ return pdf_check_encoding_has_glyphs(pdfont, pairs, num_chars);
+ case ft_CID_encrypted:
+ case ft_CID_TrueType:
+ /* Unused case. */
+ return false;
+ default:
+ return false;
+ }
+}
+
+/*
+ * Find a font resource compatible with a given font.
+ */
+static int
+pdf_find_font_resource(gx_device_pdf *pdev, gs_font *font,
+ pdf_resource_type_t type,
+ pdf_font_resource_t **ppdfont,
+ pdf_char_glyph_pairs_t *cgp,
+ bool compatible_encoding)
+{
+ pdf_resource_t **pchain = pdev->resources[type].chains;
+ pdf_resource_t *pres;
+ int i;
+
+ for (i = 0; i < NUM_RESOURCE_CHAINS; i++) {
+ for (pres = pchain[i]; pres != 0; pres = pres->next) {
+ pdf_font_resource_t *pdfont = (pdf_font_resource_t *)pres;
+ const gs_font_base *cfont;
+ gs_font *ofont = font;
+ int code;
+
+ if (font->FontType != pdfont->FontType)
+ continue;
+ if (pdfont->FontType == ft_composite) {
+ gs_font_type0 *font0 = (gs_font_type0 *)font;
+
+ ofont = font0->data.FDepVector[0]; /* See pdf_make_font_resource. */
+ cfont = pdf_font_resource_font(pdfont->u.type0.DescendantFont, false);
+ if (font0->data.CMap->WMode != pdfont->u.type0.WMode)
+ continue;
+ } else
+ cfont = pdf_font_resource_font(pdfont, false);
+ if (!pdf_is_CID_font(ofont) &&
+ (compatible_encoding
+ ? !pdf_is_compatible_encoding(pdev, pdfont, font, cgp->s, cgp->num_all_chars)
+ : !pdf_font_has_glyphs(pdev, pdfont, font, cgp->s, cgp->num_all_chars)))
+ continue;
+ if (cfont == 0)
+ continue;
+ code = gs_copied_can_copy_glyphs((const gs_font *)cfont, ofont,
+ &cgp->s[cgp->unused_offset].glyph, cgp->num_unused_chars,
+ sizeof(pdf_char_glyph_pair_t), true);
+ if (code == gs_error_unregistered) /* Debug purpose only. */
+ return code;
+ if(code > 0) {
+ *ppdfont = pdfont;
+ return 1;
+ }
+ }
+ }
+ return 0;
+}
+
+/*
+ * Find a type0 font resource for a gived descendent name and CMap name.
+ */
+static int
+pdf_find_type0_font_resource(gx_device_pdf *pdev, const pdf_font_resource_t *pdsubf,
+ const gs_const_string *CMapName, uint font_index, pdf_font_resource_t **ppdfont)
+{
+ pdf_resource_t **pchain = pdev->resources[resourceFont].chains;
+ pdf_resource_t *pres;
+ int i;
+
+ for (i = 0; i < NUM_RESOURCE_CHAINS; i++) {
+ for (pres = pchain[i]; pres != 0; pres = pres->next) {
+ pdf_font_resource_t *pdfont = (pdf_font_resource_t *)pres;
+
+ if (pdfont->FontType != ft_composite)
+ continue;
+ if (pdfont->u.type0.DescendantFont != pdsubf)
+ continue;
+ if (pdfont->u.type0.font_index != font_index)
+ continue;
+ if (pdfont->BaseFont.size != pdsubf->BaseFont.size + CMapName->size + 1)
+ continue;
+ if (memcmp(pdfont->BaseFont.data + pdsubf->BaseFont.size + 1,
+ CMapName->data, CMapName->size))
+ continue;
+ *ppdfont = pdfont;
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static int pdf_make_font_resource(gx_device_pdf *pdev, gs_font *font,
+ pdf_font_resource_t **ppdfont,
+ pdf_char_glyph_pairs_t *cgp);
+
+/*
+ * Create or find a CID font resource object for a glyph set.
+ */
+int
+pdf_obtain_cidfont_resource(gx_device_pdf *pdev, gs_font *subfont,
+ pdf_font_resource_t **ppdsubf,
+ pdf_char_glyph_pairs_t *cgp)
+{
+ int code = 0;
+
+ code = pdf_attached_font_resource(pdev, subfont, ppdsubf, NULL, NULL, NULL, NULL);
+ if (code < 0)
+ return code;
+ if (*ppdsubf != NULL) {
+ const gs_font_base *cfont = pdf_font_resource_font(*ppdsubf, false);
+
+ code = gs_copied_can_copy_glyphs((const gs_font *)cfont, subfont,
+ &cgp->s[cgp->unused_offset].glyph, cgp->num_unused_chars,
+ sizeof(pdf_char_glyph_pair_t), true);
+ if (code > 0)
+ return 0;
+ if (code < 0)
+ return code;
+ *ppdsubf = NULL;
+ }
+ code = pdf_find_font_resource(pdev, subfont,
+ resourceCIDFont, ppdsubf, cgp, true);
+ if (code < 0)
+ return code;
+ if (*ppdsubf == NULL) {
+ code = pdf_make_font_resource(pdev, subfont, ppdsubf, cgp);
+ if (code < 0)
+ return code;
+ }
+ return pdf_attach_font_resource(pdev, subfont, *ppdsubf);
+}
+
+/*
+ * Refine index of BaseEncoding.
+ */
+static int
+pdf_refine_encoding_index(const gx_device_pdf *pdev, int index, bool is_standard)
+{
+ if (pdev->ForOPDFRead) {
+ /*
+ * Allow Postscript encodings only.
+ */
+ switch (index) {
+
+ case ENCODING_INDEX_STANDARD: return index;
+ case ENCODING_INDEX_ISOLATIN1: return index;
+ default:
+ return ENCODING_INDEX_STANDARD;
+ }
+ }
+ /*
+ * Per the PDF 1.3 documentation, there are only 3 BaseEncoding
+ * values allowed for non-embedded fonts. Pick one here.
+ */
+ switch (index) {
+ case ENCODING_INDEX_WINANSI:
+ case ENCODING_INDEX_MACROMAN:
+ case ENCODING_INDEX_MACEXPERT:
+ return index;
+ case ENCODING_INDEX_STANDARD:
+ if (is_standard)
+ return index;
+ /* Falls through. */
+ default:
+ return ENCODING_INDEX_WINANSI;
+ }
+}
+
+/*
+ * Create a font resource object for a gs_font of Type 3.
+ */
+int
+pdf_make_font3_resource(gx_device_pdf *pdev, gs_font *font,
+ pdf_font_resource_t **ppdfont)
+{
+ const gs_font_base *bfont = (const gs_font_base *)font;
+ pdf_font_resource_t *pdfont;
+ byte *cached;
+ int code;
+
+ cached = gs_alloc_bytes(pdev->pdf_memory, 256/8, "pdf_make_font3_resource");
+ if (cached == NULL)
+ return_error(gs_error_VMerror);
+ code = font_resource_encoded_alloc(pdev, &pdfont, bfont->id,
+ ft_user_defined, pdf_write_contents_bitmap);
+ if (code < 0) {
+ gs_free_object(pdev->pdf_memory, cached, "pdf_make_font3_resource");
+ return code;
+ }
+ memset(cached, 0, 256 / 8);
+ pdfont->mark_glyph = font->dir->ccache.mark_glyph; /* For pdf_font_resource_enum_ptrs. */
+ pdfont->u.simple.s.type3.bitmap_font = false;
+ pdfont->u.simple.BaseEncoding = pdf_refine_encoding_index(pdev,
+ bfont->nearest_encoding_index, true);
+ pdfont->u.simple.s.type3.char_procs = NULL;
+ pdfont->u.simple.s.type3.cached = cached;
+ if (pdfont->FontType == ft_user_defined && bfont->FontBBox.p.x == 0.0 &&
+ bfont->FontBBox.p.y == 0.00 && bfont->FontBBox.q.x == 0.00 &&
+ bfont->FontBBox.q.y == 0.0) {
+ /* I think this can only happen with a type 3 (bitmap) font from PCL.
+ * If we leave the BBox as 0 then we end up putting a 0 0 1000 1000 in
+ * the PDF. This causes Acrobat to be unable to search or highlight
+ * the search results. I think that we will always get a consistent
+ * type of font from the PCL interpreter, and if so the correct BBox
+ * sould always is 0 0 1 -1.
+ */
+ pdfont->u.simple.s.type3.FontBBox.p.x = 0;
+ pdfont->u.simple.s.type3.FontBBox.p.y = 0;
+ pdfont->u.simple.s.type3.FontBBox.q.x = 1;
+ pdfont->u.simple.s.type3.FontBBox.q.y = -1;
+ } else {
+ pdfont->u.simple.s.type3.FontBBox.p.x = bfont->FontBBox.p.x;
+ pdfont->u.simple.s.type3.FontBBox.p.y = bfont->FontBBox.p.y;
+ pdfont->u.simple.s.type3.FontBBox.q.x = bfont->FontBBox.q.x;
+ pdfont->u.simple.s.type3.FontBBox.q.y = bfont->FontBBox.q.y;
+ }
+ pdfont->u.simple.s.type3.FontMatrix = bfont->FontMatrix;
+ pdfont->u.simple.s.type3.Resources = cos_dict_alloc(pdev, "pdf_make_font3_resource");
+ if (pdfont->u.simple.s.type3.Resources == NULL)
+ return_error(gs_error_VMerror);
+ /* Adobe viewers have a precision problem with small font matrices : */
+ /* Don't perform this test if all entries are 0, leads to infinite loop! */
+ if (pdfont->u.simple.s.type3.FontMatrix.xx != 0.0 ||
+ pdfont->u.simple.s.type3.FontMatrix.xy != 0.0 ||
+ pdfont->u.simple.s.type3.FontMatrix.yx != 0.0 ||
+ pdfont->u.simple.s.type3.FontMatrix.yy != 0.0) {
+ while (any_abs(pdfont->u.simple.s.type3.FontMatrix.xx) < 0.001 &&
+ any_abs(pdfont->u.simple.s.type3.FontMatrix.xy) < 0.001 &&
+ any_abs(pdfont->u.simple.s.type3.FontMatrix.yx) < 0.001 &&
+ any_abs(pdfont->u.simple.s.type3.FontMatrix.yy) < 0.001) {
+ pdfont->u.simple.s.type3.FontMatrix.xx *= 10;
+ pdfont->u.simple.s.type3.FontMatrix.xy *= 10;
+ pdfont->u.simple.s.type3.FontMatrix.yx *= 10;
+ pdfont->u.simple.s.type3.FontMatrix.yy *= 10;
+ }
+ }
+ *ppdfont = pdfont;
+ return 0;
+}
+
+/*
+ * Create a font resource object for a gs_font. Return 1 iff the
+ * font was newly created (it's a roudiment, keeping reverse compatibility).
+ * This procedure is only intended to be called
+ * from a few places in the text code.
+ */
+static int
+pdf_make_font_resource(gx_device_pdf *pdev, gs_font *font,
+ pdf_font_resource_t **ppdfont,
+ pdf_char_glyph_pairs_t *cgp)
+{
+ int index = -1;
+ int BaseEncoding = ENCODING_INDEX_UNKNOWN;
+ pdf_font_embed_t embed;
+ pdf_font_descriptor_t *pfd = 0;
+ int (*font_alloc)(gx_device_pdf *, pdf_font_resource_t **,
+ gs_id, pdf_font_descriptor_t *);
+ gs_font *base_font = font; /* A roudiment from old code. Keep it for a while. */
+ pdf_font_resource_t *pdfont;
+ pdf_standard_font_t *const psfa =
+ pdev->text->outline_fonts->standard_fonts;
+ int code = 0;
+
+ if (pdev->version < psdf_version_level2_with_TT) {
+ switch(font->FontType) {
+ case ft_TrueType:
+ case ft_CID_TrueType:
+ return_error(gs_error_undefined);
+ default:
+ break;
+ }
+ }
+ if (pdev->ForOPDFRead && !pdev->HaveCIDSystem) {
+ switch(font->FontType) {
+ case ft_CID_encrypted:
+ case ft_CID_TrueType:
+ return_error(gs_error_undefined);
+ default:
+ break;
+ }
+ }
+ if (!pdev->HaveCFF) {
+ if (font->FontType == ft_encrypted2)
+ return_error(gs_error_undefined);
+ }
+ embed = pdf_font_embed_status(pdev, base_font, &index, cgp->s, cgp->num_all_chars);
+ if (pdev->CompatibilityLevel < 1.3)
+ if (embed != FONT_EMBED_NO && font->FontType == ft_CID_TrueType)
+ return_error(gs_error_rangecheck);
+ if (embed == FONT_EMBED_STANDARD) {
+ pdf_standard_font_t *psf = &psfa[index];
+
+ if (psf->pdfont == NULL ||
+ !pdf_is_compatible_encoding(pdev, psf->pdfont, font,
+ cgp->s, cgp->num_all_chars)) {
+ code = pdf_font_std_alloc(pdev, ppdfont, (psf->pdfont == NULL), base_font->id,
+ (gs_font_base *)base_font, index);
+ if (code < 0)
+ return code;
+ if (psf->pdfont == NULL)
+ psf->pdfont = *ppdfont;
+ (*ppdfont)->u.simple.BaseEncoding = pdf_refine_encoding_index(pdev,
+ ((const gs_font_base *)base_font)->nearest_encoding_index, true);
+ code = 1;
+ } else
+ *ppdfont = psf->pdfont;
+ return code;
+ }
+
+ switch (font->FontType) {
+ case ft_CID_encrypted:
+ case ft_CID_TrueType:
+ font_alloc = pdf_font_cidfont_alloc;
+ break;
+ case ft_encrypted:
+ case ft_encrypted2:
+ case ft_TrueType:
+ font_alloc = pdf_font_simple_alloc;
+ break;
+ case ft_user_defined:
+ case ft_PCL_user_defined:
+ case ft_MicroType:
+ case ft_GL2_stick_user_defined:
+ case ft_GL2_531:
+ code = pdf_make_font3_resource(pdev, font, ppdfont);
+ if (code < 0)
+ return code;
+ return 1;
+ default:
+ return_error(gs_error_invalidfont);
+ }
+
+ /* Create an appropriate font resource and descriptor. */
+ if (embed == FONT_EMBED_YES) {
+ /*
+ * HACK: Acrobat Reader 3 has a bug that makes cmap formats 4
+ * and 6 not work in embedded TrueType fonts. Consequently, it
+ * can only handle embedded TrueType fonts if all the glyphs
+ * referenced by the Encoding have numbers 0-255. Check for
+ * this now.
+ */
+ if (font->FontType == ft_TrueType &&
+ pdev->CompatibilityLevel <= 1.2 && !pdev->ForOPDFRead
+ ) {
+ int i;
+
+ for (i = 0; i <= 0xff; ++i) {
+ gs_glyph glyph =
+ font->procs.encode_char(font, (gs_char)i,
+ GLYPH_SPACE_INDEX);
+
+ if (glyph == GS_NO_GLYPH ||
+ (glyph >= GS_MIN_GLYPH_INDEX &&
+ glyph <= GS_MIN_GLYPH_INDEX + 0xff)
+ )
+ continue;
+ /* Can't embed, punt. */
+ return_error(gs_error_rangecheck);
+ }
+ }
+ }
+ if (font->FontType == ft_encrypted || font->FontType == ft_encrypted2
+ || (font->FontType == ft_TrueType && pdev->ForOPDFRead)
+ || (font->FontType == ft_TrueType && ((const gs_font_base *)base_font)->nearest_encoding_index != ENCODING_INDEX_UNKNOWN)) {
+ /* Yet more crazy heuristics. If we embed a TrueType font and don't subset it, then
+ * we preserve the CMAP subtable(s) rather than generatng new ones. The problem is
+ * that if we ake the font symbolic, Acrobat uses the 1,0 CMAP, whereas if we don't
+ * It uses the 3,1 CMAP (and the Encoding). If these two are not compatible, then
+ * the result will be different. So, if we are not subsetting the font, and the original
+ * font wasn't symbolic (it has an Encoding) then we will create a new Encoding, and
+ * in pdf_write_font_descriptor we wil take back off the symbolic flag.
+ */
+ /* Removed the addition of Encodings to TrueType fonts as we always write
+ * these with the Symbolic flag set. The comment below explains why we
+ * previously wrote these, but as the comment notes this was incorrect.
+ * Removed as it is causing preflight problems, and is specifically
+ * disallowed with PDF/A output.
+ */
+ /*
+ * We write True Types with Symbolic flag set.
+ * PDF spec says that "symbolic font should not specify Encoding entry"
+ * (see section 5.5, the article "Encodings for True Type fonts", paragraph 3).
+ * However Acrobat Reader 4,5,6 fail when TT font with no Encoding
+ * appears in a document together with a CID font with a non-standard CMap
+ * (AR 4 and 5 claim "The encoding (CMap) specified by a font is corrupted."
+ * (we read it as "The encoding or CMap specified by a font is corrupted.",
+ * and apply the 1st alternative)). We believe that AR is buggy,
+ * and therefore we write an Encoding with non-CID True Type fonts.
+ * Hopely other viewers can ignore Encoding in such case. Actually in this case
+ * an Encoding doesn't add an useful information.
+ */
+ BaseEncoding = pdf_refine_encoding_index(pdev,
+ ((const gs_font_base *)base_font)->nearest_encoding_index, false);
+ }
+ if ((code = pdf_font_descriptor_alloc(pdev, &pfd,
+ (gs_font_base *)base_font,
+ embed == FONT_EMBED_YES)) < 0 ||
+ (code = font_alloc(pdev, &pdfont, base_font->id, pfd)) < 0
+ )
+ return code;
+
+ if (!pdf_is_CID_font(font)) {
+ pdfont->u.simple.BaseEncoding = BaseEncoding;
+ pdfont->mark_glyph = font->dir->ccache.mark_glyph;
+ }
+
+ if (pdev->PDFA != 0 && font->FontType == ft_TrueType) {
+ /* The Adobe preflight tool for PDF/A
+ checks whether Width or W include elements
+ for all characters in the True Type font.
+ Due to that we need to provide a width
+ for .notdef glyph.
+ (It's a part of the bug 688790).
+ */
+ gs_font_base *cfont = pdf_font_descriptor_font(pfd, false/*any*/);
+ gs_glyph notdef_glyph = copied_get_notdef((const gs_font *)cfont);
+ pdf_glyph_widths_t widths;
+ double cdevproc_result[10] = {0,0,0,0,0, 0,0,0,0,0};
+ double *w, *v, *w0;
+
+ if (notdef_glyph != GS_NO_GLYPH) {
+ code = pdf_obtain_cidfont_widths_arrays(pdev, pdfont, font->WMode, &w, &w0, &v);
+ if (code < 0)
+ return code;
+ widths.Width.w = 0;
+ code = pdf_glyph_widths(pdfont, font->WMode, notdef_glyph,
+ font, &widths, cdevproc_result);
+ if (code < 0)
+ return code;
+ w[0] = widths.Width.w;
+ pdfont->used[0] |= 0x80;
+ }
+ }
+ *ppdfont = pdfont;
+ return 1;
+}
+
+/*
+ * Check for simple font.
+ */
+bool
+pdf_is_simple_font(gs_font *font)
+{
+ return (font->FontType == ft_encrypted ||
+ font->FontType == ft_encrypted2 ||
+ font->FontType == ft_TrueType ||
+ font->FontType == ft_user_defined ||
+ font->FontType == ft_PCL_user_defined ||
+ font->FontType == ft_MicroType ||
+ font->FontType == ft_GL2_stick_user_defined ||
+ font->FontType == ft_GL2_531);
+}
+
+/*
+ * Check for CID font.
+ */
+bool
+pdf_is_CID_font(gs_font *font)
+{
+ return (font->FontType == ft_CID_encrypted ||
+ font->FontType == ft_CID_TrueType);
+}
+
+/*
+ * Enumerate glyphs for a text.
+ */
+static int
+pdf_next_char_glyph(gs_text_enum_t *penum, const gs_string *pstr,
+ /* const */ gs_font *font, bool font_is_simple,
+ gs_char *char_code, gs_char *cid, gs_glyph *glyph)
+{
+ int code = font->procs.next_char_glyph(penum, char_code, glyph);
+
+ if (code == 2) /* end of string */
+ return code;
+ if (code < 0)
+ return code;
+ if (font_is_simple) {
+ *cid = *char_code;
+ *glyph = font->procs.encode_char(font, *char_code, GLYPH_SPACE_NAME);
+ if (*glyph == GS_NO_GLYPH)
+ return 3;
+ } else {
+ if (*glyph < GS_MIN_CID_GLYPH)
+ return 3; /* Not sure why, copied from scan_cmap_text. */
+ *cid = *glyph - GS_MIN_CID_GLYPH; /* CID */
+ }
+ return 0;
+}
+
+static void
+store_glyphs(pdf_char_glyph_pairs_t *cgp,
+ byte *glyph_usage, int char_cache_size,
+ gs_char char_code, gs_char cid, gs_glyph glyph)
+{
+ int j;
+
+ for (j = 0; j < cgp->num_all_chars; j++)
+ if (cgp->s[j].chr == cid)
+ break;
+ if (j < cgp->num_all_chars)
+ return;
+ cgp->s[cgp->num_all_chars].glyph = glyph;
+ cgp->s[cgp->num_all_chars].chr = char_code;
+ cgp->num_all_chars++;
+ if (glyph_usage == 0 || !(glyph_usage[cid / 8] & (0x80 >> (cid & 7)))) {
+ cgp->s[cgp->unused_offset + cgp->num_unused_chars].glyph = glyph;
+ cgp->s[cgp->unused_offset + cgp->num_unused_chars].chr = char_code;
+ cgp->num_unused_chars++;
+ }
+ /* We are disliked that gs_copied_can_copy_glyphs can get redundant
+ * glyphs, if Encoding specifies several codes for same glyph.
+ * But we need the positional correspondence
+ * of glyphs to codes for pdf_is_compatible_encoding.
+ * Redundant glyphs isn't a big payment for it
+ * because they happen seldom.
+ */
+}
+
+static gs_char
+pdf_new_char_code_in_pdfont(pdf_char_glyph_pairs_t *cgp, gs_glyph glyph, int *last_reserved_char)
+{ /* Returns 256 if encoding overflows. */
+ int j, ch;
+
+ for (j = 0; j < cgp->num_all_chars; j++)
+ if (cgp->s[j].glyph == glyph)
+ break;
+ if (j < cgp->num_all_chars)
+ return cgp->s[j].chr;
+ ch = ++*last_reserved_char;
+ cgp->s[cgp->num_all_chars].glyph = glyph;
+ cgp->s[cgp->num_all_chars].chr = ch;
+ cgp->num_all_chars++;
+ cgp->s[cgp->unused_offset + cgp->num_unused_chars].glyph = glyph;
+ cgp->s[cgp->unused_offset + cgp->num_unused_chars].chr = ch;
+ cgp->num_unused_chars++;
+ return ch;
+}
+
+static gs_char
+pdf_reserve_char_code_in_pdfont(pdf_font_resource_t *pdfont, pdf_char_glyph_pairs_t *cgp, gs_glyph glyph,
+ int *last_reserved_char)
+{ /* Returns 256 if encoding overflows. */
+ /* This function is pretty slow, but we can't find a better algorithm.
+ It works for the case of glyphshow with no proper encoding,
+ which we believe comes from poorly designed documents.
+ */
+ int j, ch;
+
+ for (j = 0; j < cgp->num_all_chars; j++)
+ if (cgp->s[j].glyph == glyph)
+ break;
+ if (j < cgp->num_all_chars)
+ return cgp->s[j].chr;
+
+ for (ch = 0; ch < 256; ch++) {
+ pdf_encoding_element_t *pet = &pdfont->u.simple.Encoding[ch];
+
+ if (glyph == pet->glyph)
+ return ch;
+ }
+ /* If the font has a known encoding, prefer .notdef codes. */
+ if (pdfont->u.simple.preferred_encoding_index != -1) {
+ const ushort *enc = gs_c_known_encodings[pdfont->u.simple.preferred_encoding_index];
+
+ if (standard_glyph_code_for_notdef == GS_NO_GLYPH)
+ standard_glyph_code_for_notdef =
+ gs_c_name_glyph((const byte *)".notdef", 7) - gs_c_min_std_encoding_glyph;
+ for (ch = *last_reserved_char + 1; ch < 256; ch++) {
+ pdf_encoding_element_t *pet = &pdfont->u.simple.Encoding[ch];
+
+ if (pet->glyph == GS_NO_GLYPH && enc[ch] == standard_glyph_code_for_notdef) {
+ *last_reserved_char = ch;
+ break;
+ }
+ }
+ }
+ /* Otherwise use any code unused in the font. */
+ if (ch > 255) {
+ ch = *last_reserved_char + 1;
+ for (; ch < 255; ch++) {
+ pdf_encoding_element_t *pet = &pdfont->u.simple.Encoding[ch];
+
+ if (pet->glyph == GS_NO_GLYPH)
+ break;
+ }
+ *last_reserved_char = ch;
+ }
+ cgp->s[cgp->num_all_chars].glyph = glyph;
+ cgp->s[cgp->num_all_chars].chr = ch;
+ cgp->num_all_chars++;
+ cgp->s[cgp->unused_offset + cgp->num_unused_chars].glyph = glyph;
+ cgp->s[cgp->unused_offset + cgp->num_unused_chars].chr = ch;
+ cgp->num_unused_chars++;
+ return ch;
+}
+
+/* Allocate storage for the glyph set of the text. */
+static int
+pdf_alloc_text_glyphs_table(gx_device_pdf *pdev, pdf_text_enum_t *penum, const gs_string *pstr)
+{
+ const int go = (pstr != NULL ? pstr->size : penum->text.size);
+ const int struct_size = sizeof(pdf_char_glyph_pairs_t) +
+ sizeof(pdf_char_glyph_pair_t) * (2 * go - 1);
+ pdf_char_glyph_pairs_t *cgp = (pdf_char_glyph_pairs_t *)gs_alloc_bytes(penum->memory,
+ struct_size, "pdf_alloc_text_glyphs_table");
+ if (cgp == NULL)
+ return_error(gs_error_VMerror);
+ penum->cgp = cgp;
+ cgp->unused_offset = go;
+ cgp->num_all_chars = 0;
+ cgp->num_unused_chars = 0;
+ return 0;
+}
+
+/* Build the glyph set of the text. */
+static int
+pdf_make_text_glyphs_table(pdf_text_enum_t *penum, const gs_string *pstr,
+ byte *glyph_usage, int char_cache_size)
+{
+ gs_text_enum_t scan = *(gs_text_enum_t *)penum;
+ gs_font *font = (gs_font *)penum->current_font;
+ bool font_is_simple = pdf_is_simple_font(font);
+ pdf_char_glyph_pairs_t *cgp = penum->cgp;
+ gs_char char_code, cid;
+ gs_glyph glyph;
+ int code;
+
+ cgp->num_unused_chars = 0;
+ cgp->num_all_chars = 0;
+ if (pstr != NULL) {
+ scan.text.data.bytes = pstr->data;
+ scan.text.size = pstr->size;
+ scan.index = 0;
+ /* if TEXT_FROM_CHARS the data was converted to bytes earlier */
+ if ( scan.text.operation & TEXT_FROM_CHARS )
+ scan.text.operation = ((scan.text.operation & ~TEXT_FROM_CHARS) | TEXT_FROM_STRING);
+ }
+ for (;;) {
+ code = pdf_next_char_glyph(&scan, pstr, font, font_is_simple,
+ &char_code, &cid, &glyph);
+ if (code == 2) /* end of string */
+ break;
+ if (code == 3) /* no glyph */
+ continue;
+ if (code < 0)
+ return code;
+ if (cgp->num_all_chars > cgp->unused_offset)
+ return_error(gs_error_unregistered); /* Must not happen. */
+ if (glyph_usage != 0 && cid > char_cache_size)
+ continue;
+ store_glyphs(cgp, glyph_usage, char_cache_size,
+ char_code, cid, glyph);
+ }
+ return 0;
+}
+
+/* Build the glyph set of the glyphshow text, and re_encode the text. */
+static int
+pdf_make_text_glyphs_table_unencoded(gx_device_pdf *pdev, pdf_char_glyph_pairs_t *cgp,
+ gs_font *font, const gs_string *pstr, const gs_glyph *gdata,
+ int *ps_encoding_index)
+{
+ int i, j, code;
+ gs_char ch;
+ gs_const_string gname;
+ gs_glyph *gid = (gs_glyph *)pstr->data; /* pdf_text_process allocs enough space. */
+ gs_font_base *bfont;
+ bool unknown = false;
+ pdf_font_resource_t *pdfont;
+ int ei, start_ei = -1;
+
+ code = pdf_attached_font_resource(pdev, font, &pdfont, NULL, NULL, NULL, NULL);
+ if (code < 0)
+ return code;
+ /* Translate glyph name indices into gscencs.c indices. */
+ for (i = 0; i < pstr->size; i++) {
+ int code = font->procs.glyph_name(font, gdata[i], &gname);
+
+ if (code < 0)
+ return code;
+ gid[i] = gs_c_name_glyph(gname.data, gname.size);
+ if (gid[i] == GS_NO_GLYPH) {
+ /* Use global glyph name. */
+ /* Assuming this can't fail in a middle of a text,
+ because TEXT_FROM_GLYPHS never works for Postscript. */
+ gid[i] = gdata[i];
+ unknown = true;
+ }
+ }
+do_unknown:
+ if (unknown) {
+ int last_reserved_char = -1;
+ /* Using global glyph names. */
+
+ /* Try to find an existing font resource, which has necessary glyphs.
+ Doing so to prevent creating multiple font copies.
+ */
+ cgp->num_unused_chars = 0;
+ cgp->num_all_chars = 0;
+ for (i = 0; i < pstr->size; i++) {
+ /* Temporary stub gid instead cid and char_code : */
+ store_glyphs(cgp, NULL, 0, gdata[i], gdata[i], gdata[i]);
+ }
+ code = pdf_find_font_resource(pdev, font, resourceFont, &pdfont, cgp, false);
+ if (code < 0)
+ return code;
+ if (code) {
+ /* Found one - make it be current. */
+ code = pdf_attach_font_resource(pdev, font, pdfont);
+ if (code < 0)
+ return code;
+ }
+ /* Try to add glyphs to the current font resource. . */
+ cgp->num_unused_chars = 0;
+ cgp->num_all_chars = 0;
+
+ if(pdfont != NULL)
+ last_reserved_char = pdfont->u.simple.last_reserved_char;
+
+ for (i = 0; i < pstr->size; i++) {
+
+ if (pdfont == NULL)
+ ch = 256; /* Force new encoding. */
+ else
+ ch = pdf_reserve_char_code_in_pdfont(pdfont, cgp, gdata[i], &last_reserved_char);
+ if (ch > 255) {
+ if(pdfont != NULL)
+ last_reserved_char = pdfont->u.simple.last_reserved_char;
+ /* Start a new font/encoding. */
+ last_reserved_char = -1;
+
+ cgp->num_unused_chars = 0;
+ cgp->num_all_chars = 0;
+ for (i = 0; i < pstr->size; i++) {
+ ch = pdf_new_char_code_in_pdfont(cgp, gdata[i], &last_reserved_char);
+ if (ch > 255) {
+ /* More than 255 unknown characters in a text.
+ It must not happen because TEXT_FROM_GLYPHS
+ never works for Postscript. */
+ return_error(gs_error_unregistered);
+ }
+ }
+ }
+ }
+ if (pdfont != NULL)
+ pdfont->u.simple.last_reserved_char = last_reserved_char;
+ /* Change glyphs to char codes in the text : */
+ for (i = 0; i < pstr->size; i++) {
+ /* Picked up by Coverity, if pdfont is NULL then the call might dereference it */
+ if (pdfont != NULL) {
+ /* A trick : pdf_reserve_char_code_in_pdfont here simply encodes with cgp. */
+ ch = pdf_reserve_char_code_in_pdfont(pdfont, cgp, gdata[i], &pdfont->u.simple.last_reserved_char);
+ pstr->data[i] = ch;
+ } else {
+ /* So if pdffont is NULL, do the 'trick' code mentioned above.
+ * If that fails (I believe it shouod not), then return an error.
+ */
+ int j;
+
+ for (j = 0; j < cgp->num_all_chars; j++)
+ if (cgp->s[j].glyph == gdata[i])
+ break;
+ if (j < cgp->num_all_chars)
+ pstr->data[i] = cgp->s[j].chr;
+ else
+ return_error(gs_error_unregistered);
+ }
+ }
+ return 0;
+ }
+ /* Now we know it's a base font, bcause it has glyph names. */
+ bfont = (gs_font_base *)font;
+ if (start_ei < 0)
+ start_ei = bfont->nearest_encoding_index;
+ if (start_ei < 0)
+ start_ei = 0;
+ /* Find an acceptable encodng, starting from start_ei.
+ We need a conservative search to minimize the probability
+ of encoding conflicts.
+ */
+ for (j = 0, ei = start_ei; gs_c_known_encodings[j]; j++, ei++) {
+ if (!gs_c_known_encodings[ei])
+ ei = 0;
+ /* Restrict with PDF encodings, because others give frequent conflicts. */
+ if (ei > 5) /* Hack : gscedata.c must provide a constant. */
+ continue;
+ cgp->num_unused_chars = 0;
+ cgp->num_all_chars = 0;
+ for (i = 0; i < pstr->size; i++) {
+ ch = gs_c_decode(gid[i], ei);
+ if (ch == GS_NO_CHAR)
+ break;
+ if (ch > 255)
+ break; /* MacGlyphEncoding defines extra glyphs. */
+ /* pstr->data[i] = (byte)ch; Can't do because pstr->data and gid
+ are same pointer. Will do in a separate pass below. */
+ store_glyphs(cgp, NULL, 0, ch, ch, gdata[i]);
+ }
+ *ps_encoding_index = ei;
+ if (i == pstr->size) {
+ /* Change glyphs to char codes in the text : */
+ for (i = 0; i < pstr->size; i++)
+ pstr->data[i] = (byte)gs_c_decode(gid[i], ei);
+ return 0;
+ }
+ }
+ unknown = true;
+ goto do_unknown;
+}
+
+/* Get/make font resource for the font with a known encoding. */
+static int
+pdf_obtain_font_resource_encoded(gx_device_pdf *pdev, gs_font *font,
+ pdf_font_resource_t **ppdfont, pdf_char_glyph_pairs_t *cgp)
+{
+ int code;
+ pdf_font_resource_t *pdfont_not_allowed = NULL;
+
+ if (*ppdfont != 0) {
+ gs_font_base *cfont = pdf_font_resource_font(*ppdfont, false);
+
+ if (font->FontType != ft_user_defined &&
+ font->FontType != ft_PCL_user_defined &&
+ font->FontType != ft_MicroType &&
+ font->FontType != ft_GL2_stick_user_defined &&
+ font->FontType != ft_GL2_531) {
+ code = gs_copied_can_copy_glyphs((gs_font *)cfont, font,
+ &cgp->s[cgp->unused_offset].glyph, cgp->num_unused_chars,
+ sizeof(pdf_char_glyph_pair_t), true);
+ if (code < 0)
+ code = 1;
+ } else
+ code = 1;
+ if (code == 0) {
+ pdfont_not_allowed = *ppdfont;
+ *ppdfont = 0;
+ } else if(!pdf_is_compatible_encoding(pdev, *ppdfont, font,
+ cgp->s, cgp->num_all_chars)) {
+ pdfont_not_allowed = *ppdfont;
+ *ppdfont = 0;
+ }
+ }
+ if (*ppdfont == 0) {
+ gs_font *base_font = font;
+ gs_font *below;
+ bool same_encoding = true;
+
+ /*
+ * Find the "lowest" base font that has the same outlines.
+ * We use its FontName for font resource.
+ */
+ while ((below = base_font->base) != base_font &&
+ base_font->procs.same_font(base_font, below, FONT_SAME_OUTLINES))
+ base_font = below;
+ if (base_font != font)
+ same_encoding = ((base_font->procs.same_font(base_font, font,
+ FONT_SAME_ENCODING) & FONT_SAME_ENCODING) != 0);
+ /* Find or make font resource. */
+ code = pdf_attached_font_resource(pdev, base_font, ppdfont, NULL, NULL, NULL, NULL);
+ if (code < 0)
+ return code;
+ if (base_font != font) {
+ if (pdfont_not_allowed == *ppdfont)
+ *ppdfont = NULL;
+ }
+ if(*ppdfont != NULL && !pdf_is_compatible_encoding(pdev, *ppdfont,
+ base_font, cgp->s, cgp->num_all_chars))
+ *ppdfont = NULL;
+ if (*ppdfont == NULL || *ppdfont == pdfont_not_allowed) {
+ pdf_resource_type_t type =
+ (pdf_is_CID_font(base_font) ? resourceCIDFont
+ : resourceFont);
+ *ppdfont = NULL;
+ code = pdf_find_font_resource(pdev, base_font, type, ppdfont, cgp, true);
+ if (code < 0)
+ return code;
+ if (*ppdfont == NULL) {
+ code = pdf_make_font_resource(pdev, base_font, ppdfont, cgp);
+ if (code < 0)
+ return code;
+ }
+ if (base_font != font && same_encoding) {
+ code = pdf_attach_font_resource(pdev, base_font, *ppdfont);
+ if (code < 0)
+ return code;
+ }
+ }
+ code = pdf_attach_font_resource(pdev, font, *ppdfont);
+ if (code < 0)
+ return code;
+ }
+ return 0;
+}
+
+/* Mark glyphs used in the text with the font resource. */
+static int
+pdf_mark_text_glyphs(const gs_text_enum_t *penum, const gs_string *pstr,
+ byte *glyph_usage, int char_cache_size)
+{
+ gs_text_enum_t scan = *penum;
+ gs_font *font = (gs_font *)penum->current_font;
+ bool font_is_simple = pdf_is_simple_font(font);
+ gs_char char_code, cid;
+ gs_glyph glyph;
+
+ if (glyph_usage == NULL)
+ return 0;
+
+ if (pstr != NULL) {
+ scan.text.data.bytes = pstr->data;
+ scan.text.size = pstr->size;
+ scan.index = 0;
+ /* if TEXT_FROM_CHARS the data was converted to bytes earlier */
+ if ( scan.text.operation & TEXT_FROM_CHARS )
+ scan.text.operation =
+ ((scan.text.operation & ~TEXT_FROM_CHARS) | TEXT_FROM_STRING);
+ }
+ for (;;) {
+ int code = pdf_next_char_glyph(&scan, pstr, font, font_is_simple,
+ &char_code, &cid, &glyph);
+
+ if (code == 2) /* end of string */
+ break;
+ if (code == 3) /* no glyph */
+ continue;
+ if (code < 0)
+ return code;
+ if (cid >= char_cache_size)
+ continue;
+ glyph_usage[cid / 8] |= 0x80 >> (cid & 7);
+ }
+ return 0;
+}
+
+/* Mark glyphs used in the glyphshow text with the font resource. */
+static int
+pdf_mark_text_glyphs_unencoded(const gs_text_enum_t *penum, const gs_string *pstr,
+ byte *glyph_usage, int char_cache_size)
+{
+ int i;
+
+ for(i = 0; i < pstr->size; i++) {
+ byte ch = pstr->data[i];
+
+ if (ch >= char_cache_size)
+ return_error(gs_error_rangecheck);
+ glyph_usage[ch / 8] |= 0x80 >> (ch & 7);
+ }
+ return 0;
+}
+
+/*
+ * Create or find a font resource object for a text.
+ */
+int
+pdf_obtain_font_resource(pdf_text_enum_t *penum,
+ const gs_string *pstr, pdf_font_resource_t **ppdfont)
+{
+ gx_device_pdf *pdev = (gx_device_pdf *)penum->dev;
+ gs_font *font = (gs_font *)penum->current_font;
+ byte *glyph_usage = 0;
+ double *real_widths;
+ int char_cache_size, width_cache_size;
+ int code;
+
+ if (font->FontType == ft_composite) {
+ /* Must not happen, because we always split composite fonts into descendents. */
+ return_error(gs_error_unregistered);
+ }
+ code = pdf_attached_font_resource(pdev, font, ppdfont,
+ &glyph_usage, &real_widths, &char_cache_size, &width_cache_size);
+ /* *ppdfont is NULL if no resource attached. */
+ if (code < 0)
+ return code;
+ if (penum->cgp == NULL) {
+ code = pdf_alloc_text_glyphs_table(pdev, penum, pstr);
+ if (code < 0)
+ return code;
+ code = pdf_make_text_glyphs_table(penum, pstr,
+ glyph_usage, char_cache_size);
+ if (code < 0)
+ return code;
+ }
+ code = pdf_obtain_font_resource_encoded(pdev, font, ppdfont, penum->cgp);
+ if (code < 0)
+ return code;
+ code = pdf_attached_font_resource(pdev, font, ppdfont,
+ &glyph_usage, &real_widths, &char_cache_size, &width_cache_size);
+ if (code < 0)
+ return code;
+ return pdf_mark_text_glyphs((const gs_text_enum_t *)penum, pstr, glyph_usage, char_cache_size);
+}
+
+/*
+ * Create or find a font resource object for a glyphshow text.
+ */
+int
+pdf_obtain_font_resource_unencoded(pdf_text_enum_t *penum,
+ const gs_string *pstr, pdf_font_resource_t **ppdfont, const gs_glyph *gdata)
+{
+ gx_device_pdf *pdev = (gx_device_pdf *)penum->dev;
+ gs_font *font = (gs_font *)penum->current_font;
+ byte *glyph_usage = 0;
+ double *real_widths = 0;
+ int char_cache_size = 0, width_cache_size = 0;
+ int code, ps_encoding_index = -1;
+
+ if (font->FontType == ft_composite) {
+ /* Must not happen, because we always split composite fonts into descendents. */
+ return_error(gs_error_unregistered);
+ }
+ code = pdf_attached_font_resource(pdev, font, ppdfont,
+ &glyph_usage, &real_widths, &char_cache_size, &width_cache_size);
+ if (code < 0)
+ return code;
+ /* *ppdfont is NULL if no resource attached. */
+ if (*ppdfont != NULL)
+ ps_encoding_index = (*ppdfont)->u.simple.preferred_encoding_index;
+ if (penum->cgp == NULL) {
+ code = pdf_alloc_text_glyphs_table(pdev, penum, pstr);
+ if (code < 0)
+ return code;
+ code = pdf_make_text_glyphs_table_unencoded(pdev, penum->cgp, font, pstr, gdata, &ps_encoding_index);
+ if (code < 0)
+ return code;
+ }
+ code = pdf_obtain_font_resource_encoded(pdev, font, ppdfont, penum->cgp);
+ if (code < 0)
+ return code;
+ code = pdf_attached_font_resource(pdev, font, ppdfont,
+ &glyph_usage, &real_widths, &char_cache_size, &width_cache_size);
+ if (code < 0)
+ return code;
+ (*ppdfont)->u.simple.preferred_encoding_index = ps_encoding_index;
+ return pdf_mark_text_glyphs_unencoded((const gs_text_enum_t *)penum,
+ pstr, glyph_usage, char_cache_size);
+}
+
+static inline bool
+strings_equal(const gs_const_string *s1, const gs_const_string *s2)
+{
+ return s1->size == s2->size &&
+ !memcmp(s1->data, s2->data, s1->size);
+}
+
+/*
+ * Create or find a parent Type 0 font resource object for a CID font resource.
+ */
+int
+pdf_obtain_parent_type0_font_resource(gx_device_pdf *pdev, pdf_font_resource_t *pdsubf,
+ uint font_index, const gs_const_string *CMapName, pdf_font_resource_t **pdfont)
+{
+ if (pdsubf->u.cidfont.parent != 0 &&
+ font_index == pdsubf->u.cidfont.parent->u.type0.font_index &&
+ strings_equal(CMapName, &pdsubf->u.cidfont.parent->u.type0.CMapName))
+ *pdfont = pdsubf->u.cidfont.parent;
+ else {
+ /*
+ * PDF spec 1.4 section 5.6 "Composite Fonts" says :
+ *
+ * PDF 1.2 introduces a general architecture for composite fonts that theoretically
+ * allows a Type 0 font to have multiple descendants,which might themselves be
+ * Type 0 fonts.However,in versions up to and including PDF 1.4,only a single
+ * descendant is allowed,which must be a CIDFont (not a font).This restriction
+ * may be relaxed in a future PDF version.
+ */
+
+ if (pdsubf->u.cidfont.parent == NULL ||
+ pdf_find_type0_font_resource(pdev, pdsubf, CMapName, font_index, pdfont) <= 0) {
+ int code = pdf_font_type0_alloc(pdev, pdfont, gs_no_id, pdsubf, CMapName);
+
+ if (code < 0)
+ return code;
+ (*pdfont)->u.type0.font_index = font_index;
+ }
+ pdsubf->u.cidfont.parent = *pdfont;
+ }
+ return 0;
+}
+
+gs_char
+pdf_find_glyph(pdf_font_resource_t *pdfont, gs_glyph glyph)
+{
+ if (pdfont->FontType != ft_user_defined &&
+ pdfont->FontType != ft_PCL_user_defined &&
+ pdfont->FontType != ft_MicroType &&
+ pdfont->FontType != ft_GL2_stick_user_defined &&
+ pdfont->FontType != ft_GL2_531)
+ return GS_NO_CHAR;
+ else {
+ pdf_encoding_element_t *pet = pdfont->u.simple.Encoding;
+ int i, i0 = -1;
+
+ if (pdfont->u.simple.FirstChar > pdfont->u.simple.LastChar)
+ return (gs_char)0;
+ for (i = pdfont->u.simple.FirstChar; i <= pdfont->u.simple.LastChar; i++, pet++) {
+ if (pet->glyph == glyph)
+ return (gs_char)i;
+ if (i0 == -1 && pet->glyph == GS_NO_GLYPH)
+ i0 = i;
+ }
+ if (i0 != -1)
+ return (gs_char)i0;
+ if (i < 256)
+ return (gs_char)i;
+ return GS_NO_CHAR;
+ }
+}
+
+/*
+ * Compute the cached values in the text processing state from the text
+ * parameters, current_font, and pis->ctm. Return either an error code (<
+ * 0) or a mask of operation attributes that the caller must emulate.
+ * Currently the only such attributes are TEXT_ADD_TO_ALL_WIDTHS and
+ * TEXT_ADD_TO_SPACE_WIDTH. Note that this procedure fills in all the
+ * values in ppts->values, not just the ones that need to be set now.
+ */
+static int
+transform_delta_inverse(const gs_point *pdelta, const gs_matrix *pmat,
+ gs_point *ppt)
+{
+ int code = gs_distance_transform_inverse(pdelta->x, pdelta->y, pmat, ppt);
+ gs_point delta;
+
+ if (code < 0)
+ return code;
+ if (ppt->y == 0)
+ return 0;
+ /* Check for numerical fuzz. */
+ code = gs_distance_transform(ppt->x, 0.0, pmat, &delta);
+ if (code < 0)
+ return 0; /* punt */
+ if (fabs(delta.x - pdelta->x) < 0.01 && fabs(delta.y - pdelta->y) < 0.01) {
+ /* Close enough to y == 0: device space error < 0.01 pixel. */
+ ppt->y = 0;
+ }
+ return 0;
+}
+
+float pdf_calculate_text_size(gs_imager_state *pis, pdf_font_resource_t *pdfont,
+ const gs_matrix *pfmat, gs_matrix *smat, gs_matrix *tmat,
+ gs_font *font, gx_device_pdf *pdev)
+{
+ gs_matrix orig_matrix;
+ double
+ sx = pdev->HWResolution[0] / 72.0,
+ sy = pdev->HWResolution[1] / 72.0;
+ float size;
+
+ /* Get the original matrix of the base font. */
+
+ {
+ gs_font_base *cfont = pdf_font_resource_font(pdfont, false);
+
+ if (pdfont->FontType == ft_user_defined ||
+ pdfont->FontType == ft_PCL_user_defined ||
+ pdfont->FontType == ft_MicroType ||
+ pdfont->FontType == ft_GL2_stick_user_defined ||
+ pdfont->FontType == ft_GL2_531)
+ orig_matrix = pdfont->u.simple.s.type3.FontMatrix;
+ else if (cfont != 0) {
+ /*
+ * The text matrix to be computed relatively to the
+ * embedded font matrix.
+ */
+ orig_matrix = cfont->FontMatrix;
+ } else {
+ /*
+ * We don't embed the font.
+ * The text matrix to be computed relatively to
+ * standard font matrix.
+ */
+ pdf_font_orig_matrix(font, &orig_matrix);
+ }
+ }
+
+ /* Compute the scaling matrix and combined matrix. */
+
+ gs_matrix_invert(&orig_matrix, smat);
+ gs_matrix_multiply(smat, pfmat, smat);
+ *tmat = ctm_only(pis);
+ tmat->tx = tmat->ty = 0;
+ gs_matrix_multiply(smat, tmat, tmat);
+
+ /* Try to find a reasonable size value. This isn't necessary, */
+ /* but it's worth a little effort. */
+
+ size = hypot(tmat->yx, tmat->yy) / sy;
+ if (size < 0.01)
+ size = hypot(tmat->xx, tmat->xy) / sx;
+ if (size < 0.01)
+ size = 1;
+
+ return(size);
+}
+
+int
+pdf_update_text_state(pdf_text_process_state_t *ppts,
+ const pdf_text_enum_t *penum,
+ pdf_font_resource_t *pdfont, const gs_matrix *pfmat)
+{
+ gx_device_pdf *const pdev = (gx_device_pdf *)penum->dev;
+ gs_font *font = penum->current_font;
+ gs_fixed_point cpt;
+ gs_matrix smat, tmat;
+ float size;
+ float c_s = 0, w_s = 0;
+ int mask = 0;
+ int code = gx_path_current_point(penum->path, &cpt);
+
+ if (code < 0)
+ return code;
+
+ size = pdf_calculate_text_size(penum->pis, pdfont, pfmat, &smat, &tmat, penum->current_font, pdev);
+ /* Check for spacing parameters we can handle, and transform them. */
+
+ if (penum->text.operation & TEXT_ADD_TO_ALL_WIDTHS) {
+ if (penum->current_font->WMode == 0) {
+ gs_point pt;
+
+ code = transform_delta_inverse(&penum->text.delta_all, &smat, &pt);
+ if (code >= 0 && pt.y == 0)
+ c_s = pt.x * size;
+ else
+ mask |= TEXT_ADD_TO_ALL_WIDTHS;
+ }
+ else
+ mask |= TEXT_ADD_TO_ALL_WIDTHS;
+ }
+
+ if (penum->text.operation & TEXT_ADD_TO_SPACE_WIDTH) {
+ gs_point pt;
+
+ code = transform_delta_inverse(&penum->text.delta_space, &smat, &pt);
+ if (code >= 0 && pt.y == 0 && penum->text.space.s_char == 32)
+ w_s = pt.x * size;
+ else
+ mask |= TEXT_ADD_TO_SPACE_WIDTH;
+ }
+ /* Store the updated values. */
+
+ tmat.xx /= size;
+ tmat.xy /= size;
+ tmat.yx /= size;
+ tmat.yy /= size;
+ tmat.tx += fixed2float(cpt.x);
+ tmat.ty += fixed2float(cpt.y);
+
+ ppts->values.character_spacing = c_s;
+ ppts->values.pdfont = pdfont;
+ ppts->values.size = size;
+ ppts->values.matrix = tmat;
+ ppts->values.render_mode = penum->pis->text_rendering_mode;
+ ppts->values.word_spacing = w_s;
+ ppts->font = font;
+
+ if (font->PaintType == 2 && penum->pis->text_rendering_mode == 0)
+ {
+ gs_imager_state *pis = penum->pis;
+ gs_font *font = penum->current_font;
+ double scaled_width = font->StrokeWidth != 0 ? font->StrokeWidth : 0.001;
+ double saved_width = pis->line_params.half_width;
+ /*
+ * See stream_to_text in gdevpdfu.c re the computation of
+ * the scaling value.
+ */
+ double scale = 72.0 / pdev->HWResolution[1];
+
+ if (font->FontMatrix.yy != 0)
+ scaled_width *= fabs(font->orig_FontMatrix.yy) * size * scale;
+ else
+ scaled_width *= fabs(font->orig_FontMatrix.xy) * size * scale;
+
+ if (tmat.yy != 0)
+ scaled_width *= tmat.yy;
+ else
+ scaled_width *= tmat.xy;
+
+ ppts->values.render_mode = 1;
+
+ /* Sort out any pending glyphs */
+ code = pdf_set_PaintType0_params(pdev, pis, size, scaled_width, &ppts->values);
+ if (code < 0)
+ return code;
+
+ pis->line_params.half_width = scaled_width / 2;
+ code = pdf_set_text_process_state(pdev, (const gs_text_enum_t *)penum,
+ ppts);
+ if (code < 0)
+ return code;
+
+ pis->line_params.half_width = saved_width;
+ } else {
+ code = pdf_set_text_process_state(pdev, (const gs_text_enum_t *)penum,
+ ppts);
+ }
+ return (code < 0 ? code : mask);
+}
+
+/*
+ * Set up commands to make the output state match the processing state.
+ * General graphics state commands are written now; text state commands
+ * are written later.
+ */
+int
+pdf_set_text_process_state(gx_device_pdf *pdev,
+ const gs_text_enum_t *pte, /* for pdcolor, pis */
+ pdf_text_process_state_t *ppts)
+{
+ /*
+ * Setting the stroke parameters may exit text mode, causing the
+ * settings of the text parameters to be lost. Therefore, we set the
+ * stroke parameters first.
+ */
+ if (pdf_render_mode_uses_stroke(pdev, &ppts->values)) {
+ /* Write all the parameters for stroking. */
+ gs_imager_state *pis = pte->pis;
+ float save_width = pis->line_params.half_width;
+ int code;
+
+ if (pdev->context == PDF_IN_STRING) {
+ code = sync_text_state(pdev);
+ if (code < 0)
+ return code;
+ }
+
+ code = pdf_open_contents(pdev, PDF_IN_STREAM);
+ if (code < 0)
+ return code;
+
+ code = pdf_prepare_stroke(pdev, pis);
+ if (code >= 0) {
+ code = gdev_vector_prepare_stroke((gx_device_vector *)pdev,
+ pis, NULL, NULL, 1);
+ if (code < 0)
+ return code;
+ }
+
+ code = pdf_open_contents(pdev, PDF_IN_STRING);
+ if (code < 0)
+ return code;
+
+ pis->line_params.half_width = save_width;
+ if (code < 0)
+ return code;
+ }
+
+ /* Now set all the other parameters. */
+
+ return pdf_set_text_state_values(pdev, &ppts->values);
+}
+
+static int
+store_glyph_width(pdf_glyph_width_t *pwidth, int wmode, const gs_matrix *scale,
+ const gs_glyph_info_t *pinfo)
+{
+ double w, v;
+
+ gs_distance_transform(pinfo->width[wmode].x, pinfo->width[wmode].y, scale, &pwidth->xy);
+ if (wmode)
+ w = pwidth->xy.y, v = pwidth->xy.x;
+ else
+ w = pwidth->xy.x, v = pwidth->xy.y;
+ pwidth->w = w;
+ if (v != 0)
+ return 1;
+ gs_distance_transform(pinfo->v.x, pinfo->v.y, scale, &pwidth->v);
+ return 0;
+}
+
+static int
+get_missing_width(gs_font_base *cfont, int wmode, const gs_matrix *scale_c,
+ pdf_glyph_widths_t *pwidths)
+{
+ gs_font_info_t finfo;
+ int code;
+
+ code = cfont->procs.font_info((gs_font *)cfont, NULL,
+ FONT_INFO_MISSING_WIDTH, &finfo);
+ if (code < 0)
+ return code;
+ if (wmode) {
+ gs_distance_transform(0.0, -finfo.MissingWidth, scale_c, &pwidths->real_width.xy);
+ pwidths->Width.xy.x = 0;
+ pwidths->Width.xy.y = pwidths->real_width.xy.y;
+ pwidths->Width.w = pwidths->real_width.w =
+ pwidths->Width.xy.y;
+ pwidths->Width.v.x = - pwidths->Width.xy.y / 2;
+ pwidths->Width.v.y = - pwidths->Width.xy.y;
+ } else {
+ gs_distance_transform(finfo.MissingWidth, 0.0, scale_c, &pwidths->real_width.xy);
+ pwidths->Width.xy.x = pwidths->real_width.xy.x;
+ pwidths->Width.xy.y = 0;
+ pwidths->Width.w = pwidths->real_width.w =
+ pwidths->Width.xy.x;
+ pwidths->Width.v.x = pwidths->Width.v.y = 0;
+ }
+ /*
+ * Don't mark the width as known, just in case this is an
+ * incrementally defined font.
+ */
+ return 1;
+}
+
+/*
+ * Get the widths (unmodified from the copied font,
+ * and possibly modified from the original font) of a given glyph.
+ * Return 1 if the width was defaulted to MissingWidth.
+ * Return TEXT_PROCESS_CDEVPROC if a CDevProc callout is needed.
+ * cdevproc_result != NULL if we restart after a CDevProc callout.
+ */
+int
+pdf_glyph_widths(pdf_font_resource_t *pdfont, int wmode, gs_glyph glyph,
+ gs_font *orig_font, pdf_glyph_widths_t *pwidths,
+ const double cdevproc_result[10])
+{
+ gs_font_base *cfont = pdf_font_resource_font(pdfont, false);
+ gs_font *ofont = orig_font;
+ gs_glyph_info_t info;
+ gs_matrix scale_c, scale_o;
+ int code, rcode = 0;
+ gs_point v;
+ int allow_cdevproc_callout = (orig_font->FontType == ft_CID_TrueType
+ || orig_font->FontType == ft_CID_encrypted
+ ? GLYPH_INFO_CDEVPROC : 0); /* fixme : allow more font types. */
+
+ if (ofont->FontType == ft_composite)
+ return_error(gs_error_unregistered); /* Must not happen. */
+ code = glyph_orig_matrix((const gs_font *)cfont, glyph, &scale_c);
+ if (code < 0)
+ return code;
+ code = glyph_orig_matrix(ofont, glyph, &scale_o);
+ if (code < 0)
+ return code;
+ gs_matrix_scale(&scale_c, 1000.0, 1000.0, &scale_c);
+ gs_matrix_scale(&scale_o, 1000.0, 1000.0, &scale_o);
+ pwidths->Width.v.x = pwidths->Width.v.y = 0;
+ pwidths->real_width.v.x = pwidths->real_width.v.y = 0;
+ pwidths->real_width.w = pwidths->real_width.xy.x = pwidths->real_width.xy.y = 0;
+ pwidths->replaced_v = false;
+ pwidths->ignore_wmode = false;
+ if (glyph == GS_NO_GLYPH)
+ return get_missing_width(cfont, wmode, &scale_c, pwidths);
+ code = cfont->procs.glyph_info((gs_font *)cfont, glyph, NULL,
+ GLYPH_INFO_WIDTH0 |
+ (GLYPH_INFO_WIDTH0 << wmode) |
+ GLYPH_INFO_OUTLINE_WIDTHS |
+ (GLYPH_INFO_VVECTOR0 << wmode),
+ &info);
+ /* For CID fonts the PDF spec requires the x-component of v-vector
+ to be equal to half glyph width, and AR5 takes it from W, DW.
+ So make a compatibe data here.
+ */
+ if ((code == gs_error_undefined || !(info.members & (GLYPH_INFO_WIDTH0 << wmode)))) {
+ /* If we got an undefined error, and its a type 1/CFF font, try to
+ * find the /.notdef glyph and use its width instead (as this is the
+ * glyph which will be rendered). We don't do this for other font types
+ * as it seems Acrobat/Distiller may not do so either.
+ */
+ if (code == gs_error_undefined && (ofont->FontType == ft_encrypted || ofont->FontType == ft_encrypted2)) {
+ int index;
+ gs_glyph notdef_glyph;
+
+ for (index = 0;
+ (ofont->procs.enumerate_glyph((gs_font *)ofont, &index,
+ (GLYPH_SPACE_NAME), &notdef_glyph)) >= 0 &&
+ index != 0;) {
+ if (gs_font_glyph_is_notdef((gs_font_base *)ofont, notdef_glyph)) {
+ code = ofont->procs.glyph_info((gs_font *)ofont, notdef_glyph, NULL,
+ GLYPH_INFO_WIDTH0 << wmode,
+ &info);
+
+ if (code < 0)
+ return code;
+ code = store_glyph_width(&pwidths->Width, wmode, &scale_c, &info);
+ if (code < 0)
+ return code;
+ rcode |= code;
+ if (info.members & (GLYPH_INFO_VVECTOR0 << wmode))
+ gs_distance_transform(info.v.x, info.v.y, &scale_c, &v);
+ else
+ v.x = v.y = 0;
+ break;
+ }
+ }
+ } else {
+ code = get_missing_width(cfont, wmode, &scale_c, pwidths);
+ if (code < 0)
+ v.y = 0;
+ else
+ v.y = pwidths->Width.v.y;
+ if (wmode) {
+ pdf_glyph_widths_t widths1;
+
+ if (get_missing_width(cfont, 0, &scale_c, &widths1) < 0)
+ v.x = 0;
+ else
+ v.x = widths1.Width.w / 2;
+ } else
+ v.x = pwidths->Width.v.x;
+ }
+ } else if (code < 0)
+ return code;
+ else {
+ code = store_glyph_width(&pwidths->Width, wmode, &scale_c, &info);
+ if (code < 0)
+ return code;
+ rcode |= code;
+ if (info.members & (GLYPH_INFO_VVECTOR0 << wmode))
+ gs_distance_transform(info.v.x, info.v.y, &scale_c, &v);
+ else
+ v.x = v.y = 0;
+ if (wmode && pdf_is_CID_font(ofont)) {
+ if (info.members & (GLYPH_INFO_WIDTH0 << wmode)) {
+ gs_point xy;
+
+ gs_distance_transform(info.width[0].x, info.width[0].y, &scale_c, &xy);
+ v.x = xy.x / 2;
+ } else {
+ pdf_glyph_widths_t widths1;
+
+ if (get_missing_width(cfont, 0, &scale_c, &widths1) < 0)
+ v.x = 0;
+ else
+ v.x = widths1.Width.w / 2;
+ }
+ }
+ }
+ pwidths->Width.v = v;
+ if (code > 0 && !pdf_is_CID_font(ofont))
+ pwidths->Width.xy.x = pwidths->Width.xy.y = pwidths->Width.w = 0;
+ if (cdevproc_result == NULL) {
+ info.members = 0;
+ code = ofont->procs.glyph_info(ofont, glyph, NULL,
+ (GLYPH_INFO_WIDTH0 << wmode) |
+ (GLYPH_INFO_VVECTOR0 << wmode) |
+ allow_cdevproc_callout,
+ &info);
+ /* fixme : Move this call before cfont->procs.glyph_info. */
+ if (info.members & GLYPH_INFO_CDEVPROC) {
+ if (allow_cdevproc_callout)
+ return TEXT_PROCESS_CDEVPROC;
+ else
+ return_error(gs_error_rangecheck);
+ }
+ } else {
+ info.width[0].x = cdevproc_result[0];
+ info.width[0].y = cdevproc_result[1];
+ info.width[1].x = cdevproc_result[6];
+ info.width[1].y = cdevproc_result[7];
+ info.v.x = (wmode ? cdevproc_result[8] : 0);
+ info.v.y = (wmode ? cdevproc_result[9] : 0);
+ info.members = (GLYPH_INFO_WIDTH0 << wmode) |
+ (wmode ? GLYPH_INFO_VVECTOR1 : 0);
+ code = 0;
+ }
+ if (code == gs_error_undefined || !(info.members & (GLYPH_INFO_WIDTH0 << wmode)))
+ pwidths->real_width = pwidths->Width;
+ else if (code < 0)
+ return code;
+ else {
+ if ((info.members & (GLYPH_INFO_VVECTOR0 | GLYPH_INFO_VVECTOR1)) != 0) {
+ pwidths->replaced_v = true;
+ if ((info.members & GLYPH_INFO_VVECTOR1) == 0 && wmode == 1)
+ pwidths->ignore_wmode = true;
+ }
+ else
+ info.v.x = info.v.y = 0;
+ code = store_glyph_width(&pwidths->real_width, wmode, &scale_o, &info);
+ if (code < 0)
+ return code;
+ rcode |= code;
+ gs_distance_transform(info.v.x, info.v.y, &scale_o, &pwidths->real_width.v);
+ }
+ return rcode;
+}
+
+static int
+pdf_choose_output_char_code(gx_device_pdf *pdev, pdf_text_enum_t *penum, gs_char *pch)
+{
+ gs_char ch;
+ gs_font *font = penum->current_font;
+
+ if (penum->text.operation & TEXT_FROM_SINGLE_GLYPH) {
+ byte buf[1];
+ int char_code_length;
+ gs_glyph glyph = penum->text.data.d_glyph;
+ int code = pdf_encode_glyph((gs_font_base *)font, glyph,
+ buf, sizeof(buf), &char_code_length);
+
+ if (code < 0) {
+ /* Must not happen, becuse pdf_encode_glyph was passed in process_plain_text.*/
+ ch = GS_NO_CHAR;
+ } else if (char_code_length != 1) {
+ /* Must not happen with type 3 fonts.*/
+ ch = GS_NO_CHAR;
+ } else
+ ch = buf[0];
+ } else if (penum->orig_font->FontType == ft_composite) {
+ gs_font_type0 *font0 = (gs_font_type0 *)penum->orig_font;
+ gs_glyph glyph = penum->returned.current_glyph;
+
+ if (font0->data.FMapType == fmap_CMap) {
+ pdf_font_resource_t *pdfont;
+ int code = pdf_attached_font_resource(pdev, font, &pdfont, NULL, NULL, NULL, NULL);
+
+ if (code < 0)
+ return code;
+ ch = pdf_find_glyph(pdfont, glyph);
+ } else
+ ch = penum->returned.current_char;
+ } else {
+ ch = penum->returned.current_char;
+ /* Keep for records : glyph = font->procs.encode_char(font, ch, GLYPH_SPACE_NAME); */
+ /*
+ * If glyph == GS_NO_GLYPH, we should replace it with
+ * a notdef glyph, but we don't know how to do with Type 3 fonts.
+ */
+ }
+ *pch = ch;
+ return 0;
+}
+
+static int
+pdf_choose_output_glyph_hame(gx_device_pdf *pdev, pdf_text_enum_t *penum, gs_const_string *gnstr, gs_glyph glyph)
+{
+ if (penum->orig_font->FontType == ft_composite || penum->orig_font->procs.glyph_name(penum->orig_font, glyph, gnstr) < 0) {
+ /* couldn't find a glyph name, so make one up! This can happen if we are handling PCL and the glyph
+ * (character code) is less than 29, the PCL glyph names start with /.notdef at 29. We also need to
+ * do this for composite fonts.
+ */
+ char buf[6];
+ byte *p;
+
+ gnstr->size = 5;
+ p = (byte *)gs_alloc_string(pdev->pdf_memory, gnstr->size, "pdf_text_set_cache");
+ if (p == NULL)
+ return_error(gs_error_VMerror);
+ gs_sprintf(buf, "g%04x", (unsigned int)(glyph & 0xFFFF));
+ memcpy(p, buf, 5);
+ gnstr->data = p;
+ }
+ return 0;
+}
+
+/* ---------------- Main entry ---------------- */
+
+/*
+ * Fall back to the default text processing code when needed.
+ */
+int
+pdf_default_text_begin(gs_text_enum_t *pte, const gs_text_params_t *text,
+ gs_text_enum_t **ppte)
+{
+ gs_text_params_t text1 = *text;
+
+ if(pte->current_font->FontType == 3 && (text1.operation & TEXT_DO_NONE)) {
+ /* We need a real drawing to accumulate charproc. */
+ text1.operation &= ~TEXT_DO_NONE;
+ text1.operation |= TEXT_DO_DRAW;
+ }
+ return gx_default_text_begin(pte->dev, pte->pis, &text1, pte->current_font,
+ pte->path, pte->pdcolor, pte->pcpath,
+ pte->memory, ppte);
+}
+
+static int install_PS_charproc_accumulator(gx_device_pdf *pdev, gs_text_enum_t *pte,
+ gs_text_enum_t *pte_default, pdf_text_enum_t *const penum)
+{
+ int code;
+
+ penum->returned.current_char = pte_default->returned.current_char;
+ penum->returned.current_glyph = pte_default->returned.current_glyph;
+ pdev->charproc_ctm = penum->pis->ctm;
+ if (penum->current_font->FontType == ft_user_defined &&
+ penum->outer_CID == GS_NO_GLYPH &&
+ !(penum->pte_default->text.operation & TEXT_DO_CHARWIDTH)) {
+ /* The condition above must be consistent with one in pdf_text_set_cache,
+ which decides to apply pdf_set_charproc_attrs. */
+ gs_matrix m;
+ pdf_font_resource_t *pdfont;
+
+ code = pdf_start_charproc_accum(pdev);
+ if (code < 0)
+ return code;
+
+ /* We need to give FreeType some room for accuracy when
+ * retrieving the outline. We will use a scale factor of 100
+ * (see below). Because this appears to the regular path
+ * handling code as if it were at the page level, the co-ords
+ * would be clipped frequently, so we temporarily hack the
+ * width and height of the device here to ensure it doesn't.
+ * Note that this requires some careful manipulation of the
+ * CTM in various places, which want numbers in the font
+ * co-ordinate space, not the scaled user space.
+ */
+ pdev->width *= 100;
+ pdev->height *= 100;
+
+ pdf_viewer_state_from_imager_state(pdev, pte->pis, pte->pdcolor);
+ /* Set line params to unallowed values so that
+ they'll synchronize with writing them out on the first use.
+ Doing so because PDF viewer inherits them from the
+ contents stream when executing the charproc,
+ but at this moment we don't know in what contexts
+ it will be used. */
+ pdev->state.line_params.half_width = -1;
+ pdev->state.line_params.start_cap = gs_cap_unknown;
+ pdev->state.line_params.end_cap = gs_cap_unknown;
+ pdev->state.line_params.dash_cap = gs_cap_unknown;
+ pdev->state.line_params.join = gs_join_unknown;
+ pdev->state.line_params.miter_limit = -1;
+ pdev->state.line_params.dash.pattern_size = -1;
+ /* Must set an identity CTM for the charproc accumulation.
+ The function show_proceed (called from gs_text_process above)
+ executed gsave, so we are safe to change CTM now.
+ Note that BuildChar may change CTM before calling setcachedevice. */
+ gs_make_identity(&m);
+ /* See comment above, we actually want to use a scale factor
+ * of 100 in order to give FreeType some room in the fixed
+ * precision calculations when retrieing the outline. So in
+ * fact we don't use the identity CTM, but a 100x100 matrix
+ * Originally tried 1000, but that was too likely to cause
+ * clipping or arithmetic overflow.
+ */
+ gs_matrix_scale(&m, 100, 100, &m);
+ gs_matrix_fixed_from_matrix(&penum->pis->ctm, &m);
+
+ /* Choose a character code to use with the charproc. */
+ code = pdf_choose_output_char_code(pdev, penum, &penum->output_char_code);
+ if (code < 0)
+ return code;
+ code = pdf_attached_font_resource(pdev, penum->current_font, &pdfont, NULL, NULL, NULL, NULL);
+ if (code < 0)
+ return code;
+ pdev->font3 = (pdf_resource_t *)pdfont;
+ pdev->substream_Resources = pdfont->u.simple.s.type3.Resources;
+ penum->charproc_accum = true;
+ pdev->accumulating_charproc = true;
+ pdev->charproc_BBox.p.x = 10000;
+ pdev->charproc_BBox.p.y = 10000;
+ pdev->charproc_BBox.q.x = 0;
+ pdev->charproc_BBox.q.y = 0;
+ return TEXT_PROCESS_RENDER;
+ }
+ return 0;
+}
+
+static int install_charproc_accumulator(gx_device_pdf *pdev, gs_text_enum_t *pte,
+ gs_text_enum_t *pte_default, pdf_text_enum_t *const penum)
+{
+ int code;
+
+ pdev->charproc_ctm = penum->pis->ctm;
+ if ((penum->current_font->FontType == ft_user_defined ||
+ penum->current_font->FontType == ft_PCL_user_defined ||
+ penum->current_font->FontType == ft_MicroType ||
+ penum->current_font->FontType == ft_GL2_stick_user_defined ||
+ penum->current_font->FontType == ft_GL2_531) &&
+ penum->outer_CID == GS_NO_GLYPH &&
+ !(penum->pte_default->text.operation & TEXT_DO_CHARWIDTH)) {
+ /* The condition above must be consistent with one in pdf_text_set_cache,
+ which decides to apply pdf_set_charproc_attrs. */
+ gs_matrix m;
+ pdf_font_resource_t *pdfont;
+
+ pdev->PS_accumulator = false;
+ code = pdf_start_charproc_accum(pdev);
+ if (code < 0)
+ return code;
+
+ pdf_viewer_state_from_imager_state(pdev, pte->pis, pte->pdcolor);
+ /* Set line params to unallowed values so that
+ they'll synchronize with writing them out on the first use.
+ Doing so because PDF viewer inherits them from the
+ contents stream when executing the charproc,
+ but at this moment we don't know in what contexts
+ it will be used. */
+ pdev->state.line_params.half_width = -1;
+ pdev->state.line_params.start_cap = gs_cap_unknown;
+ pdev->state.line_params.end_cap = gs_cap_unknown;
+ pdev->state.line_params.dash_cap = gs_cap_unknown;
+ pdev->state.line_params.join = gs_join_unknown;
+ pdev->state.line_params.miter_limit = -1;
+ pdev->state.line_params.dash.pattern_size = -1;
+ /* Must set an identity CTM for the charproc accumulation.
+ The function show_proceed (called from gs_text_process above)
+ executed gsave, so we are safe to change CTM now.
+ Note that BuildChar may change CTM before calling setcachedevice. */
+ gs_make_identity(&m);
+ gs_matrix_fixed_from_matrix(&penum->pis->ctm, &m);
+
+ /* Choose a character code to use with the charproc. */
+ code = pdf_choose_output_char_code(pdev, penum, &penum->output_char_code);
+ if (code < 0)
+ return code;
+ code = pdf_attached_font_resource(pdev, penum->current_font, &pdfont, NULL, NULL, NULL, NULL);
+ if (code < 0)
+ return code;
+ pdev->font3 = (pdf_resource_t *)pdfont;
+ pdev->substream_Resources = pdfont->u.simple.s.type3.Resources;
+ penum->charproc_accum = true;
+ pdev->accumulating_charproc = true;
+ return TEXT_PROCESS_RENDER;
+ }
+ return 0;
+}
+
+static int complete_charproc(gx_device_pdf *pdev, gs_text_enum_t *pte,
+ gs_text_enum_t *pte_default, pdf_text_enum_t *const penum,
+ bool was_PS_type3)
+{
+ gs_const_string gnstr;
+ int code;
+
+ if (pte_default->returned.current_glyph == GS_NO_GLYPH)
+ return gs_error_undefined;
+ code = pdf_choose_output_glyph_hame(pdev, penum, &gnstr, pte_default->returned.current_glyph);
+ if (code < 0) {
+ return code;
+ }
+
+ if ((penum->current_font->FontType == ft_user_defined ||
+ penum->current_font->FontType == ft_PCL_user_defined ||
+ penum->current_font->FontType == ft_MicroType ||
+ penum->current_font->FontType == ft_GL2_stick_user_defined ||
+ penum->current_font->FontType == ft_GL2_531) &&
+ stell(pdev->strm) == 0)
+ {
+ char glyph[256], FontName[gs_font_name_max + 1], KeyName[256];
+ int len;
+
+ len = min(gs_font_name_max, gnstr.size);
+ memcpy(glyph, gnstr.data, len);
+ glyph[len] = 0x00;
+ len = min(gs_font_name_max, penum->current_font->font_name.size);
+ memcpy(FontName, penum->current_font->font_name.chars, len);
+ FontName[len] = 0x00;
+ len = min(gs_font_name_max, penum->current_font->key_name.size);
+ memcpy(KeyName, penum->current_font->key_name.chars, len);
+ KeyName[len] = 0x00;
+
+ emprintf4(pdev->memory,
+ "ERROR: Page %d used undefined glyph '%s' from type 3 font '%s', key '%s'\n",
+ pdev->next_page, glyph, FontName, KeyName);
+ stream_puts(pdev->strm, "0 0 0 0 0 0 d1\n");
+ }
+
+ if (was_PS_type3) {
+ /* See below, we scaled the device height and width to prevent
+ * clipping of the CharProc operations, now we need to undo that.
+ */
+ pdev->width /= 100;
+ pdev->height /= 100;
+ }
+ code = pdf_end_charproc_accum(pdev, penum->current_font, penum->cgp,
+ pte_default->returned.current_glyph, penum->output_char_code, &gnstr);
+ if (code < 0)
+ return code;
+ pdev->accumulating_charproc = false;
+ penum->charproc_accum = false;
+ code = gx_default_text_restore_state(pte_default);
+ if (code < 0)
+ return code;
+ gs_text_release(pte_default, "pdf_text_process");
+ penum->pte_default = 0;
+
+ return 0;
+}
+
+/* Nasty hackery. The PCL 'stick font' is drawn by constructing a path, and then stroking it.
+ * The stroke width is calculated under the influence of the device default matrix, which
+ * ensures it is the same, no matter what size the font is drawn at. Of course we are
+ * capturing the glyph under an identity matrix, so using the device matrix blows it up.
+ * What we do is replace the get_initial_matrix method for the course of the charproc
+ * accumulation *only*, and return a more sensible 'device' matrix.
+ * We know that the Charproc CTM is a combination of the font point size, and the default device
+ * matrix, so we just take off the default matrix, and invert the font scaling.
+ * This will scale the linewidth nicely for us. It does mean that if we use the
+ * same font at a different size, then the width will not be 'correct', but I
+ * think this is good enough.
+ */
+static void pdf_type3_get_initial_matrix(gx_device * dev, gs_matrix * pmat)
+{
+ gx_device_pdf *pdev = (gx_device_pdf *)dev;
+
+ pmat->xx = pdev->charproc_ctm.xx;
+ pmat->xy = pdev->charproc_ctm.xy;
+ pmat->yx = pdev->charproc_ctm.yx;
+ pmat->yy = pdev->charproc_ctm.yy;
+ pmat->tx = 0;
+ pmat->ty = 0;
+ gs_matrix_invert(pmat, pmat);
+ gs_matrix_scale(pmat, pdev->HWResolution[0] / 72.0, pdev->HWResolution[0] / 72.0, pmat);
+}
+
+static int pdf_query_purge_cached_char(const gs_memory_t *mem, cached_char *cc, void *data)
+{
+ cached_char *to_purge = (cached_char *)data;
+
+
+ if (cc->code == to_purge->code && cc_pair(cc) == cc_pair(to_purge) &&
+ cc->subpix_origin.x == to_purge->subpix_origin.x &&
+ cc->subpix_origin.y == to_purge->subpix_origin.y &&
+ cc->wmode == to_purge->wmode && cc_depth(cc) == cc_depth(to_purge)
+ )
+ return 1;
+ return 0;
+}
+
+/*
+ * Continue processing text. This is the 'process' procedure in the text
+ * enumerator. Per the check in pdf_text_begin, we know the operation is
+ * not a charpath, but it could be anything else.
+ */
+int
+pdf_text_process(gs_text_enum_t *pte)
+{
+ pdf_text_enum_t *const penum = (pdf_text_enum_t *)pte;
+ uint operation = pte->text.operation;
+ uint size = pte->text.size - pte->index;
+ gs_text_enum_t *pte_default;
+ PROCESS_TEXT_PROC((*process));
+ int code, early_accumulator = 0;
+ gx_device_pdf *pdev = (gx_device_pdf *)penum->dev;
+#define BUF_SIZE 100 /* arbitrary > 0 */
+ /* Use a union to ensure alignment. */
+ union bu_ {
+ byte bytes[BUF_SIZE];
+ gs_char chars[BUF_SIZE / sizeof(gs_char)];
+ gs_glyph glyphs[BUF_SIZE / sizeof(gs_glyph)];
+ } buf;
+
+ if (!penum->pte_default) {
+ /* Don't need to sync before exiting charproc. */
+ code = pdf_prepare_text_drawing(pdev, pte);
+ if (code == gs_error_rangecheck) {
+ /* Fallback to the default implermentation for handling
+ a transparency with CompatibilityLevel<=1.3 . */
+ goto default_impl;
+ }
+ if (code < 0)
+ return code;
+ }
+ if (!penum->pte_default) {
+ pdev->charproc_just_accumulated = false;
+ if (penum->cdevproc_callout) {
+ /* Restore after TEXT_PROCESS_CDEVPROC in scan_cmap_text. */
+ penum->current_font = penum->orig_font;
+ }
+ }
+ if ((penum->current_font->FontType == ft_user_defined ||
+ penum->current_font->FontType == ft_PCL_user_defined ||
+ penum->current_font->FontType == ft_MicroType ||
+ penum->current_font->FontType == ft_GL2_stick_user_defined ||
+ penum->current_font->FontType == ft_GL2_531) &&
+ (penum->text.operation & TEXT_DO_ANY_CHARPATH)
+ && !pdev->type3charpath) {
+ pdev->type3charpath = true;
+ if (!penum->charproc_accum)
+ goto default_impl;
+ }
+
+ code = -1; /* to force default implementation */
+
+ /*
+ * If we fell back to the default implementation, continue using it.
+ */
+ top:
+ pte_default = penum->pte_default;
+ /* pte_default is a signal that we can't handle the glyph in the current
+ * font 'natively'. This means either a type 3 font (for PostScript we
+ * need to return to the PS interpreter to run the BuildChar), or a font
+ * type we can't or don't support in PDF. In the latter case we have the
+ * BuildChar render the glyph and store it in the cache, spot the usage
+ * in the pdfwrite image routine and we store the resluting bitmap in a
+ * Type 3 font. NB if the glyph is uncached then it just ends up in the
+ * output as a bitmap image.
+ */
+ if (pte_default) {
+ gs_char *cdata;
+
+ cdata = (gs_char *)pte->text.data.chars;
+ if (penum->charproc_accum) {
+ /* We've been accumulating a Type 3 CharProc, so now we need to
+ * finish off and store it in the font.
+ */
+ code = complete_charproc(pdev, pte, pte_default, penum, true);
+ if (code < 0)
+ return code;
+ if (!pdev->type3charpath)
+ goto top;
+ else
+ goto default_impl;
+ }
+
+ /* Sepcial checks for PCL bitmap fonts, or the HP/GL-2 stick font */
+ if (penum->current_font->FontType == ft_PCL_user_defined
+ && operation & TEXT_FROM_CHARS && !pdev->type3charpath &&
+ cdata[pte->index] <= 255) {
+ /* We can only get to this point with the character already in use
+ * if we detected in process_text_return_width that the glyph had
+ * been flushed from teh cache. If this happens with PCL bitmap
+ * fonts then we assume that the 'slot' has been reused. In this
+ * case we cannot add the glyph to the current font. By not
+ * capturing at this level the bitmap will still be added to a
+ * type 3 font, but a new one. Best we can do as PS/PDF do not
+ * allow redefinition of fonts.
+ */
+ pdf_font_resource_t *pdfont;
+
+ code = pdf_attached_font_resource(pdev, (gs_font *)penum->current_font, &pdfont, NULL, NULL, NULL, NULL);
+ if (code < 0)
+ return code;
+
+ if (pdfont->u.simple.s.type3.cached[cdata[pte->index] >> 3] & (0x80 >> (cdata[pte->index] & 7)))
+ early_accumulator = 0;
+ else
+ early_accumulator = 1;
+ }
+ if ((penum->current_font->FontType == ft_GL2_stick_user_defined ||
+ penum->current_font->FontType == ft_GL2_531 || penum->current_font->FontType == ft_MicroType)
+ && operation & TEXT_FROM_CHARS && !pdev->type3charpath) {
+ /* Check for anamorphic sacling, we msut not cache stick font
+ * in this case, as the stroke width will vary with the scaling.
+ */
+
+ /* To test the scaling we'll map two unit vectors, calculate the
+ * length of the transformed vectors and see if they are the same
+ * x' = ax + cy + Tx
+ * y' = bx + dy + Ty
+ * We set Tx and Ty to zero (we only care about scale not translation)
+ * We then test 0,0->0,1 and 0,0 -> 1,0. Because one term is always 0
+ * this means we can simplify the math.
+ */
+ /* Vector 0,0 -> 0,1 x is always 0, Tx and Ty are 0 =>
+ * x' = cy = c
+ * y' = dy = d
+ */
+ /* Vector 0,0 -> 1,0 y is always 0, Tx and Ty are 0 =>
+ * x' = ax = a
+ * y' = bx = b
+ */
+ /* Length of hypotenuse squared = sum of squares We don't care
+ * about the length's actual magnitude, so we can compare the
+ * squared length
+ */
+ float x0y1, x1y0;
+
+ x0y1 = (penum->pis->ctm.yx * penum->pis->ctm.yx) +
+ (penum->pis->ctm.yy * penum->pis->ctm.yy);
+ x1y0 = (penum->pis->ctm.xx * penum->pis->ctm.xx) +
+ (penum->pis->ctm.xy * penum->pis->ctm.xy);
+
+ if (x0y1 == x1y0)
+ early_accumulator = 1;
+ }
+ if (early_accumulator) {
+ if (cdata[pte->index] <= 255) {
+ /* The enumerator is created in pdf_default_text_begin and *is*
+ * a show enumerator, so this is safe. We need to update the
+ * graphics state pointer below which is why we need this.
+ */
+ gs_show_enum psenum = *(gs_show_enum *)pte_default;
+ gs_state *pgs = (gs_state *)penum->pis;
+ gs_text_enum_procs_t special_procs = *pte_default->procs;
+ void (*save_proc)(gx_device *, gs_matrix *) = pdev->procs.get_initial_matrix;
+ extern_st(st_gs_show_enum);
+ gs_matrix m, savem;
+
+ special_procs.set_cache = pdf_text_set_cache;
+ pte_default->procs = &special_procs;
+
+ {
+ /* We should not come here if we already have a cached character (except for the special case
+ * of redefined characters in PCL downloaded fonts). If we do, it means that we are doing
+ * 'page bursting', ie each page is going to a separate file, and the cache has an entry
+ * from previous pages, but the PDF font resource doesn't. So empty the cached character
+ * from the cache, to ensure the character description gets executed.
+ */
+ gs_font *rfont = (pte->fstack.depth < 0 ? pte->current_font : pte->fstack.items[0].font);
+ gs_font *pfont = (pte->fstack.depth < 0 ? pte->current_font :
+ pte->fstack.items[pte->fstack.depth].font);
+ int wmode = rfont->WMode;
+ gs_log2_scale_point log2_scale = {0,0};
+ gs_fixed_point subpix_origin = {0,0};
+ cached_fm_pair *pair;
+ cached_char *cc;
+
+ code = gx_lookup_fm_pair(pfont, &ctm_only(pte->pis), &log2_scale, false, &pair);
+ if (code < 0)
+ return code;
+ cc = gx_lookup_cached_char(pfont, pair, pte_default->text.data.chars[pte_default->index], wmode, 1, &subpix_origin);
+ if (cc != 0) {
+ gx_purge_selected_cached_chars(pfont->dir, pdf_query_purge_cached_char, (void *)cc);
+ }
+ }
+ /* charproc completion will restore a gstate */
+ gs_gsave(pgs);
+ /* Assigning the imager state pointer to the graphics state pointer
+ * makes sure that when we make the CTM into the identity it
+ * affects both.
+ */
+ psenum.pgs = (gs_state *)psenum.pis;
+ /* Save the current FontMatrix */
+ savem = pgs->font->FontMatrix;
+ /* Make the FontMatrix the identity otherwise we will apply
+ * it twice, once in the glyph description and again in the
+ * text matrix.
+ */
+ gs_make_identity(&m);
+ pgs->font->FontMatrix = m;
+
+ pgs->char_tm_valid = 0;
+ pgs->char_tm.txy_fixed_valid = 0;
+ pgs->current_point.x = pgs->current_point.y = 0;
+ pgs->char_tm.txy_fixed_valid = 0;
+
+ pte_default->returned.current_char = penum->returned.current_char =
+ pte->text.data.chars[pte->index];
+
+ code = install_charproc_accumulator(pdev, pte, pte_default, penum);
+ if (code < 0)
+ return code;
+
+ /* We can't capture more than one glyph at a time, so amek this one
+ * otherwise the gs_text_process would try to render all the glyphs
+ * in the string.
+ */
+ if (pte->text.size != 1)
+ pte_default->text.size = pte->index + 1;
+ /* We need a special 'initial matrix' method for stick fonts,
+ * See pdf_type3_get_initial_matrix above.
+ */
+ pdev->procs.get_initial_matrix = pdf_type3_get_initial_matrix;
+
+ pdev->pte = pte_default; /* CAUTION: See comment in gdevpdfx.h . */
+ code = gs_text_process(pte_default);
+ if (code < 0)
+ return code;
+ pdev->pte = NULL; /* CAUTION: See comment in gdevpdfx.h . */
+ pdev->charproc_just_accumulated = false;
+
+ /* Restore the saved FontMatrix, so that it gets applied
+ * as part of the text matrix
+ */
+ pgs->font->FontMatrix = savem;
+ /* Restore the default 'initial matrix' mathod. */
+ pdev->procs.get_initial_matrix = save_proc;
+ penum->returned.current_char = pte_default->returned.current_char;
+ penum->returned.current_glyph = pte_default->returned.current_glyph;
+ code = pdf_choose_output_char_code(pdev, penum, &penum->output_char_code);
+ if (code < 0)
+ return code;
+
+ /* complete_charproc will release the text enumerator 'pte_default', we assume
+ * that we are holding the only reference to it. Note that complete_charproc
+ * also sets penum->pte_default to 0, so that we don't re-entre this section of
+ * code but process the text normally, now that we have captured the glyph description.
+ */
+ code = complete_charproc(pdev, pte, pte_default, penum, false);
+ if (penum->current_font->FontType == ft_PCL_user_defined)
+ {
+ /* PCL bitmap fonts can have glyphs 'reused'. We can't handle
+ * handle this in a single PDF font, so we need to know when
+ * it has happened and fall back to the 'default' implementation.
+ * In order to do this, we create a cached character representation
+ * and add it to the cache. If the glyph is reused then the
+ * act of redefining it will remove this entry from the cache.
+ * We check if the glyph is in the cache in process_text_return_width
+ * and come back into this code. In the test above for PCL bitmap
+ * fonts we also check to see if the PDF font already has a
+ *'used' entry. If it does we know that its a redefinition, and
+ * we don't try to capture the glyph.
+ */
+ cached_char *cc;
+ cached_fm_pair *pair;
+ gs_log2_scale_point log2_scale = {0,0};
+ gs_font *rfont = (pte->fstack.depth < 0 ? pte->current_font : pte->fstack.items[0].font);
+ int wmode = rfont->WMode;
+ pdf_font_resource_t *pdfont;
+
+ /* This code copied from show_cache_setup */
+ gs_memory_t *mem = penum->memory;
+ gx_device_memory *dev =
+ gs_alloc_struct(mem, gx_device_memory, &st_device_memory,
+ "show_cache_setup(dev_cache)");
+
+ if (dev == 0) {
+ gs_free_object(mem, dev, "show_cache_setup(dev_cache)");
+ return_error(gs_error_VMerror);
+ }
+ gs_make_mem_mono_device(dev, mem, penum->dev);
+ dev->HWResolution[0] = pgs->device->HWResolution[0];
+ dev->HWResolution[1] = pgs->device->HWResolution[1];
+ /* Retain these devices. */
+ gx_device_retain((gx_device *)dev, true);
+ /* end of code copied from show_cache_setup */
+
+ /* This copied from set_cache */
+ code = gx_alloc_char_bits(pte->current_font->dir, dev, NULL,
+ 0, 0, &log2_scale, 1, &cc);
+ if (code < 0)
+ return code;
+
+ /* The following code is copied from show_update, case sws_cache */
+ code = gx_lookup_fm_pair(pte->current_font, &ctm_only(pte->pis),
+ &log2_scale, false, &pair);
+ if (code < 0)
+ return code;
+
+ /* These members of the cached character are required
+ * in order to find the cache entry later
+ */
+ cc->code = penum->returned.current_char;
+ cc->wmode = wmode;
+ cc->subpix_origin.x = cc->subpix_origin.y = 0;
+ log2_scale.x = log2_scale.y = 0;
+ code = gx_add_cached_char(pte->current_font->dir, dev,
+ cc, pair, &log2_scale);
+ if (code < 0)
+ return code;
+ /* End of code from show_update */
+
+ /* Finally, dispose of the device, which we don't actually need */
+ gx_device_retain((gx_device *)dev, false);
+
+ /* Its possible for a bitmapped PCL font to not execute setcachedevice (invalid empty glyph)
+ * which means that the pdfwrite override doesn't see it, and we don't note that the
+ * glyph is 'cached' in the PDF font resource, but the 'used' flag does get set.
+ * This causes our detection some problems, so mark it 'cached' here to make sure.
+ */
+ code = pdf_attached_font_resource(pdev, (gs_font *)penum->current_font, &pdfont, NULL, NULL, NULL, NULL);
+ if (code < 0)
+ return code;
+
+ pdfont->u.simple.s.type3.cached[cdata[pte->index] >> 3] |= 0x80 >> (cdata[pte->index] & 7);
+ }
+ size = pte->text.size - pte->index;
+ if (code < 0)
+ return code;
+ if (!pdev->type3charpath)
+ goto top;
+ else
+ goto default_impl;
+ }
+ }
+
+ /* Now we run the default text procedure to see what happens.
+ * PCL bitmap fonts and the HP/GL-2 stick font are handled above.
+ * If its a PostScript type 3 font (and some other kinds)
+ * then it will return TEXT_PROCESS_RENDER and we need to exit
+ * to the interpreter to run the glyph description. Otherwise we just
+ * run the BuildChar, and later capture the cached bitmap for incorporation
+ * into a type 3 font. Note that sometimes we will handle PCL or HP/GL-2
+ * fonts by not capturing them above, and they will come through here.
+ * The stick font will not be captured as a font at all, just vectors
+ * stored into the content stream (for osme kinds of scale/shear),
+ * bitmap fonts will be captured in the pdfwrite image routine. These
+ * are added to a type 3 font in the same way as glyphs which need to
+ * be rendered.
+ */
+ {
+ gs_show_enum *penum = (gs_show_enum *)pte_default;
+ int save_can_cache = penum->can_cache;
+ const gs_color_space *pcs;
+ const gs_client_color *pcc;
+ gs_imager_state * pis = pte->pis;
+ gs_state *pgs = (gs_state *)pis;
+
+ /* If we are using a high level pattern, we must not use the cached character, as the
+ * cache hasn't seen the pattern. So set can_cache to < 0, which prevents us consulting
+ * the cache, or storing the result in it.
+ */
+ if (gx_hld_get_color_space_and_ccolor(pis, (const gx_drawing_color *)pgs->color[0].dev_color, &pcs, &pcc) == pattern_color_space)
+ penum->can_cache = -1;
+ pdev->pte = pte_default; /* CAUTION: See comment in gdevpdfx.h . */
+ code = gs_text_process(pte_default);
+
+ penum->can_cache = save_can_cache;
+
+ pdev->pte = NULL; /* CAUTION: See comment in gdevpdfx.h . */
+ pdev->charproc_just_accumulated = false;
+ }
+
+ /* If the BuildChar returned TEXT_PROCESS_RENDER then we want to try and
+ * capture this as a type 3 font, so start the accumulator now. Note
+ * that the setcachedevice handling can still abort this later. In which
+ * case we get the rendered bitmap, just as for non type 3 fonts.
+ */
+ if (code == TEXT_PROCESS_RENDER && !pdev->type3charpath) {
+ int code1;
+
+ code1 = install_PS_charproc_accumulator(pdev, pte, pte_default, penum);
+ if (code1 != 0)
+ return code1;
+ }
+
+ gs_text_enum_copy_dynamic(pte, pte_default, true);
+ if (code)
+ return code;
+ gs_text_release(pte_default, "pdf_text_process");
+ penum->pte_default = 0;
+ if (pdev->type3charpath)
+ pdev->type3charpath = false;
+ return 0;
+ }
+ {
+ gs_font *font = pte->orig_font; /* Not sure. Changed for CDevProc callout. Was pte->current_font */
+
+ switch (font->FontType) {
+ case ft_CID_encrypted:
+ case ft_CID_TrueType:
+ process = process_cid_text;
+ break;
+ case ft_encrypted:
+ case ft_encrypted2:
+ case ft_TrueType:
+ case ft_user_defined:
+ case ft_PCL_user_defined:
+ case ft_MicroType:
+ case ft_GL2_stick_user_defined:
+ case ft_GL2_531:
+ /* The data may be either glyphs or characters. */
+ process = process_plain_text;
+ break;
+ case ft_composite:
+ process =
+ (((gs_font_type0 *)font)->data.FMapType == fmap_CMap ?
+ process_cmap_text :
+ process_composite_text);
+ break;
+ default:
+ goto skip;
+ }
+ }
+
+ /*
+ * We want to process the entire string in a single call, but we may
+ * need to modify it. Copy it to a buffer. Note that it may consist
+ * of bytes, gs_chars, or gs_glyphs.
+ */
+
+ if (operation & (TEXT_FROM_STRING | TEXT_FROM_BYTES)) {
+ if (size < sizeof(gs_glyph))
+ size = sizeof(gs_glyph); /* for process_cid_text */
+ } else if (operation & TEXT_FROM_CHARS)
+ size *= sizeof(gs_char);
+ else if (operation & TEXT_FROM_SINGLE_CHAR)
+ size = sizeof(gs_char);
+ else if (operation & TEXT_FROM_GLYPHS)
+ size *= sizeof(gs_glyph);
+ else if (operation & TEXT_FROM_SINGLE_GLYPH)
+ size = sizeof(gs_glyph);
+ else
+ goto skip;
+
+ /* Now that we are using the working buffer for text in composite fonts, we must make sure
+ * it is large enough. Each input character code may be promoted to a gs_glyph, in scan_cmap_text
+ * when processing a Type 0 font with a type 1 descendant font. This routine uses
+ * pdf_make_text_glyphs_table_unencoded (called from pdf_obtain_font_resource_unencoded) which
+ * also may require an identically sized buffer, so we need:
+ * num__input_characters * sizeof(gs_glyph) * 2.
+ */
+ if (pte->orig_font->FontType == ft_composite) {
+ if (size < (pte->text.size - pte->index) * sizeof(gs_glyph) * 2)
+ size = (pte->text.size - pte->index) * sizeof(gs_glyph) * 2;
+ }
+
+ if (size <= sizeof(buf)) {
+ code = process(pte, buf.bytes, size);
+ } else {
+ byte *buf = gs_alloc_string(pte->memory, size, "pdf_text_process");
+
+ if (buf == 0)
+ return_error(gs_error_VMerror);
+ code = process(pte, buf, size);
+ gs_free_string(pte->memory, buf, size, "pdf_text_process");
+ }
+ skip:
+ if (code < 0 ||
+ ((pte->current_font->FontType == ft_user_defined ||
+ pte->current_font->FontType == ft_PCL_user_defined ||
+ pte->current_font->FontType == ft_MicroType ||
+ pte->current_font->FontType == ft_GL2_stick_user_defined ||
+ pte->current_font->FontType == ft_GL2_531 ||
+ pte->current_font->FontType == ft_TrueType) &&
+ code != TEXT_PROCESS_INTERVENE &&
+ penum->index < penum->text.size)) {
+ if (code == gs_error_unregistered) /* Debug purpose only. */
+ return code;
+ if (code == gs_error_VMerror)
+ return code;
+ if (code == gs_error_invalidfont) /* Bug 688370. */
+ return code;
+ default_impl:
+ /* Fall back to the default implementation. */
+ code = pdf_default_text_begin(pte, &pte->text, &pte_default);
+ if (code < 0)
+ return code;
+ penum->pte_default = pte_default;
+ gs_text_enum_copy_dynamic(pte_default, pte, false);
+ }
+ /* The 'process' procedure might also have set pte_default itself. */
+ if (penum->pte_default && !code)
+ goto top;
+ return code;
+ /*
+ * This function uses an unobvious algorithm while handling type 3 fonts.
+ * It runs 'process' to copy text until a glyph, which was not copied to
+ * output font. Then it installs pte_default and falls back to default
+ * implementation with PS interpreter callout. The callout executes
+ * BuildChar/BuildGlyph with setcachedevice. The latter calls
+ * pdf_set_charproc_attrs, which sets up an accumulator
+ * of graphic objects to a pdf_begin_resource stream.
+ * When the callout completes, pdf_text_process calls pdf_end_charproc_accum
+ * and later resumes the normal (non-default) text enumeration, repeating the
+ * the "callouted" glyph AT SECOND TIME. We can't do without the second pass
+ * becauase in the first pass the glyph widths is unknown.
+ */
+ /*
+ * Another unobvious thing is a CDevProc callout.
+ * If 'process' returns with TEXT_PROCESS_CDEVPROC,
+ * an interpreter callout will happen, and the function will be called again
+ * with pte->cdevproc_result_valid = true. Then it restatrs with taking
+ * glyph metrics from pte->cdevproc_result instead obtaining them with
+ * font->procs.glyph_info .
+ */
+}
diff --git a/devices/vector/gdevpdtt.h b/devices/vector/gdevpdtt.h
new file mode 100644
index 000000000..28cbcd926
--- /dev/null
+++ b/devices/vector/gdevpdtt.h
@@ -0,0 +1,332 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Internal text processing interface for pdfwrite */
+
+#ifndef gdevpdtt_INCLUDED
+# define gdevpdtt_INCLUDED
+
+/*
+ * This file is only used internally to define the interface between
+ * gdevpdtt.c, gdevpdtc.c, and gdevpdte.c.
+ */
+
+/* ---------------- Coordinate systems ---------------- */
+
+/*
+
+ The current text code has to deal with 6 different coordinate systems.
+ This situation is complex, confusing, and fragile, but despite literally
+ years of struggle we have been unable to understand how to simplify it.
+
+ 1) PostScript user space at the time a text operator is invoked. The
+ width values for ashow, xshow, etc. are specified in this space: these
+ values appear in the delta_all, delta_space, x_widths, and y_widths
+ members of the text member of the common part of the text enumerator
+ (pdf_text_enum_t).
+
+ 2) Device space. For the pdfwrite device, this is a straightforward space
+ with (0,0) in the lower left corner. The usual resolution is 720 dpi,
+ but this is user-settable to provide a tradeoff between file size and
+ bitmap resolution (for rendered Patterns and fonts that have to be
+ converted to bitmaps).
+
+ 3) PDF user space. During the processing of text operators, this is
+ always the same as the default PDF user space, in which 1 unit = 1/72",
+ because an Acrobat quirk apparently requires this. (See stream_to_text
+ in gdevpdfu.c.)
+
+ 4) Font design space. This is the space in which the font's character
+ outlines and advance widths are specified, including the width values
+ returned by font->procs.glyph_info and by pdf_char_widths.
+
+ 5) PDF unscaled text space. This space is used for the PDF width
+ adjustment values (Tc, Tw, and TL parameters, and " operator) and
+ positioning coordinates (Td and TD operators).
+
+ 6) PDF text space. This space is used for the width adjustments for the
+ TJ operator.
+
+ The following convert between these spaces:
+
+ - The PostScript CTM (pte->pis->ctm) maps #1 to #2.
+
+ - The mapping from #3 to #2 is a scaling by pdev->HWResolution / 72.
+
+ - The mapping from #5 to #6 is a scaling by the size member of
+ pdf_text_state_values_t, which is the size value for the Tf operator.
+
+ - The matrix member of pdf_text_state_values_t maps #5 to #2, which is the
+ matrix for the Tm operator or its abbreviations.
+
+ - The FontMatrix of a font maps #4 to #1.
+
+ Note that the PDF text matrix (set by the Tm operator) maps #5 to #3.
+ However, this is not actually stored anywhere: it is computed by
+ multiplying the #5->#2 matrix by the #2->#3 scaling.
+
+*/
+
+/* ---------------- Types and structures ---------------- */
+
+#ifndef pdf_char_glyph_pair_DEFINED
+# define pdf_char_glyph_pair_DEFINED
+typedef struct pdf_char_glyph_pair_s pdf_char_glyph_pair_t;
+#endif
+
+#ifndef pdf_char_glyph_pairs_DEFINED
+# define pdf_char_glyph_pairs_DEFINED
+typedef struct pdf_char_glyph_pairs_s pdf_char_glyph_pairs_t;
+#endif
+
+/* Define a structure for a text characters list. */
+/* It must not contain pointers due to variable length. */
+struct pdf_char_glyph_pairs_s {
+ int num_all_chars;
+ int num_unused_chars;
+ int unused_offset; /* The origin of the unused character table.*/
+ pdf_char_glyph_pair_t s[1]; /* Variable length. */
+};
+
+/* Define the text enumerator. */
+typedef struct pdf_text_enum_s {
+ gs_text_enum_common;
+ gs_text_enum_t *pte_default;
+ gs_fixed_point origin;
+ bool charproc_accum;
+ bool cdevproc_callout;
+ double cdevproc_result[10];
+ pdf_char_glyph_pairs_t *cgp;
+ gs_char output_char_code;
+} pdf_text_enum_t;
+#define private_st_pdf_text_enum()\
+ extern_st(st_gs_text_enum);\
+ gs_private_st_suffix_add2(st_pdf_text_enum, pdf_text_enum_t,\
+ "pdf_text_enum_t", pdf_text_enum_enum_ptrs, pdf_text_enum_reloc_ptrs,\
+ st_gs_text_enum, pte_default, cgp)
+
+/*
+ * Define quantities derived from the current font and CTM, used within
+ * the text processing loop. NOTE: This structure has no GC descriptor
+ * and must therefore only be used locally (allocated on the C stack).
+ */
+typedef struct pdf_text_process_state_s {
+ pdf_text_state_values_t values;
+ gs_font *font;
+} pdf_text_process_state_t;
+
+/*
+ * Define the structure used to return glyph width information. Note that
+ * there are two different sets of width information: real-number (x,y)
+ * values, which give the true advance width, and an integer value, which
+ * gives an X advance width for WMode = 0 or a Y advance width for WMode = 1.
+ * The return value from pdf_glyph_width() indicates which of these is/are
+ * valid.
+ */
+typedef struct pdf_glyph_width_s {
+ double w;
+ gs_point xy;
+ gs_point v; /* glyph origin shift */
+} pdf_glyph_width_t;
+typedef struct pdf_glyph_widths_s {
+ pdf_glyph_width_t Width; /* unmodified, for Widths */
+ pdf_glyph_width_t real_width; /* possibly modified, for rendering */
+ bool replaced_v;
+ bool ignore_wmode;
+ gs_rect BBox; /* Only used by eps2write */
+} pdf_glyph_widths_t;
+
+/* ---------------- Procedures ---------------- */
+
+/*
+ * Declare the procedures for processing different species of text.
+ * These procedures may, but need not, copy pte->text into buf
+ * (a caller-supplied buffer large enough to hold the string).
+ */
+#define PROCESS_TEXT_PROC(proc)\
+ int proc(gs_text_enum_t *pte, void *vbuf, uint bsize)
+
+/* ------ gdevpdtt.c ------ */
+
+/*
+ * Compute and return the orig_matrix of a font.
+ */
+int pdf_font_orig_matrix(const gs_font *font, gs_matrix *pmat);
+
+/*
+ * Check the Encoding compatibility
+ */
+bool pdf_check_encoding_compatibility(const pdf_font_resource_t *pdfont,
+ const pdf_char_glyph_pair_t *pairs, int num_chars);
+
+/*
+ * Create or find a font resource object for a text.
+ */
+int
+pdf_obtain_font_resource(pdf_text_enum_t *penum,
+ const gs_string *pstr, pdf_font_resource_t **ppdfont);
+
+/*
+ * Create or find a font resource object for a glyphshow text.
+ */
+int pdf_obtain_font_resource_unencoded(pdf_text_enum_t *penum,
+ const gs_string *pstr, pdf_font_resource_t **ppdfont, const gs_glyph *gdata);
+
+/*
+ * Create or find a CID font resource object for a glyph set.
+ */
+int pdf_obtain_cidfont_resource(gx_device_pdf *pdev, gs_font *subfont,
+ pdf_font_resource_t **ppdsubf,
+ pdf_char_glyph_pairs_t *cgp);
+
+/*
+ * Create or find a parent Type 0 font resource object for a CID font resource.
+ */
+int pdf_obtain_parent_type0_font_resource(gx_device_pdf *pdev, pdf_font_resource_t *pdsubf,
+ uint font_index, const gs_const_string *CMapName, pdf_font_resource_t **pdfont);
+
+/*
+ * Retrive font resource attached to a font.
+ * allocating glyph_usage and real_widths on request.
+ */
+int pdf_attached_font_resource(gx_device_pdf *pdev, gs_font *font,
+ pdf_font_resource_t **pdfont, byte **glyph_usage,
+ double **real_widths, int *num_chars, int *num_widths);
+
+/*
+ * Attach font resource to a font.
+ */
+int pdf_attach_font_resource(gx_device_pdf *pdev, gs_font *font,
+ pdf_font_resource_t *pdfont);
+
+/*
+ * Locate a font cache element.
+ */
+pdf_font_cache_elem_t **
+pdf_locate_font_cache_elem(gx_device_pdf *pdev, gs_font *font);
+
+/*
+ * Create a font resource object for a gs_font of Type 3.
+ */
+int pdf_make_font3_resource(gx_device_pdf *pdev, gs_font *font,
+ pdf_font_resource_t **ppdfont);
+
+/*
+ * Compute the cached values in the text processing state from the text
+ * parameters, pdfont, and pis->ctm. Return either an error code (< 0) or a
+ * mask of operation attributes that the caller must emulate. Currently the
+ * only such attributes are TEXT_ADD_TO_ALL_WIDTHS and
+ * TEXT_ADD_TO_SPACE_WIDTH.
+ */
+int pdf_update_text_state(pdf_text_process_state_t *ppts,
+ const pdf_text_enum_t *penum,
+ pdf_font_resource_t *pdfont,
+ const gs_matrix *pfmat);
+
+/*
+ * Set up commands to make the output state match the processing state.
+ * General graphics state commands are written now; text state commands
+ * are written later.
+ */
+int pdf_set_text_process_state(gx_device_pdf *pdev,
+ const gs_text_enum_t *pte, /*for pdcolor, pis*/
+ pdf_text_process_state_t *ppts);
+
+/*
+ * Get the widths (unmodified and possibly modified) of a glyph in a (base)
+ * font. If the width is cachable (implying that the w values area valid),
+ * return 0; if only the xy values are valid, or the width is not cachable
+ * for some other reason, return 1.
+ * Return TEXT_PROCESS_CDEVPROC if a CDevProc callout is needed.
+ * cdevproc_result != NULL if we restart after a CDevProc callout.
+ */
+int pdf_glyph_widths(pdf_font_resource_t *pdfont, int wmode, gs_glyph glyph,
+ gs_font *font, pdf_glyph_widths_t *pwidths,
+ const double cdevproc_result[10]);
+
+/*
+ * Fall back to the default text processing code when needed.
+ */
+int pdf_default_text_begin(gs_text_enum_t *pte, const gs_text_params_t *text,
+ gs_text_enum_t **ppte);
+
+/*
+ * Check for simple font.
+ */
+bool pdf_is_simple_font(gs_font *font);
+
+/*
+ * Check for CID font.
+ */
+bool pdf_is_CID_font(gs_font *font);
+
+/* Release a text characters colloction. */
+void pdf_text_release_cgp(pdf_text_enum_t *penum);
+
+/* Return char code by glyph. */
+gs_char pdf_find_glyph(pdf_font_resource_t *pdfont, gs_glyph glyph);
+
+/* ------ gdevpdtc.c ------ */
+
+PROCESS_TEXT_PROC(process_composite_text);
+PROCESS_TEXT_PROC(process_cmap_text);
+PROCESS_TEXT_PROC(process_cid_text);
+
+/* ------ gdevpdte.c ------ */
+
+PROCESS_TEXT_PROC(process_plain_text);
+
+/*
+ * Process a string with a simple gs_font.
+ */
+int pdf_process_string_aux(pdf_text_enum_t *penum, gs_string *pstr,
+ const gs_glyph *gdata, const gs_matrix *pfmat,
+ pdf_text_process_state_t *ppts);
+
+/*
+ * Emulate TEXT_ADD_TO_ALL_WIDTHS and/or TEXT_ADD_TO_SPACE_WIDTH,
+ * and implement TEXT_REPLACE_WIDTHS if requested.
+ * Uses and updates ppts->values.matrix; uses ppts->values.pdfont.
+ */
+int process_text_modify_width(pdf_text_enum_t *pte, gs_font *font,
+ pdf_text_process_state_t *ppts,
+ const gs_const_string *pstr,
+ gs_point *pdpt, const gs_glyph *gdata, bool composite, int decoded_bytes);
+
+/*
+ * Add char code pair to ToUnicode CMap,
+ * creating the CMap on neccessity.
+ */
+int
+pdf_add_ToUnicode(gx_device_pdf *pdev, gs_font *font, pdf_font_resource_t *pdfont,
+ gs_glyph glyph, gs_char ch, const gs_const_string *gnstr);
+
+/*
+ * Get character code from a glyph code.
+ * An usage of this function is very undesirable,
+ * because a glyph may be unlisted in Encoding.
+ */
+int pdf_encode_glyph(gs_font_base *bfont, gs_glyph glyph0,
+ byte *buf, int buf_size, int *char_code_length);
+
+int pdf_shift_text_currentpoint(pdf_text_enum_t *penum, gs_point *wpt);
+
+void adjust_first_last_char(pdf_font_resource_t *pdfont, byte *str, int size);
+
+float pdf_calculate_text_size(gs_imager_state *pis, pdf_font_resource_t *pdfont,
+ const gs_matrix *pfmat, gs_matrix *smat, gs_matrix *tmat,
+ gs_font *font, gx_device_pdf *pdev);
+#endif /* gdevpdtt_INCLUDED */
diff --git a/devices/vector/gdevpdtv.c b/devices/vector/gdevpdtv.c
new file mode 100644
index 000000000..599f7516e
--- /dev/null
+++ b/devices/vector/gdevpdtv.c
@@ -0,0 +1,5509 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+/*
+ * This file contains substantial parts of toolbin/encs2c.ps,
+ * which generated the remainder of the file mechanically from
+ * gs_std_e.ps gs_il1_e.ps gs_sym_e.ps gs_dbt_e.ps
+ * gs_wan_e.ps gs_mro_e.ps gs_mex_e.ps gs_mgl_e.ps
+ * gs_lgo_e.ps gs_lgx_e.ps gs_css_e.ps
+ *
+ * This source file is maintained manually under source code control,
+ * however its content should be regenerated by using encs2c.ps
+ * if changes are required.
+ */
+
+#include "gdevpdtv.h"
+
+/*
+ * Glyph attributes for every glyph <= GS_C_PDF_MAX_GOOD_GLYPH
+ * packed 4 per byte least significant bits first.
+ */
+const unsigned char gs_c_pdf_glyph_type[] = {
+124,
+241,
+255,
+255,
+255,
+0,
+0,
+0,
+12,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+12,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+76,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+28,
+3,
+0,
+0,
+0,
+0,
+0,
+0,
+12,
+4,
+0,
+0,
+0,
+0,
+0,
+0,
+220,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+12,
+192,
+0,
+0,
+0,
+0,
+0,
+0,
+60,
+1,
+3,
+0,
+0,
+0,
+0,
+0,
+12,
+0,
+12,
+0,
+0,
+0,
+0,
+0,
+28,
+12,
+48,
+0,
+0,
+0,
+0,
+0,
+12,
+0,
+192,
+0,
+0,
+0,
+0,
+0,
+92,
+49,
+0,
+0,
+0,
+0,
+0,
+0,
+12,
+0,
+0,
+12,
+0,
+0,
+0,
+0,
+12,
+0,
+0,
+48,
+0,
+0,
+0,
+0,
+76,
+4,
+0,
+192,
+0,
+0,
+0,
+0,
+12,
+0,
+1,
+0,
+3,
+0,
+0,
+0,
+12,
+0,
+0,
+0,
+12,
+0,
+0,
+0,
+76,
+48,
+12,
+0,
+48,
+0,
+0,
+0,
+12,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+12,
+4,
+48,
+0,
+0,
+0,
+0,
+0,
+76,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+12,
+0,
+192,
+0,
+0,
+0,
+0,
+0,
+12,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+12,
+48,
+1,
+0,
+0,
+0,
+0,
+0,
+12,
+4,
+0,
+0,
+0,
+0,
+0,
+0,
+12,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+12,
+0,
+12,
+0,
+0,
+0,
+0,
+0,
+12,
+0,
+0,
+48,
+0,
+0,
+0,
+0,
+12,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+12,
+52,
+48,
+0,
+0,
+0,
+0,
+0,
+12,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+60,
+0,
+0,
+0,
+3,
+0,
+0,
+0,
+12,
+0,
+192,
+0,
+0,
+0,
+0,
+0,
+60,
+0,
+0,
+0,
+12,
+0,
+0,
+0,
+12,
+4,
+0,
+0,
+0,
+0,
+0,
+0,
+60,
+0,
+12,
+0,
+0,
+0,
+0,
+0,
+12,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+60,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+12,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+60,
+4,
+1,
+0,
+0,
+0,
+0,
+0,
+12,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+60,
+64,
+0,
+48,
+0,
+0,
+0,
+0,
+12,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+28,
+0,
+192,
+0,
+0,
+0,
+0,
+0,
+12,
+12,
+12,
+0,
+0,
+0,
+0,
+0,
+60,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+12,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+28,
+48,
+3,
+0,
+3,
+0,
+0,
+0,
+12,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+28,
+0,
+48,
+0,
+0,
+0,
+0,
+0,
+12,
+0,
+0,
+0,
+12,
+0,
+0,
+0,
+0,
+0,
+0,
+4,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+12,
+0,
+0,
+0,
+0,
+0,
+0,
+12,
+192,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+3,
+48,
+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,
+0,
+0,
+0,
+0,
+4,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+192,
+12,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+3,
+0,
+3,
+0,
+0,
+0,
+0,
+4,
+0,
+4,
+0,
+0,
+0,
+0,
+0,
+48,
+192,
+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,
+0,
+0,
+0,
+0,
+0,
+4,
+48,
+48,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+48,
+15,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+12,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+64,
+192,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+4,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+12,
+51,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+4,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+48,
+0,
+48,
+0,
+0,
+0,
+0,
+0,
+12,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+195,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+60,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+4,
+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,
+0,
+0,
+0,
+0,
+12,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+48,
+1,
+1,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+64,
+0,
+48,
+0,
+0,
+0,
+0,
+0,
+0,
+196,
+0,
+0,
+0,
+0,
+0,
+0,
+4,
+16,
+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,
+0,
+0,
+0,
+0,
+0,
+0,
+3,
+4,
+0,
+0,
+0,
+0,
+0,
+12,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+48,
+4,
+1,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+12,
+208,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+1,
+16,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+48,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+4,
+12,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+64,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+52,
+17,
+1,
+0,
+0,
+0,
+0,
+0,
+0,
+192,
+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,
+0,
+0,
+0,
+0,
+0,
+12,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+112,
+12,
+16,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+1,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+4,
+48,
+12,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+48,
+192,
+1,
+0,
+0,
+0,
+0,
+0,
+192,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+4,
+12,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+3,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+48,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+68,
+48,
+16,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+192,
+12,
+0,
+0,
+0,
+0,
+0,
+48,
+13,
+1,
+0,
+0,
+0,
+0,
+0,
+4,
+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,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+12,
+16,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+3,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+12,
+0,
+0,
+0,
+0,
+0,
+0,
+64,
+192,
+16,
+0,
+0,
+0,
+0,
+0,
+4,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+16,
+0,
+5,
+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,
+0,
+0,
+0,
+0,
+12,
+51,
+0,
+0,
+0,
+0,
+0,
+0,
+64,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+12,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+12,
+192,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+48,
+3,
+17,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+12,
+0,
+0,
+0,
+0,
+0,
+4,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+4,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+48,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+4,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+192,
+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,
+0,
+0,
+0,
+0,
+0,
+60,
+60,
+1,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+64,
+0,
+28,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+3,
+0,
+0,
+0,
+0,
+0,
+0,
+12,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+192,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+192,
+12,
+0,
+0,
+0,
+0,
+0,
+0,
+12,
+48,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+1,
+3,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+4,
+0,
+12,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+48,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+48,
+204,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+48,
+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,
+0,
+0,
+0,
+0,
+0,
+48,
+0,
+3,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+12,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+12,
+0,
+0,
+0,
+0,
+0,
+0,
+192,
+0,
+0,
+0,
+0,
+0,
+0,
+112,
+16,
+48,
+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,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+48,
+12,
+3,
+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,
+0,
+0,
+0,
+0,
+0,
+208,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+12,
+0,
+0,
+0,
+0,
+0,
+48,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+48,
+0,
+0,
+0,
+0,
+0,
+0,
+12,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+48,
+0,
+3,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+16,
+0,
+0,
+0,
+0,
+0,
+0,
+64,
+192,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+48,
+12,
+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,
+0,
+0,
+0,
+0,
+0,
+192,
+0,
+48,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+48,
+16,
+3,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+192,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+192,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+48,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+4,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+48,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+64,
+12,
+51,
+0,
+0,
+0,
+0,
+0,
+0,
+192,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+192,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+64,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+64,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+16,
+12,
+0,
+0,
+0,
+0,
+64,
+0,
+4,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+192,
+48,
+192,
+3,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+192,
+0,
+48,
+0,
+0,
+0,
+0,
+192,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+192,
+0,
+52,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+64,
+0,
+12,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+192,
+0,
+0,
+0,
+0,
+0,
+192,
+48,
+0,
+3,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+64,
+0,
+12,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+48,
+48,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+64,
+48,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+64,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+64,
+4,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+192,
+0,
+12,
+3,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+48,
+0,
+0,
+0,
+0,
+0,
+64,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+192,
+112,
+0,
+48,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+192,
+0,
+68,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+12,
+0,
+0,
+0,
+0,
+0,
+48,
+48,
+3,
+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,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+48,
+12,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+192,
+48,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+48,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+48,
+0,
+12,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+12,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+48,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+64,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+48,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+48,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+48,
+12,
+1,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+12,
+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,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+48,
+64,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+12,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+48,
+0,
+51,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+12,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+192,
+0,
+0,
+0,
+0,
+0,
+0,
+48,
+12,
+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,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+1,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+16,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+12,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+48,
+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,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+48,
+12,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+192,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+60,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+48,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+12,
+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,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+64,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+12,
+0,
+0,
+0,
+0,
+0,
+48,
+12,
+16,
+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,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+48,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+192,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+12,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+48,
+0,
+4,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+16,
+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,
+0,
+0,
+0,
+0,
+0,
+68,
+3,
+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,
+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,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+12,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+48,
+0,
+0,
+0,
+0,
+0,
+0,
+192,
+0,
+0,
+0,
+0,
+0,
+0,
+48,
+0,
+3,
+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,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+12,
+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,
+0,
+0,
+0,
+0,
+0,
+0,
+192,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+1,
+0,
+3,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+12,
+0,
+0,
+0,
+0,
+0,
+0,
+3,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+48,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+1,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+192,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+51,
+4,
+3,
+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,
+0,
+0,
+0,
+0,
+3,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+48,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+1,
+192,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+4,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+51,
+0,
+1,
+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,
+0,
+0,
+0,
+0,
+3,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+12,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+192,
+0,
+0,
+0,
+0,
+0,
+0,
+1,
+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,
+0,
+0,
+0,
+0,
+0,
+0,
+51,
+0,
+1,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+12,
+0,
+0,
+0,
+0,
+0,
+0,
+3,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+48,
+192,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+3,
+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,
+0,
+0,
+0,
+0,
+0,
+0,
+49,
+12,
+1,
+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,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+192,
+0,
+0,
+0,
+0,
+0,
+0,
+48,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+12,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+48,
+0,
+1,
+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,
+0,
+0,
+0,
+0,
+0,
+64,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+4,
+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,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+48,
+0,
+1,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+76,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+48,
+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,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+48,
+0,
+3,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+64,
+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,
+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,
+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,
+0,
+0,
+0,
+16,
+0,
+3,
+0,
+0,
+0,
+0,
+0,
+0,
+64,
+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,
+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,
+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,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+48,
+192,
+3,
+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,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+48,
+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,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+192,
+0,
+0,
+0,
+0,
+0,
+0,
+16,
+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,
+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,
+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,
+0,
+0,
+0,
+0,
+0,
+192,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+1,
+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,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+48,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+192,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+48,
+0,
+1,
+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,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+48,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+192,
+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,
+0,
+0,
+0,
+0,
+0,
+48,
+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,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+16,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+192,
+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,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+48,
+0,
+3,
+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,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+48,
+192,
+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,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+48,
+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,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+192,
+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,
+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,
+0,
+0,
+0,
+0,
+0,
+0,
+3,
+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,
+0,
+0,
+0,
+0,
+0,
+192,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+48,
+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,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+3,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+64,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+48,
+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,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+16,
+0,
+3,
+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,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+16,
+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,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+3,
+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,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+16,
+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,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+48,
+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,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+48,
+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,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+48,
+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,
+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,
+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,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+48
+};
diff --git a/devices/vector/gdevpdtv.h b/devices/vector/gdevpdtv.h
new file mode 100644
index 000000000..0c7c3ffb5
--- /dev/null
+++ b/devices/vector/gdevpdtv.h
@@ -0,0 +1,37 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+/*
+ * This file contains substantial parts of toolbin/encs2c.ps,
+ * which generated the remainder of the file mechanically from
+ * gs_std_e.ps gs_il1_e.ps gs_sym_e.ps gs_dbt_e.ps
+ * gs_wan_e.ps gs_mro_e.ps gs_mex_e.ps gs_mgl_e.ps
+ * gs_lgo_e.ps gs_lgx_e.ps gs_css_e.ps
+ *
+ * This source file is maintained manually under source code control,
+ * however its content should be regenerated by using encs2c.ps
+ * if changes are required.
+ */
+
+#ifndef gdevpdtv_INCLUDED
+#define gdevpdtv_INCLUDED
+
+#define GS_C_PDF_MAX_GOOD_GLYPH 21894
+#define GS_C_PDF_GOOD_GLYPH_MASK 1
+#define GS_C_PDF_GOOD_NON_SYMBOL_MASK 2
+
+extern const unsigned char gs_c_pdf_glyph_type[];
+
+#endif /* gdevpdtv_INCLUDED */
diff --git a/devices/vector/gdevpdtw.c b/devices/vector/gdevpdtw.c
new file mode 100644
index 000000000..e7f64a4c3
--- /dev/null
+++ b/devices/vector/gdevpdtw.c
@@ -0,0 +1,895 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Font resource writing for pdfwrite text */
+#include "memory_.h"
+#include "gx.h"
+#include "gserrors.h"
+#include "gxfcmap.h"
+#include "gxfont.h"
+#include "gxfcopy.h"
+#include "gscencs.h"
+#include "gdevpsf.h"
+#include "gdevpdfx.h"
+#include "gdevpdfo.h" /* for object->written */
+#include "gdevpdtd.h" /* for writing FontDescriptor */
+#include "gdevpdtf.h"
+#include "gdevpdti.h" /* for writing bitmap fonts Encoding */
+#include "gdevpdtw.h"
+#include "gdevpdtv.h"
+#include "sarc4.h"
+
+static const char *const encoding_names[] = {
+ KNOWN_REAL_ENCODING_NAMES
+};
+
+/* ================ Font resource writing ================ */
+
+/* ---------------- Private ---------------- */
+
+/* Write the Widths for a font. */
+static int
+pdf_write_Widths(gx_device_pdf *pdev, int first, int last, const double *widths)
+{
+ stream *s = pdev->strm;
+ int i;
+
+ if (first > last)
+ first = last = 0;
+ pprintd2(s, "/FirstChar %d/LastChar %d/Widths[", first, last);
+ for (i = first; i <= last; ++i)
+ pprintg1(s, (i & 15 ? " %g" : "\n%g"), psdf_round(widths[i], 100, 10));
+ stream_puts(s, "]\n");
+ return 0;
+}
+
+/* Check strings equality. */
+static bool
+strings_equal(const gs_const_string *str0, const gs_const_string *str1)
+{
+ return str0->size == str1->size &&
+ !memcmp(str0->data, str1->data, str0->size);
+}
+
+/* Check if an encoding element differs from a standard one. */
+static int
+pdf_different_encoding_element(const pdf_font_resource_t *pdfont, int ch, int encoding_index)
+{
+ if (pdfont->u.simple.Encoding[ch].is_difference)
+ return 1;
+ else if (encoding_index != ENCODING_INDEX_UNKNOWN) {
+ gs_glyph glyph0 = gs_c_known_encode(ch, encoding_index);
+ gs_glyph glyph1 = pdfont->u.simple.Encoding[ch].glyph;
+ gs_const_string str;
+ int code = gs_c_glyph_name(glyph0, &str);
+
+ if (code < 0)
+ return code; /* Must not happen */
+ if (glyph1 != GS_NO_GLYPH)
+ if (!strings_equal(&str, &pdfont->u.simple.Encoding[ch].str))
+ return 1;
+ }
+ return 0;
+}
+
+/* Find an index of a different encoding element. */
+int
+pdf_different_encoding_index(const pdf_font_resource_t *pdfont, int ch0)
+{
+ gs_encoding_index_t base_encoding = pdfont->u.simple.BaseEncoding;
+ int ch, code;
+
+ for (ch = ch0; ch < 256; ++ch) {
+ code = pdf_different_encoding_element(pdfont, ch, base_encoding);
+ if (code < 0)
+ return code; /* Must not happen */
+ if (code)
+ break;
+ }
+ return ch;
+}
+
+/* Check for unknown encode (simple fonts only). */
+static bool
+pdf_simple_font_needs_ToUnicode(const pdf_font_resource_t *pdfont)
+{
+ int ch;
+ unsigned char mask = (pdfont->FontType == ft_encrypted || pdfont->FontType == ft_encrypted2
+ ? GS_C_PDF_GOOD_GLYPH_MASK : GS_C_PDF_GOOD_NON_SYMBOL_MASK);
+
+ if (pdfont->u.simple.Encoding == NULL)
+ return true; /* Bitmap Type 3 fonts have no pdfont->u.simple.Encoding . */
+ if (pdfont->FontType == ft_TrueType)
+ /*
+ TrueType fonts are always written as symbolic, and so they do not have
+ an Encoding entry (SVN revision 11735, bugs #690744, #691036, #691319).
+ In this circumstance, write the ToUnicode map to get a searchable PDF.
+ */
+ return true;
+ for (ch = 0; ch < 256; ++ch) {
+ pdf_encoding_element_t *pet = &pdfont->u.simple.Encoding[ch];
+ gs_glyph glyph = pet->glyph;
+
+ if (glyph == GS_NO_GLYPH)
+ continue;
+ if (glyph < gs_c_min_std_encoding_glyph || glyph >= GS_MIN_CID_GLYPH) {
+ if (pet->str.size == 0)
+ return true;
+ glyph = gs_c_name_glyph(pet->str.data, pet->str.size);
+ if (glyph == GS_NO_GLYPH)
+ return true;
+ }
+ glyph -= gs_c_min_std_encoding_glyph;
+ if( glyph > GS_C_PDF_MAX_GOOD_GLYPH ||
+ !(gs_c_pdf_glyph_type[glyph >> 2] & (mask << (( glyph & 3 )<<1) )))
+ return true;
+ }
+ return false;
+}
+
+/* Write Encoding differencrs. */
+int
+pdf_write_encoding(gx_device_pdf *pdev, const pdf_font_resource_t *pdfont, long id, int ch)
+{
+ /* Note : this truncates extended glyph names to original names. */
+ stream *s;
+ gs_encoding_index_t base_encoding = pdfont->u.simple.BaseEncoding;
+ const int sl = strlen(gx_extendeg_glyph_name_separator);
+ int prev = 256, code, cnt = 0;
+
+ pdf_open_separate(pdev, id, resourceEncoding);
+ s = pdev->strm;
+ stream_puts(s, "<</Type/Encoding");
+ if (base_encoding < 0 && pdev->ForOPDFRead)
+ base_encoding = ENCODING_INDEX_STANDARD;
+ if (base_encoding > 0)
+ pprints1(s, "/BaseEncoding/%s", encoding_names[base_encoding]);
+ stream_puts(s, "/Differences[");
+ for (; ch < 256; ++ch) {
+ code = pdf_different_encoding_element(pdfont, ch, base_encoding);
+ if (code < 0)
+ return code; /* Must not happen */
+ if (code == 0 && (pdfont->FontType == ft_user_defined ||
+ pdfont->FontType == ft_PCL_user_defined ||
+ pdfont->FontType == ft_MicroType ||
+ pdfont->FontType == ft_GL2_stick_user_defined ||
+ pdfont->FontType == ft_GL2_531)) {
+ /* PDF 1.4 spec Appendix H Note 42 says that
+ * Acrobat 4 can't properly handle Base Encoding.
+ * Enforce writing differences against that.
+ */
+ if (pdfont->used[ch >> 3] & 0x80 >> (ch & 7))
+ if (pdfont->u.simple.Encoding[ch].str.size)
+ code = 1;
+ }
+ if (code) {
+ const byte *d = pdfont->u.simple.Encoding[ch].str.data;
+ int i, l = pdfont->u.simple.Encoding[ch].str.size;
+
+ if (pdev->HavePDFWidths) {
+ for (i = 0; i + sl < l; i++)
+ if (!memcmp(d + i, gx_extendeg_glyph_name_separator, sl)) {
+ l = i;
+ break;
+ }
+ }
+ if (ch != prev + 1) {
+ pprintd1(s, "\n%d", ch);
+ cnt = 1;
+ } else if (!(cnt++ & 15))
+ stream_puts(s, "\n");
+ pdf_put_name(pdev, d, l);
+ prev = ch;
+ }
+ }
+ stream_puts(s, "]>>\n");
+ pdf_end_separate(pdev, resourceEncoding);
+ return 0;
+}
+
+/* Write Encoding reference. */
+int
+pdf_write_encoding_ref(gx_device_pdf *pdev,
+ const pdf_font_resource_t *pdfont, long id)
+{
+ stream *s = pdev->strm;
+
+ if (id != 0) {
+ pprintld1(s, "/Encoding %ld 0 R", id);
+ pdf_record_usage_by_parent(pdev, id, pdfont->object->id);
+ }
+ else if (pdfont->u.simple.BaseEncoding > 0) {
+ gs_encoding_index_t base_encoding = pdfont->u.simple.BaseEncoding;
+ pprints1(s, "/Encoding/%s", encoding_names[base_encoding]);
+ }
+ return 0;
+}
+
+/* Write the Subtype and Encoding for a simple font. */
+static int
+pdf_write_simple_contents(gx_device_pdf *pdev,
+ const pdf_font_resource_t *pdfont)
+{
+ stream *s = pdev->strm;
+ long diff_id = 0;
+ int ch = (pdfont->u.simple.Encoding ? 0 : 256);
+ int code = 0;
+
+ ch = pdf_different_encoding_index(pdfont, ch);
+ if (ch < 256)
+ diff_id = pdf_obj_ref(pdev);
+ code = pdf_write_encoding_ref(pdev, pdfont, diff_id);
+ if (code < 0)
+ return code;
+ pprints1(s, "/Subtype/%s>>\n",
+ (pdfont->FontType == ft_TrueType ? "TrueType" :
+ pdfont->u.simple.s.type1.is_MM_instance ? "MMType1" : "Type1"));
+ pdf_end_separate(pdev, resourceFont);
+ if (diff_id) {
+ mark_font_descriptor_symbolic(pdfont);
+ code = pdf_write_encoding(pdev, pdfont, diff_id, ch);
+ if (code < 0)
+ return code;
+ }
+ return 0;
+}
+
+/*
+ * Write the W[2] entries for a CIDFont. *pdfont is known to be a
+ * CIDFont (type 0 or 2).
+ */
+static bool
+pdf_compute_CIDFont_default_widths(const pdf_font_resource_t *pdfont, int wmode, int *pdw, int *pdv)
+{
+ psf_glyph_enum_t genum;
+ gs_glyph glyph;
+ ushort counts[1500]; /* Some CID fonts use vertical widths 1026 .*/
+ int dw_count = 0, i, dwi = 0, neg_count = 0, pos_count = 0;
+ double *w = (wmode ? pdfont->u.cidfont.Widths2 : pdfont->Widths);
+
+ /* We don't wont to scan for both negative and positive widths,
+ * to save the C stack space.
+ * Doubtly they both are used in same font.
+ * So just count positive and negative widths separately
+ * and use the corresponding sign.
+ * fixme : implement 2 hystograms.
+ */
+ psf_enumerate_bits_begin(&genum, NULL,
+ wmode ? pdfont->u.cidfont.used2 : pdfont->used,
+ pdfont->count, GLYPH_SPACE_INDEX);
+ memset(counts, 0, sizeof(counts));
+ while (!psf_enumerate_glyphs_next(&genum, &glyph)) {
+ int i = glyph - GS_MIN_CID_GLYPH;
+
+ if ( i < pdfont->count) { /* safety */
+ int width = (int)(w[i] + 0.5);
+
+ counts[min(any_abs(width), countof(counts) - 1)]++;
+ if (width > 0)
+ pos_count++;
+ else if (width < 0)
+ neg_count++;
+ }
+ }
+ for (i = 1; i < countof(counts); ++i)
+ if (counts[i] > dw_count)
+ dwi = i, dw_count = counts[i];
+ *pdw = (neg_count > pos_count ? -dwi : dwi);
+ *pdv = 0;
+ if (wmode) {
+ psf_enumerate_glyphs_reset(&genum);
+ while (!psf_enumerate_glyphs_next(&genum, &glyph)) {
+ int i = glyph - GS_MIN_CID_GLYPH;
+
+ if ( i < pdfont->count) { /* safety */
+ int width = (int)(w[i] + 0.5);
+
+ if (min(any_abs(width), countof(counts) - 1) == any_abs(dwi)) {
+ *pdv = (int)(pdfont->u.cidfont.v[i * 2 + 1] + 0.5);
+ break;
+ }
+ }
+ }
+ }
+ return (dw_count + counts[0] > 0);
+}
+
+/*
+ * Write the [D]W[2] entries for a CIDFont. *pdfont is known to be a
+ * CIDFont (type 0 or 2).
+ */
+static int
+pdf_write_CIDFont_widths(gx_device_pdf *pdev,
+ const pdf_font_resource_t *pdfont, int wmode)
+{
+ /*
+ * The values of the CIDFont width keys are as follows:
+ * DW = w (default 0)
+ * W = [{c [w ...] | cfirst clast w}*]
+ * DW2 = [vy w1y] (default [880 -1000])
+ * W2 = [{c [w1y vx vy ...] | cfirst clast w1y vx vy}*]
+ */
+ stream *s = pdev->strm;
+ psf_glyph_enum_t genum;
+ gs_glyph glyph;
+ int dw = 0, dv = 0, prev = -2;
+ const char *Widths_key = (wmode ? "/W2" : "/W");
+ double *w = (wmode ? pdfont->u.cidfont.Widths2 : pdfont->Widths);
+
+ /* Compute and write default width : */
+ if (pdf_compute_CIDFont_default_widths(pdfont, wmode, &dw, &dv)) {
+ if (wmode) {
+ pprintd2(s, "/DW2 [%d %d]\n", dv, dw);
+ } else
+ pprintd1(s, "/DW %d\n", dw);
+ }
+
+ /*
+ * Now write all widths different from the default one. Currently we make no
+ * attempt to optimize this: we write every width individually.
+ */
+ psf_enumerate_bits_begin(&genum, NULL,
+ wmode ? pdfont->u.cidfont.used2 : pdfont->used,
+ pdfont->count, GLYPH_SPACE_INDEX);
+ {
+ while (!psf_enumerate_glyphs_next(&genum, &glyph)) {
+ int cid = glyph - GS_MIN_CID_GLYPH;
+ int width = (int)(w[cid] + 0.5);
+
+#if 0 /* Must write zero widths - see test file of the bug Bug 687681.
+ We don't enumerate unused glyphs here due to pdfont->used. */
+ if (width == 0)
+ continue; /* Don't write for unused glyphs. */
+#else
+ { /* Check whether copied font really have this glyph.
+ debugged with 401-01.ps, which uses undefined CIDs. */
+ gs_font_base *pfont = pdf_font_resource_font(pdfont, false);
+ gs_glyph_info_t info;
+
+ if (pfont->FontType == ft_TrueType) {
+ /* We're converting a Type 42 into CIDFontType2. */
+ /* We know that CIDs equal to char codes. */
+ gs_glyph glyph1;
+ int ch = glyph & 0xff;
+
+ glyph1 = pfont->procs.encode_char((gs_font *)pfont, ch, GLYPH_SPACE_NAME);
+ if (cid == 0 && glyph1 == GS_NO_GLYPH)
+ glyph1 = copied_get_notdef((gs_font *)pdf_font_resource_font(pdfont, false));
+ if (glyph1 == GS_NO_GLYPH)
+ continue;
+ if (pfont->procs.glyph_info((gs_font *)pfont, glyph1, NULL, 0, &info) < 0)
+ continue;
+ } else if (pfont->procs.glyph_info((gs_font *)pfont, glyph, NULL, 0, &info) < 0)
+ continue;
+ }
+#endif
+ if (cid == prev + 1) {
+ if (wmode) {
+ int vx = (int)(pdfont->u.cidfont.v[cid * 2 + 0] + 0.5);
+ int vy = (int)(pdfont->u.cidfont.v[cid * 2 + 1] + 0.5);
+
+ pprintd3(s, "\n%d %d %d", width, vx, vy);
+ } else
+ pprintd1(s, "\n%d", width);
+ } else if (pdev->PDFA == 0 && width == dw &&
+ (!wmode || (int)(pdfont->u.cidfont.v[cid * 2 + 0] + 0.5) ==
+ (int)(pdfont->Widths[cid] / 2 + 0.5)) &&
+ (!wmode || (int)(pdfont->u.cidfont.v[cid * 2 + 1] + 0.5) == dv))
+ continue;
+ else {
+ if (prev >= 0)
+ stream_puts(s, "]\n");
+ else {
+ stream_puts(s, Widths_key);
+ stream_puts(s, "[");
+ }
+ if (wmode) {
+ int vx = (int)(pdfont->u.cidfont.v[cid * 2 + 0] + 0.5);
+ int vy = (int)(pdfont->u.cidfont.v[cid * 2 + 1] + 0.5);
+
+ pprintd4(s, "%d[%d %d %d", cid, width, vx, vy);
+ } else
+ pprintd2(s, "%d[%d", cid, width);
+ }
+ prev = cid;
+ }
+ if (prev >= 0)
+ stream_puts(s, "]]\n");
+ }
+
+ return 0;
+}
+
+/* ---------------- Specific FontTypes ---------------- */
+
+/* Write the contents of a Type 0 font resource. */
+int
+pdf_write_contents_type0(gx_device_pdf *pdev, pdf_font_resource_t *pdfont)
+{
+ stream *s = pdev->strm;
+
+ /*
+ * The Encoding name might be missing if an error occurred when
+ * creating the font resource.
+ */
+ if (pdfont->u.type0.Encoding_name[0])
+ pprints1(s, "/Encoding %s", pdfont->u.type0.Encoding_name);
+ pprintld1(s, "/DescendantFonts[%ld 0 R]",
+ pdf_font_id(pdfont->u.type0.DescendantFont));
+ stream_puts(s, "/Subtype/Type0>>\n");
+ pdf_end_separate(pdev, resourceFont);
+ return 0;
+}
+
+/*
+ * Finish writing the contents of a Type 3 font resource (FontBBox, Widths,
+ * Subtype).
+ */
+int
+pdf_finish_write_contents_type3(gx_device_pdf *pdev,
+ pdf_font_resource_t *pdfont)
+{
+ stream *s = pdev->strm;
+
+ pdf_write_font_bbox_float(pdev, &pdfont->u.simple.s.type3.FontBBox);
+ pdf_write_Widths(pdev, pdfont->u.simple.FirstChar,
+ pdfont->u.simple.LastChar, pdfont->Widths);
+ stream_puts(s, "/Subtype/Type3>>\n");
+ pdf_end_separate(pdev, resourceFont);
+ return 0;
+}
+
+/* Write the contents of a standard (base 14) font resource. */
+int
+pdf_write_contents_std(gx_device_pdf *pdev, pdf_font_resource_t *pdfont)
+{
+ return pdf_write_simple_contents(pdev, pdfont);
+}
+
+/* Write the contents of a simple (Type 1 or Type 42) font resource. */
+int
+pdf_write_contents_simple(gx_device_pdf *pdev, pdf_font_resource_t *pdfont)
+{
+ pdf_write_Widths(pdev, pdfont->u.simple.FirstChar,
+ pdfont->u.simple.LastChar, pdfont->Widths);
+ return pdf_write_simple_contents(pdev, pdfont);
+}
+
+/* Write the contents of a CIDFont resource. */
+static int
+write_contents_cid_common(gx_device_pdf *pdev, pdf_font_resource_t *pdfont,
+ int subtype)
+{
+ /* Write [D]W[2], CIDSystemInfo, and Subtype, and close the object. */
+ stream *s = pdev->strm;
+ int code;
+
+ if (pdfont->Widths != 0) {
+ code = pdf_write_CIDFont_widths(pdev, pdfont, 0);
+ if (code < 0)
+ return code;
+ } else {
+ /* With a vertical font, the viewer uses /DW
+ to determine glyph width to compute its v-vector. */
+ stream_puts(s, "/DW 0\n");
+ }
+ if (pdfont->u.cidfont.Widths2 != 0) {
+ code = pdf_write_CIDFont_widths(pdev, pdfont, 1);
+ if (code < 0)
+ return code;
+ }
+ if (pdfont->u.cidfont.CIDSystemInfo_id)
+ pprintld1(s, "/CIDSystemInfo %ld 0 R",
+ pdfont->u.cidfont.CIDSystemInfo_id);
+ pprintd1(s, "/Subtype/CIDFontType%d>>\n", subtype);
+ pdf_end_separate(pdev, resourceFont);
+ return 0;
+}
+int
+pdf_write_contents_cid0(gx_device_pdf *pdev, pdf_font_resource_t *pdfont)
+{
+ return write_contents_cid_common(pdev, pdfont, 0);
+}
+int
+pdf_write_contents_cid2(gx_device_pdf *pdev, pdf_font_resource_t *pdfont)
+{
+ int count = pdfont->count;
+ long map_id = 0;
+ psf_glyph_enum_t genum;
+ gs_glyph glyph;
+ int code;
+
+ /* Check for the identity CIDMap. */
+ psf_enumerate_bits_begin(&genum, NULL, pdfont->used, count,
+ GLYPH_SPACE_INDEX);
+ while (!psf_enumerate_glyphs_next(&genum, &glyph)) {
+ int cid = glyph - GS_MIN_CID_GLYPH;
+ int gid = pdfont->u.cidfont.CIDToGIDMap[cid];
+
+ if (gid != cid) { /* non-identity map */
+ map_id = pdf_obj_ref(pdev);
+ pprintld1(pdev->strm, "/CIDToGIDMap %ld 0 R\n", map_id);
+ break;
+ }
+ }
+
+ if (map_id == 0 && pdf_font_descriptor_embedding(pdfont->FontDescriptor)) {
+ code = stream_puts(pdev->strm, "/CIDToGIDMap /Identity\n");
+ if (code < 0)
+ return code;
+ }
+
+ code = write_contents_cid_common(pdev, pdfont, 2);
+ if (code < 0)
+ return code;
+
+ if (map_id && pdf_font_descriptor_embedding(pdfont->FontDescriptor)) {
+ pdf_data_writer_t writer;
+ int i;
+
+ pdf_begin_data_stream(pdev, &writer,
+ DATA_STREAM_BINARY | (pdev->CompressFonts ? DATA_STREAM_COMPRESS : 0),
+ /* Don't set DATA_STREAM_ENCRYPT since we write to a temporary file.
+ See comment in pdf_begin_encrypt. */
+ map_id);
+ for (i = 0; i < pdfont->u.cidfont.CIDToGIDMapLength; ++i) {
+ uint gid = pdfont->u.cidfont.CIDToGIDMap[i];
+
+ stream_putc(writer.binary.strm, (byte)(gid >> 8));
+ stream_putc(writer.binary.strm, (byte)(gid));
+ }
+ code = pdf_end_data(&writer);
+ }
+ return code;
+}
+
+/* ---------------- External entries ---------------- */
+
+/* Write a font resource. */
+static int
+pdf_write_font_resource(gx_device_pdf *pdev, pdf_font_resource_t *pdfont)
+{
+ stream *s;
+ cos_dict_t *pcd_Resources = NULL;
+ char *base14_name = NULL;
+ int id;
+
+ if (pdfont->cmap_ToUnicode != NULL && pdfont->res_ToUnicode == NULL)
+ if (pdfont->FontType == ft_composite ||
+ ((pdfont->FontType == ft_encrypted || pdfont->FontType == ft_encrypted2 ||
+ pdfont->FontType == ft_TrueType || pdfont->FontType == ft_user_defined ||
+ pdfont->FontType == ft_GL2_stick_user_defined || pdfont->FontType == ft_PCL_user_defined ||
+ pdfont->FontType == ft_MicroType || pdfont->FontType == ft_GL2_531) &&
+ pdf_simple_font_needs_ToUnicode(pdfont))
+ ) {
+ pdf_resource_t *prcmap;
+ int code = pdf_cmap_alloc(pdev, pdfont->cmap_ToUnicode, &prcmap, -1);
+
+ if (code < 0)
+ return code;
+ pdfont->res_ToUnicode = prcmap;
+ }
+ if (pdev->CompatibilityLevel >= 1.2 &&
+ (pdfont->FontType == ft_user_defined ||
+ pdfont->FontType == ft_PCL_user_defined ||
+ pdfont->FontType == ft_MicroType ||
+ pdfont->FontType == ft_GL2_stick_user_defined ||
+ pdfont->FontType == ft_GL2_531) &&
+ pdfont->u.simple.s.type3.Resources != NULL &&
+ pdfont->u.simple.s.type3.Resources->elements != NULL) {
+ int code;
+
+ pcd_Resources = pdfont->u.simple.s.type3.Resources;
+ pcd_Resources->id = pdf_obj_ref(pdev);
+ pdf_open_separate(pdev, pcd_Resources->id, resourceFont);
+ code = COS_WRITE(pcd_Resources, pdev);
+ if (code < 0)
+ return code;
+ pdf_end_separate(pdev, resourceFont);
+ }
+ pdf_open_separate(pdev, pdf_font_id(pdfont), resourceFont);
+ s = pdev->strm;
+ stream_puts(s, "<<");
+ if (pdfont->BaseFont.size > 0) {
+ stream_puts(s, "/BaseFont");
+ if (pdfont->FontDescriptor && !pdf_font_descriptor_embedding(pdfont->FontDescriptor)
+ && (base14_name = (char *)pdf_find_base14_name((byte *)pdfont->BaseFont.data, (unsigned int)pdfont->BaseFont.size)))
+ pdf_put_name(pdev, (byte *)base14_name, (unsigned int)strlen(base14_name));
+ else
+ pdf_put_name(pdev, (byte *)pdfont->BaseFont.data, pdfont->BaseFont.size);
+ }
+ if (pdfont->FontDescriptor) {
+ id = pdf_font_descriptor_id(pdfont->FontDescriptor);
+ pprintld1(s, "/FontDescriptor %ld 0 R", id);
+ if (pdev->Linearise) {
+ pdf_set_font_descriptor_usage(pdev, pdfont->object->id, pdfont->FontDescriptor);
+ }
+ }
+ if (pdfont->res_ToUnicode) {
+ id = pdf_resource_id((const pdf_resource_t *)pdfont->res_ToUnicode);
+ pprintld1(s, "/ToUnicode %ld 0 R", id);
+ pdf_record_usage_by_parent(pdev, id, pdfont->object->id);
+ }
+ if (pdev->CompatibilityLevel > 1.0)
+ stream_puts(s, "/Type/Font\n");
+ else
+ pprintld1(s, "/Type/Font/Name/R%ld\n", pdf_font_id(pdfont));
+ if (pdev->ForOPDFRead && pdfont->global)
+ stream_puts(s, "/.Global true\n");
+ if (pcd_Resources != NULL) {
+ id = pcd_Resources->id;
+ pprintld1(s, "/Resources %ld 0 R\n", id);
+ pdf_record_usage_by_parent(pdev, id, pdfont->object->id);
+ }
+ return pdfont->write_contents(pdev, pdfont);
+}
+
+/*
+ * Close the text-related parts of a document, including writing out font
+ * and related resources.
+ */
+int
+write_font_resources(gx_device_pdf *pdev, pdf_resource_list_t *prlist)
+{
+ int j;
+ pdf_resource_t *pres;
+
+ for (j = 0; j < NUM_RESOURCE_CHAINS; ++j)
+ for (pres = prlist->chains[j]; pres != 0; pres = pres->next) {
+ pdf_font_resource_t *const pdfont = (pdf_font_resource_t *)pres;
+
+ if (pdf_resource_id(pres) != -1) {
+ int code = pdf_compute_BaseFont(pdev, pdfont, true);
+
+ if (code < 0)
+ return code;
+ code = pdf_write_font_resource(pdev, pdfont);
+ if (code < 0)
+ return code;
+ pdfont->object->written = true;
+ }
+ }
+ return 0;
+}
+int
+pdf_finish_resources(gx_device_pdf *pdev, pdf_resource_type_t type,
+ int (*finish_proc)(gx_device_pdf *,
+ pdf_resource_t *))
+{
+ int j;
+ pdf_resource_t *pres;
+
+ for (j = 0; j < NUM_RESOURCE_CHAINS; ++j)
+ for (pres = pdev->resources[type].chains[j];
+ pres != 0; pres = pres->next
+ ) {
+ int code = finish_proc(pdev, pres);
+
+ if (code < 0)
+ return code;
+ }
+ return 0;
+}
+
+/* ================ CMap resource writing ================ */
+
+/*
+ * Write the CIDSystemInfo for a CIDFont or a CMap.
+ */
+static int
+pdf_write_cid_system_info_to_stream(gx_device_pdf *pdev, stream *s,
+ const gs_cid_system_info_t *pcidsi, gs_id object_id)
+{
+ byte *Registry, *Ordering;
+
+ Registry = gs_alloc_bytes(pdev->pdf_memory, pcidsi->Registry.size, "temporary buffer for Registry");
+ if (!Registry)
+ return(gs_note_error(gs_error_VMerror));
+ Ordering = gs_alloc_bytes(pdev->pdf_memory, pcidsi->Ordering.size, "temporary buffer for Registry");
+ if (!Ordering) {
+ gs_free_object(pdev->pdf_memory, Registry, "free temporary Registry buffer");
+ return(gs_note_error(gs_error_VMerror));
+ }
+ memcpy(Registry, pcidsi->Registry.data, pcidsi->Registry.size);
+ memcpy(Ordering, pcidsi->Ordering.data, pcidsi->Ordering.size);
+ if (pdev->KeyLength && object_id != 0) {
+ stream_arcfour_state sarc4;
+ int code;
+
+ code = pdf_encrypt_init(pdev, object_id, &sarc4);
+ if (code < 0) {
+ gs_free_object(pdev->pdf_memory, Registry, "free temporary Registry buffer");
+ gs_free_object(pdev->pdf_memory, Ordering, "free temporary Ordering buffer");
+ return(gs_note_error(code));
+ }
+ s_arcfour_process_buffer(&sarc4, Registry, pcidsi->Registry.size);
+ code = pdf_encrypt_init(pdev, object_id, &sarc4);
+ if (code < 0) {
+ gs_free_object(pdev->pdf_memory, Registry, "free temporary Registry buffer");
+ gs_free_object(pdev->pdf_memory, Ordering, "free temporary Ordering buffer");
+ return(gs_note_error(code));
+ }
+ s_arcfour_process_buffer(&sarc4, Ordering, pcidsi->Ordering.size);
+ }
+ stream_puts(s, "<<\n/Registry");
+ s_write_ps_string(s, Registry, pcidsi->Registry.size, PRINT_HEX_NOT_OK);
+ stream_puts(s, "\n/Ordering");
+ s_write_ps_string(s, Ordering, pcidsi->Ordering.size, PRINT_HEX_NOT_OK);
+ pprintd1(s, "\n/Supplement %d\n>>\n", pcidsi->Supplement);
+ gs_free_object(pdev->pdf_memory, Registry, "free temporary Registry buffer");
+ gs_free_object(pdev->pdf_memory, Ordering, "free temporary Ordering buffer");
+ return 0;
+}
+
+int
+pdf_write_cid_system_info(gx_device_pdf *pdev,
+ const gs_cid_system_info_t *pcidsi, gs_id object_id)
+{
+ return pdf_write_cid_system_info_to_stream(pdev, pdev->strm, pcidsi, object_id);
+}
+
+int
+pdf_write_cid_systemInfo_separate(gx_device_pdf *pdev, const gs_cid_system_info_t *pcidsi, long *id)
+{
+ int code;
+
+ *id = pdf_begin_separate(pdev, resourceCIDSystemInfo);
+ code = pdf_write_cid_system_info(pdev, pcidsi, *id);
+ pdf_end_separate(pdev, resourceCIDSystemInfo);
+ return code;
+}
+
+/*
+ * Write a CMap resource. We pass the CMap object as well as the resource,
+ * because we write CMaps when they are created.
+ */
+int
+pdf_write_cmap(gx_device_pdf *pdev, const gs_cmap_t *pcmap,
+ pdf_resource_t **ppres /*CMap*/, int font_index_only)
+{
+ int code;
+ pdf_data_writer_t writer;
+ gs_const_string alt_cmap_name;
+ const gs_const_string *cmap_name = &pcmap->CMapName;
+
+ code = pdf_begin_data_stream(pdev, &writer,
+ DATA_STREAM_NOT_BINARY |
+ /* Don't set DATA_STREAM_ENCRYPT since we write to a temporary file.
+ See comment in pdf_begin_encrypt. */
+ (pdev->CompressFonts ?
+ DATA_STREAM_COMPRESS : 0), gs_no_id);
+ if (code < 0)
+ return code;
+ *ppres = writer.pres;
+ writer.pres->where_used = 0; /* CMap isn't a PDF resource. */
+ if (!pcmap->ToUnicode) {
+ byte buf[200];
+ cos_dict_t *pcd = (cos_dict_t *)writer.pres->object;
+ stream s;
+
+ code = cos_dict_put_c_key_int(pcd, "/WMode", pcmap->WMode);
+ if (code < 0)
+ return code;
+ buf[0] = '/';
+ memcpy(buf + 1, pcmap->CMapName.data, pcmap->CMapName.size);
+ code = cos_dict_put_c_key_string(pcd, "/CMapName",
+ buf, pcmap->CMapName.size + 1);
+ if (code < 0)
+ return code;
+ s_init(&s, pdev->memory);
+ swrite_string(&s, buf, sizeof(buf));
+ code = pdf_write_cid_system_info_to_stream(pdev, &s, pcmap->CIDSystemInfo, 0);
+ if (code < 0)
+ return code;
+ code = cos_dict_put_c_key_string(pcd, "/CIDSystemInfo",
+ buf, stell(&s));
+ if (code < 0)
+ return code;
+ code = cos_dict_put_string_copy(pcd, "/Type", "/CMap");
+ if (code < 0)
+ return code;
+ }
+ if (pcmap->CMapName.size == 0) {
+ /* Create an arbitrary name (for ToUnicode CMap). */
+ alt_cmap_name.data = (byte *)(*ppres)->rname;
+ alt_cmap_name.size = strlen((*ppres)->rname);
+ cmap_name = &alt_cmap_name;
+ }
+ code = psf_write_cmap(pdev->memory, writer.binary.strm, pcmap,
+ pdf_put_name_chars_proc(pdev),
+ cmap_name, font_index_only);
+ if (code < 0)
+ return code;
+ code = pdf_end_data(&writer);
+ if (code < 0)
+ return code;
+ return code;
+}
+
+static const char *OneByteIdentityH[] = {
+ "/CIDInit /ProcSet findresource begin",
+ "12 dict begin",
+ "begincmap",
+ "/CIDSystemInfo 3 dict dup begin",
+ "/Registry (Adobe) def",
+ "/Ordering (Identity) def",
+ "/Supplement 0 def",
+ "end def",
+ "/CMapName /OneByteIdentityH def",
+ "/CMapVersion 1.000 def",
+ "/CMapType 1 def",
+ "/UIDOffset 0 def",
+ "/XUID [1 10 25404 9999] def",
+ "/WMode 0 def",
+ "1 begincodespacerange",
+ "<00> <FF>",
+ "endcodespacerange",
+ "1 begincidrange",
+ "<00> <FF> 0",
+ "endcidrange",
+ "endcmap",
+ "CMapName currentdict /CMap defineresource pop",
+ "end",
+ "end",
+NULL};
+
+/*
+ * Write OneByteIdentityH CMap.
+ */
+int
+pdf_write_OneByteIdentityH(gx_device_pdf *pdev)
+{
+ int code, i;
+ pdf_data_writer_t writer;
+ cos_dict_t *pcd;
+ char buf[200];
+ static const gs_cid_system_info_t cidsi = {{(const byte *)"Adobe", 5}, {(const byte *)"Identity", 8}, 0};
+ long id;
+
+ if (pdev->IdentityCIDSystemInfo_id == gs_no_id) {
+ code = pdf_write_cid_systemInfo_separate(pdev, &cidsi, &id);
+ if (code < 0)
+ return code;
+ pdev->IdentityCIDSystemInfo_id = id;
+ }
+ if (pdev->OneByteIdentityH != NULL)
+ return 0;
+ code = pdf_begin_data_stream(pdev, &writer,
+ DATA_STREAM_NOT_BINARY |
+ /* Don't set DATA_STREAM_ENCRYPT since we write to a temporary file.
+ See comment in pdf_begin_encrypt. */
+ (pdev->CompressFonts ?
+ DATA_STREAM_COMPRESS : 0), gs_no_id);
+ if (code < 0)
+ return code;
+ pdev->OneByteIdentityH = writer.pres;
+ pcd = (cos_dict_t *)writer.pres->object;
+ code = cos_dict_put_string_copy(pcd, "/CMapName", "/OneByteIdentityH");
+ if (code < 0)
+ return code;
+ gs_sprintf(buf, "%ld 0 R", pdev->IdentityCIDSystemInfo_id);
+ code = cos_dict_put_string_copy(pcd, "/CIDSystemInfo", buf);
+ if (code < 0)
+ return code;
+ code = cos_dict_put_string_copy(pcd, "/Type", "/CMap");
+ if (code < 0)
+ return code;
+ for (i = 0; OneByteIdentityH[i]; i++) {
+ stream_puts(pdev->strm, OneByteIdentityH[i]);
+ stream_putc(pdev->strm, '\n');
+ }
+ return pdf_end_data(&writer);
+}
diff --git a/devices/vector/gdevpdtw.h b/devices/vector/gdevpdtw.h
new file mode 100644
index 000000000..abd4c2641
--- /dev/null
+++ b/devices/vector/gdevpdtw.h
@@ -0,0 +1,79 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Font and CMap resource writing API for pdfwrite */
+
+#ifndef gdevpdtw_INCLUDED
+# define gdevpdtw_INCLUDED
+
+/*
+ * The procedures declared here are called only from gdevpdtf.c: they are
+ * not intended to be called from anywhere else.
+ */
+
+/* ---------------- Font resource writing ---------------- */
+
+/*
+ * Each of these procedures is referenced only from a single place in
+ * gdevpdtf.c. Their prototype and functionality must match the definition
+ * of pdf_font_write_contents_proc_t in gdevpdtf.h.
+ */
+int
+ pdf_write_contents_type0(gx_device_pdf *pdev, pdf_font_resource_t *pdfont),
+ pdf_finish_write_contents_type3(gx_device_pdf *pdev,
+ pdf_font_resource_t *pdfont),
+ pdf_write_contents_std(gx_device_pdf *pdev, pdf_font_resource_t *pdfont),
+ pdf_write_contents_simple(gx_device_pdf *pdev, pdf_font_resource_t *pdfont),
+ pdf_write_contents_cid0(gx_device_pdf *pdev, pdf_font_resource_t *pdfont),
+ pdf_write_contents_cid2(gx_device_pdf *pdev, pdf_font_resource_t *pdfont),
+ pdf_different_encoding_index(const pdf_font_resource_t *pdfont, int ch0),
+ pdf_write_encoding(gx_device_pdf *pdev, const pdf_font_resource_t *pdfont, long id, int ch),
+ pdf_write_encoding_ref(gx_device_pdf *pdev, const pdf_font_resource_t *pdfont, long id);
+
+/* ---------------- CMap resource writing ---------------- */
+
+#ifndef gs_cid_system_info_DEFINED
+# define gs_cid_system_info_DEFINED
+typedef struct gs_cid_system_info_s gs_cid_system_info_t;
+#endif
+#ifndef gs_cmap_DEFINED
+# define gs_cmap_DEFINED
+typedef struct gs_cmap_s gs_cmap_t;
+#endif
+
+/*
+ * Write the CIDSystemInfo for a CIDFont or a CMap.
+ */
+int pdf_write_cid_system_info(gx_device_pdf *pdev,
+ const gs_cid_system_info_t *pcidsi, gs_id object_id);
+
+/* Write CIDSystemInfo */
+int pdf_write_cid_systemInfo_separate(gx_device_pdf *pdev,
+ const gs_cid_system_info_t *pcidsi, long *id);
+
+/*
+ * Write a CMap resource. We pass the CMap object as well as the resource,
+ * because we write CMaps when they are created.
+ */
+int pdf_write_cmap(gx_device_pdf *pdev, const gs_cmap_t *pcmap,
+ pdf_resource_t **ppres, int font_index_only);
+
+/*
+ * Write OneByteIdentityH CMap.
+ */
+int pdf_write_OneByteIdentityH(gx_device_pdf *pdev);
+
+#endif /* gdevpdtw_INCLUDED */
diff --git a/devices/vector/gdevpdtx.h b/devices/vector/gdevpdtx.h
new file mode 100644
index 000000000..c1ef99f51
--- /dev/null
+++ b/devices/vector/gdevpdtx.h
@@ -0,0 +1,96 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Shared implementation definitions for pdfwrite text and fonts */
+
+#ifndef gdevpdtx_INCLUDED
+# define gdevpdtx_INCLUDED
+
+#include "gdevpdt.h"
+
+/*
+ * The pdfwrite text code is designed in layers. Each layer uses only
+ * facilities defined in that layer and those below it. The layers and the
+ * code files that define them are:
+ *
+ * Text processing
+ * gdevpdtt.c - main text processing and utilities
+ * gdevpdtc.c - processing for composite and CID-based fonts
+ * gdevpdte.c - processing for Encoding-based (i.e., all other) fonts
+ * gdevpdts.c - text state bookkeeping
+ * gdevpdt[st].h - definitions shared by the above
+ * Font resources
+ * gdevpdtf.h - interface and public definitions
+ * gdevpdtf.c - implementation of font resources, except for writing
+ * gdevpdtw.[hc] - font resource writing
+ * Font descriptors
+ * gdevpdtd.h - interface and public definitions
+ * gdevpdtd.c - implementation of font descriptors
+ * Base fonts
+ * gdevpdtb.h - interface and public definitions
+ * gdevpdtb.c - implementation of base fonts
+ *
+ * There is also a module for bitmap font processing:
+ * gdevpdti.h - interface and public definitions
+ * gdevpdti.c - implementation of bitmap fonts
+ * This handles both text processing and font resource management for
+ * Type 3 bitmap fonts, which don't fit into the layer structure very well.
+ */
+
+/* ================ Types and structures ================ */
+
+/*
+ * Define the structure for managing the text and font state. This is
+ * made up of several individually opaque components.
+ */
+
+#ifndef pdf_bitmap_fonts_DEFINED
+# define pdf_bitmap_fonts_DEFINED
+typedef struct pdf_bitmap_fonts_s pdf_bitmap_fonts_t;
+#endif
+#ifndef pdf_outline_fonts_DEFINED
+# define pdf_outline_fonts_DEFINED
+typedef struct pdf_outline_fonts_s pdf_outline_fonts_t;
+#endif
+#ifndef pdf_text_state_DEFINED
+# define pdf_text_state_DEFINED
+typedef struct pdf_text_state_s pdf_text_state_t;
+#endif
+
+/*typedef struct pdf_text_data_s pdf_text_data_t;*/ /* gdevpdfx.h */
+struct pdf_text_data_s {
+ pdf_outline_fonts_t *outline_fonts; /* gdevpdtf.c */
+ pdf_bitmap_fonts_t *bitmap_fonts; /* gdevpdti.c */
+ pdf_text_state_t *text_state; /* gdevpdts.c */
+};
+#define private_st_pdf_text_data() /* gdevpdt.c */\
+ gs_private_st_ptrs3(st_pdf_text_data, pdf_text_data_t, "pdf_text_data_t",\
+ pdf_text_data_enum_ptrs, pdf_text_data_reloc_ptrs,\
+ outline_fonts, bitmap_fonts, text_state)
+
+/* ================ Procedures (internal utilities) ================ */
+
+/* The typedef will eventually move to gdevpdfx.h. */
+typedef struct pdf_font_resource_s pdf_font_resource_t;
+
+/* ---------------- Font accessing ---------------- */
+
+/* Get the object ID of a font resource. */
+long pdf_font_id(const pdf_font_resource_t *pdfont);
+/* Register charproc fonts with the page or substream. */
+int pdf_used_charproc_resources(gx_device_pdf *pdev, pdf_font_resource_t *pdfont);
+
+#endif /* gdevpdtx_INCLUDED */
diff --git a/devices/vector/gdevpsdf.h b/devices/vector/gdevpsdf.h
new file mode 100644
index 000000000..90681af59
--- /dev/null
+++ b/devices/vector/gdevpsdf.h
@@ -0,0 +1,510 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Common output syntax and parameters for PostScript and PDF writers */
+
+#ifndef gdevpsdf_INCLUDED
+# define gdevpsdf_INCLUDED
+
+/*
+ * This file should really be named gdevpsd.h or gdevpsdx.h, but we're
+ * keeping its current name for backward compatibility.
+ */
+
+#include "gdevvec.h"
+#include "gsparam.h"
+#include "strimpl.h"
+#include "sa85x.h"
+#include "scfx.h"
+#include "spsdf.h"
+
+extern const stream_template s_DCTE_template; /* don't want all of sdct.h */
+
+/*
+ * NOTE: the default filter for color and gray images should be DCTEncode.
+ * We don't currently set this, because the management of the parameters
+ * for this filter simply doesn't work.
+ */
+
+/* ---------------- Distiller parameters ---------------- */
+
+/* Parameters for controlling distillation of images. */
+typedef struct psdf_image_params_s {
+ gs_c_param_list *ACSDict; /* JPEG */
+ bool AntiAlias;
+ bool AutoFilter;
+ int Depth;
+ gs_c_param_list *Dict; /* JPEG or CCITTFax */
+ bool Downsample;
+ float DownsampleThreshold;
+ enum psdf_downsample_type {
+ ds_Average,
+ ds_Bicubic,
+ ds_Subsample
+ } DownsampleType;
+#define psdf_ds_names\
+ "Average", "Bicubic", "Subsample"
+ bool Encode;
+ const char *Filter;
+ int Resolution;
+ enum psdf_autofilter_type {
+ af_Jpeg,
+ af_Jpeg2000
+ } AutoFilterStrategy;
+#define psdf_afs_names\
+ "JPEG", "JPEG2000"
+ const stream_template *filter_template;
+} psdf_image_params;
+
+/* Complete distiller parameters. */
+typedef struct psdf_distiller_params_s {
+
+ /* General parameters */
+
+ bool ASCII85EncodePages;
+ enum psdf_auto_rotate_pages {
+ arp_None,
+ arp_All,
+ arp_PageByPage
+ } AutoRotatePages;
+#define psdf_arp_names\
+ "None", "All", "PageByPage"
+ enum psdf_binding {
+ binding_Left,
+ binding_Right
+ } Binding;
+#define psdf_binding_names\
+ "Left", "Right"
+ bool CompressPages;
+ enum psdf_default_rendering_intent {
+ ri_Default,
+ ri_Perceptual,
+ ri_Saturation,
+ ri_RelativeColorimetric,
+ ri_AbsoluteColorimetric
+ } DefaultRenderingIntent;
+#define psdf_ri_names\
+ "Default", "Perceptual", "Saturation", "RelativeColorimetric",\
+ "AbsoluteColorimetric"
+ bool DetectBlends;
+ bool DoThumbnails;
+ long ImageMemory;
+ bool LockDistillerParams;
+ bool LZWEncodePages;
+ int OPM;
+ bool PreserveOPIComments;
+ bool UseFlateCompression;
+
+ /* Color processing parameters */
+
+ gs_const_string CalCMYKProfile;
+ gs_const_string CalGrayProfile;
+ gs_const_string CalRGBProfile;
+ gs_const_string sRGBProfile;
+ enum psdf_color_conversion_strategy {
+ ccs_LeaveColorUnchanged,
+ ccs_UseDeviceDependentColor, /* not in Acrobat Distiller 4.0 */
+ ccs_UseDeviceIndependentColor,
+ ccs_UseDeviceIndependentColorForImages,
+ ccs_sRGB,
+ ccs_CMYK,
+ ccs_Gray,
+ ccs_RGB,
+ ccs_ByObjectType
+ } ColorConversionStrategy;
+#define psdf_ccs_names\
+ "LeaveColorUnchanged", "UseDeviceDependentColor",\
+ "UseDeviceIndependentColor", "UseDeviceIndependentColorForImages",\
+ "sRGB", "CMYK", "Gray", "RGB", "ByObjectType"
+
+ bool PreserveHalftoneInfo;
+ bool PreserveOverprintSettings;
+ enum psdf_transfer_function_info {
+ tfi_Preserve,
+ tfi_Apply,
+ tfi_Remove
+ } TransferFunctionInfo;
+#define psdf_tfi_names\
+ "Preserve", "Apply", "Remove"
+ enum psdf_ucr_and_bg_info {
+ ucrbg_Preserve,
+ ucrbg_Remove
+ } UCRandBGInfo;
+#define psdf_ucrbg_names\
+ "Preserve", "Remove"
+
+ /* Color sampled image parameters */
+
+ psdf_image_params ColorImage;
+ bool ConvertCMYKImagesToRGB;
+ bool ConvertImagesToIndexed;
+
+ /* Grayscale sampled image parameters */
+
+ psdf_image_params GrayImage;
+
+ /* Monochrome sampled image parameters */
+
+ psdf_image_params MonoImage;
+
+ /* Font embedding parameters */
+
+ gs_param_string_array AlwaysEmbed;
+ gs_param_string_array NeverEmbed;
+ enum psdf_cannot_embed_font_policy {
+ cefp_OK,
+ cefp_Warning,
+ cefp_Error
+ } CannotEmbedFontPolicy;
+#define psdf_cefp_names\
+ "OK", "Warning", "Error"
+ bool EmbedAllFonts;
+ int MaxSubsetPct;
+ bool SubsetFonts;
+ gs_param_string PSDocOptions;
+ gs_param_string_array PSPageOptions;
+} psdf_distiller_params;
+
+/* Declare templates for default image compression filters. */
+extern const stream_template s_CFE_template;
+extern const stream_template s_zlibE_template;
+
+/* define some macros to use in setting up the default values of the pdfwrite device */
+#define psdf_general_param_defaults(ascii)\
+ ascii, /* ASCIIEncodePages */ \
+ arp_None, /* Auto-RotatePages */ \
+ binding_Left, /* Binding */\
+ 1, /* CompressPages (true) */ \
+ ri_Default, /* rendering intent */ \
+ 1, /* DetectBlends (true) */ \
+ 0, /* DoThumbnails (false) */ \
+ 500000, /* ImageMemory */ \
+ 0, /* LockDistillerParams (false) */ \
+ 0, /* LZWEncodePages (false) */ \
+ 1, /* Overprintmode (OPM) */ \
+ 0, /* PreserveOPIComments (false) */ \
+ 1, /* UseFlateCompression (true) */ \
+ /* Color processing parameters */\
+ {0}, /* calCMYKProfile */ \
+ {0}, /* CalGrayProfile */ \
+ {0}, /* calRGBProfile */ \
+ {0}, /* sRGBProfile */ \
+ ccs_LeaveColorUnchanged, /* ColorConversionStrategy */ \
+ 0, /* PreserveHalftoneInfo (false) */ \
+ 0, /* PreserveOverprintSettings (false) */ \
+ tfi_Preserve, /* TransferFunctionInfo */ \
+ ucrbg_Remove /* UCRandBGInfo */
+
+#define psdf_color_image_param_defaults\
+ { NULL, /* ACSDict (JPEG) */ \
+ 0, /* AntiAlias (false) */ \
+ 0, /* AutoFilter (false) */ \
+ -1, /* Depth */ \
+ NULL, /* Dict (JPEG or CCITTFax) */ \
+ 0, /* Downsample (false) */ \
+ 1.5, /* Donwsample threshold */ \
+ ds_Bicubic, /* Downsample type */ \
+ 1, /* Encode (true) */ \
+ 0, /* compression biccufilter */ \
+ 150, /* Downsample resolution */ \
+ af_Jpeg, /* AutoFilterStrategy */ \
+ &s_zlibE_template /* Filter stream template */ \
+ }, 0, /* ConvertCMYKImagesToRGB (false) */ \
+ 1 /* ConvertImagesToIndexed (true) */
+
+#define psdf_gray_image_param_defaults\
+ { NULL, /* ACSDict (JPEG) */ \
+ 0, /* AntiAlias (false) */ \
+ 0, /* AutoFilter (false) */ \
+ -1, /* Depth */ \
+ NULL, /* Dict (JPEG or CCITTFax) */ \
+ 0, /* Downsample (false) */ \
+ 1.5, /* Donwsample threshold */ \
+ ds_Subsample, /* Downsample type */ \
+ 1, /* Encode (true) */ \
+ 0, /* compression filter */ \
+ 150, /* Downsample resolution */ \
+ af_Jpeg, /* AutoFilterStrategy */ \
+ &s_zlibE_template /* Filter stream template */ \
+ }
+
+#define psdf_mono_image_param_defaults\
+ { NULL, /* ACSDict (JPEG) */ \
+ 0, /* AntiAlias (false) */ \
+ 0, /* AutoFilter (false) */ \
+ -1, /* Depth */ \
+ NULL, /* Dict (JPEG or CCITTFax) */ \
+ 0, /* Downsample (false) */ \
+ 2.0, /* Donwsample threshold */ \
+ ds_Subsample, /* Downsample type */ \
+ 1, /* Encode (true) */ \
+ "CCITTFaxEncode", /* compression filter */ \
+ 300, /* Downsample resolution */ \
+ af_Jpeg, /* AutoFilterStrategy */ \
+ &s_CFE_template /* Filter stream template */ \
+ }
+
+#define psdf_font_param_defaults\
+ {0}, /* AlwaysEmbed (array of names) */ \
+ {0}, /* NeverEmbed (array of names) */ \
+ cefp_Warning, /* CannotEmbedFontPolicy */ \
+ 1, /* EmbedAllFonts (true) */ \
+ 100, /* Max Subset Percent */ \
+ 1 /* Subset Fonts (true) */\
+
+#define psdf_PSOption_param_defaults\
+ {0}, /* PSDocOptions */\
+ {0} /* PSPageOptions */
+
+/* Define PostScript/PDF versions, corresponding roughly to Adobe versions. */
+typedef enum {
+ psdf_version_level1 = 1000, /* Red Book Level 1 */
+ psdf_version_level1_color = 1100, /* Level 1 + colorimage + CMYK color */
+ psdf_version_level2 = 2000, /* Red Book Level 2 */
+ psdf_version_level2_with_TT = 2010, /* Adobe release 2010 with Type 42 fonts */
+ psdf_version_level2_plus = 2017, /* Adobe release 2017 */
+ psdf_version_ll3 = 3010 /* LanguageLevel 3, release 3010 */
+} psdf_version;
+
+/* Define the extended device structure. */
+#define gx_device_psdf_common\
+ gx_device_vector_common;\
+ psdf_version version;\
+ bool binary_ok; /* derived from ASCII85EncodePages */\
+ bool HaveCFF;\
+ bool HaveTrueTypes;\
+ bool HaveCIDSystem;\
+ double ParamCompatibilityLevel;\
+ psdf_distiller_params params
+
+typedef struct gx_device_psdf_s {
+ gx_device_psdf_common;
+} gx_device_psdf;
+
+#define psdf_initial_values(version, ascii)\
+ vector_initial_values,\
+ version,\
+ !(ascii),\
+ true,\
+ true,\
+ false,\
+ 1.3,\
+ { psdf_general_param_defaults(ascii),\
+ psdf_color_image_param_defaults,\
+ psdf_gray_image_param_defaults,\
+ psdf_mono_image_param_defaults,\
+ psdf_font_param_defaults,\
+ psdf_PSOption_param_defaults\
+ }
+/* st_device_psdf is never instantiated per se, but we still need to */
+/* extern its descriptor for the sake of subclasses. */
+extern_st(st_device_psdf);
+#define public_st_device_psdf() /* in gdevpsdu.c */\
+ BASIC_PTRS(device_psdf_ptrs) {\
+ GC_OBJ_ELT2(gx_device_psdf, params.ColorImage.ACSDict,\
+ params.ColorImage.Dict),\
+ GC_CONST_STRING_ELT(gx_device_psdf, params.CalCMYKProfile),\
+ GC_CONST_STRING_ELT(gx_device_psdf, params.CalGrayProfile),\
+ GC_CONST_STRING_ELT(gx_device_psdf, params.CalRGBProfile),\
+ GC_CONST_STRING_ELT(gx_device_psdf, params.sRGBProfile),\
+ GC_OBJ_ELT2(gx_device_psdf, params.GrayImage.ACSDict,\
+ params.GrayImage.Dict),\
+ GC_OBJ_ELT2(gx_device_psdf, params.MonoImage.ACSDict,\
+ params.MonoImage.Dict),\
+ GC_OBJ_ELT2(gx_device_psdf, params.AlwaysEmbed.data,\
+ params.NeverEmbed.data),\
+ GC_CONST_STRING_ELT(gx_device_psdf, params.PSDocOptions),\
+ GC_OBJ_ELT(gx_device_psdf, params.PSPageOptions.data)\
+ };\
+ gs_public_st_basic_super_final(st_device_psdf, gx_device_psdf,\
+ "gx_device_psdf", device_psdf_ptrs, device_psdf_data,\
+ &st_device_vector, 0, gx_device_finalize)
+#define st_device_psdf_max_ptrs (st_device_vector_max_ptrs + 14)
+
+/* Get/put parameters. */
+int gdev_psdf_get_param(gx_device *dev, char *Param, void *list);
+dev_proc_get_params(gdev_psdf_get_params);
+dev_proc_put_params(gdev_psdf_put_params);
+
+dev_proc_dev_spec_op(gdev_psdf_dev_spec_op);
+/* ---------------- Vector implementation procedures ---------------- */
+
+ /* Imager state */
+int psdf_setlinewidth(gx_device_vector * vdev, double width);
+int psdf_setlinecap(gx_device_vector * vdev, gs_line_cap cap);
+int psdf_setlinejoin(gx_device_vector * vdev, gs_line_join join);
+int psdf_setmiterlimit(gx_device_vector * vdev, double limit);
+int psdf_setdash(gx_device_vector * vdev, const float *pattern,
+ uint count, double offset);
+int psdf_setflat(gx_device_vector * vdev, double flatness);
+int psdf_setlogop(gx_device_vector * vdev, gs_logical_operation_t lop,
+ gs_logical_operation_t diff);
+
+ /* Paths */
+#define psdf_dopath gdev_vector_dopath
+int psdf_dorect(gx_device_vector * vdev, fixed x0, fixed y0, fixed x1,
+ fixed y1, gx_path_type_t type);
+int psdf_beginpath(gx_device_vector * vdev, gx_path_type_t type);
+int psdf_moveto(gx_device_vector * vdev, double x0, double y0,
+ double x, double y, gx_path_type_t type);
+int psdf_lineto(gx_device_vector * vdev, double x0, double y0,
+ double x, double y, gx_path_type_t type);
+int psdf_curveto(gx_device_vector * vdev, double x0, double y0,
+ double x1, double y1, double x2,
+ double y2, double x3, double y3, gx_path_type_t type);
+int psdf_closepath(gx_device_vector * vdev, double x0, double y0,
+ double x_start, double y_start, gx_path_type_t type);
+
+/* ---------------- Binary (image) data procedures ---------------- */
+
+/* Define the structure for writing binary data. */
+typedef struct psdf_binary_writer_s {
+ gs_memory_t *memory;
+ stream *target; /* underlying stream */
+ stream *strm;
+ gx_device_psdf *dev; /* may be unused */
+ /* Due to recently introduced field cos_object_t::input_strm
+ * the binary writer may be simplified significantly.
+ * Keeping the old structure until we have time
+ * for this optimization.
+ */
+} psdf_binary_writer;
+extern_st(st_psdf_binary_writer);
+#define public_st_psdf_binary_writer() /* in gdevpsdu.c */\
+ gs_public_st_ptrs3(st_psdf_binary_writer, psdf_binary_writer,\
+ "psdf_binary_writer", psdf_binary_writer_enum_ptrs,\
+ psdf_binary_writer_reloc_ptrs, target, strm, dev)
+#define psdf_binary_writer_max_ptrs 3
+
+/* Begin writing binary data. */
+int psdf_begin_binary(gx_device_psdf * pdev, psdf_binary_writer * pbw);
+
+/* Add an encoding filter. The client must have allocated the stream state, */
+/* if any, using pdev->v_memory. */
+int psdf_encode_binary(psdf_binary_writer * pbw,
+ const stream_template * template, stream_state * ss);
+
+/* Add a 2-D CCITTFax encoding filter. */
+/* Set EndOfBlock iff the stream is not ASCII85 encoded. */
+int psdf_CFE_binary(psdf_binary_writer * pbw, int w, int h, bool invert);
+
+/*
+ * Acquire parameters, and optionally set up the filter for, a DCTEncode
+ * filter. This is a separate procedure so it can be used to validate
+ * filter parameters when they are set, rather than waiting until they are
+ * used. pbw = NULL means just set up the stream state.
+ */
+int psdf_DCT_filter(gs_param_list *plist /* may be NULL */,
+ stream_state /*stream_DCTE_state*/ *st,
+ int Columns, int Rows, int Colors,
+ psdf_binary_writer *pbw /* may be NULL */);
+
+/* Decive whether to convert an image to RGB. */
+bool psdf_is_converting_image_to_RGB(const gx_device_psdf * pdev,
+ const gs_imager_state * pis, const gs_pixel_image_t * pim);
+
+/* Set up compression and downsampling filters for an image. */
+/* Note that this may modify the image parameters. */
+/* If pctm is NULL, downsampling is not used. */
+/* pis only provides UCR and BG information for CMYK => RGB conversion. */
+int psdf_setup_image_filters(gx_device_psdf *pdev, psdf_binary_writer *pbw,
+ gs_pixel_image_t *pim, const gs_matrix *pctm,
+ const gs_imager_state * pis, bool lossless,
+ bool in_line);
+
+int new_setup_image_filters(gx_device_psdf *pdev, psdf_binary_writer *pbw,
+ gs_pixel_image_t *pim, const gs_matrix *pctm,
+ const gs_imager_state * pis, bool lossless,
+ bool in_line, bool colour_conversion);
+
+/* Set up compression filters for a lossless image, with no downsampling, */
+/* no color space conversion, and only lossless filters. */
+/* Note that this may modify the image parameters. */
+int psdf_setup_lossless_filters(gx_device_psdf *pdev, psdf_binary_writer *pbw,
+ gs_pixel_image_t *pim, bool in_line);
+
+int new_setup_lossless_filters(gx_device_psdf *pdev, psdf_binary_writer *pbw,
+ gs_pixel_image_t *pim, bool in_line, bool colour_conversion);
+
+
+int new_resize_input(psdf_binary_writer *pbw, int width, int num_comps, int bpc_in, int bpc_out);
+
+/* Finish writing binary data. */
+int psdf_end_binary(psdf_binary_writer * pbw);
+
+/* Set up image compression chooser. */
+int psdf_setup_compression_chooser(psdf_binary_writer *pbw,
+ gx_device_psdf *pdev,
+ int width, int height, int depth,
+ int bits_per_sample);
+
+/* Set up an "image to mask" filter. */
+int psdf_setup_image_to_mask_filter(psdf_binary_writer *pbw, gx_device_psdf *pdev,
+ int width, int height, int depth, int bits_per_sample, uint *MaskColor);
+
+/* Set up an image colors filter. */
+int psdf_setup_image_colors_filter(psdf_binary_writer *pbw,
+ gx_device_psdf *pdev, gs_pixel_image_t * pim,
+ const gs_imager_state *pis);
+
+/* ---------------- Symbolic data printing ---------------- */
+
+/* Backward compatibility definitions. */
+#define psdf_write_string(s, str, size, print_ok)\
+ s_write_ps_string(s, str, size, print_ok)
+#define psdf_alloc_position_stream(ps, mem)\
+ s_alloc_position_stream(ps, mem)
+#define psdf_alloc_param_printer(pplist, ppp, s, mem)\
+ s_alloc_param_printer(pplist, ppp, s, mem)
+#define psdf_free_param_printer(plist)\
+ s_free_param_printer(plist)
+
+/* ---------------- Other procedures ---------------- */
+
+/* Define the commands for setting the fill or stroke color. */
+typedef struct psdf_set_color_commands_s {
+ const char *setgray;
+ const char *setrgbcolor;
+ const char *setcmykcolor;
+ const char *setcolorspace;
+ const char *setcolor;
+ const char *setcolorn;
+} psdf_set_color_commands_t;
+/* Define the standard color-setting commands (with PDF names). */
+extern const psdf_set_color_commands_t
+ psdf_set_fill_color_commands, psdf_set_stroke_color_commands;
+
+/*
+ * Adjust a gx_color_index to compensate for the fact that the bit pattern
+ * of gx_color_index isn't representable.
+ */
+gx_color_index psdf_adjust_color_index(gx_device_vector *vdev,
+ gx_color_index color);
+
+/* Set the fill or stroke color. */
+int psdf_set_color(gx_device_vector *vdev, const gx_drawing_color *pdc,
+ const psdf_set_color_commands_t *ppscc, bool UseOldColor);
+/* Round a double value to a specified precision. */
+double psdf_round(double v, int precision, int radix);
+
+/* stubs to disable get_bits, get_bits_rectangle */
+dev_proc_get_bits(psdf_get_bits);
+dev_proc_get_bits_rectangle(psdf_get_bits_rectangle);
+
+/* intercept and ignore overprint compositor creation */
+dev_proc_create_compositor(psdf_create_compositor);
+
+#endif /* gdevpsdf_INCLUDED */
diff --git a/devices/vector/gdevpsdi.c b/devices/vector/gdevpsdi.c
new file mode 100644
index 000000000..fead2ce29
--- /dev/null
+++ b/devices/vector/gdevpsdi.c
@@ -0,0 +1,976 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Image compression for PostScript and PDF writers */
+#include "stdio_.h" /* for jpeglib.h */
+#include "jpeglib_.h" /* for sdct.h */
+#include "math_.h"
+#include "string_.h"
+#include "gx.h"
+#include "gserrors.h"
+#include "gscspace.h"
+#include "gdevpsdf.h"
+#include "gdevpsds.h"
+#include "gxdevmem.h"
+#include "gxcspace.h"
+#include "gsparamx.h"
+#include "strimpl.h"
+#include "scfx.h"
+#include "slzwx.h"
+#include "spngpx.h"
+#include "szlibx.h"
+#include "gsicc_manage.h"
+#ifdef USE_LDF_JB2
+#include "sjbig2_luratech.h"
+#endif
+#ifdef USE_LWF_JP2
+#include "sjpx_luratech.h"
+#endif
+#include "sisparam.h"
+
+/* Define parameter-setting procedures. */
+extern stream_state_proc_put_params(s_CF_put_params, stream_CF_state);
+extern stream_template s_IScale_template;
+
+/* ---------------- Image compression ---------------- */
+
+/*
+ * Add a filter to expand or reduce the pixel width if needed.
+ * At least one of bpc_in and bpc_out is 8; the other is 1, 2, 4, or 8,
+ * except if bpc_out is 8, bpc_in may be 12 (or 16).
+ */
+static int
+pixel_resize(psdf_binary_writer * pbw, int width, int num_components,
+ int bpc_in, int bpc_out)
+{
+ gs_memory_t *mem = pbw->dev->v_memory;
+ const stream_template *templat;
+ stream_1248_state *st;
+ int code;
+
+ if (bpc_out == bpc_in)
+ return 0;
+ if (bpc_in != 8) {
+ static const stream_template *const exts[17] = {
+ 0, &s_1_8_template, &s_2_8_template, 0, &s_4_8_template,
+ 0, 0, 0, 0, 0, 0, 0, &s_12_8_template, 0, 0, 0, &s_16_8_template
+ };
+
+ templat = exts[bpc_in];
+ } else {
+ static const stream_template *const rets[5] = {
+ 0, &s_8_1_template, &s_8_2_template, 0, &s_8_4_template
+ };
+
+ templat = rets[bpc_out];
+ }
+ st = (stream_1248_state *)
+ s_alloc_state(mem, templat->stype, "pixel_resize state");
+ if (st == 0)
+ return_error(gs_error_VMerror);
+ code = psdf_encode_binary(pbw, templat, (stream_state *) st);
+ if (code < 0) {
+ gs_free_object(mem, st, "pixel_resize state");
+ return code;
+ }
+ s_1248_init(st, width, num_components);
+ return 0;
+}
+
+static int
+convert_color(gx_device *pdev, const gs_color_space *pcs, const gs_imager_state * pis,
+ gs_client_color *cc, float c[3])
+{
+ int code;
+ gx_device_color dc;
+
+ cs_restrict_color(cc, pcs);
+ code = pcs->type->remap_color(cc, pcs, &dc, pis, pdev, gs_color_select_texture);
+ if (code < 0)
+ return code;
+ c[0] = (float)((int)(dc.colors.pure >> pdev->color_info.comp_shift[0]) & ((1 << pdev->color_info.comp_bits[0]) - 1));
+ c[1] = (float)((int)(dc.colors.pure >> pdev->color_info.comp_shift[1]) & ((1 << pdev->color_info.comp_bits[1]) - 1));
+ c[2] = (float)((int)(dc.colors.pure >> pdev->color_info.comp_shift[2]) & ((1 << pdev->color_info.comp_bits[2]) - 1));
+ return 0;
+}
+
+/* A heuristic choice of DCT compression parameters - see bug 687174. */
+static int
+choose_DCT_params(gx_device *pdev, const gs_color_space *pcs,
+ const gs_imager_state * pis,
+ gs_c_param_list *list, gs_c_param_list **param,
+ stream_state *st)
+{
+ gx_device_memory mdev;
+ gs_client_color cc;
+ int code;
+ float c[4][3];
+ const float MIN_FLOAT = - MAX_FLOAT;
+ const float domination = (float)0.25;
+ const int one = 1, zero = 0;
+
+ if (pcs->type->num_components(pcs) != 3)
+ return 0;
+ if (*param != NULL) {
+ /* Make a copy of the parameter list since we will modify it. */
+ code = param_list_copy((gs_param_list *)list, (gs_param_list *)*param);
+ if (code < 0)
+ return code;
+ }
+ *param = list;
+
+ /* Create a local memory device for transforming colors to DeviceRGB. */
+ gs_make_mem_device(&mdev, gdev_mem_device_for_bits(24), pdev->memory, 0, NULL);
+ gx_device_retain((gx_device *)&mdev, true); /* prevent freeing */
+ set_linear_color_bits_mask_shift((gx_device *)&mdev);
+ mdev.color_info.separable_and_linear = GX_CINFO_SEP_LIN;
+ /* Set mem device icc profile */
+ gsicc_init_device_profile_struct((gx_device *) &mdev, NULL, 0);
+ if (pis) {
+ /* Check for an RGB-like color space.
+ To recognize that we make a matrix as it were a linear operator,
+ suppress an ununiformity by subtracting the image of {0,0,0},
+ and then check for giagonal domination. */
+ cc.paint.values[0] = cc.paint.values[1] = cc.paint.values[2] = MIN_FLOAT;
+ code = convert_color((gx_device *)&mdev, pcs, pis, &cc, c[3]);
+ if (code < 0)
+ return code;
+ cc.paint.values[0] = MAX_FLOAT; cc.paint.values[1] = MIN_FLOAT; cc.paint.values[2] = MIN_FLOAT;
+ code = convert_color((gx_device *)&mdev, pcs, pis, &cc, c[0]);
+ if (code < 0)
+ return code;
+ cc.paint.values[0] = MIN_FLOAT; cc.paint.values[1] = MAX_FLOAT; cc.paint.values[2] = MIN_FLOAT;
+ code = convert_color((gx_device *)&mdev, pcs, pis, &cc, c[1]);
+ if (code < 0)
+ return code;
+ cc.paint.values[0] = MIN_FLOAT; cc.paint.values[1] = MIN_FLOAT; cc.paint.values[2] = MAX_FLOAT;
+ code = convert_color((gx_device *)&mdev, pcs, pis, &cc, c[2]);
+ if (code < 0)
+ return code;
+ c[0][0] -= c[3][0]; c[0][1] -= c[3][1]; c[0][2] -= c[3][2];
+ c[1][0] -= c[3][0]; c[1][1] -= c[3][1]; c[1][2] -= c[3][2];
+ c[2][0] -= c[3][0]; c[2][1] -= c[3][1]; c[2][2] -= c[3][2];
+ c[0][0] = any_abs(c[0][0]); c[0][1] = any_abs(c[0][1]); c[0][2] = any_abs(c[0][2]);
+ c[1][0] = any_abs(c[1][0]); c[1][1] = any_abs(c[1][1]); c[1][2] = any_abs(c[1][2]);
+ c[2][0] = any_abs(c[2][0]); c[2][1] = any_abs(c[2][1]); c[2][2] = any_abs(c[2][2]);
+ if (c[0][0] * domination > c[0][1] && c[0][0] * domination > c[0][2] &&
+ c[1][1] * domination > c[1][0] && c[1][1] * domination > c[1][2] &&
+ c[2][2] * domination > c[2][0] && c[2][2] * domination > c[2][1]) {
+ /* Yes, it looks like an RGB color space.
+ Replace ColorTransform with 1. */
+ code = param_write_int((gs_param_list *)list, "ColorTransform", &one);
+ if (code < 0)
+ goto error;
+ goto done;
+ }
+
+ /* Check for a Lab-like color space.
+ Colors {v,0,0} should map to grays. */
+ cc.paint.values[0] = MAX_FLOAT; cc.paint.values[1] = cc.paint.values[2] = 0;
+ convert_color((gx_device *)&mdev, pcs, pis, &cc, c[0]);
+ cc.paint.values[0] /= 2;
+ convert_color((gx_device *)&mdev, pcs, pis, &cc, c[1]);
+ cc.paint.values[0] /= 2;
+ convert_color((gx_device *)&mdev, pcs, pis, &cc, c[2]);
+ c[0][1] -= c[0][0]; c[0][2] -= c[0][0];
+ c[1][1] -= c[1][0]; c[1][2] -= c[1][0];
+ c[2][1] -= c[2][0]; c[2][2] -= c[2][0];
+ c[0][1] = any_abs(c[0][1]); c[0][2] = any_abs(c[0][2]);
+ c[1][1] = any_abs(c[1][1]); c[1][2] = any_abs(c[1][2]);
+ c[2][1] = any_abs(c[2][1]); c[2][2] = any_abs(c[2][2]);
+ }
+ if (pis && c[0][0] * domination > c[0][1] && c[0][0] * domination > c[0][2] &&
+ c[1][0] * domination > c[1][1] && c[1][0] * domination > c[1][2] &&
+ c[2][0] * domination > c[2][1] && c[2][0] * domination > c[2][2]) {
+ /* Yes, it looks like an Lab color space.
+ Replace ColorTransform with 0. */
+ code = param_write_int((gs_param_list *)list, "ColorTransform", &zero);
+ if (code < 0)
+ goto error;
+ } else {
+ /* Unknown color space type.
+ Replace /HSamples [1 1 1 1] /VSamples [1 1 1 1] to avoid quality degradation. */
+ gs_param_string a;
+ static const byte v[4] = {1, 1, 1, 1};
+
+ a.data = v;
+ a.size = 4;
+ a.persistent = true;
+ code = param_write_string((gs_param_list *)list, "HSamples", &a);
+ if (code < 0)
+ goto error;
+ code = param_write_string((gs_param_list *)list, "VSamples", &a);
+ if (code < 0)
+ goto error;
+ }
+done:
+ gs_c_param_list_read(list);
+ gx_device_finalize(pdev->memory, &mdev);
+ return 0;
+error:
+ gx_device_finalize(pdev->memory, &mdev);
+ return code;
+}
+
+/* Add the appropriate image compression filter, if any. */
+static int
+setup_image_compression(psdf_binary_writer *pbw, const psdf_image_params *pdip,
+ const gs_pixel_image_t * pim, const gs_imager_state * pis,
+ bool lossless)
+{
+ gx_device_psdf *pdev = pbw->dev;
+ gs_memory_t *mem = pdev->v_memory;
+ const stream_template *templat = pdip->filter_template;
+ const stream_template *lossless_template =
+ (pdev->params.UseFlateCompression &&
+ pdev->version >= psdf_version_ll3 ?
+ &s_zlibE_template : &s_LZWE_template);
+ const gs_color_space *pcs = pim->ColorSpace; /* null if mask */
+ int Colors = (pcs ? gs_color_space_num_components(pcs) : 1);
+ bool Indexed =
+ (pcs != 0 &&
+ gs_color_space_get_index(pcs) == gs_color_space_index_Indexed);
+ gs_c_param_list *dict = pdip->Dict;
+ stream_state *st;
+ int code;
+
+# ifdef USE_LWF_JP2
+ if (lossless && templat == &s_jpxe_template && !Indexed)
+ lossless_template = &s_jpxe_template;
+# endif
+ if (!pdip->Encode) /* no compression */
+ return 0;
+ if (pdip->AutoFilter) {
+ /*
+ * Disregard the requested filter. What we should do at this point
+ * is analyze the image to decide whether to use JPEG encoding
+ * (DCTEncode with ACSDict) or the lossless filter. However, since
+ * we don't buffer the entire image, we'll make the choice on-fly,
+ * forking the image data into 3 streams : (1) JPEG, (2) lossless,
+ * (3) the compression chooser. In this case this function is
+ * called 2 times with different values of the 'lossless' argument.
+ */
+ if (lossless) {
+ templat = lossless_template;
+ } else if (templat == NULL || templat == &s_zlibE_template ||
+ templat == &s_LZWE_template) {
+ templat = &s_DCTE_template;
+ }
+ dict = pdip->ACSDict;
+ } else if (!lossless)
+ return gs_error_rangecheck; /* Reject the alternative stream. */
+ if (pdev->version < psdf_version_ll3 && templat == &s_zlibE_template)
+ templat = lossless_template;
+ if (dict != NULL) /* Some interpreters don't supply filter parameters. */
+ gs_c_param_list_read(dict); /* ensure param list is in read mode */
+ if (templat == 0) /* no compression */
+ return 0;
+ if (pim->Width < 200 && pim->Height < 200) /* Prevent a fixed overflow. */
+ if (pim->Width * pim->Height * Colors * pim->BitsPerComponent <= 160)
+ return 0; /* not worth compressing */
+ /* Only use DCTE for 8-bit, non-Indexed data. */
+ if (templat == &s_DCTE_template) {
+ if (Indexed ||
+ !(pdip->Downsample ?
+ pdip->Depth == 8 ||
+ (pdip->Depth == -1 && pim->BitsPerComponent == 8) :
+ pim->BitsPerComponent == 8)
+ ) {
+ /* Use LZW/Flate instead. */
+ templat = lossless_template;
+ }
+ }
+ st = s_alloc_state(mem, templat->stype, "setup_image_compression");
+ if (st == 0)
+ return_error(gs_error_VMerror);
+
+ st->templat = templat;
+
+ if (templat->set_defaults)
+ (*templat->set_defaults) (st);
+ if (templat == &s_CFE_template) {
+ stream_CFE_state *const ss = (stream_CFE_state *) st;
+
+ if (pdip->Dict != 0 && pdip->filter_template == templat) {
+ s_CF_put_params((gs_param_list *)pdip->Dict,
+ (stream_CF_state *)ss); /* ignore errors */
+ } else {
+ ss->K = -1;
+ ss->BlackIs1 = true;
+ }
+ ss->Columns = pim->Width;
+ ss->Rows = (ss->EndOfBlock ? 0 : pim->Height);
+ } else if ((templat == &s_LZWE_template ||
+ templat == &s_zlibE_template) &&
+ pdev->version >= psdf_version_ll3) {
+ /* If not Indexed, add a PNGPredictor filter. */
+ if (!Indexed) {
+ code = psdf_encode_binary(pbw, templat, st);
+ if (code < 0)
+ goto fail;
+ templat = &s_PNGPE_template;
+ st = s_alloc_state(mem, templat->stype, "setup_image_compression");
+ if (st == 0) {
+ code = gs_note_error(gs_error_VMerror);
+ goto fail;
+ }
+ st->templat = templat;
+ if (templat->set_defaults)
+ (*templat->set_defaults) (st);
+ {
+ stream_PNGP_state *const ss = (stream_PNGP_state *) st;
+
+ ss->Colors = Colors;
+ ss->Columns = pim->Width;
+ }
+ }
+ } else if (templat == &s_DCTE_template) {
+ gs_c_param_list list, *param = dict;
+
+ gs_c_param_list_write(&list, mem);
+ code = choose_DCT_params((gx_device *)pbw->dev, pcs, pis, &list, &param, st);
+ if (code < 0) {
+ gs_c_param_list_release(&list);
+ return code;
+ }
+ code = psdf_DCT_filter((gs_param_list *)param,
+ st, pim->Width, pim->Height, Colors, pbw);
+ gs_c_param_list_release(&list);
+ if (code < 0)
+ goto fail;
+ /* psdf_DCT_filter already did the psdf_encode_binary. */
+ return 0;
+ } else {
+# ifdef USE_LDF_JB2
+ if (templat == &s_jbig2encode_template) {
+ stream_jbig2encode_state *state = (stream_jbig2encode_state *)st;
+
+ state->width = pim->Width;
+ state->height = pim->Height;
+ }
+# endif
+# ifdef USE_LWF_JP2
+ if (templat == &s_jpxe_template) {
+ stream_jpxe_state *state = (stream_jpxe_state *)st;
+ int ncomps = pim->ColorSpace->type->num_components(pim->ColorSpace);
+
+ /* HACK : We choose a JPX color space from the number of components :
+ CIEBasedA goes as gs_jpx_cs_gray,
+ CIEBasedABC and DeviceN(3) go as gs_jpx_cs_rgb,
+ CIEBasedABCD and DeviceN(4) go as gs_jpx_cs_cmyk.
+ */
+ switch (ncomps) {
+ case 1 : state->colorspace = gs_jpx_cs_gray; break;
+ case 3 : state->colorspace = gs_jpx_cs_rgb; break;
+ case 4 : state->colorspace = gs_jpx_cs_cmyk; break;
+ default:
+ return_error(gs_error_unregistered); /* Must not happen. */
+ }
+ state->width = pim->Width;
+ state->height = pim->Height;
+ state->bpc = pim->BitsPerComponent;
+ state->components = ncomps;
+ state->lossless = lossless;
+ /* Other encode parameters are not implemented yet.
+ Therefore ACSDict is being ignored. */
+ }
+# endif
+ }
+ code = psdf_encode_binary(pbw, templat, st);
+ if (code >= 0)
+ return 0;
+ fail:
+ gs_free_object(mem, st, "setup_image_compression");
+ return code;
+}
+
+/* Determine whether an image should be downsampled. */
+static bool
+do_downsample(const psdf_image_params *pdip, const gs_pixel_image_t *pim,
+ double resolution)
+{
+ double factor = resolution / pdip->Resolution;
+
+ return (pdip->Downsample && factor >= pdip->DownsampleThreshold &&
+ factor <= pim->Width && factor <= pim->Height);
+}
+
+/* Add downsampling, antialiasing, and compression filters. */
+/* Uses AntiAlias, Depth, DownsampleThreshold, DownsampleType, Resolution. */
+/* Assumes do_downsampling() is true. */
+static int
+setup_downsampling(psdf_binary_writer * pbw, const psdf_image_params * pdip,
+ gs_pixel_image_t * pim, const gs_imager_state * pis,
+ double resolution, bool lossless)
+{
+ gx_device_psdf *pdev = pbw->dev;
+ const stream_template *templat = &s_Subsample_template;
+ float factor = resolution / pdip->Resolution;
+ int orig_bpc = pim->BitsPerComponent;
+ int orig_width = pim->Width;
+ int orig_height = pim->Height;
+ stream_state *st;
+ int code;
+
+ switch (pdip->DownsampleType) {
+ case ds_Subsample:
+ templat = &s_Subsample_template;
+ break;
+ case ds_Average:
+ templat = &s_Average_template;
+ break;
+ case ds_Bicubic:
+ templat = &s_IScale_template;
+ /* We now use the Mitchell filter instead of the 'bicubic' filter
+ * because it gives better results.
+ templat = &s_Bicubic_template;
+ */
+ break;
+ default:
+ dmprintf1(pdev->v_memory, "Unsupported downsample type %d\n", pdip->DownsampleType);
+ return gs_note_error(gs_error_rangecheck);
+ }
+
+ if (pdip->DownsampleType != ds_Bicubic) {
+ /* If downsample type is not bicubic, ensure downsample factor is
+ * an integer if we're close to one (< 0.1) or silently switch to
+ * bicubic transform otherwise. See bug #693917. */
+ float rfactor = floor(factor + 0.5);
+ if (fabs(rfactor-factor) < 0.1)
+ factor = rfactor; /* round factor to nearest integer */
+ else
+ templat = &s_Bicubic_template; /* switch to bicubic */
+ }
+
+ st = s_alloc_state(pdev->v_memory, templat->stype,
+ "setup_downsampling");
+ if (st == 0)
+ return_error(gs_error_VMerror);
+ if (templat->set_defaults)
+ templat->set_defaults(st);
+
+ if (templat != &s_IScale_template)
+ {
+ stream_Downsample_state *const ss = (stream_Downsample_state *) st;
+
+ ss->Colors =
+ (pim->ColorSpace == 0 ? 1 /*mask*/ :
+ gs_color_space_num_components(pim->ColorSpace));
+ ss->WidthIn = pim->Width;
+ ss->HeightIn = pim->Height;
+ ss->XFactor = ss->YFactor = factor;
+ ss->AntiAlias = pdip->AntiAlias;
+ ss->padX = ss->padY = false; /* should be true */
+
+ if (templat->init)
+ templat->init(st);
+ pim->BitsPerComponent = pdip->Depth;
+ pim->Width = s_Downsample_size_out(pim->Width, factor, false);
+ pim->Height = s_Downsample_size_out(pim->Height, factor, false);
+ gs_matrix_scale(&pim->ImageMatrix, (double)pim->Width / orig_width,
+ (double)pim->Height / orig_height,
+ &pim->ImageMatrix);
+ /****** NO ANTI-ALIASING YET ******/
+ if ((code = setup_image_compression(pbw, pdip, pim, pis, lossless)) < 0 ||
+ (code = pixel_resize(pbw, pim->Width, ss->Colors,
+ 8, pdip->Depth)) < 0 ||
+ (code = psdf_encode_binary(pbw, templat, st)) < 0 ||
+ (code = pixel_resize(pbw, orig_width, ss->Colors,
+ orig_bpc, 8)) < 0
+ ) {
+ gs_free_object(pdev->v_memory, st, "setup_image_compression");
+ return code;
+ }
+ } else {
+ /* The setup for the Mitchell filter is quite different to the other filters
+ * because it isn't one of ours.
+ */
+ int Colors = (pim->ColorSpace == 0 ? 1 /*mask*/ :
+ gs_color_space_num_components(pim->ColorSpace));
+
+ stream_image_scale_state *ss = (stream_image_scale_state *)st;
+
+ ss->params.EntireWidthIn = ss->params.WidthIn = ss->params.PatchWidthIn = pim->Width;
+ ss->params.EntireHeightIn = ss->params.HeightIn = ss->params.PatchHeightIn = pim->Height;
+ ss->params.EntireWidthOut = ss->params.WidthOut = ss->params.PatchWidthOut = s_Downsample_size_out(pim->Width, factor, false);
+ ss->params.EntireHeightOut = ss->params.HeightOut = ss->params.PatchHeightOut = s_Downsample_size_out(pim->Height, factor, false);
+ ss->params.BitsPerComponentIn = ss->params.BitsPerComponentOut = pdip->Depth;
+ ss->params.spp_interp = ss->params.spp_decode = Colors;
+ ss->params.TopMargin = ss->params.LeftMarginIn = ss->params.LeftMarginOut = 0;
+ ss->params.src_y_offset = 0;
+ ss->params.early_cm = true;
+ ss->params.MaxValueIn = ss->params.MaxValueOut = (int)pow(2, pdip->Depth);;
+
+ if (templat->init)
+ templat->init(st);
+ pim->Width = s_Downsample_size_out(pim->Width, factor, false);
+ pim->Height = s_Downsample_size_out(pim->Height, factor, false);
+ pim->BitsPerComponent = pdip->Depth;
+ gs_matrix_scale(&pim->ImageMatrix, (double)pim->Width / orig_width,
+ (double)pim->Height / orig_height,
+ &pim->ImageMatrix);
+ /****** NO ANTI-ALIASING YET ******/
+ if ((code = setup_image_compression(pbw, pdip, pim, pis, lossless)) < 0 ||
+ (code = pixel_resize(pbw, pim->Width, Colors,
+ 8, pdip->Depth)) < 0 ||
+ (code = psdf_encode_binary(pbw, templat, st)) < 0 ||
+ (code = pixel_resize(pbw, orig_width, Colors,
+ orig_bpc, 8)) < 0
+ ) {
+ gs_free_object(pdev->v_memory, st, "setup_image_compression");
+ return code;
+ }
+ }
+ return 0;
+}
+
+/* Decive whether to convert an image to RGB. */
+bool
+psdf_is_converting_image_to_RGB(const gx_device_psdf * pdev,
+ const gs_imager_state * pis, const gs_pixel_image_t * pim)
+{
+ return pdev->params.ConvertCMYKImagesToRGB &&
+ pis != 0 && pim->ColorSpace &&
+ (gs_color_space_get_index(pim->ColorSpace) == gs_color_space_index_DeviceCMYK ||
+ (gs_color_space_get_index(pim->ColorSpace) == gs_color_space_index_ICC
+ && gsicc_get_default_type(pim->ColorSpace->cmm_icc_profile_data) ==
+ gs_color_space_index_DeviceCMYK));
+}
+
+static inline void
+adjust_auto_filter_strategy(gx_device_psdf *pdev,
+ psdf_image_params *params, gs_c_param_list *plist,
+ const gs_pixel_image_t * pim, bool in_line)
+{
+#ifdef USE_LWF_JP2
+ if (!in_line && params->Depth > 1 && pdev->ParamCompatibilityLevel >= 1.5 &&
+ pim->ColorSpace->type->index != gs_color_space_index_Indexed &&
+ params->AutoFilter &&
+ params->AutoFilterStrategy != af_Jpeg) {
+ params->Filter = "/JPXEncode";
+ params->filter_template = &s_jpxe_template;
+ params->Dict = plist;
+ }
+#endif
+}
+
+static inline void
+adjust_auto_filter_strategy_mono(gx_device_psdf *pdev,
+ psdf_image_params *params, gs_c_param_list *plist,
+ const gs_pixel_image_t * pim, bool in_line)
+{
+#ifdef USE_LDF_JB2
+ if (!in_line && pdev->ParamCompatibilityLevel >= 1.5 &&
+ params->AutoFilter &&
+ pim->ColorSpace->type->index != gs_color_space_index_Indexed) {
+ params->Filter = "/JBIG2Encode";
+ params->filter_template = &s_jbig2encode_template;
+ params->Dict = plist;
+ }
+#endif
+}
+
+/* Set up compression and downsampling filters for an image. */
+/* Note that this may modify the image parameters. */
+int
+psdf_setup_image_filters(gx_device_psdf * pdev, psdf_binary_writer * pbw,
+ gs_pixel_image_t * pim, const gs_matrix * pctm,
+ const gs_imager_state * pis, bool lossless, bool in_line)
+{
+ /*
+ * The following algorithms are per Adobe Tech Note # 5151,
+ * "Acrobat Distiller Parameters", revised 16 September 1996
+ * for Acrobat(TM) Distiller(TM) 3.0.
+ *
+ * The control structure is a little tricky, because filter
+ * pipelines must be constructed back-to-front.
+ */
+ int code = 0;
+ psdf_image_params params;
+ int bpc = pim->BitsPerComponent;
+ int bpc_out = pim->BitsPerComponent = min(bpc, 8);
+ int ncomp;
+ double resolution;
+
+ /*
+ * The Adobe documentation doesn't say this, but mask images are
+ * compressed on the same basis as 1-bit-deep monochrome images,
+ * except that anti-aliasing (resolution/depth tradeoff) is not
+ * allowed.
+ */
+ if (pim->ColorSpace == NULL) { /* mask image */
+ params = pdev->params.MonoImage;
+ params.Depth = 1;
+ ncomp = 1;
+ } else {
+ ncomp = gs_color_space_num_components(pim->ColorSpace);
+ if (pim->ColorSpace->type->index == gs_color_space_index_Indexed) {
+ params = pdev->params.ColorImage;
+ /* Ensure we don't use JPEG on a /Indexed colour space */
+ params.AutoFilter = false;
+ params.Filter = "FlateEncode";
+ } else {
+ if (ncomp == 1) {
+ if (bpc == 1)
+ params = pdev->params.MonoImage;
+ else
+ params = pdev->params.GrayImage;
+ if (params.Depth == -1)
+ params.Depth = bpc;
+ } else {
+ params = pdev->params.ColorImage;
+ /* params.Depth is reset below */
+ }
+ }
+ }
+
+ /*
+ * We can compute the image resolution by:
+ * W / (W * ImageMatrix^-1 * CTM / HWResolution).
+ * We can replace W by 1 to simplify the computation.
+ */
+ if (pctm == 0)
+ resolution = -1;
+ else {
+ gs_point pt;
+
+ /* We could do both X and Y, but why bother? */
+ code = gs_distance_transform_inverse(1.0, 0.0, &pim->ImageMatrix, &pt);
+ if (code < 0)
+ return code;
+ gs_distance_transform(pt.x, pt.y, pctm, &pt);
+ resolution = 1.0 / hypot(pt.x / pdev->HWResolution[0],
+ pt.y / pdev->HWResolution[1]);
+ }
+ if (ncomp == 1 && pim->ColorSpace && pim->ColorSpace->type->index != gs_color_space_index_Indexed) {
+ /* Monochrome, gray, or mask */
+ /* Check for downsampling. */
+ if (do_downsample(&params, pim, resolution)) {
+ /* Use the downsampled depth, not the original data depth. */
+ if (params.Depth == 1) {
+ params.Filter = pdev->params.MonoImage.Filter;
+ params.filter_template = pdev->params.MonoImage.filter_template;
+ params.Dict = pdev->params.MonoImage.Dict;
+ adjust_auto_filter_strategy_mono(pdev, &params, pdev->params.MonoImage.Dict, pim, in_line);
+ } else {
+ params.Filter = pdev->params.GrayImage.Filter;
+ params.filter_template = pdev->params.GrayImage.filter_template;
+ params.Dict = pdev->params.GrayImage.Dict;
+ adjust_auto_filter_strategy(pdev, &params, pdev->params.GrayImage.Dict, pim, in_line);
+ }
+ code = setup_downsampling(pbw, &params, pim, pis, resolution, lossless);
+ } else {
+ adjust_auto_filter_strategy(pdev, &params, pdev->params.GrayImage.Dict, pim, in_line);
+ code = setup_image_compression(pbw, &params, pim, pis, lossless);
+ }
+ if (code < 0)
+ return code;
+ code = pixel_resize(pbw, pim->Width, ncomp, bpc, bpc_out);
+ } else {
+ /* Color */
+ bool cmyk_to_rgb = psdf_is_converting_image_to_RGB(pdev, pis, pim);
+
+ if (cmyk_to_rgb) {
+ gs_memory_t *mem = pdev->v_memory;
+
+ /* {csrc} decref old colorspace? */
+ rc_decrement_only_cs(pim->ColorSpace, "psdf_setup_image_filters");
+ pim->ColorSpace = gs_cspace_new_DeviceRGB(mem);
+ }
+ if (params.Depth == -1)
+ params.Depth = (cmyk_to_rgb ? 8 : bpc_out);
+ if (do_downsample(&params, pim, resolution)) {
+ adjust_auto_filter_strategy(pdev, &params, pdev->params.ColorImage.Dict, pim, in_line);
+ code = setup_downsampling(pbw, &params, pim, pis, resolution, lossless);
+ } else {
+ adjust_auto_filter_strategy(pdev, &params, pdev->params.ColorImage.Dict, pim, in_line);
+ code = setup_image_compression(pbw, &params, pim, pis, lossless);
+ }
+ if (code < 0)
+ return code;
+ if (cmyk_to_rgb) {
+ gs_memory_t *mem = pdev->v_memory;
+ stream_C2R_state *ss = (stream_C2R_state *)
+ s_alloc_state(mem, s_C2R_template.stype, "C2R state");
+ int code = pixel_resize(pbw, pim->Width, 3, 8, bpc_out);
+
+ if (code < 0 ||
+ (code = psdf_encode_binary(pbw, &s_C2R_template,
+ (stream_state *) ss)) < 0 ||
+ (code = pixel_resize(pbw, pim->Width, 4, bpc, 8)) < 0
+ )
+ return code;
+ s_C2R_init(ss, pis);
+ } else {
+ code = pixel_resize(pbw, pim->Width, ncomp, bpc, bpc_out);
+ if (code < 0)
+ return code;
+ }
+ }
+ return code;
+}
+
+/* Set up compression filters for a lossless image, with no downsampling, */
+/* no color space conversion, and only lossless filters. */
+/* Note that this may modify the image parameters. */
+int
+psdf_setup_lossless_filters(gx_device_psdf *pdev, psdf_binary_writer *pbw,
+ gs_pixel_image_t *pim, bool in_line)
+{
+ /*
+ * Set up a device with modified parameters for computing the image
+ * compression filters. Don't allow downsampling or lossy compression.
+ */
+ gx_device_psdf ipdev;
+
+ ipdev = *pdev;
+ ipdev.params.ColorImage.AutoFilter = false;
+ ipdev.params.ColorImage.Downsample = false;
+ ipdev.params.ColorImage.Filter = "FlateEncode";
+ ipdev.params.ColorImage.filter_template = &s_zlibE_template;
+ ipdev.params.ConvertCMYKImagesToRGB = false;
+ ipdev.params.GrayImage.AutoFilter = false;
+ ipdev.params.GrayImage.Downsample = false;
+ ipdev.params.GrayImage.Filter = "FlateEncode";
+ ipdev.params.GrayImage.filter_template = &s_zlibE_template;
+ return psdf_setup_image_filters(&ipdev, pbw, pim, NULL, NULL, true, in_line);
+}
+
+/* Set up image compression chooser. */
+int
+psdf_setup_compression_chooser(psdf_binary_writer *pbw, gx_device_psdf *pdev,
+ int width, int height, int depth, int bits_per_sample)
+{
+ int code;
+ stream_state *ss = s_alloc_state(pdev->memory, s_compr_chooser_template.stype,
+ "psdf_setup_compression_chooser");
+
+ if (ss == 0)
+ return_error(gs_error_VMerror);
+ ss->templat = &s_compr_chooser_template;
+
+ pbw->memory = pdev->memory;
+ pbw->strm = pdev->strm; /* just a stub - will not write to it. */
+ pbw->dev = pdev;
+ pbw->target = pbw->strm; /* Since s_add_filter may insert NullEncode to comply buffering,
+ will need to close a chain of filetrs. */
+ code = psdf_encode_binary(pbw, &s_compr_chooser_template, ss);
+ if (code < 0)
+ return code;
+ code = s_compr_chooser_set_dimensions((stream_compr_chooser_state *)ss,
+ width, height, depth, bits_per_sample);
+ return code;
+}
+
+/* Set up an "image to mask" filter. */
+int
+psdf_setup_image_to_mask_filter(psdf_binary_writer *pbw, gx_device_psdf *pdev,
+ int width, int height, int depth, int bits_per_sample, uint *MaskColor)
+{
+ int code;
+ stream_state *ss = s_alloc_state(pdev->memory, s__image_colors_template.stype,
+ "psdf_setup_image_colors_filter");
+
+ if (ss == 0)
+ return_error(gs_error_VMerror);
+ pbw->memory = pdev->memory;
+ pbw->dev = pdev;
+ code = psdf_encode_binary(pbw, &s__image_colors_template, ss);
+ if (code < 0)
+ return code;
+ s_image_colors_set_dimensions((stream_image_colors_state *)ss,
+ width, height, depth, bits_per_sample);
+ s_image_colors_set_mask_colors((stream_image_colors_state *)ss, MaskColor);
+ return 0;
+}
+
+/* Set up an image colors filter. */
+int
+psdf_setup_image_colors_filter(psdf_binary_writer *pbw,
+ gx_device_psdf *pdev, gs_pixel_image_t * pim,
+ const gs_imager_state *pis)
+{ /* fixme: currently it's a stub convertion to mask. */
+ int code;
+ stream_state *ss = s_alloc_state(pdev->memory, s__image_colors_template.stype,
+ "psdf_setup_image_colors_filter");
+ int i;
+
+ if (ss == 0)
+ return_error(gs_error_VMerror);
+ pbw->memory = pdev->memory;
+ pbw->dev = pdev;
+ code = psdf_encode_binary(pbw, &s__image_colors_template, ss);
+ if (code < 0)
+ return code;
+ s_image_colors_set_dimensions((stream_image_colors_state *)ss,
+ pim->Width, pim->Height,
+ gs_color_space_num_components(pim->ColorSpace),
+ pim->BitsPerComponent);
+ s_image_colors_set_color_space((stream_image_colors_state *)ss,
+ (gx_device *)pdev, pim->ColorSpace, pis, pim->Decode);
+ pim->BitsPerComponent = pdev->color_info.comp_bits[0]; /* Same precision for all components. */
+ for (i = 0; i < pdev->color_info.num_components; i++) {
+ pim->Decode[i * 2 + 0] = 0;
+ pim->Decode[i * 2 + 1] = 1;
+ }
+ return 0;
+}
+
+/* Set up compression and downsampling filters for an image. */
+/* Note that this may modify the image parameters. */
+int
+new_setup_image_filters(gx_device_psdf * pdev, psdf_binary_writer * pbw,
+ gs_pixel_image_t * pim, const gs_matrix * pctm,
+ const gs_imager_state * pis, bool lossless, bool in_line,
+ bool colour_conversion)
+{
+ /*
+ * The following algorithms are per Adobe Tech Note # 5151,
+ * "Acrobat Distiller Parameters", revised 16 September 1996
+ * for Acrobat(TM) Distiller(TM) 3.0.
+ *
+ * The control structure is a little tricky, because filter
+ * pipelines must be constructed back-to-front.
+ */
+ int code = 0;
+ psdf_image_params params;
+ int bpc = pim->BitsPerComponent;
+ int bpc_out = pim->BitsPerComponent = min(bpc, 8);
+ int ncomp;
+ double resolution;
+
+ /*
+ * The Adobe documentation doesn't say this, but mask images are
+ * compressed on the same basis as 1-bit-deep monochrome images,
+ * except that anti-aliasing (resolution/depth tradeoff) is not
+ * allowed.
+ */
+ if (pim->ColorSpace == NULL) { /* mask image */
+ params = pdev->params.MonoImage;
+ params.Depth = 1;
+ ncomp = 1;
+ } else {
+ ncomp = gs_color_space_num_components(pim->ColorSpace);
+ if (pim->ColorSpace->type->index == gs_color_space_index_Indexed) {
+ params = pdev->params.ColorImage;
+ /* Ensure we don't use JPEG on a /Indexed colour space */
+ params.AutoFilter = false;
+ params.Filter = "FlateEncode";
+ } else {
+ if (ncomp == 1) {
+ if (bpc == 1)
+ params = pdev->params.MonoImage;
+ else
+ params = pdev->params.GrayImage;
+ if (params.Depth == -1)
+ params.Depth = bpc;
+ } else {
+ params = pdev->params.ColorImage;
+ /* params.Depth is reset below */
+ }
+ }
+ }
+
+ /*
+ * We can compute the image resolution by:
+ * W / (W * ImageMatrix^-1 * CTM / HWResolution).
+ * We can replace W by 1 to simplify the computation.
+ */
+ if (pctm == 0)
+ resolution = -1;
+ else {
+ gs_point pt;
+
+ /* We could do both X and Y, but why bother? */
+ code = gs_distance_transform_inverse(1.0, 0.0, &pim->ImageMatrix, &pt);
+ if (code < 0)
+ return code;
+ gs_distance_transform(pt.x, pt.y, pctm, &pt);
+ resolution = 1.0 / hypot(pt.x / pdev->HWResolution[0],
+ pt.y / pdev->HWResolution[1]);
+ }
+ if (ncomp == 1 && pim->ColorSpace && pim->ColorSpace->type->index != gs_color_space_index_Indexed) {
+ /* Monochrome, gray, or mask */
+ /* Check for downsampling. */
+ if (do_downsample(&params, pim, resolution)) {
+ /* Use the downsampled depth, not the original data depth. */
+ if (params.Depth == 1) {
+ params.Filter = pdev->params.MonoImage.Filter;
+ params.filter_template = pdev->params.MonoImage.filter_template;
+ params.Dict = pdev->params.MonoImage.Dict;
+ adjust_auto_filter_strategy_mono(pdev, &params, pdev->params.MonoImage.Dict, pim, in_line);
+ } else {
+ params.Filter = pdev->params.GrayImage.Filter;
+ params.filter_template = pdev->params.GrayImage.filter_template;
+ params.Dict = pdev->params.GrayImage.Dict;
+ adjust_auto_filter_strategy(pdev, &params, pdev->params.GrayImage.Dict, pim, in_line);
+ }
+ code = setup_downsampling(pbw, &params, pim, pis, resolution, lossless);
+ } else {
+ adjust_auto_filter_strategy(pdev, &params, pdev->params.GrayImage.Dict, pim, in_line);
+ code = setup_image_compression(pbw, &params, pim, pis, lossless);
+ }
+ if (code < 0)
+ return code;
+ code = pixel_resize(pbw, pim->Width, ncomp, bpc, bpc_out);
+ } else {
+ /* Color */
+ if (params.Depth == -1)
+ params.Depth = (colour_conversion ? 8 : bpc_out);
+ if (do_downsample(&params, pim, resolution)) {
+ adjust_auto_filter_strategy(pdev, &params, pdev->params.ColorImage.Dict, pim, in_line);
+ code = setup_downsampling(pbw, &params, pim, pis, resolution, lossless);
+ } else {
+ adjust_auto_filter_strategy(pdev, &params, pdev->params.ColorImage.Dict, pim, in_line);
+ code = setup_image_compression(pbw, &params, pim, pis, lossless);
+ }
+ if (code < 0)
+ return code;
+ code = pixel_resize(pbw, pim->Width, ncomp, bpc, bpc_out);
+ if (code < 0)
+ return code;
+ }
+ return code;
+}
+
+int
+new_setup_lossless_filters(gx_device_psdf *pdev, psdf_binary_writer *pbw,
+ gs_pixel_image_t *pim, bool in_line,
+ bool colour_conversion)
+{
+ /*
+ * Set up a device with modified parameters for computing the image
+ * compression filters. Don't allow downsampling or lossy compression.
+ */
+ gx_device_psdf ipdev;
+
+ ipdev = *pdev;
+ ipdev.params.ColorImage.AutoFilter = false;
+ ipdev.params.ColorImage.Downsample = false;
+ ipdev.params.ColorImage.Filter = "FlateEncode";
+ ipdev.params.ColorImage.filter_template = &s_zlibE_template;
+ ipdev.params.ConvertCMYKImagesToRGB = false;
+ ipdev.params.GrayImage.AutoFilter = false;
+ ipdev.params.GrayImage.Downsample = false;
+ ipdev.params.GrayImage.Filter = "FlateEncode";
+ ipdev.params.GrayImage.filter_template = &s_zlibE_template;
+ return new_setup_image_filters(&ipdev, pbw, pim, NULL, NULL, true, in_line, colour_conversion);
+}
+
+int new_resize_input(psdf_binary_writer *pbw, int width, int num_comps, int bpc_in, int bpc_out)
+{
+ return pixel_resize(pbw, width, num_comps, bpc_in, bpc_out);
+}
diff --git a/devices/vector/gdevpsdp.c b/devices/vector/gdevpsdp.c
new file mode 100644
index 000000000..b0b611937
--- /dev/null
+++ b/devices/vector/gdevpsdp.c
@@ -0,0 +1,1198 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* (Distiller) parameter handling for PostScript and PDF writers */
+#include "string_.h"
+#include "jpeglib_.h" /* for sdct.h */
+#include "gx.h"
+#include "gserrors.h"
+#include "gsutil.h"
+#include "gxdevice.h"
+#include "gsparamx.h"
+#include "gdevpsdf.h"
+#include "strimpl.h" /* for short-sighted compilers */
+#include "scfx.h"
+#include "sdct.h"
+#include "slzwx.h"
+#include "srlx.h"
+#include "szlibx.h"
+#include "gdevvec.h"
+#ifdef USE_LDF_JB2
+#include "sjbig2_luratech.h"
+#endif
+#ifdef USE_LWF_JP2
+#include "sjpx_luratech.h"
+#endif
+
+/* Define a (bogus) GC descriptor for gs_param_string. */
+/* The only ones we use are GC-able and not persistent. */
+gs_private_st_composite(st_gs_param_string, gs_param_string, "gs_param_string",
+ param_string_enum_ptrs, param_string_reloc_ptrs);
+static
+ENUM_PTRS_WITH(param_string_enum_ptrs, gs_param_string *pstr) return 0;
+case 0: return ENUM_CONST_STRING(pstr);
+ENUM_PTRS_END
+static
+RELOC_PTRS_WITH(param_string_reloc_ptrs, gs_param_string *pstr)
+{
+ gs_const_string str;
+
+ str.data = pstr->data, str.size = pstr->size;
+ RELOC_CONST_STRING_VAR(str);
+ pstr->data = str.data;
+}
+RELOC_PTRS_END
+gs_private_st_element(st_param_string_element, gs_param_string,
+ "gs_param_string[]", param_string_elt_enum_ptrs,
+ param_string_elt_reloc_ptrs, st_gs_param_string);
+
+/* ---------------- Get/put Distiller parameters ---------------- */
+
+/*
+ * ColorConversionStrategy is supposed to affect output color space
+ * according to the following table. ****** NOT IMPLEMENTED YET ******
+
+PS Input: LeaveCU UseDIC UseDICFI sRGB
+Gray art Gray CalGray/ICCBased Gray Gray
+Gray image Gray CalGray/ICCBased CalGray/ICCBased Gray
+RGB art RGB CalGray/ICCBased RGB CalRGB/sRGB
+RGB image RGB CalGray/ICCBased CalRGB/ICCBased CalRGB/sRGB
+CMYK art CMYK LAB/ICCBased CMYK CalRGB/sRGB
+CMYK image CMYK LAB/ICCBased LAB/ICCBased CalRGB/sRGB
+CIE art Cal/ICC Cal/ICC Cal/ICC CalRGB/sRGB
+CIE image Cal/ICC Cal/ICC Cal/ICC CalRGB/sRGB
+
+ */
+
+/*
+ * The Always/NeverEmbed parameters are defined as being incremental. Since
+ * this isn't compatible with the general property of page devices that if
+ * you do a currentpagedevice, doing a setpagedevice later will restore the
+ * same state, we actually define the parameters in sets of 3:
+ * - AlwaysEmbed is used for incremental additions.
+ * - ~AlwaysEmbed is used for incremental deletions.
+ * - .AlwaysEmbed is used for the complete list.
+ * and analogously for NeverEmbed.
+ */
+
+typedef struct psdf_image_filter_name_s {
+ const char *pname;
+ const stream_template *templat;
+ psdf_version min_version;
+} psdf_image_filter_name;
+
+static const psdf_image_filter_name Poly_filters[] = {
+ {"DCTEncode", &s_DCTE_template},
+ {"FlateEncode", &s_zlibE_template, psdf_version_ll3},
+ {"LZWEncode", &s_LZWE_template},
+#ifdef USE_LWF_JP2
+ {"JPXEncode", &s_jpxe_template},
+#endif
+ {0, 0}
+};
+
+static const psdf_image_filter_name Mono_filters[] = {
+ {"CCITTFaxEncode", &s_CFE_template},
+ {"FlateEncode", &s_zlibE_template, psdf_version_ll3},
+ {"LZWEncode", &s_LZWE_template},
+ {"RunLengthEncode", &s_RLE_template},
+#ifdef USE_LDF_JB2
+ {"JBIG2Encode", &s_jbig2encode_template},
+#endif
+ {0, 0}
+};
+
+typedef struct psdf_image_param_names_s {
+ const char *ACSDict; /* not used for mono */
+ const char *Dict;
+ const char *DownsampleType;
+ float DownsampleThreshold_default;
+ const psdf_image_filter_name *filter_names;
+ const char *Filter;
+ const char *AutoFilterStrategy;
+ gs_param_item_t items[9]; /* AutoFilter (not used for mono), */
+ /* AntiAlias, */
+ /* Depth, Downsample, DownsampleThreshold, */
+ /* Encode, Resolution, AutoFilterStrategy, end marker */
+} psdf_image_param_names_t;
+#define pi(key, type, memb) { key, type, offset_of(psdf_image_params, memb) }
+#define psdf_image_param_names(acs, aa, af, de, di, ds, dt, dst, dstd, e, f, fns, r, afs)\
+ acs, di, dt, dstd, fns, f, afs, {\
+ pi(af, gs_param_type_bool, AutoFilter),\
+ pi(aa, gs_param_type_bool, AntiAlias),\
+ pi(de, gs_param_type_int, Depth),\
+ pi(ds, gs_param_type_bool, Downsample),\
+ pi(dst, gs_param_type_float, DownsampleThreshold),\
+ pi(e, gs_param_type_bool, Encode),\
+ pi(r, gs_param_type_int, Resolution),\
+ gs_param_item_end\
+ }
+
+static const psdf_image_param_names_t Color_names = {
+ psdf_image_param_names(
+ "ColorACSImageDict", "AntiAliasColorImages", "AutoFilterColorImages",
+ "ColorImageDepth", "ColorImageDict",
+ "DownsampleColorImages", "ColorImageDownsampleType",
+ "ColorImageDownsampleThreshold", 1.5,
+ "EncodeColorImages", "ColorImageFilter", Poly_filters,
+ "ColorImageResolution", 0
+ )
+};
+static const psdf_image_param_names_t Gray_names = {
+ psdf_image_param_names(
+ "GrayACSImageDict", "AntiAliasGrayImages", "AutoFilterGrayImages",
+ "GrayImageDepth", "GrayImageDict",
+ "DownsampleGrayImages", "GrayImageDownsampleType",
+ "GrayImageDownsampleThreshold", 2.0,
+ "EncodeGrayImages", "GrayImageFilter", Poly_filters,
+ "GrayImageResolution", 0
+ )
+};
+static const psdf_image_param_names_t Mono_names = {
+ psdf_image_param_names(
+ 0, "AntiAliasMonoImages", 0,
+ "MonoImageDepth", "MonoImageDict",
+ "DownsampleMonoImages", "MonoImageDownsampleType",
+ "MonoImageDownsampleThreshold", 2.0,
+ "EncodeMonoImages", "MonoImageFilter", Mono_filters,
+ "MonoImageResolution", 0
+ )
+};
+static const psdf_image_param_names_t Color_names15 = {
+ psdf_image_param_names(
+ "ColorACSImageDict", "AntiAliasColorImages", "AutoFilterColorImages",
+ "ColorImageDepth", "ColorImageDict",
+ "DownsampleColorImages", "ColorImageDownsampleType",
+ "ColorImageDownsampleThreshold", 1.5,
+ "EncodeColorImages", "ColorImageFilter", Poly_filters,
+ "ColorImageResolution", "ColorImageAutoFilterStrategy"
+ )
+};
+static const psdf_image_param_names_t Gray_names15 = {
+ psdf_image_param_names(
+ "GrayACSImageDict", "AntiAliasGrayImages", "AutoFilterGrayImages",
+ "GrayImageDepth", "GrayImageDict",
+ "DownsampleGrayImages", "GrayImageDownsampleType",
+ "GrayImageDownsampleThreshold", 2.0,
+ "EncodeGrayImages", "GrayImageFilter", Poly_filters,
+ "GrayImageResolution", "GrayImageAutoFilterStrategy"
+ )
+};
+#undef pi
+static const char *const AutoRotatePages_names[] = {
+ psdf_arp_names, 0
+};
+static const char *const ColorConversionStrategy_names[] = {
+ psdf_ccs_names, 0
+};
+static const char *const DownsampleType_names[] = {
+ psdf_ds_names, 0
+};
+static const char *const AutoFilterStrategy_names[] = {
+ psdf_afs_names, 0
+};
+static const char *const Binding_names[] = {
+ psdf_binding_names, 0
+};
+static const char *const DefaultRenderingIntent_names[] = {
+ psdf_ri_names, 0
+};
+static const char *const TransferFunctionInfo_names[] = {
+ psdf_tfi_names, 0
+};
+static const char *const UCRandBGInfo_names[] = {
+ psdf_ucrbg_names, 0
+};
+static const char *const CannotEmbedFontPolicy_names[] = {
+ psdf_cefp_names, 0
+};
+
+static const gs_param_item_t psdf_param_items[] = {
+#define pi(key, type, memb) { key, type, offset_of(psdf_distiller_params, memb) }
+
+ /* General parameters */
+
+ pi("ASCII85EncodePages", gs_param_type_bool, ASCII85EncodePages),
+ /* (AutoRotatePages) */
+ /* (Binding) */
+ pi("CompressPages", gs_param_type_bool, CompressPages),
+ /* (DefaultRenderingIntent) */
+ pi("DetectBlends", gs_param_type_bool, DetectBlends),
+ pi("DoThumbnails", gs_param_type_bool, DoThumbnails),
+ pi("ImageMemory", gs_param_type_long, ImageMemory),
+ /* (LockDistillerParams) */
+ pi("LZWEncodePages", gs_param_type_bool, LZWEncodePages),
+ pi("OPM", gs_param_type_int, OPM),
+ pi("PreserveHalftoneInfo", gs_param_type_bool, PreserveHalftoneInfo),
+ pi("PreserveOPIComments", gs_param_type_bool, PreserveOPIComments),
+ pi("PreserveOverprintSettings", gs_param_type_bool, PreserveOverprintSettings),
+ /* (TransferFunctionInfo) */
+ /* (UCRandBGInfo) */
+ pi("UseFlateCompression", gs_param_type_bool, UseFlateCompression),
+
+ /* Color image processing parameters */
+
+ pi("ConvertCMYKImagesToRGB", gs_param_type_bool, ConvertCMYKImagesToRGB),
+ pi("ConvertImagesToIndexed", gs_param_type_bool, ConvertImagesToIndexed),
+
+ /* Font embedding parameters */
+
+ /* (CannotEmbedFontPolicy) */
+ pi("EmbedAllFonts", gs_param_type_bool, EmbedAllFonts),
+ pi("MaxSubsetPct", gs_param_type_int, MaxSubsetPct),
+ pi("SubsetFonts", gs_param_type_bool, SubsetFonts),
+
+#undef pi
+ gs_param_item_end
+};
+
+/* -------- Get parameters -------- */
+
+static int
+psdf_write_name(gs_param_list *plist, const char *key, const char *str)
+{
+ gs_param_string pstr;
+
+ param_string_from_string(pstr, str);
+ return param_write_name(plist, key, &pstr);
+}
+
+static int
+psdf_write_string_param(gs_param_list *plist, const char *key,
+ const gs_const_string *pstr)
+{
+ gs_param_string ps;
+
+ ps.data = pstr->data;
+ ps.size = pstr->size;
+ ps.persistent = false;
+ return param_write_string(plist, key, &ps);
+}
+
+/*
+ * Get an image Dict parameter. Note that we return a default (empty)
+ * dictionary if the parameter has never been set.
+ */
+static int
+psdf_get_image_dict_param(gs_param_list * plist, const gs_param_name pname,
+ gs_c_param_list *plvalue)
+{
+ gs_param_dict dict;
+ int code;
+
+ if (pname == 0)
+ return 0;
+ dict.size = 12; /* enough for all param dicts we know about */
+ if ((code = param_begin_write_dict(plist, pname, &dict, false)) < 0)
+ return code;
+ if (plvalue != 0) {
+ gs_c_param_list_read(plvalue);
+ code = param_list_copy(dict.list, (gs_param_list *)plvalue);
+ }
+ param_end_write_dict(plist, pname, &dict);
+ return code;
+}
+
+/* Get a set of image-related parameters. */
+static int
+psdf_get_image_params(gs_param_list * plist,
+ const psdf_image_param_names_t * pnames, psdf_image_params * params)
+{
+ /* Skip AutoFilter for mono images. */
+ const gs_param_item_t *items =
+ (pnames->items[0].key == 0 ? pnames->items + 1 : pnames->items);
+ int code;
+
+ /*
+ * We must actually return a value for every parameter, so that
+ * all parameter names will be recognized as settable by -d or -s
+ * from the command line.
+ */
+ code = gs_param_write_items(plist, params, NULL, items);
+ if (code < 0)
+ return code;
+
+ code = psdf_get_image_dict_param(plist, pnames->ACSDict, params->ACSDict);
+ if (code < 0)
+ return code;
+
+ /* (AntiAlias) */
+ /* (AutoFilter) */
+ /* (Depth) */
+ code = psdf_get_image_dict_param(plist, pnames->Dict, params->Dict);
+ if (code < 0)
+ return code;
+
+ /* (Downsample) */
+ code = psdf_write_name(plist, pnames->DownsampleType,
+ DownsampleType_names[params->DownsampleType]);
+ if (code < 0)
+ return code;
+
+ /* (DownsampleThreshold) */
+ /* (Encode) */
+ code = psdf_write_name(plist, pnames->Filter,
+ (params->Filter == 0 ?
+ pnames->filter_names[0].pname :
+ params->Filter));
+ if (code < 0)
+ return code;
+
+ /* (Resolution) */
+ if (pnames->AutoFilterStrategy != 0)
+ code = psdf_write_name(plist, pnames->AutoFilterStrategy,
+ AutoFilterStrategy_names[params->AutoFilterStrategy]);
+ if (code < 0)
+ return code;
+
+ return code;
+}
+
+/* Get a font embedding parameter. */
+static int
+psdf_get_embed_param(gs_param_list *plist, gs_param_name allpname,
+ const gs_param_string_array *psa)
+{
+ int code = param_write_name_array(plist, allpname, psa);
+
+ if (code >= 0)
+ code = param_write_name_array(plist, allpname + 1, psa);
+ return code;
+}
+
+/* Transfer a collection of parameters. */
+static const byte xfer_item_sizes[] = {
+ GS_PARAM_TYPE_SIZES(0)
+};
+/* Get parameters. */
+static
+int gdev_psdf_get_image_param(gx_device_psdf *pdev, const psdf_image_param_names_t *image_names,
+ psdf_image_params * params, char *Param, gs_param_list * plist)
+{
+ const gs_param_item_t *pi;
+ int code;
+
+ for (pi = image_names->items; pi->key != 0; ++pi) {
+ if (strcmp(pi->key, Param) == 0) {
+ const char *key = pi->key;
+ const void *pvalue = (const void *)((const char *)params + pi->offset);
+ int size = xfer_item_sizes[pi->type];
+ gs_param_typed_value typed;
+
+ memcpy(&typed.value, pvalue, size);
+ typed.type = pi->type;
+ code = (*plist->procs->xmit_typed) (plist, key, &typed);
+ return code;
+ }
+ }
+ /* We only have an ACSDict for color image parameters */
+ if (image_names->ACSDict) {
+ if (strcmp(Param, image_names->ACSDict) == 0)
+ return psdf_get_image_dict_param(plist, image_names->ACSDict, params->ACSDict);
+ }
+ if (strcmp(Param, image_names->Dict) == 0)
+ return psdf_get_image_dict_param(plist, image_names->Dict, params->Dict);
+
+ if (strcmp(Param, image_names->DownsampleType) == 0)
+ return psdf_write_name(plist, image_names->DownsampleType,
+ DownsampleType_names[params->DownsampleType]);
+ if (strcmp(Param, image_names->Filter) == 0)
+ return psdf_write_name(plist, image_names->Filter,
+ (params->Filter == 0 ?
+ image_names->filter_names[0].pname :
+ params->Filter));
+#ifdef USE_LWF_JP2
+ if (image_names->AutoFilterStrategy != 0)
+ if (strcmp(Param, image_names->AutoFilterStrategy) == 0)
+ return psdf_write_name(plist, image_names->AutoFilterStrategy,
+ (params->AutoFilterStrategy == 0 ?
+ "JPEG2000" : params->AutoFilterStrategy));
+#endif
+ return gs_error_undefined;
+}
+int
+gdev_psdf_get_param(gx_device *dev, char *Param, void *list)
+{
+ gx_device_psdf *pdev = (gx_device_psdf *) dev;
+ const psdf_image_param_names_t *image_names;
+ const gs_param_item_t *pi;
+ gs_param_list * plist = (gs_param_list *)list;
+ int code = 0;
+
+ code = gdev_vector_get_param(dev, Param, list);
+ if (code != gs_error_undefined)
+ return code;
+
+ /* General parameters first */
+ for (pi = psdf_param_items; pi->key != 0; ++pi) {
+ if (strcmp(pi->key, Param) == 0) {
+ const char *key = pi->key;
+ const void *pvalue = (const void *)((const char *)&pdev + pi->offset);
+ int size = xfer_item_sizes[pi->type];
+ gs_param_typed_value typed;
+
+ memcpy(&typed.value, pvalue, size);
+ typed.type = pi->type;
+ code = (*plist->procs->xmit_typed) (plist, key, &typed);
+ return code;
+ }
+ }
+
+ /* Color image parameters */
+ if (pdev->ParamCompatibilityLevel >= 1.5)
+ image_names = &Color_names15;
+ else
+ image_names = &Color_names;
+
+ code = gdev_psdf_get_image_param(pdev, image_names, &pdev->params.ColorImage, Param, plist);
+ if (code != gs_error_undefined)
+ return code;
+
+ /* Grey image parameters */
+ if (pdev->ParamCompatibilityLevel >= 1.5)
+ image_names = &Gray_names15;
+ else
+ image_names = &Gray_names;
+
+ code = gdev_psdf_get_image_param(pdev, image_names, &pdev->params.GrayImage, Param, plist);
+ if (code != gs_error_undefined)
+ return code;
+
+ /* Mono image parameters */
+ code = gdev_psdf_get_image_param(pdev, &Mono_names, &pdev->params.MonoImage, Param, plist);
+ if (code != gs_error_undefined)
+ return code;
+
+ if (strcmp(Param, "AutoRotatePages") == 0) {
+ return(psdf_write_name(plist, "AutoRotatePages",
+ AutoRotatePages_names[(int)pdev->params.AutoRotatePages]));
+ }
+ if (strcmp(Param, "Binding") == 0) {
+ return(psdf_write_name(plist, "Binding",
+ Binding_names[(int)pdev->params.Binding]));
+ }
+ if (strcmp(Param, "DefaultRenderingIntent") == 0) {
+ return(psdf_write_name(plist, "DefaultRenderingIntent",
+ DefaultRenderingIntent_names[(int)pdev->params.DefaultRenderingIntent]));
+ }
+ if (strcmp(Param, "TransferFunctionInfo") == 0) {
+ return(psdf_write_name(plist, "TransferFunctionInfo",
+ TransferFunctionInfo_names[(int)pdev->params.TransferFunctionInfo]));
+ }
+ if (strcmp(Param, "UCRandBGInfo") == 0) {
+ return(psdf_write_name(plist, "UCRandBGInfo",
+ UCRandBGInfo_names[(int)pdev->params.UCRandBGInfo]));
+ }
+ if (strcmp(Param, "ColorConversionStrategy") == 0) {
+ return(psdf_write_name(plist, "ColorConversionStrategy",
+ ColorConversionStrategy_names[(int)pdev->params.ColorConversionStrategy]));
+ }
+ if (strcmp(Param, "CalCMYKProfile") == 0) {
+ return(psdf_write_string_param(plist, "CalCMYKProfile",
+ &pdev->params.CalCMYKProfile));
+ }
+ if (strcmp(Param, "CalGrayProfile") == 0) {
+ return(psdf_write_string_param(plist, "CalGrayProfile",
+ &pdev->params.CalGrayProfile));
+ }
+ if (strcmp(Param, "CalRGBProfile") == 0) {
+ return(psdf_write_string_param(plist, "CalRGBProfile",
+ &pdev->params.CalRGBProfile));
+ }
+ if (strcmp(Param, "sRGBProfile") == 0) {
+ return(psdf_write_string_param(plist, "sRGBProfile",
+ &pdev->params.sRGBProfile));
+ }
+ if (strcmp(Param, ".AlwaysEmbed") == 0) {
+ return(psdf_get_embed_param(plist, ".AlwaysEmbed", &pdev->params.AlwaysEmbed));
+ }
+ if (strcmp(Param, ".NeverEmbed") == 0) {
+ return(psdf_get_embed_param(plist, ".NeverEmbed", &pdev->params.NeverEmbed));
+ }
+ if (strcmp(Param, "CannotEmbedFontPolicy") == 0) {
+ return(psdf_write_name(plist, "CannotEmbedFontPolicy",
+ CannotEmbedFontPolicy_names[(int)pdev->params.CannotEmbedFontPolicy]));
+ }
+ return gs_error_undefined;
+}
+
+int
+gdev_psdf_get_params(gx_device * dev, gs_param_list * plist)
+{
+ gx_device_psdf *pdev = (gx_device_psdf *) dev;
+ int code = gdev_vector_get_params(dev, plist);
+ if (code < 0)
+ return code;
+
+ code = gs_param_write_items(plist, &pdev->params, NULL, psdf_param_items);
+ if (code < 0)
+ return code;
+
+ /* General parameters */
+
+ code = psdf_write_name(plist, "AutoRotatePages",
+ AutoRotatePages_names[(int)pdev->params.AutoRotatePages]);
+ if (code < 0)
+ return code;
+
+ code = psdf_write_name(plist, "Binding",
+ Binding_names[(int)pdev->params.Binding]);
+ if (code < 0)
+ return code;
+
+ code = psdf_write_name(plist, "DefaultRenderingIntent",
+ DefaultRenderingIntent_names[(int)pdev->params.DefaultRenderingIntent]);
+ if (code < 0)
+ return code;
+
+ code = psdf_write_name(plist, "TransferFunctionInfo",
+ TransferFunctionInfo_names[(int)pdev->params.TransferFunctionInfo]);
+ if (code < 0)
+ return code;
+
+ code = psdf_write_name(plist, "UCRandBGInfo",
+ UCRandBGInfo_names[(int)pdev->params.UCRandBGInfo]);
+ if (code < 0)
+ return code;
+
+ /* Color sampled image parameters */
+
+ code = psdf_get_image_params(plist,
+ (pdev->ParamCompatibilityLevel >= 1.5 ? &Color_names15 : &Color_names),
+ &pdev->params.ColorImage);
+ if (code < 0)
+ return code;
+
+ code = psdf_write_name(plist, "ColorConversionStrategy",
+ ColorConversionStrategy_names[(int)pdev->params.ColorConversionStrategy]);
+ if (code < 0)
+ return code;
+
+ code = psdf_write_string_param(plist, "CalCMYKProfile",
+ &pdev->params.CalCMYKProfile);
+ if (code < 0)
+ return code;
+
+ code = psdf_write_string_param(plist, "CalGrayProfile",
+ &pdev->params.CalGrayProfile);
+ if (code < 0)
+ return code;
+
+ code = psdf_write_string_param(plist, "CalRGBProfile",
+ &pdev->params.CalRGBProfile);
+ if (code < 0)
+ return code;
+
+ code = psdf_write_string_param(plist, "sRGBProfile",
+ &pdev->params.sRGBProfile);
+ if (code < 0)
+ return code;
+
+ /* Gray sampled image parameters */
+
+ code = psdf_get_image_params(plist,
+ (pdev->ParamCompatibilityLevel >= 1.5 ? &Gray_names15 : &Gray_names),
+ &pdev->params.GrayImage);
+ if (code < 0)
+ return code;
+
+ /* Mono sampled image parameters */
+
+ code = psdf_get_image_params(plist, &Mono_names, &pdev->params.MonoImage);
+ if (code < 0)
+ return code;
+
+ /* Font embedding parameters */
+
+ code = psdf_get_embed_param(plist, ".AlwaysEmbed", &pdev->params.AlwaysEmbed);
+ if (code < 0)
+ return code;
+
+ code = psdf_get_embed_param(plist, ".NeverEmbed", &pdev->params.NeverEmbed);
+ if (code < 0)
+ return code;
+
+ code = psdf_write_name(plist, "CannotEmbedFontPolicy",
+ CannotEmbedFontPolicy_names[(int)pdev->params.CannotEmbedFontPolicy]);
+
+ return code;
+}
+
+/* -------- Put parameters -------- */
+
+extern stream_state_proc_put_params(s_CF_put_params, stream_CF_state);
+extern stream_state_proc_put_params(s_DCTE_put_params, stream_DCT_state);
+typedef stream_state_proc_put_params((*ss_put_params_t), stream_state);
+
+static int
+psdf_read_string_param(gs_param_list *plist, const char *key,
+ gs_const_string *pstr, gs_memory_t *mem, int ecode)
+{
+ gs_param_string ps;
+ int code;
+
+ switch (code = param_read_string(plist, key, &ps)) {
+ case 0: {
+ uint size = ps.size;
+ byte *data = gs_alloc_string(mem, size, "psdf_read_string_param");
+
+ if (data == 0)
+ return_error(gs_error_VMerror);
+ memcpy(data, ps.data, size);
+ pstr->data = data;
+ pstr->size = size;
+ break;
+ }
+ default:
+ ecode = code;
+ case 1:
+ break;
+ }
+ return ecode;
+}
+
+/*
+ * The arguments and return value for psdf_put_enum are different because
+ * we must cast the value both going in and coming out.
+ */
+static int
+psdf_put_enum(gs_param_list *plist, const char *key, int value,
+ const char *const pnames[], int *pecode)
+{
+ *pecode = param_put_enum(plist, key, &value, pnames, *pecode);
+ return value;
+}
+
+static int
+psdf_CF_put_params(gs_param_list * plist, stream_state * st)
+{
+ stream_CFE_state *const ss = (stream_CFE_state *) st;
+
+ (*s_CFE_template.set_defaults) (st);
+ ss->K = -1;
+ ss->BlackIs1 = true;
+ return s_CF_put_params(plist, (stream_CF_state *) ss);
+}
+
+static int
+psdf_DCT_put_params(gs_param_list * plist, stream_state * st)
+{
+ return psdf_DCT_filter(plist, st, 8 /*nominal*/, 8 /*ibid.*/, 3 /*ibid.*/,
+ NULL);
+}
+
+/* Put [~](Always|Never)Embed parameters. */
+/* Returns 0 = OK, 1 = no paramewter specified, <0 = error. */
+static int
+param_read_embed_array(gs_param_list * plist, gs_param_name pname,
+ gs_param_string_array * psa)
+{
+ int code;
+
+ psa->data = 0, psa->size = 0;
+ switch (code = param_read_name_array(plist, pname, psa)) {
+ default:
+ param_signal_error(plist, pname, code);
+ case 0:
+ case 1:
+ break;
+ }
+ return code;
+}
+static bool
+param_string_eq(const gs_param_string *ps1, const gs_param_string *ps2)
+{
+ return !bytes_compare(ps1->data, ps1->size, ps2->data, ps2->size);
+}
+static int
+add_embed(gs_param_string_array *prsa, const gs_param_string_array *psa,
+ gs_memory_t *mem)
+{
+ uint i;
+ gs_param_string *const rdata =
+ (gs_param_string *)prsa->data; /* break const */
+ uint count = prsa->size;
+
+ for (i = 0; i < psa->size; ++i) {
+ uint j;
+
+ for (j = 0; j < count; ++j)
+ if (param_string_eq(&psa->data[i], &rdata[j]))
+ break;
+ if (j == count) {
+ uint size = psa->data[i].size;
+ byte *data = gs_alloc_string(mem, size, "add_embed");
+
+ if (data == 0)
+ return_error(gs_error_VMerror);
+ memcpy(data, psa->data[i].data, size);
+ rdata[count].data = data;
+ rdata[count].size = size;
+ rdata[count].persistent = false;
+ count++;
+ }
+ }
+ prsa->size = count;
+ return 0;
+}
+static void
+delete_embed(gs_param_string_array *prsa, const gs_param_string_array *pnsa,
+ gs_memory_t *mem)
+{
+ uint i;
+ gs_param_string *const rdata =
+ (gs_param_string *)prsa->data; /* break const */
+ uint count = prsa->size;
+
+ for (i = pnsa->size; i-- > 0;) {
+ uint j;
+
+ for (j = count; j-- > 0;)
+ if (param_string_eq(&pnsa->data[i], &rdata[j]))
+ break;
+ if (j + 1 != 0) {
+ gs_free_const_string(mem, rdata[j].data, rdata[j].size,
+ "delete_embed");
+ rdata[j] = rdata[--count];
+ }
+ }
+ prsa->size = count;
+}
+static int merge_embed(gs_param_string_array * psa, gs_param_string_array * asa,
+ gs_memory_t *mem)
+{
+ gs_param_string_array rsa;
+ gs_param_string *rdata;
+ int code;
+
+ rdata = gs_alloc_struct_array(mem, psa->size + asa->size,
+ gs_param_string,
+ &st_param_string_element,
+ "psdf_put_embed_param(update)");
+ if (rdata == 0)
+ return_error(gs_error_VMerror);
+ memcpy(rdata, psa->data, psa->size * sizeof(*psa->data));
+ rsa.data = rdata;
+ rsa.size = psa->size;
+ rsa.persistent = false;
+ code = add_embed(&rsa, asa, mem);
+ if (code < 0) {
+ gs_free_object(mem, rdata, "psdf_put_embed_param(update)");
+ return code;
+ }
+ gs_free_const_object(mem, psa->data, "psdf_put_embed_param(free)");
+ *psa = rsa;
+ return 0;
+}
+
+static int
+psdf_put_embed_param(gs_param_list * plist, gs_param_name notpname,
+ gs_param_name pname, gs_param_string_array * psa,
+ gs_memory_t *mem, int ecode)
+{
+ gs_param_name allpname = pname + 1;
+ gs_param_string_array sa, nsa, asa;
+ int code;
+
+ mem = gs_memory_stable(mem);
+ code = param_read_embed_array(plist, pname, &sa);
+ if (code < 0)
+ return code;
+ if (code == 0) {
+ /* Optimize for sa == *psa. */
+ int i;
+
+ if (sa.size == psa->size) {
+ for (i = 0; i < sa.size; ++i) {
+ if (!param_string_eq(&sa.data[i], &psa->data[i]))
+ break;
+ }
+ } else
+ i = -1;
+ if (i == sa.size) {
+ /* equal, no-op. */
+ } else {
+ delete_embed(psa, psa, mem);
+ code = merge_embed(psa, &sa, mem);
+ if (code < 0)
+ return code;
+ }
+ }
+ code = param_read_embed_array(plist, notpname, &nsa);
+ if (code < 0)
+ return code;
+ if (nsa.data != 0)
+ delete_embed(psa, &nsa, mem);
+ code = param_read_embed_array(plist, allpname, &asa);
+ if (code < 0)
+ return code;
+ if (asa.data != 0) {
+ code = merge_embed(psa, &asa, mem);
+ if (code < 0)
+ return code;
+ }
+ if (psa->data)
+ psa->data = gs_resize_object(mem, (gs_param_string *)psa->data, psa->size,
+ "psdf_put_embed_param(resize)");
+ return 0;
+}
+
+/* Put an image Dict parameter. */
+static int
+psdf_put_image_dict_param(gs_param_list * plist, const gs_param_name pname,
+ gs_c_param_list **pplvalue,
+ const stream_template * templat,
+ ss_put_params_t put_params, gs_memory_t * mem)
+{
+ gs_param_dict dict;
+ gs_c_param_list *plvalue = *pplvalue;
+ int code;
+
+ mem = gs_memory_stable(mem);
+ switch (code = param_begin_read_dict(plist, pname, &dict, false)) {
+ default:
+ param_signal_error(plist, pname, code);
+ return code;
+ case 1:
+ return 0;
+ case 0: {
+ /* Check the parameter values now. */
+ stream_state *ss = s_alloc_state(mem, templat->stype, pname);
+
+ if (ss == 0)
+ return_error(gs_error_VMerror);
+ ss->templat = templat;
+ if (templat->set_defaults)
+ templat->set_defaults(ss);
+ code = put_params(dict.list, ss);
+ if (templat->release)
+ templat->release(ss);
+ gs_free_object(mem, ss, pname);
+ if (code < 0) {
+ param_signal_error(plist, pname, code);
+ } else {
+ plvalue = gs_c_param_list_alloc(mem, pname);
+ if (plvalue == 0)
+ return_error(gs_error_VMerror);
+ gs_c_param_list_write(plvalue, mem);
+ code = param_list_copy((gs_param_list *)plvalue,
+ dict.list);
+ if (code < 0) {
+ gs_c_param_list_release(plvalue);
+ gs_free_object(mem, plvalue, pname);
+ plvalue = *pplvalue;
+ }
+ }
+ }
+ param_end_read_dict(plist, pname, &dict);
+ break;
+ }
+ if (plvalue != *pplvalue) {
+ if (*pplvalue)
+ gs_c_param_list_release(*pplvalue);
+ *pplvalue = plvalue;
+ }
+ return code;
+}
+
+/* Put a set of image-related parameters. */
+static int
+psdf_put_image_params(const gx_device_psdf * pdev, gs_param_list * plist,
+ const psdf_image_param_names_t * pnames,
+ psdf_image_params * params, int ecode)
+{
+ gs_param_string fs;
+ /*
+ * Since this procedure can be called before the device is open,
+ * we must use pdev->memory rather than pdev->v_memory.
+ */
+ gs_memory_t *mem = pdev->memory;
+ gs_param_name pname;
+ /* Skip AutoFilter for mono images. */
+ const gs_param_item_t *items =
+ (pnames->items[0].key == 0 ? pnames->items + 1 : pnames->items);
+ int code = gs_param_read_items(plist, params, items);
+
+ if ((pname = pnames->ACSDict) != 0) {
+ code = psdf_put_image_dict_param(plist, pname, &params->ACSDict,
+ &s_DCTE_template,
+ psdf_DCT_put_params, mem);
+ if (code < 0)
+ ecode = code;
+ }
+ /* (AntiAlias) */
+ /* (AutoFilter) */
+ /* (Depth) */
+ if ((pname = pnames->Dict) != 0) {
+ const stream_template *templat;
+ ss_put_params_t put_params;
+
+ /* Hack to determine what kind of a Dict we want: */
+ if (pnames->Dict[0] == 'M')
+ templat = &s_CFE_template,
+ put_params = psdf_CF_put_params;
+ else
+ templat = &s_DCTE_template,
+ put_params = psdf_DCT_put_params;
+ code = psdf_put_image_dict_param(plist, pname, &params->Dict,
+ templat, put_params, mem);
+ if (code < 0)
+ ecode = code;
+ }
+ /* (Downsample) */
+ params->DownsampleType = (enum psdf_downsample_type)
+ psdf_put_enum(plist, pnames->DownsampleType,
+ (int)params->DownsampleType, DownsampleType_names,
+ &ecode);
+ /* (DownsampleThreshold) */
+ /* (Encode) */
+ /* Process AutoFilterStrategy before Filter, because it sets defaults
+ for the latter. */
+ if (pnames->AutoFilterStrategy != NULL) {
+ switch (code = param_read_string(plist, pnames->AutoFilterStrategy, &fs)) {
+ case 0:
+ {
+ const psdf_image_filter_name *pn = pnames->filter_names;
+ const char *param_name = 0;
+
+ if (gs_param_string_eq(&fs, "JPEG")) {
+ params->AutoFilterStrategy = af_Jpeg;
+ param_name = "DCTEncode";
+ } else {
+ if (gs_param_string_eq(&fs, "JPEG2000")) {
+ params->AutoFilterStrategy = af_Jpeg2000;
+ param_name = "JPXEncode";
+ } else {
+ ecode = gs_error_rangecheck;
+ goto ipe1;
+ }
+ }
+ while (pn->pname != 0 && !gs_param_string_eq(&fs, param_name))
+ pn++;
+ if (pn->pname != 0 && pn->min_version <= pdev->version) {
+ params->Filter = pn->pname;
+ params->filter_template = pn->templat;
+ }
+ break;
+ }
+ default:
+ ecode = code;
+ ipe1:param_signal_error(plist, pnames->AutoFilterStrategy, ecode);
+ case 1:
+ break;
+ }
+ }
+
+ switch (code = param_read_string(plist, pnames->Filter, &fs)) {
+ case 0:
+ {
+ const psdf_image_filter_name *pn = pnames->filter_names;
+
+ while (pn->pname != 0 && !gs_param_string_eq(&fs, pn->pname))
+ pn++;
+ if (pn->pname == 0 || pn->min_version > pdev->version) {
+ ecode = gs_error_rangecheck;
+ goto ipe;
+ }
+ params->Filter = pn->pname;
+ params->filter_template = pn->templat;
+ break;
+ }
+ default:
+ ecode = code;
+ ipe:param_signal_error(plist, pnames->Filter, ecode);
+ case 1:
+ break;
+ }
+ /* (Resolution) */
+ if (ecode >= 0) { /* Force parameters to acceptable values. */
+ if (params->Resolution < 1)
+ params->Resolution = 1;
+ if (params->DownsampleThreshold < 1 ||
+ params->DownsampleThreshold > 10)
+ params->DownsampleThreshold = pnames->DownsampleThreshold_default;
+ switch (params->Depth) {
+ default:
+ params->Depth = -1;
+ case 1:
+ case 2:
+ case 4:
+ case 8:
+ case -1:
+ break;
+ }
+ }
+ return ecode;
+}
+
+/* Put parameters. */
+int
+gdev_psdf_put_params(gx_device * dev, gs_param_list * plist)
+{
+ gx_device_psdf *pdev = (gx_device_psdf *) dev;
+ gs_memory_t *mem =
+ (pdev->v_memory ? pdev->v_memory : dev->memory);
+ int ecode, code = 0;
+ psdf_distiller_params params;
+
+ params = pdev->params;
+
+ /*
+ * If LockDistillerParams was true and isn't being set to false,
+ * ignore all other psdf parameters. However, do not ignore the
+ * standard device parameters.
+ */
+ ecode = code = param_read_bool(plist, "LockDistillerParams",
+ &params.LockDistillerParams);
+
+ if ((pdev->params.LockDistillerParams && params.LockDistillerParams)) {
+ /* If we are not going to use the parameters we must still read them
+ * in order to use up all the keys, otherwise, if we are being called
+ * from .installpagedevice, it will error out on unconsumed keys. We
+ * use a dummy params structure to read into, but we must make sure any
+ * pointers are not copied from the real params structure, or they get
+ * overwritten.
+ */
+ params.CalCMYKProfile.size = params.CalGrayProfile.size = params.CalRGBProfile.size = params.sRGBProfile.size = 0;
+ params.CalCMYKProfile.data = 0;params.CalGrayProfile.data = params.CalRGBProfile.data = params.sRGBProfile.data = (byte *)0;
+
+ params.ColorImage.ACSDict = params.ColorImage.Dict = 0;
+ params.GrayImage.ACSDict = params.GrayImage.Dict = 0;
+ params.MonoImage.ACSDict = params.MonoImage.Dict = 0;
+ params.AlwaysEmbed.data = params.NeverEmbed.data = 0;
+ params.AlwaysEmbed.size = params.AlwaysEmbed.persistent = params.NeverEmbed.size = params.NeverEmbed.persistent = 0;
+ }
+
+ /* General parameters. */
+
+ code = gs_param_read_items(plist, &params, psdf_param_items);
+ if (code < 0)
+ return code;
+
+ params.AutoRotatePages = (enum psdf_auto_rotate_pages)
+ psdf_put_enum(plist, "AutoRotatePages", (int)params.AutoRotatePages,
+ AutoRotatePages_names, &ecode);
+ params.Binding = (enum psdf_binding)
+ psdf_put_enum(plist, "Binding", (int)params.Binding,
+ Binding_names, &ecode);
+ params.DefaultRenderingIntent = (enum psdf_default_rendering_intent)
+ psdf_put_enum(plist, "DefaultRenderingIntent",
+ (int)params.DefaultRenderingIntent,
+ DefaultRenderingIntent_names, &ecode);
+ params.TransferFunctionInfo = (enum psdf_transfer_function_info)
+ psdf_put_enum(plist, "TransferFunctionInfo",
+ (int)params.TransferFunctionInfo,
+ TransferFunctionInfo_names, &ecode);
+ params.UCRandBGInfo = (enum psdf_ucr_and_bg_info)
+ psdf_put_enum(plist, "UCRandBGInfo", (int)params.UCRandBGInfo,
+ UCRandBGInfo_names, &ecode);
+ ecode = param_put_bool(plist, "UseFlateCompression",
+ &params.UseFlateCompression, ecode);
+
+ /* Color sampled image parameters */
+
+ ecode = psdf_put_image_params(pdev, plist,
+ (pdev->ParamCompatibilityLevel >= 1.5 ? &Color_names15 : &Color_names),
+ &params.ColorImage, ecode);
+ params.ColorConversionStrategy = (enum psdf_color_conversion_strategy)
+ psdf_put_enum(plist, "ColorConversionStrategy",
+ (int)params.ColorConversionStrategy,
+ ColorConversionStrategy_names, &ecode);
+ ecode = psdf_read_string_param(plist, "CalCMYKProfile",
+ &params.CalCMYKProfile, mem, ecode);
+ ecode = psdf_read_string_param(plist, "CalGrayProfile",
+ &params.CalGrayProfile, mem, ecode);
+ ecode = psdf_read_string_param(plist, "CalRGBProfile",
+ &params.CalRGBProfile, mem, ecode);
+ ecode = psdf_read_string_param(plist, "sRGBProfile",
+ &params.sRGBProfile, mem, ecode);
+
+ /* Gray sampled image parameters */
+
+ ecode = psdf_put_image_params(pdev, plist,
+ (pdev->ParamCompatibilityLevel >= 1.5 ? &Gray_names15 : &Gray_names),
+ &params.GrayImage, ecode);
+
+ /* Mono sampled image parameters */
+
+ ecode = psdf_put_image_params(pdev, plist, &Mono_names,
+ &params.MonoImage, ecode);
+
+ /* Font embedding parameters */
+
+ ecode = psdf_put_embed_param(plist, "~AlwaysEmbed", ".AlwaysEmbed",
+ &params.AlwaysEmbed, mem, ecode);
+ ecode = psdf_put_embed_param(plist, "~NeverEmbed", ".NeverEmbed",
+ &params.NeverEmbed, mem, ecode);
+ params.CannotEmbedFontPolicy = (enum psdf_cannot_embed_font_policy)
+ psdf_put_enum(plist, "CannotEmbedFontPolicy",
+ (int)params.CannotEmbedFontPolicy,
+ CannotEmbedFontPolicy_names, &ecode);
+ if (ecode < 0) {
+ code = ecode;
+ goto exit;
+ }
+
+ /* ps2write-specific output configuration options */
+ code = psdf_read_string_param(plist, "PSDocOptions",
+ (gs_const_string *)&params.PSDocOptions, mem, ecode);
+ if (code < 0)
+ goto exit;
+
+ code = param_read_embed_array(plist, "PSPageOptions", &params.PSPageOptions);
+ if (code < 0)
+ goto exit;
+
+ code = gdev_vector_put_params(dev, plist);
+
+exit:
+ if (!(pdev->params.LockDistillerParams && params.LockDistillerParams)) {
+ /* Only update the device paramters if there was no error */
+ pdev->params = params;
+ } else {
+ /* We read a bunch of parameters and are now throwing them away. Either because there
+ * was an error, or because the parameters were locked. We need to tidy up any memory
+ * we allocated to hold these parameters.
+ */
+ gs_memory_t *stable_mem = gs_memory_stable(mem);
+
+ if (params.NeverEmbed.data != 0)
+ gs_free_object(stable_mem, (void *)params.NeverEmbed.data, "free dummy param NeverEmbed");
+ if (params.AlwaysEmbed.data != 0)
+ gs_free_object(stable_mem, (void *)params.AlwaysEmbed.data, "free dummy param AlwaysEmbed");
+ if (params.CalCMYKProfile.data != 0)
+ gs_free_string(stable_mem, (void *)params.CalCMYKProfile.data, params.CalCMYKProfile.size, "free dummy param CalCMYKProfile");
+ if (params.CalGrayProfile.data != 0)
+ gs_free_string(stable_mem, (void *)params.CalGrayProfile.data, params.CalGrayProfile.size, "free dummy param CalGrayProfile");
+ if (params.CalRGBProfile.data != 0)
+ gs_free_string(stable_mem, (void *)params.CalRGBProfile.data, params.CalRGBProfile.size, "free dummy param CalRGBProfile");
+ if (params.sRGBProfile.data != 0)
+ gs_free_string(stable_mem, (void *)params.sRGBProfile.data, params.sRGBProfile.size, "free dummy param sRGBProfile");
+ if (params.ColorImage.ACSDict)
+ gs_c_param_list_release(params.ColorImage.ACSDict);
+ if (params.ColorImage.Dict)
+ gs_c_param_list_release(params.ColorImage.Dict);
+ if (params.GrayImage.ACSDict)
+ gs_c_param_list_release(params.GrayImage.ACSDict);
+ if (params.GrayImage.Dict)
+ gs_c_param_list_release(params.GrayImage.Dict);
+ if (params.MonoImage.ACSDict)
+ gs_c_param_list_release(params.MonoImage.ACSDict);
+ if (params.MonoImage.Dict)
+ gs_c_param_list_release(params.MonoImage.Dict);
+ }
+ return code;
+}
diff --git a/devices/vector/gdevpsds.c b/devices/vector/gdevpsds.c
new file mode 100644
index 000000000..5292cdf81
--- /dev/null
+++ b/devices/vector/gdevpsds.c
@@ -0,0 +1,1376 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Image processing streams for PostScript and PDF writers */
+#include "gx.h"
+#include "memory_.h"
+#include "gserrors.h"
+#include "gxdcconv.h"
+#include "gdevpsds.h"
+#include "gxbitmap.h"
+#include "gxcspace.h"
+#include "gsdcolor.h"
+#include "gscspace.h"
+#include "gxdevcli.h"
+#include "gxistate.h"
+
+/* ---------------- Convert between 1/2/4/12 and 8 bits ---------------- */
+
+gs_private_st_simple(st_1248_state, stream_1248_state, "stream_1248_state");
+
+/* Initialize an expansion or reduction stream. */
+int
+s_1248_init(stream_1248_state *ss, int Columns, int samples_per_pixel)
+{
+ ss->samples_per_row = Columns * samples_per_pixel;
+ return ss->templat->init((stream_state *)ss);
+}
+
+/* Initialize the state. */
+static int
+s_1_init(stream_state * st)
+{
+ stream_1248_state *const ss = (stream_1248_state *) st;
+
+ ss->left = ss->samples_per_row;
+ ss->bits_per_sample = 1;
+ return 0;
+}
+static int
+s_2_init(stream_state * st)
+{
+ stream_1248_state *const ss = (stream_1248_state *) st;
+
+ ss->left = ss->samples_per_row;
+ ss->bits_per_sample = 2;
+ return 0;
+}
+static int
+s_4_init(stream_state * st)
+{
+ stream_1248_state *const ss = (stream_1248_state *) st;
+
+ ss->left = ss->samples_per_row;
+ ss->bits_per_sample = 4;
+ return 0;
+}
+static int
+s_12_init(stream_state * st)
+{
+ stream_1248_state *const ss = (stream_1248_state *) st;
+
+ ss->left = ss->samples_per_row;
+ ss->bits_per_sample = 12; /* not needed */
+ return 0;
+}
+static int
+s_16_init(stream_state * st)
+{
+ stream_1248_state *const ss = (stream_1248_state *) st;
+
+ ss->left = ss->samples_per_row;
+ ss->bits_per_sample = 16; /* not needed */
+ return 0;
+}
+
+/* Process one buffer. */
+#define BEGIN_1248\
+ stream_1248_state * const ss = (stream_1248_state *)st;\
+ const byte *p = pr->ptr;\
+ const byte *rlimit = pr->limit;\
+ byte *q = pw->ptr;\
+ byte *wlimit = pw->limit;\
+ uint left = ss->left;\
+ int status;\
+ int n
+#define END_1248\
+ pr->ptr = p;\
+ pw->ptr = q;\
+ ss->left = left;\
+ return status
+
+/* N-to-8 expansion */
+#define FOREACH_N_8(in, nout)\
+ status = 0;\
+ for ( ; p < rlimit; left -= n, q += n, ++p ) {\
+ byte in = p[1];\
+ n = min(left, nout);\
+ if ( wlimit - q < n ) {\
+ status = 1;\
+ break;\
+ }\
+ switch ( n ) {\
+ case 0: left = ss->samples_per_row; --p; continue;
+#define END_FOREACH_N_8\
+ }\
+ }
+static int
+s_N_8_process(stream_state * st, stream_cursor_read * pr,
+ stream_cursor_write * pw, bool last)
+{
+ BEGIN_1248;
+
+ switch (ss->bits_per_sample) {
+
+ case 1:{
+ FOREACH_N_8(in, 8)
+ case 8:
+ q[8] = (byte) - (in & 1);
+ case 7:
+ q[7] = (byte) - ((in >> 1) & 1);
+ case 6:
+ q[6] = (byte) - ((in >> 2) & 1);
+ case 5:
+ q[5] = (byte) - ((in >> 3) & 1);
+ case 4:
+ q[4] = (byte) - ((in >> 4) & 1);
+ case 3:
+ q[3] = (byte) - ((in >> 5) & 1);
+ case 2:
+ q[2] = (byte) - ((in >> 6) & 1);
+ case 1:
+ q[1] = (byte) - (in >> 7);
+ END_FOREACH_N_8;
+ }
+ break;
+
+ case 2:{
+ static const byte b2[4] =
+ {0x00, 0x55, 0xaa, 0xff};
+
+ FOREACH_N_8(in, 4)
+ case 4:
+ q[4] = b2[in & 3];
+ case 3:
+ q[3] = b2[(in >> 2) & 3];
+ case 2:
+ q[2] = b2[(in >> 4) & 3];
+ case 1:
+ q[1] = b2[in >> 6];
+ END_FOREACH_N_8;
+ }
+ break;
+
+ case 4:{
+ static const byte b4[16] =
+ {
+ 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
+ 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff
+ };
+
+ FOREACH_N_8(in, 2)
+ case 2:
+ q[2] = b4[in & 0xf];
+ case 1:
+ q[1] = b4[in >> 4];
+ END_FOREACH_N_8;
+ }
+ break;
+
+ default:
+ return ERRC;
+ }
+
+ END_1248;
+}
+
+/* 12-to-8 "expansion" */
+static int
+s_12_8_process(stream_state * st, stream_cursor_read * pr,
+ stream_cursor_write * pw, bool last)
+{
+ BEGIN_1248;
+
+ n = ss->samples_per_row; /* misuse n to avoid a compiler warning */
+ status = 0;
+ for (; rlimit - p >= 2; ++q) {
+ if (q >= wlimit) {
+ status = 1;
+ break;
+ }
+ if (left == 0)
+ left = n;
+ if ((n - left) & 1) {
+ q[1] = (byte)((p[1] << 4) | (p[2] >> 4));
+ p += 2, --left;
+ } else {
+ q[1] = *++p;
+ if (!--left)
+ ++p;
+ }
+ }
+
+ END_1248;
+}
+
+/* 16-to-8 "expansion" */
+static int
+s_16_8_process(stream_state * st, stream_cursor_read * pr,
+ stream_cursor_write * pw, bool last)
+{
+ BEGIN_1248;
+
+ n = ss->samples_per_row; /* misuse n to avoid a compiler warning */
+ status = 0;
+ for (; rlimit - p >= 2; ++q) {
+ if (q >= wlimit) {
+ status = 1;
+ break;
+ }
+ q[1] = (byte)p[1]; /* Set output to the high byte of the input */
+ p+=2; /* Discard the low byte */
+ }
+ END_1248;
+}
+
+/* 8-to-N reduction */
+#define FOREACH_8_N(out, nin)\
+ byte out;\
+ status = 1;\
+ for ( ; q < wlimit; left -= n, p += n, ++q ) {\
+ n = min(left, nin);\
+ if ( rlimit - p < n ) {\
+ status = 0;\
+ break;\
+ }\
+ out = 0;\
+ switch ( n ) {\
+ case 0: left = ss->samples_per_row; --q; continue;
+#define END_FOREACH_8_N\
+ q[1] = out;\
+ }\
+ }
+static int
+s_8_N_process(stream_state * st, stream_cursor_read * pr,
+ stream_cursor_write * pw, bool last)
+{
+ BEGIN_1248;
+
+ switch (ss->bits_per_sample) {
+
+ case 1:{
+ FOREACH_8_N(out, 8)
+ case 8:
+ out = p[8] >> 7;
+ case 7:
+ out |= (p[7] >> 7) << 1;
+ case 6:
+ out |= (p[6] >> 7) << 2;
+ case 5:
+ out |= (p[5] >> 7) << 3;
+ case 4:
+ out |= (p[4] >> 7) << 4;
+ case 3:
+ out |= (p[3] >> 7) << 5;
+ case 2:
+ out |= (p[2] >> 7) << 6;
+ case 1:
+ out |= p[1] & 0x80;
+ END_FOREACH_8_N;
+ }
+ break;
+
+ case 2:{
+ FOREACH_8_N(out, 4)
+ case 4:
+ out |= p[4] >> 6;
+ case 3:
+ out |= (p[3] >> 6) << 2;
+ case 2:
+ out |= (p[2] >> 6) << 4;
+ case 1:
+ out |= p[1] & 0xc0;
+ END_FOREACH_8_N;
+ }
+ break;
+
+ case 4:{
+ FOREACH_8_N(out, 2)
+ case 2:
+ out |= p[2] >> 4;
+ case 1:
+ out |= p[1] & 0xf0;
+ END_FOREACH_8_N;
+ }
+ break;
+
+ default:
+ return ERRC;
+ }
+
+ END_1248;
+}
+
+const stream_template s_1_8_template = {
+ &st_1248_state, s_1_init, s_N_8_process, 1, 8
+};
+const stream_template s_2_8_template = {
+ &st_1248_state, s_2_init, s_N_8_process, 1, 4
+};
+const stream_template s_4_8_template = {
+ &st_1248_state, s_4_init, s_N_8_process, 1, 2
+};
+const stream_template s_12_8_template = {
+ &st_1248_state, s_12_init, s_12_8_process, 1, 2
+};
+const stream_template s_16_8_template = {
+ &st_1248_state, s_16_init, s_16_8_process, 1, 2
+};
+
+const stream_template s_8_1_template = {
+ &st_1248_state, s_1_init, s_8_N_process, 8, 1
+};
+const stream_template s_8_2_template = {
+ &st_1248_state, s_2_init, s_8_N_process, 4, 1
+};
+const stream_template s_8_4_template = {
+ &st_1248_state, s_4_init, s_8_N_process, 2, 1
+};
+
+/* ---------------- Color space conversion ---------------- */
+
+/* ------ Convert CMYK to RGB ------ */
+
+private_st_C2R_state();
+
+/* Initialize a CMYK => RGB conversion stream. */
+int
+s_C2R_init(stream_C2R_state *ss, const gs_imager_state *pis)
+{
+ ss->pis = pis;
+ return 0;
+}
+
+/* Set default parameter values (actually, just clear pointers). */
+static void
+s_C2R_set_defaults(stream_state * st)
+{
+ stream_C2R_state *const ss = (stream_C2R_state *) st;
+
+ ss->pis = 0;
+}
+
+/* Process one buffer. */
+static int
+s_C2R_process(stream_state * st, stream_cursor_read * pr,
+ stream_cursor_write * pw, bool last)
+{
+ stream_C2R_state *const ss = (stream_C2R_state *) st;
+ const byte *p = pr->ptr;
+ const byte *rlimit = pr->limit;
+ byte *q = pw->ptr;
+ byte *wlimit = pw->limit;
+
+ for (; rlimit - p >= 4 && wlimit - q >= 3; p += 4, q += 3) {
+ byte bc = p[1], bm = p[2], by = p[3], bk = p[4];
+ frac rgb[3];
+
+ color_cmyk_to_rgb(byte2frac(bc), byte2frac(bm), byte2frac(by),
+ byte2frac(bk), ss->pis, rgb, ss->pis->memory);
+ q[1] = frac2byte(rgb[0]);
+ q[2] = frac2byte(rgb[1]);
+ q[3] = frac2byte(rgb[2]);
+ }
+ pr->ptr = p;
+ pw->ptr = q;
+ return (rlimit - p < 4 ? 0 : 1);
+}
+
+const stream_template s_C2R_template = {
+ &st_C2R_state, 0 /*NULL */ , s_C2R_process, 4, 3, 0, s_C2R_set_defaults
+};
+
+/* ------ Convert any color space to Indexed ------ */
+
+private_st_IE_state();
+static
+ENUM_PTRS_WITH(ie_state_enum_ptrs, stream_IE_state *st) return 0;
+case 0: return ENUM_OBJ(st->Decode);
+case 1: return ENUM_BYTESTRING(&st->Table);
+ENUM_PTRS_END
+static
+RELOC_PTRS_WITH(ie_state_reloc_ptrs, stream_IE_state *st)
+{
+ RELOC_VAR(st->Decode);
+ RELOC_BYTESTRING_VAR(st->Table);
+}
+RELOC_PTRS_END
+
+/* Set defaults. */
+static void
+s_IE_set_defaults(stream_state * st)
+{
+ stream_IE_state *const ss = (stream_IE_state *) st;
+
+ ss->Decode = 0; /* clear pointers */
+ gs_bytestring_from_string(&ss->Table, 0, 0);
+}
+
+/* Initialize the state. */
+static int
+s_IE_init(stream_state * st)
+{
+ stream_IE_state *const ss = (stream_IE_state *) st;
+ int key_index = (1 << ss->BitsPerIndex) * ss->NumComponents;
+ int i;
+
+ if (ss->Table.data == 0 || ss->Table.size < key_index)
+ return ERRC; /****** WRONG ******/
+ /* Initialize Table with default values. */
+ memset(ss->Table.data, 0, ss->NumComponents);
+ ss->Table.data[ss->Table.size - 1] = 0;
+ for (i = 0; i < countof(ss->hash_table); ++i)
+ ss->hash_table[i] = key_index;
+ ss->next_index = 0;
+ ss->in_bits_left = 0;
+ ss->next_component = 0;
+ ss->byte_out = 1;
+ ss->x = 0;
+ return 0;
+}
+
+/* Process a buffer. */
+static int
+s_IE_process(stream_state * st, stream_cursor_read * pr,
+ stream_cursor_write * pw, bool last)
+{
+ stream_IE_state *const ss = (stream_IE_state *) st;
+ /* Constant values from the state */
+ const int bpc = ss->BitsPerComponent;
+ const int num_components = ss->NumComponents;
+ const int end_index = (1 << ss->BitsPerIndex) * num_components;
+ byte *const table = ss->Table.data;
+ byte *const key = table + end_index;
+ /* Dynamic values from the state */
+ uint byte_in = ss->byte_in;
+ int in_bits_left = ss->in_bits_left;
+ int next_component = ss->next_component;
+ uint byte_out = ss->byte_out;
+ /* Other dynamic values */
+ const byte *p = pr->ptr;
+ const byte *rlimit = pr->limit;
+ byte *q = pw->ptr;
+ byte *wlimit = pw->limit;
+ int status = 0;
+
+ for (;;) {
+ uint hash, reprobe;
+ int i, index;
+
+ /* Check for a filled output byte. */
+ if (byte_out >= 0x100) {
+ if (q >= wlimit) {
+ status = 1;
+ break;
+ }
+ *++q = (byte)byte_out;
+ byte_out = 1;
+ }
+ /* Acquire a complete input value. */
+ while (next_component < num_components) {
+ const float *decode = &ss->Decode[next_component * 2];
+ int sample;
+
+ if (in_bits_left == 0) {
+ if (p >= rlimit)
+ goto out;
+ byte_in = *++p;
+ in_bits_left = 8;
+ }
+ /* An input sample can never span a byte boundary. */
+ in_bits_left -= bpc;
+ sample = (byte_in >> in_bits_left) & ((1 << bpc) - 1);
+ /* Scale the sample according to Decode. */
+ sample = (int)((decode[0] +
+ (sample / (float)((1 << bpc) - 1) *
+ (decode[1] - decode[0]))) * 255 + 0.5);
+ key[next_component++] =
+ (sample < 0 ? 0 : sample > 255 ? 255 : (byte)sample);
+ }
+ /* Look up the input value. */
+ for (hash = 0, i = 0; i < num_components; ++i)
+ hash = hash + 23 * key[i]; /* adhoc */
+ reprobe = (hash / countof(ss->hash_table)) | 137; /* adhoc */
+ for (hash %= countof(ss->hash_table);
+ memcmp(table + ss->hash_table[hash], key, num_components);
+ hash = (hash + reprobe) % countof(ss->hash_table)
+ )
+ DO_NOTHING;
+ index = ss->hash_table[hash];
+ if (index == end_index) {
+ /* The match was on an empty entry. */
+ if (ss->next_index == end_index) {
+ /* Too many different values. */
+ status = ERRC;
+ break;
+ }
+ ss->hash_table[hash] = index = ss->next_index;
+ ss->next_index += num_components;
+ memcpy(table + index, key, num_components);
+ }
+ byte_out = (byte_out << ss->BitsPerIndex) + index / num_components;
+ next_component = 0;
+ if (++(ss->x) == ss->Width) {
+ /* Handle input and output padding. */
+ in_bits_left = 0;
+ if (byte_out != 1)
+ while (byte_out < 0x100)
+ byte_out <<= 1;
+ ss->x = 0;
+ }
+ }
+out:
+ pr->ptr = p;
+ pw->ptr = q;
+ ss->byte_in = byte_in;
+ ss->in_bits_left = in_bits_left;
+ ss->next_component = next_component;
+ ss->byte_out = byte_out;
+ /* For simplicity, always update the record of the table size. */
+ ss->Table.data[ss->Table.size - 1] =
+ (ss->next_index == 0 ? 0 :
+ ss->next_index / ss->NumComponents - 1);
+ return status;
+}
+
+const stream_template s_IE_template = {
+ &st_IE_state, s_IE_init, s_IE_process, 1, 1,
+ 0 /* NULL */, s_IE_set_defaults
+};
+
+/* ---------------- Downsampling ---------------- */
+
+/* Return the number of samples after downsampling. */
+int
+s_Downsample_size_out(int size_in, float factor, bool pad)
+{
+ return ((pad ? size_in + factor - 1 : size_in) / factor);
+}
+
+static void
+s_Downsample_set_defaults(register stream_state * st)
+{
+ stream_Downsample_state *const ss = (stream_Downsample_state *)st;
+
+ s_Downsample_set_defaults_inline(ss);
+}
+
+static int
+s_Downsample_init_common(stream_state * st)
+{
+ stream_Downsample_state *const ss = (stream_Downsample_state *) st;
+ ss->x = ss->y = 0;
+ return 0;
+}
+
+/* ------ Subsample ------ */
+
+gs_private_st_simple(st_Subsample_state, stream_Subsample_state,
+ "stream_Subsample_state");
+
+/* Initialize the state. */
+static int
+s_Subsample_init(stream_state * st)
+{
+ stream_Subsample_state *const ss = (stream_Subsample_state *) st;
+ int xf = ss->XFactor;
+
+ if ((float)xf != ss->XFactor) {
+ dmprintf1(st->memory,
+ "Subsample filter does not support non-integer downsample factor (%f)\n",
+ ss->XFactor);
+ return ERRC;
+ }
+ return s_Downsample_init_common(st);
+}
+
+/* Process one buffer. */
+static int
+s_Subsample_process(stream_state * st, stream_cursor_read * pr,
+ stream_cursor_write * pw, bool last)
+{
+ stream_Subsample_state *const ss = (stream_Subsample_state *) st;
+ const byte *p = pr->ptr;
+ const byte *rlimit = pr->limit;
+ byte *q = pw->ptr;
+ byte *wlimit = pw->limit;
+ int spp = ss->Colors;
+ int width = ss->WidthIn, height = ss->HeightIn;
+ int xf = ss->XFactor, yf = ss->YFactor;
+ int xf2 = xf / 2, yf2 = yf / 2;
+ int xlimit = (width / xf) * xf, ylimit = (height / yf) * yf;
+ int xlast =
+ (ss->padX && xlimit < width ? xlimit + (width % xf) / 2 : -1);
+ int ylast =
+ (ss->padY && ylimit < height ? ylimit + (height % yf) / 2 : -1);
+ int x = ss->x, y = ss->y;
+ int status = 0;
+
+ if_debug4m('w', st->memory,
+ "[w]subsample: x=%d, y=%d, rcount=%ld, wcount=%ld\n",
+ x, y, (long)(rlimit - p), (long)(wlimit - q));
+ for (; rlimit - p >= spp; p += spp) {
+ if (((y % yf == yf2 && y < ylimit) || y == ylast) &&
+ ((x % xf == xf2 && x < xlimit) || x == xlast)
+ ) {
+ if (wlimit - q < spp) {
+ status = 1;
+ break;
+ }
+ memcpy(q + 1, p + 1, spp);
+ q += spp;
+ }
+ if (++x == width)
+ x = 0, ++y;
+ }
+ if_debug5m('w', st->memory,
+ "[w]subsample: x'=%d, y'=%d, read %ld, wrote %ld, status = %d\n",
+ x, y, (long)(p - pr->ptr), (long)(q - pw->ptr), status);
+ pr->ptr = p;
+ pw->ptr = q;
+ ss->x = x, ss->y = y;
+ return status;
+}
+
+const stream_template s_Subsample_template = {
+ &st_Subsample_state, s_Subsample_init, s_Subsample_process, 4, 4,
+ 0 /* NULL */, s_Downsample_set_defaults
+};
+
+/* ------ Average ------ */
+
+private_st_Average_state();
+
+/* Set default parameter values (actually, just clear pointers). */
+static void
+s_Average_set_defaults(stream_state * st)
+{
+ stream_Average_state *const ss = (stream_Average_state *) st;
+
+ s_Downsample_set_defaults(st);
+ /* Clear pointers */
+ ss->sums = 0;
+}
+
+/* Initialize the state. */
+static int
+s_Average_init(stream_state * st)
+{
+ stream_Average_state *const ss = (stream_Average_state *) st;
+ int xf = ss->XFactor;
+
+ if ((float)xf != ss->XFactor) {
+ dmprintf1(st->memory,
+ "Average filter does not support non-integer downsample factor (%f)\n",
+ ss->XFactor);
+ return ERRC;
+ }
+
+ ss->sum_size =
+ ss->Colors * ((ss->WidthIn + xf - 1) / xf);
+ ss->copy_size = ss->sum_size -
+ (ss->padX || (ss->WidthIn % xf == 0) ? 0 : ss->Colors);
+ if (ss->sums)
+ gs_free_object(st->memory, ss->sums, "Average sums");
+ ss->sums =
+ (uint *)gs_alloc_byte_array(st->memory, ss->sum_size,
+ sizeof(uint), "Average sums");
+ if (ss->sums == 0)
+ return ERRC; /****** WRONG ******/
+ memset(ss->sums, 0, ss->sum_size * sizeof(uint));
+ return s_Downsample_init_common(st);
+}
+
+/* Release the state. */
+static void
+s_Average_release(stream_state * st)
+{
+ stream_Average_state *const ss = (stream_Average_state *) st;
+
+ gs_free_object(st->memory, ss->sums, "Average sums");
+}
+
+/* Process one buffer. */
+static int
+s_Average_process(stream_state * st, stream_cursor_read * pr,
+ stream_cursor_write * pw, bool last)
+{
+ stream_Average_state *const ss = (stream_Average_state *) st;
+ const byte *p = pr->ptr;
+ const byte *rlimit = pr->limit;
+ byte *q = pw->ptr;
+ byte *wlimit = pw->limit;
+ int spp = ss->Colors;
+ int width = ss->WidthIn;
+ int xf = ss->XFactor, yf = ss->YFactor;
+ int x = ss->x, y = ss->y;
+ uint *sums = ss->sums;
+ int status = 0;
+
+top:
+ if (y == yf || (last && p >= rlimit && ss->padY && y != 0)) {
+ /* We're copying averaged values to the output. */
+ int ncopy = min(ss->copy_size - x, wlimit - q);
+
+ if (ncopy) {
+ int scale = xf * y;
+
+ while (--ncopy >= 0)
+ *++q = (byte) (sums[x++] / scale);
+ }
+ if (x < ss->copy_size) {
+ status = 1;
+ goto out;
+ }
+ /* Done copying. */
+ x = y = 0;
+ memset(sums, 0, ss->sum_size * sizeof(uint));
+ }
+ while (rlimit - p >= spp) {
+ uint *bp = sums + x / xf * spp;
+ int i;
+
+ for (i = spp; --i >= 0;)
+ *bp++ += *++p;
+ if (++x == width) {
+ x = 0;
+ ++y;
+ goto top;
+ }
+ }
+out:
+ pr->ptr = p;
+ pw->ptr = q;
+ ss->x = x, ss->y = y;
+ return status;
+}
+
+const stream_template s_Average_template = {
+ &st_Average_state, s_Average_init, s_Average_process, 4, 4,
+ s_Average_release, s_Average_set_defaults
+};
+
+/* ------ Bicubic ------ */
+
+private_st_Bicubic_state();
+
+/* Set default parameter values (actually, just clear pointers). */
+static void
+s_Bicubic_set_defaults(stream_state * st)
+{
+ stream_Bicubic_state *const ss = (stream_Bicubic_state *) st;
+
+ s_Downsample_set_defaults(st);
+
+ ss->data = NULL;
+}
+
+/* Initialize the state. */
+static int
+s_Bicubic_init(stream_state * st)
+{
+ stream_Bicubic_state *const ss = (stream_Bicubic_state *) st;
+
+ if (ss->WidthIn < 4 || ss->HeightIn < 4)
+ return ERRC;
+
+ /* bicubic interpolation requires 4 lines of data */
+
+ ss->l_size = (ss->WidthIn * ss->Colors);
+ ss->d_size = (ss->l_size * 4);
+ ss->d_len = 0;
+ ss->y_in = 0;
+
+ if (ss->data)
+ gs_free_object(st->memory, ss->data, "Bicubic data");
+ ss->data = (byte *)gs_alloc_bytes(st->memory, ss->d_size, "Bicubic data");
+ if (ss->data == NULL)
+ return ERRC; /****** WRONG ******/
+
+ return s_Downsample_init_common(st);
+}
+
+/* Release the state. */
+static void
+s_Bicubic_release(stream_state * st)
+{
+ stream_Bicubic_state *const ss = (stream_Bicubic_state *) st;
+
+ gs_free_object(st->memory, ss->data, "Bicubic data");
+}
+
+static inline byte
+s_Bicubic_data_at(stream_Bicubic_state *const ss, int x, int y, int c)
+{
+ ulong idx;
+ if (y >= ss->HeightIn)
+ y = ss->HeightIn - 1;
+ y -= ss->y_in;
+ idx = ss->l_size * (y < 0 ? 0 : y) +
+ (x < 0 ? 0 : x >= ss->WidthIn ? ss->WidthIn-1 : x) * ss->Colors + c;
+ return (idx < ss->d_len) ? ss->data[idx] : 0;
+}
+
+static inline double
+s_Bicubic_interpolate(double *b, double delta)
+{
+ return b[1] + 0.5 * delta * (b[2] - b[0]
+ + delta * (2.0 * b[0] - 5.0 * b[1] + 4.0 * b[2] - b[3]
+ + delta * (3.0 * (b[1] - b[2]) + b[3] - b[0])));
+}
+
+static void
+s_Bicubic_interpolate_pixel(stream_Bicubic_state *const ss, int x_out,
+ int y_out, byte *out)
+{
+ double v1[4], v2[4], v;
+ double x = x_out * ss->XFactor;
+ double y = y_out * ss->YFactor;
+ double dx = x - floor(x), dy = y - floor(y);
+ int start_x = floor(x) - 1, start_y = floor(y) - 1;
+ int c, i, k;
+
+ for (c = 0; c < ss->Colors; c++) {
+ for (i = 0; i < 4; i++) {
+ for (k = 0; k < 4; k++)
+ v1[k] = s_Bicubic_data_at(ss, start_x + k, start_y + i, c);
+ v2[i] = s_Bicubic_interpolate(v1, dx);
+ }
+ v = s_Bicubic_interpolate(v2, dy);
+ out[c] = (v < 0.0f ? 0 : v > 255.0f ? 255 : (byte)floor(v + 0.5));
+ }
+}
+
+/* Process one buffer. */
+static int
+s_Bicubic_process(stream_state * st, stream_cursor_read * pr,
+ stream_cursor_write * pw, bool last)
+{
+ stream_Bicubic_state *const ss = (stream_Bicubic_state *) st;
+ int widthOut = s_Downsample_size_out(ss->WidthIn, ss->XFactor, ss->padX);
+ int heightOut = s_Downsample_size_out(ss->HeightIn, ss->YFactor, ss->padY);
+ int req_y;
+
+ for (;;) {
+ /* Find required y-offset in data buffer before doing more work */
+ req_y = floor(ss->y * ss->YFactor) - 1;
+ if (req_y < 0)
+ req_y = 0;
+
+ if (ss->y >= heightOut) {
+ /* output has been produced, ignore remaining input */
+ pr->ptr = pr->limit;
+ return 0;
+ }
+
+ if ((ss->d_len < ss->d_size) && (pr->ptr < pr->limit)) {
+ /* fill buffer using available data from input stream */
+ ulong copy = min(ss->d_size - ss->d_len, pr->limit - pr->ptr);
+ memcpy(ss->data + ss->d_len, pr->ptr + 1, copy);
+ ss->d_len += copy;
+ pr->ptr += copy;
+ }
+
+ while ((ss->y_in < req_y) && (ss->d_len >= ss->l_size)) {
+ /* remove one line from data buffer to reach req_y */
+ memmove(ss->data, ss->data + ss->l_size, ss->d_len - ss->l_size);
+ ss->d_len -= ss->l_size;
+ ss->y_in += 1;
+ }
+
+ if ((ss->d_len < ss->d_size) || (ss->y_in < req_y)) {
+ if (pr->ptr < pr->limit)
+ continue;
+ if (!last)
+ return 0; /* need more bytes in */
+ if (ss->y_in < req_y)
+ return 0; /* unable to produce any output */
+ }
+
+ while (ss->x < widthOut) {
+ if (pw->ptr + ss->Colors > pw->limit)
+ return 1; /* need more space out */
+
+ s_Bicubic_interpolate_pixel(ss, ss->x, ss->y, pw->ptr + 1);
+ ss->x++;
+ pw->ptr += ss->Colors;
+ }
+ ss->x = 0;
+ ss->y += 1;
+ }
+
+ return 0;
+}
+
+const stream_template s_Bicubic_template = {
+ &st_Bicubic_state, s_Bicubic_init, s_Bicubic_process, 4, 4,
+ s_Bicubic_release, s_Bicubic_set_defaults
+};
+
+/* ---------------- Image compression chooser ---------------- */
+
+private_st_compr_chooser_state();
+
+/* Initialize the state. */
+static int
+s_compr_chooser_init(stream_state * st)
+{
+ stream_compr_chooser_state *const ss = (stream_compr_chooser_state *) st;
+
+ ss->choice = 0;
+ ss->width = ss->height = ss->depth = ss->bits_per_sample = 0;
+ ss->sample = 0;
+ ss->samples_count = 0;
+ ss->bits_left = 0;
+ ss->packed_data = 0;
+ ss->lower_plateaus = ss->upper_plateaus = 0;
+ ss->gradients = 0;
+ return 0;
+}
+
+/* Set image dimensions. */
+int
+s_compr_chooser_set_dimensions(stream_compr_chooser_state * ss, int width,
+ int height, int depth, int bits_per_sample)
+{
+ ss->width = width;
+ ss->height = height;
+ ss->depth = depth;
+ ss->bits_per_sample = bits_per_sample;
+ ss->sample = gs_alloc_bytes(ss->memory, width * depth, "s_compr_chooser_set_dimensions");
+ if (ss->sample == 0)
+ return_error(gs_error_VMerror);
+ return 0;
+}
+
+/* Release state. */
+static void
+s_compr_chooser_release(stream_state * st)
+{
+ stream_compr_chooser_state *const ss = (stream_compr_chooser_state *) st;
+
+ gs_free_object(ss->memory, ss->sample, "s_compr_chooser_release");
+}
+
+/* Estimate a row for photo/lineart recognition. */
+static void
+s_compr_chooser__estimate_row(stream_compr_chooser_state *const ss, byte *p)
+{
+ /* This function uses a statistical algorithm being not well defined.
+
+ We compute areas covered by gradients,
+ separately with small width (line art)
+ and with big width (photo).
+ Making the choice based on the areas.
+
+ Note that we deal with horizontal frequencies only.
+ Dealing with vertical ones would be too expensive.
+ */
+ const int delta = 256 / 16; /* about 1/16 of the color range */
+ const int max_lineart_boundary_width = 3; /* pixels */
+ const int max_gradient_constant = 10; /* pixels */
+ int i, j0 = 0, j1 = 0;
+ int w0 = p[0], w1 = p[0], v;
+ ulong plateau_count = 0, lower_plateaus = 0;
+ ulong upper_plateaus = 0, gradients = 0;
+ bool lower = false, upper = false;
+
+ for (i = 1; i < ss->width; i++) {
+ v = p[i];
+ if (!lower) {
+ if (w1 < v) {
+ if (!upper)
+ j1 = i - 1;
+ w1 = v;
+ upper = true;
+ } else if (w1 == v && j1 < i - max_gradient_constant)
+ j1 = i - max_gradient_constant; /* inner constant plateaw */
+ else if (upper && w1 - delta > v) {
+ /* end of upper plateau at w1-delta...w1 */
+ for (j0 = i - 1; j0 > j1 && w1 - delta <= p[j0]; j0--) DO_NOTHING;
+ /* upper plateau j0+1...i-1 */
+ if(j0 > 0 && i < ss->width - 1) /* ignore sides */
+ upper_plateaus += i - j0;
+ plateau_count ++;
+ if (j0 > j1) {
+ /* upgrade j1...j0 */
+ if (j0 > j1 + max_lineart_boundary_width)
+ gradients += j0 - j1;
+ }
+ j1 = i;
+ upper = false;
+ w0 = w1;
+ continue;
+ }
+ }
+ if (!upper) {
+ if (w0 > v) {
+ if (!lower)
+ j1 = i - 1;
+ w0 = v;
+ lower = true;
+ } else if (w0 == v && j1 < i - max_gradient_constant)
+ j1 = i - max_gradient_constant; /* inner constant plateaw */
+ else if (lower && w0 + delta < v) {
+ /* end of lower plateau at w0...w0+delta */
+ for (j0 = i - 1; j0 > j1 && w0 + delta >= p[j0]; j0--) DO_NOTHING;
+ /* lower plateau j0+1...i-1 */
+ if(j0 > 0 && i < ss->width - 1) /* ignore sides */
+ lower_plateaus += i - j0;
+ plateau_count ++;
+ if (j0 > j1) {
+ /* downgrade j1...j0 */
+ if (j0 > j1 + max_lineart_boundary_width)
+ gradients += j0 - j1;
+ }
+ j1 = i;
+ lower = false;
+ w1 = w0;
+ }
+ }
+ }
+ if (plateau_count > ss->width / 6) {
+ /* Possibly a dithering, can't recognize.
+ It would be better to estimate frequency histogram rather than
+ rough quantity, but we hope that the simpler test can work fine.
+ */
+ } else if (!plateau_count) /* a pseudo-constant color through entire row */
+ DO_NOTHING; /* ignore such lines */
+ else {
+ int plateaus;
+ ss->lower_plateaus += lower_plateaus;
+ ss->upper_plateaus += upper_plateaus;
+ ss->gradients += gradients;
+ plateaus = min(ss->lower_plateaus, ss->upper_plateaus); /* (fore/back)ground */
+ if (ss->gradients >= 10000 && ss->gradients > plateaus / 6)
+ ss->choice = 1; /* choice is made : photo */
+ else if (plateaus >= 100000 && plateaus / 5000 >= ss->gradients)
+ ss->choice = 2; /* choice is made : lineart */
+ }
+}
+
+/* Recognize photo/lineart. */
+static void
+s_compr_chooser__recognize(stream_compr_chooser_state * ss)
+{
+ int i;
+ byte *p = ss->sample;
+
+ for (i = 0; i < ss->depth; i++, p += ss->width)
+ s_compr_chooser__estimate_row(ss, p);
+ /* todo: make decision */
+}
+
+/* Uppack data and recognize photo/lineart. */
+static void
+s_compr_chooser__unpack_and_recognize(stream_compr_chooser_state *const ss,
+ const byte *data, int length)
+{
+ /*
+ * Input samples are packed ABCABCABC..., but the sample[] array of
+ * unpacked values is stored AAA...BBB...CCC. i counts samples within
+ * a pixel, multiplied by width; j counts pixels.
+ */
+ uint i = (ss->samples_count % ss->depth) * ss->width;
+ uint j = ss->samples_count / ss->depth;
+ const byte *p = data;
+ int l = length;
+
+ while (l) {
+ if (ss->bits_left < 8) {
+ uint k = (sizeof(ss->packed_data) * 8 - ss->bits_left) / 8;
+
+ k = min(k, l);
+ for (; k; k--, l--, p++, ss->bits_left += 8)
+ ss->packed_data = (ss->packed_data << 8) + *p;
+ }
+ while (ss->bits_left >= ss->bits_per_sample) {
+ uint k = ss->bits_left - ss->bits_per_sample;
+ ulong v = ss->packed_data >> k;
+
+ ss->packed_data -= (v << k);
+ ss->bits_left -= ss->bits_per_sample;
+ if (ss->bits_per_sample > 8)
+ v >>= ss->bits_per_sample - 8;
+ else
+ v <<= 8 - ss->bits_per_sample;
+ ss->sample[i + j] = (byte)v; /* scaled to 0...255 */
+ i += ss->width;
+ if (i >= ss->width * ss->depth)
+ i = 0, j++;
+ ss->samples_count++;
+ if (ss->samples_count >= ss->width * ss->depth) {
+ s_compr_chooser__recognize(ss);
+ ss->packed_data = 0;
+ ss->bits_left = 0;
+ ss->samples_count = 0;
+ i = j = 0;
+ }
+ }
+ }
+}
+
+/* Process a buffer. */
+static int
+s_compr_chooser_process(stream_state * st, stream_cursor_read * pr,
+ stream_cursor_write * pw, bool last)
+{
+ stream_compr_chooser_state *const ss = (stream_compr_chooser_state *) st;
+ int l = pr->limit - pr->ptr;
+
+ if (ss->width >= 3) /* Can't process narrow images. */
+ s_compr_chooser__unpack_and_recognize(ss, pr->ptr + 1, l);
+ pr->ptr += l;
+ return 0;
+}
+
+const stream_template s_compr_chooser_template = {
+ &st_compr_chooser_state, s_compr_chooser_init, s_compr_chooser_process, 1, 1,
+ s_compr_chooser_release, 0 /* NULL */
+};
+
+/* Get choice */
+uint
+s_compr_chooser__get_choice(stream_compr_chooser_state *ss, bool force)
+{
+ ulong plateaus = min(ss->lower_plateaus, ss->upper_plateaus);
+
+ if (ss->choice)
+ return ss->choice;
+ if (force) {
+ if (ss->gradients > plateaus / 12) /* messenger16.pdf, page 3. */
+ return 1; /* photo */
+ else if (plateaus / 5000 >= ss->gradients)
+ return 2; /* lineart */
+ }
+ return 0;
+}
+
+/* ---------------- Am image color conversion filter ---------------- */
+
+private_st_image_colors_state();
+
+/* Initialize the state. */
+static int
+s_image_colors_init(stream_state * st)
+{
+ stream_image_colors_state *const ss = (stream_image_colors_state *) st;
+
+ ss->width = ss->height = ss->depth = ss->bits_per_sample = 0;
+ ss->output_bits_buffer = 0;
+ ss->output_bits_buffered = 0;
+ ss->output_depth = 1;
+ ss->output_component_index = ss->output_depth;
+ ss->output_bits_per_sample = 1;
+ ss->output_component_bits_written = 0;
+ ss->raster = 0;
+ ss->row_bits = 0;
+ ss->row_bits_passed = 0;
+ ss->row_alignment_bytes = 0;
+ ss->row_alignment_bytes_left = 0;
+ ss->input_component_index = 0;
+ ss->input_bits_buffer = 0;
+ ss->input_bits_buffered = 0;
+ ss->convert_color = 0;
+ ss->pcs = 0;
+ ss->pdev = 0;
+ ss->pis = 0;
+ return 0;
+}
+
+static int
+s_image_colors_convert_color_to_mask(stream_image_colors_state *ss)
+{
+ int i, ii;
+
+ for (i = ii = 0; i < ss->depth; i++, ii += 2)
+ if (ss->input_color[i] < ss->MaskColor[ii] ||
+ ss->input_color[i] > ss->MaskColor[ii + 1])
+ break;
+ ss->output_color[0] = (i < ss->depth ? 1 : 0);
+ return 0;
+}
+
+static int
+s_image_colors_convert_to_device_color(stream_image_colors_state * ss)
+{
+ gs_client_color cc;
+ gx_device_color dc;
+ int i, code;
+ double v0 = (1 << ss->bits_per_sample) - 1;
+ double v1 = (1 << ss->output_bits_per_sample) - 1;
+
+ for (i = 0; i < ss->depth; i++)
+ cc.paint.values[i] = ss->input_color[i] *
+ (ss->Decode[i * 2 + 1] - ss->Decode[i * 2]) / v0 + ss->Decode[i * 2];
+
+ code = ss->pcs->type->remap_color(&cc, ss->pcs, &dc, ss->pis,
+ ss->pdev, gs_color_select_texture);
+ if (code < 0)
+ return code;
+ for (i = 0; i < ss->output_depth; i++) {
+ uint m = (1 << ss->pdev->color_info.comp_bits[i]) - 1;
+ uint w = (dc.colors.pure >> ss->pdev->color_info.comp_shift[i]) & m;
+
+ ss->output_color[i] = (uint)(v1 * w / m + 0.5);
+ }
+ return 0;
+}
+
+/* Set masc colors dimensions. */
+void
+s_image_colors_set_mask_colors(stream_image_colors_state * ss, uint *MaskColor)
+{
+ ss->convert_color = s_image_colors_convert_color_to_mask;
+ memcpy(ss->MaskColor, MaskColor, ss->depth * sizeof(MaskColor[0]) * 2);
+}
+
+/* Set image dimensions. */
+void
+s_image_colors_set_dimensions(stream_image_colors_state * ss,
+ int width, int height, int depth, int bits_per_sample)
+{
+ ss->width = width;
+ ss->height = height;
+ ss->depth = depth;
+ ss->bits_per_sample = bits_per_sample;
+ ss->row_bits = bits_per_sample * depth * width;
+ ss->raster = bitmap_raster(ss->row_bits);
+ ss->row_alignment_bytes = 0; /* (ss->raster * 8 - ss->row_bits) / 8) doesn't work. */
+}
+
+void
+s_image_colors_set_color_space(stream_image_colors_state * ss, gx_device *pdev,
+ const gs_color_space *pcs, const gs_imager_state *pis,
+ float *Decode)
+{
+ ss->output_depth = pdev->color_info.num_components;
+ ss->output_component_index = ss->output_depth;
+ ss->output_bits_per_sample = pdev->color_info.comp_bits[0]; /* Same precision for all components. */
+ ss->convert_color = s_image_colors_convert_to_device_color;
+ ss->pdev = pdev;
+ ss->pcs = pcs;
+ ss->pis = pis;
+ memcpy(ss->Decode, Decode, ss->depth * sizeof(Decode[0]) * 2);
+}
+
+void
+s_new_image_colors_set_color_space(stream_image_colors_state * ss, gx_device *pdev,
+ const gs_color_space *pcs, const gs_imager_state *pis,
+ float *Decode)
+{
+ ss->output_depth = pdev->color_info.num_components;
+ ss->output_component_index = ss->output_depth;
+ ss->output_bits_per_sample = pdev->color_info.comp_bits[0]; /* Same precision for all components. */
+ ss->convert_color = s_image_colors_convert_to_device_color;
+ ss->pdev = pdev;
+ ss->pcs = pcs;
+ ss->pis = pis;
+ memcpy(ss->Decode, Decode, ss->depth * sizeof(Decode[0]) * 2);
+}
+
+/* Process a buffer. */
+static int
+s_image_colors_process(stream_state * st, stream_cursor_read * pr,
+ stream_cursor_write * pw, bool last)
+{
+ stream_image_colors_state *const ss = (stream_image_colors_state *) st;
+
+ for (;;) {
+ if (pw->ptr >= pw->limit)
+ return 1;
+ if (ss->row_bits_passed >= ss->row_bits) {
+ ss->row_alignment_bytes_left = ss->row_alignment_bytes;
+ ss->input_bits_buffered = 0;
+ ss->input_bits_buffer = 0; /* Just to simplify the debugging. */
+ if (ss->output_bits_buffered) {
+ *(++pw->ptr) = ss->output_bits_buffer;
+ ss->output_bits_buffered = 0;
+ ss->output_bits_buffer = 0;
+ }
+ ss->row_bits_passed = 0;
+ continue;
+ }
+ if (ss->row_alignment_bytes_left) {
+ uint k = pr->limit - pr->ptr;
+
+ if (k > ss->row_alignment_bytes_left)
+ k = ss->row_alignment_bytes_left;
+ pr->ptr += k;
+ ss->row_alignment_bytes_left -= k;
+ if (pr->ptr >= pr->limit)
+ return 0;
+ }
+ if (ss->output_component_index < ss->output_depth) {
+ for (;ss->output_component_index < ss->output_depth;) {
+ uint fitting = (uint)(8 - ss->output_bits_buffered);
+ uint v, w, u, n, m;
+
+ if (pw->ptr >= pw->limit)
+ return 1;
+ v = ss->output_color[ss->output_component_index];
+ n = ss->output_bits_per_sample - ss->output_component_bits_written; /* no. of bits left */
+ w = v - ((v >> n) << n); /* the current component without written bits. */
+ if (fitting > n)
+ fitting = n; /* no. of bits to write. */
+ m = n - fitting; /* no. of bits will left. */
+ u = w >> m; /* bits to write (near lsb). */
+ ss->output_bits_buffer |= u << (8 - ss->output_bits_buffered - fitting);
+ ss->output_bits_buffered += fitting;
+ if (ss->output_bits_buffered >= 8) {
+ *(++pw->ptr) = ss->output_bits_buffer;
+ ss->output_bits_buffered = 0;
+ ss->output_bits_buffer = 0;
+ }
+ ss->output_component_bits_written += fitting;
+ if (ss->output_component_bits_written >= ss->output_bits_per_sample) {
+ ss->output_component_index++;
+ ss->output_component_bits_written = 0;
+ }
+ }
+ ss->row_bits_passed += ss->bits_per_sample * ss->depth;
+ continue;
+ }
+ if (ss->input_bits_buffered < ss->bits_per_sample) {
+ if (pr->ptr >= pr->limit)
+ return 0;
+ ss->input_bits_buffer = (ss->input_bits_buffer << 8) | *++pr->ptr;
+ ss->input_bits_buffered += 8;
+ /* fixme: delay shifting the input ptr until input_bits_buffer is cleaned. */
+ }
+ if (ss->input_bits_buffered >= ss->bits_per_sample) {
+ uint w;
+
+ ss->input_bits_buffered -= ss->bits_per_sample;
+ ss->input_color[ss->input_component_index] = w = ss->input_bits_buffer >> ss->input_bits_buffered;
+ ss->input_bits_buffer &= ~(w << ss->input_bits_buffered);
+ ss->input_component_index++;
+ if (ss->input_component_index >= ss->depth) {
+ int code = ss->convert_color(ss);
+
+ if (code < 0)
+ return ERRC;
+ ss->output_component_index = 0;
+ ss->input_component_index = 0;
+ }
+ }
+ }
+}
+
+const stream_template s__image_colors_template = {
+ &st_stream_image_colors_state, s_image_colors_init, s_image_colors_process, 1, 1,
+ NULL, NULL
+};
diff --git a/devices/vector/gdevpsds.h b/devices/vector/gdevpsds.h
new file mode 100644
index 000000000..10e3666f3
--- /dev/null
+++ b/devices/vector/gdevpsds.h
@@ -0,0 +1,247 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Image processing stream interface for PostScript and PDF writers */
+
+#ifndef gdevpsds_INCLUDED
+# define gdevpsds_INCLUDED
+
+#include "strimpl.h"
+#include "gsiparam.h"
+
+/* ---------------- Depth conversion ---------------- */
+
+/* Convert between 1/2/4/12 bits and 8 bits. */
+typedef struct stream_1248_state_s {
+ stream_state_common;
+ /* The following are set at initialization time. */
+ uint samples_per_row; /* >0 */
+ int bits_per_sample; /* 1, 2, 4, 12 */
+ /* The following are updated dynamically. */
+ uint left; /* # of samples left in current row */
+} stream_1248_state;
+
+/* Convert N (1, 2, 4, 12, 16) bits to 8. */
+extern const stream_template s_1_8_template;
+extern const stream_template s_2_8_template;
+extern const stream_template s_4_8_template;
+extern const stream_template s_12_8_template;
+extern const stream_template s_16_8_template;
+
+/* Reduce 8 bits to N (1, 2, 4). */
+/* We do not currently support converting 8 bits to 12 or 16. */
+extern const stream_template s_8_1_template;
+extern const stream_template s_8_2_template;
+extern const stream_template s_8_4_template;
+
+/* Initialize an expansion or reduction stream. */
+int s_1248_init(stream_1248_state *ss, int Columns, int samples_per_pixel);
+
+/* ---------------- Color space conversion ---------------- */
+
+/* Convert (8-bit) CMYK to RGB. */
+typedef struct stream_C2R_state_s {
+ stream_state_common;
+ /* The following are set at initialization time. */
+ const gs_imager_state *pis; /* for UCR & BG */
+} stream_C2R_state;
+
+#define private_st_C2R_state() /* in gdevpsds.c */\
+ gs_private_st_ptrs1(st_C2R_state, stream_C2R_state, "stream_C2R_state",\
+ c2r_enum_ptrs, c2r_reloc_ptrs, pis)
+extern const stream_template s_C2R_template;
+
+/* Initialize a CMYK => RGB conversion stream. */
+int s_C2R_init(stream_C2R_state *ss, const gs_imager_state *pis);
+
+/* Convert an image to indexed form (IndexedEncode filter). */
+typedef struct stream_IE_state_s {
+ stream_state_common;
+ /* The client sets the following before initializing the stream. */
+ int BitsPerComponent; /* 1, 2, 4, 8 */
+ int NumComponents;
+ int Width; /* pixels per scan line, > 0 */
+ int BitsPerIndex; /* 1, 2, 4, 8 */
+ /*
+ * Note: this is not quite the same as the Decode array for images:
+ * [0..1] designates the range of the corresponding component of the
+ * color space, not the literal values 0..1. This is the same for
+ * all color spaces except Lab, where the default values here are
+ * [0 1 0 1 0 1] rather than [0 100 amin amax bmin bmax].
+ */
+ const float *Decode;
+ /*
+ * The client must provide a Table whose size is at least
+ * ((1 << BitsPerIndex) + 1) * NumComponents. After the stream is
+ * closed, the first (N + 1) * NumComponents bytes of the Table
+ * will hold the palette, where N is the contents of the last byte of
+ * the Table.
+ */
+ gs_bytestring Table;
+ /* The following change dynamically. */
+ int hash_table[400]; /* holds byte offsets in Table */
+ int next_index; /* next Table offset to assign */
+ uint byte_in;
+ int in_bits_left;
+ int next_component;
+ uint byte_out;
+ int x;
+} stream_IE_state;
+
+#define private_st_IE_state() /* in gdevpsds.c */\
+ gs_public_st_composite(st_IE_state, stream_IE_state, "stream_IE_state",\
+ ie_state_enum_ptrs, ie_state_reloc_ptrs)
+
+extern const stream_template s_IE_template;
+
+/* ---------------- Downsampling ---------------- */
+
+/* Downsample, possibly with anti-aliasing. */
+#define stream_Downsample_state_common\
+ stream_state_common;\
+ /* The client sets the following before initialization. */\
+ int Colors;\
+ int WidthIn, HeightIn;\
+ float XFactor, YFactor;\
+ bool AntiAlias;\
+ bool padX, padY; /* keep excess samples */\
+ /* The following are updated dynamically. */\
+ int x, y /* position within input image */
+#define s_Downsample_set_defaults_inline(ss)\
+ ((ss)->AntiAlias = (ss)->padX = (ss)->padY = false)
+typedef struct stream_Downsample_state_s {
+ stream_Downsample_state_common;
+} stream_Downsample_state;
+
+/* Return the number of samples after downsampling. */
+int s_Downsample_size_out(int size_in, float factor, bool pad);
+
+/* Subsample */
+typedef struct stream_Subsample_state_s {
+ stream_Downsample_state_common;
+} stream_Subsample_state;
+extern const stream_template s_Subsample_template;
+
+/* Average */
+typedef struct stream_Average_state_s {
+ stream_Downsample_state_common;
+ uint sum_size;
+ uint copy_size;
+ uint *sums; /* accumulated sums for average */
+} stream_Average_state;
+
+#define private_st_Average_state() /* in gdevpsds.c */\
+ gs_private_st_ptrs1(st_Average_state, stream_Average_state,\
+ "stream_Average_state", avg_enum_ptrs, avg_reloc_ptrs, sums)
+extern const stream_template s_Average_template;
+
+/* Bicubic */
+typedef struct stream_Bicubic_state_s {
+ stream_Downsample_state_common;
+ int y_in;
+ ulong l_size, d_size, d_len;
+ byte *data;
+} stream_Bicubic_state;
+
+#define private_st_Bicubic_state() /* in gdevpsds.c */\
+ gs_private_st_ptrs1(st_Bicubic_state, stream_Bicubic_state,\
+ "stream_Bicubic_state", bcb_enum_ptrs, bcb_reloc_ptrs, data)
+extern const stream_template s_Bicubic_template;
+
+/* ---------------- Image compression chooser ---------------- */
+
+typedef struct stream_compr_chooser_state_s {
+ stream_state_common;
+ uint choice;
+ uint width, height, depth, bits_per_sample;
+ uint samples_count, bits_left;
+ ulong packed_data;
+ byte *sample;
+ ulong upper_plateaus, lower_plateaus;
+ ulong gradients;
+} stream_compr_chooser_state;
+
+#define private_st_compr_chooser_state() /* in gdevpsds.c */\
+ gs_private_st_ptrs1(st_compr_chooser_state, stream_compr_chooser_state, \
+ "stream_compr_chooser_state",\
+ compr_chooser_enum_ptrs, compr_chooser_reloc_ptrs, sample)
+
+extern const stream_template s_compr_chooser_template;
+
+/* Set image dimensions. */
+int
+s_compr_chooser_set_dimensions(stream_compr_chooser_state * st, int width,
+ int height, int depth, int bits_per_sample);
+
+/* Get choice */
+uint s_compr_chooser__get_choice(stream_compr_chooser_state *st, bool force);
+
+/* ---------------- Am image color conversion filter ---------------- */
+
+#ifndef gx_device_DEFINED
+# define gx_device_DEFINED
+typedef struct gx_device_s gx_device;
+#endif
+
+typedef struct stream_image_colors_state_s stream_image_colors_state;
+
+struct stream_image_colors_state_s {
+ stream_state_common;
+ uint width, height, depth, bits_per_sample;
+ byte output_bits_buffer;
+ uint output_bits_buffered;
+ uint output_component_bits_written;
+ uint output_component_index;
+ uint output_depth, output_bits_per_sample;
+ uint raster;
+ uint row_bits;
+ uint row_bits_passed;
+ uint row_alignment_bytes;
+ uint row_alignment_bytes_left;
+ uint input_component_index;
+ uint input_bits_buffer;
+ uint input_bits_buffered;
+ uint input_color[GS_IMAGE_MAX_COLOR_COMPONENTS];
+ uint output_color[GS_IMAGE_MAX_COLOR_COMPONENTS];
+ uint MaskColor[GS_IMAGE_MAX_COLOR_COMPONENTS * 2];
+ float Decode[GS_IMAGE_MAX_COLOR_COMPONENTS * 2];
+ const gs_color_space *pcs;
+ gx_device *pdev;
+ const gs_imager_state *pis;
+ int (*convert_color)(stream_image_colors_state *);
+};
+
+#define private_st_image_colors_state() /* in gdevpsds.c */\
+ gs_private_st_ptrs3(st_stream_image_colors_state, stream_image_colors_state,\
+ "stream_image_colors_state", stream_image_colors_enum_ptrs,\
+ stream_image_colors_reloc_ptrs, pcs, pdev, pis)
+
+void s_image_colors_set_dimensions(stream_image_colors_state * st,
+ int width, int height, int depth, int bits_per_sample);
+
+void s_image_colors_set_mask_colors(stream_image_colors_state * ss, uint *MaskColor);
+
+void s_image_colors_set_color_space(stream_image_colors_state * ss, gx_device *pdev,
+ const gs_color_space *pcs, const gs_imager_state *pis,
+ float *Decode);
+
+void s_new_image_colors_set_color_space(stream_image_colors_state * ss, gx_device *pdev,
+ const gs_color_space *pcs, const gs_imager_state *pis,
+ float *Decode);
+
+extern const stream_template s__image_colors_template;
+
+#endif /* gdevpsds_INCLUDED */
diff --git a/devices/vector/gdevpsdu.c b/devices/vector/gdevpsdu.c
new file mode 100644
index 000000000..7e331bf8f
--- /dev/null
+++ b/devices/vector/gdevpsdu.c
@@ -0,0 +1,520 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Common utilities for PostScript and PDF writers */
+#include "stdio_.h" /* for FILE for jpeglib.h */
+#include "jpeglib_.h" /* for sdct.h */
+#include "memory_.h"
+#include "gx.h"
+#include "gserrors.h"
+#include "gdevpsdf.h"
+#include "strimpl.h"
+#include "sa85x.h"
+#include "scfx.h"
+#include "sdct.h"
+#include "sjpeg.h"
+#include "spprint.h"
+#include "gsovrc.h"
+#include "gsicc_cache.h"
+
+/* Structure descriptors */
+public_st_device_psdf();
+public_st_psdf_binary_writer();
+
+/* Standard color command names. */
+const psdf_set_color_commands_t psdf_set_fill_color_commands = {
+ "g", "rg", "k", "cs", "sc", "scn"
+};
+const psdf_set_color_commands_t psdf_set_stroke_color_commands = {
+ "G", "RG", "K", "CS", "SC", "SCN"
+};
+
+/* Define parameter-setting procedures. */
+extern stream_state_proc_put_params(s_DCTE_put_params, stream_DCT_state);
+
+/* ---------------- Vector implementation procedures ---------------- */
+
+int
+psdf_setlinewidth(gx_device_vector * vdev, double width)
+{
+ pprintg1(gdev_vector_stream(vdev), "%g w\n", width);
+ return 0;
+}
+
+int
+psdf_setlinecap(gx_device_vector * vdev, gs_line_cap cap)
+{
+ switch (cap) {
+ case gs_cap_butt:
+ case gs_cap_round:
+ case gs_cap_square:
+ pprintd1(gdev_vector_stream(vdev), "%d J\n", cap);
+ break;
+ case gs_cap_triangle:
+ /* If we get a PCL triangle cap, substitute with a round cap */
+ pprintd1(gdev_vector_stream(vdev), "%d J\n", gs_cap_round);
+ break;
+ default:
+ /* Ensure we don't write a broken file if we don't recognise the cap */
+ emprintf1(vdev->memory,
+ "Unknown line cap enumerator %d, substituting butt\n",
+ cap);
+ pprintd1(gdev_vector_stream(vdev), "%d J\n", gs_cap_butt);
+ break;
+ }
+ return 0;
+}
+
+int
+psdf_setlinejoin(gx_device_vector * vdev, gs_line_join join)
+{
+ switch (join) {
+ case gs_join_miter:
+ case gs_join_round:
+ case gs_join_bevel:
+ pprintd1(gdev_vector_stream(vdev), "%d j\n", join);
+ break;
+ case gs_join_none:
+ /* If we get a PCL triangle join, substitute with a bevel join */
+ pprintd1(gdev_vector_stream(vdev), "%d j\n", gs_join_bevel);
+ break;
+ case gs_join_triangle:
+ /* If we get a PCL triangle join, substitute with a miter join */
+ pprintd1(gdev_vector_stream(vdev), "%d j\n", gs_join_miter);
+ break;
+ default:
+ /* Ensure we don't write a broken file if we don't recognise the join */
+ emprintf1(vdev->memory,
+ "Unknown line join enumerator %d, substituting miter\n",
+ join);
+ pprintd1(gdev_vector_stream(vdev), "%d j\n", gs_join_miter);
+ break;
+ }
+ return 0;
+}
+
+int
+psdf_setmiterlimit(gx_device_vector * vdev, double limit)
+{
+ pprintg1(gdev_vector_stream(vdev), "%g M\n", limit);
+ return 0;
+}
+
+int
+psdf_setdash(gx_device_vector * vdev, const float *pattern, uint count,
+ double offset)
+{
+ stream *s = gdev_vector_stream(vdev);
+ int i;
+
+ stream_puts(s, "[ ");
+ for (i = 0; i < count; ++i)
+ pprintg1(s, "%g ", pattern[i]);
+ pprintg1(s, "] %g d\n", offset);
+ return 0;
+}
+
+int
+psdf_setflat(gx_device_vector * vdev, double flatness)
+{
+ pprintg1(gdev_vector_stream(vdev), "%g i\n", flatness);
+ return 0;
+}
+
+int
+psdf_setlogop(gx_device_vector * vdev, gs_logical_operation_t lop,
+ gs_logical_operation_t diff)
+{
+/****** SHOULD AT LEAST DETECT SET-0 & SET-1 ******/
+ return 0;
+}
+
+int
+psdf_dorect(gx_device_vector * vdev, fixed x0, fixed y0, fixed x1, fixed y1,
+ gx_path_type_t type)
+{
+ int code = (*vdev_proc(vdev, beginpath)) (vdev, type);
+
+ if (code < 0)
+ return code;
+ pprintg4(gdev_vector_stream(vdev), "%g %g %g %g re\n",
+ fixed2float(x0), fixed2float(y0),
+ fixed2float(x1 - x0), fixed2float(y1 - y0));
+ return (*vdev_proc(vdev, endpath)) (vdev, type);
+}
+
+int
+psdf_beginpath(gx_device_vector * vdev, gx_path_type_t type)
+{
+ return 0;
+}
+
+int
+psdf_moveto(gx_device_vector * vdev, double x0, double y0, double x, double y,
+ gx_path_type_t type)
+{
+ pprintg2(gdev_vector_stream(vdev), "%g %g m\n", x, y);
+ return 0;
+}
+
+int
+psdf_lineto(gx_device_vector * vdev, double x0, double y0, double x, double y,
+ gx_path_type_t type)
+{
+ pprintg2(gdev_vector_stream(vdev), "%g %g l\n", x, y);
+ return 0;
+}
+
+int
+psdf_curveto(gx_device_vector * vdev, double x0, double y0,
+ double x1, double y1, double x2, double y2, double x3, double y3,
+ gx_path_type_t type)
+{
+ if (x1 == x0 && y1 == y0 && x2 == x3 && y2 == y3)
+ pprintg2(gdev_vector_stream(vdev), "%g %g l\n", x3, y3);
+ else if (x1 == x0 && y1 == y0)
+ pprintg4(gdev_vector_stream(vdev), "%g %g %g %g v\n",
+ x2, y2, x3, y3);
+ else if (x3 == x2 && y3 == y2)
+ pprintg4(gdev_vector_stream(vdev), "%g %g %g %g y\n",
+ x1, y1, x2, y2);
+ else
+ pprintg6(gdev_vector_stream(vdev), "%g %g %g %g %g %g c\n",
+ x1, y1, x2, y2, x3, y3);
+ return 0;
+}
+
+int
+psdf_closepath(gx_device_vector * vdev, double x0, double y0,
+ double x_start, double y_start, gx_path_type_t type)
+{
+ stream_puts(gdev_vector_stream(vdev), "h\n");
+ return 0;
+}
+
+/* endpath is deliberately omitted. */
+
+/* ---------------- Utilities ---------------- */
+
+gx_color_index
+psdf_adjust_color_index(gx_device_vector *vdev, gx_color_index color)
+{
+ /*
+ * Since gx_no_color_index is all 1's, we can't represent
+ * a CMYK color consisting of full ink in all 4 components.
+ * However, this color must be available for registration marks.
+ * gxcmap.c fudges this by changing the K component to 254;
+ * undo this fudge here.
+ */
+ return (color == (gx_no_color_index ^ 1) ? gx_no_color_index : color);
+}
+
+/* Round a double value to a specified precision. */
+double
+psdf_round(double v, int precision, int radix)
+{
+ double mul = 1;
+ double w = v;
+
+ if (w <= 0)
+ return w;
+ while (w < precision) {
+ w *= radix;
+ mul *= radix;
+ }
+ return (int)(w + 0.5) / mul;
+}
+
+/*
+ * Since we only have 8 bits of color to start with, round the
+ * values to 3 digits for more compact output.
+ */
+static inline double
+round_byte_color(gx_color_index cv)
+{
+ return (int)((uint)cv * (1000.0 / 255.0) + 0.5) / 1000.0;
+}
+int
+psdf_set_color(gx_device_vector * vdev, const gx_drawing_color * pdc,
+ const psdf_set_color_commands_t *ppscc, bool UseOldColor)
+{
+ const char *setcolor;
+ int num_des_comps, code;
+ cmm_dev_profile_t *dev_profile;
+
+ if (UseOldColor) {
+ num_des_comps = vdev->color_info.num_components;
+ } else {
+ code = dev_proc((gx_device *)vdev, get_profile)((gx_device *)vdev, &dev_profile);
+ if (code < 0)
+ return code;
+ num_des_comps = gsicc_get_device_profile_comps(dev_profile);
+ }
+ if (!gx_dc_is_pure(pdc))
+ return_error(gs_error_rangecheck);
+ {
+ stream *s = gdev_vector_stream(vdev);
+ gx_color_index color =
+ psdf_adjust_color_index(vdev, gx_dc_pure_color(pdc));
+ /*
+ * Normally we would precompute all of v0 .. v3, but gcc 2.7.2.3
+ * generates incorrect code for Intel CPUs if we do this. The code
+ * below is longer, but does less computation in some cases.
+ */
+ double v3 = round_byte_color(color & 0xff);
+
+ switch (num_des_comps) {
+ case 4:
+ /* if (v0 == 0 && v1 == 0 && v2 == 0 && ...) */
+ if ((color & 0xffffff00) == 0 && ppscc->setgray != 0) {
+ v3 = 1.0 - v3;
+ goto g;
+ }
+ pprintg4(s, "%g %g %g %g", round_byte_color(color >> 24),
+ round_byte_color((color >> 16) & 0xff),
+ round_byte_color((color >> 8) & 0xff), v3);
+ setcolor = ppscc->setcmykcolor;
+ break;
+ case 3:
+ /* if (v1 == v2 && v2 == v3 && ...) */
+ if (!((color ^ (color >> 8)) & 0xffff) && ppscc->setgray != 0)
+ goto g;
+ pprintg3(s, "%g %g %g", round_byte_color((color >> 16) & 0xff),
+ round_byte_color((color >> 8) & 0xff), v3);
+ setcolor = ppscc->setrgbcolor;
+ break;
+ case 1:
+ g:
+ pprintg1(s, "%g", v3);
+ setcolor = ppscc->setgray;
+ break;
+ default: /* can't happen */
+ return_error(gs_error_rangecheck);
+ }
+ if (setcolor)
+ pprints1(s, " %s\n", setcolor);
+ }
+ return 0;
+}
+
+/* ---------------- Binary data writing ---------------- */
+
+/* Begin writing binary data. */
+int
+psdf_begin_binary(gx_device_psdf * pdev, psdf_binary_writer * pbw)
+{
+ gs_memory_t *mem = pbw->memory = pdev->v_memory;
+
+ pbw->target = pdev->strm;
+ pbw->dev = pdev;
+ pbw->strm = 0; /* for GC in case of failure */
+ /* If not binary, set up the encoding stream. */
+ if (!pdev->binary_ok) {
+#define BUF_SIZE 100 /* arbitrary */
+ byte *buf = gs_alloc_bytes(mem, BUF_SIZE, "psdf_begin_binary(buf)");
+ stream_A85E_state *ss = (stream_A85E_state *)
+ s_alloc_state(mem, s_A85E_template.stype,
+ "psdf_begin_binary(stream_state)");
+ stream *s = s_alloc(mem, "psdf_begin_binary(stream)");
+
+ if (buf == 0 || ss == 0 || s == 0) {
+ gs_free_object(mem, s, "psdf_begin_binary(stream)");
+ gs_free_object(mem, ss, "psdf_begin_binary(stream_state)");
+ gs_free_object(mem, buf, "psdf_begin_binary(buf)");
+ return_error(gs_error_VMerror);
+ }
+ ss->templat = &s_A85E_template;
+ s_init_filter(s, (stream_state *)ss, buf, BUF_SIZE, pdev->strm);
+#undef BUF_SIZE
+ pbw->strm = s;
+ } else {
+ pbw->strm = pdev->strm;
+ }
+ return 0;
+}
+
+/* Add an encoding filter. The client must have allocated the stream state, */
+/* if any, using pdev->v_memory. */
+int
+psdf_encode_binary(psdf_binary_writer * pbw, const stream_template * templat,
+ stream_state * ss)
+{
+ return (s_add_filter(&pbw->strm, templat, ss, pbw->memory) == 0 ?
+ gs_note_error(gs_error_VMerror) : 0);
+}
+
+/*
+ * Acquire parameters, and optionally set up the filter for, a DCTEncode
+ * filter. This is a separate procedure so it can be used to validate
+ * filter parameters when they are set, rather than waiting until they are
+ * used. pbw = NULL means just set up the stream state.
+ */
+int
+psdf_DCT_filter(gs_param_list *plist /* may be NULL */,
+ stream_state /*stream_DCTE_state*/ *st,
+ int Columns, int Rows, int Colors,
+ psdf_binary_writer *pbw /* may be NULL */)
+{
+ stream_DCT_state *const ss = (stream_DCT_state *) st;
+ gs_memory_t *mem = st->memory;
+ jpeg_compress_data *jcdp;
+ gs_c_param_list rcc_list;
+ int code;
+
+ /*
+ * "Wrap" the actual Dict or ACSDict parameter list in one that
+ * sets Rows, Columns, and Colors.
+ */
+ gs_c_param_list_write(&rcc_list, mem);
+ if ((code = param_write_int((gs_param_list *)&rcc_list, "Rows",
+ &Rows)) < 0 ||
+ (code = param_write_int((gs_param_list *)&rcc_list, "Columns",
+ &Columns)) < 0 ||
+ (code = param_write_int((gs_param_list *)&rcc_list, "Colors",
+ &Colors)) < 0
+ ) {
+ goto rcc_fail;
+ }
+ gs_c_param_list_read(&rcc_list);
+ if (plist)
+ gs_c_param_list_set_target(&rcc_list, plist);
+ /* Allocate space for IJG parameters. */
+ jcdp = gs_alloc_struct_immovable(mem, jpeg_compress_data,
+ &st_jpeg_compress_data, "zDCTE");
+ if (jcdp == 0)
+ return_error(gs_error_VMerror);
+ ss->data.compress = jcdp;
+ jcdp->memory = ss->jpeg_memory = mem; /* set now for allocation */
+ if ((code = gs_jpeg_create_compress(ss)) < 0)
+ goto dcte_fail; /* correct to do jpeg_destroy here */
+ /* Read parameters from dictionary */
+ code = s_DCTE_put_params((gs_param_list *)&rcc_list, ss);
+ if (code < 0)
+ return code;
+ /* Create the filter. */
+ jcdp->templat = s_DCTE_template;
+ /* Make sure we get at least a full scan line of input. */
+ ss->scan_line_size = jcdp->cinfo.input_components *
+ jcdp->cinfo.image_width;
+ /* Profile not used in pdfwrite output */
+ ss->icc_profile = NULL;
+ jcdp->templat.min_in_size =
+ max(s_DCTE_template.min_in_size, ss->scan_line_size);
+ /* Make sure we can write the user markers in a single go. */
+ jcdp->templat.min_out_size =
+ max(s_DCTE_template.min_out_size, ss->Markers.size);
+ if (pbw)
+ code = psdf_encode_binary(pbw, &jcdp->templat, st);
+ if (code >= 0) {
+ gs_c_param_list_release(&rcc_list);
+ return 0;
+ }
+ dcte_fail:
+ gs_jpeg_destroy(ss);
+ gs_free_object(mem, jcdp, "setup_image_compression");
+ ss->data.compress = NULL; /* Avoid problems with double frees later */
+ rcc_fail:
+ gs_c_param_list_release(&rcc_list);
+ return code;
+}
+
+/* Add a 2-D CCITTFax encoding filter. */
+/* Set EndOfBlock iff the stream is not ASCII85 encoded. */
+int
+psdf_CFE_binary(psdf_binary_writer * pbw, int w, int h, bool invert)
+{
+ gs_memory_t *mem = pbw->memory;
+ const stream_template *templat = &s_CFE_template;
+ stream_CFE_state *st =
+ gs_alloc_struct(mem, stream_CFE_state, templat->stype,
+ "psdf_CFE_binary");
+ int code;
+
+ if (st == 0)
+ return_error(gs_error_VMerror);
+ (*templat->set_defaults) ((stream_state *) st);
+ st->K = -1;
+ st->Columns = w;
+ st->Rows = 0;
+ st->BlackIs1 = !invert;
+ st->EndOfBlock = pbw->strm->state->templat != &s_A85E_template;
+ code = psdf_encode_binary(pbw, templat, (stream_state *) st);
+ if (code < 0)
+ gs_free_object(mem, st, "psdf_CFE_binary");
+ return code;
+}
+
+/* Finish writing binary data. */
+int
+psdf_end_binary(psdf_binary_writer * pbw)
+{
+ int status = s_close_filters(&pbw->strm, pbw->target);
+
+ return (status >= 0 ? 0 : gs_note_error(gs_error_ioerror));
+}
+
+/* ---------------- Overprint, Get Bits ---------------- */
+
+/*
+ * High level devices cannot perform get_bits or get_bits_rectangle
+ * operations, for obvious reasons.
+ */
+int
+psdf_get_bits(gx_device * dev, int y, byte * data, byte ** actual_data)
+{
+ if (dev_proc(dev, get_alpha_bits)(dev, go_graphics) > 1)
+ emprintf1(dev->memory,
+ "Can't set GraphicsAlphaBits > 1 with a vector device %s.\n",
+ dev->dname);
+ return_error(gs_error_unregistered);
+}
+
+int
+psdf_get_bits_rectangle(
+ gx_device * dev,
+ const gs_int_rect * prect,
+ gs_get_bits_params_t * params,
+ gs_int_rect ** unread )
+{
+ return_error(gs_error_unregistered);
+}
+
+/*
+ * Create compositor procedure for PostScript/PDF writer. Since these
+ * devices directly support overprint (and have access to the imager
+ * state), no compositor is required for overprint support. Hence, this
+ * routine just recognizes and discards invocations of the overprint
+ * compositor.
+ */
+int
+psdf_create_compositor(
+ gx_device * dev,
+ gx_device ** pcdev,
+ const gs_composite_t * pct,
+ gs_imager_state * pis,
+ gs_memory_t * mem,
+ gx_device * cdev)
+{
+ if (gs_is_overprint_compositor(pct)) {
+ *pcdev = dev;
+ return 0;
+ } else {
+ if (dev->parent)
+ return gx_default_create_compositor(dev->parent, pcdev, pct, pis, mem, cdev);
+ else
+ return gx_default_create_compositor(dev, pcdev, pct, pis, mem, cdev);
+ }
+}
diff --git a/devices/vector/gdevpsf.h b/devices/vector/gdevpsf.h
new file mode 100644
index 000000000..edacfa8a1
--- /dev/null
+++ b/devices/vector/gdevpsf.h
@@ -0,0 +1,309 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* PostScript/PDF font writing interface */
+
+#ifndef gdevpsf_INCLUDED
+# define gdevpsf_INCLUDED
+
+#include "gsccode.h"
+#include "gsgdata.h"
+
+/* ---------------- Embedded font writing ---------------- */
+
+#ifndef gs_font_DEFINED
+# define gs_font_DEFINED
+typedef struct gs_font_s gs_font;
+#endif
+#ifndef gs_font_base_DEFINED
+# define gs_font_base_DEFINED
+typedef struct gs_font_base_s gs_font_base;
+#endif
+#ifndef stream_DEFINED
+# define stream_DEFINED
+typedef struct stream_s stream;
+#endif
+
+/*
+ * Define the structure used for enumerating the glyphs in a font or a
+ * font subset. This type is opaque to clients: we declare it here only
+ * so that clients can allocate it on the stack.
+ */
+typedef struct psf_glyph_enum_s psf_glyph_enum_t;
+struct psf_glyph_enum_s {
+ gs_font *font;
+ struct su_ {
+ union sus_ {
+ const gs_glyph *list; /* if subset given by a list */
+ const byte *bits; /* if CID or TT subset given by a bitmap */
+ } selected;
+ uint size;
+ } subset;
+ gs_glyph_space_t glyph_space;
+ ulong index;
+ int (*enumerate_next)(psf_glyph_enum_t *, gs_glyph *);
+};
+
+/*
+ * Begin enumerating the glyphs in a font or a font subset. If subset_size
+ * > 0 but subset_glyphs == 0, enumerate all glyphs in [0 .. subset_size-1]
+ * (as integer glyphs, i.e., offset by gs_min_cid_glyph).
+ */
+void psf_enumerate_list_begin(psf_glyph_enum_t *ppge, gs_font *font,
+ const gs_glyph *subset_list,
+ uint subset_size,
+ gs_glyph_space_t glyph_space);
+/* Backward compatibility */
+#define psf_enumerate_glyphs_begin psf_enumerate_list_begin
+
+/* Begin enumerating CID or TT glyphs in a subset given by a bit vector. */
+/* Note that subset_size is given in bits, not in bytes. */
+void psf_enumerate_bits_begin(psf_glyph_enum_t *ppge, gs_font *font,
+ const byte *subset_bits, uint subset_size,
+ gs_glyph_space_t glyph_space);
+/* Backward compatibility */
+#define psf_enumerate_cids_begin(ppge, font, bits, size)\
+ psf_enumerate_bits_begin(ppge, font, bits, size, GLYPH_SPACE_NAME)
+
+/*
+ * Reset a glyph enumeration.
+ */
+void psf_enumerate_glyphs_reset(psf_glyph_enum_t *ppge);
+
+/*
+ * Enumerate the next glyph in a font or a font subset.
+ * Return 0 if more glyphs, 1 if done, <0 if error.
+ */
+int psf_enumerate_glyphs_next(psf_glyph_enum_t *ppge, gs_glyph *pglyph);
+
+/*
+ * Add composite glyph pieces to a list of glyphs. Does not sort or
+ * remove duplicates. max_pieces is the maximum number of pieces that a
+ * single glyph can have: if this value is not known, the caller should
+ * use max_count.
+ */
+int psf_add_subset_pieces(gs_glyph *glyphs, uint *pcount, uint max_count,
+ uint max_pieces, gs_font *font);
+
+/*
+ * Sort a list of glyphs and remove duplicates. Return the number of glyphs
+ * in the result.
+ */
+int psf_sort_glyphs(gs_glyph *glyphs, int count);
+
+/*
+ * Return the index of a given glyph in a sorted list of glyphs, or -1
+ * if the glyph is not present.
+ */
+int psf_sorted_glyphs_index_of(const gs_glyph *glyphs, int count,
+ gs_glyph glyph);
+/*
+ * Determine whether a sorted list of glyphs includes a given glyph.
+ */
+bool psf_sorted_glyphs_include(const gs_glyph *glyphs, int count,
+ gs_glyph glyph);
+
+/*
+ * Define the internal structure that holds glyph information for an
+ * outline-based font to be written. Currently this only applies to
+ * Type 1, Type 2, and CIDFontType 0 fonts, but someday it might also
+ * be usable with TrueType (Type 42) and CIDFontType 2 fonts.
+ */
+#define MAX_CFF_MISC_STRINGS 40
+#define MAX_CFF_STD_STRINGS 500 /* 391 entries used */
+typedef struct psf_outline_glyphs_s {
+ gs_glyph notdef;
+ /* gs_glyph subset_data[256 * 3 + 1]; *3 for seac, +1 for .notdef */
+ gs_glyph *subset_data;
+ gs_glyph *subset_glyphs; /* 0 or subset_data */
+ uint subset_size;
+} psf_outline_glyphs_t;
+
+#ifndef gs_font_type1_DEFINED
+# define gs_font_type1_DEFINED
+typedef struct gs_font_type1_s gs_font_type1;
+#endif
+
+/* Define the type for the glyph data callback procedure. */
+typedef int (*glyph_data_proc_t)(gs_font_base *, gs_glyph,
+ gs_glyph_data_t *, gs_font_type1 **);
+
+/* Check that all selected glyphs can be written. */
+int psf_check_outline_glyphs(gs_font_base *pfont,
+ psf_glyph_enum_t *ppge,
+ glyph_data_proc_t glyph_data);
+
+/*
+ * Gather glyph information for a Type 1, Type 2, or CIDFontType 0 font.
+ * Note that the glyph_data procedure returns both the outline string and
+ * a gs_font_type1 (Type 1 or Type 2) font: for Type 1 or Type 2 fonts,
+ * this is the original font, but for CIDFontType 0 fonts, it is the
+ * FDArray element. If subset_glyphs != 0, this procedure removes
+ * undefined glyphs from the list it builds.
+ */
+int psf_get_outline_glyphs(psf_outline_glyphs_t *pglyphs,
+ gs_font_base *pfont, gs_glyph *subset_glyphs,
+ uint subset_size, glyph_data_proc_t glyph_data);
+
+/* ------ Exported by gdevpsf1.c ------ */
+
+/* Gather glyph information for a Type 1 or Type 2 font. */
+int psf_type1_glyph_data(gs_font_base *, gs_glyph, gs_glyph_data_t *,
+ gs_font_type1 **);
+int psf_get_type1_glyphs(psf_outline_glyphs_t *pglyphs,
+ gs_font_type1 *pfont,
+ gs_glyph *subset_glyphs, uint subset_size);
+
+/*
+ * Write a Type 1 font definition. This procedure does not allocate
+ * or free any data.
+ */
+#define WRITE_TYPE1_EEXEC 1
+#define WRITE_TYPE1_ASCIIHEX 2 /* use ASCII hex rather than binary */
+#define WRITE_TYPE1_EEXEC_PAD 4 /* add 512 0s */
+#define WRITE_TYPE1_EEXEC_MARK 8 /* assume 512 0s will be added */
+#define WRITE_TYPE1_POSTSCRIPT 16 /* don't observe ATM restrictions */
+#define WRITE_TYPE1_WITH_LENIV 32 /* don't allow lenIV = -1 */
+int psf_write_type1_font(stream *s, gs_font_type1 *pfont, int options,
+ gs_glyph *subset_glyphs, uint subset_size,
+ const gs_const_string *alt_font_name,
+ int lengths[3]);
+
+/* ------ Exported by gdevpsf2.c ------ */
+
+/*
+ * Write a Type 1 or Type 2 font definition as CFF.
+ * This procedure does not allocate or free any data.
+ */
+#define WRITE_TYPE2_NO_LENIV 1 /* always use lenIV = -1 */
+#define WRITE_TYPE2_CHARSTRINGS 2 /* convert T1 charstrings to T2 */
+#define WRITE_TYPE2_AR3 4 /* work around bugs in Acrobat Reader 3 */
+#define WRITE_TYPE2_NO_GSUBRS 8 /* omit GlobalSubrs */
+int psf_write_type2_font(stream *s, gs_font_type1 *pfont, int options,
+ gs_glyph *subset_glyphs, uint subset_size,
+ const gs_const_string *alt_font_name,
+ gs_int_rect *FontBBox);
+
+#ifndef gs_font_cid0_DEFINED
+# define gs_font_cid0_DEFINED
+typedef struct gs_font_cid0_s gs_font_cid0;
+#endif
+
+/*
+ * Write a CIDFontType 0 font definition as CFF. The options are
+ * the same as for psf_write_type2_font. subset_cids is a bit vector of
+ * subset_size bits (not bytes).
+ * This procedure does not allocate or free any data.
+ */
+int psf_write_cid0_font(stream *s, gs_font_cid0 *pfont, int options,
+ const byte *subset_cids, uint subset_size,
+ const gs_const_string *alt_font_name);
+
+/* ------ Exported by gdevpsfm.c ------ */
+
+/*
+ * Write a CMap in its customary (source) form.
+ * This procedure does not allocate or free any data.
+ */
+#ifndef gs_cmap_DEFINED
+# define gs_cmap_DEFINED
+typedef struct gs_cmap_s gs_cmap_t;
+#endif
+typedef int (*psf_put_name_chars_proc_t)(stream *, const byte *, uint);
+int psf_write_cmap(const gs_memory_t *mem, stream *s, const gs_cmap_t *pcmap,
+ psf_put_name_chars_proc_t put_name_chars,
+ const gs_const_string *alt_cmap_name, int font_index_only);
+/* ------ Exported by gdevpsft.c ------ */
+
+extern const long default_defaultWidthX;
+
+/*
+ * Write a TrueType (Type 42) font definition.
+ * This procedure does not allocate or free any data.
+ */
+#ifndef gs_font_type42_DEFINED
+# define gs_font_type42_DEFINED
+typedef struct gs_font_type42_s gs_font_type42;
+#endif
+#define WRITE_TRUETYPE_CMAP 1 /* generate cmap from the Encoding */
+#define WRITE_TRUETYPE_NAME 2 /* generate name if missing */
+#define WRITE_TRUETYPE_POST 4 /* generate post if missing */
+#define WRITE_TRUETYPE_NO_TRIMMED_TABLE 8 /* not OK to use cmap format 6 */
+#define WRITE_TRUETYPE_HVMTX 16 /* generate [hv]mtx from glyph_info */
+#define WRITE_TRUETYPE_UNICODE_CMAP 32 /* For PDF/A or other non-symbolic TT font,
+ * write a 3,1 (Windows Unicode) cmap instead of
+ * a 3,0 one.
+ */
+int psf_write_truetype_font(stream *s, gs_font_type42 *pfont, int options,
+ gs_glyph *subset_glyphs, uint subset_size,
+ const gs_const_string *alt_font_name);
+
+/*
+ * Write a "stripped" TrueType font definition. All tables are written
+ * verbatim, except for deleting the bogus Adobe marker "tables" gdir, glyx,
+ * and locx, and also deleting glyf and loca.
+ * This procedure does not allocate or free any data.
+ *
+ * The purpose of "stripped" fonts is simply to store all of the non-glyph
+ * information from a TrueType-family font in a structure that can be easily
+ * stored, accessed, and eventually combined with the glyph information.
+ * The only intended client for this function is the font copying code in
+ * gxfcopy.c, q.v. In particular, "stripped" fonts are not fully valid
+ * fonts because they lack glyph 0 (the .notdef glyph).
+ */
+int psf_write_truetype_stripped(stream *s, gs_font_type42 *pfont);
+
+#ifndef gs_font_cid2_DEFINED
+# define gs_font_cid2_DEFINED
+typedef struct gs_font_cid2_s gs_font_cid2;
+#endif
+
+/*
+ * Write a CIDFontType 2 font definition. This differs from
+ * psf_write_truetype_font in that the subset, if any, is specified
+ * as a bit vector (as for psf_write_cid0_font) rather than a list of glyphs.
+ * Also, none of the options currently have any effect. The only tables
+ * written are:
+ * - The "required" tables: head, hhea, loca, maxp, cvt_, prep, glyf,
+ * hmtx, fpgm.
+ * - If present in the font: post, gasp, kern, vhea, vmtx.
+ * Note that in particular, the cmap, name, and OS/2 tables are omitted.
+ * NOTE: it is the client's responsibility to ensure that if the subset
+ * contains any composite glyphs, the components of the composites are
+ * included explicitly in the subset.
+ * This procedure does not allocate or free any data.
+ */
+int psf_write_cid2_font(stream *s, gs_font_cid2 *pfont, int options,
+ const byte *subset_glyphs, uint subset_size,
+ const gs_const_string *alt_font_name);
+
+/*
+ * Write a "stripped" CIDFontType 2 font definition. This is the same
+ * as an ordinary CIDFontType 2 definition, minus glyf and loca.
+ */
+int psf_write_cid2_stripped(stream *s, gs_font_cid2 *pfont);
+
+/* ------ Exported by gdevpsfx.c ------ */
+
+/*
+ * Convert a Type 1 CharString to (unencrypted) Type 2.
+ * This procedure does not allocate or free any data.
+ * NOTE: this procedure expands all Subrs in-line.
+ */
+int psf_convert_type1_to_type2(stream *s, const gs_glyph_data_t *pgd,
+ gs_font_type1 *pfont);
+
+#endif /* gdevpsf_INCLUDED */
diff --git a/devices/vector/gdevpsf1.c b/devices/vector/gdevpsf1.c
new file mode 100644
index 000000000..370686440
--- /dev/null
+++ b/devices/vector/gdevpsf1.c
@@ -0,0 +1,952 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Write an embedded Type 1 font */
+#include "math.h"
+#include "memory_.h"
+#include "gx.h"
+#include "gserrors.h"
+#include "gsccode.h"
+#include "gsmatrix.h"
+#include "gxfixed.h"
+#include "gxfont.h"
+#include "gxfont1.h"
+#include "gxmatrix.h" /* for gxtype1.h */
+#include "gxtype1.h"
+#include "strimpl.h" /* required by Watcom compiler (why?) */
+#include "stream.h"
+#include "sfilter.h"
+#include "spsdf.h"
+#include "sstring.h"
+#include "spprint.h"
+#include "gdevpsf.h"
+
+/* ------ Utilities shared with CFF writer ------ */
+
+/* Gather glyph information for a Type 1 or Type 2 font. */
+int
+psf_type1_glyph_data(gs_font_base *pbfont, gs_glyph glyph,
+ gs_glyph_data_t *pgd, gs_font_type1 **ppfont)
+{
+ gs_font_type1 *const pfont = (gs_font_type1 *)pbfont;
+
+ *ppfont = pfont;
+ return pfont->data.procs.glyph_data(pfont, glyph, pgd);
+}
+int
+psf_get_type1_glyphs(psf_outline_glyphs_t *pglyphs, gs_font_type1 *pfont,
+ gs_glyph *subset_glyphs, uint subset_size)
+{
+ return psf_get_outline_glyphs(pglyphs, (gs_font_base *)pfont,
+ subset_glyphs, subset_size,
+ psf_type1_glyph_data);
+}
+
+/* ------ Main program ------ */
+
+/* Write a (named) array of floats. */
+static int
+write_float_array(gs_param_list *plist, const char *key, const float *values,
+ int count)
+{
+ if (count != 0) {
+ gs_param_float_array fa;
+
+ fa.persistent = false;
+ fa.size = count;
+ fa.data = values;
+ return param_write_float_array(plist, key, &fa);
+ }
+ return 0;
+}
+
+/* Write a UniqueID and/or XUID. */
+static void
+write_uid(stream *s, const gs_uid *puid)
+{
+ if (uid_is_UniqueID(puid))
+ pprintld1(s, "/UniqueID %ld def\n", puid->id);
+ else if (uid_is_XUID(puid)) {
+ uint i, n = uid_XUID_size(puid);
+
+ stream_puts(s, "/XUID [");
+ for (i = 0; i < n; ++i)
+ pprintld1(s, "%ld ", uid_XUID_values(puid)[i]);
+ stream_puts(s, "] readonly def\n");
+ }
+}
+
+/* Write the font name. */
+static void
+write_font_name(stream *s, const gs_font_type1 *pfont,
+ const gs_const_string *alt_font_name, bool as_name)
+{
+ const byte *c;
+ const byte *name = (alt_font_name ? alt_font_name->data : pfont->font_name.chars);
+ int n = (alt_font_name ? alt_font_name->size : pfont->font_name.size);
+
+ if (n == 0)
+ /* empty name, may need to write it as empty string */
+ stream_puts(s, (as_name ? "/" : "()"));
+ else {
+ for (c = (byte *)"()<>[]{}/% \n\r\t\b\f\004\033"; *c; c++)
+ if (memchr(name, *c, n))
+ break;
+ if (*c || memchr(name, 0, n)) {
+ /* name contains whitespace (NUL included) or a PostScript separator */
+ byte pssebuf[1 + 4 * gs_font_name_max + 1]; /* "(" + "\ooo" * gs_font_name_max + ")" */
+ stream_cursor_read r;
+ stream_cursor_write w;
+
+ pssebuf[0] = '(';
+ r.limit = (r.ptr = name - 1) + n;
+ w.limit = (w.ptr = pssebuf) + sizeof pssebuf - 1;
+ s_PSSE_template.process(NULL, &r, &w, true);
+ stream_write(s, pssebuf, w.ptr - pssebuf + 1);
+ if (as_name)
+ stream_puts(s, " cvn");
+ } else {
+ /* name without any special characters */
+ if (as_name)
+ stream_putc(s, '/');
+ stream_write(s, name, n);
+ }
+ }
+}
+/*
+ * Write the Encoding array. This is a separate procedure only for
+ * readability.
+ */
+static int
+write_Encoding(stream *s, gs_font_type1 *pfont, int options,
+ gs_glyph *subset_glyphs, uint subset_size, gs_glyph notdef)
+{
+ stream_puts(s, "/Encoding ");
+ switch (pfont->encoding_index) {
+ case ENCODING_INDEX_STANDARD:
+ stream_puts(s, "StandardEncoding");
+ break;
+ case ENCODING_INDEX_ISOLATIN1:
+ /* ATM only recognizes StandardEncoding. */
+ if (options & WRITE_TYPE1_POSTSCRIPT) {
+ stream_puts(s, "ISOLatin1Encoding");
+ break;
+ }
+ default:{
+ gs_char i;
+
+ stream_puts(s, "256 array\n");
+ stream_puts(s, "0 1 255 {1 index exch /.notdef put} for\n");
+ for (i = 0; i < 256; ++i) {
+ gs_glyph glyph =
+ (*pfont->procs.encode_char)
+ ((gs_font *)pfont, (gs_char)i, GLYPH_SPACE_NAME);
+ gs_const_string namestr;
+
+ if (subset_glyphs && subset_size) {
+ /*
+ * Only write Encoding entries for glyphs in the
+ * subset. Use binary search to check each glyph,
+ * since subset_glyphs are sorted.
+ */
+ if (!psf_sorted_glyphs_include(subset_glyphs,
+ subset_size, glyph))
+ continue;
+ }
+ if (glyph != gs_no_glyph && glyph != notdef &&
+ pfont->procs.glyph_name((gs_font *)pfont, glyph,
+ &namestr) >= 0
+ ) {
+ pprintd1(s, "dup %d /", (int)i);
+ stream_write(s, namestr.data, namestr.size);
+ stream_puts(s, " put\n");
+ }
+ }
+ stream_puts(s, "readonly");
+ }
+ }
+ stream_puts(s, " def\n");
+ return 0;
+}
+
+static int WriteNumber (byte *dest, int value)
+{
+ if (value >= -107 && value <= 107) {
+ *dest = value + 139;
+ return 1;
+ } else {
+ if (value >= 108 && value <= 1131) {
+ int quotient = (int)floor((value - 108) / (double)256);
+ dest[0] = quotient + 247;
+ dest[1] = value - 108 - quotient * 256;
+ return 2;
+ } else {
+ if (value <= -108 && value >= -1131) {
+ int quotient = (int)floor((value + 108) / -256);
+ int newval = value + 256 * quotient + 108;
+ dest[0] = quotient + 251;
+ dest[1] = newval * -1;
+ return 2;
+ } else {
+ dest[0] = 255;
+ dest[1] = value >> 24;
+ dest[2] = (value & 0xFF0000) >> 16;
+ dest[3] = (value & 0xFF00) >> 8;
+ dest[4] = value & 0xFF;
+ return 5;
+ }
+ }
+ }
+ return 0;
+}
+
+/* The following 2 routines attempt to parse out Multiple Master 'OtherSubrs'
+ * calls, and replace the multiple arguments to $Blend with the two 'base'
+ * parameters. This works reasonably well but can be defeated. FOr example a
+ * CharString which puts some parameters on the operand stack, then calls a
+ * Subr which puts the remaining parameters on the stack, and calls a MM
+ * OtherSubr (constructions like this have been observed). In general we
+ * work around this by storing the operands on the stack, but it is possible
+ * that the values are calculated (eg x y div) which is a common way to get
+ * float values into the interpreter. This will defeat the code below.
+ *
+ * The only way to solve this is to actually fully interpret the CharString
+ * and any /Subrs it calls, and then emit the result as a non-MM CharString
+ * by blending the values. This would mean writing a new routine like
+ * 'psf_convert_type1_to_type2' (see gdevpsfx.c) or modifying that routine
+ * so that it outputs type 1 CharStrings (which is probably simpler to do).
+ */
+static int CheckSubrForMM (gs_glyph_data_t *gdata, gs_font_type1 *pfont)
+{
+ crypt_state state = crypt_charstring_seed;
+ int code = 0;
+ gs_bytestring *data = (gs_bytestring *)&gdata->bits;
+ byte *source = data->data, *end = source + data->size;
+ int CurrentNumberIndex = 0, Stack[32];
+
+ memset(Stack, 0x00, sizeof(Stack));
+ gs_type1_decrypt(source, source, data->size, &state);
+
+ if(pfont->data.lenIV)
+ source += pfont->data.lenIV;
+
+ while (source < end) {
+ if (*source < 32) {
+ /* Command */
+ switch (*source) {
+ case 12:
+ if (*(source + 1) == 16) {
+ if (CurrentNumberIndex < 1)
+ return gs_error_rangecheck;
+ switch(Stack[CurrentNumberIndex-1]) {
+ case 18:
+ code = 6;
+ break;
+ case 17:
+ code = 4;
+ break;
+ case 16:
+ code = 3;
+ break;
+ case 15:
+ code = 2;
+ break;
+ case 14:
+ code = 1;
+ break;
+ default:
+ code = 0;
+ break;
+ }
+ source += 2;
+ } else {
+ source +=2;
+ }
+ break;
+ default:
+ source++;
+ break;
+ }
+ CurrentNumberIndex = 0;
+ } else {
+ /* Number */
+ if (*source < 247) {
+ Stack[CurrentNumberIndex++] = *source++ - 139;
+ } else {
+ if (*source < 251) {
+ Stack[CurrentNumberIndex] = ((*source++ - 247) * 256) + 108;
+ Stack[CurrentNumberIndex++] += *source++;
+ } else {
+ if (*source < 255) {
+ Stack[CurrentNumberIndex] = ((*source++ - 251) * -256) - 108;
+ Stack[CurrentNumberIndex++] -= *source++;
+ } else {
+ Stack[CurrentNumberIndex] = *source++ << 24;
+ Stack[CurrentNumberIndex] += *source++ << 16;
+ Stack[CurrentNumberIndex] += *source++ << 8;
+ Stack[CurrentNumberIndex] += *source++;
+ }
+ }
+ }
+ }
+ }
+ state = crypt_charstring_seed;
+ source = data->data;
+ gs_type1_encrypt(source, source, data->size, &state);
+ return code;
+}
+
+static int strip_othersubrs(gs_glyph_data_t *gdata, gs_font_type1 *pfont, byte *stripped, byte *SubrsWithMM)
+{
+ crypt_state state = crypt_charstring_seed;
+ gs_bytestring *data = (gs_bytestring *)&gdata->bits;
+ byte *source = data->data, *dest = stripped, *end = source + data->size;
+ int i, dest_length = 0, CurrentNumberIndex = 0, Stack[64], written;
+ int OnlyCalcLength = 0;
+ char Buffer[16];
+
+ if (stripped == NULL) {
+ OnlyCalcLength = 1;
+ dest = (byte *)&Buffer;
+ }
+
+ gs_type1_decrypt(source, source, data->size, &state);
+
+ if(pfont->data.lenIV >= 0) {
+ for (i=0;i<pfont->data.lenIV;i++) {
+ if (!OnlyCalcLength)
+ *dest++ = *source++;
+ }
+ dest_length += pfont->data.lenIV;
+ }
+ while (source < end) {
+ if (*source < 32) {
+ /* Command */
+ switch (*source) {
+ case 12:
+ if (*(source + 1) == 16) {
+ /* Callothersubsr, the only thing we care about */
+ switch(Stack[CurrentNumberIndex-1]) {
+ /* If we find a Multiple Master call, remove all but the
+ * first set of arguments. Mimics the result of a call.
+ * Adobe 'encourages' the use of Subrs to do MM, but
+ * the spec doens't say you have to, so we need to be
+ * prepared, just in case. I doubt we will ever execute
+ * this code.
+ */
+ case 14:
+ CurrentNumberIndex -= pfont->data.WeightVector.count - 1;
+ for (i = 0;i < CurrentNumberIndex;i++) {
+ written = WriteNumber(dest, Stack[i]);
+ dest_length += written;
+ if (!OnlyCalcLength)
+ dest += written;
+ }
+ source += 2;
+ break;
+ case 15:
+ CurrentNumberIndex -= (pfont->data.WeightVector.count - 1) * 2;
+ for (i = 0;i < CurrentNumberIndex;i++) {
+ written = WriteNumber(dest, Stack[i]);
+ dest_length += written;
+ if (!OnlyCalcLength)
+ dest += written;
+ }
+ source += 2;
+ break;
+ case 16:
+ CurrentNumberIndex -= (pfont->data.WeightVector.count - 1) * 3;
+ for (i = 0;i < CurrentNumberIndex;i++) {
+ written = WriteNumber(dest, Stack[i]);
+ dest_length += written;
+ if (!OnlyCalcLength)
+ dest += written;
+ }
+ source += 2;
+ break;
+ case 17:
+ CurrentNumberIndex -= (pfont->data.WeightVector.count - 1) * 4;
+ for (i = 0;i < CurrentNumberIndex;i++) {
+ written = WriteNumber(dest, Stack[i]);
+ dest_length += written;
+ if (!OnlyCalcLength)
+ dest += written;
+ }
+ source += 2;
+ break;
+ case 18:
+ CurrentNumberIndex -= (pfont->data.WeightVector.count - 1) * 6;
+ for (i = 0;i < CurrentNumberIndex;i++) {
+ written = WriteNumber(dest, Stack[i]);
+ dest_length += written;
+ if (!OnlyCalcLength)
+ dest += written;
+ }
+ source += 2;
+ break;
+ default:
+ for (i = 0;i < CurrentNumberIndex;i++) {
+ written = WriteNumber(dest, Stack[i]);
+ dest_length += written;
+ if (!OnlyCalcLength)
+ dest += written;
+ }
+ if (!OnlyCalcLength) {
+ *dest++ = *source++;
+ *dest++ = *source++;
+ } else {
+ source += 2;
+ }
+ dest_length += 2;
+ break;
+ }
+ } else {
+ for (i = 0;i < CurrentNumberIndex;i++) {
+ written = WriteNumber(dest, Stack[i]);
+ dest_length += written;
+ if (!OnlyCalcLength)
+ dest += written;
+ }
+ if (!OnlyCalcLength) {
+ *dest++ = *source++;
+ *dest++ = *source++;
+ } else {
+ source += 2;
+ }
+ dest_length += 2;
+ }
+ break;
+ case 10:
+ if (CurrentNumberIndex != 0 && SubrsWithMM[Stack[CurrentNumberIndex - 1]] != 0) {
+ int index = Stack[CurrentNumberIndex - 1];
+ int StackBase = CurrentNumberIndex - 1 - pfont->data.WeightVector.count * SubrsWithMM[index];
+
+ CurrentNumberIndex--; /* Remove the subr index */
+
+ for (i=0;i < StackBase; i++) {
+ written = WriteNumber(dest, Stack[i]);
+ dest_length += written;
+ if (!OnlyCalcLength)
+ dest += written;
+ }
+ for (i=0;i<SubrsWithMM[index];i++) {
+ written = WriteNumber(dest, Stack[StackBase + i]);
+ dest_length += written;
+ if (!OnlyCalcLength)
+ dest += written;
+ }
+ source++;
+ } else {
+ for (i = 0;i < CurrentNumberIndex;i++) {
+ written = WriteNumber(dest, Stack[i]);
+ dest_length += written;
+ if (!OnlyCalcLength)
+ dest += written;
+ }
+ if (!OnlyCalcLength)
+ *dest++ = *source++;
+ else
+ source++;
+ dest_length++;
+ }
+ break;
+ default:
+ for (i = 0;i < CurrentNumberIndex;i++) {
+ written = WriteNumber(dest, Stack[i]);
+ dest_length += written;
+ if (!OnlyCalcLength)
+ dest += written;
+ }
+ if (!OnlyCalcLength)
+ *dest++ = *source++;
+ else
+ source++;
+ dest_length++;
+ }
+ CurrentNumberIndex = 0;
+ } else {
+ /* Number */
+ if (*source < 247) {
+ Stack[CurrentNumberIndex++] = *source++ - 139;
+ } else {
+ if (*source < 251) {
+ Stack[CurrentNumberIndex] = ((*source++ - 247) * 256) + 108;
+ Stack[CurrentNumberIndex++] += *source++;
+ } else {
+ if (*source < 255) {
+ Stack[CurrentNumberIndex] = ((*source++ - 251) * -256) - 108;
+ Stack[CurrentNumberIndex++] -= *source++;
+ } else {
+ source++;
+ Stack[CurrentNumberIndex] = *source++ << 24;
+ Stack[CurrentNumberIndex] += *source++ << 16;
+ Stack[CurrentNumberIndex] += *source++ << 8;
+ Stack[CurrentNumberIndex++] += *source++;
+ }
+ }
+ }
+ }
+ }
+ source = data->data;
+ state = crypt_charstring_seed;
+ gs_type1_encrypt(source, source, data->size, &state);
+
+ if (!OnlyCalcLength) {
+ state = crypt_charstring_seed;
+ gs_type1_encrypt(stripped, stripped, dest_length, &state);
+ }
+ return dest_length;
+}
+
+/*
+ * Write the Private dictionary. This is a separate procedure only for
+ * readability. write_CharString is a parameter so that we can encrypt
+ * Subrs and CharStrings when the font's lenIV == -1 but we are writing
+ * the font with lenIV = 0.
+ */
+static int
+write_Private(stream *s, gs_font_type1 *pfont,
+ gs_glyph *subset_glyphs, uint subset_size,
+ gs_glyph notdef, int lenIV,
+ int (*write_CharString)(stream *, const void *, uint),
+ const param_printer_params_t *ppp)
+{
+ const gs_type1_data *const pdata = &pfont->data;
+ printer_param_list_t rlist;
+ gs_param_list *const plist = (gs_param_list *)&rlist;
+ int code = s_init_param_printer(&rlist, ppp, s);
+ byte *SubrsWithMM = 0;
+
+ if (code < 0)
+ return 0;
+ stream_puts(s, "dup /Private 17 dict dup begin\n");
+ stream_puts(s, "/-|{string currentfile exch readstring pop}executeonly def\n");
+ stream_puts(s, "/|-{noaccess def}executeonly def\n");
+ stream_puts(s, "/|{noaccess put}executeonly def\n");
+ {
+ static const gs_param_item_t private_items[] = {
+ {"BlueFuzz", gs_param_type_int,
+ offset_of(gs_type1_data, BlueFuzz)},
+ {"BlueScale", gs_param_type_float,
+ offset_of(gs_type1_data, BlueScale)},
+ {"BlueShift", gs_param_type_float,
+ offset_of(gs_type1_data, BlueShift)},
+ {"ExpansionFactor", gs_param_type_float,
+ offset_of(gs_type1_data, ExpansionFactor)},
+ {"ForceBold", gs_param_type_bool,
+ offset_of(gs_type1_data, ForceBold)},
+ {"LanguageGroup", gs_param_type_int,
+ offset_of(gs_type1_data, LanguageGroup)},
+ {"RndStemUp", gs_param_type_bool,
+ offset_of(gs_type1_data, RndStemUp)},
+ gs_param_item_end
+ };
+ gs_type1_data defaults;
+
+ defaults.BlueFuzz = 1;
+ defaults.BlueScale = (float)0.039625;
+ defaults.BlueShift = 7.0;
+ defaults.ExpansionFactor = (float)0.06;
+ defaults.ForceBold = false;
+ defaults.LanguageGroup = 0;
+ defaults.RndStemUp = true;
+ code = gs_param_write_items(plist, pdata, &defaults, private_items);
+ if (code < 0)
+ return code;
+ if (lenIV != 4) {
+ code = param_write_int(plist, "lenIV", &lenIV);
+ if (code < 0)
+ return code;
+ }
+ write_float_array(plist, "BlueValues", pdata->BlueValues.values,
+ pdata->BlueValues.count);
+ write_float_array(plist, "OtherBlues", pdata->OtherBlues.values,
+ pdata->OtherBlues.count);
+ write_float_array(plist, "FamilyBlues", pdata->FamilyBlues.values,
+ pdata->FamilyBlues.count);
+ write_float_array(plist, "FamilyOtherBlues", pdata->FamilyOtherBlues.values,
+ pdata->FamilyOtherBlues.count);
+ write_float_array(plist, "StdHW", pdata->StdHW.values,
+ pdata->StdHW.count);
+ write_float_array(plist, "StdVW", pdata->StdVW.values,
+ pdata->StdVW.count);
+ write_float_array(plist, "StemSnapH", pdata->StemSnapH.values,
+ pdata->StemSnapH.count);
+ write_float_array(plist, "StemSnapV", pdata->StemSnapV.values,
+ pdata->StemSnapV.count);
+ }
+ write_uid(s, &pfont->UID);
+ stream_puts(s, "/MinFeature{16 16} def\n");
+ stream_puts(s, "/password 5839 def\n");
+
+ /*
+ * Write the Subrs. We always write them all, even for subsets.
+ * (We will fix this someday.)
+ */
+
+ {
+ int n, i;
+ gs_glyph_data_t gdata;
+ int code;
+
+ gdata.memory = pfont->memory;
+ for (n = 0;
+ (code = pdata->procs.subr_data(pfont, n, false, &gdata)) !=
+ gs_error_rangecheck;
+ ) {
+ ++n;
+ if (code >= 0)
+ gs_glyph_data_free(&gdata, "write_Private(Subrs)");
+ }
+ if (pfont->data.WeightVector.count != 0)
+ SubrsWithMM = gs_alloc_bytes(pfont->memory, n, "Subrs record");
+
+ pprintd1(s, "/Subrs %d array\n", n);
+
+ /* prescan the /Subrs array to see if any of the Subrs call out to OtherSubrs */
+ if (pfont->data.WeightVector.count != 0) {
+ for (i = 0; i < n; ++i) {
+ if ((code = pdata->procs.subr_data(pfont, i, false, &gdata)) >= 0) {
+ code = CheckSubrForMM(&gdata, pfont);
+ if (code < 0) {
+ if (SubrsWithMM != 0)
+ gs_free_object(pfont->memory, SubrsWithMM, "free Subrs record");
+ return code;
+ }
+ if (SubrsWithMM != 0)
+ SubrsWithMM[i] = code;
+ }
+ }
+ }
+
+ for (i = 0; i < n; ++i)
+ if ((code = pdata->procs.subr_data(pfont, i, false, &gdata)) >= 0) {
+ char buf[50];
+
+ if (gdata.bits.size) {
+ if (pfont->data.WeightVector.count != 0) {
+ byte *stripped;
+ int length;
+
+ length = strip_othersubrs(&gdata, pfont, NULL, SubrsWithMM);
+ stripped = gs_alloc_bytes(pfont->memory, length, "Subrs copy for OtherSubrs");
+ code = strip_othersubrs(&gdata, pfont, stripped, SubrsWithMM);
+ if (code < 0) {
+ if (SubrsWithMM != 0)
+ gs_free_object(pfont->memory, SubrsWithMM, "free Subrs record");
+ return code;
+ }
+ gs_sprintf(buf, "dup %d %u -| ", i, code);
+ stream_puts(s, buf);
+ write_CharString(s, stripped, code);
+ gs_free_object(pfont->memory, stripped, "free Subrs copy for OtherSubrs");
+ } else {
+ gs_sprintf(buf, "dup %d %u -| ", i, gdata.bits.size);
+ stream_puts(s, buf);
+ write_CharString(s, gdata.bits.data, gdata.bits.size);
+ }
+ stream_puts(s, " |\n");
+ }
+ gs_glyph_data_free(&gdata, "write_Private(Subrs)");
+ }
+ stream_puts(s, "|-\n");
+ }
+
+ /* We don't write OtherSubrs -- there had better not be any! */
+
+ /* Write the CharStrings. */
+
+ {
+ int num_chars = 0;
+ gs_glyph glyph;
+ psf_glyph_enum_t genum;
+ gs_glyph_data_t gdata;
+ int code;
+
+ gdata.memory = pfont->memory;
+ psf_enumerate_glyphs_begin(&genum, (gs_font *)pfont, subset_glyphs,
+ (subset_glyphs ? subset_size : 0),
+ GLYPH_SPACE_NAME);
+ for (glyph = gs_no_glyph;
+ (code = psf_enumerate_glyphs_next(&genum, &glyph)) != 1;
+ )
+ if (code == 0 &&
+ (code = pdata->procs.glyph_data(pfont, glyph, &gdata)) >= 0
+ ) {
+ ++num_chars;
+ gs_glyph_data_free(&gdata, "write_Private(CharStrings)");
+ }
+ pprintd1(s, "2 index /CharStrings %d dict dup begin\n", num_chars);
+ psf_enumerate_glyphs_reset(&genum);
+ for (glyph = gs_no_glyph;
+ (code = psf_enumerate_glyphs_next(&genum, &glyph)) != 1;
+ )
+ if (code == 0 &&
+ (code = pdata->procs.glyph_data(pfont, glyph, &gdata)) >= 0
+ ) {
+ gs_const_string gstr;
+ int code;
+ byte *stripped;
+
+ code = pfont->procs.glyph_name((gs_font *)pfont, glyph, &gstr);
+ if (code < 0) {
+ if (SubrsWithMM != 0)
+ gs_free_object(pfont->memory, SubrsWithMM, "free Subrs record");
+ return code;
+ }
+
+ stream_puts(s, "/");
+ stream_write(s, gstr.data, gstr.size);
+
+ if (pfont->data.WeightVector.count != 0) {
+ gs_bytestring *data = (gs_bytestring *)&gdata.bits;
+
+ stripped = gs_alloc_bytes(pfont->memory, data->size, "CharStrings copy for OtherSubrs");
+ code = strip_othersubrs(&gdata, pfont, stripped, SubrsWithMM);
+ if (code < 0) {
+ if (SubrsWithMM != 0)
+ gs_free_object(pfont->memory, SubrsWithMM, "free Subrs record");
+ return code;
+ }
+ pprintd1(s, " %d -| ", code);
+ write_CharString(s, stripped, code);
+ gs_free_object(pfont->memory, stripped, "free CharStrings copy for OtherSubrs");
+ } else {
+ pprintd1(s, " %d -| ", gdata.bits.size);
+ write_CharString(s, gdata.bits.data, gdata.bits.size);
+ }
+
+ stream_puts(s, " |-\n");
+ gs_glyph_data_free(&gdata, "write_Private(CharStrings)");
+ }
+ }
+ if (SubrsWithMM != 0)
+ gs_free_object(pfont->memory, SubrsWithMM, "free Subrs record");
+
+ /* Wrap up. */
+
+ stream_puts(s, "end\nend\nreadonly put\nnoaccess put\n");
+ s_release_param_printer(&rlist);
+ return 0;
+}
+
+/* Encrypt and write a CharString. */
+static int
+stream_write_encrypted(stream *s, const void *ptr, uint count)
+{
+ const byte *const data = ptr;
+ crypt_state state = crypt_charstring_seed;
+ byte buf[50]; /* arbitrary */
+ uint left, n;
+ int code = 0;
+
+ for (left = count; left > 0; left -= n) {
+ n = min(left, sizeof(buf));
+ gs_type1_encrypt(buf, data + count - left, n, &state);
+ code = stream_write(s, buf, n);
+ }
+ return code;
+}
+
+/* Write one FontInfo entry. */
+static void
+write_font_info(stream *s, const char *key, const gs_const_string *pvalue,
+ int do_write)
+{
+ if (do_write) {
+ pprints1(s, "\n/%s ", key);
+ s_write_ps_string(s, pvalue->data, pvalue->size, PRINT_HEX_NOT_OK);
+ stream_puts(s, " def");
+ }
+}
+
+/* Write the definition of a Type 1 font. */
+int
+psf_write_type1_font(stream *s, gs_font_type1 *pfont, int options,
+ gs_glyph *orig_subset_glyphs, uint orig_subset_size,
+ const gs_const_string *alt_font_name, int lengths[3])
+{
+ stream *es = s;
+ gs_offset_t start = stell(s);
+ param_printer_params_t ppp;
+ printer_param_list_t rlist;
+ gs_param_list *const plist = (gs_param_list *)&rlist;
+ stream AXE_stream;
+ stream_AXE_state AXE_state;
+ byte AXE_buf[200]; /* arbitrary */
+ stream exE_stream;
+ stream_exE_state exE_state;
+ byte exE_buf[200]; /* arbitrary */
+ psf_outline_glyphs_t glyphs;
+ int lenIV = pfont->data.lenIV;
+ int (*write_CharString)(stream *, const void *, uint) = stream_write;
+ int code = psf_get_type1_glyphs(&glyphs, pfont, orig_subset_glyphs,
+ orig_subset_size);
+
+ if (code < 0)
+ return code;
+
+ /* Initialize the parameter printer. */
+
+ ppp = param_printer_params_default;
+ ppp.item_suffix = " def\n";
+ ppp.print_ok =
+ (options & WRITE_TYPE1_ASCIIHEX ? 0 : PRINT_BINARY_OK) |
+ PRINT_HEX_NOT_OK;
+ code = s_init_param_printer(&rlist, &ppp, s);
+ if (code < 0)
+ return code;
+
+ /* Write the font header. */
+
+ stream_puts(s, "%!FontType1-1.0: ");
+ write_font_name(s, pfont, alt_font_name, false);
+ stream_puts(s, "\n11 dict begin\n");
+
+ /* Write FontInfo. */
+
+ stream_puts(s, "/FontInfo 5 dict dup begin");
+ {
+ gs_font_info_t info;
+ int code = pfont->procs.font_info((gs_font *)pfont, NULL,
+ (FONT_INFO_COPYRIGHT | FONT_INFO_NOTICE |
+ FONT_INFO_FAMILY_NAME | FONT_INFO_FULL_NAME),
+ &info);
+
+ if (code >= 0) {
+ write_font_info(s, "Copyright", &info.Copyright,
+ info.members & FONT_INFO_COPYRIGHT);
+ write_font_info(s, "Notice", &info.Notice,
+ info.members & FONT_INFO_NOTICE);
+ write_font_info(s, "FamilyName", &info.FamilyName,
+ info.members & FONT_INFO_FAMILY_NAME);
+ write_font_info(s, "FullName", &info.FullName,
+ info.members & FONT_INFO_FULL_NAME);
+ }
+ }
+ stream_puts(s, "\nend readonly def\n");
+
+ /* Write the main font dictionary. */
+
+ stream_puts(s, "/FontName ");
+ write_font_name(s, pfont, alt_font_name, true);
+ stream_puts(s, " def\n");
+ code = write_Encoding(s, pfont, options, glyphs.subset_glyphs,
+ glyphs.subset_size, glyphs.notdef);
+ if (code < 0)
+ return code;
+ pprintg6(s, "/FontMatrix [%g %g %g %g %g %g] readonly def\n",
+ pfont->FontMatrix.xx, pfont->FontMatrix.xy,
+ pfont->FontMatrix.yx, pfont->FontMatrix.yy,
+ pfont->FontMatrix.tx, pfont->FontMatrix.ty);
+ write_uid(s, &pfont->UID);
+ pprintg4(s, "/FontBBox {%g %g %g %g} readonly def\n",
+ pfont->FontBBox.p.x, pfont->FontBBox.p.y,
+ pfont->FontBBox.q.x, pfont->FontBBox.q.y);
+ {
+ static const gs_param_item_t font_items[] = {
+ {"FontType", gs_param_type_int,
+ offset_of(gs_font_type1, FontType)},
+ {"PaintType", gs_param_type_int,
+ offset_of(gs_font_type1, PaintType)},
+ {"StrokeWidth", gs_param_type_float,
+ offset_of(gs_font_type1, StrokeWidth)},
+ gs_param_item_end
+ };
+
+ code = gs_param_write_items(plist, pfont, NULL, font_items);
+ if (code < 0)
+ return code;
+ }
+
+ /*
+ * This is nonsense. We cna't write the WeightVector alonr from a Multiple
+ * Master and expect any sensible results. Since its useless alone, there's
+ * no point in emitting it at all. Leaving the code in place in case we
+ * decide to write MM fonts one day.
+ {
+ const gs_type1_data *const pdata = &pfont->data;
+
+ write_float_array(plist, "WeightVector", pdata->WeightVector.values,
+ pdata->WeightVector.count);
+ }
+ */
+ stream_puts(s, "currentdict end\n");
+
+ /* Write the Private dictionary. */
+
+ if (lenIV < 0 && (options & WRITE_TYPE1_WITH_LENIV)) {
+ /* We'll have to encrypt the CharStrings. */
+ lenIV = 0;
+ write_CharString = stream_write_encrypted;
+ }
+ if (options & WRITE_TYPE1_EEXEC) {
+ stream_puts(s, "currentfile eexec\n");
+ lengths[0] = (int)(stell(s) - start);
+ start = stell(s);
+ if (options & WRITE_TYPE1_ASCIIHEX) {
+ s_init(&AXE_stream, s->memory);
+ s_init_state((stream_state *)&AXE_state, &s_AXE_template, NULL);
+ s_init_filter(&AXE_stream, (stream_state *)&AXE_state,
+ AXE_buf, sizeof(AXE_buf), es);
+ /* We have to set this after s_init_filter() as that function
+ * sets it to true.
+ */
+ AXE_state.EndOfData = false;
+ es = &AXE_stream;
+ }
+ s_init(&exE_stream, s->memory);
+ s_init_state((stream_state *)&exE_state, &s_exE_template, NULL);
+ exE_state.cstate = 55665;
+ s_init_filter(&exE_stream, (stream_state *)&exE_state,
+ exE_buf, sizeof(exE_buf), es);
+ es = &exE_stream;
+ /*
+ * Note: eexec encryption always writes/skips 4 initial bytes, not
+ * the number of initial bytes given by pdata->lenIV.
+ */
+ stream_puts(es, "****");
+ }
+ code = write_Private(es, pfont, glyphs.subset_glyphs, glyphs.subset_size,
+ glyphs.notdef, lenIV, write_CharString, &ppp);
+ if (code < 0)
+ return code;
+ stream_puts(es, "dup/FontName get exch definefont pop\n");
+ if (options & WRITE_TYPE1_EEXEC) {
+ if (options & (WRITE_TYPE1_EEXEC_PAD | WRITE_TYPE1_EEXEC_MARK))
+ stream_puts(es, "mark ");
+ stream_puts(es, "currentfile closefile\n");
+ s_close_filters(&es, s);
+ lengths[1] = (int)(stell(s) - start);
+ start = stell(s);
+ if (options & WRITE_TYPE1_EEXEC_PAD) {
+ int i;
+
+ for (i = 0; i < 8; ++i)
+ stream_puts(s, "\n0000000000000000000000000000000000000000000000000000000000000000");
+ stream_puts(s, "\ncleartomark\n");
+ }
+ lengths[2] = (int)(stell(s) - start);
+ } else {
+ lengths[0] = (int)(stell(s) - start);
+ lengths[1] = lengths[2] = 0;
+ }
+
+ /* Wrap up. */
+
+ s_release_param_printer(&rlist);
+ return 0;
+}
diff --git a/devices/vector/gdevpsf2.c b/devices/vector/gdevpsf2.c
new file mode 100644
index 000000000..7e76ee1a5
--- /dev/null
+++ b/devices/vector/gdevpsf2.c
@@ -0,0 +1,1840 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Write an embedded CFF font with either Type 1 or Type 2 CharStrings */
+#include "math_.h" /* for fabs */
+#include "memory_.h"
+#include "gx.h"
+#include "gxarith.h"
+#include "gscencs.h"
+#include "gserrors.h"
+#include "gsccode.h"
+#include "gscrypt1.h"
+#include "gsmatrix.h"
+#include "gsutil.h"
+#include "gxfixed.h"
+#include "gxfont.h"
+#include "gxfont1.h"
+#include "gxfcid.h"
+#include "stream.h"
+#include "gdevpsf.h"
+
+/* Define additional opcodes used in Dicts, but not in CharStrings. */
+#define CD_LONGINT 29
+#define CD_REAL 30
+
+/* Define the count of standard strings. */
+#define NUM_STD_STRINGS 391
+
+/* Define whether or not to skip writing an empty Subrs Index. */
+#define SKIP_EMPTY_SUBRS
+
+/* Define the structure for the string table. */
+typedef struct cff_string_item_s {
+ gs_const_string key;
+ int index1; /* index + 1, 0 means empty */
+} cff_string_item_t;
+typedef struct cff_string_table_s {
+ cff_string_item_t *items;
+ int count;
+ int size;
+ uint total;
+ int reprobe;
+} cff_string_table_t;
+
+/* Define the state of the CFF writer. */
+typedef struct cff_writer_s {
+ int options;
+ stream *strm;
+ gs_font_base *pfont; /* type1 or cid0 */
+ glyph_data_proc_t glyph_data;
+ gs_offset_t offset_size;
+ gs_offset_t start_pos;
+ cff_string_table_t std_strings;
+ cff_string_table_t strings;
+ gs_int_rect FontBBox;
+} cff_writer_t;
+typedef struct cff_glyph_subset_s {
+ psf_outline_glyphs_t glyphs;
+ int num_encoded; /* glyphs.subset_data[1..num_e] are encoded */
+ int num_encoded_chars; /* Encoding has num_e_chars defined entries */
+} cff_glyph_subset_t;
+
+/* ---------------- Output utilities ---------------- */
+
+/* ------ String tables ------ */
+
+/* Initialize a string table. */
+static void
+cff_string_table_init(cff_string_table_t *pcst, cff_string_item_t *items,
+ int size)
+{
+ int reprobe = 17;
+
+ memset(items, 0, size * sizeof(*items));
+ pcst->items = items;
+ pcst->count = 0;
+ pcst->size = size;
+ while (reprobe != 1 && igcd(size, reprobe) != 1)
+ reprobe = (reprobe * 2 + 1) % size;
+ pcst->total = 0;
+ pcst->reprobe = reprobe;
+}
+
+/* Add a string to a string table. */
+static int
+cff_string_add(cff_string_table_t *pcst, const byte *data, uint size)
+{
+ int index;
+
+ if (pcst->count >= pcst->size)
+ return_error(gs_error_limitcheck);
+ index = pcst->count++;
+ pcst->items[index].key.data = data;
+ pcst->items[index].key.size = size;
+ pcst->total += size;
+ return index;
+}
+
+/* Look up a string, optionally adding it. */
+/* Return 1 if the string was added. */
+static int
+cff_string_index(cff_string_table_t *pcst, const byte *data, uint size,
+ bool enter, int *pindex)
+{
+ /****** FAILS IF TABLE FULL AND KEY MISSING ******/
+ int j = (size == 0 ? 0 : data[0] * 23 + data[size - 1] * 59 + size);
+ int index, c = 0;
+
+ while ((index = pcst->items[j %= pcst->size].index1) != 0) {
+ --index;
+ if (!bytes_compare(pcst->items[index].key.data,
+ pcst->items[index].key.size, data, size)) {
+ *pindex = index;
+ return 0;
+ }
+ j += pcst->reprobe;
+ if (++c >= pcst->size)
+ break;
+ }
+ if (!enter)
+ return_error(gs_error_undefined);
+ index = cff_string_add(pcst, data, size);
+ if (index < 0)
+ return index;
+ pcst->items[j].index1 = index + 1;
+ *pindex = index;
+ return 1;
+}
+
+/* Get the SID for a string or a glyph. */
+static int
+cff_string_sid(cff_writer_t *pcw, const byte *data, uint size)
+{
+ int index;
+ int code = cff_string_index(&pcw->std_strings, data, size, false, &index);
+
+ if (code < 0) {
+ code = cff_string_index(&pcw->strings, data, size, true, &index);
+ if (code < 0)
+ return code;
+ index += NUM_STD_STRINGS;
+ }
+ return index;
+}
+static int
+cff_glyph_sid(cff_writer_t *pcw, gs_glyph glyph)
+{
+ gs_const_string str;
+ int code =
+ pcw->pfont->procs.glyph_name((gs_font *)pcw->pfont, glyph, &str);
+
+ if (code < 0)
+ return code;
+ return cff_string_sid(pcw, str.data, str.size);
+}
+
+/* ------ Low level ------ */
+
+static void
+put_card16(cff_writer_t *pcw, uint c16)
+{
+ sputc(pcw->strm, (byte)(c16 >> 8));
+ sputc(pcw->strm, (byte)c16);
+}
+static int
+offset_size(uint offset)
+{
+ int size = 1;
+
+ while (offset > 255)
+ offset >>= 8, ++size;
+ return size;
+}
+static void
+put_offset(cff_writer_t *pcw, int offset)
+{
+ int i;
+
+ for (i = pcw->offset_size - 1; i >= 0; --i)
+ sputc(pcw->strm, (byte)(offset >> (i * 8)));
+}
+static int
+put_bytes(stream * s, const byte *ptr, uint count)
+{
+ uint used;
+
+ sputs(s, ptr, count, &used);
+ return (int)used;
+}
+static int
+check_ioerror(stream * s)
+{
+ uint used;
+
+ return sputs(s, (byte *)&used, 0, &used);
+}
+
+/* ------ Data types ------ */
+
+#define CE_OFFSET 32
+static void
+cff_put_op(cff_writer_t *pcw, int op)
+{
+ if (op >= CE_OFFSET) {
+ sputc(pcw->strm, cx_escape);
+ sputc(pcw->strm, (byte)(op - CE_OFFSET));
+ } else
+ sputc(pcw->strm, (byte)op);
+}
+static void
+cff_put_int(cff_writer_t *pcw, int i)
+{
+ stream *s = pcw->strm;
+
+ if (i >= -107 && i <= 107)
+ sputc(s, (byte)(i + 139));
+ else if (i <= 1131 && i >= 0)
+ put_card16(pcw, (c_pos2_0 << 8) + i - 108);
+ else if (i >= -1131 && i < 0)
+ put_card16(pcw, (c_neg2_0 << 8) - i - 108);
+ else if (i >= -32768 && i <= 32767) {
+ sputc(s, c2_shortint);
+ put_card16(pcw, i & 0xffff);
+ } else {
+ sputc(s, CD_LONGINT);
+ put_card16(pcw, i >> 16);
+ put_card16(pcw, i & 0xffff);
+ }
+}
+static void
+cff_put_int_value(cff_writer_t *pcw, int i, int op)
+{
+ cff_put_int(pcw, i);
+ cff_put_op(pcw, op);
+}
+static void
+cff_put_int_if_ne(cff_writer_t *pcw, int i, int i_default, int op)
+{
+ if (i != i_default)
+ cff_put_int_value(pcw, i, op);
+}
+static void
+cff_put_bool(cff_writer_t *pcw, bool b)
+{
+ cff_put_int(pcw, (b ? 1 : 0));
+}
+static void
+cff_put_bool_value(cff_writer_t *pcw, bool b, int op)
+{
+ cff_put_bool(pcw, b);
+ cff_put_op(pcw, op);
+}
+static void
+cff_put_real(cff_writer_t *pcw, double f)
+{
+ if (f == (int)f)
+ cff_put_int(pcw, (int)f);
+ else {
+ /* Use decimal representation. */
+ char str[50];
+ byte b = 0xff;
+ const char *p;
+
+ gs_sprintf(str, "%g", f);
+ sputc(pcw->strm, CD_REAL);
+ for (p = str; ; ++p) {
+ int digit;
+
+ switch (*p) {
+ case 0:
+ goto done;
+ case '.':
+ digit = 0xa; break;
+ case '+':
+ continue;
+ case '-':
+ digit = 0xe; break;
+ case 'e': case 'E':
+ if (p[1] == '-')
+ digit = 0xc, ++p;
+ else
+ digit = 0xb;
+ break;
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ digit = *p - '0';
+ break;
+ default: /* can't happen */
+ digit = 0xd; /* invalid */
+ break;
+ }
+ if (b == 0xff)
+ b = (digit << 4) + 0xf;
+ else {
+ sputc(pcw->strm, (byte)((b & 0xf0) + digit));
+ b = 0xff;
+ }
+ }
+ done:
+ sputc(pcw->strm, b);
+ }
+}
+static void
+cff_put_real_value(cff_writer_t *pcw, double f, int op)
+{
+ cff_put_real(pcw, f);
+ cff_put_op(pcw, op);
+}
+static void
+cff_put_real_if_ne(cff_writer_t *pcw, double f, double f_default, int op)
+{
+ if ((float)f != (float)f_default)
+ cff_put_real_value(pcw, f, op);
+}
+static void
+cff_put_real_deltarray(cff_writer_t *pcw, const float *pf, int count, int op)
+{
+ float prev = 0;
+ int i;
+
+ if (count <= 0)
+ return;
+ for (i = 0; i < count; ++i) {
+ float f = pf[i];
+
+ cff_put_real(pcw, f - prev);
+ prev = f;
+ }
+ cff_put_op(pcw, op);
+}
+static int
+cff_put_string(cff_writer_t *pcw, const byte *data, uint size)
+{
+ int sid = cff_string_sid(pcw, data, size);
+
+ if (sid < 0)
+ return sid;
+ cff_put_int(pcw, sid);
+ return 0;
+}
+static int
+cff_put_string_value(cff_writer_t *pcw, const byte *data, uint size, int op)
+{
+ int code = cff_put_string(pcw, data, size);
+
+ if (code >= 0)
+ cff_put_op(pcw, op);
+ return code;
+}
+static int
+cff_extra_lenIV(const cff_writer_t *pcw, const gs_font_type1 *pfont)
+{
+ return (pcw->options & WRITE_TYPE2_NO_LENIV ?
+ max(pfont->data.lenIV, 0) : 0);
+}
+static bool
+cff_convert_charstrings(const cff_writer_t *pcw, const gs_font_base *pfont)
+{
+ return (pfont->FontType != ft_encrypted2 &&
+ (pcw->options & WRITE_TYPE2_CHARSTRINGS) != 0);
+}
+static int
+cff_put_CharString(cff_writer_t *pcw, const byte *data, uint size,
+ gs_font_type1 *pfont)
+{
+ int lenIV = pfont->data.lenIV;
+ stream *s = pcw->strm;
+
+ if (cff_convert_charstrings(pcw, (gs_font_base *)pfont)) {
+ gs_glyph_data_t gdata;
+ int code;
+
+ gdata.memory = pfont->memory;
+ gs_glyph_data_from_string(&gdata, data, size, NULL);
+ code = psf_convert_type1_to_type2(s, &gdata, pfont);
+ if (code < 0)
+ return code;
+ } else if (lenIV < 0 || !(pcw->options & WRITE_TYPE2_NO_LENIV))
+ put_bytes(s, data, size);
+ else if (size >= lenIV) {
+ /* Remove encryption. */
+ crypt_state state = crypt_charstring_seed;
+ byte buf[50]; /* arbitrary */
+ uint left, n;
+
+ for (left = lenIV; left > 0; left -= n) {
+ n = min(left, sizeof(buf));
+ gs_type1_decrypt(buf, data + lenIV - left, n, &state);
+ }
+ for (left = size - lenIV; left > 0; left -= n) {
+ n = min(left, sizeof(buf));
+ gs_type1_decrypt(buf, data + size - left, n, &state);
+ put_bytes(s, buf, n);
+ }
+ }
+ return 0;
+}
+static uint
+cff_Index_size(uint count, uint total)
+{
+ return (count == 0 ? 2 :
+ 3 + offset_size(total + 1) * (count + 1) + total);
+}
+static void
+cff_put_Index_header(cff_writer_t *pcw, uint count, uint total)
+{
+ put_card16(pcw, count);
+ if (count > 0) {
+ pcw->offset_size = offset_size(total + 1);
+ sputc(pcw->strm, (byte)pcw->offset_size);
+ put_offset(pcw, 1);
+ }
+}
+static void
+cff_put_Index(cff_writer_t *pcw, const cff_string_table_t *pcst)
+{
+ uint j, offset;
+
+ if (pcst->count == 0) {
+ put_card16(pcw, 0);
+ return;
+ }
+ cff_put_Index_header(pcw, pcst->count, pcst->total);
+ for (j = 0, offset = 1; j < pcst->count; ++j) {
+ offset += pcst->items[j].key.size;
+ put_offset(pcw, offset);
+ }
+ for (j = 0; j < pcst->count; ++j)
+ put_bytes(pcw->strm, pcst->items[j].key.data, pcst->items[j].key.size);
+}
+
+/* ---------------- Main code ---------------- */
+
+/* ------ Header ------ */
+
+/* Write the header, setting offset_size. */
+static int
+cff_write_header(cff_writer_t *pcw, uint end_offset)
+{
+ pcw->offset_size = (end_offset > 0x7fff ? 3 : 2);
+ put_bytes(pcw->strm, (const byte *)"\001\000\004", 3);
+ sputc(pcw->strm, (byte)pcw->offset_size);
+ return 0;
+}
+
+/* ------ Top Dict ------ */
+
+/*
+ * There are 3 variants of this: Type 1 / Type 2 font, CIDFontType 0
+ * CIDFont, and FDArray entry for CIDFont.
+ */
+
+typedef enum {
+ TOP_version = 0,
+ TOP_Notice = 1,
+ TOP_FullName = 2,
+ TOP_FamilyName = 3,
+ TOP_Weight = 4,
+ TOP_FontBBox = 5,
+ TOP_UniqueID = 13,
+ TOP_XUID = 14,
+ TOP_charset = 15, /* (offset or predefined index) */
+#define charset_ISOAdobe 0
+#define charset_Expert 1
+#define charset_ExpertSubset 2
+#define charset_DEFAULT 0
+ TOP_Encoding = 16, /* (offset or predefined index) */
+#define Encoding_Standard 0
+#define Encoding_Expert 1
+#define Encoding_DEFAULT 0
+ TOP_CharStrings = 17, /* (offset) */
+ TOP_Private = 18, /* (offset) */
+ TOP_Copyright = 32,
+ TOP_isFixedPitch = 33,
+#define isFixedPitch_DEFAULT false
+ TOP_ItalicAngle = 34,
+#define ItalicAngle_DEFAULT 0
+ TOP_UnderlinePosition = 35,
+#define UnderlinePosition_DEFAULT (-100)
+ TOP_UnderlineThickness = 36,
+#define UnderlineThickness_DEFAULT 50
+ TOP_PaintType = 37,
+#define PaintType_DEFAULT 0
+ TOP_CharstringType = 38,
+#define CharstringType_DEFAULT 2
+ TOP_FontMatrix = 39, /* default is [0.001 0 0 0.001 0 0] */
+ TOP_StrokeWidth = 40,
+#define StrokeWidth_DEFAULT 0
+ TOP_ROS = 62,
+ TOP_CIDFontVersion = 63,
+#define CIDFontVersion_DEFAULT 0
+ TOP_CIDFontRevision = 64,
+#define CIDFontRevision_DEFAULT 0
+ TOP_CIDFontType = 65,
+#define CIDFontType_DEFAULT 0
+ TOP_CIDCount = 66,
+#define CIDCount_DEFAULT 8720
+ TOP_UIDBase = 67,
+ TOP_FDArray = 68, /* (offset) */
+ TOP_FDSelect = 69, /* (offset) */
+ TOP_FontName = 70 /* only used in FDArray "fonts" */
+} Top_op;
+
+static int
+cff_get_Top_info_common(cff_writer_t *pcw, gs_font_base *pbfont,
+ bool full_info, gs_font_info_t *pinfo)
+{
+ pinfo->Flags_requested = FONT_IS_FIXED_WIDTH;
+ /* Preset defaults */
+ pinfo->members = 0;
+ pinfo->Flags = pinfo->Flags_returned = 0;
+ pinfo->ItalicAngle = ItalicAngle_DEFAULT;
+ pinfo->UnderlinePosition = UnderlinePosition_DEFAULT;
+ pinfo->UnderlineThickness = UnderlineThickness_DEFAULT;
+ return pbfont->procs.font_info
+ ((gs_font *)pbfont, NULL,
+ (full_info ?
+ FONT_INFO_FLAGS | FONT_INFO_ITALIC_ANGLE |
+ FONT_INFO_UNDERLINE_POSITION |
+ FONT_INFO_UNDERLINE_THICKNESS : 0) |
+ (FONT_INFO_COPYRIGHT | FONT_INFO_NOTICE |
+ FONT_INFO_FAMILY_NAME | FONT_INFO_FULL_NAME),
+ pinfo);
+}
+static void
+cff_write_Top_common(cff_writer_t *pcw, gs_font_base *pbfont,
+ bool write_FontMatrix, const gs_font_info_t *pinfo)
+{
+ /*
+ * The Adobe documentation doesn't make it at all clear that if the
+ * FontMatrix is missing (defaulted) in a CFF CIDFont, all of the
+ * FontMatrices of the subfonts in FDArray are multiplied by 1000.
+ * (This is documented for ordinary CIDFonts, but not for CFF CIDFonts.)
+ * Because of this, the FontMatrix for a CFF CIDFont must be written
+ * even if if is the default. write_FontMatrix controls this.
+ */
+ /* (version) */
+ if (pinfo->members & FONT_INFO_NOTICE)
+ cff_put_string_value(pcw, pinfo->Notice.data, pinfo->Notice.size,
+ TOP_Notice);
+ if (pinfo->members & FONT_INFO_FULL_NAME)
+ cff_put_string_value(pcw, pinfo->FullName.data, pinfo->FullName.size,
+ TOP_FullName);
+ if (pinfo->members & FONT_INFO_FAMILY_NAME)
+ cff_put_string_value(pcw, pinfo->FamilyName.data,
+ pinfo->FamilyName.size, TOP_FamilyName);
+ if (pcw->FontBBox.p.x != 0 || pcw->FontBBox.p.y != 0 ||
+ pcw->FontBBox.q.x != 0 || pcw->FontBBox.q.y != 0
+ ) {
+ /* An omitted FontBBox is equivalent to an empty one. */
+ /*
+ * Since Acrobat Reader 4 on Solaris doesn't like
+ * an omitted FontBBox, we copy it here from
+ * the font descriptor, because the base font
+ * is allowed to omit it's FontBBox.
+ */
+ cff_put_real(pcw, pcw->FontBBox.p.x);
+ cff_put_real(pcw, pcw->FontBBox.p.y);
+ cff_put_real(pcw, pcw->FontBBox.q.x);
+ cff_put_real(pcw, pcw->FontBBox.q.y);
+ cff_put_op(pcw, TOP_FontBBox);
+ }
+ if (uid_is_UniqueID(&pbfont->UID))
+ cff_put_int_value(pcw, pbfont->UID.id, TOP_UniqueID);
+ else if (uid_is_XUID(&pbfont->UID)) {
+ int j;
+
+ for (j = 0; j < uid_XUID_size(&pbfont->UID); ++j)
+ cff_put_int(pcw, uid_XUID_values(&pbfont->UID)[j]);
+ cff_put_op(pcw, TOP_XUID);
+ }
+ /*
+ * Acrobat Reader 3 gives an error if a CFF font includes any of the
+ * following opcodes.
+ */
+ if (!(pcw->options & WRITE_TYPE2_AR3)) {
+ if (pinfo->members & FONT_INFO_COPYRIGHT)
+ cff_put_string_value(pcw, pinfo->Copyright.data,
+ pinfo->Copyright.size, TOP_Copyright);
+ if (pinfo->Flags & pinfo->Flags_returned & FONT_IS_FIXED_WIDTH)
+ cff_put_bool_value(pcw, true, TOP_isFixedPitch);
+ cff_put_real_if_ne(pcw, pinfo->ItalicAngle, ItalicAngle_DEFAULT,
+ TOP_ItalicAngle);
+ cff_put_int_if_ne(pcw, pinfo->UnderlinePosition,
+ UnderlinePosition_DEFAULT, TOP_UnderlinePosition);
+ cff_put_int_if_ne(pcw, pinfo->UnderlineThickness,
+ UnderlineThickness_DEFAULT, TOP_UnderlineThickness);
+ cff_put_int_if_ne(pcw, pbfont->PaintType, PaintType_DEFAULT,
+ TOP_PaintType);
+ }
+ {
+ static const gs_matrix fm_default = {
+ constant_matrix_body(0.001, 0, 0, 0.001, 0, 0)
+ };
+
+ if (write_FontMatrix ||
+ pbfont->FontMatrix.xx != fm_default.xx ||
+ pbfont->FontMatrix.xy != 0 || pbfont->FontMatrix.yx != 0 ||
+ pbfont->FontMatrix.yy != fm_default.yy ||
+ pbfont->FontMatrix.tx != 0 || pbfont->FontMatrix.ty != 0
+ ) {
+ cff_put_real(pcw, pbfont->FontMatrix.xx);
+ cff_put_real(pcw, pbfont->FontMatrix.xy);
+ cff_put_real(pcw, pbfont->FontMatrix.yx);
+ cff_put_real(pcw, pbfont->FontMatrix.yy);
+ cff_put_real(pcw, pbfont->FontMatrix.tx);
+ cff_put_real(pcw, pbfont->FontMatrix.ty);
+ cff_put_op(pcw, TOP_FontMatrix);
+ }
+ }
+ cff_put_real_if_ne(pcw, pbfont->StrokeWidth, StrokeWidth_DEFAULT,
+ TOP_StrokeWidth);
+}
+
+/* Type 1 or Type 2 font */
+static void
+cff_write_Top_font(cff_writer_t *pcw, uint Encoding_offset,
+ uint charset_offset, uint CharStrings_offset,
+ uint Private_offset, uint Private_size)
+{
+ gs_font_base *pbfont = (gs_font_base *)pcw->pfont;
+ gs_font_info_t info;
+
+ cff_get_Top_info_common(pcw, pbfont, true, &info);
+ cff_write_Top_common(pcw, pbfont, false, &info);
+ cff_put_int(pcw, Private_size);
+ cff_put_int_value(pcw, Private_offset, TOP_Private);
+ cff_put_int_value(pcw, CharStrings_offset, TOP_CharStrings);
+ cff_put_int_if_ne(pcw, charset_offset, charset_DEFAULT, TOP_charset);
+ cff_put_int_if_ne(pcw, Encoding_offset, Encoding_DEFAULT, TOP_Encoding);
+ {
+ int type = (pcw->options & WRITE_TYPE2_CHARSTRINGS ? 2 :
+ pbfont->FontType == ft_encrypted2 ? 2 : 1);
+
+ cff_put_int_if_ne(pcw, type, CharstringType_DEFAULT,
+ TOP_CharstringType);
+ }
+}
+
+/* CIDFontType 0 CIDFont */
+static void
+cff_write_ROS(cff_writer_t *pcw, const gs_cid_system_info_t *pcidsi)
+{
+ cff_put_string(pcw, pcidsi->Registry.data, pcidsi->Registry.size);
+ cff_put_string(pcw, pcidsi->Ordering.data, pcidsi->Ordering.size);
+ cff_put_int_value(pcw, pcidsi->Supplement, TOP_ROS);
+}
+static void
+cff_write_Top_cidfont(cff_writer_t *pcw, uint charset_offset,
+ uint CharStrings_offset, uint FDSelect_offset,
+ uint Font_offset, const gs_font_info_t *pinfo)
+{
+ gs_font_base *pbfont = (gs_font_base *)pcw->pfont;
+ gs_font_cid0 *pfont = (gs_font_cid0 *)pbfont;
+
+ cff_write_ROS(pcw, &pfont->cidata.common.CIDSystemInfo);
+ cff_write_Top_common(pcw, pbfont, true, pinfo); /* full_info = true */
+ cff_put_int_if_ne(pcw, charset_offset, charset_DEFAULT, TOP_charset);
+ cff_put_int_value(pcw, CharStrings_offset, TOP_CharStrings);
+ /*
+ * CIDFontVersion and CIDFontRevision aren't used consistently,
+ * so we don't currently write them. CIDFontType is always 0.
+ */
+ cff_put_int_if_ne(pcw, pfont->cidata.common.CIDCount, CIDCount_DEFAULT,
+ TOP_CIDCount);
+ /* We don't use UIDBase. */
+ cff_put_int_value(pcw, Font_offset, TOP_FDArray);
+ cff_put_int_value(pcw, FDSelect_offset, TOP_FDSelect);
+}
+
+/* FDArray Index for CIDFont (offsets only) */
+static void
+cff_write_FDArray_offsets(cff_writer_t *pcw, uint *FDArray_offsets,
+ int num_fonts)
+{
+ int j;
+
+ cff_put_Index_header(pcw, num_fonts,
+ FDArray_offsets[num_fonts] - FDArray_offsets[0]);
+ for (j = 1; j <= num_fonts; ++j)
+ put_offset(pcw, FDArray_offsets[j] - FDArray_offsets[0] + 1);
+}
+
+/* FDArray entry for CIDFont */
+static void
+cff_write_Top_fdarray(cff_writer_t *pcw, gs_font_base *pbfont,
+ uint Private_offset, uint Private_size)
+{
+ const gs_font_name *pfname = &pbfont->font_name;
+ gs_font_info_t info;
+
+ cff_get_Top_info_common(pcw, pbfont, false, &info);
+ cff_write_Top_common(pcw, pbfont, false, &info);
+ cff_put_int(pcw, Private_size);
+ cff_put_int_value(pcw, Private_offset, TOP_Private);
+ if (pfname->size == 0)
+ pfname = &pbfont->key_name;
+ if (pfname->size) {
+ cff_put_string(pcw, pfname->chars, pfname->size);
+ cff_put_op(pcw, TOP_FontName);
+ }
+}
+
+/* ------ Private Dict ------ */
+
+/* Defaults are noted in comments. */
+typedef enum {
+ PRIVATE_BlueValues = 6, /* (deltarray) */
+ PRIVATE_OtherBlues = 7, /* (deltarray) */
+ PRIVATE_FamilyBlues = 8, /* (deltarray) */
+ PRIVATE_FamilyOtherBlues = 9, /* (deltarray) */
+ PRIVATE_StdHW = 10,
+ PRIVATE_StdVW = 11,
+ PRIVATE_Subrs = 19, /* (offset, relative to Private Dict) */
+ PRIVATE_defaultWidthX = 20,
+#define defaultWidthX_DEFAULT fixed_0
+ PRIVATE_nominalWidthX = 21,
+#define nominalWidthX_DEFAULT fixed_0
+ PRIVATE_BlueScale = 41,
+#define BlueScale_DEFAULT 0.039625
+ PRIVATE_BlueShift = 42,
+#define BlueShift_DEFAULT 7
+ PRIVATE_BlueFuzz = 43,
+#define BlueFuzz_DEFAULT 1
+ PRIVATE_StemSnapH = 44, /* (deltarray) */
+ PRIVATE_StemSnapV = 45, /* (deltarray) */
+ PRIVATE_ForceBold = 46,
+#define ForceBold_DEFAULT false
+ PRIVATE_ForceBoldThreshold = 47,
+#define ForceBoldThreshold_DEFAULT 0
+ PRIVATE_lenIV = 48,
+#define lenIV_DEFAULT (-1)
+ PRIVATE_LanguageGroup = 49,
+#define LanguageGroup_DEFAULT 0
+ PRIVATE_ExpansionFactor = 50,
+#define ExpansionFactor_DEFAULT 0.06
+ PRIVATE_initialRandomSeed = 51
+#define initialRandomSeed_DEFAULT 0
+} Private_op;
+
+const long default_defaultWidthX = defaultWidthX_DEFAULT; /* For gdevpsfx.c */
+
+static void
+cff_write_Private(cff_writer_t *pcw, uint Subrs_offset,
+ const gs_font_type1 *pfont)
+{
+#define PUT_FLOAT_TABLE(member, op)\
+ cff_put_real_deltarray(pcw, pfont->data.member.values,\
+ pfont->data.member.count, op)
+
+ PUT_FLOAT_TABLE(BlueValues, PRIVATE_BlueValues);
+ PUT_FLOAT_TABLE(OtherBlues, PRIVATE_OtherBlues);
+ PUT_FLOAT_TABLE(FamilyBlues, PRIVATE_FamilyBlues);
+ PUT_FLOAT_TABLE(FamilyOtherBlues, PRIVATE_FamilyOtherBlues);
+ if (pfont->data.StdHW.count > 0)
+ cff_put_real_value(pcw, pfont->data.StdHW.values[0], PRIVATE_StdHW);
+ if (pfont->data.StdVW.count > 0)
+ cff_put_real_value(pcw, pfont->data.StdVW.values[0], PRIVATE_StdVW);
+ if (Subrs_offset)
+ cff_put_int_value(pcw, Subrs_offset, PRIVATE_Subrs);
+ if (pfont->FontType != ft_encrypted) {
+ if (pfont->data.defaultWidthX != defaultWidthX_DEFAULT)
+ cff_put_real_value(pcw, fixed2float(pfont->data.defaultWidthX),
+ PRIVATE_defaultWidthX);
+ if (pfont->data.nominalWidthX != nominalWidthX_DEFAULT)
+ cff_put_real_value(pcw, fixed2float(pfont->data.nominalWidthX),
+ PRIVATE_nominalWidthX);
+ cff_put_int_if_ne(pcw, pfont->data.initialRandomSeed,
+ initialRandomSeed_DEFAULT,
+ PRIVATE_initialRandomSeed);
+ }
+ cff_put_real_if_ne(pcw, pfont->data.BlueScale, BlueScale_DEFAULT,
+ PRIVATE_BlueScale);
+ cff_put_real_if_ne(pcw, pfont->data.BlueShift, BlueShift_DEFAULT,
+ PRIVATE_BlueShift);
+ cff_put_int_if_ne(pcw, pfont->data.BlueFuzz, BlueFuzz_DEFAULT,
+ PRIVATE_BlueFuzz);
+ PUT_FLOAT_TABLE(StemSnapH, PRIVATE_StemSnapH);
+ PUT_FLOAT_TABLE(StemSnapV, PRIVATE_StemSnapV);
+ if (pfont->data.ForceBold != ForceBold_DEFAULT)
+ cff_put_bool_value(pcw, pfont->data.ForceBold,
+ PRIVATE_ForceBold);
+ /* (ForceBoldThreshold) */
+ if (!(pcw->options & WRITE_TYPE2_NO_LENIV))
+ cff_put_int_if_ne(pcw, pfont->data.lenIV, lenIV_DEFAULT,
+ PRIVATE_lenIV);
+ cff_put_int_if_ne(pcw, pfont->data.LanguageGroup, LanguageGroup_DEFAULT,
+ PRIVATE_LanguageGroup);
+ cff_put_real_if_ne(pcw, pfont->data.ExpansionFactor,
+ ExpansionFactor_DEFAULT, PRIVATE_ExpansionFactor);
+ /* initialRandomSeed was handled above */
+
+#undef PUT_FLOAT_TABLE
+}
+
+/* ------ CharStrings Index ------ */
+
+/* These are separate procedures only for readability. */
+static int
+cff_write_CharStrings_offsets(cff_writer_t *pcw, psf_glyph_enum_t *penum,
+ uint *pcount)
+{
+ gs_font_base *pfont = pcw->pfont;
+ int offset;
+ gs_glyph glyph;
+ uint count;
+ stream poss;
+ int code;
+
+ s_init(&poss, NULL);
+ psf_enumerate_glyphs_reset(penum);
+ for (glyph = gs_no_glyph, count = 0, offset = 1;
+ (code = psf_enumerate_glyphs_next(penum, &glyph)) != 1;
+ ++count) {
+ gs_glyph_data_t gdata;
+ gs_font_type1 *pfd;
+ int gcode;
+
+ gdata.memory = pfont->memory;
+ if (code == 0 &&
+ (gcode = pcw->glyph_data(pfont, glyph, &gdata, &pfd)) >= 0
+ ) {
+ int extra_lenIV;
+
+ if (gdata.bits.size >= (extra_lenIV = cff_extra_lenIV(pcw, pfd))) {
+ if (cff_convert_charstrings(pcw, (gs_font_base *)pfd)) {
+ swrite_position_only(&poss);
+ code = psf_convert_type1_to_type2(&poss, &gdata, pfd);
+ if (code < 0)
+ return code;
+ offset += stell(&poss);
+ } else
+ offset += gdata.bits.size - extra_lenIV;
+ }
+ gs_glyph_data_free(&gdata, "cff_write_CharStrings_offsets");
+ }
+ put_offset(pcw, offset);
+ }
+ *pcount = count;
+ return offset - 1;
+}
+static void
+cff_write_CharStrings(cff_writer_t *pcw, psf_glyph_enum_t *penum,
+ uint charstrings_count, uint charstrings_size)
+{
+ gs_font_base *pfont = pcw->pfont;
+ uint ignore_count;
+ gs_glyph glyph;
+ int code;
+
+ cff_put_Index_header(pcw, charstrings_count, charstrings_size);
+ cff_write_CharStrings_offsets(pcw, penum, &ignore_count);
+ psf_enumerate_glyphs_reset(penum);
+ for (glyph = gs_no_glyph;
+ (code = psf_enumerate_glyphs_next(penum, &glyph)) != 1;
+ ) {
+ gs_glyph_data_t gdata;
+ gs_font_type1 *pfd;
+
+ gdata.memory = pfont->memory;
+ if (code == 0 &&
+ (code = pcw->glyph_data(pfont, glyph, &gdata, &pfd)) >= 0
+ ) {
+ cff_put_CharString(pcw, gdata.bits.data, gdata.bits.size, pfd);
+ gs_glyph_data_free(&gdata, "cff_write_CharStrings");
+ }
+ }
+}
+
+/* ------ [G]Subrs Index ------ */
+
+/*
+ * Currently, we always write all the Subrs, even for subsets.
+ * We will fix this someday.
+ */
+
+static uint
+cff_write_Subrs_offsets(cff_writer_t *pcw, uint *pcount, gs_font_type1 *pfont,
+ bool global)
+{
+ int extra_lenIV = cff_extra_lenIV(pcw, pfont);
+ int j, offset;
+ int code;
+ gs_glyph_data_t gdata;
+
+ gdata.memory = pfont->memory;
+ for (j = 0, offset = 1;
+ (code = pfont->data.procs.subr_data(pfont, j, global, &gdata)) !=
+ gs_error_rangecheck;
+ ++j) {
+ if (code >= 0 && gdata.bits.size >= extra_lenIV)
+ offset += gdata.bits.size - extra_lenIV;
+ put_offset(pcw, offset);
+ if (code >= 0)
+ gs_glyph_data_free(&gdata, "cff_write_Subrs_offsets");
+ }
+ *pcount = j;
+ return offset - 1;
+}
+
+static void
+cff_write_Subrs(cff_writer_t *pcw, uint subrs_count, uint subrs_size,
+ gs_font_type1 *pfont, bool global)
+{
+ int j;
+ uint ignore_count;
+ gs_glyph_data_t gdata;
+ int code;
+
+ gdata.memory = pfont->memory;
+ cff_put_Index_header(pcw, subrs_count, subrs_size);
+ cff_write_Subrs_offsets(pcw, &ignore_count, pfont, global);
+ for (j = 0;
+ (code = pfont->data.procs.subr_data(pfont, j, global, &gdata)) !=
+ gs_error_rangecheck;
+ ++j) {
+ if (code >= 0) {
+ cff_put_CharString(pcw, gdata.bits.data, gdata.bits.size, pfont);
+ gs_glyph_data_free(&gdata, "cff_write_Subrs");
+ }
+ }
+}
+
+/* ------ Encoding/charset ------ */
+
+static uint
+cff_Encoding_size(int num_encoded, int num_encoded_chars)
+{
+ int n = min(num_encoded, 255);
+
+ return 2 + n +
+ (num_encoded_chars > n ?
+ 1 + (num_encoded_chars - n) * 3 : 0);
+}
+
+static int
+cff_write_Encoding(cff_writer_t *pcw, cff_glyph_subset_t *pgsub)
+{
+ stream *s = pcw->strm;
+ /* This procedure is only used for Type 1 / Type 2 fonts. */
+ gs_font_type1 *pfont = (gs_font_type1 *)pcw->pfont;
+ byte used[255], index[255], supplement[256];
+ int num_enc = min(pgsub->num_encoded, sizeof(index));
+ int nsupp = 0;
+ int j;
+
+ memset(used, 0, num_enc);
+ for (j = 0; j < 256; ++j) {
+ gs_glyph glyph = pfont->procs.encode_char((gs_font *)pfont,
+ (gs_char)j,
+ GLYPH_SPACE_NAME);
+ int i;
+
+ if (glyph == gs_no_glyph || glyph == pgsub->glyphs.notdef)
+ continue;
+ i = psf_sorted_glyphs_index_of(pgsub->glyphs.subset_data + 1,
+ pgsub->num_encoded, glyph);
+ if (i < 0)
+ continue; /* encoded but not in subset */
+ if (i >= sizeof(used) || used[i])
+ supplement[nsupp++] = j;
+ else
+ index[i] = j, used[i] = 1;
+ }
+ sputc(s, (byte)(nsupp ? 0x80 : 0));
+ sputc(s, (byte)num_enc);
+#ifdef DEBUG
+ { int num_enc_chars = pgsub->num_encoded_chars;
+
+ if (nsupp != num_enc_chars - num_enc)
+ lprintf3("nsupp = %d, num_enc_chars = %d, num_enc = %d\n",
+ nsupp, num_enc_chars, num_enc);
+ for (j = 0; j < num_enc; ++j)
+ if (!used[j])
+ lprintf2("glyph %d = 0x%lx not used\n", j,
+ pgsub->glyphs.subset_data[j + 1]);
+ }
+#endif
+ put_bytes(s, index, num_enc);
+ if (nsupp) {
+ /* Write supplementary entries for multiply-encoded glyphs. */
+ sputc(s, (byte)nsupp);
+ for (j = 0; j < nsupp; ++j) {
+ byte chr = supplement[j];
+
+ sputc(s, chr);
+ put_card16(pcw,
+ cff_glyph_sid(pcw,
+ pfont->procs.encode_char((gs_font *)pfont,
+ (gs_char)chr,
+ GLYPH_SPACE_NAME)));
+ }
+ }
+ return 0;
+}
+
+static int
+cff_write_charset(cff_writer_t *pcw, cff_glyph_subset_t *pgsub)
+{
+ int j;
+
+ sputc(pcw->strm, 0);
+ for (j = 1; j < pgsub->glyphs.subset_size; ++j)
+ put_card16(pcw, cff_glyph_sid(pcw, pgsub->glyphs.subset_data[j]));
+ return 0;
+}
+static int
+cff_write_cidset(cff_writer_t *pcw, psf_glyph_enum_t *penum)
+{
+ gs_glyph glyph;
+ int code;
+
+ sputc(pcw->strm, 0);
+ psf_enumerate_glyphs_reset(penum);
+ while ((code = psf_enumerate_glyphs_next(penum, &glyph)) == 0) {
+ /* Skip glyph 0 (the .notdef glyph), which is always first. */
+ if (glyph != gs_min_cid_glyph)
+ put_card16(pcw, (uint)(glyph - gs_min_cid_glyph));
+ }
+ return min(code, 0);
+}
+
+/* ------ FDSelect ------ */
+
+/* Determine the size of FDSelect. */
+static uint
+cff_FDSelect_size(cff_writer_t *pcw, psf_glyph_enum_t *penum, uint *pformat)
+{
+ gs_font_cid0 *const pfont = (gs_font_cid0 *)pcw->pfont;
+ gs_font_base *const pbfont = (gs_font_base *)pfont;
+ gs_glyph glyph;
+ int prev = -1;
+ uint linear_size = 1, range_size = 5;
+ int code;
+
+ /* Determine whether format 0 or 3 is more efficient. */
+ psf_enumerate_glyphs_reset(penum);
+ while ((code = psf_enumerate_glyphs_next(penum, &glyph)) == 0) {
+ int font_index;
+
+ code = pfont->cidata.glyph_data(pbfont, glyph, NULL, &font_index);
+ if (code >= 0) {
+ if (font_index != prev)
+ range_size += 3, prev = font_index;
+ ++linear_size;
+ }
+ }
+ if (range_size < linear_size) {
+ *pformat = 3;
+ return range_size;
+ } else {
+ *pformat = 0;
+ return linear_size;
+ }
+}
+
+/* Write FDSelect. size and format were returned by cff_FDSelect_size. */
+static int
+cff_write_FDSelect(cff_writer_t *pcw, psf_glyph_enum_t *penum, uint size,
+ int format)
+{
+ stream *s = pcw->strm;
+ gs_font_cid0 *const pfont = (gs_font_cid0 *)pcw->pfont;
+ gs_font_base *const pbfont = (gs_font_base *)pfont;
+ gs_glyph glyph;
+ int prev = -1;
+ uint cid_count = 0;
+ int code;
+
+ spputc(s, (byte)format);
+ psf_enumerate_glyphs_reset(penum);
+ switch (format) {
+ case 3: /* ranges */
+ put_card16(pcw, (size - 5) / 3);
+ while ((code = psf_enumerate_glyphs_next(penum, &glyph)) == 0) {
+ int font_index;
+
+ code = pfont->cidata.glyph_data(pbfont, glyph, NULL, &font_index);
+ if (code >= 0) {
+ if (font_index != prev) {
+ put_card16(pcw, cid_count);
+ sputc(s, (byte)font_index);
+ prev = font_index;
+ }
+ ++cid_count;
+ }
+ }
+ put_card16(pcw, cid_count);
+ break;
+ case 0: /* linear table */
+ while ((code = psf_enumerate_glyphs_next(penum, &glyph)) == 0) {
+ int font_index;
+
+ code = pfont->cidata.glyph_data(pbfont, glyph, NULL, &font_index);
+ if (code >= 0)
+ sputc(s, (byte)font_index);
+ }
+ break;
+ default: /* not possible */
+ return_error(gs_error_rangecheck);
+ }
+ return 0;
+}
+
+/* ------ Main procedure ------ */
+
+/* Write the CFF definition of a Type 1 or Type 2 font. */
+int
+psf_write_type2_font(stream *s, gs_font_type1 *pfont, int options,
+ gs_glyph *subset_glyphs, uint subset_size,
+ const gs_const_string *alt_font_name,
+ gs_int_rect *FontBBox)
+{
+ gs_font_base *const pbfont = (gs_font_base *)pfont;
+ cff_writer_t writer;
+ cff_glyph_subset_t subset;
+ cff_string_item_t *std_string_items;
+ cff_string_item_t *string_items;
+ gs_const_string font_name;
+ stream poss;
+ uint charstrings_count, charstrings_size;
+ uint subrs_count, subrs_size;
+ uint gsubrs_count, gsubrs_size, encoding_size, charset_size;
+ uint number_of_glyphs = 0, number_of_strings;
+ /*
+ * Set the offsets and sizes to the largest reasonable values
+ * (see below).
+ */
+ uint
+ Top_size = 0x7fffff,
+ GSubrs_offset,
+ Encoding_offset,
+ charset_offset,
+ CharStrings_offset,
+ Private_offset,
+ Private_size = 0x7fffff,
+ Subrs_offset,
+ End_offset = 0x7fffff;
+ int j;
+ psf_glyph_enum_t genum;
+ gs_glyph glyph;
+ long start_pos;
+ uint offset;
+ int code;
+
+ /* Allocate the string tables. */
+ psf_enumerate_glyphs_begin(&genum, (gs_font *)pfont,
+ NULL, 0, GLYPH_SPACE_NAME);
+ while ((code = psf_enumerate_glyphs_next(&genum, &glyph)) != 1)
+ number_of_glyphs++;
+ subset.glyphs.subset_data = (gs_glyph *)gs_alloc_bytes(pfont->memory,
+ number_of_glyphs * sizeof(glyph), "psf_write_type2_font");
+ number_of_strings = number_of_glyphs + MAX_CFF_MISC_STRINGS;
+ std_string_items = (cff_string_item_t *)gs_alloc_bytes(pfont->memory,
+ (MAX_CFF_STD_STRINGS + number_of_strings) * sizeof(cff_string_item_t),
+ "psf_write_type2_font");
+ if (std_string_items == NULL || subset.glyphs.subset_data == NULL)
+ return_error(gs_error_VMerror);
+ string_items = std_string_items + MAX_CFF_STD_STRINGS;
+
+ /* Get subset glyphs. */
+ code = psf_get_type1_glyphs(&subset.glyphs, pfont, subset_glyphs,
+ subset_size);
+ if (code < 0)
+ return code;
+ if (subset.glyphs.notdef == gs_no_glyph)
+ return_error(gs_error_rangecheck); /* notdef is required */
+
+ /* If we're writing Type 2 CharStrings, don't encrypt them. */
+ if (options & WRITE_TYPE2_CHARSTRINGS) {
+ options |= WRITE_TYPE2_NO_LENIV;
+ if (pfont->FontType != ft_encrypted2)
+ pfont->data.defaultWidthX = pfont->data.nominalWidthX = 0;
+ }
+ writer.options = options;
+ s_init(&poss, NULL);
+ swrite_position_only(&poss);
+ writer.strm = &poss;
+ writer.pfont = pbfont;
+ writer.glyph_data = psf_type1_glyph_data;
+ writer.offset_size = 1; /* arbitrary */
+ writer.start_pos = stell(s);
+ writer.FontBBox = *FontBBox;
+
+ /* Initialize the enumeration of the glyphs. */
+ psf_enumerate_glyphs_begin(&genum, (gs_font *)pfont,
+ subset.glyphs.subset_glyphs,
+ (subset.glyphs.subset_glyphs ?
+ subset.glyphs.subset_size : 0),
+ GLYPH_SPACE_NAME);
+
+ /* Shuffle the glyphs into the order .notdef, encoded, unencoded. */
+ {
+ gs_glyph encoded[256];
+ int num_enc, num_enc_chars;
+
+ /* Get the list of encoded glyphs. */
+ for (j = 0, num_enc_chars = 0; j < 256; ++j) {
+ glyph = pfont->procs.encode_char((gs_font *)pfont, (gs_char)j,
+ GLYPH_SPACE_NAME);
+ if (glyph != gs_no_glyph && glyph != subset.glyphs.notdef &&
+ (subset.glyphs.subset_glyphs == 0 ||
+ psf_sorted_glyphs_include(subset.glyphs.subset_data,
+ subset.glyphs.subset_size, glyph)))
+ encoded[num_enc_chars++] = glyph;
+ }
+ subset.num_encoded_chars = num_enc_chars;
+ subset.num_encoded = num_enc =
+ psf_sort_glyphs(encoded, num_enc_chars);
+
+ /* Get the complete list of glyphs if we don't have it already. */
+ if (!subset.glyphs.subset_glyphs) {
+ int num_glyphs = 0;
+
+ psf_enumerate_glyphs_reset(&genum);
+ while ((code = psf_enumerate_glyphs_next(&genum, &glyph)) != 1)
+ if (code == 0) {
+ if (num_glyphs == number_of_glyphs)
+ return_error(gs_error_limitcheck);
+ subset.glyphs.subset_data[num_glyphs++] = glyph;
+ }
+ subset.glyphs.subset_size =
+ psf_sort_glyphs(subset.glyphs.subset_data, num_glyphs);
+ subset.glyphs.subset_glyphs = subset.glyphs.subset_data;
+ }
+
+ /* Move the unencoded glyphs to the top of the list. */
+ /*
+ * We could do this in time N rather than N log N with a two-finger
+ * algorithm, but it doesn't seem worth the trouble right now.
+ */
+ {
+ int from = subset.glyphs.subset_size;
+ int to = from;
+
+ while (from > 0) {
+ glyph = subset.glyphs.subset_data[--from];
+ if (glyph != subset.glyphs.notdef &&
+ !psf_sorted_glyphs_include(encoded, num_enc, glyph))
+ subset.glyphs.subset_data[--to] = glyph;
+ }
+#ifdef DEBUG
+ if (to != num_enc + 1)
+ lprintf2("to = %d, num_enc + 1 = %d\n", to, num_enc + 1);
+#endif
+ }
+
+ /* Move .notdef and the encoded glyphs to the bottom of the list. */
+ subset.glyphs.subset_data[0] = subset.glyphs.notdef;
+ memcpy(subset.glyphs.subset_data + 1, encoded,
+ sizeof(encoded[0]) * num_enc);
+ }
+
+ /* Set the font name. */
+ if (alt_font_name)
+ font_name = *alt_font_name;
+ else
+ font_name.data = pfont->font_name.chars,
+ font_name.size = pfont->font_name.size;
+
+ /* Initialize the string tables. */
+ cff_string_table_init(&writer.std_strings, std_string_items,
+ MAX_CFF_STD_STRINGS);
+ for (j = 0; (glyph = gs_c_known_encode((gs_char)j,
+ ENCODING_INDEX_CFFSTRINGS)) != gs_no_glyph;
+ ++j) {
+ gs_const_string str;
+ int ignore;
+
+ gs_c_glyph_name(glyph, &str);
+ cff_string_index(&writer.std_strings, str.data, str.size, true,
+ &ignore);
+ }
+ cff_string_table_init(&writer.strings, string_items, number_of_strings);
+
+ /* Enter miscellaneous strings in the string table. */
+ cff_write_Top_font(&writer, 0, 0, 0, 0, 0);
+
+ /* Enter the glyph names in the string table. */
+ /* (Note that we have changed the glyph list.) */
+ psf_enumerate_glyphs_begin(&genum, (gs_font *)pfont,
+ subset.glyphs.subset_data,
+ subset.glyphs.subset_size,
+ GLYPH_SPACE_NAME);
+ while ((code = psf_enumerate_glyphs_next(&genum, &glyph)) != 1)
+ if (code == 0) {
+ code = cff_glyph_sid(&writer, glyph);
+ if (code < 0)
+ return code;
+ }
+
+ /*
+ * The CFF specification says that the Encoding, charset, CharStrings,
+ * Private, and Local Subr sections may be in any order. To minimize
+ * the risk of incompatibility with Adobe software, we produce them in
+ * the order just mentioned.
+ */
+
+ /*
+ * Compute the size of the GSubrs Index, if not omitted.
+ */
+ if ((options & WRITE_TYPE2_NO_GSUBRS) != 0 ||
+ cff_convert_charstrings(&writer, pbfont) /* we expand all Subrs */
+ )
+ gsubrs_count = 0, gsubrs_size = 0;
+ else
+ gsubrs_size = cff_write_Subrs_offsets(&writer, &gsubrs_count,
+ pfont, true);
+
+ /*
+ * Compute the size of the Encoding. For simplicity, we currently
+ * always store the Encoding explicitly. Note that because CFF stores
+ * the Encoding in an "inverted" form, we need to count the number of
+ * glyphs that occur at more than one place in the Encoding.
+ */
+ encoding_size = cff_Encoding_size(subset.num_encoded,
+ subset.num_encoded_chars);
+
+ /*
+ * Compute the size of the charset. For simplicity, we currently
+ * always store the charset explicitly.
+ */
+ charset_size = 1 + (subset.glyphs.subset_size - 1) * 2;
+
+ /* Compute the size of the CharStrings Index. */
+ code = cff_write_CharStrings_offsets(&writer, &genum, &charstrings_count);
+ if (code < 0)
+ return code;
+ charstrings_size = (uint)code;
+
+ /* Compute the size of the (local) Subrs Index. */
+#ifdef SKIP_EMPTY_SUBRS
+ subrs_size =
+ (cff_convert_charstrings(&writer, pbfont) ? 0 :
+ cff_write_Subrs_offsets(&writer, &subrs_count, pfont, false));
+#else
+ if (cff_convert_charstrings(&writer, pbfont))
+ subrs_count = 0; /* we expand all Subrs */
+ subrs_size = cff_write_Subrs_offsets(&writer, &subrs_count, pfont, false);
+#endif
+
+ /*
+ * The offsets of the Private Dict and the CharStrings Index
+ * depend on the size of the Top Dict; the offset of the Subrs also
+ * depends on the size of the Private Dict. However, the size of the
+ * Top Dict depends on the offsets of the CharStrings Index, the
+ * charset, and the Encoding, and on the offset and size of the Private
+ * Dict, because of the variable-length encoding of the offsets and
+ * size; for the same reason, the size of the Private Dict depends on
+ * the offset of the Subrs. Fortunately, the relationship between the
+ * value of an offset or size and the size of its encoding is monotonic.
+ * Therefore, we start by assuming the largest reasonable value for all
+ * the sizes and iterate until everything converges.
+ */
+ iter:
+ swrite_position_only(&poss);
+ writer.strm = &poss;
+
+ /* Compute the offsets. */
+ GSubrs_offset = 4 + cff_Index_size(1, font_name.size) +
+ cff_Index_size(1, Top_size) +
+ cff_Index_size(writer.strings.count, writer.strings.total);
+ Encoding_offset = GSubrs_offset +
+ cff_Index_size(gsubrs_count, gsubrs_size);
+ charset_offset = Encoding_offset + encoding_size;
+ CharStrings_offset = charset_offset + charset_size;
+ Private_offset = CharStrings_offset +
+ cff_Index_size(charstrings_count, charstrings_size);
+ Subrs_offset = Private_size; /* relative to Private Dict */
+
+ write:
+ if(check_ioerror(writer.strm))
+ return_error(gs_error_ioerror);
+ start_pos = stell(writer.strm);
+ /* Write the header, setting offset_size. */
+ cff_write_header(&writer, End_offset);
+
+ /* Write the names Index. */
+ cff_put_Index_header(&writer, 1, font_name.size);
+ put_offset(&writer, font_name.size + 1);
+ put_bytes(writer.strm, font_name.data, font_name.size);
+
+ /* Write the Top Index. */
+ cff_put_Index_header(&writer, 1, Top_size);
+ put_offset(&writer, Top_size + 1);
+ offset = stell(writer.strm) - start_pos;
+ cff_write_Top_font(&writer, Encoding_offset, charset_offset,
+ CharStrings_offset,
+ Private_offset, Private_size);
+ Top_size = stell(writer.strm) - start_pos - offset;
+
+ /* Write the strings Index. */
+ cff_put_Index(&writer, &writer.strings);
+ if(check_ioerror(writer.strm))
+ return_error(gs_error_ioerror);
+
+ /* Write the GSubrs Index, if any, checking the offset. */
+ offset = stell(writer.strm) - start_pos;
+ if_debug2m('l', s->memory, "[l]GSubrs = %u => %u\n", GSubrs_offset, offset);
+ if (offset > GSubrs_offset)
+ return_error(gs_error_rangecheck);
+ GSubrs_offset = offset;
+ if (gsubrs_count == 0 || cff_convert_charstrings(&writer, pbfont))
+ cff_put_Index_header(&writer, 0, 0);
+ else
+ cff_write_Subrs(&writer, gsubrs_count, gsubrs_size, pfont, true);
+
+ /* Write the Encoding. */
+ cff_write_Encoding(&writer, &subset);
+
+ /* Write the charset. */
+ cff_write_charset(&writer, &subset);
+
+ /* Write the CharStrings Index, checking the offset. */
+ offset = stell(writer.strm) - start_pos;
+ if (offset > CharStrings_offset)
+ return_error(gs_error_rangecheck);
+ CharStrings_offset = offset;
+ cff_write_CharStrings(&writer, &genum, charstrings_count,
+ charstrings_size);
+ if(check_ioerror(writer.strm))
+ return_error(gs_error_ioerror);
+
+ /* Write the Private Dict, checking the offset. */
+ offset = stell(writer.strm) - start_pos;
+ if (offset > Private_offset)
+ return_error(gs_error_rangecheck);
+ Private_offset = offset;
+ cff_write_Private(&writer, (subrs_size == 0 ? 0 : Subrs_offset), pfont);
+ Private_size = stell(writer.strm) - start_pos - offset;
+
+ /* Write the Subrs Index, checking the offset. */
+ offset = stell(writer.strm) - (start_pos + Private_offset);
+ if (offset > Subrs_offset)
+ return_error(gs_error_rangecheck);
+ Subrs_offset = offset;
+ if (cff_convert_charstrings(&writer, pbfont))
+ cff_put_Index_header(&writer, 0, 0);
+ else if (subrs_size != 0)
+ cff_write_Subrs(&writer, subrs_count, subrs_size, pfont, false);
+
+ /* Check the final offset. */
+ if(check_ioerror(writer.strm))
+ return_error(gs_error_ioerror);
+ offset = stell(writer.strm) - start_pos;
+ if (offset > End_offset)
+ return_error(gs_error_rangecheck);
+ if (offset == End_offset) {
+ /* The iteration has converged. Write the result. */
+ if (writer.strm == &poss) {
+ writer.strm = s;
+ goto write;
+ }
+ } else {
+ /* No convergence yet. */
+ End_offset = offset;
+ goto iter;
+ }
+
+ /* All done. */
+ gs_free_object(pfont->memory, std_string_items, "psf_write_type2_font");
+ gs_free_object(pfont->memory, subset.glyphs.subset_data, "psf_write_type2_font");
+ return 0;
+}
+
+/* Write the CFF definition of a CIDFontType 0 font (CIDFont). */
+static int
+cid0_glyph_data(gs_font_base *pbfont, gs_glyph glyph, gs_glyph_data_t *pgd,
+ gs_font_type1 **ppfont)
+{
+ gs_font_cid0 *const pfont = (gs_font_cid0 *)pbfont;
+ int font_index;
+ int code = pfont->cidata.glyph_data(pbfont, glyph, pgd, &font_index);
+
+ if (code >= 0)
+ *ppfont = pfont->cidata.FDArray[font_index];
+ return code;
+}
+#ifdef DEBUG
+static int
+offset_error(const char *msg)
+{
+ if_debug1('l', "[l]%s offset error\n", msg);
+ return gs_error_rangecheck;
+}
+#else
+# define offset_error(msg) gs_error_rangecheck
+#endif
+int
+psf_write_cid0_font(stream *s, gs_font_cid0 *pfont, int options,
+ const byte *subset_cids, uint subset_size,
+ const gs_const_string *alt_font_name)
+{
+ /*
+ * CIDFontType 0 fonts differ from ordinary Type 1 / Type 2 fonts
+ * as follows:
+ * The TOP Dict starts with a ROS operator.
+ * The TOP Dict must include FDArray and FDSelect operators.
+ * The TOP Dict may include CIDFontVersion, CIDFontRevision,
+ * CIDFontType, CIDCount, and UIDBase operators.
+ * The TOP Dict must not include an Encoding operator.
+ * The charset is defined in terms of CIDs rather than SIDs.
+ * FDArray references a Font Index in which each element is a Dict
+ * defining a font without charset, Encoding, or CharStrings.
+ * FDSelect references a structure mapping CIDs to font numbers.
+ */
+ gs_font_base *const pbfont = (gs_font_base *)pfont;
+ cff_writer_t writer;
+ cff_string_item_t std_string_items[500]; /* 391 entries used */
+ /****** HOW TO DETERMINE THE SIZE OF STRINGS? ******/
+ cff_string_item_t string_items[500 /* character names */ +
+ 40 /* misc. values */];
+ gs_const_string font_name;
+ stream poss;
+ uint charstrings_count, charstrings_size;
+ uint gsubrs_count, gsubrs_size;
+ uint charset_size, fdselect_size, fdselect_format;
+ uint subrs_count[256], subrs_size[256];
+ /*
+ * Set the offsets and sizes to the largest reasonable values
+ * (see below).
+ */
+ uint
+ Top_size = 0x7fffff,
+ GSubrs_offset = 0x1ffffff,
+ charset_offset = 0x1ffffff,
+ FDSelect_offset = 0x1ffffff,
+ CharStrings_offset = 0x1ffffff,
+ Font_offset = 0x1ffffff,
+ FDArray_offsets[257],
+ Private_offsets[257],
+ Subrs_offsets[257],
+ End_offset = 0x1ffffff;
+ int j;
+ psf_glyph_enum_t genum;
+ gs_font_info_t info;
+ long start_pos;
+ uint offset;
+ int num_fonts = pfont->cidata.FDArray_size;
+ int code;
+
+ /* Initialize the enumeration of the glyphs. */
+ psf_enumerate_cids_begin(&genum, (gs_font *)pfont, subset_cids,
+ subset_size);
+
+ /* Check that the font can be written. */
+ code = psf_check_outline_glyphs((gs_font_base *)pfont, &genum,
+ cid0_glyph_data);
+ if (code < 0)
+ return code;
+ /* The .notdef glyph (glyph 0) must be included. */
+ if (subset_cids && subset_size > 0 && !(subset_cids[0] & 0x80))
+ return_error(gs_error_rangecheck);
+
+ writer.options = options;
+ s_init(&poss, NULL);
+ swrite_position_only(&poss);
+ writer.strm = &poss;
+ writer.pfont = pbfont;
+ writer.glyph_data = cid0_glyph_data;
+ writer.offset_size = 1; /* arbitrary */
+ writer.start_pos = stell(s);
+ writer.FontBBox.p.x = writer.FontBBox.p.y = 0;
+ writer.FontBBox.q.x = writer.FontBBox.q.y = 0;
+
+ /* Set the font name. */
+ if (alt_font_name)
+ font_name = *alt_font_name;
+ else if (pfont->font_name.size)
+ font_name.data = pfont->font_name.chars,
+ font_name.size = pfont->font_name.size;
+ else
+ font_name.data = pfont->key_name.chars,
+ font_name.size = pfont->key_name.size;
+
+ /* Initialize the string tables. */
+ cff_string_table_init(&writer.std_strings, std_string_items,
+ countof(std_string_items));
+ cff_string_table_init(&writer.strings, string_items,
+ countof(string_items));
+
+ /* Make all entries in the string table. */
+ cff_write_ROS(&writer, &pfont->cidata.common.CIDSystemInfo);
+ for (j = 0; j < num_fonts; ++j) {
+ gs_font_type1 *pfd = pfont->cidata.FDArray[j];
+
+ cff_write_Top_fdarray(&writer, (gs_font_base *)pfd, 0, 0);
+ }
+
+ /*
+ * The CFF specification says that sections after the initial Indexes
+ * may be in any order. To minimize the risk of incompatibility with
+ * Adobe software, we produce them in the order illustrated in the
+ * specification.
+ */
+
+ /* Initialize the offset arrays. */
+ for (j = 0; j <= num_fonts; ++j)
+ FDArray_offsets[j] = Private_offsets[j] = Subrs_offsets[j] =
+ 0x7effffff / num_fonts * j + 0x1000000;
+
+ /*
+ * Compute the size of the GSubrs Index, if not omitted.
+ * Arbitrarily use FDArray[0] to access the GSubrs and to determine
+ * the CharString type.
+ */
+ if ((options & WRITE_TYPE2_NO_GSUBRS) != 0 ||
+ cff_convert_charstrings(&writer,
+ (const gs_font_base *)pfont->cidata.FDArray[0])
+ /* we expand all Subrs */
+ )
+ gsubrs_count = 0, gsubrs_size = 0;
+ else
+ gsubrs_size = cff_write_Subrs_offsets(&writer, &gsubrs_count,
+ pfont->cidata.FDArray[0], true);
+
+ /*
+ * Compute the size of the charset. For simplicity, we currently
+ * always store the charset explicitly.
+ */
+ swrite_position_only(&poss);
+ cff_write_cidset(&writer, &genum);
+ charset_size = stell(&poss);
+
+ /* Compute the size of the FDSelect strucure. */
+ fdselect_size = cff_FDSelect_size(&writer, &genum, &fdselect_format);
+
+ /* Compute the size of the CharStrings Index. */
+ /* Compute the size of the CharStrings Index. */
+ code = cff_write_CharStrings_offsets(&writer, &genum, &charstrings_count);
+ if (code < 0)
+ return code;
+ charstrings_size = (uint)code;
+
+ /* Compute the size of the (local) Subrs Indexes. */
+ for (j = 0; j < num_fonts; ++j) {
+ gs_font_type1 *pfd = pfont->cidata.FDArray[j];
+
+#ifdef SKIP_EMPTY_SUBRS
+ subrs_size[j] =
+ (cff_convert_charstrings(&writer, (gs_font_base *)pfd) ? 0 :
+ cff_write_Subrs_offsets(&writer, &subrs_count[j], pfd, false));
+#else
+ if (cff_convert_charstrings(&writer, (gs_font_base *)pfd))
+ subrs_count[j] = 0; /* we expand all Subrs */
+ subrs_size[j] = cff_write_Subrs_offsets(&writer, &subrs_count[j], pfd, false);
+#endif
+ }
+
+ /* Get the font_info once, since it may be expensive. */
+ cff_get_Top_info_common(&writer, (gs_font_base *)pfont, true, &info);
+
+ /*
+ * The offsets of the Private Dict and the CharStrings Index
+ * depend on the size of the Top Dict; the offset of the Subrs also
+ * depends on the size of the Private Dict. However, the size of the
+ * Top Dict depends on the offsets of the CharStrings Index and the
+ * charset, and on the offset and size of the Private Dict,
+ * because of the variable-length encoding of the offsets and
+ * size; for the same reason, the size of the Private Dict depends on
+ * the offset of the Subrs. Fortunately, the relationship between the
+ * value of an offset or size and the size of its encoding is monotonic.
+ * Therefore, we start by assuming the largest reasonable value for all
+ * the sizes and iterate until everything converges.
+ */
+ iter:
+ swrite_position_only(&poss);
+ writer.strm = &poss;
+
+ /* Compute the offsets. */
+ GSubrs_offset = 4 + cff_Index_size(1, font_name.size) +
+ cff_Index_size(1, Top_size) +
+ cff_Index_size(writer.strings.count, writer.strings.total);
+ charset_offset = GSubrs_offset +
+ cff_Index_size(gsubrs_count, gsubrs_size);
+ FDSelect_offset = charset_offset + charset_size;
+ CharStrings_offset = FDSelect_offset + fdselect_size;
+ if_debug4m('l', s->memory,
+ "[l]GSubrs at %u, charset at %u, FDSelect at %u, CharStrings at %u\n",
+ GSubrs_offset, charset_offset, FDSelect_offset, CharStrings_offset);
+
+ write:
+ start_pos = stell(writer.strm);
+ if_debug1m('l', s->memory, "[l]start_pos = %ld\n", start_pos);
+ /* Write the header, setting offset_size. */
+ cff_write_header(&writer, End_offset);
+
+ /* Write the names Index. */
+ cff_put_Index_header(&writer, 1, font_name.size);
+ put_offset(&writer, font_name.size + 1);
+ put_bytes(writer.strm, font_name.data, font_name.size);
+
+ /* Write the Top Index. */
+ cff_put_Index_header(&writer, 1, Top_size);
+ put_offset(&writer, Top_size + 1);
+ offset = stell(writer.strm) - start_pos;
+ cff_write_Top_cidfont(&writer, charset_offset, CharStrings_offset,
+ FDSelect_offset, Font_offset, &info);
+ Top_size = stell(writer.strm) - start_pos - offset;
+ if_debug1m('l', s->memory, "[l]Top_size = %u\n", Top_size);
+
+ /* Write the strings Index. */
+ cff_put_Index(&writer, &writer.strings);
+
+ /* Write the GSubrs Index, if any, checking the offset. */
+ offset = stell(writer.strm) - start_pos;
+ if_debug2m('l', s->memory, "[l]GSubrs = %u => %u\n", GSubrs_offset, offset);
+ if (offset > GSubrs_offset)
+ return_error(gs_error_rangecheck);
+ GSubrs_offset = offset;
+ if (gsubrs_count == 0 ||
+ cff_convert_charstrings(&writer,
+ (const gs_font_base *)pfont->cidata.FDArray[0])
+ )
+ cff_put_Index_header(&writer, 0, 0);
+ else
+ cff_write_Subrs(&writer, gsubrs_count, gsubrs_size,
+ pfont->cidata.FDArray[0], true);
+
+ /* Write the charset. */
+ if_debug1m('l', s->memory, "[l]charset = %"PRId64"\n", (int64_t)(stell(writer.strm) - start_pos));
+ cff_write_cidset(&writer, &genum);
+
+ /* Write the FDSelect structure, checking the offset. */
+ offset = stell(writer.strm) - start_pos;
+ if_debug2m('l', s->memory, "[l]FDSelect = %u => %u\n", FDSelect_offset, offset);
+ if (offset > FDSelect_offset)
+ return_error(offset_error("FDselect"));
+ FDSelect_offset = offset;
+ cff_write_FDSelect(&writer, &genum, fdselect_size, fdselect_format);
+
+ /* Write the CharStrings Index, checking the offset. */
+ offset = stell(writer.strm) - start_pos;
+ if_debug2m('l', s->memory, "[l]CharStrings = %u => %u\n", CharStrings_offset, offset);
+ if (offset > CharStrings_offset)
+ return_error(offset_error("CharStrings"));
+ CharStrings_offset = offset;
+ cff_write_CharStrings(&writer, &genum, charstrings_count,
+ charstrings_size);
+
+ /* Write the Font Dict Index. */
+ offset = stell(writer.strm) - start_pos;
+ if_debug2m('l', s->memory, "[l]Font = %u => %u\n", Font_offset, offset);
+ if (offset > Font_offset)
+ return_error(offset_error("Font"));
+ Font_offset = offset;
+ cff_write_FDArray_offsets(&writer, FDArray_offsets, num_fonts);
+ offset = stell(writer.strm) - start_pos;
+ if_debug2m('l', s->memory, "[l]FDArray[0] = %u => %u\n", FDArray_offsets[0], offset);
+ if (offset > FDArray_offsets[0])
+ return_error(offset_error("FDArray[0]"));
+ FDArray_offsets[0] = offset;
+ for (j = 0; j < num_fonts; ++j) {
+ gs_font_type1 *pfd = pfont->cidata.FDArray[j];
+
+ /* If we're writing Type 2 CharStrings, don't encrypt them. */
+ if (options & WRITE_TYPE2_CHARSTRINGS) {
+ options |= WRITE_TYPE2_NO_LENIV;
+ if (pfd->FontType != ft_encrypted2)
+ pfd->data.defaultWidthX = pfd->data.nominalWidthX = 0;
+ }
+ cff_write_Top_fdarray(&writer, (gs_font_base *)pfd, Private_offsets[j],
+ Private_offsets[j + 1] - Private_offsets[j]);
+ offset = stell(writer.strm) - start_pos;
+ if_debug3m('l', s->memory, "[l]FDArray[%d] = %u => %u\n", j + 1,
+ FDArray_offsets[j + 1], offset);
+ if (offset > FDArray_offsets[j + 1])
+ return_error(offset_error("FDArray"));
+ FDArray_offsets[j + 1] = offset;
+ }
+
+ /* Write the Private Dicts, checking the offset. */
+ for (j = 0; ; ++j) {
+ gs_font_type1 *pfd;
+
+ offset = stell(writer.strm) - start_pos;
+ if_debug3m('l', s->memory, "[l]Private[%d] = %u => %u\n",
+ j, Private_offsets[j], offset);
+ if (offset > Private_offsets[j])
+ return_error(offset_error("Private"));
+ Private_offsets[j] = offset;
+ if (j == num_fonts)
+ break;
+ pfd = pfont->cidata.FDArray[j];
+ cff_write_Private(&writer,
+ (subrs_size[j] == 0 ? 0 : Subrs_offsets[j]), pfd);
+ }
+
+ /* Write the Subrs Indexes, checking the offsets. */
+ for (j = 0; ; ++j) {
+ gs_font_type1 *pfd;
+
+ offset = stell(writer.strm) - (start_pos + Private_offsets[j]);
+ if_debug3m('l', s->memory, "[l]Subrs[%d] = %u => %u\n",
+ j, Subrs_offsets[j], offset);
+ if (offset > Subrs_offsets[j])
+ return_error(offset_error("Subrs"));
+ Subrs_offsets[j] = offset;
+ if (j == num_fonts)
+ break;
+ pfd = pfont->cidata.FDArray[j];
+ if (cff_convert_charstrings(&writer, (gs_font_base *)pfd))
+ cff_put_Index_header(&writer, 0, 0);
+ else if (subrs_size[j] != 0)
+ cff_write_Subrs(&writer, subrs_count[j], subrs_size[j], pfd, false);
+ }
+
+ /* Check the final offset. */
+ offset = stell(writer.strm) - start_pos;
+ if_debug2m('l', s->memory, "[l]End = %u => %u\n", End_offset, offset);
+ if (offset > End_offset)
+ return_error(offset_error("End"));
+ if (offset == End_offset) {
+ /* The iteration has converged. Write the result. */
+ if (writer.strm == &poss) {
+ writer.strm = s;
+ goto write;
+ }
+ } else {
+ /* No convergence yet. */
+ End_offset = offset;
+ goto iter;
+ }
+
+ /* All done. */
+ return 0;
+}
diff --git a/devices/vector/gdevpsfm.c b/devices/vector/gdevpsfm.c
new file mode 100644
index 000000000..052969a40
--- /dev/null
+++ b/devices/vector/gdevpsfm.c
@@ -0,0 +1,318 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Write a CMap */
+#include "gx.h"
+#include "gserrors.h"
+#include "gxfcmap.h"
+#include "stream.h"
+#include "spprint.h"
+#include "spsdf.h"
+#include "gdevpsf.h"
+
+/* ---------------- Utilities ---------------- */
+
+typedef struct cmap_operators_s {
+ const char *beginchar;
+ const char *endchar;
+ const char *beginrange;
+ const char *endrange;
+} cmap_operators_t;
+static const cmap_operators_t
+ cmap_cid_operators = {
+ "begincidchar\n", "endcidchar\n",
+ "begincidrange\n", "endcidrange\n"
+ },
+ cmap_notdef_operators = {
+ "beginnotdefchar\n", "endnotdefchar\n",
+ "beginnotdefrange\n", "endnotdefrange\n"
+ };
+
+/* Write a gs_string with a prefix. */
+static void
+pput_string_entry(stream *s, const char *prefix, const gs_const_string *pstr)
+{
+ stream_puts(s, prefix);
+ stream_write(s, pstr->data, pstr->size);
+}
+
+/* Write a hex string. */
+static void
+pput_hex(stream *s, const byte *pcid, int size)
+{
+ int i;
+ static const char *const hex_digits = "0123456789abcdef";
+
+ for (i = 0; i < size; ++i) {
+ stream_putc(s, hex_digits[pcid[i] >> 4]);
+ stream_putc(s, hex_digits[pcid[i] & 0xf]);
+ }
+}
+
+/* Write a list of code space ranges. */
+static void
+cmap_put_ranges(stream *s, const gx_code_space_range_t *pcsr, int count)
+{
+ int i;
+
+ pprintd1(s, "%d begincodespacerange\n", count);
+ for (i = 0; i < count; ++i, ++pcsr) {
+ stream_puts(s, "<");
+ pput_hex(s, pcsr->first, pcsr->size);
+ stream_puts(s, "><");
+ pput_hex(s, pcsr->last, pcsr->size);
+ stream_puts(s, ">\n");
+ }
+ stream_puts(s, "endcodespacerange\n");
+}
+
+/* Write one CIDSystemInfo dictionary. */
+static void
+cmap_put_system_info(stream *s, const gs_cid_system_info_t *pcidsi)
+{
+ if (cid_system_info_is_null(pcidsi)) {
+ stream_puts(s, " null ");
+ } else {
+ stream_puts(s, " 3 dict dup begin\n");
+ stream_puts(s, "/Registry ");
+ s_write_ps_string(s, pcidsi->Registry.data, pcidsi->Registry.size, 0);
+ stream_puts(s, " def\n/Ordering ");
+ s_write_ps_string(s, pcidsi->Ordering.data, pcidsi->Ordering.size, 0);
+ pprintd1(s, " def\n/Supplement %d def\nend ", pcidsi->Supplement);
+ }
+}
+
+/* Write one code map. */
+static int
+cmap_put_code_map(const gs_memory_t *mem,
+ stream *s, int which, const gs_cmap_t *pcmap,
+ const cmap_operators_t *pcmo,
+ psf_put_name_chars_proc_t put_name_chars,
+ int font_index_only)
+{
+ /* For simplicity, produce one entry for each lookup range. */
+ gs_cmap_lookups_enum_t lenum;
+ int font_index = (pcmap->num_fonts <= 1 ? 0 : -1);
+ int code;
+
+ for (gs_cmap_lookups_enum_init(pcmap, which, &lenum);
+ (code = gs_cmap_enum_next_lookup(&lenum)) == 0; ) {
+ gs_cmap_lookups_enum_t counter;
+ int num_entries = 0;
+ int gi;
+
+ if (font_index_only >= 0 && lenum.entry.font_index != font_index_only)
+ continue;
+ if (font_index_only < 0 && lenum.entry.font_index != font_index) {
+ pprintd1(s, "%d usefont\n", lenum.entry.font_index);
+ font_index = lenum.entry.font_index;
+ }
+ /* Count the number of entries in this lookup range. */
+ counter = lenum;
+ while (gs_cmap_enum_next_entry(&counter) == 0)
+ ++num_entries;
+ for (gi = 0; gi < num_entries; gi += 100) {
+ int i = gi, ni = min(i + 100, num_entries);
+ const char *end;
+
+ pprintd1(s, "%d ", ni - i);
+ if (lenum.entry.key_is_range) {
+ if (lenum.entry.value_type == CODE_VALUE_CID || lenum.entry.value_type == CODE_VALUE_NOTDEF) {
+ stream_puts(s, pcmo->beginrange);
+ end = pcmo->endrange;
+ } else { /* must be def, not notdef */
+ stream_puts(s, "beginbfrange\n");
+ end = "endbfrange\n";
+ }
+ } else {
+ if (lenum.entry.value_type == CODE_VALUE_CID || lenum.entry.value_type == CODE_VALUE_NOTDEF) {
+ stream_puts(s, pcmo->beginchar);
+ end = pcmo->endchar;
+ } else { /* must be def, not notdef */
+ stream_puts(s, "beginbfchar\n");
+ end = "endbfchar\n";
+ }
+ }
+ for (; i < ni; ++i) {
+ int j;
+ long value;
+ int value_size;
+
+ DISCARD(gs_cmap_enum_next_entry(&lenum)); /* can't fail */
+ value_size = lenum.entry.value.size;
+ for (j = 0; j <= lenum.entry.key_is_range; ++j) {
+ stream_putc(s, '<');
+ pput_hex(s, lenum.entry.key[j], lenum.entry.key_size);
+ stream_putc(s, '>');
+ }
+ for (j = 0, value = 0; j < value_size; ++j)
+ value = (value << 8) + lenum.entry.value.data[j];
+ switch (lenum.entry.value_type) {
+ case CODE_VALUE_CID:
+ case CODE_VALUE_NOTDEF:
+ pprintld1(s, "%ld", value);
+ break;
+ case CODE_VALUE_CHARS:
+ stream_putc(s, '<');
+ pput_hex(s, lenum.entry.value.data, value_size);
+ stream_putc(s, '>');
+ break;
+ case CODE_VALUE_GLYPH: {
+ gs_const_string str;
+ int code = pcmap->glyph_name(mem, (gs_glyph)value, &str,
+ pcmap->glyph_name_data);
+
+ if (code < 0)
+ return code;
+ stream_putc(s, '/');
+ code = put_name_chars(s, str.data, str.size);
+ if (code < 0)
+ return code;
+ }
+ break;
+ default: /* not possible */
+ return_error(gs_error_unregistered);
+ }
+ stream_putc(s, '\n');
+ }
+ stream_puts(s, end);
+ }
+ }
+ return code;
+}
+
+/* ---------------- Main program ---------------- */
+
+/* Write a CMap in its standard (source) format. */
+int
+psf_write_cmap(const gs_memory_t *mem,
+ stream *s, const gs_cmap_t *pcmap,
+ psf_put_name_chars_proc_t put_name_chars,
+ const gs_const_string *alt_cmap_name, int font_index_only)
+{
+ const gs_const_string *const cmap_name =
+ (alt_cmap_name ? alt_cmap_name : &pcmap->CMapName);
+ const gs_cid_system_info_t *const pcidsi = pcmap->CIDSystemInfo;
+
+ switch (pcmap->CMapType) {
+ case 0: case 1: case 2:
+ break;
+ default:
+ return_error(gs_error_rangecheck);
+ }
+
+ /* Write the header. */
+
+ if (!pcmap->ToUnicode) {
+ stream_puts(s, "%!PS-Adobe-3.0 Resource-CMap\n");
+ stream_puts(s, "%%DocumentNeededResources: ProcSet (CIDInit)\n");
+ stream_puts(s, "%%IncludeResource: ProcSet (CIDInit)\n");
+ pput_string_entry(s, "%%BeginResource: CMap (", cmap_name);
+ pput_string_entry(s, ")\n%%Title: (", cmap_name);
+ pput_string_entry(s, " ", &pcidsi->Registry);
+ pput_string_entry(s, " ", &pcidsi->Ordering);
+ pprintd1(s, " %d)\n", pcidsi->Supplement);
+ pprintg1(s, "%%%%Version: %g\n", pcmap->CMapVersion);
+ }
+ stream_puts(s, "/CIDInit /ProcSet findresource begin\n");
+ stream_puts(s, "12 dict begin\nbegincmap\n");
+
+ /* Write the fixed entries. */
+
+ pprintd1(s, "/CMapType %d def\n", pcmap->CMapType);
+ stream_puts(s, "/CMapName/");
+ put_name_chars(s, cmap_name->data, cmap_name->size);
+ stream_puts(s, " def\n");
+ if (!pcmap->ToUnicode) {
+ pprintg1(s, "/CMapVersion %g def\n", pcmap->CMapVersion);
+ stream_puts(s, "/CIDSystemInfo");
+ if (font_index_only >= 0 && font_index_only < pcmap->num_fonts) {
+ cmap_put_system_info(s, pcidsi + font_index_only);
+ } else if (pcmap->num_fonts == 1) {
+ cmap_put_system_info(s, pcidsi);
+ } else {
+ int i;
+
+ pprintd1(s, " %d array\n", pcmap->num_fonts);
+ for (i = 0; i < pcmap->num_fonts; ++i) {
+ pprintd1(s, "dup %d", i);
+ cmap_put_system_info(s, pcidsi + i);
+ stream_puts(s, "put\n");
+ }
+ }
+ stream_puts(s, " def\n");
+ if (uid_is_XUID(&pcmap->uid)) {
+ uint i, n = uid_XUID_size(&pcmap->uid);
+ const long *values = uid_XUID_values(&pcmap->uid);
+
+ stream_puts(s, "/XUID [");
+ for (i = 0; i < n; ++i)
+ pprintld1(s, " %ld", values[i]);
+ stream_puts(s, "] def\n");
+ }
+ pprintld1(s, "/UIDOffset %ld def\n", pcmap->UIDOffset);
+ pprintd1(s, "/WMode %d def\n", pcmap->WMode);
+ }
+
+ /* Write the code space ranges. */
+
+ {
+ gs_cmap_ranges_enum_t renum;
+#define MAX_RANGES 100
+ gx_code_space_range_t ranges[MAX_RANGES];
+ int code, count = 0;
+
+ for (gs_cmap_ranges_enum_init(pcmap, &renum);
+ (code = gs_cmap_enum_next_range(&renum)) == 0; ) {
+ if (count == MAX_RANGES) {
+ cmap_put_ranges(s, ranges, count);
+ count = 0;
+ }
+ ranges[count++] = renum.range;
+ }
+ if (code < 0)
+ return code;
+ if (count)
+ cmap_put_ranges(s, ranges, count);
+#undef MAX_RANGES
+ }
+
+ /* Write the code and notdef data. */
+
+ {
+ int code;
+
+ code = cmap_put_code_map(mem, s, 1, pcmap, &cmap_notdef_operators,
+ put_name_chars, font_index_only);
+ if (code < 0)
+ return code;
+ code = cmap_put_code_map(mem, s, 0, pcmap, &cmap_cid_operators,
+ put_name_chars, font_index_only);
+ if (code < 0)
+ return code;
+ }
+
+ /* Write the trailer. */
+
+ stream_puts(s, "endcmap\n");
+ stream_puts(s, "CMapName currentdict /CMap defineresource pop\nend end\n");
+ if (!pcmap->ToUnicode) {
+ stream_puts(s, "%%EndResource\n");
+ stream_puts(s, "%%EOF\n");
+ }
+
+ return 0;
+}
diff --git a/devices/vector/gdevpsft.c b/devices/vector/gdevpsft.c
new file mode 100644
index 000000000..824b0200e
--- /dev/null
+++ b/devices/vector/gdevpsft.c
@@ -0,0 +1,1413 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Write an embedded TrueType font */
+#include "memory_.h"
+#include <stdlib.h> /* for qsort */
+#include <math.h> /* for floor */
+#include "gx.h"
+#include "gscencs.h"
+#include "gserrors.h"
+#include "gsmatrix.h"
+#include "gsutil.h"
+#include "gxfcid.h"
+#include "gxfont.h"
+#include "gxfont42.h"
+#include "gxttf.h"
+#include "stream.h"
+#include "spprint.h"
+#include "gdevpsf.h"
+
+/* Internally used options */
+#define WRITE_TRUETYPE_STRIPPED 0x1000 /* internal */
+#define WRITE_TRUETYPE_CID 0x2000 /* internal */
+
+#define MAX_COMPOSITE_PIECES 3 /* adhoc */
+
+/*
+ * The following are only for debugging. They force various format choices
+ * in the output. The normal (non-debugging) values for all of these are
+ * as indicated in the comments.
+ *
+ * Note that these options interact. Here is the complete list of settings
+ * that make sense.
+ 0 -1,0,1 N/A 0,1 0,1
+ 0xf000 -1 N/A 1 0,1
+ 0xf000 0,1 0,1 1 0,1
+ */
+/* Define whether to use the 0xf000 character bias for generated tables. */
+#define TT_BIAS 0xf000 /* 0xf000 */
+/* Define whether to use cmap format 6 never(-1), sometimes(0), always(1). */
+#define TT_FORCE_CMAP_6 0 /* 0 */
+/* Define whether to use the bias for the cmap format 6 "first code". */
+#define TT_BIAS_CMAP_6 0 /* 0 */
+/* Define whether to generate an OS/2 table if none is supplied. */
+#define TT_GENERATE_OS_2 1 /* 1 */
+/* Define whether to adjust the OS/2 range bits. */
+#define TT_ADJUST_OS_2 1 /* 1 */
+/*
+ * End of options.
+ */
+
+/* ---------------- Utilities ---------------- */
+
+/* Pad to a multiple of 4 bytes. */
+static void
+put_pad(stream *s, uint length)
+{
+ static const byte pad_to_4[3] = {0, 0, 0};
+
+ stream_write(s, pad_to_4, (uint)(-(int)length & 3));
+}
+
+/* Put short and long values on a stream. */
+static void
+put_ushort(stream *s, uint v)
+{
+ stream_putc(s, (byte)(v >> 8));
+ stream_putc(s, (byte)v);
+}
+static void
+put_short(stream *s, short v)
+{
+ stream_putc(s, (byte)(v >> 8));
+ stream_putc(s, (byte)v);
+}
+static void
+put_ulong(stream *s, ulong v)
+{
+ put_ushort(s, (uint)(v >> 16));
+ put_ushort(s, (uint)v);
+}
+static void
+put_loca(stream *s, ulong offset, int indexToLocFormat)
+{
+ if (indexToLocFormat)
+ put_ulong(s, offset);
+ else
+ put_ushort(s, (uint)(offset >> 1));
+}
+
+/* Get or put 2- or 4-byte quantities from/into a table. */
+#define U8(p) ((uint)((p)[0]))
+#define S8(p) (int)((U8(p) ^ 0x80) - 0x80)
+#define U16(p) (((uint)((p)[0]) << 8) + (p)[1])
+#define S16(p) (int)((U16(p) ^ 0x8000) - 0x8000)
+#define u32(p) get_u32_msb(p)
+static void
+put_u16(byte *p, uint v)
+{
+ p[0] = (byte)(v >> 8);
+ p[1] = (byte)v;
+}
+static void
+put_u32(byte *p, ulong v)
+{
+ put_u16(p, (ushort)(v >> 16));
+ put_u16(p + 2, (ushort)v);
+}
+static ulong
+put_table(byte tab[16], const char *tname, ulong checksum, ulong offset,
+ uint length)
+{
+ memcpy(tab, (const byte *)tname, 4);
+ put_u32(tab + 4, checksum);
+ put_u32(tab + 8, offset + 0x40000000);
+ put_u32(tab + 12, (ulong)length);
+ return offset + round_up(length, 4);
+}
+
+/* Write one range of a TrueType font. */
+static int
+write_range(stream *s, gs_font_type42 *pfont, ulong start, uint length)
+{
+ ulong base = start, size = length;
+
+ if_debug3m('l', s->memory, "[l]write_range pos = %"PRId64", start = %"PRIu32", length = %"PRIu32"\n",
+ stell(s), start, length);
+ while (size > 0) {
+ const byte *ptr;
+ int code;
+
+ code = pfont->data.string_proc(pfont, base, size, &ptr);
+ if (code < 0)
+ return code;
+ if (!code)
+ code = size;
+ stream_write(s, ptr, code);
+ base += code;
+ size -= code;
+ }
+ return 0;
+}
+
+/*
+ * Determine the Macintosh glyph number for a given character, if any.
+ * If no glyph can be found, return -1 and store the name in *pstr.
+ */
+static int
+mac_glyph_index(gs_font *font, int ch, gs_const_string *pstr, int *index)
+{
+ gs_glyph glyph = font->procs.encode_char(font, (gs_char)ch,
+ GLYPH_SPACE_NAME);
+ int code;
+
+ if (glyph == gs_no_glyph) {
+ *index = 0;
+ return 0; /* .notdef */
+ }
+ code = font->procs.glyph_name(font, glyph, pstr);
+ if (code < 0)
+ return code;
+ if (glyph < gs_min_cid_glyph) {
+ gs_char mac_char;
+ gs_glyph mac_glyph;
+ gs_const_string mstr;
+
+ /* Look (not very hard) for a match in the Mac glyph space. */
+ if (ch >= 32 && ch <= 126)
+ mac_char = ch - 29;
+ else if (ch >= 128 && ch <= 255)
+ mac_char = ch - 30;
+ else {
+ *index = -1;
+ return 0;
+ }
+ mac_glyph = gs_c_known_encode(mac_char, ENCODING_INDEX_MACGLYPH);
+ if (mac_glyph == gs_no_glyph) {
+ *index = -1;
+ return 0;
+ }
+ code = gs_c_glyph_name(mac_glyph, &mstr);
+ if (code < 0)
+ return code;
+ if (!bytes_compare(pstr->data, pstr->size, mstr.data, mstr.size)) {
+ *index = (int)mac_char;
+ return 0;
+ }
+ }
+ *index = -1;
+ return 0;
+}
+
+/* ---------------- Individual tables ---------------- */
+
+/* ------ cmap ------ */
+
+/* Write a generated cmap table. */
+static const byte cmap_initial_0[] = {
+ 0, 0, /* table version # = 0 */
+ 0, 2, /* # of encoding tables = 2 */
+
+ /* First table, Macintosh */
+ 0, 1, /* platform ID = Macintosh */
+ 0, 0, /* platform encoding ID = ??? */
+ 0, 0, 0, 4+8+8, /* offset to table start */
+ /* Second table, Windows */
+ 0, 3, /* platform ID = Microsoft */
+ 0, 0, /* platform encoding ID = unknown */
+ 0, 0, 1, 4+8+8+6, /* offset to table start */
+
+ /* Start of Macintosh format 0 table */
+ 0, 0, /* format = 0, byte encoding table */
+ 1, 6, /* length */
+ 0, 0 /* version number */
+};
+static const byte cmap_initial_6[] = {
+ 0, 0, /* table version # = 0 */
+ 0, 2, /* # of encoding tables = 2 */
+
+ /* First table, Macintosh */
+ 0, 1, /* platform ID = Macintosh */
+ 0, 0, /* platform encoding ID = ??? */
+ 0, 0, 0, 4+8+8, /* offset to table start */
+ /* Second table, Windows */
+ 0, 3, /* platform ID = Microsoft */
+ 0, 0, /* platform encoding ID = unknown */
+ 0, 0, 0, 4+8+8+10, /* offset to table start */
+ /* *VARIABLE*, add 2 x # of entries */
+
+ /* Start of Macintosh format 6 table */
+ 0, 6, /* format = 6, trimmed table mapping */
+ 0, 10, /* length *VARIABLE*, add 2 x # of entries */
+ 0, 0, /* version number */
+ 0, 0, /* first character code */
+ 0, 0 /* # of entries *VARIABLE* */
+};
+static const byte cmap_unicode_initial_6[] = {
+ 0, 0, /* table version # = 0 */
+ 0, 2, /* # of encoding tables = 2 */
+
+ /* First table, Macintosh */
+ 0, 1, /* platform ID = Macintosh */
+ 0, 0, /* platform encoding ID = ??? */
+ 0, 0, 0, 4+8+8, /* offset to table start */
+ /* Second table, Windows */
+ 0, 3, /* platform ID = Microsoft */
+ 0, 1, /* platform encoding ID = Unicode */
+ 0, 0, 0, 4+8+8+10, /* offset to table start */
+ /* *VARIABLE*, add 2 x # of entries */
+
+ /* Start of Macintosh format 6 table */
+ 0, 6, /* format = 6, trimmed table mapping */
+ 0, 10, /* length *VARIABLE*, add 2 x # of entries */
+ 0, 0, /* version number */
+ 0, 0, /* first character code */
+ 0, 0 /* # of entries *VARIABLE* */
+};
+static const byte cmap_initial_4[] = {
+ 0, 0, /* table version # = 0 */
+ 0, 1, /* # of encoding tables = 2 */
+
+ /* Single table, Windows */
+ 0, 3, /* platform ID = Microsoft */
+ 0, 0, /* platform encoding ID = unknown */
+ 0, 0, 0, 4+8 /* offset to table start */
+};
+static const byte cmap_sub_initial[] = {
+ 0, 4, /* format = 4, segment mapping */
+ 0, 32, /* length ** VARIABLE, add 2 x # of glyphs ** */
+ 0, 0, /* version # */
+ 0, 4, /* 2 x segCount */
+ 0, 4, /* searchRange = 2 x 2 ^ floor(log2(segCount)) */
+ 0, 1, /* floor(log2(segCount)) */
+ 0, 0, /* 2 x segCount - searchRange */
+
+ 0, 0, /* endCount[0] **VARIABLE** */
+ 255, 255, /* endCount[1] */
+ 0, 0, /* reservedPad */
+ 0, 0, /* startCount[0] **VARIABLE** */
+ 255, 255, /* startCount[1] */
+ 0, 0, /* idDelta[0] */
+ 0, 1, /* idDelta[1] */
+ 0, 4, /* idRangeOffset[0] */
+ 0, 0 /* idRangeOffset[1] */
+};
+/*
+ * The following nonsense is required because C defines sizeof()
+ * inconsistently.
+ */
+#define CMAP_ENTRIES_SIZE (256 * 2)
+static void
+write_cmap_0(stream *s, byte* entries /*[CMAP_ENTRIES_SIZE]*/, uint num_glyphs)
+{
+ int i;
+
+ if (CMAP_ENTRIES_SIZE - 2 * num_glyphs>0)
+ memset(entries + 2 * num_glyphs, 0, CMAP_ENTRIES_SIZE - 2 * num_glyphs);
+ stream_write(s, cmap_initial_0, sizeof(cmap_initial_0));
+ for (i = 0; i <= 0xff; ++i)
+ sputc(s, (byte)entries[2 * i + 1]);
+}
+static void
+write_cmap_6(stream *s, byte *entries /*[CMAP_ENTRIES_SIZE]*/, uint first_code,
+ uint first_entry, uint num_entries)
+{
+ byte cmap_data[sizeof(cmap_initial_6)];
+
+ memcpy(cmap_data, cmap_initial_6, sizeof(cmap_initial_6));
+ put_u16(cmap_data + 18,
+ U16(cmap_data + 18) + num_entries * 2); /* offset */
+ put_u16(cmap_data + 22,
+ U16(cmap_data + 22) + num_entries * 2); /* length */
+ put_u16(cmap_data + 26,
+#if TT_BIAS_CMAP_6
+ first_code +
+#endif
+ first_entry);
+ put_u16(cmap_data + 28, num_entries);
+ stream_write(s, cmap_data, sizeof(cmap_data));
+ stream_write(s, entries + first_entry * 2, num_entries * 2);
+}
+static void write_unicode_cmap_6(stream *s, byte *entries, uint first_code,
+ uint first_entry, uint num_entries)
+{
+ byte cmap_data[sizeof(cmap_unicode_initial_6)];
+
+ memcpy(cmap_data, cmap_unicode_initial_6, sizeof(cmap_unicode_initial_6));
+ put_u16(cmap_data + 18,
+ U16(cmap_data + 18) + num_entries * 2); /* offset */
+ put_u16(cmap_data + 22,
+ U16(cmap_data + 22) + num_entries * 2); /* length */
+ put_u16(cmap_data + 26, first_entry);
+ put_u16(cmap_data + 28, num_entries);
+ stream_write(s, cmap_data, sizeof(cmap_data));
+ stream_write(s, entries + first_entry * 2, num_entries * 2);
+}
+static void
+write_cmap(stream *s, gs_font *font, uint first_code, int num_glyphs,
+ gs_glyph max_glyph, int options, uint cmap_length)
+{
+ byte cmap_sub[sizeof(cmap_sub_initial)];
+ byte entries[CMAP_ENTRIES_SIZE];
+ int first_entry = 0, end_entry = num_glyphs;
+ bool can_use_trimmed = !(options & WRITE_TRUETYPE_NO_TRIMMED_TABLE);
+ uint merge = 0;
+ uint num_entries;
+ int i;
+
+ /* Collect the table entries. */
+
+ for (i = 0; i < num_glyphs; ++i) {
+ gs_glyph glyph =
+ font->procs.encode_char(font, (gs_char)i, GLYPH_SPACE_INDEX);
+ uint glyph_index;
+
+ if (glyph == gs_no_glyph || glyph < GS_MIN_GLYPH_INDEX ||
+ glyph > max_glyph
+ )
+ glyph = GS_MIN_GLYPH_INDEX;
+ glyph_index = (uint)(glyph - GS_MIN_GLYPH_INDEX);
+ merge |= glyph_index;
+ put_u16(entries + 2 * i, glyph_index);
+ }
+ while (end_entry > first_entry && !U16(entries + 2 * end_entry - 2))
+ --end_entry;
+ while (first_entry < end_entry && !U16(entries + 2 * first_entry))
+ ++first_entry;
+ num_entries = end_entry - first_entry;
+
+ /* Write the table header and Macintosh sub-table (if any). */
+
+ if (options & WRITE_TRUETYPE_UNICODE_CMAP) {
+ write_unicode_cmap_6(s, entries, first_code, first_entry, num_entries);
+
+ /* Write the Windows sub-table. */
+ memcpy(cmap_sub, cmap_sub_initial, sizeof(cmap_sub_initial));
+ put_u16(cmap_sub + 2, U16(cmap_sub + 2) + num_entries * 2); /* length */
+ put_u16(cmap_sub + 14, end_entry - 1); /* endCount[0] */
+ put_u16(cmap_sub + 20, first_entry); /* startCount[0] */
+ stream_write(s, cmap_sub, sizeof(cmap_sub));
+ stream_write(s, entries + first_entry * 2, num_entries * 2);
+ put_pad(s, cmap_length);
+ return;
+ }
+#if TT_FORCE_CMAP_6 > 0
+ /* Always use format 6. */
+ write_cmap_6(s, entries, first_code, first_entry, num_entries);
+#else
+# if TT_FORCE_CMAP_6 < 0
+ /* Never use format 6. Use format 0 if possible. */
+ if (merge == (byte)merge)
+ write_cmap_0(s, entries, num_glyphs);
+ else
+# else /* TT_FORCE_CMAP == 0 */
+ /*
+ * Use format 0 if possible and (economical or format 6 disallowed),
+ * otherwise format 6 if allowed.
+ */
+ if (merge == (byte)merge && (num_entries <= 127 || !can_use_trimmed))
+ write_cmap_0(s, entries, num_glyphs);
+ else if (can_use_trimmed)
+ write_cmap_6(s, entries, first_code, first_entry, num_entries);
+ else
+# endif
+ {
+ /*
+ * Punt. Acrobat Reader 3 can't handle any other Mac table format.
+ * (AR3 for Linux doesn't seem to be able to handle Windows format,
+ * either, but maybe AR3 for Windows can.)
+ */
+ stream_write(s, cmap_initial_4, sizeof(cmap_initial_4));
+ }
+#endif
+
+ /* Write the Windows sub-table. */
+
+ memcpy(cmap_sub, cmap_sub_initial, sizeof(cmap_sub_initial));
+ put_u16(cmap_sub + 2, U16(cmap_sub + 2) + num_entries * 2); /* length */
+ put_u16(cmap_sub + 14, first_code + end_entry - 1); /* endCount[0] */
+ put_u16(cmap_sub + 20, first_code + first_entry); /* startCount[0] */
+ stream_write(s, cmap_sub, sizeof(cmap_sub));
+ stream_write(s, entries + first_entry * 2, num_entries * 2);
+ put_pad(s, cmap_length);
+}
+static uint
+size_cmap(gs_font *font, uint first_code, int num_glyphs, gs_glyph max_glyph,
+ int options)
+{
+ stream poss;
+
+ s_init(&poss, NULL);
+ swrite_position_only(&poss);
+ write_cmap(&poss, font, first_code, num_glyphs, max_glyph, options, 0);
+ return stell(&poss);
+}
+
+/* ------ hmtx/vmtx ------ */
+
+static void
+write_mtx(stream *s, gs_font_type42 *pfont, const gs_type42_mtx_t *pmtx,
+ int wmode)
+{
+ uint num_metrics = pmtx->numMetrics;
+ uint len = num_metrics * 4;
+ double factor = (double)pfont->data.unitsPerEm * (wmode ? -1 : 1);
+ float sbw[4];
+ uint i;
+
+ sbw[0] = sbw[1] = sbw[2] = sbw[3] = 0; /* in case of failures */
+ for (i = 0; i < pmtx->numMetrics; ++i) {
+ float f;
+ DISCARD(pfont->data.get_metrics(pfont, i, wmode, sbw));
+ /* the temporary assignment to a float is necessary for AIX else the result is always 0 if sbw[] < 0
+ this happens even with gcc and I'm not sure why it happens at all nor why only on AIX */
+ f = (float) (sbw[wmode + 2] * factor); /* width */
+ put_short(s, (short) floor(f + 0.5));
+ f = (float) (sbw[wmode] * factor); /* lsb, may be <0 */
+ put_short(s, (short) floor(f + 0.5));
+ }
+ for (; len < pmtx->length; ++i, len += 2) {
+ float f;
+ DISCARD(pfont->data.get_metrics(pfont, i, wmode, sbw));
+ f = (float) (sbw[wmode] * factor); /* lsb, may be <0 */
+ put_short(s, (short) floor(f + 0.5));
+ }
+}
+
+/* Compute the metrics from the glyph_info. */
+static uint
+size_mtx(gs_font_type42 *pfont, gs_type42_mtx_t *pmtx, uint max_glyph,
+ int wmode)
+{
+ int prev_width = min_int;
+ uint last_width = 0; /* pacify compilers */
+ double factor = pfont->data.unitsPerEm * (wmode ? -1 : 1);
+ uint i;
+
+ for (i = 0; i <= max_glyph; ++i) {
+ float sbw[4];
+ int code = pfont->data.get_metrics(pfont, i, wmode, sbw);
+ int width;
+
+ if (code < 0)
+ continue;
+ width = (int)(sbw[wmode + 2] * factor + 0.5);
+ if (width != prev_width)
+ prev_width = width, last_width = i;
+ }
+ pmtx->numMetrics = last_width + 1;
+ pmtx->length = pmtx->numMetrics * 4 + (max_glyph - last_width) * 2;
+ return pmtx->length;
+}
+
+/* ------ name ------ */
+
+/* Write a generated name table. */
+static const byte name_initial[] = {
+ 0, 0, /* format */
+ 0, 1, /* # of records = 1 */
+ 0, 18, /* start of string storage */
+
+ 0, 2, /* platform ID = ISO */
+ 0, 2, /* encoding ID = ISO 8859-1 */
+ 0, 0, /* language ID (none) */
+ 0, 6, /* name ID = PostScript name */
+ 0, 0, /* length *VARIABLE* */
+ 0, 0 /* start of string within string storage */
+};
+static uint
+size_name(const gs_const_string *font_name)
+{
+ return sizeof(name_initial) + font_name->size;
+}
+static void
+write_name(stream *s, const gs_const_string *font_name)
+{
+ byte name_bytes[sizeof(name_initial)];
+
+ memcpy(name_bytes, name_initial, sizeof(name_initial));
+ put_u16(name_bytes + 14, font_name->size);
+ stream_write(s, name_bytes, sizeof(name_bytes));
+ stream_write(s, font_name->data, font_name->size);
+ put_pad(s, size_name(font_name));
+}
+
+/* ------ OS/2 ------ */
+
+/* Write a generated OS/2 table. */
+#define OS_2_LENGTH1 offset_of(ttf_OS_2_t, sxHeight[0]) /* OS/2 version 1. */
+#define OS_2_LENGTH2 offset_of(ttf_OS_2_t, usLowerOpticalPointSize[0]) /* OS/2 version 2. */
+#define OS_2_LENGTH5 sizeof(ttf_OS_2_t) /* OS/2 version 5 (OpenType 1.7) */
+
+static void
+update_OS_2(ttf_OS_2_t *pos2, uint first_glyph, int num_glyphs)
+{
+ put_u16(pos2->usFirstCharIndex, first_glyph);
+ put_u16(pos2->usLastCharIndex, first_glyph + num_glyphs - 1);
+#if TT_ADJUST_OS_2
+ if (first_glyph >= 0xf000) {
+ /* This font is being treated as a symbolic font. */
+ memset(pos2->ulUnicodeRanges, 0, sizeof(pos2->ulUnicodeRanges));
+ pos2->ulUnicodeRanges[7] = 8; /* bit 60, private use range */
+ memset(pos2->ulCodePageRanges, 0, sizeof(pos2->ulCodePageRanges));
+ pos2->ulCodePageRanges[3] = 1; /* bit 31, symbolic */
+ }
+#endif
+}
+static void
+write_OS_2(stream *s, gs_font *font, uint first_glyph, int num_glyphs)
+{
+ ttf_OS_2_t os2;
+ gs_font_info_t info;
+ int code;
+
+ /*
+ * We don't bother to set most of the fields. The really important
+ * ones, which affect character mapping, are usFirst/LastCharIndex.
+ * We also need to set usWeightClass and usWidthClass to avoid
+ * crashing ttfdump. Version 1 86-byte structure has all the fields
+ * we need.
+ */
+ memset(&os2, 0, sizeof(os2));
+ put_u16(os2.version, 1);
+ put_u16(os2.usWeightClass, 400); /* Normal */
+ put_u16(os2.usWidthClass, 5); /* Normal */
+ update_OS_2(&os2, first_glyph, num_glyphs);
+
+ /*
+ * We should also preserve the licensed embedding rights, to prevent
+ * 'laundering' a TrueType font. These can be non-zero even when embedding is permitted.
+ */
+ memset(&info, 0x00, sizeof(gs_font_info_t));
+ code = font->procs.font_info(font, NULL, FONT_INFO_EMBEDDING_RIGHTS, &info);
+ if (code == 0 && (info.members & FONT_INFO_EMBEDDING_RIGHTS)) {
+ put_u16(os2.fsType, info.EmbeddingRights);
+ }
+
+ stream_write(s, &os2, offset_of(ttf_OS_2_t, sxHeight[0]));
+ put_pad(s, offset_of(ttf_OS_2_t, sxHeight[0]));
+}
+
+/* ------ post ------ */
+
+/* Construct and then write the post table. */
+typedef struct post_glyph_s {
+ byte char_index;
+ byte size;
+ ushort glyph_index;
+} post_glyph_t;
+static int
+compare_post_glyphs(const void *pg1, const void *pg2)
+{
+ gs_glyph g1 = ((const post_glyph_t *)pg1)->glyph_index,
+ g2 = ((const post_glyph_t *)pg2)->glyph_index;
+
+ return (g1 < g2 ? -1 : g1 > g2 ? 1 : 0);
+}
+typedef struct post_s {
+ post_glyph_t glyphs[256 + 1];
+ int count, glyph_count;
+ uint length;
+} post_t;
+
+/*
+ * If necessary, compute the length of the post table. Note that we
+ * only generate post entries for characters in the Encoding.
+ */
+static int
+compute_post(gs_font *font, post_t *post)
+{
+ int i;
+
+ for (i = 0, post->length = 32 + 2; i <= 255; ++i) {
+ gs_const_string str;
+ gs_glyph glyph = font->procs.encode_char(font, (gs_char)i,
+ GLYPH_SPACE_INDEX);
+ int mac_index;
+
+ int code = mac_glyph_index(font, i, &str, &mac_index);
+ if (code < 0)
+ return code;
+ if (mac_index != 0) {
+ post->glyphs[post->count].char_index = i;
+ post->glyphs[post->count].size =
+ (mac_index < 0 ? str.size + 1 : 0);
+ post->glyphs[post->count].glyph_index = glyph - GS_MIN_GLYPH_INDEX;
+ post->count++;
+ }
+ }
+ if (post->count) {
+ int j;
+
+ qsort(post->glyphs, post->count, sizeof(post->glyphs[0]),
+ compare_post_glyphs);
+ /* Eliminate duplicate references to the same glyph. */
+ for (i = j = 0; i < post->count; ++i) {
+ if (i == 0 ||
+ post->glyphs[i].glyph_index !=
+ post->glyphs[i - 1].glyph_index
+ ) {
+ post->length += post->glyphs[i].size;
+ post->glyphs[j++] = post->glyphs[i];
+ }
+ }
+ post->count = j;
+ post->glyph_count = post->glyphs[post->count - 1].glyph_index + 1;
+ }
+ post->length += post->glyph_count * 2;
+ return 0;
+}
+
+/* Write the post table */
+static int
+write_post(stream *s, gs_font *font, post_t *post)
+{
+ byte post_initial[32 + 2];
+ uint name_index;
+ uint glyph_index;
+ int i;
+
+ memset(post_initial, 0, 32);
+ put_u32(post_initial, 0x00020000);
+ put_u16(post_initial + 32, post->glyph_count);
+ stream_write(s, post_initial, sizeof(post_initial));
+
+ /* Write the name index table. */
+
+ for (i = 0, name_index = 258, glyph_index = 0; i < post->count; ++i) {
+ gs_const_string str;
+ int ch = post->glyphs[i].char_index;
+ int mac_index;
+ int code = mac_glyph_index(font, ch, &str, &mac_index);
+
+ if (code < 0)
+ return code;
+ for (; glyph_index < post->glyphs[i].glyph_index; ++glyph_index)
+ put_ushort(s, 0);
+ glyph_index++;
+ if (mac_index >= 0)
+ put_ushort(s, mac_index);
+ else {
+ put_ushort(s, name_index);
+ name_index++;
+ }
+ }
+
+ /* Write the string names of the glyphs. */
+
+ for (i = 0; i < post->count; ++i) {
+ gs_const_string str;
+ int ch = post->glyphs[i].char_index;
+ int mac_index;
+ int code = mac_glyph_index(font, ch, &str, &mac_index);
+
+ if (code < 0)
+ return code;
+ if (mac_index < 0) {
+ spputc(s, (byte)str.size);
+ stream_write(s, str.data, str.size);
+ }
+ }
+ put_pad(s, post->length);
+ return 0;
+}
+
+static inline bool check_position(const gs_memory_t *mem, gs_offset_t pos1, gs_offset_t pos2)
+{
+ if (pos1 == pos2)
+ return false;
+ emprintf2(mem,
+ "Actual TT subtable offset %"PRId64" differs from one in the TT header %"PRId64".\n",
+ pos1,
+ pos2);
+ return true;
+}
+
+static void remove_table(byte *tables, char *tag, uint *numTables)
+{
+ /* Not a high performance implementation because it is called seldom. */
+ int i;
+
+ for (i = 0; i < *numTables;) {
+ byte *tab = tables + i * 16;
+
+ if (!memcmp(tab, tag, 4)) {
+ memmove(tab, tab + 16, 16 * (*numTables - i - 1));
+ --*numTables;
+ } else
+ ++i;
+ }
+}
+
+/* ---------------- Main program ---------------- */
+
+/* Write the definition of a TrueType font. */
+static int
+compare_table_tags(const void *pt1, const void *pt2)
+{
+ ulong t1 = u32(pt1), t2 = u32(pt2);
+
+ return (t1 < t2 ? -1 : t1 > t2 ? 1 : 0);
+}
+static int
+psf_write_truetype_data(stream *s, gs_font_type42 *pfont, int options,
+ psf_glyph_enum_t *penum, bool is_subset,
+ const gs_const_string *alt_font_name)
+{
+ gs_font *const font = (gs_font *)pfont;
+ gs_const_string font_name;
+ byte OffsetTable[12];
+ uint numTables_stored, numTables, numTables_out;
+ byte tables[MAX_NUM_TT_TABLES * 16];
+ uint i;
+ ulong offset;
+ gs_glyph glyph, glyph_prev;
+ ulong max_glyph;
+ uint glyf_length, loca_length;
+ ulong glyf_checksum = 0L; /****** NO CHECKSUM ******/
+ ulong loca_checksum[2] = {0L,0L};
+ ulong glyf_alignment = 0;
+ uint numGlyphs = 0; /* original value from maxp */
+ byte head[56]; /* 0 mod 4 */
+ gs_type42_mtx_t mtx[2];
+ post_t post;
+ ulong head_checksum, file_checksum = 0;
+ int indexToLocFormat = 0;
+ bool
+ writing_cid = (options & WRITE_TRUETYPE_CID) != 0,
+ writing_stripped = (options & WRITE_TRUETYPE_STRIPPED) != 0,
+ generate_mtx = (options & WRITE_TRUETYPE_HVMTX) != 0,
+ no_generate = writing_cid | writing_stripped,
+ have_cmap = no_generate,
+ have_name = !(options & WRITE_TRUETYPE_NAME),
+ have_OS_2 = no_generate,
+ have_post = no_generate;
+ int have_hvhea[2];
+ uint cmap_length = 0;
+ ulong OS_2_start = 0;
+ uint OS_2_length = OS_2_LENGTH1;
+ ulong maxp_start = 0;
+ struct { int glyf, loca, cmap, name, os_2, mtx[2], post, head;
+ } subtable_positions;
+ gs_offset_t start_position = stell(s);
+ int enlarged_numGlyphs = 0;
+ int code;
+ int TTCFontOffset = 0;
+
+ memset(&subtable_positions, 0, sizeof(subtable_positions));
+ have_hvhea[0] = have_hvhea[1] = 0;
+ if (alt_font_name)
+ font_name = *alt_font_name;
+ else
+ font_name.data = font->font_name.chars,
+ font_name.size = font->font_name.size;
+
+ /*
+ * Count the number of tables, including the eventual glyf and loca
+ * (which may not actually be present in the font), and copy the
+ * table directory.
+ */
+
+#define W(a,b,c,d)\
+ ( ((a) << 24) + ((b) << 16) + ((c) << 8) + (d))
+
+ READ_SFNTS(pfont, 0, 12, OffsetTable);
+ if (u32(OffsetTable) == W('t','t','c','f')) {
+ READ_SFNTS(pfont, 12, 4, OffsetTable);
+ TTCFontOffset = u32(OffsetTable);
+ READ_SFNTS(pfont, TTCFontOffset, 12, OffsetTable);
+ }
+ numTables_stored = U16(OffsetTable + 4);
+ for (i = numTables = 0; i < numTables_stored; ++i) {
+ byte tab[16];
+ byte data[54];
+ ulong start;
+ uint length;
+
+ if (numTables == MAX_NUM_TT_TABLES)
+ return_error(gs_error_limitcheck);
+ READ_SFNTS(pfont, TTCFontOffset + 12 + i * 16, 16, tab);
+ start = u32(tab + 8);
+ length = u32(tab + 12);
+ /* Copy the table data now (a rudiment of old code). */
+ memcpy(&tables[numTables * 16], tab, 16);
+
+ switch (u32(tab)) {
+ case W('h','e','a','d'):
+ if (length < 54)
+ return_error(gs_error_invalidfont);
+ length = 54; /* bug 688409 fig2.eps has length=56. */
+ READ_SFNTS(pfont, start, length, data);
+ memcpy(head, data, length);
+ continue;
+ case W('g','l','y','f'): /* synthesized */
+ case W('g','l','y','x'): /* Adobe bogus */
+ case W('l','o','c','a'): /* synthesized */
+ case W('l','o','c','x'): /* Adobe bogus */
+ case W('g','d','i','r'): /* Adobe marker */
+ continue;
+ case W('c','m','a','p'):
+ if (options & (WRITE_TRUETYPE_CMAP | WRITE_TRUETYPE_CID))
+ continue;
+ have_cmap = true;
+ break;
+ case W('m','a','x','p'):
+ READ_SFNTS(pfont, start, length, data);
+ numGlyphs = U16(data + 4);
+ maxp_start = start;
+ break;
+ case W('n','a','m','e'):
+ if (writing_cid)
+ continue;
+ have_name = true;
+ break;
+ case W('O','S','/','2'):
+ if (writing_cid)
+ continue;
+ have_OS_2 = true;
+ if (length > OS_2_LENGTH5)
+ return_error(gs_error_invalidfont);
+ OS_2_start = start;
+ OS_2_length = length;
+ continue;
+ case W('p','o','s','t'):
+ have_post = true;
+ break;
+ case W('h','h','e','a'):
+ have_hvhea[0] = 1;
+ break;
+ case W('v','h','e','a'):
+ have_hvhea[1] = 1;
+ break;
+ case W('h','m','t','x'):
+ case W('v','m','t','x'):
+ if (generate_mtx)
+ continue;
+ /* falls through */
+ case W('c','v','t',' '):
+ case W('f','p','g','m'):
+ case W('g','a','s','p'):
+ case W('k','e','r','n'):
+ case W('p','r','e','p'):
+ break; /* always copy these if present */
+ case W('E','B','D','T'):
+ case W('E','B','L','C'):
+ case W('E','B','S','C'):
+ continue;
+ default:
+ if (writing_cid)
+ continue;
+ break;
+ }
+ numTables++;
+ }
+
+ /*
+ * Enumerate the glyphs to get the size of glyf and loca,
+ * and to compute the checksums for these tables.
+ */
+
+ /****** NO CHECKSUMS YET ******/
+ for (max_glyph = 0, glyf_length = 0;
+ (code = psf_enumerate_glyphs_next(penum, &glyph)) != 1;
+ ) {
+ uint glyph_index;
+ gs_glyph_data_t glyph_data;
+
+ if (glyph < gs_min_cid_glyph)
+ return_error(gs_error_invalidfont);
+ glyph_index = glyph & ~GS_GLYPH_TAG;
+ if_debug1m('L', s->memory, "[L]glyph_index %u\n", glyph_index);
+ glyph_data.memory = pfont->memory;
+ if ((code = pfont->data.get_outline(pfont, glyph_index, &glyph_data)) >= 0) {
+ /* Since indexToLocFormat==0 assumes even glyph lengths,
+ round it up here. If later we choose indexToLocFormat==1,
+ subtract the glyf_alignment to compensate it. */
+ uint l = (glyph_data.bits.size + 1) & ~1;
+
+ max_glyph = max(max_glyph, glyph_index);
+ glyf_length += l;
+ if (l != glyph_data.bits.size)
+ glyf_alignment++;
+ if_debug1m('L', s->memory, "[L] size %u\n", glyph_data.bits.size);
+ gs_glyph_data_free(&glyph_data, "psf_write_truetype_data");
+ }
+ }
+ if (writing_stripped) {
+ glyf_length = 0;
+ loca_length = 0;
+ } else {
+ if (max_glyph + 1 > numGlyphs) {
+ /* Either original font is wrong,
+ or we added glyphs to it due to font merge.
+ Need to adjust maxp, hmtx, vmtx, vdmx, hdmx,
+ assuming that the merge doesn't change hhea
+ and other tables.
+ Since changing hdmx, vdmx is too difficult,
+ and since they're not required for PDF,
+ we'll simply skip them.
+ */
+ enlarged_numGlyphs = max_glyph + 1;
+ if (enlarged_numGlyphs > 0xFFFF) {
+ emprintf1(pfont->memory,
+ "The number of glyphs %d exceeds capability of True Type format.\n",
+ enlarged_numGlyphs);
+ return_error(gs_error_unregistered);
+ }
+ loca_length = (enlarged_numGlyphs + 1) << 2;
+ remove_table(tables, (char *)"hdmx", &numTables);
+ remove_table(tables, (char *)"vdmx", &numTables);
+ } else
+ loca_length = (numGlyphs + 1) << 2;
+ indexToLocFormat = (glyf_length > 0x1fffc);
+ if (!indexToLocFormat)
+ loca_length >>= 1;
+ else
+ glyf_length -= glyf_alignment;
+ /* Acrobat Reader won't accept fonts with empty glyfs. */
+ if (glyf_length == 0)
+ glyf_length = 1;
+ }
+ if_debug2m('l', s->memory, "[l]max_glyph = %lu, glyf_length = %lu\n",
+ (ulong)max_glyph, (ulong)glyf_length);
+
+ /*
+ * If necessary, compute the length of the post table. Note that we
+ * only generate post entries for characters in the Encoding. */
+
+ if (!have_post) {
+ memset(&post, 0, sizeof(post));
+ if (options & WRITE_TRUETYPE_POST) {
+ code = compute_post(font, &post);
+ if (code < 0)
+ return code;
+ } else
+ post.length = 32; /* dummy table */
+ }
+
+ /* Fix up the head table. */
+
+ memset(head + 8, 0, 4);
+ head[50] = 0x00;
+ head[51] = (byte)indexToLocFormat;
+ memset(head + 54, 0, 2);
+ for (head_checksum = 0, i = 0; i < 56; i += 4)
+ head_checksum += u32(&head[i]);
+
+ /*
+ * Construct the table directory, except for glyf, loca, head, OS/2,
+ * and, if necessary, generated cmap, name, and post tables.
+ * Note that the existing directory is already sorted by tag.
+ */
+
+ numTables_out = numTables + 1 /* head */
+ + !writing_stripped * 2 /* glyf, loca */
+ + generate_mtx * (have_hvhea[0] + have_hvhea[1]) /* hmtx, vmtx */
+ + !have_OS_2 /* OS/2 */
+ + !have_cmap + !have_name + !have_post;
+ if (numTables_out >= MAX_NUM_TT_TABLES)
+ return_error(gs_error_limitcheck);
+ offset = 12 + numTables_out * 16;
+ for (i = 0; i < numTables; ++i) {
+ byte *tab = &tables[i * 16];
+ ulong length = u32(tab + 12);
+
+ offset += round_up(length, 4);
+ }
+
+ /* Make the table directory entries for generated tables. */
+
+ {
+ byte *tab = &tables[numTables * 16];
+
+ if (!writing_stripped) {
+ subtable_positions.glyf = offset;
+ offset = put_table(tab, "glyf", glyf_checksum,
+ offset, glyf_length);
+ tab += 16;
+ subtable_positions.loca = offset;
+ offset = put_table(tab, "loca", loca_checksum[indexToLocFormat],
+ offset, loca_length);
+ tab += 16;
+ }
+
+ if (!have_cmap) {
+ cmap_length = size_cmap(font, TT_BIAS, 256,
+ GS_MIN_GLYPH_INDEX + max_glyph, options);
+ subtable_positions.cmap = offset;
+ offset = put_table(tab, "cmap", 0L /****** NO CHECKSUM ******/,
+ offset, cmap_length);
+ tab += 16;
+ }
+
+ if (!have_name) {
+ subtable_positions.name = offset;
+ offset = put_table(tab, "name", 0L /****** NO CHECKSUM ******/,
+ offset, size_name(&font_name));
+ tab += 16;
+ }
+
+ if (!no_generate) {
+ subtable_positions.os_2 = offset;
+ offset = put_table(tab, "OS/2", 0L /****** NO CHECKSUM ******/,
+ offset, OS_2_length);
+ tab += 16;
+ }
+
+ if (generate_mtx)
+ for (i = 0; i < 2; ++i)
+ if (have_hvhea[i]) {
+ subtable_positions.mtx[i] = offset;
+ offset = put_table(tab, (i ? "vmtx" : "hmtx"),
+ 0L /****** NO CHECKSUM ******/,
+ offset,
+ size_mtx(pfont, &mtx[i], max_glyph, i));
+ tab += 16;
+ }
+
+ if (!have_post) {
+ subtable_positions.post = offset;
+ offset = put_table(tab, "post", 0L /****** NO CHECKSUM ******/,
+ offset, post.length);
+ tab += 16;
+ }
+
+ /*
+ * Note that the 'head' table must have length 54, even though
+ * it occupies 56 bytes on the file.
+ */
+ subtable_positions.head = offset;
+ offset = put_table(tab, "head", head_checksum, offset, 54);
+ tab += 16;
+ }
+ numTables = numTables_out;
+
+ /* Write the font header. */
+
+ {
+ static const byte version[4] = {0, 1, 0, 0};
+
+ stream_write(s, version, 4);
+ }
+ put_ushort(s, numTables);
+ for (i = 0; 1 << i <= numTables; ++i)
+ DO_NOTHING;
+ --i;
+ put_ushort(s, 16 << i); /* searchRange */
+ put_ushort(s, i); /* entrySelectors */
+ put_ushort(s, numTables * 16 - (16 << i)); /* rangeShift */
+
+ /* Write the table directory. */
+
+ qsort(tables, numTables, 16, compare_table_tags);
+ offset = 12 + numTables * 16;
+ for (i = 0; i < numTables; ++i) {
+ const byte *tab = &tables[i * 16];
+ byte entry[16];
+
+ memcpy(entry, tab, 16);
+ if (entry[8] < 0x40) {
+ /* Not a generated table. */
+ uint length = u32(tab + 12);
+
+ put_u32(entry + 8, offset);
+ offset += round_up(length, 4);
+ } else {
+ entry[8] -= 0x40;
+ }
+ stream_write(s, entry, 16);
+ }
+
+ /* Write tables other than the ones we generate here. */
+
+ for (i = 0; i < numTables; ++i) {
+ const byte *tab = &tables[i * 16];
+
+ if (tab[8] < 0x40) {
+ ulong start = u32(tab + 8);
+ uint length = u32(tab + 12);
+
+ switch (u32(tab)) {
+ case W('O','S','/','2'):
+ if (!have_cmap) {
+ /*
+ * Adjust the first and last character indices in the OS/2
+ * table to reflect the values in the generated cmap.
+ */
+ byte pos2[OS_2_LENGTH5];
+ ttf_OS_2_t os2;
+
+ READ_SFNTS(pfont, OS_2_start, OS_2_length, pos2);
+ memcpy(&os2, pos2, min(OS_2_length, sizeof(os2)));
+ update_OS_2(&os2, TT_BIAS, 256);
+ stream_write(s, &os2, OS_2_length);
+ put_pad(s, OS_2_length);
+ } else {
+ /* Just copy the existing OS/2 table. */
+ write_range(s, pfont, OS_2_start, OS_2_length);
+ put_pad(s, OS_2_length);
+ }
+ break;
+ case W('m','a','x','p'):
+ if (enlarged_numGlyphs) {
+ /* Must keep the table size. */
+ byte buf[6];
+
+ READ_SFNTS(pfont, maxp_start, sizeof(buf), buf);
+ put_u16(buf + 4, enlarged_numGlyphs);
+ stream_write(s, buf, min(length, sizeof(buf)));
+ if (length > sizeof(buf)) /* Paranoid Safety */
+ write_range(s, pfont, start + sizeof(buf), length - sizeof(buf));
+ } else
+ write_range(s, pfont, start, length);
+ break;
+ case W('h','h','e','a'):
+ case W('v','h','e','a'):
+ if (generate_mtx) {
+ write_range(s, pfont, start, length - 2); /* 34 */
+ put_ushort(s, mtx[tab[0] == 'v'].numMetrics);
+ break;
+ }
+ /* falls through */
+ default:
+ write_range(s, pfont, start, length);
+ }
+ put_pad(s, length);
+ }
+ }
+
+ if (!writing_stripped) {
+ int n = max(numGlyphs, enlarged_numGlyphs) + 1;
+
+ /* Write glyf. */
+
+ if (check_position(pfont->memory,
+ subtable_positions.glyf + start_position,
+ stell(s)))
+ return_error(gs_error_unregistered);
+ psf_enumerate_glyphs_reset(penum);
+ for (offset = 0; psf_enumerate_glyphs_next(penum, &glyph) != 1; ) {
+ gs_glyph_data_t glyph_data;
+
+ glyph_data.memory = pfont->memory;
+ if ((code = pfont->data.get_outline(pfont,
+ glyph & ~GS_GLYPH_TAG,
+ &glyph_data)) >= 0
+ ) {
+ uint l = glyph_data.bits.size, zero = 0;
+
+ if (!indexToLocFormat)
+ l = (l + 1) & ~1;
+ stream_write(s, glyph_data.bits.data, glyph_data.bits.size);
+ if (glyph_data.bits.size < l)
+ stream_write(s, &zero, 1);
+ offset += l;
+ if_debug2m('L', s->memory, "[L]glyf index = %u, size = %u\n",
+ i, glyph_data.bits.size);
+ gs_glyph_data_free(&glyph_data, "psf_write_truetype_data");
+ }
+ }
+ if_debug1m('l', s->memory, "[l]glyf final offset = %lu\n", offset);
+ /* Add a dummy byte if necessary to make glyf non-empty. */
+ while (offset < glyf_length)
+ stream_putc(s, 0), ++offset;
+ put_pad(s, (uint)offset);
+
+ /* Write loca. */
+
+ if (check_position(pfont->memory,
+ subtable_positions.loca + start_position,
+ stell(s)))
+ return_error(gs_error_unregistered);
+ psf_enumerate_glyphs_reset(penum);
+ glyph_prev = 0;
+ for (offset = 0; psf_enumerate_glyphs_next(penum, &glyph) != 1; ) {
+ gs_glyph_data_t glyph_data;
+ uint glyph_index = glyph & ~GS_GLYPH_TAG;
+
+ for (; glyph_prev <= glyph_index; ++glyph_prev)
+ put_loca(s, offset, indexToLocFormat);
+ glyph_data.memory = pfont->memory;
+ if ((code = pfont->data.get_outline(pfont, glyph_index,
+ &glyph_data)) >= 0
+ ) {
+ uint l = glyph_data.bits.size;
+
+ if (!indexToLocFormat)
+ l = (l + 1) & ~1;
+ offset += l;
+ gs_glyph_data_free(&glyph_data, "psf_write_truetype_data");
+ }
+
+ }
+ /* Pad to numGlyphs + 1 entries (including the trailing entry). */
+ for (; glyph_prev < n; ++glyph_prev)
+ put_loca(s, offset, indexToLocFormat);
+ put_pad(s, loca_length);
+
+ /* If necessary, write cmap, name, and OS/2. */
+
+ if (!have_cmap) {
+ if (check_position(pfont->memory,
+ subtable_positions.cmap + start_position,
+ stell(s)))
+ return_error(gs_error_unregistered);
+ write_cmap(s, font, TT_BIAS, 256, GS_MIN_GLYPH_INDEX + max_glyph,
+ options, cmap_length);
+ }
+ if (!have_name) {
+ if (check_position(pfont->memory,
+ subtable_positions.name + start_position,
+ stell(s)))
+ return_error(gs_error_unregistered);
+ write_name(s, &font_name);
+ }
+ if (!have_OS_2) {
+ if (check_position(pfont->memory,
+ subtable_positions.os_2 + start_position,
+ stell(s)))
+ return_error(gs_error_unregistered);
+ write_OS_2(s, font, TT_BIAS, 256);
+ }
+
+ /* If necessary, write [hv]mtx. */
+
+ if (generate_mtx)
+ for (i = 0; i < 2; ++i)
+ if (have_hvhea[i]) {
+ if (check_position(pfont->memory,
+ subtable_positions.mtx[i] + start_position,
+ stell(s)))
+ return_error(gs_error_unregistered);
+ write_mtx(s, pfont, &mtx[i], i);
+ put_pad(s, mtx[i].length);
+ }
+
+ /* If necessary, write post. */
+
+ if (!have_post) {
+ if (check_position(pfont->memory,
+ subtable_positions.post + start_position,
+ stell(s)))
+ return_error(gs_error_unregistered);
+ if (options & WRITE_TRUETYPE_POST) {
+ code = write_post(s, font, &post);
+ if (code < 0)
+ return code;
+ } else {
+ byte post_initial[32 + 2];
+
+ memset(post_initial, 0, 32);
+ put_u32(post_initial, 0x00030000);
+ stream_write(s, post_initial, 32);
+ }
+ }
+ }
+
+ /* Write head. */
+
+ /****** CHECKSUM WAS NEVER COMPUTED ******/
+ /*
+ * The following nonsense is to avoid warnings about the constant
+ * 0xb1b0afbaL being "unsigned in ANSI C, signed with -traditional".
+ */
+#if ARCH_SIZEOF_LONG > ARCH_SIZEOF_INT
+# define HEAD_MAGIC 0xb1b0afbaL
+#else
+# define HEAD_MAGIC ((ulong)~0x4e4f5045)
+#endif
+ put_u32(head + 8, HEAD_MAGIC - file_checksum); /* per spec */
+#undef HEAD_MAGIC
+ if (check_position(pfont->memory,
+ subtable_positions.head + start_position,
+ stell(s)))
+ return_error(gs_error_unregistered);
+ stream_write(s, head, 56);
+
+ return 0;
+}
+
+/* Write a TrueType font. */
+int
+psf_write_truetype_font(stream *s, gs_font_type42 *pfont, int options,
+ gs_glyph *orig_subset_glyphs, uint orig_subset_size,
+ const gs_const_string *alt_font_name)
+{
+ gs_font *const font = (gs_font *)pfont;
+ psf_glyph_enum_t genum;
+ gs_glyph subset_data[256 * MAX_COMPOSITE_PIECES];
+ gs_glyph *subset_glyphs = orig_subset_glyphs;
+ uint subset_size = orig_subset_size;
+
+ /* Sort the subset glyphs, if any. */
+
+ if (subset_glyphs) {
+ /* Add the component glyphs for composites. */
+ int code;
+
+ memcpy(subset_data, orig_subset_glyphs,
+ sizeof(gs_glyph) * subset_size);
+ subset_glyphs = subset_data;
+ code = psf_add_subset_pieces(subset_glyphs, &subset_size,
+ countof(subset_data),
+ countof(subset_data),
+ font);
+ if (code < 0)
+ return code;
+ subset_size = psf_sort_glyphs(subset_glyphs, subset_size);
+ }
+ psf_enumerate_glyphs_begin(&genum, font, subset_glyphs,
+ (subset_glyphs ? subset_size : 0),
+ GLYPH_SPACE_INDEX);
+ return psf_write_truetype_data(s, pfont, options & ~WRITE_TRUETYPE_CID,
+ &genum, subset_glyphs != 0, alt_font_name);
+}
+/* Write a stripped TrueType font. */
+int
+psf_write_truetype_stripped(stream *s, gs_font_type42 *pfont)
+{
+ psf_glyph_enum_t genum;
+ byte no_subset = 0;
+
+ psf_enumerate_bits_begin(&genum, (gs_font *)pfont, &no_subset, 0,
+ GLYPH_SPACE_INDEX);
+ return psf_write_truetype_data(s, pfont, WRITE_TRUETYPE_STRIPPED,
+ &genum, true, NULL);
+}
+
+/* Write a CIDFontType 2 font. */
+int
+psf_write_cid2_font(stream *s, gs_font_cid2 *pfont, int options,
+ const byte *subset_bits, uint subset_size,
+ const gs_const_string *alt_font_name)
+{
+ gs_font *const font = (gs_font *)pfont;
+ psf_glyph_enum_t genum;
+
+ psf_enumerate_bits_begin(&genum, font, subset_bits,
+ (subset_bits ? subset_size : 0),
+ GLYPH_SPACE_INDEX);
+ return psf_write_truetype_data(s, (gs_font_type42 *)font,
+ options | WRITE_TRUETYPE_CID, &genum,
+ subset_bits != 0, alt_font_name);
+}
+
+/* Write a stripped CIDFontType 2 font. */
+int
+psf_write_cid2_stripped(stream *s, gs_font_cid2 *pfont)
+{
+ gs_font *const font = (gs_font *)pfont;
+ psf_glyph_enum_t genum;
+ byte no_subset = 0;
+
+ psf_enumerate_bits_begin(&genum, font, &no_subset, 0,
+ GLYPH_SPACE_INDEX);
+ return psf_write_truetype_data(s, (gs_font_type42 *)font,
+ WRITE_TRUETYPE_STRIPPED |
+ WRITE_TRUETYPE_CID,
+ &genum, true, NULL);
+}
diff --git a/devices/vector/gdevpsfu.c b/devices/vector/gdevpsfu.c
new file mode 100644
index 000000000..291b6d4f4
--- /dev/null
+++ b/devices/vector/gdevpsfu.c
@@ -0,0 +1,353 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* PostScript/PDF font writing utilities */
+#include "memory_.h"
+#include <stdlib.h> /* for qsort */
+#include "gx.h"
+#include "gserrors.h"
+#include "gsmatrix.h" /* for gxfont.h */
+#include "gxfont.h"
+#include "gdevpsf.h"
+
+/* Begin enumerating the glyphs in a font or a font subset. */
+static int
+enumerate_font_next(psf_glyph_enum_t *ppge, gs_glyph *pglyph)
+{
+ gs_font *font = ppge->font;
+ int index = (int)ppge->index;
+ int code = font->procs.enumerate_glyph(font, &index,
+ ppge->glyph_space, pglyph);
+
+ ppge->index = index;
+ return (index == 0 ? 1 : code < 0 ? code : 0);
+}
+static int
+enumerate_glyphs_next(psf_glyph_enum_t *ppge, gs_glyph *pglyph)
+{
+ if (ppge->index >= ppge->subset.size)
+ return 1;
+ *pglyph = ppge->subset.selected.list[ppge->index++];
+ return 0;
+}
+static int
+enumerate_range_next(psf_glyph_enum_t *ppge, gs_glyph *pglyph)
+{
+ if (ppge->index >= ppge->subset.size)
+ return 1;
+ *pglyph = (gs_glyph)(ppge->index++ + gs_min_cid_glyph);
+ return 0;
+}
+void
+psf_enumerate_list_begin(psf_glyph_enum_t *ppge, gs_font *font,
+ const gs_glyph *subset_list, uint subset_size,
+ gs_glyph_space_t glyph_space)
+{
+ ppge->font = font;
+ ppge->subset.selected.list = subset_list;
+ ppge->subset.size = subset_size;
+ ppge->glyph_space = glyph_space;
+ ppge->enumerate_next =
+ (subset_list ? enumerate_glyphs_next :
+ subset_size ? enumerate_range_next : enumerate_font_next);
+ psf_enumerate_glyphs_reset(ppge);
+}
+
+/* Begin enumerating CID or TT glyphs in a subset given by a bit vector. */
+static int
+enumerate_bits_next(psf_glyph_enum_t *ppge, gs_glyph *pglyph)
+{
+ for (; ppge->index < ppge->subset.size; ppge->index++)
+ if (ppge->subset.selected.bits[ppge->index >> 3] & (0x80 >> (ppge->index & 7))) {
+ *pglyph = (gs_glyph)(ppge->index++ + gs_min_cid_glyph);
+ return 0;
+ }
+ return 1;
+}
+void
+psf_enumerate_bits_begin(psf_glyph_enum_t *ppge, gs_font *font,
+ const byte *subset_bits, uint subset_size,
+ gs_glyph_space_t glyph_space)
+{
+ ppge->font = font;
+ ppge->subset.selected.bits = subset_bits;
+ ppge->subset.size = subset_size;
+ ppge->glyph_space = glyph_space;
+ ppge->enumerate_next =
+ (subset_bits ? enumerate_bits_next :
+ subset_size ? enumerate_range_next : enumerate_font_next);
+ psf_enumerate_glyphs_reset(ppge);
+}
+
+/* Reset a glyph enumeration. */
+void
+psf_enumerate_glyphs_reset(psf_glyph_enum_t *ppge)
+{
+ ppge->index = 0;
+}
+
+/* Enumerate the next glyph in a font or a font subset. */
+/* Return 0 if more glyphs, 1 if done, <0 if error. */
+int
+psf_enumerate_glyphs_next(psf_glyph_enum_t *ppge, gs_glyph *pglyph)
+{
+ return ppge->enumerate_next(ppge, pglyph);
+}
+
+/*
+ * Add composite glyph pieces to a list of glyphs. Does not sort or
+ * remove duplicates. max_pieces is the maximum number of pieces that a
+ * single glyph can have: if this value is not known, the caller should
+ * use max_count.
+ */
+int
+psf_add_subset_pieces(gs_glyph *glyphs, uint *pcount, uint max_count,
+ uint max_pieces, gs_font *font)
+{
+ uint i;
+ uint count = *pcount;
+
+ for (i = 0; i < count; ++i) {
+ gs_glyph_info_t info;
+ int code;
+
+ if (count + max_pieces > max_count) {
+ /* Check first to make sure there is enough room. */
+ code = font->procs.glyph_info(font, glyphs[i], NULL,
+ GLYPH_INFO_NUM_PIECES, &info);
+ if (code < 0)
+ continue;
+ if (count + info.num_pieces > max_count)
+ return_error(gs_error_rangecheck);
+ }
+ info.pieces = &glyphs[count];
+ code = font->procs.glyph_info(font, glyphs[i], NULL,
+ GLYPH_INFO_NUM_PIECES |
+ GLYPH_INFO_PIECES, &info);
+ if (code >= 0)
+ count += info.num_pieces;
+ }
+ *pcount = count;
+ return 0;
+}
+
+/*
+ * Sort a list of glyphs and remove duplicates. Return the number of glyphs
+ * in the result.
+ */
+static int
+compare_glyphs(const void *pg1, const void *pg2)
+{
+ gs_glyph g1 = *(const gs_glyph *)pg1, g2 = *(const gs_glyph *)pg2;
+
+ return (g1 < g2 ? -1 : g1 > g2 ? 1 : 0);
+}
+int
+psf_sort_glyphs(gs_glyph *glyphs, int count)
+{
+ int i, n;
+
+ qsort(glyphs, count, sizeof(*glyphs), compare_glyphs);
+ for (i = n = 0; i < count; ++i)
+ if (i == 0 || glyphs[i] != glyphs[i - 1])
+ glyphs[n++] = glyphs[i];
+ return n;
+}
+
+/*
+ * Return the index of a given glyph in a sorted list of glyphs, or -1
+ * if the glyph is not present.
+ */
+int
+psf_sorted_glyphs_index_of(const gs_glyph *glyphs, int count, gs_glyph glyph)
+{
+ int lo = 0, hi = count - 1;
+
+ if (hi < 0)
+ return -1;
+ if (glyph < glyphs[0] || glyph > glyphs[hi])
+ return -1;
+ /*
+ * Loop invariants: hi > lo;
+ * glyphs[lo] <= glyph <= glyphs[hi].
+ */
+ while (hi - lo > 1) {
+ int mid = (lo + hi) >> 1;
+
+ if (glyph >= glyphs[mid])
+ lo = mid;
+ else
+ hi = mid;
+ }
+ return (glyph == glyphs[lo] ? lo : glyph == glyphs[hi] ? hi : -1);
+}
+/* Determine whether a sorted list of glyphs includes a given glyph. */
+bool
+psf_sorted_glyphs_include(const gs_glyph *glyphs, int count, gs_glyph glyph)
+{
+ return psf_sorted_glyphs_index_of(glyphs, count, glyph) >= 0;
+}
+
+/* Check that all selected glyphs can be written. */
+int
+psf_check_outline_glyphs(gs_font_base *pfont, psf_glyph_enum_t *ppge,
+ glyph_data_proc_t glyph_data)
+{
+ uint members = GLYPH_INFO_WIDTH0 << pfont->WMode;
+ gs_glyph glyph;
+ int code, good_glyphs = 0;
+
+ while ((code = psf_enumerate_glyphs_next(ppge, &glyph)) != 1) {
+ gs_glyph_data_t gdata;
+ gs_font_type1 *ignore_font;
+ gs_glyph_info_t info;
+
+ if (code < 0)
+ return code;
+ gdata.memory = pfont->memory;
+ code = glyph_data(pfont, glyph, &gdata, &ignore_font);
+ /*
+ * If the glyph isn't defined by a CharString, glyph_data will
+ * return a typecheck error. But if there's merely a glyph in
+ * in the Encoding that isn't defined, glyph_data will return an
+ * undefined error, which is OK.
+ */
+ if (code < 0) {
+ if (code == gs_error_undefined)
+ continue;
+ return code;
+ }
+ gs_glyph_data_free(&gdata, "psf_check_outline_glyphs");
+ /*
+ * If the font has a CDevProc or calls a non-standard OtherSubr,
+ * glyph_info will return a rangecheck error.
+ */
+ code = pfont->procs.glyph_info((gs_font *)pfont, glyph, NULL,
+ members, &info);
+
+ /* It may be that a single glyph is bad (eg no (h)sbw), we'll ignore it */
+ /* here, the glyph may not be included in any subset, or not used at all */
+ /* (ie the /.notdef). If an invalid glyoh is actually used then the text */
+ /* processing will still signal an error causing the document to fail. */
+ if(code == gs_error_invalidfont)
+ continue;
+
+ if (code < 0)
+ return code;
+ good_glyphs++;
+ }
+ if(good_glyphs)
+ return 0;
+ else
+ return_error(gs_error_invalidfont);
+}
+
+/* Gather glyph information for a Type 1 or Type 2 font. */
+int
+psf_get_outline_glyphs(psf_outline_glyphs_t *pglyphs, gs_font_base *pfont,
+ gs_glyph *orig_subset_glyphs, uint orig_subset_size,
+ glyph_data_proc_t glyph_data)
+{
+ gs_glyph notdef = gs_no_glyph;
+ gs_glyph *subset_glyphs = orig_subset_glyphs;
+ uint subset_size = orig_subset_size;
+
+ if (subset_glyphs) {
+ if (subset_size > countof(pglyphs->subset_data))
+ return_error(gs_error_limitcheck);
+ memcpy(pglyphs->subset_data, orig_subset_glyphs,
+ sizeof(gs_glyph) * subset_size);
+ subset_glyphs = pglyphs->subset_data;
+ }
+
+ {
+ /*
+ * Make sure that this font can be written out. Specifically, it
+ * must have no CharStrings defined by PostScript procedures, no
+ * non-standard OtherSubrs, and no CDevProc.
+ */
+ psf_glyph_enum_t genum;
+ int code;
+
+ psf_enumerate_glyphs_begin(&genum, (gs_font *)pfont, subset_glyphs,
+ (subset_glyphs ? subset_size : 0),
+ GLYPH_SPACE_NAME);
+ code = psf_check_outline_glyphs(pfont, &genum, glyph_data);
+ if (code < 0)
+ return code;
+ }
+
+ {
+ /*
+ * Detect the .notdef glyph, needed for subset fonts and to
+ * eliminate unnecessary Encoding assignments.
+ */
+ psf_glyph_enum_t genum;
+ gs_glyph glyph;
+ int code;
+
+ psf_enumerate_glyphs_begin(&genum, (gs_font *)pfont, NULL, 0,
+ GLYPH_SPACE_NAME);
+ while ((code = psf_enumerate_glyphs_next(&genum, &glyph)) != 1) {
+ if (gs_font_glyph_is_notdef(pfont, glyph)) {
+ notdef = glyph;
+ break;
+ }
+ }
+ }
+
+ if (subset_glyphs) {
+ /*
+ * For subset fonts, we must ensure that characters referenced
+ * by seac are also included. Note that seac creates at most
+ * 2 pieces.
+ */
+ int code = psf_add_subset_pieces(subset_glyphs, &subset_size,
+ countof(pglyphs->subset_data) - 1, 2,
+ (gs_font *)pfont);
+ uint keep_size, i;
+
+ if (code < 0)
+ return code;
+ /* Subset fonts require .notdef. */
+ if (notdef == gs_no_glyph)
+ return_error(gs_error_rangecheck);
+ /* Remove undefined glyphs. */
+ for (i = 0, keep_size = 0; i < subset_size; ++i) {
+ gs_glyph_info_t info;
+ gs_glyph glyph = subset_glyphs[i];
+
+ /*
+ * The documentation for the glyph_info procedure says that
+ * using members = 0 is an inexpensive way to find out
+ * whether a given glyph exists, but the implementations
+ * don't actually do this. Request an inexpensive value.
+ */
+ if (pfont->procs.glyph_info((gs_font *)pfont, glyph, NULL,
+ GLYPH_INFO_NUM_PIECES, &info) >= 0)
+ subset_glyphs[keep_size++] = glyph;
+ }
+ subset_size = keep_size;
+ /* Sort the glyphs. Make sure .notdef is included. */
+ subset_glyphs[subset_size++] = notdef;
+ subset_size = psf_sort_glyphs(subset_glyphs, subset_size);
+ }
+
+ pglyphs->notdef = notdef;
+ pglyphs->subset_glyphs = subset_glyphs;
+ pglyphs->subset_size = subset_size;
+ return 0;
+}
diff --git a/devices/vector/gdevpsfx.c b/devices/vector/gdevpsfx.c
new file mode 100644
index 000000000..d2dbd127a
--- /dev/null
+++ b/devices/vector/gdevpsfx.c
@@ -0,0 +1,949 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Convert Type 1 Charstrings to Type 2 */
+#include "math_.h"
+#include "memory_.h"
+#include "gx.h"
+#include "gserrors.h"
+#include "gxfixed.h"
+#include "gxmatrix.h" /* for gsfont.h */
+#include "gxfont.h"
+#include "gxfont1.h"
+#include "gxtype1.h"
+#include "stream.h"
+#include "gdevpsf.h"
+#include "gxistate.h"
+
+/* ------ Type 1 Charstring parsing ------ */
+
+/*
+ * The parsing code handles numbers on its own; it reports callsubr and
+ * return operators to the caller, but also executes them.
+ *
+ * Only the following elements of the Type 1 state are used:
+ * ostack, os_count, ipstack, ips_count
+ */
+
+#define CE_OFFSET 32 /* offset for extended opcodes */
+
+typedef struct {
+ fixed v0, v1; /* coordinates */
+ ushort index; /* sequential index of hint */
+} cv_stem_hint;
+typedef struct {
+ int count;
+ int current; /* cache cursor for search */
+ /*
+ * For dotsection and Type 1 Charstring hint replacement,
+ * we store active hints at the bottom of the table, and
+ * replaced hints at the top.
+ */
+ int replaced_count; /* # of replaced hints at top */
+ cv_stem_hint data[max_total_stem_hints];
+} cv_stem_hint_table;
+
+/* Skip over the initial bytes in a Charstring, if any. */
+static void
+skip_iv(gs_type1_state *pcis)
+{
+ int skip = pcis->pfont->data.lenIV;
+ ip_state_t *ipsp = &pcis->ipstack[pcis->ips_count - 1];
+ const byte *cip = ipsp->cs_data.bits.data;
+ crypt_state state = crypt_charstring_seed;
+
+ for (; skip > 0; ++cip, --skip)
+ decrypt_skip_next(*cip, state);
+ ipsp->ip = cip;
+ ipsp->dstate = state;
+}
+
+/*
+ * Set up for parsing a Type 1 Charstring.
+ *
+ * Only uses the following elements of *pfont:
+ * data.lenIV
+ */
+static void
+type1_next_init(gs_type1_state *pcis, const gs_glyph_data_t *pgd,
+ gs_font_type1 *pfont)
+{
+ gs_type1_interp_init(pcis, NULL, NULL, NULL, NULL, false, 0, pfont);
+ pcis->flex_count = flex_max;
+ pcis->ipstack[0].cs_data = *pgd;
+ skip_iv(pcis);
+}
+
+/* Clear the Type 1 operand stack. */
+static inline void
+type1_clear(gs_type1_state *pcis)
+{
+ pcis->os_count = 0;
+}
+
+/* Execute a callsubr. */
+static int
+type1_callsubr(gs_type1_state *pcis, int index)
+{
+ gs_font_type1 *pfont = pcis->pfont;
+ ip_state_t *ipsp1 = &pcis->ipstack[pcis->ips_count];
+ int code = pfont->data.procs.subr_data(pfont, index, false,
+ &ipsp1->cs_data);
+
+ if (code < 0)
+ return_error(code);
+ pcis->ips_count++;
+ skip_iv(pcis);
+ return code;
+}
+
+/* Add 1 or 3 stem hints. */
+static int
+type1_stem1(gs_type1_state *pcis, cv_stem_hint_table *psht, const fixed *pv,
+ fixed lsb, byte *active_hints)
+{
+ fixed v0 = pv[0] + lsb, v1 = v0 + pv[1];
+ cv_stem_hint *bot = &psht->data[0];
+ cv_stem_hint *orig_top = bot + psht->count;
+ cv_stem_hint *top = orig_top;
+
+ if (psht->count >= max_total_stem_hints)
+ return_error(gs_error_limitcheck);
+ while (top > bot &&
+ (v0 < top[-1].v0 || (v0 == top[-1].v0 && v1 < top[-1].v1))
+ ) {
+ *top = top[-1];
+ top--;
+ }
+ if (top > bot && v0 == top[-1].v0 && v1 == top[-1].v1) {
+ /* Duplicate hint, don't add it. */
+ memmove(top, top + 1, (char *)orig_top - (char *)top);
+ if (active_hints) {
+ uint index = top[-1].index;
+
+ active_hints[index >> 3] |= 0x80 >> (index & 7);
+ }
+ return 0;
+ }
+ top->v0 = v0;
+ top->v1 = v1;
+ psht->count++;
+ return 0;
+}
+static void
+type1_stem3(gs_type1_state *pcis, cv_stem_hint_table *psht, const fixed *pv3,
+ fixed lsb, byte *active_hints)
+{
+ type1_stem1(pcis, psht, pv3, lsb, active_hints);
+ type1_stem1(pcis, psht, pv3 + 2, lsb, active_hints);
+ type1_stem1(pcis, psht, pv3 + 4, lsb, active_hints);
+}
+
+/*
+ * Get the next operator from a Type 1 Charstring. This procedure handles
+ * numbers, div, blend, pop, and callsubr/return.
+ */
+static int
+type1_next(gs_type1_state *pcis)
+{
+ ip_state_t *ipsp = &pcis->ipstack[pcis->ips_count - 1];
+ const byte *cip, *cipe;
+ crypt_state state;
+#define CLEAR (csp = pcis->ostack - 1)
+ fixed *csp = &pcis->ostack[pcis->os_count - 1];
+ const bool encrypted = pcis->pfont->data.lenIV >= 0;
+ int c, code, num_results, c0;
+
+ load:
+ cip = ipsp->ip;
+ cipe = ipsp->cs_data.bits.data + ipsp->cs_data.bits.size;
+ state = ipsp->dstate;
+ for (;;) {
+ if (cip >= cipe)
+ /* We used to treat buffer overrun as a simple invalid font, now we assume that
+ * there is an implicit endchar, so we return a particular error for later
+ * interception. Returning an error allows any other code to continue as before.
+ * Part of bug #693170 where the fonts are invalid (no endchar on some glyphs).
+ */
+ return_error(gs_error_unknownerror);
+ c0 = *cip++;
+ charstring_next(c0, state, c, encrypted);
+ if (c >= c_num1) {
+ /* This is a number, decode it and push it on the stack. */
+ if (c < c_pos2_0) { /* 1-byte number */
+ decode_push_num1(csp, pcis->ostack, c);
+ } else if (c < cx_num4) { /* 2-byte number */
+ decode_push_num2(csp, pcis->ostack, c, cip, state, encrypted);
+ } else if (c == cx_num4) { /* 4-byte number */
+ long lw;
+
+ decode_num4(lw, cip, state, encrypted);
+ CS_CHECK_PUSH(csp, pcis->ostack);
+ *++csp = int2fixed(lw);
+ if (lw != fixed2long(*csp)) {
+ /*
+ * The integer was too large to handle in fixed point.
+ * Handle this case specially.
+ */
+ code = gs_type1_check_float(&state, encrypted, &cip, csp, lw);
+ if (code < 0)
+ return code;
+ }
+ } else /* not possible */
+ return_error(gs_error_invalidfont);
+ continue;
+ }
+#ifdef DEBUG
+ if (gs_debug_c('1')) {
+ const fixed *p;
+
+ for (p = pcis->ostack; p <= csp; ++p)
+ dmprintf1(pcis->pis->memory, " %g", fixed2float(*p));
+ if (c == cx_escape) {
+ crypt_state cstate = state;
+ int cn;
+
+ charstring_next(*cip, cstate, cn, encrypted);
+ dmprintf1(pcis->pis->memory, " [*%d]\n", cn);
+ } else
+ dmprintf1(pcis->pis->memory, " [%d]\n", c);
+ }
+#endif
+ switch ((char_command) c) {
+ default:
+ break;
+ case c_undef0:
+ case c_undef2:
+ case c_undef17:
+ return_error(gs_error_invalidfont);
+ case c_callsubr:
+ code = type1_callsubr(pcis, fixed2int_var(*csp) +
+ pcis->pfont->data.subroutineNumberBias);
+ if (code < 0)
+ return_error(code);
+ ipsp->ip = cip, ipsp->dstate = state;
+ --csp;
+ ++ipsp;
+ goto load;
+ case c_return:
+ if (pcis->ips_count > 1) {
+ gs_glyph_data_free(&ipsp->cs_data, "type1_next");
+ pcis->ips_count--;
+ --ipsp;
+ } else
+ return_error(gs_error_invalidfont);
+ goto load;
+ case c_undoc15:
+ /* See gstype1.h for information on this opcode. */
+ CLEAR;
+ continue;
+ case cx_escape:
+ charstring_next(*cip, state, c, encrypted);
+ ++cip;
+ switch ((char1_extended_command) c) {
+ default:
+ c += CE_OFFSET;
+ break;
+ case ce1_div:
+ csp[-1] = float2fixed((double)csp[-1] / (double)*csp);
+ --csp;
+ continue;
+ case ce1_undoc15: /* see gstype1.h */
+ CLEAR;
+ continue;
+ case ce1_callothersubr:
+ switch (fixed2int_var(*csp)) {
+ case 0:
+ pcis->ignore_pops = 2;
+ break; /* pass to caller */
+ case 3:
+ pcis->ignore_pops = 1;
+ break; /* pass to caller */
+ case 14:
+ num_results = 1; goto blend;
+ case 15:
+ num_results = 2; goto blend;
+ case 16:
+ num_results = 3; goto blend;
+ case 17:
+ num_results = 4; goto blend;
+ case 18:
+ num_results = 6;
+ blend:
+ code = gs_type1_blend(pcis, csp, num_results);
+ if (code < 0)
+ return code;
+ csp -= code;
+ continue;
+ default:
+ break; /* pass to caller */
+ }
+ break;
+ case ce1_pop:
+ if (pcis->ignore_pops != 0) {
+ pcis->ignore_pops--;
+ continue;
+ }
+ return_error(gs_error_rangecheck);
+ }
+ break;
+ }
+ break;
+ }
+ ipsp->ip = cip, ipsp->dstate = state;
+ pcis->ips_count = ipsp + 1 - &pcis->ipstack[0];
+ pcis->os_count = csp + 1 - &pcis->ostack[0];
+ return c;
+}
+
+/* ------ Output ------ */
+
+/* Put 2 or 4 bytes on a stream (big-endian). */
+static void
+sputc2(stream *s, int i)
+{
+ sputc(s, (byte)(i >> 8));
+ sputc(s, (byte)i);
+}
+static void
+sputc4(stream *s, int i)
+{
+ sputc2(s, i >> 16);
+ sputc2(s, i);
+}
+
+/* Put a Type 2 operator on a stream. */
+static void
+type2_put_op(stream *s, int op)
+{
+ if (op >= CE_OFFSET) {
+ spputc(s, cx_escape);
+ spputc(s, (byte)(op - CE_OFFSET));
+ } else
+ sputc(s, (byte)op);
+}
+
+/* Put a Type 2 number on a stream. */
+static void
+type2_put_int(stream *s, int i)
+{
+ if (i >= -107 && i <= 107)
+ sputc(s, (byte)(i + 139));
+ else if (i <= 1131 && i >= 0)
+ sputc2(s, (c_pos2_0 << 8) + i - 108);
+ else if (i >= -1131 && i < 0)
+ sputc2(s, (c_neg2_0 << 8) - i - 108);
+ else if (i >= -32768 && i <= 32767) {
+ spputc(s, c2_shortint);
+ sputc2(s, i);
+ } else {
+ /*
+ * We can't represent this number directly: compute it.
+ * (This can be done much more efficiently in particular cases;
+ * we'll do this if it ever seems worthwhile.)
+ */
+ type2_put_int(s, i >> 10);
+ type2_put_int(s, 1024);
+ type2_put_op(s, CE_OFFSET + ce2_mul);
+ type2_put_int(s, i & 1023);
+ type2_put_op(s, CE_OFFSET + ce2_add);
+ }
+}
+
+/* Put a fixed value on a stream. */
+static void
+type2_put_fixed(stream *s, fixed v)
+{
+ if (fixed_is_int(v))
+ type2_put_int(s, fixed2int_var(v));
+ else if (v >= int2fixed(-32768) && v < int2fixed(32768)) {
+ /* We can represent this as a 16:16 number. */
+ spputc(s, cx_num4);
+ sputc4(s, v << (16 - _fixed_shift));
+ } else {
+ type2_put_int(s, fixed2int_var(v));
+ type2_put_fixed(s, fixed_fraction(v));
+ type2_put_op(s, CE_OFFSET + ce2_add);
+ }
+}
+
+/* Put a stem hint table on a stream. */
+static void
+type2_put_stems(stream *s, int os_count, const cv_stem_hint_table *psht, int op)
+{
+ fixed prev = 0;
+ int pushed = os_count;
+ int i;
+
+ for (i = 0; i < psht->count; ++i, pushed += 2) {
+ fixed v0 = psht->data[i].v0;
+ fixed v1 = psht->data[i].v1;
+
+ if (pushed > ostack_size - 2) {
+ type2_put_op(s, op);
+ pushed = 0;
+ }
+ type2_put_fixed(s, v0 - prev);
+ type2_put_fixed(s, v1 - v0);
+ prev = v1;
+ }
+ type2_put_op(s, op);
+}
+
+/* Put out a hintmask command. */
+static void
+type2_put_hintmask(stream *s, const byte *mask, uint size)
+{
+ uint ignore;
+
+ type2_put_op(s, c2_hintmask);
+ sputs(s, mask, size, &ignore);
+}
+
+/* ------ Main program ------ */
+
+/*
+ * Convert a Type 1 Charstring to (unencrypted) Type 2.
+ * For simplicity, we expand all Subrs in-line.
+ * We still need to optimize the output using these patterns:
+ * (vhcurveto hvcurveto)* (vhcurveto hrcurveto | vrcurveto) =>
+ * vhcurveto
+ * (hvcurveto vhcurveto)* (hvcurveto vrcurveto | hrcurveto) =>
+ * hvcurveto
+ */
+#define MAX_STACK ostack_size
+int
+psf_convert_type1_to_type2(stream *s, const gs_glyph_data_t *pgd,
+ gs_font_type1 *pfont)
+{
+ gs_type1_state cis;
+ cv_stem_hint_table hstem_hints; /* horizontal stem hints */
+ cv_stem_hint_table vstem_hints; /* vertical stem hints */
+ bool first = true;
+ bool need_moveto = true;
+ bool replace_hints = false;
+ bool hints_changed = false;
+ bool width_on_stack = false;
+ enum {
+ dotsection_in = 0,
+ dotsection_out = -1
+ } dotsection_flag = dotsection_out;
+ byte active_hints[(max_total_stem_hints + 7) / 8];
+ byte dot_save_hints[(max_total_stem_hints + 7) / 8];
+ uint hintmask_size;
+#define HINTS_CHANGED()\
+ BEGIN\
+ hints_changed = replace_hints;\
+ if (hints_changed)\
+ CHECK_OP(); /* see below */\
+ END
+#define CHECK_HINTS_CHANGED()\
+ BEGIN\
+ if (hints_changed) {\
+ type2_put_hintmask(s, active_hints, hintmask_size);\
+ hints_changed = false;\
+ }\
+ END
+ /*
+ * In order to combine Type 1 operators, we usually delay writing
+ * out operators (but not their operands). We must keep track of
+ * the stack depth so we don't exceed it when combining operators.
+ */
+ int depth; /* of operands on stack */
+ int prev_op; /* operator to write, -1 if none */
+#define CLEAR_OP()\
+ (depth = 0, prev_op = -1)
+#define CHECK_OP()\
+ BEGIN\
+ if (prev_op >= 0) {\
+ type2_put_op(s, prev_op);\
+ CLEAR_OP();\
+ }\
+ END
+ fixed mx0 = 0, my0 = 0; /* See ce1_setcurrentpoint. */
+
+ /* In case we do not get an sbw or hsbw op */
+ cis.lsb.x = cis.lsb.y = cis.width.x = cis.width.y = fixed_0;
+
+ /*
+ * Do a first pass to collect hints. Note that we must also process
+ * [h]sbw, because the hint coordinates are relative to the lsb.
+ */
+ hstem_hints.count = hstem_hints.replaced_count = hstem_hints.current = 0;
+ vstem_hints.count = vstem_hints.replaced_count = vstem_hints.current = 0;
+ type1_next_init(&cis, pgd, pfont);
+ for (;;) {
+ int c = type1_next(&cis);
+ fixed *csp = &cis.ostack[cis.os_count - 1];
+
+ switch (c) {
+ default:
+ /* We used to treat buffer overrun as a simple invalid font, now we assume that
+ * there is an implicit endchar, this is handled by looking for a specific error.
+ * Part of bug #693170 where the fonts are invalid (no endchar on some glyphs).
+ */
+ if (c == gs_error_unknownerror)
+ break;
+ if (c < 0)
+ return c;
+ type1_clear(&cis);
+ continue;
+ case c1_hsbw:
+ gs_type1_sbw(&cis, cis.ostack[0], fixed_0, cis.ostack[1], fixed_0);
+ goto clear;
+ case cx_hstem:
+ type1_stem1(&cis, &hstem_hints, csp - 1, cis.lsb.y, NULL);
+ goto clear;
+ case cx_vstem:
+ type1_stem1(&cis, &vstem_hints, csp - 1, cis.lsb.x, NULL);
+ goto clear;
+ case CE_OFFSET + ce1_sbw:
+ gs_type1_sbw(&cis, cis.ostack[0], cis.ostack[1],
+ cis.ostack[2], cis.ostack[3]);
+ goto clear;
+ case CE_OFFSET + ce1_vstem3:
+ type1_stem3(&cis, &vstem_hints, csp - 5, cis.lsb.x, NULL);
+ goto clear;
+ case CE_OFFSET + ce1_hstem3:
+ type1_stem3(&cis, &hstem_hints, csp - 5, cis.lsb.y, NULL);
+ clear:
+ type1_clear(&cis);
+ continue;
+ case ce1_callothersubr:
+ if (*csp == int2fixed(3))
+ replace_hints = true;
+ if (*csp == int2fixed(12) || *csp == int2fixed(13))
+ cis.os_count -= fixed2int(csp[-1]);
+ cis.os_count -= 2;
+ continue;
+ case CE_OFFSET + ce1_dotsection:
+ replace_hints = true;
+ continue;
+ case CE_OFFSET + ce1_seac:
+ case cx_endchar:
+ break;
+ }
+ break;
+ }
+ /*
+ * Number the hints for hintmask. We must do this even if we never
+ * replace hints, because type1_stem# uses the index to set bits in
+ * active_hints.
+ */
+ {
+ int i;
+
+ for (i = 0; i < hstem_hints.count; ++i)
+ hstem_hints.data[i].index = i;
+ for (i = 0; i < vstem_hints.count; ++i)
+ vstem_hints.data[i].index = i + hstem_hints.count;
+ }
+ if (replace_hints) {
+ hintmask_size =
+ (hstem_hints.count + vstem_hints.count + 7) / 8;
+ memset(active_hints, 0, hintmask_size);
+ } else
+ hintmask_size = 0;
+
+ /* Do a second pass to write the result. */
+ type1_next_init(&cis, pgd, pfont);
+ CLEAR_OP();
+ for (;;) {
+ int c = type1_next(&cis);
+ fixed *csp = &cis.ostack[cis.os_count - 1];
+#define POP(n)\
+ (csp -= (n), cis.os_count -= (n))
+ int i;
+ fixed mx, my;
+
+ if (need_moveto && ((c >= cx_rlineto && c <= cx_rrcurveto) ||
+ c == cx_vhcurveto || c == cx_hvcurveto))
+ {
+ mx = my = 0;
+ need_moveto = false;
+ CHECK_OP();
+ if (first) {
+ if (width_on_stack) {
+ type2_put_fixed(s, *csp); /* width */
+ /* We need to move all the stored numeric values up by
+ * one in the stack, eliminating the width, so that later
+ * processing when we handle the drswing operator emits the correct
+ * values. This is different to the 'move' case below.
+ */
+ cis.os_count--;
+ for (i = 0; i < cis.os_count; ++i)
+ cis.ostack[i] = cis.ostack[i+1];
+ }
+ mx += cis.lsb.x + mx0, my += cis.lsb.y + my0;
+ first = false;
+ }
+ CHECK_HINTS_CHANGED();
+ if (mx == 0) {
+ type2_put_fixed(s, my);
+ depth = 1, prev_op = cx_vmoveto;
+ } else if (my == 0) {
+ type2_put_fixed(s, mx);
+ depth = 1, prev_op = cx_hmoveto;
+ } else {
+ type2_put_fixed(s, mx);
+ type2_put_fixed(s, my);
+ depth = 2, prev_op = cx_rmoveto;
+ }
+ }
+
+ switch (c) {
+ default:
+ /* We used to treat buffer overrun as a simple invalid font, now we assume that
+ * there is an implicit endchar, this is handled by looking for a specific error.
+ * Part of bug #693170 where the fonts are invalid (no endchar on some glyphs).
+ */
+ if (c == gs_error_unknownerror) {
+ type2_put_op(s, cx_endchar);
+ return 0;
+ }
+ if (c < 0)
+ return c;
+ if (c >= CE_OFFSET)
+ return_error(gs_error_rangecheck);
+ /* The Type 1 use of all other operators is the same in Type 2. */
+ copy:
+ CHECK_OP();
+ CHECK_HINTS_CHANGED();
+ put:
+ for (i = 0; i < cis.os_count; ++i)
+ type2_put_fixed(s, cis.ostack[i]);
+ depth += cis.os_count;
+ prev_op = c;
+ type1_clear(&cis);
+ continue;
+ case cx_hstem:
+ type1_stem1(&cis, &hstem_hints, csp - 1, cis.lsb.y, active_hints);
+ hint:
+ HINTS_CHANGED();
+ type1_clear(&cis);
+ continue;
+ case cx_vstem:
+ type1_stem1(&cis, &vstem_hints, csp - 1, cis.lsb.x, active_hints);
+ goto hint;
+ case CE_OFFSET + ce1_vstem3:
+ type1_stem3(&cis, &vstem_hints, csp - 5, cis.lsb.x, active_hints);
+ goto hint;
+ case CE_OFFSET + ce1_hstem3:
+ type1_stem3(&cis, &hstem_hints, csp - 5, cis.lsb.y, active_hints);
+ goto hint;
+ case CE_OFFSET + ce1_dotsection:
+ if (dotsection_flag == dotsection_out) {
+ memcpy(dot_save_hints, active_hints, hintmask_size);
+ memset(active_hints, 0, hintmask_size);
+ dotsection_flag = dotsection_in;
+ } else {
+ memcpy(active_hints, dot_save_hints, hintmask_size);
+ dotsection_flag = dotsection_out;
+ }
+ HINTS_CHANGED();
+ continue;
+ case c1_closepath:
+ need_moveto = true;
+ continue;
+ case CE_OFFSET + ce1_setcurrentpoint:
+ if (first) {
+ /* A workaround for fonts which use ce1_setcurrentpoint
+ in an illegal way for shifting a path.
+ See t1_hinter__setcurrentpoint for more information. */
+ mx0 = csp[-1], my0 = *csp;
+ }
+ continue;
+ case cx_vmoveto:
+ mx = 0, my = *csp;
+ POP(1); goto move;
+ case cx_hmoveto:
+ mx = *csp, my = 0;
+ POP(1); goto move;
+ case cx_rmoveto:
+ mx = csp[-1], my = *csp;
+ POP(2);
+ move:
+ need_moveto = false;
+ CHECK_OP();
+ if (first) {
+ if (cis.os_count)
+ type2_put_fixed(s, *csp); /* width */
+ mx += cis.lsb.x + mx0, my += cis.lsb.y + my0;
+ first = false;
+ }
+ if (cis.flex_count != flex_max) {
+ /* We're accumulating points for a flex. */
+ if (type1_next(&cis) != ce1_callothersubr)
+ return_error(gs_error_rangecheck);
+ csp = &cis.ostack[cis.os_count - 1];
+ if (*csp != int2fixed(2) || csp[-1] != fixed_0)
+ return_error(gs_error_rangecheck);
+ cis.flex_count++;
+ csp[-1] = mx, *csp = my;
+ continue;
+ }
+ CHECK_HINTS_CHANGED();
+ if (mx == 0) {
+ type2_put_fixed(s, my);
+ depth = 1, prev_op = cx_vmoveto;
+ } else if (my == 0) {
+ type2_put_fixed(s, mx);
+ depth = 1, prev_op = cx_hmoveto;
+ } else {
+ type2_put_fixed(s, mx);
+ type2_put_fixed(s, my);
+ depth = 2, prev_op = cx_rmoveto;
+ }
+ type1_clear(&cis);
+ continue;
+ case c1_hsbw:
+ gs_type1_sbw(&cis, cis.ostack[0], fixed_0, cis.ostack[1], fixed_0);
+ /*
+ * Leave the l.s.b. on the operand stack for the initial hint,
+ * moveto, or endchar command.
+ */
+ cis.ostack[0] = cis.ostack[1];
+ sbw:
+ /* cff_write_Private doesn't write defaultWidthX
+ when called with the Type 1 font,
+ so the reader will assume
+ defaultWidthX = defaultWidthX_DEFAULT
+ Use the latter here.
+ */
+ if (cis.ostack[0] == default_defaultWidthX)
+ cis.os_count = 0;
+ else {
+ cis.ostack[0] -= default_defaultWidthX;
+ cis.os_count = 1;
+ width_on_stack = true;
+ }
+ if (hstem_hints.count) {
+ if (cis.os_count)
+ type2_put_fixed(s, cis.ostack[0]);
+ type2_put_stems(s, cis.os_count, &hstem_hints,
+ (replace_hints ? c2_hstemhm : cx_hstem));
+ cis.os_count = 0;
+ width_on_stack = false;
+ }
+ if (vstem_hints.count) {
+ if (cis.os_count)
+ type2_put_fixed(s, cis.ostack[0]);
+ type2_put_stems(s, cis.os_count, &vstem_hints,
+ (replace_hints ? c2_vstemhm : cx_vstem));
+ cis.os_count = 0;
+ width_on_stack = false;
+ }
+ continue;
+ case CE_OFFSET + ce1_seac:
+ /*
+ * It is an undocumented feature of the Type 2 CharString
+ * format that endchar + 4 or 5 operands is equivalent to
+ * seac with an implicit asb operand + endchar with 0 or 1
+ * operands. Remove the asb argument from the stack, but
+ * adjust the adx argument to compensate for the fact that
+ * Type 2 CharStrings don't have any concept of l.s.b.
+ */
+ csp[-3] += cis.lsb.x - csp[-4];
+ memmove(csp - 4, csp - 3, sizeof(*csp) * 4);
+ POP(1);
+ /* (falls through) */
+ case cx_endchar:
+ CHECK_OP();
+ for (i = 0; i < cis.os_count; ++i)
+ type2_put_fixed(s, cis.ostack[i]);
+ type2_put_op(s, cx_endchar);
+ return 0;
+ case CE_OFFSET + ce1_sbw:
+ gs_type1_sbw(&cis, cis.ostack[0], cis.ostack[1],
+ cis.ostack[2], cis.ostack[3]);
+ cis.ostack[0] = cis.ostack[2];
+ goto sbw;
+ case ce1_callothersubr:
+ CHECK_OP();
+ switch (fixed2int_var(*csp)) {
+ default:
+ return_error(gs_error_rangecheck);
+ case 0:
+ /*
+ * The operand stack contains: delta to reference point,
+ * 6 deltas for the two curves, fd, final point, 3, 0.
+ */
+ csp[-18] += csp[-16], csp[-17] += csp[-15];
+ memmove(csp - 16, csp - 14, sizeof(*csp) * 11);
+ cis.os_count -= 6, csp -= 6;
+ /*
+ * We could optimize by using [h]flex[1],
+ * but it isn't worth the trouble.
+ */
+ c = CE_OFFSET + ce2_flex;
+ cis.flex_count = flex_max; /* not inside flex */
+ cis.ignore_pops = 2;
+ goto copy;
+ case 1:
+ cis.flex_count = 0;
+ cis.os_count -= 2;
+ continue;
+ /*case 2:*/ /* detected in *moveto */
+ case 3:
+ memset(active_hints, 0, hintmask_size);
+ HINTS_CHANGED();
+ cis.ignore_pops = 1;
+ cis.os_count -= 2;
+ continue;
+ case 12:
+ case 13:
+ /* Counter control is not implemented. */
+ cis.os_count -= 2 + fixed2int(csp[-1]);
+ continue;
+ }
+ /*
+ * The remaining cases are strictly for optimization.
+ */
+ case cx_rlineto:
+ if (depth > MAX_STACK - 2)
+ goto copy;
+ switch (prev_op) {
+ case cx_rlineto: /* rlineto+ => rlineto */
+ goto put;
+ case cx_rrcurveto: /* rrcurveto+ rlineto => rcurveline */
+ c = c2_rcurveline;
+ goto put;
+ default:
+ goto copy;
+ }
+ case cx_hlineto: /* hlineto (vlineto hlineto)* [vlineto] => hlineto */
+ if (depth > MAX_STACK - 1 ||
+ prev_op != (depth & 1 ? cx_vlineto : cx_hlineto))
+ goto copy;
+ c = prev_op;
+ goto put;
+ case cx_vlineto: /* vlineto (hlineto vlineto)* [hlineto] => vlineto */
+ if (depth > MAX_STACK - 1 ||
+ prev_op != (depth & 1 ? cx_hlineto : cx_vlineto))
+ goto copy;
+ c = prev_op;
+ goto put;
+ case cx_hvcurveto: /* hvcurveto (vhcurveto hvcurveto)* => hvcurveto */
+ /* (vhcurveto hvcurveto)+ => vhcurveto */
+ /*
+ * We have to check (depth & 1) because the last curve might
+ * have 5 parameters rather than 4 (see rrcurveto below).
+ */
+ if ((depth & 1) || depth > MAX_STACK - 4 ||
+ prev_op != (depth & 4 ? cx_vhcurveto : cx_hvcurveto))
+ goto copy;
+ c = prev_op;
+ goto put;
+ case cx_vhcurveto: /* vhcurveto (hvcurveto vhcurveto)* => vhcurveto */
+ /* (hvcurveto vhcurveto)+ => hvcurveto */
+ /* See above re the (depth & 1) check. */
+ if ((depth & 1) || depth > MAX_STACK - 4 ||
+ prev_op != (depth & 4 ? cx_hvcurveto : cx_vhcurveto))
+ goto copy;
+ c = prev_op;
+ goto put;
+ case cx_rrcurveto:
+ if (depth == 0) {
+ if (csp[-1] == 0) {
+ /* A|0 B C D 0 F rrcurveto => [A] B C D F vvcurveto */
+ c = c2_vvcurveto;
+ csp[-1] = csp[0];
+ if (csp[-5] == 0) {
+ memmove(csp - 5, csp - 4, sizeof(*csp) * 4);
+ POP(2);
+ } else
+ POP(1);
+ } else if (*csp == 0) {
+ /* A B|0 C D E 0 rrcurveto => [B] A C D E hhcurveto */
+ c = c2_hhcurveto;
+ if (csp[-4] == 0) {
+ memmove(csp - 4, csp - 3, sizeof(*csp) * 3);
+ POP(2);
+ } else {
+ *csp = csp[-5], csp[-5] = csp[-4], csp[-4] = *csp;
+ POP(1);
+ }
+ }
+ /*
+ * We could also optimize:
+ * 0 B C D E F|0 rrcurveto => B C D E [F] vhcurveto
+ * A 0 C D E|0 F rrcurveto => A C D F [E] hvcurveto
+ * but this gets in the way of subsequent optimization
+ * of multiple rrcurvetos, so we don't do it.
+ */
+ goto copy;
+ }
+ if (depth > MAX_STACK - 6)
+ goto copy;
+ switch (prev_op) {
+ case c2_hhcurveto: /* hrcurveto (x1 0 x2 y2 x3 0 rrcurveto)* => */
+ /* hhcurveto */
+ if (csp[-4] == 0 && *csp == 0) {
+ memmove(csp - 4, csp - 3, sizeof(*csp) * 3);
+ c = prev_op;
+ POP(2);
+ goto put;
+ }
+ goto copy;
+ case c2_vvcurveto: /* rvcurveto (0 y1 x2 y2 0 y3 rrcurveto)* => */
+ /* vvcurveto */
+ if (csp[-5] == 0 && csp[-1] == 0) {
+ memmove(csp - 5, csp - 4, sizeof(*csp) * 3);
+ csp[-2] = *csp;
+ c = prev_op;
+ POP(2);
+ goto put;
+ }
+ goto copy;
+ case cx_hvcurveto:
+ if (depth & 1)
+ goto copy;
+ if (!(depth & 4))
+ goto hrc;
+ vrc: /* (vhcurveto hvcurveto)+ vrcurveto => vhcurveto */
+ /* hvcurveto (vhcurveto hvcurveto)* vrcurveto => hvcurveto */
+ if (csp[-5] != 0)
+ goto copy;
+ memmove(csp - 5, csp - 4, sizeof(*csp) * 5);
+ c = prev_op;
+ POP(1);
+ goto put;
+ case cx_vhcurveto:
+ if (depth & 1)
+ goto copy;
+ if (!(depth & 4))
+ goto vrc;
+ hrc: /* (hvcurveto vhcurveto)+ hrcurveto => hvcurveto */
+ /* vhcurveto (hvcurveto vhcurveto)* hrcurveto => vhcurveto */
+ if (csp[-4] != 0)
+ goto copy;
+ /* A 0 C D E F => A C D F E */
+ memmove(csp - 4, csp - 3, sizeof(*csp) * 2);
+ csp[-2] = *csp;
+ c = prev_op;
+ POP(1);
+ goto put;
+ case cx_rlineto: /* rlineto+ rrcurveto => rlinecurve */
+ c = c2_rlinecurve;
+ goto put;
+ case cx_rrcurveto: /* rrcurveto+ => rrcurveto */
+ goto put;
+ default:
+ goto copy;
+ }
+ }
+ }
+}
diff --git a/devices/vector/gdevpsu.c b/devices/vector/gdevpsu.c
new file mode 100644
index 000000000..129616686
--- /dev/null
+++ b/devices/vector/gdevpsu.c
@@ -0,0 +1,360 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* PostScript-writing utilities */
+#include "math_.h"
+#include "time_.h"
+#include "stat_.h"
+#include "unistd_.h"
+#include "gx.h"
+#include "gscdefs.h"
+#include "gxdevice.h"
+#include "gdevpsu.h"
+#include "spprint.h"
+#include "stream.h"
+#include "gserrors.h"
+
+/* ---------------- Low level ---------------- */
+
+/* Write a 0-terminated array of strings as lines. */
+int
+psw_print_lines(FILE *f, const char *const lines[])
+{
+ int i;
+ for (i = 0; lines[i] != 0; ++i) {
+ if (fprintf(f, "%s\n", lines[i]) < 0)
+ return_error(gs_error_ioerror);
+ }
+ return 0;
+}
+
+/* Write the ProcSet name. */
+static void
+psw_put_procset_name(stream *s, const gx_device *dev,
+ const gx_device_pswrite_common_t *pdpc)
+{
+ pprints1(s, "GS_%s", dev->dname);
+ pprintd3(s, "_%d_%d_%d",
+ (int)pdpc->LanguageLevel,
+ (int)(pdpc->LanguageLevel * 10 + 0.5) % 10,
+ pdpc->ProcSet_version);
+}
+static void
+psw_print_procset_name(FILE *f, const gx_device *dev,
+ const gx_device_pswrite_common_t *pdpc)
+{
+ byte buf[100]; /* arbitrary */
+ stream s;
+
+ s_init(&s, dev->memory);
+ swrite_file(&s, f, buf, sizeof(buf));
+ psw_put_procset_name(&s, dev, pdpc);
+ sflush(&s);
+}
+
+/* Write a bounding box. */
+static void
+psw_print_bbox(FILE *f, const gs_rect *pbbox)
+{
+ fprintf(f, "%%%%BoundingBox: %d %d %d %d\n",
+ (int)floor(pbbox->p.x), (int)floor(pbbox->p.y),
+ (int)ceil(pbbox->q.x), (int)ceil(pbbox->q.y));
+ fprintf(f, "%%%%HiResBoundingBox: %f %f %f %f\n",
+ pbbox->p.x, pbbox->p.y, pbbox->q.x, pbbox->q.y);
+}
+
+/* ---------------- File level ---------------- */
+
+static const char *const psw_ps_header[] = {
+ "%!PS-Adobe-3.0",
+ "%%Pages: (atend)",
+ 0
+};
+
+static const char *const psw_eps_header[] = {
+ "%!PS-Adobe-3.0 EPSF-3.0",
+ 0
+};
+
+static const char *const psw_begin_prolog[] = {
+ "%%EndComments",
+ "%%BeginProlog",
+ "% This copyright applies to everything between here and the %%EndProlog:",
+ /* copyright */
+ /* begin ProcSet */
+ 0
+};
+
+/*
+ * To achieve page independence, every page must in the general case
+ * set page parameters. To preserve duplexing the page cannot set page
+ * parameters. The following code checks the current page size and sets
+ * it only if it is necessary.
+ */
+static const char *const psw_ps_procset[] = {
+ /* <w> <h> <sizename> setpagesize - */
+ "/PageSize 2 array def"
+ "/setpagesize" /* x y /a4 -> - */
+ "{ PageSize aload pop " /* x y /a4 x0 y0 */
+ "3 index eq exch", /* x y /a4 bool x0 */
+ "4 index eq and" /* x y /a4 bool */
+ "{ pop pop pop"
+ "}"
+ "{ PageSize dup 1", /* x y /a4 [ ] [ ] 1 */
+ "5 -1 roll put 0 " /* x /a4 [ y] 0 */
+ "4 -1 roll put " /* /a4 */
+ "dup null eq {false} {dup where} ifelse"
+ "{ exch get exec" /* - */
+ "}",
+ "{ pop" /* - */
+ "/setpagedevice where",
+ "{ pop 1 dict dup /PageSize PageSize put setpagedevice"
+ "}",
+ "{ /setpage where"
+ "{ pop PageSize aload pop pageparams 3 {exch pop} repeat",
+ "setpage"
+ "}"
+ "if"
+ "}"
+ "ifelse"
+ "}"
+ "ifelse"
+ "}"
+ "ifelse"
+ "} bind def",
+ 0
+};
+
+static const char *const psw_end_prolog[] = {
+ "end def",
+ "%%EndResource", /* ProcSet */
+ "/pagesave null def", /* establish binding */
+ "%%EndProlog",
+ 0
+};
+
+/* Return true when the file is seekable.
+ * On Windows NT ftell() returns some non-EOF value when used on pipes.
+ */
+static bool
+is_seekable(FILE *f)
+{
+ struct stat buf;
+
+ if(fstat(fileno(f), &buf))
+ return 0;
+ return S_ISREG(buf.st_mode);
+}
+
+/*
+ * Write the file header, up through the BeginProlog. This must write to a
+ * file, not a stream, because it may be called during finalization.
+ */
+int
+psw_begin_file_header(FILE *f, const gx_device *dev, const gs_rect *pbbox,
+ gx_device_pswrite_common_t *pdpc, bool ascii)
+{
+ psw_print_lines(f, (pdpc->ProduceEPS ? psw_eps_header : psw_ps_header));
+ if (pbbox) {
+ psw_print_bbox(f, pbbox);
+ pdpc->bbox_position = 0;
+ } else if (!is_seekable(f)) { /* File is not seekable. */
+ pdpc->bbox_position = -1;
+ fputs("%%BoundingBox: (atend)\n", f);
+ fputs("%%HiResBoundingBox: (atend)\n", f);
+ } else { /* File is seekable, leave room to rewrite bbox. */
+ pdpc->bbox_position = gp_ftell_64(f);
+ fputs("%...............................................................\n", f);
+ fputs("%...............................................................\n", f);
+ }
+ fprintf(f, "%%%%Creator: %s %ld (%s)\n", gs_product, (long)gs_revision,
+ dev->dname);
+ {
+ time_t t;
+ struct tm tms;
+
+ time(&t);
+ tms = *localtime(&t);
+ fprintf(f, "%%%%CreationDate: %d/%02d/%02d %02d:%02d:%02d\n",
+ tms.tm_year + 1900, tms.tm_mon + 1, tms.tm_mday,
+ tms.tm_hour, tms.tm_min, tms.tm_sec);
+ }
+ if (ascii)
+ fputs("%%DocumentData: Clean7Bit\n", f);
+ if (pdpc->LanguageLevel >= 2.0)
+ fprintf(f, "%%%%LanguageLevel: %d\n", (int)pdpc->LanguageLevel);
+ else if (pdpc->LanguageLevel == 1.5)
+ fputs("%%Extensions: CMYK\n", f);
+ psw_print_lines(f, psw_begin_prolog);
+ fprintf(f, "%% %s\n", gs_copyright);
+ fputs("%%BeginResource: procset ", f);
+ fflush(f);
+ psw_print_procset_name(f, dev, pdpc);
+ fprintf(f, " %5.3lf %d\n/", pdpc->ProcSet_version / 1000.0, 0);
+ fflush(f);
+ psw_print_procset_name(f, dev, pdpc);
+ fputs(" 80 dict dup begin\n", f);
+ psw_print_lines(f, psw_ps_procset);
+ fflush(f);
+ if (ferror(f))
+ return_error(gs_error_ioerror);
+ return 0;
+}
+
+/*
+ * End the file header.
+ */
+int
+psw_end_file_header(FILE *f)
+{
+ return psw_print_lines(f, psw_end_prolog);
+}
+
+/*
+ * End the file.
+ */
+int
+psw_end_file(FILE *f, const gx_device *dev,
+ const gx_device_pswrite_common_t *pdpc, const gs_rect *pbbox,
+ int /* should be long */ page_count)
+{
+ if (f == NULL)
+ return 0; /* clients should be more careful */
+ fprintf(f, "%%%%Trailer\n%%%%Pages: %ld\n", (long)page_count);
+ if (ferror(f))
+ return_error(gs_error_ioerror);
+ if (dev->PageCount > 0 && pdpc->bbox_position != 0) {
+ if (pdpc->bbox_position >= 0) {
+ int64_t save_pos = gp_ftell_64(f);
+
+ gp_fseek_64(f, pdpc->bbox_position, SEEK_SET);
+ /* Theoretically the bbox device should fill in the bounding box
+ * but this does nothing because we don't write on the page.
+ * So if bbox = 0 0 0 0, replace with the device page size.
+ */
+ if(pbbox->p.x == 0 && pbbox->p.y == 0
+ && pbbox->q.x == 0 && pbbox->q.y == 0) {
+ gs_rect bbox;
+ int width = (int)(dev->width * 72.0 / dev->HWResolution[0] + 0.5);
+ int height = (int)(dev->height * 72.0 / dev->HWResolution[1] + 0.5);
+
+ bbox.p.x = 0;
+ bbox.p.y = 0;
+ bbox.q.x = width;
+ bbox.q.y = height;
+ psw_print_bbox(f, &bbox);
+ } else
+ psw_print_bbox(f, pbbox);
+ fputc('%', f);
+ if (ferror(f))
+ return_error(gs_error_ioerror);
+ gp_fseek_64(f, save_pos, SEEK_SET);
+ } else
+ psw_print_bbox(f, pbbox);
+ }
+ if (!pdpc->ProduceEPS)
+ fputs("%%EOF\n", f);
+ if (ferror(f))
+ return_error(gs_error_ioerror);
+ return 0;
+}
+
+/* ---------------- Page level ---------------- */
+
+/*
+ * Write the page header.
+ */
+int
+psw_write_page_header(stream *s, const gx_device *dev,
+ const gx_device_pswrite_common_t *pdpc,
+ bool do_scale, long page_ord, int dictsize)
+{
+ long page = dev->PageCount + 1;
+ int width = (int)(dev->width * 72.0 / dev->HWResolution[0] + 0.5);
+ int height = (int)(dev->height * 72.0 / dev->HWResolution[1] + 0.5);
+
+ pprintld2(s, "%%%%Page: %ld %ld\n", page, page_ord);
+ if (!pdpc->ProduceEPS)
+ pprintld2(s, "%%%%PageBoundingBox: 0 0 %ld %ld\n", width, height);
+
+ stream_puts(s, "%%BeginPageSetup\n");
+ /*
+ * Adobe's documentation says that page setup must be placed outside the
+ * save/restore that encapsulates the page contents, and that the
+ * showpage must be placed after the restore. This means that to
+ * achieve page independence, *every* page's setup code must include a
+ * setpagedevice that sets *every* page device parameter that is changed
+ * on *any* page. Currently, the only such parameter relevant to this
+ * driver is page size, but there might be more in the future.
+ */
+ psw_put_procset_name(s, dev, pdpc);
+ stream_puts(s, " begin\n");
+ if (!pdpc->ProduceEPS) {
+ typedef struct ps_ {
+ const char *size_name;
+ int width, height;
+ } page_size;
+ static const page_size sizes[] = {
+ {"/11x17", 792, 1224},
+ {"/a3", 842, 1191},
+ {"/a4", 595, 842},
+ {"/b5", 501, 709},
+ {"/ledger", 1224, 792},
+ {"/legal", 612, 1008},
+ {"/letter", 612, 792},
+ {"null", 0, 0}
+ };
+ const page_size *p = sizes;
+
+ while (p->size_name[0] == '/') {
+ if((p->width - 5) <= width && (p->width + 5) >= width) {
+ if((p->height - 5) <= height && (p->height + 5) >= height) {
+ break;
+ } else
+ ++p;
+ }
+ else
+ ++p;
+ }
+ pprintd2(s, "%d %d ", width, height);
+ pprints1(s, "%s setpagesize\n", p->size_name);
+ }
+ pprintd1(s, "/pagesave save store %d dict begin\n", dictsize);
+ if (do_scale)
+ pprintg2(s, "%g %g scale\n",
+ 72.0 / dev->HWResolution[0], 72.0 / dev->HWResolution[1]);
+ stream_puts(s, "%%EndPageSetup\ngsave mark\n");
+ if (s->end_status == ERRC)
+ return_error(gs_error_ioerror);
+ return 0;
+}
+
+/*
+ * Write the page trailer. We do this directly to the file, rather than to
+ * the stream, because we may have to do it during finalization.
+ */
+int
+psw_write_page_trailer(FILE *f, int num_copies, int flush)
+{
+ fprintf(f, "cleartomark end end pagesave restore\n");
+ if (num_copies != 1)
+ fprintf(f, "userdict /#copies %d put\n", num_copies);
+ fprintf(f, " %s\n%%%%PageTrailer\n", (flush ? "showpage" : "copypage"));
+ fflush(f);
+ if (ferror(f))
+ return_error(gs_error_ioerror);
+ return 0;
+}
diff --git a/devices/vector/gdevpsu.h b/devices/vector/gdevpsu.h
new file mode 100644
index 000000000..071349bd1
--- /dev/null
+++ b/devices/vector/gdevpsu.h
@@ -0,0 +1,70 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Interface to PostScript-writing utilities */
+
+#ifndef gdevpsu_INCLUDED
+# define gdevpsu_INCLUDED
+
+/* Define parameters and state for PostScript-writing drivers. */
+typedef struct gx_device_pswrite_common_s {
+ float LanguageLevel;
+ bool ProduceEPS;
+ int ProcSet_version;
+ int64_t bbox_position; /* set when writing file header */
+} gx_device_pswrite_common_t;
+#define PSWRITE_COMMON_PROCSET_VERSION 1000 /* for definitions in gdevpsu.c */
+#define PSWRITE_COMMON_VALUES(ll, eps, psv)\
+ {ll, eps, PSWRITE_COMMON_PROCSET_VERSION + (psv)}
+
+/* ---------------- Low level ---------------- */
+
+/* Write a 0-terminated array of strings as lines. */
+int psw_print_lines(FILE *f, const char *const lines[]);
+
+/* ---------------- File level ---------------- */
+
+/*
+ * Write the file header, up through the BeginProlog. This must write to a
+ * file, not a stream, because it may be called during finalization.
+ */
+int psw_begin_file_header(FILE *f, const gx_device *dev,
+ const gs_rect *pbbox,
+ gx_device_pswrite_common_t *pdpc, bool ascii);
+
+/* End the file header.*/
+int psw_end_file_header(FILE *f);
+
+/* End the file. */
+int psw_end_file(FILE *f, const gx_device *dev,
+ const gx_device_pswrite_common_t *pdpc,
+ const gs_rect *pbbox, int page_count);
+
+/* ---------------- Page level ---------------- */
+
+/*
+ * Write the page header.
+ */
+int psw_write_page_header(stream *s, const gx_device *dev,
+ const gx_device_pswrite_common_t *pdpc,
+ bool do_scale, long page_ord, int dictsize);
+/*
+ * Write the page trailer. We do this directly to the file, rather than to
+ * the stream, because we may have to do it during finalization.
+ */
+int psw_write_page_trailer(FILE *f, int num_copies, int flush);
+
+#endif /* gdevpsu_INCLUDED */
diff --git a/devices/vector/gdevpx.c b/devices/vector/gdevpx.c
new file mode 100644
index 000000000..4ecb91c73
--- /dev/null
+++ b/devices/vector/gdevpx.c
@@ -0,0 +1,2551 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* H-P PCL XL driver */
+#include "math_.h"
+#include "memory_.h"
+#include "gx.h"
+#include "gserrors.h"
+#include "gsccolor.h"
+#include "gsdcolor.h"
+#include "gxiparam.h"
+#include "gxcspace.h" /* for color mapping for images */
+#include "gxdevice.h"
+#include "gxpath.h"
+#include "gdevvec.h"
+#include "strimpl.h"
+#include "srlx.h"
+#include "jpeglib_.h"
+#include "sdct.h"
+#include "sjpeg.h"
+#include "gdevpxat.h"
+#include "gdevpxen.h"
+#include "gdevpxop.h"
+#include "gdevpxut.h"
+#include "gxlum.h"
+#include "gdevpcl.h" /* for gdev_pcl_mode3compress() */
+#include "gsicc_manage.h"
+#include "gsicc_cache.h"
+#include <stdlib.h> /* abs() */
+
+/* ---------------- Device definition ---------------- */
+
+/* Define the default resolution. */
+#ifndef X_DPI
+# define X_DPI 600
+#endif
+#ifndef Y_DPI
+# define Y_DPI 600
+#endif
+
+/* Structure definition */
+#define NUM_POINTS 40 /* must be >= 3 and <= 255 */
+typedef enum {
+ POINTS_NONE,
+ POINTS_LINES,
+ POINTS_CURVES
+} point_type_t;
+typedef struct gx_device_pclxl_s {
+ gx_device_vector_common;
+ /* Additional state information */
+ pxeMediaSize_t media_size;
+ bool ManualFeed; /* map ps setpage commands to pxl */
+ bool ManualFeed_set;
+ int MediaPosition_old; /* old Position attribute - for duplex detection */
+ int MediaPosition; /* MediaPosition attribute */
+ int MediaPosition_set;
+ char MediaType_old[64]; /* old MediaType attribute - for duplex detection */
+ char MediaType[64]; /* MediaType attribute */
+ int MediaType_set;
+ int page; /* Page number starting at 0 */
+ bool Duplex; /* Duplex attribute */
+ bool Tumble; /* Tumble attribute */
+ gx_path_type_t fill_rule; /* ...winding_number or ...even_odd */
+ gx_path_type_t clip_rule; /* ditto */
+ pxeColorSpace_t color_space;
+ struct pal_ {
+ int size; /* # of bytes */
+ byte data[256 * 3]; /* up to 8-bit samples */
+ } palette;
+ struct pts_ { /* buffer for accumulating path points */
+ gs_int_point current; /* current point as of start of data */
+ point_type_t type;
+ int count;
+ gs_int_point data[NUM_POINTS];
+ } points;
+ struct ch_ { /* cache for downloaded characters */
+#define MAX_CACHED_CHARS 400
+#define MAX_CHAR_DATA 500000
+#define MAX_CHAR_SIZE 5000
+#define CHAR_HASH_FACTOR 247
+ ushort table[MAX_CACHED_CHARS * 3 / 2];
+ struct cd_ {
+ gs_id id; /* key */
+ uint size;
+ } data[MAX_CACHED_CHARS];
+ int next_in; /* next data element to fill in */
+ int next_out; /* next data element to discard */
+ int count; /* of occupied data elements */
+ ulong used;
+ } chars;
+ bool font_set;
+ int state_rotated; /* 0, 1, 2, -1, mutiple of 90 deg */
+ int CompressMode; /* std PXL enum: None=0, RLE=1, JPEG=2, DeltaRow=3 */
+ bool scaled;
+ double x_scale; /* chosen so that max(x) is scaled to 0x7FFF, to give max distinction between x values */
+ double y_scale;
+ bool pen_null;
+ bool brush_null;
+ bool iccTransform;
+} gx_device_pclxl;
+
+gs_public_st_suffix_add0_final(st_device_pclxl, gx_device_pclxl,
+ "gx_device_pclxl",
+ device_pclxl_enum_ptrs, device_pclxl_reloc_ptrs,
+ gx_device_finalize, st_device_vector);
+
+#define pclxl_device_body(dname, depth)\
+ std_device_dci_type_body(gx_device_pclxl, 0, dname, &st_device_pclxl,\
+ DEFAULT_WIDTH_10THS * X_DPI / 10,\
+ DEFAULT_HEIGHT_10THS * Y_DPI / 10,\
+ X_DPI, Y_DPI,\
+ (depth > 8 ? 3 : 1), depth,\
+ (depth > 1 ? 255 : 1), (depth > 8 ? 255 : 0),\
+ (depth > 1 ? 256 : 2), (depth > 8 ? 256 : 1))
+
+/* Driver procedures */
+static dev_proc_open_device(pclxl_open_device);
+static dev_proc_output_page(pclxl_output_page);
+static dev_proc_close_device(pclxl_close_device);
+static dev_proc_copy_mono(pclxl_copy_mono);
+static dev_proc_copy_color(pclxl_copy_color);
+static dev_proc_fill_mask(pclxl_fill_mask);
+
+static dev_proc_get_params(pclxl_get_params);
+static dev_proc_put_params(pclxl_put_params);
+
+/*static dev_proc_draw_thin_line(pclxl_draw_thin_line); */
+static dev_proc_begin_image(pclxl_begin_image);
+static dev_proc_strip_copy_rop(pclxl_strip_copy_rop);
+
+#define pclxl_device_procs(map_rgb_color, map_color_rgb)\
+{\
+ pclxl_open_device,\
+ NULL, /* get_initial_matrix */\
+ NULL, /* sync_output */\
+ pclxl_output_page,\
+ pclxl_close_device,\
+ map_rgb_color, /* differs */\
+ map_color_rgb, /* differs */\
+ gdev_vector_fill_rectangle,\
+ NULL, /* tile_rectangle */\
+ pclxl_copy_mono,\
+ pclxl_copy_color,\
+ NULL, /* draw_line */\
+ NULL, /* get_bits */\
+ pclxl_get_params,\
+ pclxl_put_params,\
+ NULL, /* map_cmyk_color */\
+ NULL, /* get_xfont_procs */\
+ NULL, /* get_xfont_device */\
+ NULL, /* map_rgb_alpha_color */\
+ gx_page_device_get_page_device,\
+ NULL, /* get_alpha_bits */\
+ NULL, /* copy_alpha */\
+ NULL, /* get_band */\
+ NULL, /* copy_rop */\
+ gdev_vector_fill_path,\
+ gdev_vector_stroke_path,\
+ pclxl_fill_mask,\
+ gdev_vector_fill_trapezoid,\
+ gdev_vector_fill_parallelogram,\
+ gdev_vector_fill_triangle,\
+ NULL /****** WRONG ******/, /* draw_thin_line */\
+ pclxl_begin_image,\
+ NULL, /* image_data */\
+ NULL, /* end_image */\
+ NULL, /* strip_tile_rectangle */\
+ pclxl_strip_copy_rop\
+}
+
+const gx_device_pclxl gs_pxlmono_device = {
+ pclxl_device_body("pxlmono", 8),
+ pclxl_device_procs(gx_default_gray_map_rgb_color, gx_default_gray_map_color_rgb)
+};
+
+const gx_device_pclxl gs_pxlcolor_device = {
+ pclxl_device_body("pxlcolor", 24),
+ pclxl_device_procs(gx_default_rgb_map_rgb_color, gx_default_rgb_map_color_rgb)
+};
+
+/* ---------------- Other utilities ---------------- */
+
+static inline stream *
+pclxl_stream(gx_device_pclxl *xdev)
+{
+ return gdev_vector_stream((gx_device_vector *)xdev);
+}
+
+/* Initialize for a page. */
+static void
+pclxl_page_init(gx_device_pclxl * xdev)
+{
+ gdev_vector_init((gx_device_vector *)xdev);
+ xdev->in_page = false;
+ xdev->fill_rule = gx_path_type_winding_number;
+ xdev->clip_rule = gx_path_type_winding_number;
+ xdev->color_space = eNoColorSpace;
+ xdev->palette.size = 0;
+ xdev->font_set = false;
+ xdev->state_rotated = 0;
+ xdev->scaled = false;
+ xdev->x_scale = 1;
+ xdev->y_scale = 1;
+ xdev->pen_null = false;
+ xdev->brush_null = false;
+}
+
+/* Test whether a RGB color is actually a gray shade. */
+#define RGB_IS_GRAY(ci) ((ci) >> 8 == ((ci) & 0xffff))
+
+/* Set the color space and (optionally) palette. */
+static void
+pclxl_set_color_space(gx_device_pclxl * xdev, pxeColorSpace_t color_space)
+{
+ if (xdev->color_space != color_space) {
+ stream *s = pclxl_stream(xdev);
+
+ px_put_ub(s, (byte)color_space);
+ px_put_ac(s, pxaColorSpace, pxtSetColorSpace);
+ xdev->color_space = color_space;
+ xdev->palette.size = 0; /* purge the cached palette */
+ }
+}
+static void
+pclxl_set_color_palette(gx_device_pclxl * xdev, pxeColorSpace_t color_space,
+ const byte * palette, uint palette_size)
+{
+ if (xdev->color_space != color_space ||
+ xdev->palette.size != palette_size ||
+ memcmp(xdev->palette.data, palette, palette_size)
+ ) {
+ stream *s = pclxl_stream(xdev);
+ static const byte csp_[] = {
+ DA(pxaColorSpace),
+ DUB(e8Bit), DA(pxaPaletteDepth),
+ pxt_ubyte_array
+ };
+
+ px_put_ub(s, (byte)color_space);
+ PX_PUT_LIT(s, csp_);
+ px_put_u(s, palette_size);
+ px_put_bytes(s, palette, palette_size);
+ px_put_ac(s, pxaPaletteData, pxtSetColorSpace);
+ xdev->color_space = color_space;
+ xdev->palette.size = palette_size;
+ memcpy(xdev->palette.data, palette, palette_size);
+ }
+}
+
+/* For caching either NullPen or NullBrush, which happens a lot for
+ * drawing masks in the PS3 CET test set.
+ *
+ * The expected null_source/op combos are:
+ * pxaNullPen/pxtSetPenSource and pxaNullBrush/pxtSetBrushSource
+ */
+static int
+pclxl_set_cached_nulls(gx_device_pclxl * xdev, px_attribute_t null_source, px_tag_t op)
+{
+ stream *s = pclxl_stream(xdev);
+ if (op == pxtSetPenSource) {
+ if (xdev->pen_null)
+ return 0;
+ else
+ xdev->pen_null = true;
+ }
+ if (op == pxtSetBrushSource) {
+ if (xdev->brush_null)
+ return 0;
+ else
+ xdev->brush_null = true;
+ }
+ px_put_uba(s, 0, (byte)null_source);
+ spputc(s, (byte)op);
+ return 0;
+}
+
+/* Set a drawing RGB color. */
+static int
+pclxl_set_color(gx_device_pclxl * xdev, const gx_drawing_color * pdc,
+ px_attribute_t null_source, px_tag_t op)
+{
+ stream *s = pclxl_stream(xdev);
+
+ if (gx_dc_is_pure(pdc)) {
+ gx_color_index color = gx_dc_pure_color(pdc);
+
+ if (op == pxtSetPenSource) xdev->pen_null = false;
+ if (op == pxtSetBrushSource) xdev->brush_null = false;
+
+ if (xdev->color_info.num_components == 1 || RGB_IS_GRAY(color)) {
+ pclxl_set_color_space(xdev, eGray);
+ px_put_uba(s, (byte) color, pxaGrayLevel);
+ } else {
+ pclxl_set_color_space(xdev, eRGB);
+ spputc(s, pxt_ubyte_array);
+ px_put_ub(s, 3);
+ spputc(s, (byte) (color >> 16));
+ spputc(s, (byte) (color >> 8));
+ spputc(s, (byte) color);
+ px_put_a(s, pxaRGBColor);
+ }
+ } else if (gx_dc_is_null(pdc) || !color_is_set(pdc)) {
+ if (op == pxtSetPenSource || op == pxtSetBrushSource)
+ return pclxl_set_cached_nulls(xdev, null_source, op);
+ else
+ px_put_uba(s, 0, null_source);
+ } else
+ return_error(gs_error_rangecheck);
+ spputc(s, (byte)op);
+ return 0;
+}
+
+/* Test whether we can handle a given color space in an image. */
+/* We cannot handle ICCBased color spaces. */
+static bool
+pclxl_can_handle_color_space(const gs_color_space * pcs)
+{
+ gs_color_space_index index;
+ /* an image with no colorspace info arrived; cannot handle */
+ if (!pcs)
+ return false;
+ index = gs_color_space_get_index(pcs);
+
+ if (index == gs_color_space_index_Indexed) {
+ if (pcs->params.indexed.use_proc)
+ return false;
+ index =
+ gs_color_space_get_index(gs_color_space_indexed_base_space(pcs));
+ }
+ else if (index == gs_color_space_index_ICC)
+ {
+ index = gsicc_get_default_type(pcs->cmm_icc_profile_data);
+ return ((index < gs_color_space_index_DevicePixel) ? true : false);
+ }
+
+ return !(index == gs_color_space_index_Separation ||
+ index == gs_color_space_index_Pattern ||
+ index == gs_color_space_index_ICC);
+}
+
+/* Test whether we can icclink-transform an image. */
+static bool
+pclxl_can_icctransform(const gs_image_t * pim)
+{
+ const gs_color_space *pcs = pim->ColorSpace;
+ int bits_per_pixel;
+ /* an image with no colorspace info arrived; cannot transform */
+ if (!pcs)
+ return false;
+ bits_per_pixel =
+ (pim->ImageMask ? 1 :
+ pim->BitsPerComponent * gs_color_space_num_components(pcs));
+
+ if ((gs_color_space_get_index(pcs) == gs_color_space_index_ICC)
+ && (bits_per_pixel == 24))
+ return true;
+
+ return false;
+}
+
+/*
+ * Avoid PXL high level images if a transfer function has been set.
+ * Allow the graphics library to render to a lower level
+ * representation with the function applied to the colors.
+ */
+
+static bool
+pclxl_nontrivial_transfer(const gs_imager_state * pis)
+{
+ gx_transfer_map *red = pis->set_transfer.red;
+ gx_transfer_map *green = pis->set_transfer.green;
+ gx_transfer_map *blue = pis->set_transfer.blue;
+
+ return (red || green || blue);
+
+}
+/* Set brush, pen, and mode for painting a path. */
+static void
+pclxl_set_paints(gx_device_pclxl * xdev, gx_path_type_t type)
+{
+ stream *s = pclxl_stream(xdev);
+ gx_path_type_t rule = type & gx_path_type_rule;
+
+ if (!(type & gx_path_type_fill) &&
+ (color_is_set(&xdev->saved_fill_color.saved_dev_color) ||
+ !gx_dc_is_null(&xdev->saved_fill_color.saved_dev_color)
+ )
+ ) {
+ pclxl_set_cached_nulls(xdev, pxaNullBrush, pxtSetBrushSource);
+ color_set_null(&xdev->saved_fill_color.saved_dev_color);
+ if (rule != xdev->fill_rule) {
+ px_put_ub(s, (byte)(rule == gx_path_type_even_odd ? eEvenOdd :
+ eNonZeroWinding));
+ px_put_ac(s, pxaFillMode, pxtSetFillMode);
+ xdev->fill_rule = rule;
+ }
+ }
+ if (!(type & gx_path_type_stroke) &&
+ (color_is_set(&xdev->saved_stroke_color.saved_dev_color) ||
+ !gx_dc_is_null(&xdev->saved_stroke_color.saved_dev_color)
+ )
+ ) {
+ pclxl_set_cached_nulls(xdev, pxaNullPen, pxtSetPenSource);
+ color_set_null(&xdev->saved_stroke_color.saved_dev_color);
+ }
+}
+
+static void
+pclxl_set_page_origin(stream *s, int x, int y)
+{
+ px_put_ssp(s, x, y);
+ px_put_ac(s, pxaPageOrigin, pxtSetPageOrigin);
+ return;
+}
+
+static void
+pclxl_set_page_scale(gx_device_pclxl * xdev, double x_scale, double y_scale)
+{
+ stream *s = pclxl_stream(xdev);
+ if (xdev->scaled) {
+ xdev->x_scale = x_scale;
+ xdev->y_scale = y_scale;
+ px_put_rp(s, x_scale, y_scale);
+ px_put_ac(s, pxaPageScale, pxtSetPageScale);
+ }
+ return;
+}
+
+static void
+pclxl_unset_page_scale(gx_device_pclxl * xdev)
+{
+ stream *s = pclxl_stream(xdev);
+ if (xdev->scaled) {
+ px_put_rp(s, 1/xdev->x_scale, 1/xdev->y_scale);
+ px_put_ac(s, pxaPageScale, pxtSetPageScale);
+ xdev->scaled = false;
+ xdev->x_scale = 1;
+ xdev->y_scale = 1;
+ }
+ return;
+}
+
+/* Set the cursor. */
+static int
+pclxl_set_cursor(gx_device_pclxl * xdev, int x, int y)
+{
+ stream *s = pclxl_stream(xdev);
+ double x_scale = 1;
+ double y_scale = 1;
+ /* Points must be one of ubyte/uint16/sint16;
+ Here we play with PageScale (one of ubyte/uint16/real32_xy) to go higher.
+ This gives us 32768 x 3.4e38 in UnitsPerMeasure.
+ If we ever need to go higher, we play with UnitsPerMeasure. */
+ if (abs(x) > 0x7FFF) {
+ x_scale = ((double) abs(x))/0x7FFF;
+ x = (x > 0 ? 0x7FFF : -0x7FFF);
+ xdev->scaled = true;
+ }
+ if (abs(y) > 0x7FFF) {
+ y_scale = ((double) abs(y))/0x7FFF;
+ y = (y > 0 ? 0x7FFF : -0x7FFF);
+ xdev->scaled = true;
+ }
+ pclxl_set_page_scale(xdev, x_scale, y_scale);
+ px_put_ssp(s, x, y);
+ px_put_ac(s, pxaPoint, pxtSetCursor);
+ pclxl_unset_page_scale(xdev);
+ return 0;
+}
+
+/* ------ Paths ------ */
+
+/* Flush any buffered path points. */
+static void
+px_put_np(stream * s, int count, pxeDataType_t dtype)
+{
+ px_put_uba(s, (byte)count, pxaNumberOfPoints);
+ px_put_uba(s, (byte)dtype, pxaPointType);
+}
+static int
+pclxl_flush_points(gx_device_pclxl * xdev)
+{
+ int count = xdev->points.count;
+
+ if (count) {
+ stream *s = pclxl_stream(xdev);
+ px_tag_t op;
+ int x = xdev->points.current.x, y = xdev->points.current.y;
+ int uor = 0, sor = 0;
+ pxeDataType_t data_type;
+ int i, di;
+ byte diffs[NUM_POINTS * 2];
+ double x_scale = 1;
+ double y_scale = 1;
+ int temp_origin_x = 0, temp_origin_y = 0;
+ int count_smalls = 0;
+
+ if (xdev->points.type != POINTS_NONE) {
+ for (i = 0; i < count; ++i) {
+ if ((abs(xdev->points.data[i].x) > 0x7FFF) || (abs(xdev->points.data[i].y) > 0x7FFF))
+ xdev->scaled = true;
+ if ((abs(xdev->points.data[i].x) < 0x8000) && (abs(xdev->points.data[i].y) < 0x8000)) {
+ if ((temp_origin_x != xdev->points.data[i].x) || (temp_origin_y != xdev->points.data[i].y)) {
+ temp_origin_x = xdev->points.data[i].x;
+ temp_origin_y = xdev->points.data[i].y;
+ count_smalls++;
+ }
+ }
+ }
+ if (xdev->scaled) {
+ /* if there are some points with small co-ordinates, we set origin to it
+ before scaling, an unset afterwards. This works around problems
+ for small co-ordinates being moved snapped to 32767 x 32767 grid points;
+ if there are more than 1, the other points
+ will be in-accurate, unfortunately */
+ if (count_smalls) {
+ pclxl_set_page_origin(s, temp_origin_x, temp_origin_y);
+ }
+ for (i = 0; i < count; ++i) {
+ x_scale = max(((double) abs(xdev->points.data[i].x - temp_origin_x))/0x7FFF , x_scale);
+ y_scale = max(((double) abs(xdev->points.data[i].y - temp_origin_y))/0x7FFF , y_scale);
+ }
+ for (i = 0; i < count; ++i) {
+ xdev->points.data[i].x = (int)((xdev->points.data[i].x - temp_origin_x)/x_scale + 0.5);
+ xdev->points.data[i].y = (int)((xdev->points.data[i].y - temp_origin_y)/y_scale + 0.5);
+ }
+ x = (int)((x - temp_origin_x)/x_scale + 0.5);
+ y = (int)((y - temp_origin_y)/y_scale + 0.5);
+ pclxl_set_page_scale(xdev, x_scale, y_scale);
+ } else {
+ /* don't reset origin if we did not scale */
+ count_smalls = 0;
+ }
+ }
+ /*
+ * Writing N lines using a point list requires 11 + 4*N or 11 +
+ * 2*N bytes, as opposed to 8*N bytes using separate commands;
+ * writing N curves requires 11 + 12*N or 11 + 6*N bytes
+ * vs. 22*N. So it's always shorter to write curves with a
+ * list (except for N = 1 with full-size coordinates, but since
+ * the difference is only 1 byte, we don't bother to ever use
+ * the non-list form), but lines are shorter only if N >= 3
+ * (again, with a 1-byte difference if N = 2 and byte
+ * coordinates).
+ */
+ switch (xdev->points.type) {
+ case POINTS_NONE:
+ return 0;
+ case POINTS_LINES:
+ op = pxtLinePath;
+ if (count < 3) {
+ for (i = 0; i < count; ++i) {
+ px_put_ssp(s, xdev->points.data[i].x,
+ xdev->points.data[i].y);
+ px_put_a(s, pxaEndPoint);
+ spputc(s, (byte)op);
+ }
+ pclxl_unset_page_scale(xdev);
+ if (count_smalls)
+ pclxl_set_page_origin(s, -temp_origin_x, -temp_origin_y);
+ goto zap;
+ }
+ /* See if we can use byte values. */
+ for (i = di = 0; i < count; ++i, di += 2) {
+ int dx = xdev->points.data[i].x - x;
+ int dy = xdev->points.data[i].y - y;
+
+ diffs[di] = (byte) dx;
+ diffs[di + 1] = (byte) dy;
+ uor |= dx | dy;
+ sor |= (dx + 0x80) | (dy + 0x80);
+ x += dx, y += dy;
+ }
+ if (!(uor & ~0xff))
+ data_type = eUByte;
+ else if (!(sor & ~0xff))
+ data_type = eSByte;
+ else
+ break;
+ op = pxtLineRelPath;
+ /* Use byte values. */
+ useb:px_put_np(s, count, data_type);
+ spputc(s, (byte)op);
+ px_put_data_length(s, count * 2); /* 2 bytes per point */
+ px_put_bytes(s, diffs, count * 2);
+ pclxl_unset_page_scale(xdev);
+ if (count_smalls)
+ pclxl_set_page_origin(s, -temp_origin_x, -temp_origin_y);
+ goto zap;
+ case POINTS_CURVES:
+ op = pxtBezierPath;
+ /* See if we can use byte values. */
+ for (i = di = 0; i < count; i += 3, di += 6) {
+ int dx1 = xdev->points.data[i].x - x;
+ int dy1 = xdev->points.data[i].y - y;
+ int dx2 = xdev->points.data[i + 1].x - x;
+ int dy2 = xdev->points.data[i + 1].y - y;
+ int dx = xdev->points.data[i + 2].x - x;
+ int dy = xdev->points.data[i + 2].y - y;
+
+ diffs[di] = (byte) dx1;
+ diffs[di + 1] = (byte) dy1;
+ diffs[di + 2] = (byte) dx2;
+ diffs[di + 3] = (byte) dy2;
+ diffs[di + 4] = (byte) dx;
+ diffs[di + 5] = (byte) dy;
+ uor |= dx1 | dy1 | dx2 | dy2 | dx | dy;
+ sor |= (dx1 + 0x80) | (dy1 + 0x80) |
+ (dx2 + 0x80) | (dy2 + 0x80) |
+ (dx + 0x80) | (dy + 0x80);
+ x += dx, y += dy;
+ }
+ if (!(uor & ~0xff))
+ data_type = eUByte;
+ else if (!(sor & ~0xff))
+ data_type = eSByte;
+ else
+ break;
+ op = pxtBezierRelPath;
+ goto useb;
+ default: /* can't happen */
+ return_error(gs_error_unknownerror);
+ }
+ px_put_np(s, count, eSInt16);
+ spputc(s, (byte)op);
+ px_put_data_length(s, count * 4); /* 2 UInt16s per point */
+ for (i = 0; i < count; ++i) {
+ px_put_s(s, xdev->points.data[i].x);
+ px_put_s(s, xdev->points.data[i].y);
+ }
+ pclxl_unset_page_scale(xdev);
+ if (count_smalls)
+ pclxl_set_page_origin(s, -temp_origin_x, -temp_origin_y);
+ zap:xdev->points.type = POINTS_NONE;
+ xdev->points.count = 0;
+ }
+ return 0;
+}
+
+/* ------ Images ------ */
+
+static image_enum_proc_plane_data(pclxl_image_plane_data);
+static image_enum_proc_end_image(pclxl_image_end_image);
+static const gx_image_enum_procs_t pclxl_image_enum_procs = {
+ pclxl_image_plane_data, pclxl_image_end_image
+};
+
+/* Begin an image. */
+static void
+pclxl_write_begin_image(gx_device_pclxl * xdev, uint width, uint height,
+ uint dest_width, uint dest_height)
+{
+ stream *s = pclxl_stream(xdev);
+
+ px_put_usa(s, width, pxaSourceWidth);
+ px_put_usa(s, height, pxaSourceHeight);
+ px_put_usp(s, dest_width, dest_height);
+ px_put_ac(s, pxaDestinationSize, pxtBeginImage);
+}
+
+/* Write rows of an image. */
+/****** IGNORES data_bit ******/
+/* 2009: we try to cope with the case of data_bit being multiple of 8 now */
+/* RLE version */
+static void
+pclxl_write_image_data_RLE(gx_device_pclxl * xdev, const byte * base, int data_bit,
+ uint raster, uint width_bits, int y, int height)
+{
+ stream *s = pclxl_stream(xdev);
+ uint width_bytes = (width_bits + 7) >> 3;
+ uint num_bytes = ROUND_UP(width_bytes, 4) * height;
+ bool compress = num_bytes >= 8;
+ int i;
+ /* cannot handle data_bit not multiple of 8, but we don't invoke this routine that way */
+ int offset = data_bit >> 3;
+ const byte *data = base + offset;
+
+ px_put_usa(s, y, pxaStartLine);
+ px_put_usa(s, height, pxaBlockHeight);
+ if (compress) {
+ stream_RLE_state rlstate;
+ stream_cursor_write w;
+ stream_cursor_read r;
+
+ /*
+ * H-P printers require that all the data for an operator be
+ * contained in a single data block. Thus, we must allocate a
+ * temporary buffer for the compressed data. Currently we don't go
+ * to the trouble of doing two passes if we can't allocate a buffer
+ * large enough for the entire transfer.
+ */
+ byte *buf = gs_alloc_bytes(xdev->v_memory, num_bytes,
+ "pclxl_write_image_data");
+
+ if (buf == 0)
+ goto nc;
+ s_RLE_set_defaults_inline(&rlstate);
+ rlstate.EndOfData = false;
+ s_RLE_init_inline(&rlstate);
+ w.ptr = buf - 1;
+ w.limit = w.ptr + num_bytes;
+ /*
+ * If we ever overrun the buffer, it means that the compressed
+ * data was larger than the uncompressed. If this happens,
+ * write the data uncompressed.
+ */
+ for (i = 0; i < height; ++i) {
+ r.ptr = data + i * raster - 1;
+ r.limit = r.ptr + width_bytes;
+ if ((*s_RLE_template.process)
+ ((stream_state *) & rlstate, &r, &w, true) != 0 ||
+ r.ptr != r.limit
+ )
+ goto ncfree;
+ r.ptr = (const byte *)"\000\000\000\000\000";
+ r.limit = r.ptr + (-(int)width_bytes & 3);
+ if ((*s_RLE_template.process)
+ ((stream_state *) & rlstate, &r, &w, true) != 0 ||
+ r.ptr != r.limit
+ )
+ goto ncfree;
+ }
+ r.ptr = r.limit;
+ if ((*s_RLE_template.process)
+ ((stream_state *) & rlstate, &r, &w, true) != 0
+ )
+ goto ncfree;
+ {
+ uint count = w.ptr + 1 - buf;
+
+ px_put_ub(s, eRLECompression);
+ px_put_ac(s, pxaCompressMode, pxtReadImage);
+ px_put_data_length(s, count);
+ px_put_bytes(s, buf, count);
+ }
+ gs_free_object(xdev->v_memory, buf, "pclxl_write_image_data");
+ return;
+ ncfree:gs_free_object(xdev->v_memory, buf, "pclxl_write_image_data");
+ }
+ nc:
+ /* Write the data uncompressed. */
+ px_put_ub(s, eNoCompression);
+ px_put_ac(s, pxaCompressMode, pxtReadImage);
+ px_put_data_length(s, num_bytes);
+ for (i = 0; i < height; ++i) {
+ px_put_bytes(s, data + i * raster, width_bytes);
+ px_put_bytes(s, (const byte *)"\000\000\000\000", -(int)width_bytes & 3);
+ }
+}
+
+static void
+pclxl_write_image_data_JPEG(gx_device_pclxl * xdev, const byte * base,
+ int data_bit, uint raster, uint width_bits, int y,
+ int height)
+{
+ stream *s = pclxl_stream(xdev);
+ uint width_bytes = (width_bits + 7) >> 3;
+ int i;
+ int count;
+ int code;
+
+ /* cannot handle data_bit not multiple of 8, but we don't invoke this routine that way */
+ int offset = data_bit >> 3;
+ const byte *data = base + offset;
+ jpeg_compress_data *jcdp =
+ gs_alloc_struct_immovable(xdev->v_memory, jpeg_compress_data,
+ &st_jpeg_compress_data,
+ "pclxl_write_image_data_JPEG(jpeg_compress_data)");
+ stream_DCT_state state;
+ stream_cursor_read r;
+ stream_cursor_write w;
+ /* Approx. The worse case is ~ header + width_bytes * height.
+ Apparently minimal SOI/DHT/DQT/SOS/EOI is 341 bytes. TO CHECK. */
+ int buffersize = 341 + width_bytes * height;
+
+ byte *buf = gs_alloc_bytes(xdev->v_memory, buffersize,
+ "pclxl_write_image_data_JPEG(buf)");
+ /* RLE can write uncompressed without extra-allocation */
+ if ((buf == 0) || (jcdp == 0)) {
+ goto failed_so_use_rle_instead;
+ }
+ /* Create the DCT encoder state. */
+ jcdp->templat = s_DCTE_template;
+ s_init_state((stream_state *) & state, &jcdp->templat, 0);
+ if (state.templat->set_defaults) {
+ state.memory = xdev->v_memory;
+ (*state.templat->set_defaults) ((stream_state *) & state);
+ state.memory = NULL;
+ }
+ state.ColorTransform = (xdev->color_info.num_components == 3 ? 1 : 0);
+ state.data.compress = jcdp;
+ state.icc_profile = NULL;
+ jcdp->memory = state.jpeg_memory = xdev->v_memory;
+ if ((code = gs_jpeg_create_compress(&state)) < 0)
+ goto cleanup_and_use_rle;
+ /* image-specific info */
+ jcdp->cinfo.image_width = width_bytes / xdev->color_info.num_components;
+ jcdp->cinfo.image_height = height;
+ switch (xdev->color_info.num_components) {
+ case 3:
+ jcdp->cinfo.input_components = 3;
+ jcdp->cinfo.in_color_space = JCS_RGB;
+ break;
+ case 1:
+ jcdp->cinfo.input_components = 1;
+ jcdp->cinfo.in_color_space = JCS_GRAYSCALE;
+ break;
+ default:
+ goto cleanup_and_use_rle;
+ break;
+ }
+ /* Set compression parameters. */
+ if ((code = gs_jpeg_set_defaults(&state)) < 0)
+ goto cleanup_and_use_rle;
+
+ if (state.templat->init)
+ (*state.templat->init) ((stream_state *)&state);
+ state.scan_line_size = jcdp->cinfo.input_components *
+ jcdp->cinfo.image_width;
+ jcdp->templat.min_in_size =
+ max(s_DCTE_template.min_in_size, state.scan_line_size);
+ jcdp->templat.min_out_size =
+ max(s_DCTE_template.min_out_size, state.Markers.size);
+
+ w.ptr = buf - 1;
+ w.limit = w.ptr + buffersize;
+ for (i = 0; i < height; ++i) {
+ r.ptr = data + i * raster - 1;
+ r.limit = r.ptr + width_bytes;
+ if (((code = (*state.templat->process)
+ ((stream_state *) & state, &r, &w, false)) != 0 && code != EOFC) || r.ptr != r.limit)
+ goto cleanup_and_use_rle;
+ }
+ count = w.ptr + 1 - buf;
+ px_put_usa(s, y, pxaStartLine);
+ px_put_usa(s, height, pxaBlockHeight);
+ px_put_ub(s, eJPEGCompression);
+ px_put_ac(s, pxaCompressMode, pxtReadImage);
+ px_put_data_length(s, count);
+ px_put_bytes(s, buf, count);
+
+ gs_free_object(xdev->v_memory, buf,
+ "pclxl_write_image_data_JPEG(buf)");
+ if (jcdp)
+ gs_jpeg_destroy(&state); /* frees *jcdp */
+ return;
+
+ cleanup_and_use_rle:
+ /* cleans up - something went wrong after allocation */
+ gs_free_object(xdev->v_memory, buf,
+ "pclxl_write_image_data_JPEG(buf)");
+ if (jcdp)
+ gs_jpeg_destroy(&state); /* frees *jcdp */
+ /* fall through to redo in RLE */
+ failed_so_use_rle_instead:
+ /* the RLE routine can write without new allocation - use as fallback. */
+ pclxl_write_image_data_RLE(xdev, data, data_bit, raster, width_bits, y,
+ height);
+ return;
+}
+
+/* DeltaRow compression (also called "mode 3"):
+ drawn heavily from gdevcljc.c:cljc_print_page(),
+ This is simplier since PCL XL does not allow
+ compression mix-and-match.
+
+ Worse case of RLE is + 1/128, but worse case of DeltaRow is + 1/8
+ */
+static void
+pclxl_write_image_data_DeltaRow(gx_device_pclxl * xdev, const byte * base, int data_bit,
+ uint raster, uint width_bits, int y, int height)
+{
+ stream *s = pclxl_stream(xdev);
+ uint width_bytes = (width_bits + 7) >> 3;
+ int worst_case_comp_size = width_bytes + (width_bytes / 8) + 1;
+ byte *cdata = 0;
+ byte *prow = 0;
+ int i;
+ int count;
+ /* cannot handle data_bit not multiple of 8, but we don't invoke this routine that way */
+ int offset = data_bit >> 3;
+ const byte *data = base + offset;
+
+ /* allocate the worst case scenario; PCL XL has an extra 2 byte per row compared to PCL5 */
+ byte *buf = gs_alloc_bytes(xdev->v_memory, (worst_case_comp_size + 2)* height,
+ "pclxl_write_image_data_DeltaRow(buf)");
+ prow = gs_alloc_bytes(xdev->v_memory, width_bytes, "pclxl_write_image_data_DeltaRow(prow)");
+ /* the RLE routine can write uncompressed without extra-allocation */
+ if ((buf == 0) || (prow == 0)) {
+ pclxl_write_image_data_RLE(xdev, data, data_bit, raster, width_bits, y, height);
+ return;
+ }
+ /* initialize the seed row */
+ memset(prow, 0, width_bytes);
+ cdata = buf;
+ for (i = 0; i < height; i++) {
+ int compressed_size = gdev_pcl_mode3compress(width_bytes, data + i * raster, prow, cdata + 2);
+ /* PCL XL prepends row data with byte count */
+ *cdata = compressed_size & 0xff;
+ *(cdata+1) = compressed_size >> 8;
+ cdata += compressed_size + 2;
+ }
+ px_put_usa(s, y, pxaStartLine);
+ px_put_usa(s, height, pxaBlockHeight);
+ px_put_ub(s, eDeltaRowCompression);
+ px_put_ac(s, pxaCompressMode, pxtReadImage);
+ count = cdata - buf;
+ px_put_data_length(s, count);
+ px_put_bytes(s, buf, count);
+
+ gs_free_object(xdev->v_memory, buf, "pclxl_write_image_data_DeltaRow(buf)");
+ gs_free_object(xdev->v_memory, prow, "pclxl_write_image_data_DeltaRow(prow)");
+ return;
+}
+
+/* calling from copy_mono/copy_color/fill_mask should never do lossy compression */
+static void
+pclxl_write_image_data(gx_device_pclxl * xdev, const byte * data,
+ int data_bit, uint raster, uint width_bits, int y,
+ int height, bool allow_lossy)
+{
+ /* If we only have 1 line, it does not make sense to do JPEG/DeltaRow */
+ if (height < 2) {
+ pclxl_write_image_data_RLE(xdev, data, data_bit, raster, width_bits,
+ y, height);
+ return;
+ }
+
+ switch (xdev->CompressMode) {
+ case eDeltaRowCompression:
+ pclxl_write_image_data_DeltaRow(xdev, data, data_bit, raster,
+ width_bits, y, height);
+ break;
+ case eJPEGCompression:
+ /* JPEG should not be used for mask or other data */
+ if (allow_lossy)
+ pclxl_write_image_data_JPEG(xdev, data, data_bit, raster,
+ width_bits, y, height);
+ else
+ pclxl_write_image_data_RLE(xdev, data, data_bit, raster,
+ width_bits, y, height);
+ break;
+ case eRLECompression:
+ default:
+ pclxl_write_image_data_RLE(xdev, data, data_bit, raster,
+ width_bits, y, height);
+ break;
+ }
+}
+
+/* End an image. */
+static void
+pclxl_write_end_image(gx_device_pclxl * xdev)
+{
+ spputc(xdev->strm, pxtEndImage);
+}
+
+/* ------ Fonts ------ */
+
+/* Write a string (single- or double-byte). */
+static void
+px_put_string(stream * s, const byte * data, uint len, bool wide)
+{
+ if (wide) {
+ spputc(s, pxt_uint16_array);
+ px_put_u(s, len);
+ px_put_bytes(s, data, len * 2);
+ } else {
+ spputc(s, pxt_ubyte_array);
+ px_put_u(s, len);
+ px_put_bytes(s, data, len);
+ }
+}
+
+/* Write a 16-bit big-endian value. */
+static void
+px_put_us_be(stream * s, uint i)
+{
+ spputc(s, (byte) (i >> 8));
+ spputc(s, (byte) i);
+}
+
+/* Define a bitmap font. The client must call px_put_string */
+/* with the font name immediately before calling this procedure. */
+static void
+pclxl_define_bitmap_font(gx_device_pclxl * xdev)
+{
+ stream *s = pclxl_stream(xdev);
+ static const byte bfh_[] = {
+ DA(pxaFontName), DUB(0), DA(pxaFontFormat),
+ pxtBeginFontHeader,
+ DUS(8 + 6 + 4 + 6), DA(pxaFontHeaderLength),
+ pxtReadFontHeader,
+ pxt_dataLengthByte, 8 + 6 + 4 + 6,
+ 0, 0, 0, 0,
+ 254, 0, (MAX_CACHED_CHARS + 255) >> 8, 0,
+ 'B', 'R', 0, 0, 0, 4
+ };
+ static const byte efh_[] = {
+ 0xff, 0xff, 0, 0, 0, 0,
+ pxtEndFontHeader
+ };
+
+ PX_PUT_LIT(s, bfh_);
+ px_put_us_be(s, (uint) (xdev->HWResolution[0] + 0.5));
+ px_put_us_be(s, (uint) (xdev->HWResolution[1] + 0.5));
+ PX_PUT_LIT(s, efh_);
+}
+
+/* Set the font. The client must call px_put_string */
+/* with the font name immediately before calling this procedure. */
+static void
+pclxl_set_font(gx_device_pclxl * xdev)
+{
+ stream *s = pclxl_stream(xdev);
+ static const byte sf_[] = {
+ DA(pxaFontName), DUB(1), DA(pxaCharSize), DUS(0), DA(pxaSymbolSet),
+ pxtSetFont
+ };
+
+ PX_PUT_LIT(s, sf_);
+}
+
+/* Define a character in a bitmap font. The client must call px_put_string */
+/* with the font name immediately before calling this procedure. */
+static void
+pclxl_define_bitmap_char(gx_device_pclxl * xdev, uint ccode,
+ const byte * data, uint raster, uint width_bits, uint height)
+{
+ stream *s = pclxl_stream(xdev);
+ uint width_bytes = (width_bits + 7) >> 3;
+ uint size = 10 + width_bytes * height;
+ uint i;
+
+ px_put_ac(s, pxaFontName, pxtBeginChar);
+ px_put_u(s, ccode);
+ px_put_a(s, pxaCharCode);
+ if (size > 0xffff) {
+ spputc(s, pxt_uint32);
+ px_put_l(s, (ulong) size);
+ } else
+ px_put_us(s, size);
+ px_put_ac(s, pxaCharDataSize, pxtReadChar);
+ px_put_data_length(s, size);
+ px_put_bytes(s, (const byte *)"\000\000\000\000\000\000", 6);
+ px_put_us_be(s, width_bits);
+ px_put_us_be(s, height);
+ for (i = 0; i < height; ++i)
+ px_put_bytes(s, data + i * raster, width_bytes);
+ spputc(s, pxtEndChar);
+}
+
+/* Write the name of the only font we define. */
+static void
+pclxl_write_font_name(gx_device_pclxl * xdev)
+{
+ stream *s = pclxl_stream(xdev);
+
+ px_put_string(s, (const byte *)"@", 1, false);
+}
+
+/* Look up a bitmap id, return the index in the character table. */
+/* If the id is missing, return an index for inserting. */
+static int
+pclxl_char_index(gx_device_pclxl * xdev, gs_id id)
+{
+ int i, i_empty = -1;
+ uint ccode;
+
+ for (i = (id * CHAR_HASH_FACTOR) % countof(xdev->chars.table);;
+ i = (i == 0 ? countof(xdev->chars.table) : i) - 1
+ ) {
+ ccode = xdev->chars.table[i];
+ if (ccode == 0)
+ return (i_empty >= 0 ? i_empty : i);
+ else if (ccode == 1) {
+ if (i_empty < 0)
+ i_empty = i;
+ else if (i == i_empty) /* full table */
+ return i;
+ } else if (xdev->chars.data[ccode].id == id)
+ return i;
+ }
+}
+
+/* Remove the character table entry at a given index. */
+static void
+pclxl_remove_char(gx_device_pclxl * xdev, int index)
+{
+ uint ccode = xdev->chars.table[index];
+ int i;
+
+ if (ccode < 2)
+ return;
+ xdev->chars.count--;
+ xdev->chars.used -= xdev->chars.data[ccode].size;
+ xdev->chars.table[index] = 1; /* mark as deleted */
+ i = (index == 0 ? countof(xdev->chars.table) : index) - 1;
+ if (xdev->chars.table[i] == 0) {
+ /* The next slot in probe order is empty. */
+ /* Mark this slot and any deleted predecessors as empty. */
+ for (i = index; xdev->chars.table[i] == 1;
+ i = (i == countof(xdev->chars.table) - 1 ? 0 : i + 1)
+ )
+ xdev->chars.table[i] = 0;
+ }
+}
+
+/* Write a bitmap as a text character if possible. */
+/* The caller must set the color, cursor, and RasterOp. */
+/* We know id != gs_no_id. */
+static int
+pclxl_copy_text_char(gx_device_pclxl * xdev, const byte * data,
+ int raster, gx_bitmap_id id, int w, int h)
+{
+ uint width_bytes = (w + 7) >> 3;
+ uint size = width_bytes * h;
+ int index;
+ uint ccode;
+ stream *s = pclxl_stream(xdev);
+
+ if (size > MAX_CHAR_SIZE)
+ return -1;
+ index = pclxl_char_index(xdev, id);
+ if ((ccode = xdev->chars.table[index]) < 2) {
+ /* Enter the character in the table. */
+ while (xdev->chars.used + size > MAX_CHAR_DATA ||
+ xdev->chars.count >= MAX_CACHED_CHARS - 2
+ ) {
+ ccode = xdev->chars.next_out;
+ index = pclxl_char_index(xdev, xdev->chars.data[ccode].id);
+ pclxl_remove_char(xdev, index);
+ xdev->chars.next_out =
+ (ccode == MAX_CACHED_CHARS - 1 ? 2 : ccode + 1);
+ }
+ index = pclxl_char_index(xdev, id);
+ ccode = xdev->chars.next_in;
+ xdev->chars.data[ccode].id = id;
+ xdev->chars.data[ccode].size = size;
+ xdev->chars.table[index] = ccode;
+ xdev->chars.next_in =
+ (ccode == MAX_CACHED_CHARS - 1 ? 2 : ccode + 1);
+ if (!xdev->chars.count++) {
+ /* This is the very first character. */
+ pclxl_write_font_name(xdev);
+ pclxl_define_bitmap_font(xdev);
+ }
+ xdev->chars.used += size;
+ pclxl_write_font_name(xdev);
+ pclxl_define_bitmap_char(xdev, ccode, data, raster, w, h);
+ }
+ if (!xdev->font_set) {
+ pclxl_write_font_name(xdev);
+ pclxl_set_font(xdev);
+ xdev->font_set = true;
+ } {
+ byte cc_bytes[2];
+
+ cc_bytes[0] = (byte) ccode;
+ cc_bytes[1] = ccode >> 8;
+ px_put_string(s, cc_bytes, 1, cc_bytes[1] != 0);
+ }
+ px_put_ac(s, pxaTextData, pxtText);
+ return 0;
+}
+
+/* ---------------- Vector implementation procedures ---------------- */
+
+static int
+pclxl_beginpage(gx_device_vector * vdev)
+{
+ gx_device_pclxl *const xdev = (gx_device_pclxl *)vdev;
+ /*
+ * We can't use gdev_vector_stream here, because this may be called
+ * from there before in_page is set.
+ */
+ stream *s = vdev->strm;
+ byte media_source = eAutoSelect; /* default */
+
+ xdev->page ++; /* even/odd for duplex front/back */
+
+/*
+ errprintf(vdev->memory, "PAGE: %d %d\n", xdev->page, xdev->NumCopies);
+ errprintf(vdev->memory, "INFO: Printing page %d...\n", xdev->page);
+ errflush(vdev->memory);
+*/
+
+ px_write_page_header(s, (const gx_device *)vdev);
+
+ if (xdev->ManualFeed_set && xdev->ManualFeed)
+ media_source = 2;
+ else if (xdev->MediaPosition_set && xdev->MediaPosition >= 0 )
+ media_source = xdev->MediaPosition;
+
+ px_write_select_media(s, (const gx_device *)vdev, &xdev->media_size,
+ &media_source,
+ xdev->page, xdev->Duplex, xdev->Tumble,
+ xdev->MediaType_set, xdev->MediaType);
+
+ spputc(s, pxtBeginPage);
+ return 0;
+}
+
+static int
+pclxl_setlinewidth(gx_device_vector * vdev, double width)
+{
+ stream *s = gdev_vector_stream(vdev);
+
+ px_put_us(s, (uint) (width+0.5));
+ px_put_ac(s, pxaPenWidth, pxtSetPenWidth);
+ return 0;
+}
+
+static int
+pclxl_setlinecap(gx_device_vector * vdev, gs_line_cap cap)
+{
+ stream *s = gdev_vector_stream(vdev);
+
+ /* The PCL XL cap styles just happen to be identical to PostScript. */
+ px_put_ub(s, (byte) cap);
+ px_put_ac(s, pxaLineCapStyle, pxtSetLineCap);
+ return 0;
+}
+
+static int
+pclxl_setlinejoin(gx_device_vector * vdev, gs_line_join join)
+{
+ stream *s = gdev_vector_stream(vdev);
+
+ if ((join < 0) || (join > 3)) {
+ emprintf1(vdev->memory,
+ "Igoring invalid linejoin enumerator %d\n",
+ join);
+ return 0;
+ }
+ /* The PCL XL join styles just happen to be identical to PostScript. */
+ px_put_ub(s, (byte) join);
+ px_put_ac(s, pxaLineJoinStyle, pxtSetLineJoin);
+ return 0;
+}
+
+static int
+pclxl_setmiterlimit(gx_device_vector * vdev, double limit)
+{
+ stream *s = gdev_vector_stream(vdev);
+ /*
+ * Amazingly enough, the PCL XL specification doesn't allow real
+ * numbers for the miter limit.
+ */
+ int i_limit = (int)(limit + 0.5);
+
+ px_put_u(s, max(i_limit, 1));
+ px_put_ac(s, pxaMiterLength, pxtSetMiterLimit);
+ return 0;
+}
+
+static int
+pclxl_setdash(gx_device_vector * vdev, const float *pattern, uint count,
+ double offset)
+{
+ stream *s = gdev_vector_stream(vdev);
+
+ if (count == 0) {
+ static const byte nac_[] = {
+ DUB(0), DA(pxaSolidLine)
+ };
+
+ PX_PUT_LIT(s, nac_);
+ } else if (count > 255)
+ return_error(gs_error_limitcheck);
+ else {
+ uint i;
+
+ /*
+ * Astoundingly, PCL XL doesn't allow real numbers here.
+ * Do the best we can.
+ */
+ spputc(s, pxt_uint16_array);
+ px_put_ub(s, (byte)count);
+ for (i = 0; i < count; ++i)
+ px_put_s(s, (uint)pattern[i]);
+ px_put_a(s, pxaLineDashStyle);
+ if (offset != 0)
+ px_put_usa(s, (uint)offset, pxaDashOffset);
+ }
+ spputc(s, pxtSetLineDash);
+ return 0;
+}
+
+static int
+pclxl_setlogop(gx_device_vector * vdev, gs_logical_operation_t lop,
+ gs_logical_operation_t diff)
+{
+ stream *s = gdev_vector_stream(vdev);
+
+ if (diff & lop_S_transparent) {
+ px_put_ub(s, (byte)(lop & lop_S_transparent ? 1 : 0));
+ px_put_ac(s, pxaTxMode, pxtSetSourceTxMode);
+ }
+ if (diff & lop_T_transparent) {
+ px_put_ub(s, (byte)(lop & lop_T_transparent ? 1 : 0));
+ px_put_ac(s, pxaTxMode, pxtSetPaintTxMode);
+ }
+ if (lop_rop(diff)) {
+ px_put_ub(s, (byte)lop_rop(lop));
+ px_put_ac(s, pxaROP3, pxtSetROP);
+ }
+ return 0;
+}
+
+static int
+pclxl_can_handle_hl_color(gx_device_vector * vdev, const gs_imager_state * pis,
+ const gx_drawing_color * pdc)
+{
+ return false;
+}
+
+static int
+pclxl_setfillcolor(gx_device_vector * vdev, const gs_imager_state * pis,
+ const gx_drawing_color * pdc)
+{
+ gx_device_pclxl *const xdev = (gx_device_pclxl *)vdev;
+
+ return pclxl_set_color(xdev, pdc, pxaNullBrush, pxtSetBrushSource);
+}
+
+static int
+pclxl_setstrokecolor(gx_device_vector * vdev, const gs_imager_state * pis,
+ const gx_drawing_color * pdc)
+{
+ gx_device_pclxl *const xdev = (gx_device_pclxl *)vdev;
+
+ return pclxl_set_color(xdev, pdc, pxaNullPen, pxtSetPenSource);
+}
+
+static int
+pclxl_dorect(gx_device_vector * vdev, fixed x0, fixed y0, fixed x1,
+ fixed y1, gx_path_type_t type)
+{
+ gx_device_pclxl *const xdev = (gx_device_pclxl *)vdev;
+ stream *s = gdev_vector_stream(vdev);
+
+ /* Check for out-of-range points. */
+#define OUT_OF_RANGE(v) (v < 0 || v >= int2fixed(0x10000))
+ if (OUT_OF_RANGE(x0) || OUT_OF_RANGE(y0) ||
+ OUT_OF_RANGE(x1) || OUT_OF_RANGE(y1)
+ )
+ return_error(gs_error_rangecheck);
+#undef OUT_OF_RANGE
+ if (type & (gx_path_type_fill | gx_path_type_stroke)) {
+ pclxl_set_paints(xdev, type);
+ px_put_usq_fixed(s, x0, y0, x1, y1);
+ px_put_ac(s, pxaBoundingBox, pxtRectangle);
+ }
+ if (type & gx_path_type_clip) {
+ static const byte cr_[] = {
+ DA(pxaBoundingBox),
+ DUB(eInterior), DA(pxaClipRegion),
+ pxtSetClipRectangle
+ };
+
+ px_put_usq_fixed(s, x0, y0, x1, y1);
+ PX_PUT_LIT(s, cr_);
+ }
+ return 0;
+}
+
+static int
+pclxl_beginpath(gx_device_vector * vdev, gx_path_type_t type)
+{
+ gx_device_pclxl *const xdev = (gx_device_pclxl *)vdev;
+ stream *s = gdev_vector_stream(vdev);
+
+ spputc(s, pxtNewPath);
+ xdev->points.type = POINTS_NONE;
+ xdev->points.count = 0;
+ return 0;
+}
+
+static int
+pclxl_moveto(gx_device_vector * vdev, double x0, double y0, double x, double y,
+ gx_path_type_t type)
+{
+ gx_device_pclxl *const xdev = (gx_device_pclxl *)vdev;
+ int code = pclxl_flush_points(xdev);
+
+ if (code < 0)
+ return code;
+ return pclxl_set_cursor(xdev,
+ xdev->points.current.x = (int)(x+0.5),
+ xdev->points.current.y = (int)(y+0.5));
+}
+
+static int
+pclxl_lineto(gx_device_vector * vdev, double x0, double y0, double x, double y,
+ gx_path_type_t type)
+{
+ gx_device_pclxl *const xdev = (gx_device_pclxl *)vdev;
+
+ if (xdev->points.type != POINTS_LINES ||
+ xdev->points.count >= NUM_POINTS
+ ) {
+ if (xdev->points.type != POINTS_NONE) {
+ int code = pclxl_flush_points(xdev);
+
+ if (code < 0)
+ return code;
+ }
+ xdev->points.current.x = (int)(x0+0.5);
+ xdev->points.current.y = (int)(y0+0.5);
+ xdev->points.type = POINTS_LINES;
+ } {
+ gs_int_point *ppt = &xdev->points.data[xdev->points.count++];
+
+ ppt->x = (int)(x+0.5), ppt->y = (int)(y+0.5);
+ }
+ return 0;
+}
+
+static int
+pclxl_curveto(gx_device_vector * vdev, double x0, double y0,
+ double x1, double y1, double x2, double y2, double x3, double y3,
+ gx_path_type_t type)
+{
+ gx_device_pclxl *const xdev = (gx_device_pclxl *)vdev;
+
+ if (xdev->points.type != POINTS_CURVES ||
+ xdev->points.count >= NUM_POINTS - 2
+ ) {
+ if (xdev->points.type != POINTS_NONE) {
+ int code = pclxl_flush_points(xdev);
+
+ if (code < 0)
+ return code;
+ }
+ xdev->points.current.x = (int)(x0+0.5);
+ xdev->points.current.y = (int)(y0+0.5);
+ xdev->points.type = POINTS_CURVES;
+ }
+ {
+ gs_int_point *ppt = &xdev->points.data[xdev->points.count];
+
+ ppt->x = (int)(x1+0.5), ppt->y = (int)(y1+0.5), ++ppt;
+ ppt->x = (int)(x2+0.5), ppt->y = (int)(y2+0.5), ++ppt;
+ ppt->x = (int)(x3+0.5), ppt->y = (int)(y3+0.5);
+ }
+ xdev->points.count += 3;
+ return 0;
+}
+
+static int
+pclxl_closepath(gx_device_vector * vdev, double x, double y,
+ double x_start, double y_start, gx_path_type_t type)
+{
+ gx_device_pclxl *const xdev = (gx_device_pclxl *)vdev;
+ stream *s = gdev_vector_stream(vdev);
+ int code = pclxl_flush_points(xdev);
+
+ if (code < 0)
+ return code;
+ spputc(s, pxtCloseSubPath);
+ xdev->points.current.x = (int)(x_start+0.5);
+ xdev->points.current.y = (int)(y_start+0.5);
+ return 0;
+}
+
+static int
+pclxl_endpath(gx_device_vector * vdev, gx_path_type_t type)
+{
+ gx_device_pclxl *const xdev = (gx_device_pclxl *)vdev;
+ stream *s = gdev_vector_stream(vdev);
+ int code = pclxl_flush_points(xdev);
+ gx_path_type_t rule = type & gx_path_type_rule;
+
+ if (code < 0)
+ return code;
+ if (type & (gx_path_type_fill | gx_path_type_stroke)) {
+ if (rule != xdev->fill_rule) {
+ px_put_ub(s, (byte)(rule == gx_path_type_even_odd ? eEvenOdd :
+ eNonZeroWinding));
+ px_put_ac(s, pxaFillMode, pxtSetFillMode);
+ xdev->fill_rule = rule;
+ }
+ pclxl_set_paints(xdev, type);
+ spputc(s, pxtPaintPath);
+ }
+ if (type & gx_path_type_clip) {
+ static const byte scr_[] = {
+ DUB(eInterior), DA(pxaClipRegion), pxtSetClipReplace
+ };
+
+ if (rule != xdev->clip_rule) {
+ px_put_ub(s, (byte)(rule == gx_path_type_even_odd ? eEvenOdd :
+ eNonZeroWinding));
+ px_put_ac(s, pxaClipMode, pxtSetClipMode);
+ xdev->clip_rule = rule;
+ }
+ PX_PUT_LIT(s, scr_);
+ }
+ return 0;
+}
+
+/* Vector implementation procedures */
+
+static const gx_device_vector_procs pclxl_vector_procs = {
+ /* Page management */
+ pclxl_beginpage,
+ /* Imager state */
+ pclxl_setlinewidth,
+ pclxl_setlinecap,
+ pclxl_setlinejoin,
+ pclxl_setmiterlimit,
+ pclxl_setdash,
+ gdev_vector_setflat,
+ pclxl_setlogop,
+ /* Other state */
+ pclxl_can_handle_hl_color,
+ pclxl_setfillcolor,
+ pclxl_setstrokecolor,
+ /* Paths */
+ gdev_vector_dopath,
+ pclxl_dorect,
+ pclxl_beginpath,
+ pclxl_moveto,
+ pclxl_lineto,
+ pclxl_curveto,
+ pclxl_closepath,
+ pclxl_endpath
+};
+
+/* ---------------- Driver procedures ---------------- */
+
+/* ------ Open/close/page ------ */
+
+/* Open the device. */
+static int
+pclxl_open_device(gx_device * dev)
+{
+ gx_device_vector *vdev = (gx_device_vector *)dev;
+ gx_device_pclxl *xdev = (gx_device_pclxl *)dev;
+ int code;
+
+ vdev->v_memory = dev->memory; /****** WRONG ******/
+ vdev->vec_procs = &pclxl_vector_procs;
+ code = gdev_vector_open_file_options(vdev, 512,
+ VECTOR_OPEN_FILE_SEQUENTIAL);
+ if (code < 0)
+ return code;
+
+ while (dev->child)
+ dev = dev->child;
+ vdev = (gx_device_vector *)dev;
+ xdev = (gx_device_pclxl *)dev;
+
+ pclxl_page_init(xdev);
+ px_write_file_header(vdev->strm, dev);
+ xdev->media_size = pxeMediaSize_next; /* no size selected */
+ memset(&xdev->chars, 0, sizeof(xdev->chars));
+ xdev->chars.next_in = xdev->chars.next_out = 2;
+ xdev->MediaPosition_set = false;
+ xdev->MediaType_set = false;
+ xdev->MediaPosition_old = eAutoSelect;
+ xdev->MediaPosition = eAutoSelect;
+ xdev->MediaType_old[0] = '\0';
+ xdev->MediaType[0] = '\0';
+ /* xdev->iccTransform = false; */ /* set true/false here to ignore command line */
+ return 0;
+}
+
+/* Wrap up ("output") a page. */
+/* We only support flush = true */
+static int
+pclxl_output_page(gx_device * dev, int num_copies, int flush)
+{
+ gx_device_pclxl *const xdev = (gx_device_pclxl *)dev;
+ stream *s;
+ int code;
+
+ /* Note that unlike close_device, end_page must not omit blank pages. */
+ if (!xdev->in_page)
+ pclxl_beginpage((gx_device_vector *)dev);
+ s = xdev->strm;
+ px_put_usa(s, (uint)num_copies, pxaPageCopies); /* num_copies */
+ spputc(s, pxtEndPage);
+ sflush(s);
+ pclxl_page_init(xdev);
+ if (ferror(xdev->file))
+ return_error(gs_error_ioerror);
+ if ((code = gx_finish_output_page(dev, num_copies, flush)) < 0)
+ return code;
+ /* Check if we need to change the output file for separate pages */
+ if (gx_outputfile_is_separate_pages(((gx_device_vector *)dev)->fname, dev->memory)) {
+ if ((code = pclxl_close_device(dev)) < 0)
+ return code;
+ code = pclxl_open_device(dev);
+ }
+ return code;
+}
+
+/* Close the device. */
+/* Note that if this is being called as a result of finalization, */
+/* the stream may no longer exist. */
+static int
+pclxl_close_device(gx_device * dev)
+{
+ gx_device_pclxl *const xdev = (gx_device_pclxl *)dev;
+ FILE *file = xdev->file;
+
+ if (xdev->strm != NULL)
+ sflush(xdev->strm);
+ if (xdev->in_page)
+ fputc(pxtEndPage, file);
+ px_write_file_trailer(file);
+ return gdev_vector_close_file((gx_device_vector *)dev);
+}
+
+/* ------ One-for-one images ------ */
+
+static const byte eBit_values[] = {
+ 0, e1Bit, 0, 0, e4Bit, 0, 0, 0, e8Bit
+};
+
+/* Copy a monochrome bitmap. */
+static int
+pclxl_copy_mono(gx_device * dev, const byte * data, int data_x, int raster,
+ gx_bitmap_id id, int x, int y, int w, int h,
+ gx_color_index zero, gx_color_index one)
+{
+ gx_device_vector *const vdev = (gx_device_vector *)dev;
+ gx_device_pclxl *const xdev = (gx_device_pclxl *)dev;
+ int code;
+ stream *s;
+ gx_color_index color0 = zero, color1 = one;
+ gx_color_index white = (1 << dev->color_info.depth) - 1;
+ gx_color_index black = 0;
+ gs_logical_operation_t lop;
+ byte palette[2 * 3];
+ int palette_size;
+ pxeColorSpace_t color_space;
+
+ fit_copy(dev, data, data_x, raster, id, x, y, w, h);
+ code = gdev_vector_update_clip_path(vdev, NULL);
+ if (code < 0)
+ return code;
+
+ /* write_image_data() needs byte-alignment,
+ * and gx_default_copy_* is more efficient than pxlcl_*
+ * for small rasters. See details in copy_color().
+ */
+ if ( ((data_x & 7) != 0) || (h == 1) || (w == 1) )
+ return gx_default_copy_mono(dev, data, data_x, raster, id,
+ x, y, w, h, zero, one);
+
+ pclxl_set_cursor(xdev, x, y);
+ if (id != gs_no_id && zero == gx_no_color_index &&
+ one != gx_no_color_index && data_x == 0
+ ) {
+ gx_drawing_color dcolor;
+
+ code = gdev_vector_update_log_op(vdev, rop3_T|lop_T_transparent);
+ if (code < 0)
+ return 0;
+
+ set_nonclient_dev_color(&dcolor, one);
+ pclxl_setfillcolor(vdev, NULL, &dcolor);
+ if (pclxl_copy_text_char(xdev, data, raster, id, w, h) >= 0)
+ return 0;
+ }
+ /*
+ * The following doesn't work if we're writing white with a mask.
+ * We'll fix it eventually.
+ *
+ * The logic goes like this: non-white + mask (transparent)
+ * works by setting the mask color to white and also declaring
+ * white-is-transparent. This doesn't work for drawing white + mask,
+ * since everything is then white+white-and-transparent. So instead
+ * we set mask color to black, invert and draw the destination/background
+ * through it, as well as drawing the white color.
+ *
+ * In rop3 terms, this is (D & ~S) | S
+ *
+ * This also only works in the case of the drawing color is white,
+ * because we need the inversion to not draw anything, (especially
+ * not the complimentary color/shade). So we have two different code paths,
+ * white + mask and non-whites + mask.
+ *
+ * There is a further refinement to this algorithm - it appears that
+ * black+mask is treated specially by the vector driver core (rendered
+ * as transparent on white), and does not work as non-white + mask.
+ * So Instead we set mask color to white and do (S & D) (i.e. draw
+ * background on mask, instead of transparent on mask).
+ *
+ */
+ if (zero == gx_no_color_index) {
+ if (one == gx_no_color_index)
+ return 0;
+ if (one != white) {
+ if (one == black) {
+ lop = (rop3_S & rop3_D);
+ } else {
+ lop = rop3_S | lop_S_transparent;
+ }
+ color0 = white;
+ } else {
+ lop = rop3_S | (rop3_D & rop3_not(rop3_S));
+ color0 = black;
+ }
+ } else if (one == gx_no_color_index) {
+ if (zero != white) {
+ if (zero == black) {
+ lop = (rop3_S & rop3_D);
+ } else {
+ lop = rop3_S | lop_S_transparent;
+ }
+ color1 = white;
+ } else {
+ lop = rop3_S | (rop3_D & rop3_not(rop3_S));
+ color1 = black;
+ }
+ } else {
+ lop = rop3_S;
+ }
+
+ if (dev->color_info.num_components == 1 ||
+ (RGB_IS_GRAY(color0) && RGB_IS_GRAY(color1))
+ ) {
+ palette[0] = (byte) color0;
+ palette[1] = (byte) color1;
+ palette_size = 2;
+ color_space = eGray;
+ if_debug2m('b', dev->memory, "color palette %02X %02X\n",
+ palette[0], palette[1]);
+ } else {
+ palette[0] = (byte) (color0 >> 16);
+ palette[1] = (byte) (color0 >> 8);
+ palette[2] = (byte) color0;
+ palette[3] = (byte) (color1 >> 16);
+ palette[4] = (byte) (color1 >> 8);
+ palette[5] = (byte) color1;
+ palette_size = 6;
+ color_space = eRGB;
+ }
+ code = gdev_vector_update_log_op(vdev, lop);
+ if (code < 0)
+ return 0;
+ pclxl_set_color_palette(xdev, color_space, palette, palette_size);
+ s = pclxl_stream(xdev);
+ {
+ static const byte mi_[] = {
+ DUB(e1Bit), DA(pxaColorDepth),
+ DUB(eIndexedPixel), DA(pxaColorMapping)
+ };
+
+ PX_PUT_LIT(s, mi_);
+ }
+ pclxl_write_begin_image(xdev, w, h, w, h);
+ pclxl_write_image_data(xdev, data, data_x, raster, w, 0, h, false);
+ pclxl_write_end_image(xdev);
+ return 0;
+}
+
+/* Copy a color bitmap. */
+static int
+pclxl_copy_color(gx_device * dev,
+ const byte * base, int sourcex, int raster, gx_bitmap_id id,
+ int x, int y, int w, int h)
+{
+ gx_device_vector *const vdev = (gx_device_vector *)dev;
+ gx_device_pclxl *const xdev = (gx_device_pclxl *)dev;
+ stream *s;
+ uint source_bit;
+ int code;
+
+ fit_copy(dev, base, sourcex, raster, id, x, y, w, h);
+ code = gdev_vector_update_clip_path(vdev, NULL);
+ if (code < 0)
+ return code;
+
+ source_bit = sourcex * dev->color_info.depth;
+
+ /* side-effect from fill/stroke may have set color space to eGray */
+ if (dev->color_info.num_components == 3)
+ pclxl_set_color_space(xdev, eRGB);
+ else if (dev->color_info.num_components == 1)
+ pclxl_set_color_space(xdev, eGray);
+
+ /* write_image_data() needs byte-alignment,
+ * and gx_default_copy_* is more efficient than pxlcl_*
+ * for small rasters.
+ *
+ * SetBrushSource + Rectangle = 21 byte for 1x1 RGB
+ * SetCursor+ BeginImage + ReadImage + EndImage = 56 bytes for 1x1 RGB
+ * 3x1 RGB at 3 different colors takes 62 bytes for pxlcl_*
+ * but gx_default_copy_* uses 63 bytes. Below 3 pixels, gx_default_copy_*
+ * is better than pxlcl_*; above 3 pixels, it is less clear;
+ * in practice, single-lines seems better coded as painted rectangles
+ * than images.
+ */
+ if ( ((source_bit & 7) != 0) || (w == 1) || (h == 1) )
+ return gx_default_copy_color(dev, base, sourcex, raster, id,
+ x, y, w, h);
+ code = gdev_vector_update_log_op(vdev, rop3_S);
+ if(code < 0)
+ return 0;
+ pclxl_set_cursor(xdev, x, y);
+ s = pclxl_stream(xdev);
+ {
+ static const byte ci_[] = {
+ DA(pxaColorDepth),
+ DUB(eDirectPixel), DA(pxaColorMapping)
+ };
+
+ px_put_ub(s, eBit_values[dev->color_info.depth /
+ dev->color_info.num_components]);
+ PX_PUT_LIT(s, ci_);
+ }
+ pclxl_write_begin_image(xdev, w, h, w, h);
+ pclxl_write_image_data(xdev, base, source_bit, raster,
+ w * dev->color_info.depth, 0, h, false);
+ pclxl_write_end_image(xdev);
+ return 0;
+}
+
+/* Fill a mask. */
+static int
+pclxl_fill_mask(gx_device * dev,
+ const byte * data, int data_x, int raster, gx_bitmap_id id,
+ int x, int y, int w, int h,
+ const gx_drawing_color * pdcolor, int depth,
+ gs_logical_operation_t lop, const gx_clip_path * pcpath)
+{
+ gx_device_vector *const vdev = (gx_device_vector *)dev;
+ gx_device_pclxl *const xdev = (gx_device_pclxl *)dev;
+ int code;
+ stream *s;
+ gx_color_index foreground;
+
+ fit_copy(dev, data, data_x, raster, id, x, y, w, h);
+ /* write_image_data() needs byte-alignment,
+ * and gx_default_copy_* is more efficient than pxlcl_*
+ * for small rasters. See details in copy_color().
+ */
+ if ((data_x & 7) != 0 || !gx_dc_is_pure(pdcolor) || depth > 1 || (w == 1) || (h == 1) )
+ return gx_default_fill_mask(dev, data, data_x, raster, id,
+ x, y, w, h, pdcolor, depth,
+ lop, pcpath);
+ code = gdev_vector_update_clip_path(vdev, pcpath);
+ foreground = gx_dc_pure_color(pdcolor);
+ if (code < 0)
+ return code;
+ code = gdev_vector_update_fill_color(vdev, NULL, pdcolor);
+ if (code < 0)
+ return 0;
+ pclxl_set_cursor(xdev, x, y);
+ if (id != gs_no_id && data_x == 0) {
+ code = gdev_vector_update_log_op(vdev, lop);
+ if (code < 0)
+ return 0;
+ if (pclxl_copy_text_char(xdev, data, raster, id, w, h) >= 0)
+ return 0;
+ }
+ /* This is similiar to the copy_mono white-on-mask,
+ * except we are drawing white on the black of a black/white mask,
+ * so we invert source, compared to copy_mono */
+ if (foreground == (1 << dev->color_info.depth) - 1) { /* white */
+ lop = rop3_not(rop3_S) | (rop3_D & rop3_S);
+ }else if (foreground == 0) { /* black */
+ lop = (rop3_S & rop3_D);
+ }else lop |= rop3_S | lop_S_transparent;
+
+ code = gdev_vector_update_log_op(vdev,
+ lop);
+ if (code < 0)
+ return 0;
+ pclxl_set_color_palette(xdev, eGray, (const byte *)"\377\000", 2);
+ s = pclxl_stream(xdev);
+ {
+ static const byte mi_[] = {
+ DUB(e1Bit), DA(pxaColorDepth),
+ DUB(eIndexedPixel), DA(pxaColorMapping)
+ };
+
+ PX_PUT_LIT(s, mi_);
+ }
+ pclxl_write_begin_image(xdev, w, h, w, h);
+ pclxl_write_image_data(xdev, data, data_x, raster, w, 0, h, false);
+ pclxl_write_end_image(xdev);
+ return 0;
+}
+
+/* Do a RasterOp. */
+static int
+pclxl_strip_copy_rop(gx_device * dev, const byte * sdata, int sourcex,
+ uint sraster, gx_bitmap_id id,
+ const gx_color_index * scolors,
+ const gx_strip_bitmap * textures,
+ const gx_color_index * tcolors,
+ int x, int y, int width, int height,
+ int phase_x, int phase_y, gs_logical_operation_t lop)
+{
+ /* Improvements possible here using PXL ROP3
+ for some combinations of args; use gx_default for now */
+ if (!rop3_uses_D(lop)) /* gx_default() cannot cope with D ops */
+ return gx_default_strip_copy_rop(dev, sdata, sourcex,
+ sraster, id,
+ scolors,
+ textures,
+ tcolors,
+ x, y, width, height,
+ phase_x, phase_y, lop);
+ return 0;
+}
+
+/* ------ High-level images ------ */
+
+#define MAX_ROW_DATA 500000 /* arbitrary */
+typedef struct pclxl_image_enum_s {
+ gdev_vector_image_enum_common;
+ gs_matrix mat;
+ struct ir_ {
+ byte *data;
+ int num_rows; /* # of allocated rows */
+ int first_y;
+ uint raster;
+ } rows;
+ bool flipped;
+ gsicc_link_t *icclink;
+} pclxl_image_enum_t;
+gs_private_st_suffix_add1(st_pclxl_image_enum, pclxl_image_enum_t,
+ "pclxl_image_enum_t", pclxl_image_enum_enum_ptrs,
+ pclxl_image_enum_reloc_ptrs, st_vector_image_enum,
+ rows.data);
+
+/* Start processing an image. */
+static int
+pclxl_begin_image(gx_device * dev,
+ const gs_imager_state * pis, const gs_image_t * pim,
+ gs_image_format_t format, const gs_int_rect * prect,
+ const gx_drawing_color * pdcolor,
+ const gx_clip_path * pcpath, gs_memory_t * mem,
+ gx_image_enum_common_t ** pinfo)
+{
+ gx_device_vector *const vdev = (gx_device_vector *)dev;
+ gx_device_pclxl *const xdev = (gx_device_pclxl *)dev;
+ const gs_color_space *pcs = pim->ColorSpace;
+ pclxl_image_enum_t *pie;
+ byte *row_data;
+ int num_rows;
+ uint row_raster;
+ /*
+ * Following should divide by num_planes, but we only handle chunky
+ * images, i.e., num_planes = 1.
+ */
+ int bits_per_pixel =
+ (pim->ImageMask ? 1 :
+ pim->BitsPerComponent * gs_color_space_num_components(pcs));
+ gs_matrix mat;
+ int code;
+
+ /*
+ * Check whether we can handle this image. PCL XL 1.0 and 2.0 only
+ * handle orthogonal transformations.
+ */
+ gs_matrix_invert(&pim->ImageMatrix, &mat);
+ gs_matrix_multiply(&mat, &ctm_only(pis), &mat);
+
+ if (pclxl_nontrivial_transfer(pis))
+ goto use_default;
+
+ /*
+ * NOTE: this predicate should be fixed to be readable and easily
+ * debugged. Each condition should be separate. See the large
+ * similar conditional in clist_begin_typed_image which has
+ * already been reworked. We can handle rotations of 90 degs +
+ * scaling + reflections. * These have one of the diagonals being
+ * zeros * (and the other diagonals having non-zeros).
+ */
+ if ((!((mat.xx * mat.yy != 0) && (mat.xy == 0) && (mat.yx == 0)) &&
+ !((mat.xx == 0) && (mat.yy == 0) && (mat.xy * mat.yx != 0))) ||
+ (pim->ImageMask ?
+ (!gx_dc_is_pure(pdcolor) || pim->CombineWithColor) :
+ ((!pclxl_can_handle_color_space(pcs) ||
+ (bits_per_pixel != 1 && bits_per_pixel != 4 &&
+ bits_per_pixel != 8 && bits_per_pixel !=24))
+ && !(pclxl_can_icctransform(pim) && xdev->iccTransform) )) ||
+ format != gs_image_format_chunky || pim->Interpolate ||
+ prect
+ )
+ goto use_default;
+ row_raster = (bits_per_pixel * pim->Width + 7) >> 3;
+ num_rows = MAX_ROW_DATA / row_raster;
+ if (num_rows > pim->Height)
+ num_rows = pim->Height;
+ if (num_rows <= 0)
+ num_rows = 1;
+ pie = gs_alloc_struct(mem, pclxl_image_enum_t, &st_pclxl_image_enum,
+ "pclxl_begin_image");
+ row_data = gs_alloc_bytes(mem, num_rows * row_raster,
+ "pclxl_begin_image(rows)");
+ if (pie == 0 || row_data == 0) {
+ code = gs_note_error(gs_error_VMerror);
+ goto fail;
+ }
+ code = gdev_vector_begin_image(vdev, pis, pim, format, prect,
+ pdcolor, pcpath, mem,
+ &pclxl_image_enum_procs,
+ (gdev_vector_image_enum_t *)pie);
+ if (code < 0)
+ return code;
+
+ /* emit a PXL XL rotation and adjust mat correspondingly */
+ pie->flipped = false;
+ if (mat.xx * mat.yy > 0) {
+ if (mat.xx < 0) {
+ stream *s = pclxl_stream(xdev);
+ mat.xx = -mat.xx;
+ mat.yy = -mat.yy;
+ mat.tx = -mat.tx;
+ mat.ty = -mat.ty;
+ px_put_ss(s,180);
+ xdev->state_rotated = 2;
+ px_put_ac(s, pxaPageAngle, pxtSetPageRotation);
+ }
+ /* leave the matrix alone if it is portrait */
+ } else if (mat.xx * mat.yy < 0) {
+ pie->flipped = true;
+ if (mat.xx < 0) {
+ stream *s = pclxl_stream(xdev);
+ mat.xx = -mat.xx;
+ mat.tx = -mat.tx;
+ px_put_ss(s,+180);
+ xdev->state_rotated = +2;
+ px_put_ac(s, pxaPageAngle, pxtSetPageRotation);
+ } else {
+ mat.yy = -mat.yy;
+ mat.ty = -mat.ty;
+ }
+ } else if (mat.xy * mat.yx < 0) {
+ /* rotate +90 or -90 */
+ float tmpf;
+ stream *s = pclxl_stream(xdev);
+ if(mat.xy > 0) {
+ mat.xx = mat.xy;
+ mat.yy = -mat.yx;
+ tmpf = mat.tx;
+ mat.tx = mat.ty;
+ mat.ty = -tmpf;
+ px_put_ss(s,-90);
+ xdev->state_rotated = -1;
+ } else {
+ mat.xx = -mat.xy;
+ mat.yy = mat.yx;
+ tmpf = mat.tx;
+ mat.tx = -mat.ty;
+ mat.ty = tmpf;
+ px_put_ss(s,+90);
+ xdev->state_rotated = +1;
+ }
+ mat.xy = mat.yx = 0;
+ px_put_ac(s, pxaPageAngle, pxtSetPageRotation);
+ } else if (mat.xy * mat.yx > 0) {
+ float tmpf;
+ stream *s = pclxl_stream(xdev);
+ pie->flipped = true;
+ if(mat.xy > 0) {
+ mat.xx = mat.xy;
+ mat.yy = mat.yx;
+ tmpf = mat.tx;
+ mat.tx = mat.ty;
+ mat.ty = tmpf;
+ px_put_ss(s,-90);
+ xdev->state_rotated = -1;
+ } else {
+ mat.xx = -mat.xy;
+ mat.yy = -mat.yx;
+ tmpf = mat.tx;
+ mat.tx = -mat.ty;
+ mat.ty = -tmpf;
+ px_put_ss(s,+90);
+ xdev->state_rotated = +1;
+ }
+ mat.xy = mat.yx = 0;
+ px_put_ac(s, pxaPageAngle, pxtSetPageRotation);
+ }
+
+ pie->mat = mat;
+ pie->rows.data = row_data;
+ pie->rows.num_rows = num_rows;
+ pie->rows.first_y = 0;
+ pie->rows.raster = row_raster;
+ if (!pim->ImageMask && !pclxl_can_handle_color_space(pcs)
+ && pclxl_can_icctransform(pim) && pcs->cmm_icc_profile_data) {
+ gsicc_rendering_param_t rendering_params;
+
+ rendering_params.black_point_comp = pis->blackptcomp;
+ rendering_params.graphics_type_tag = GS_IMAGE_TAG;
+ rendering_params.rendering_intent = pis->renderingintent;
+ pie->icclink = gsicc_get_link(pis, dev, pcs, NULL /*des */ ,
+ &rendering_params, pis->memory);
+ } else
+ pie->icclink = NULL;
+ *pinfo = (gx_image_enum_common_t *) pie;
+ {
+ gs_logical_operation_t lop = pis->log_op;
+
+ if (pim->ImageMask) {
+ const byte *palette = (const byte *)
+ (pim->Decode[0] ? "\377\000" : "\000\377");
+ gx_color_index foreground = gx_dc_pure_color(pdcolor);
+
+ code = gdev_vector_update_fill_color(vdev,
+ NULL, /* use process color */
+ pdcolor);
+ if (code < 0)
+ goto fail;
+ /* This is similiar to the copy_mono white-on-mask,
+ * except we are drawing white on the black of a black/white mask,
+ * so we invert source, compared to copy_mono */
+ if (foreground == (1 << dev->color_info.depth) - 1) { /* white */
+ lop = rop3_not(rop3_S) | (rop3_D & rop3_S);
+ }else if (foreground == 0) { /* black */
+ lop = (rop3_S & rop3_D);
+ }else lop |= rop3_S | lop_S_transparent;
+
+ code = gdev_vector_update_log_op
+ (vdev, lop);
+ if (code < 0)
+ goto fail;
+ pclxl_set_color_palette(xdev, eGray, palette, 2);
+ } else {
+ if (bits_per_pixel == 24 ) {
+ code = gdev_vector_update_log_op
+ (vdev, (pim->CombineWithColor ? lop : rop3_know_T_0(lop)));
+ if (code < 0)
+ goto fail;
+ if (dev->color_info.num_components == 1) {
+ pclxl_set_color_space(xdev, eGray);
+ } else {
+ pclxl_set_color_space(xdev, eRGB);
+ }
+ } else {
+ int bpc = pim->BitsPerComponent;
+ int num_components = pie->plane_depths[0] * pie->num_planes / bpc;
+ int sample_max = (1 << bpc) - 1;
+ byte palette[256 * 3];
+ int i;
+
+ code = gdev_vector_update_log_op
+ (vdev, (pim->CombineWithColor ? lop : rop3_know_T_0(lop)));
+ if (code < 0)
+ goto fail;
+ for (i = 0; i < 1 << bits_per_pixel; ++i) {
+ gs_client_color cc;
+ gx_device_color devc;
+ int cv = i, j;
+ gx_color_index ci;
+
+ for (j = num_components - 1; j >= 0; cv >>= bpc, --j)
+ cc.paint.values[j] = pim->Decode[j * 2] +
+ (cv & sample_max) *
+ (pim->Decode[j * 2 + 1] - pim->Decode[j * 2]) /
+ sample_max;
+ (*pcs->type->remap_color)
+ (&cc, pcs, &devc, pis, dev, gs_color_select_source);
+ if (!gx_dc_is_pure(&devc))
+ return_error(gs_error_Fatal);
+ ci = gx_dc_pure_color(&devc);
+ if (dev->color_info.num_components == 1) {
+ palette[i] = (byte)ci;
+ } else {
+ byte *ppal = &palette[i * 3];
+
+ ppal[0] = (byte) (ci >> 16);
+ ppal[1] = (byte) (ci >> 8);
+ ppal[2] = (byte) ci;
+ }
+ }
+ if (dev->color_info.num_components == 1)
+ pclxl_set_color_palette(xdev, eGray, palette,
+ 1 << bits_per_pixel);
+ else
+ pclxl_set_color_palette(xdev, eRGB, palette,
+ 3 << bits_per_pixel);
+ }
+ }
+ }
+ return 0;
+ fail:
+ gs_free_object(mem, row_data, "pclxl_begin_image(rows)");
+ gs_free_object(mem, pie, "pclxl_begin_image");
+ use_default:
+ if (dev->color_info.num_components == 1)
+ pclxl_set_color_space(xdev, eGray);
+ else
+ pclxl_set_color_space(xdev, eRGB);
+ return gx_default_begin_image(dev, pis, pim, format, prect,
+ pdcolor, pcpath, mem, pinfo);
+}
+
+/* Write one strip of an image, from pie->rows.first_y to pie->y. */
+static int
+image_transform_x(const pclxl_image_enum_t *pie, int sx)
+{
+ return (int)((pie->mat.tx + sx * pie->mat.xx + 0.5) /
+ ((const gx_device_pclxl *)pie->dev)->scale.x);
+}
+static int
+image_transform_y(const pclxl_image_enum_t *pie, int sy)
+{
+ return (int)((pie->mat.ty + sy * pie->mat.yy + 0.5) /
+ ((const gx_device_pclxl *)pie->dev)->scale.y);
+}
+
+static int
+pclxl_image_write_rows(pclxl_image_enum_t *pie)
+{
+ gx_device_pclxl *const xdev = (gx_device_pclxl *)pie->dev;
+ stream *s = pclxl_stream(xdev);
+ int y = pie->rows.first_y;
+ int h = pie->y - y;
+ int xo = image_transform_x(pie, 0);
+ int yo = image_transform_y(pie, y);
+ int dw = image_transform_x(pie, pie->width) - xo;
+ int dh = image_transform_y(pie, y + h) - yo;
+ int rows_raster=pie->rows.raster;
+ int offset_lastflippedstrip = 0;
+
+ if (pie->flipped) yo = -yo -dh;
+ if (pie->flipped)
+ offset_lastflippedstrip = pie->rows.raster * (pie->rows.num_rows - h);
+ if (dw <= 0 || dh <= 0)
+ return 0;
+ pclxl_set_cursor(xdev, xo, yo);
+ if (pie->bits_per_pixel==24) {
+ static const byte ci_[] = {
+ DA(pxaColorDepth),
+ DUB(eDirectPixel), DA(pxaColorMapping)
+ };
+
+ px_put_ub(s, eBit_values[8]);
+ PX_PUT_LIT(s, ci_);
+ if (xdev->color_info.depth==8) {
+ rows_raster/=3;
+ if (!pie->icclink) {
+ byte *in=pie->rows.data + offset_lastflippedstrip;
+ byte *out=pie->rows.data + offset_lastflippedstrip;
+ int i;
+ int j;
+ for (j=0; j<h; j++) {
+ for (i=0; i<rows_raster; i++) {
+ *out = (byte)( ((*(in+0) * (ulong) lum_red_weight) +
+ (*(in+1) * (ulong) lum_green_weight) +
+ (*(in+2) * (ulong) lum_blue_weight) +
+ (lum_all_weights / 2)) / lum_all_weights);
+ in+=3;
+ out++;
+ }
+ }
+ }
+ }
+ } else {
+ static const byte ii_[] = {
+ DA(pxaColorDepth),
+ DUB(eIndexedPixel), DA(pxaColorMapping)
+ };
+ px_put_ub(s, eBit_values[pie->bits_per_pixel]);
+ PX_PUT_LIT(s, ii_);
+ }
+ pclxl_write_begin_image(xdev, pie->width, h, dw, dh);
+ /* 8-bit gray image may compress with jpeg, but we
+ cannot tell if it is 8-bit gray or 8-bit indexed */
+ pclxl_write_image_data(xdev, pie->rows.data + offset_lastflippedstrip, 0, rows_raster,
+ rows_raster << 3, 0, h, (pie->bits_per_pixel==24 ? true : false));
+ pclxl_write_end_image(xdev);
+ return 0;
+}
+
+/* Process the next piece of an image. */
+static int
+pclxl_image_plane_data(gx_image_enum_common_t * info,
+ const gx_image_plane_t * planes, int height,
+ int *rows_used)
+{
+ pclxl_image_enum_t *pie = (pclxl_image_enum_t *) info;
+ int data_bit = planes[0].data_x * info->plane_depths[0];
+ int width_bits = pie->width * info->plane_depths[0];
+ int i;
+
+ /****** SHOULD HANDLE NON-BYTE-ALIGNED DATA ******/
+ if (width_bits != pie->bits_per_row || (data_bit & 7) != 0)
+ return_error(gs_error_rangecheck);
+ if (height > pie->height - pie->y)
+ height = pie->height - pie->y;
+ for (i = 0; i < height; pie->y++, ++i) {
+ if (pie->y - pie->rows.first_y == pie->rows.num_rows) {
+ int code = pclxl_image_write_rows(pie);
+
+ if (code < 0)
+ return code;
+ pie->rows.first_y = pie->y;
+ }
+ if (!pie->icclink)
+ memcpy(pie->rows.data +
+ pie->rows.raster * (pie->flipped ? (pie->rows.num_rows - (pie->y - pie->rows.first_y) -1) :(pie->y - pie->rows.first_y)),
+ planes[0].data + planes[0].raster * i + (data_bit >> 3),
+ pie->rows.raster);
+ else {
+ gsicc_bufferdesc_t input_buff_desc;
+ gsicc_bufferdesc_t output_buff_desc;
+ int pixels_per_row = pie->rows.raster / 3 ;
+ int out_raster_stride = pixels_per_row * info->dev->color_info.num_components;
+ gsicc_init_buffer(&input_buff_desc, 3 /*num_chan*/, 1 /*bytes_per_chan*/,
+ false/*has_alpha*/, false/*alpha_first*/, false /*is_planar*/,
+ 0 /*plane_stride*/, pie->rows.raster /*row_stride*/,
+ 1/*num_rows*/, pixels_per_row /*pixels_per_row*/);
+ gsicc_init_buffer(&output_buff_desc, info->dev->color_info.num_components, 1,
+ false, false, false,
+ 0, out_raster_stride,
+ 1, pixels_per_row);
+ gscms_transform_color_buffer(info->dev, pie->icclink,
+ &input_buff_desc,
+ &output_buff_desc,
+ (void *)(planes[0].data + planes[0].raster * i + (data_bit >> 3)), /*src*/
+ pie->rows.data +
+ out_raster_stride * (pie->flipped ? (pie->rows.num_rows - (pie->y - pie->rows.first_y) -1) : (pie->y - pie->rows.first_y)) /*des*/
+ );
+ }
+ }
+ *rows_used = height;
+ return pie->y >= pie->height;
+}
+
+/* Clean up by releasing the buffers. */
+static int
+pclxl_image_end_image(gx_image_enum_common_t * info, bool draw_last)
+{
+ pclxl_image_enum_t *pie = (pclxl_image_enum_t *) info;
+ int code = 0;
+
+ /* Write the final strip, if any. */
+ if (pie->y > pie->rows.first_y && draw_last)
+ code = pclxl_image_write_rows(pie);
+ if (draw_last) {
+ gx_device_pclxl *xdev = (gx_device_pclxl *)info->dev;
+ stream *s = pclxl_stream(xdev);
+ switch(xdev->state_rotated) {
+ case 1:
+ xdev->state_rotated = 0;
+ px_put_ss(s,-90);
+ px_put_ac(s, pxaPageAngle, pxtSetPageRotation);
+ break;
+ case -1:
+ xdev->state_rotated = 0;
+ px_put_ss(s,+90);
+ px_put_ac(s, pxaPageAngle, pxtSetPageRotation);
+ break;
+ case 2:
+ xdev->state_rotated = 0;
+ px_put_ss(s,-180);
+ px_put_ac(s, pxaPageAngle, pxtSetPageRotation);
+ break;
+ case 0:
+ default:
+ /* do nothing */
+ break;
+ }
+ }
+ gs_free_object(pie->memory, pie->rows.data, "pclxl_end_image(rows)");
+ gx_image_free_enum(&info);
+ return code;
+}
+
+/*
+ * 'pclxl_get_params()' - Get pagedevice parameters.
+ */
+
+static int /* O - Error status */
+pclxl_get_params(gx_device *dev, /* I - Device info */
+ gs_param_list *plist) /* I - Parameter list */
+{
+ gx_device_pclxl *xdev; /* PCL XL device */
+ int code; /* Return code */
+ gs_param_string s; /* Temporary string value */
+
+ /*
+ * First process the "standard" page device parameters...
+ */
+
+ if ((code = gdev_vector_get_params(dev, plist)) < 0)
+ return (code);
+
+ /*
+ * Then write the PCL-XL parameters...
+ */
+
+ xdev = (gx_device_pclxl *)dev;
+
+ if ((code = param_write_bool(plist, "Duplex", &(xdev->Duplex))) < 0)
+ return (code);
+
+ if (xdev->MediaPosition_set)
+ if ((code = param_write_int(plist, "MediaPosition",
+ &(xdev->MediaPosition))) < 0)
+ return (code);
+
+ if (xdev->MediaType_set) {
+ if ((code = param_string_from_string(s, xdev->MediaType)) < 0)
+ return (code);
+ if ((code = param_write_string(plist, "MediaType", &s)) < 0)
+ return (code);
+ }
+
+ if ((code = param_write_bool(plist, "Tumble", &(xdev->Tumble))) < 0)
+ return (code);
+
+ if ((code = param_write_int(plist, "CompressMode",
+ &(xdev->CompressMode))) < 0)
+ return (code);
+
+ if ((code = param_write_bool(plist, "iccTransform", &(xdev->iccTransform))) < 0)
+ return (code);
+
+ return (0);
+}
+
+/*
+ * 'pclxl_put_params()' - Set pagedevice parameters.
+ */
+
+static int /* O - Error status */
+pclxl_put_params(gx_device *dev, /* I - Device info */
+ gs_param_list *plist) /* I - Parameter list */
+{
+ gx_device_pclxl *xdev; /* PCL XL device */
+ int code; /* Error code */
+ int intval; /* Integer value */
+ bool boolval; /* Boolean value */
+ gs_param_string stringval; /* String value */
+
+ /*
+ * Process PCL-XL driver parameters...
+ */
+
+ xdev = (gx_device_pclxl *)dev;
+
+#define intoption(name, sname, type) \
+ if ((code = param_read_int(plist, sname, &intval)) < 0) \
+ { \
+ if_debug1('|', "Error setting %s\n", sname); \
+ param_signal_error(plist, sname, code); \
+ return (code); \
+ } \
+ else if (code == 0) \
+ { \
+ if_debug2('|', "setting %s to %d\n", sname, intval); \
+ xdev->name = (type)intval; \
+ }
+
+#define booloption(name, sname) \
+ if ((code = param_read_bool(plist, sname, &boolval)) < 0) \
+ { \
+ if_debug1('|', "Error setting bool %s\n", sname); \
+ if ((code = param_read_null(plist, sname)) < 0) \
+ { \
+ if_debug1('|', "Error setting bool %s null\n", sname); \
+ param_signal_error(plist, sname, code); \
+ return (code); \
+ } \
+ if (code == 0) \
+ xdev->name = false; \
+ } \
+ else if (code == 0) { \
+ if_debug2('|', "setting %s to %d\n", sname, boolval); \
+ xdev->name = (bool)boolval; \
+ }
+
+#define stringoption(name, sname) \
+ if ((code = param_read_string(plist, sname, &stringval)) < 0) \
+ { \
+ if_debug1('|', "Error setting %s string\n", sname); \
+ if ((code = param_read_null(plist, sname)) < 0) \
+ { \
+ if_debug1('|', "Error setting %s null\n", sname); \
+ param_signal_error(plist, sname, code); \
+ return (code); \
+ } \
+ if (code == 0) { \
+ if_debug1('|', "setting %s to empty\n", sname); \
+ xdev->name[0] = '\0'; \
+ } \
+ } \
+ else if (code == 0) { \
+ strncpy(xdev->name, (const char *)(stringval.data), \
+ stringval.size); \
+ xdev->name[stringval.size] = '\0'; \
+ if_debug2('|', "setting %s to %s\n", sname, xdev->name); \
+ }
+
+ /* We need to have *_set to distinguish defaults from explicitly sets */
+ booloption(Duplex, "Duplex")
+ if (code == 0)
+ if (xdev->Duplex) {
+ if_debug0('|', "round up page count\n");
+ xdev->page = (xdev->page+1) & ~1 ;
+ }
+ intoption(MediaPosition, "MediaPosition", int)
+ if (code == 0) {
+ xdev->MediaPosition_set = true;
+ /* round up for duplex */
+ if (xdev->MediaPosition_old != xdev->MediaPosition) {
+ if_debug0('|', "round up page count\n");
+ xdev->page = (xdev->page+1) & ~1 ;
+ xdev->MediaPosition_old = xdev->MediaPosition;
+ }
+ }
+ stringoption(MediaType, "MediaType")
+ if (code == 0) {
+ xdev->MediaType_set = true;
+ /* round up for duplex */
+ if (strcmp(xdev->MediaType_old, xdev->MediaType)) {
+ if_debug0('|', "round up page count\n");
+ xdev->page = (xdev->page+1) & ~1 ;
+ strcpy(xdev->MediaType_old, xdev->MediaType);
+ }
+ }
+ booloption(Tumble, "Tumble")
+ intoption(CompressMode, "CompressMode", int)
+ booloption(iccTransform, "iccTransform")
+
+ /*
+ * Then process standard page device parameters...
+ */
+
+ if ((code = gdev_vector_put_params(dev, plist)) < 0)
+ return (code);
+
+ return (0);
+}
diff --git a/devices/vector/gdevtxtw.c b/devices/vector/gdevtxtw.c
new file mode 100644
index 000000000..86103fca1
--- /dev/null
+++ b/devices/vector/gdevtxtw.c
@@ -0,0 +1,2402 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+/*$Id: gdevtxtw.c 7795 2007-03-23 13:56:11Z tim $ */
+/* Device for Unicode (UTF-8 or UCS2) text extraction */
+#include "memory_.h"
+#include "string_.h"
+#include "gp.h" /* for gp_file_name_sizeof */
+#include "gx.h"
+#include "gserrors.h"
+#include "gsparam.h"
+#include "gsutil.h"
+#include "gxdevice.h"
+#include "gsdevice.h" /* requires gsmatrix.h */
+#include "gxfont.h"
+#include "gxfont0.h"
+#include "gstext.h"
+#include "gxfcid.h"
+#include "gxistate.h"
+#include "gxpath.h"
+#include "gdevagl.h"
+#include "gxdevsop.h"
+#include "gzpath.h"
+#include "gdevkrnlsclass.h" /* 'standard' built in subclasses, currently First/Last Page and obejct filter */
+
+/* #define TRACE_TXTWRITE 1 */
+
+extern single_glyph_list_t *SingleGlyphList;
+extern double_glyph_list_t *DoubleGlyphList;
+extern treble_glyph_list_t *TrebleGlyphList;
+extern quad_glyph_list_t *QuadGlyphList;
+/*
+ * Define the structure used to return glyph width information. Note that
+ * there are two different sets of width information: real-number (x,y)
+ * values, which give the true advance width, and an integer value, which
+ * gives an X advance width for WMode = 0 or a Y advance width for WMode = 1.
+ * The return value from txt_glyph_width() indicates which of these is/are
+ * valid.
+ */
+typedef struct txt_glyph_width_s {
+ double w;
+ gs_point xy;
+ gs_point v; /* glyph origin shift */
+} txt_glyph_width_t;
+typedef struct txt_glyph_widths_s {
+ txt_glyph_width_t Width; /* unmodified, for Widths */
+ txt_glyph_width_t real_width; /* possibly modified, for rendering */
+ bool replaced_v;
+} txt_glyph_widths_t;
+
+/* Structure to record the Unicode characters, the total width of the text
+ * recorded, and various useful attributes such as the font, size, colour
+ * rendering mode, writing mode etc. These are stored as a series of x-ordered
+ * entries in a list, using the starting x co-ordinate.
+ */
+typedef struct text_list_entry_s {
+ struct text_list_entry_s *previous;
+ struct text_list_entry_s *next;
+
+ gs_point start;
+ gs_point end;
+ gs_point FontBBox_bottomleft, FontBBox_topright;
+ float *Widths;
+ unsigned short *Unicode_Text;
+ int Unicode_Text_Size;
+ int render_mode;
+
+ gs_matrix matrix; /* Tm et al */
+
+ gs_font *font;
+ char *FontName;
+ int wmode; /* WMode of font */
+ double PaintType0Width;
+ double size;
+} text_list_entry_t;
+
+/* Structure to maintain a list of text fragments, ordered by X co-ordinate.
+ * These structures are themselves maintained in a Y-ordered list.
+ */
+typedef struct page_text_list_s {
+ struct page_text_list_s *previous;
+ struct page_text_list_s *next;
+ gs_point start;
+ float MinY, MaxY;
+ text_list_entry_t *x_ordered_list;
+} page_text_list_t;
+
+/* A simple structure to maintain the lists of text fragments, it is also
+ * a convenient place to record the page number and anything else we may
+ * want to record that is relevant to the page rather than the text.
+ */
+typedef struct page_text_s {
+ int PageNum;
+ page_text_list_t *y_ordered_list;
+ text_list_entry_t *unsorted_text_list;
+} page_text_t;
+
+/* The custom sub-classed device structure */
+typedef struct gx_device_txtwrite_s {
+ gx_device_common;
+ page_text_t PageData;
+ char fname[gp_file_name_sizeof]; /* OutputFile */
+ FILE *file;
+ int TextFormat;
+#ifdef TRACE_TXTWRITE
+ FILE *DebugFile;
+#endif
+} gx_device_txtwrite_t;
+
+/* GC descriptor */
+gs_private_st_suffix_add0_final(st_device_txtwrite, gx_device_txtwrite_t,
+ "gx_device_txtwrite", device_txtwrite_enum_ptrs, device_txtwrite_reloc_ptrs,
+ gx_device_finalize, st_device_forward);
+
+/* Device procedures */
+static dev_proc_open_device(txtwrite_open_device);
+static dev_proc_close_device(txtwrite_close_device);
+static dev_proc_output_page(txtwrite_output_page);
+static dev_proc_fill_rectangle(txtwrite_fill_rectangle);
+static dev_proc_get_params(txtwrite_get_params);
+static dev_proc_put_params(txtwrite_put_params);
+static dev_proc_fill_path(txtwrite_fill_path);
+static dev_proc_stroke_path(txtwrite_stroke_path);
+static dev_proc_text_begin(txtwrite_text_begin);
+static dev_proc_strip_copy_rop(txtwrite_strip_copy_rop);
+static dev_proc_dev_spec_op(txtwrite_dev_spec_op);
+
+
+/* The device prototype */
+#define X_DPI 72
+#define Y_DPI 72
+
+/* Define the text enumerator. */
+typedef struct textw_text_enum_s {
+ gs_text_enum_common;
+ gs_fixed_point origin;
+ bool charproc_accum;
+ bool cdevproc_callout;
+ double cdevproc_result[10];
+ float *Widths;
+ unsigned short *TextBuffer;
+ int TextBufferIndex;
+ text_list_entry_t *text_state;
+} textw_text_enum_t;
+#define private_st_textw_text_enum()\
+ extern_st(st_gs_text_enum);\
+ gs_private_st_suffix_add0(st_textw_text_enum, textw_text_enum_t,\
+ "textw_text_enum_t", textw_text_enum_enum_ptrs, textw_text_enum_reloc_ptrs,\
+ st_gs_text_enum)
+
+private_st_textw_text_enum();
+
+const gx_device_txtwrite_t gs_txtwrite_device =
+{
+ /* Define the device as 8-bit gray scale to avoid computing halftones. */
+ std_device_dci_body(gx_device_txtwrite_t, 0, "txtwrite",
+ DEFAULT_WIDTH_10THS * X_DPI / 10,
+ DEFAULT_HEIGHT_10THS * Y_DPI / 10,
+ X_DPI, Y_DPI,
+ 1, 8, 255, 0, 256, 1),
+ {txtwrite_open_device,
+ NULL, /*gx_upright_get_initial_matrix,*/
+ NULL, /*gx_default_sync_output,*/
+ txtwrite_output_page,
+ txtwrite_close_device,
+ NULL, /*gx_default_gray_map_rgb_color,*/
+ NULL, /*gx_default_gray_map_color_rgb,*/
+ txtwrite_fill_rectangle, /* Can't be NULL and there is no gx_default_fill_rectangle! */
+ NULL, /*gx_default_tile_rectangle,*/
+ NULL, /*gx_default_copy_mono,*/
+ NULL, /*gx_default_copy_color,*/
+ NULL, /*gx_default_draw_line,*/
+ NULL, /*gx_default_get_bits,*/
+ txtwrite_get_params,
+ txtwrite_put_params,
+ NULL, /*gx_default_map_cmyk_color,*/
+ NULL, /*gx_default_get_xfont_procs,*/
+ NULL, /*gx_default_get_xfont_device,*/
+ NULL, /*gx_default_map_rgb_alpha_color,*/
+ gx_page_device_get_page_device, /*gx_page_device_get_page_device,*/
+ NULL, /* get_alpha_bits */
+ NULL, /*gx_default_copy_alpha,*/
+ NULL, /* get_band */
+ NULL, /* copy_rop */
+ txtwrite_fill_path,
+ txtwrite_stroke_path,
+ NULL, /*gx_default_fill_mask,*/
+ NULL, /*gx_default_fill_trapezoid,*/
+ NULL, /*gx_default_fill_parallelogram,*/
+ NULL, /*gx_default_fill_triangle,*/
+ NULL, /*gx_default_draw_thin_line,*/
+ NULL, /* begin image */
+ NULL, /* image_data */
+ NULL, /* end_image */
+ NULL, /*gx_default_strip_tile_rectangle,*/
+ txtwrite_strip_copy_rop,
+ NULL, /* get_clipping_box */
+ NULL, /* txtwrite_begin_typed_image */
+ NULL, /* get_bits_rectangle */
+ NULL, /*gx_default_map_color_rgb_alpha,*/
+ gx_null_create_compositor,
+ NULL, /* get_hardware_params */
+ txtwrite_text_begin,
+ NULL, /* finish_copydevice */
+ NULL, /* begin_transparency_group */
+ NULL, /* end_transparency_group */
+ NULL, /* begin_transparency_mask */
+ NULL, /* end_transparency_mask */
+ NULL, /* discard_transparency_layer */
+ NULL, /* get_color_mapping_procs */
+ NULL, /* get_color_comp_index */
+ NULL, /* encode_color */
+ NULL, /* decode_color */
+ NULL, /* pattern manager */
+ NULL, /* fill_rectangle_hl_color */
+ NULL, /* include_color_space */
+ NULL, /* fill_linear_color_scanline */
+ NULL, /* fill_linear_color_trapezoid */
+ NULL, /* fill_linear_color_triangle */
+ NULL, /* update_spot_equivalent_colors */
+ NULL, /* ret_devn_params */
+ NULL, /* fillpage */
+ NULL, /* push_transparency_state */
+ NULL, /* pop_transparency_state */
+ NULL, /* put_image */
+ txtwrite_dev_spec_op, /* dev_spec_op */
+ NULL, /* copy_planes */
+ NULL, /* get_profile */
+ NULL, /* set_graphics_type_tag */
+ NULL, /* strip_copy_rop2 */
+ NULL /* strip_tile_rect_devn */
+ },
+ { 0 }, /* Page Data */
+ { 0 }, /* Output Filename */
+ 0, /* Output FILE * */
+ 3 /* TextFormat */
+};
+
+#ifndef gx_device_textw_DEFINED
+# define gx_device_textw_DEFINED
+typedef struct gx_device_textwrite_s gx_device_textw;
+#endif
+
+static const gs_param_item_t txt_param_items[] = {
+#define pi(key, type, memb) { key, type, offset_of(gx_device_txtwrite_t, memb) }
+ pi("TextFormat", gs_param_type_int, TextFormat),
+#undef pi
+ gs_param_item_end
+};
+
+/* ---------------- Open/close/page ---------------- */
+
+static int
+txtwrite_open_device(gx_device * dev)
+{
+ gx_device_txtwrite_t *const tdev = (gx_device_txtwrite_t *) dev;
+ int code = 0;
+
+ gx_device_fill_in_procs(dev);
+ if (tdev->fname[0] == 0)
+ return_error(gs_error_undefinedfilename);
+
+ tdev->PageData.PageNum = 0;
+ tdev->PageData.y_ordered_list = NULL;
+ tdev->file = NULL;
+#ifdef TRACE_TXTWRITE
+ tdev->DebugFile = gp_fopen("/temp/txtw_dbg.txt", "wb+");
+#endif
+
+ code = install_internal_subclass_devices((gx_device **)&dev, NULL);
+ return code;
+}
+
+static int
+txtwrite_close_device(gx_device * dev)
+{
+ int code = 0;
+ gx_device_txtwrite_t *const tdev = (gx_device_txtwrite_t *) dev;
+
+ if (tdev->file) {
+ code = gx_device_close_output_file(dev, tdev->fname, tdev->file);
+ tdev->file = 0;
+ }
+
+#ifdef TRACE_TXTWRITE
+ fclose(tdev->DebugFile);
+#endif
+ return code;
+}
+
+/* Routine inspects horizontal lines of text to see if they can be collapsed
+ * into a single line. This essentially detects superscripts and subscripts
+ * as well as lines which are slightly mis-aligned.
+ */
+static int merge_vertically(gx_device_txtwrite_t *tdev)
+{
+#ifdef TRACE_TXTWRITE
+ text_list_entry_t *debug_x;
+#endif
+ page_text_list_t *y_list = tdev->PageData.y_ordered_list;
+
+ while (y_list && y_list->next) {
+ page_text_list_t *next = y_list->next;
+ bool collision = false;
+ float overlap = (y_list->start.y + y_list->MaxY) - (next->start.y + next->MinY);
+
+ if (overlap >= (y_list->MaxY - y_list->MinY) / 4) {
+ /* At least a 25% overlap, lets test for x collisions */
+ text_list_entry_t *upper = y_list->x_ordered_list, *lower;
+ while (upper && !collision) {
+ lower = next->x_ordered_list;
+ while (lower && !collision) {
+ if (upper->start.x >= lower->start.x) {
+ if (upper->start.x <= lower->end.x) {
+ /* Collision */
+ collision = true;
+ break;
+ }
+ } else {
+ if (upper->end.x > lower->start.x) {
+ /* Collision */
+ collision = true;
+ break;
+ }
+ }
+ lower = lower->next;
+ }
+ upper = upper->next;
+ }
+ if (!collision) {
+ text_list_entry_t *from, *to, *new_order, *current;
+ /* Consolidate y lists */
+ to = y_list->x_ordered_list;
+ from = next->x_ordered_list;
+#ifdef TRACE_TXTWRITE
+ fprintf(tdev->DebugFile, "\nConsolidating two horizontal lines, line 1:");
+ debug_x = from;
+ while (debug_x) {
+ fprintf(tdev->DebugFile, "\n\t");
+ fwrite(debug_x->Unicode_Text, sizeof(unsigned short), debug_x->Unicode_Text_Size, tdev->DebugFile);
+ debug_x = debug_x->next;
+ }
+ fprintf(tdev->DebugFile, "\nConsolidating two horizontal lines, line 2");
+ debug_x = to;
+ while (debug_x) {
+ fprintf(tdev->DebugFile, "\n\t");
+ fwrite(debug_x->Unicode_Text, sizeof(unsigned short), debug_x->Unicode_Text_Size, tdev->DebugFile);
+ debug_x = debug_x->next;
+ }
+#endif
+ if (from->start.x < to->start.x) {
+ current = new_order = from;
+ from = from->next;
+ } else {
+ current = new_order = to;
+ to = to->next;
+ }
+ while (to && from) {
+ if (to->start.x < from->start.x) {
+ current->next = to;
+ to->previous = current;
+ to = to->next;
+ } else {
+ current->next = from;
+ from->previous = current;
+ from = from->next;
+ }
+ current = current->next;
+ }
+ if (to) {
+ to->previous = current;
+ current->next = to;
+ } else {
+ if (from) {
+ from->previous = current;
+ current->next = from;
+ }
+ }
+ y_list->x_ordered_list = new_order;
+#ifdef TRACE_TXTWRITE
+ fprintf(tdev->DebugFile, "\nAfter:");
+ debug_x = new_order;
+ while (debug_x) {
+ fprintf(tdev->DebugFile, "\n\t");
+ fwrite(debug_x->Unicode_Text, sizeof(unsigned short), debug_x->Unicode_Text_Size, tdev->DebugFile);
+ debug_x = debug_x->next;
+ }
+ fprintf(tdev->DebugFile, "\n");
+#endif
+ y_list->next = next->next;
+ if (next->next)
+ next->next->previous = y_list;
+ gs_free(tdev->memory, next, 1, sizeof(page_text_list_entry_t), "txtwrite free text list");
+ } else
+ y_list = next;
+ } else
+ y_list = next;
+ }
+ return 0;
+}
+
+/* Routine to merge horizontally adjacent text fragments. If the distance
+ * between two horizontal fragments is small, then they are treated as one
+ * frament of text, if its larger then we insert a space (and set the Width
+ * entry appropriately). Otherwise we leave them as separate.
+ */
+static int merge_horizontally(gx_device_txtwrite_t *tdev)
+{
+#ifdef TRACE_TXTWRITE
+ text_list_entry_t *debug_x;
+#endif
+ unsigned short UnicodeSpace = 0x20;
+ page_text_list_t *y_list = tdev->PageData.y_ordered_list;
+
+ while (y_list) {
+ float average_width;
+ text_list_entry_t *from, *to;
+ from = y_list->x_ordered_list;
+ to = from->next;
+
+ while (from && to) {
+ average_width = (from->end.x - from->start.x) / from->Unicode_Text_Size;
+
+ if (to->start.x - from->end.x < average_width / 2) {
+ /* consolidate fragments */
+ unsigned short *NewText;
+ float *NewWidths;
+
+ NewText = (unsigned short *)gs_malloc(tdev->memory->stable_memory,
+ (from->Unicode_Text_Size + to->Unicode_Text_Size), sizeof(unsigned short), "txtwrite alloc working text buffer");
+ NewWidths = (float *)gs_malloc(tdev->memory->stable_memory,
+ (from->Unicode_Text_Size + to->Unicode_Text_Size), sizeof(float), "txtwrite alloc Widths array");
+ if (!NewText || !NewWidths) {
+ if (NewText)
+ gs_free(tdev->memory, NewText, from->Unicode_Text_Size + to->Unicode_Text_Size, sizeof (unsigned short), "free working text fragment");
+ /* ran out of memory, don't consolidate */
+ from = from->next;
+ to = to->next;
+ } else {
+#ifdef TRACE_TXTWRITE
+ fprintf(tdev->DebugFile, "Consolidating two horizontal fragments in one line, before:\n\t");
+ fwrite(from->Unicode_Text, sizeof(unsigned short), from->Unicode_Text_Size, tdev->DebugFile);
+ fprintf(tdev->DebugFile, "\n\t");
+ fwrite(to->Unicode_Text, sizeof(unsigned short), to->Unicode_Text_Size, tdev->DebugFile);
+#endif
+ memcpy(NewText, from->Unicode_Text, from->Unicode_Text_Size * sizeof(unsigned short));
+ memcpy(&NewText[from->Unicode_Text_Size], to->Unicode_Text, to->Unicode_Text_Size * sizeof(unsigned short));
+ memcpy(NewWidths, from->Widths, from->Unicode_Text_Size * sizeof(float));
+ memcpy(&NewWidths[from->Unicode_Text_Size], to->Widths, to->Unicode_Text_Size * sizeof(float));
+ gs_free(tdev->memory, from->Unicode_Text, from->Unicode_Text_Size, sizeof (unsigned short), "free consolidated text fragment");
+ gs_free(tdev->memory, to->Unicode_Text, to->Unicode_Text_Size, sizeof (unsigned short), "free consolidated text fragment");
+ gs_free(tdev->memory, from->Widths, from->Unicode_Text_Size, sizeof (float), "free consolidated Widths array");
+ gs_free(tdev->memory, to->Widths, to->Unicode_Text_Size, sizeof (float), "free consolidated Widths array");
+ gs_free(tdev->memory, to->FontName, 1, strlen(from->FontName) + 1, "free FontName");
+ from->Unicode_Text = NewText;
+ from->Unicode_Text_Size += to->Unicode_Text_Size;
+ from->Widths = NewWidths;
+#ifdef TRACE_TXTWRITE
+ fprintf(tdev->DebugFile, "After:\n\t");
+ fwrite(from->Unicode_Text, sizeof(unsigned short), from->Unicode_Text_Size, tdev->DebugFile);
+#endif
+ from->end = to->end;
+ from->next = to->next;
+ if (from->next)
+ from->next->previous = from;
+ gs_free(tdev->memory, to, 1, sizeof(text_list_entry_t), "free consolidated fragment");
+ to = from->next;
+ }
+ } else {
+ if (to->start.x - from->end.x < average_width *2){
+ unsigned short *NewText;
+ float *NewWidths;
+
+ NewText = (unsigned short *)gs_malloc(tdev->memory->stable_memory,
+ (from->Unicode_Text_Size + to->Unicode_Text_Size + 1), sizeof(unsigned short), "txtwrite alloc text state");
+ NewWidths = (float *)gs_malloc(tdev->memory->stable_memory,
+ (from->Unicode_Text_Size + to->Unicode_Text_Size + 1), sizeof(float), "txtwrite alloc Widths array");
+ if (!NewText || !NewWidths) {
+ if (NewText)
+ gs_free(tdev->memory, NewText, from->Unicode_Text_Size + to->Unicode_Text_Size, sizeof (unsigned short), "free working text fragment");
+ /* ran out of memory, don't consolidate */
+ from = from->next;
+ to = to->next;
+ } else {
+ memcpy(NewText, from->Unicode_Text, from->Unicode_Text_Size * sizeof(unsigned short));
+ memcpy(&NewText[from->Unicode_Text_Size], &UnicodeSpace, sizeof(unsigned short));
+ memcpy(&NewText[from->Unicode_Text_Size + 1], to->Unicode_Text, to->Unicode_Text_Size * sizeof(unsigned short));
+ memcpy(NewWidths, from->Widths, from->Unicode_Text_Size * sizeof(float));
+ NewWidths[from->Unicode_Text_Size] = to->start.x - from->end.x;
+ memcpy(&NewWidths[from->Unicode_Text_Size + 1], to->Widths, to->Unicode_Text_Size * sizeof(float));
+ gs_free(tdev->memory, from->Unicode_Text, from->Unicode_Text_Size, sizeof (unsigned short), "free consolidated text fragment");
+ gs_free(tdev->memory, to->Unicode_Text, to->Unicode_Text_Size, sizeof (unsigned short), "free consolidated text fragment");
+ gs_free(tdev->memory, from->Widths, from->Unicode_Text_Size, sizeof (float), "free consolidated Widths array");
+ gs_free(tdev->memory, to->Widths, to->Unicode_Text_Size, sizeof (float), "free consolidated Widths array");
+ gs_free(tdev->memory, to->FontName, 1, strlen(from->FontName) + 1, "free FontName");
+ from->Unicode_Text = NewText;
+ from->Unicode_Text_Size += to->Unicode_Text_Size + 1;
+ from->Widths = NewWidths;
+ from->end = to->end;
+ from->next = to->next;
+ if (from->next)
+ from->next->previous = from;
+ gs_free(tdev->memory, to, 1, sizeof(text_list_entry_t), "free consolidated fragment");
+ to = from->next;
+ }
+ } else {
+ from = from->next;
+ to = to->next;
+ }
+ }
+ }
+ y_list = y_list->next;
+ }
+ return 0;
+}
+
+static int write_simple_text(unsigned short *text, int count, gx_device_txtwrite_t *tdev)
+{
+ switch(tdev->TextFormat) {
+ case 2:
+ fwrite(text, sizeof (unsigned short), count, tdev->file);
+ break;
+ case 3:
+ {
+ int i;
+ unsigned short *UTF16 = (unsigned short *)text;
+ unsigned char UTF8[3];
+
+ for (i=0;i<count;i++) {
+ if (*UTF16 < 0x80) {
+ UTF8[0] = *UTF16 & 0xff;
+ fwrite (UTF8, sizeof(unsigned char), 1, tdev->file);
+ } else {
+ if (*UTF16 < 0x800) {
+ UTF8[0] = (*UTF16 >> 6) + 0xC0;
+ UTF8[1] = (*UTF16 & 0x3F) + 0x80;
+ fwrite (UTF8, sizeof(unsigned char), 2, tdev->file);
+ } else {
+ UTF8[0] = (*UTF16 >> 12) + 0xE0;
+ UTF8[1] = ((*UTF16 >> 6) & 0x3F) + 0x80;
+ UTF8[2] = (*UTF16 & 0x3F) + 0x80;
+ fwrite (UTF8, sizeof(unsigned char), 3, tdev->file);
+ }
+ }
+ UTF16++;
+ }
+ }
+ break;
+ default:
+ return gs_note_error(gs_error_rangecheck);
+ break;
+ }
+ return 0;
+}
+
+static int simple_text_output(gx_device_txtwrite_t *tdev)
+{
+ int chars_wide;
+ float char_size, min_size, min_width_size;
+#ifdef TRACE_TXTWRITE
+ text_list_entry_t *debug_x;
+#endif
+ text_list_entry_t * x_entry;
+ page_text_list_t *y_list;
+ unsigned short UnicodeSpace = 0x20, UnicodeEOL[2] = {0x00D, 0x0a};
+
+ merge_vertically(tdev);
+
+ merge_horizontally(tdev);
+
+ min_size = (float)tdev->width;
+ /* Estimate maximum text density */
+ y_list = tdev->PageData.y_ordered_list;
+ while (y_list) {
+ x_entry = y_list->x_ordered_list;
+ while (x_entry) {
+ if (x_entry->size < min_size)
+ min_size = x_entry->size;
+ x_entry = x_entry->next;
+ }
+ y_list = y_list->next;
+ }
+
+ min_width_size = min_size;
+ y_list = tdev->PageData.y_ordered_list;
+ while (y_list) {
+ float width;
+
+ x_entry = y_list->x_ordered_list;
+ while (x_entry) {
+ width = (x_entry->end.x - x_entry->start.x) / x_entry->Unicode_Text_Size;
+ if (width < min_width_size && width >= (float)min_size * 0.75)
+ min_width_size = width;
+ x_entry = x_entry->next;
+ }
+ y_list = y_list->next;
+ }
+
+ min_size = min_width_size;
+ chars_wide = (int)ceil(tdev->width / min_size);
+ char_size = (float)tdev->width / (float)chars_wide;
+
+ y_list = tdev->PageData.y_ordered_list;
+ while (y_list) {
+ float xpos = 0;
+ x_entry = y_list->x_ordered_list;
+ while (x_entry) {
+ while (xpos < x_entry->start.x) {
+ write_simple_text(&UnicodeSpace, 1, tdev);
+ xpos += char_size;
+ }
+ write_simple_text(x_entry->Unicode_Text, x_entry->Unicode_Text_Size, tdev);
+ xpos += x_entry->Unicode_Text_Size * char_size;
+ if (x_entry->next) {
+ x_entry = x_entry->next;
+ } else {
+ x_entry = NULL;
+ }
+ }
+ write_simple_text((unsigned short *)&UnicodeEOL, 2, tdev);
+ if (y_list->next) {
+ y_list = y_list->next;
+ } else {
+ y_list = NULL;
+ }
+ }
+ return 0;
+}
+
+static int escaped_Unicode (unsigned short Unicode, char *Buf)
+{
+ switch (Unicode)
+ {
+ case 0x3C: gs_sprintf(Buf, "&lt;"); break;
+ case 0x3E: gs_sprintf(Buf, "&gt;"); break;
+ case 0x26: gs_sprintf(Buf, "&amp;"); break;
+ case 0x22: gs_sprintf(Buf, "&quot;"); break;
+ case 0x27: gs_sprintf(Buf, "&apos;"); break;
+ default:
+ if (Unicode >= 32 && Unicode <= 127)
+ gs_sprintf(Buf, "%c", Unicode);
+ else
+ gs_sprintf(Buf, "&#x%x;", Unicode);
+ break;
+ }
+
+ return 0;
+}
+
+static int decorated_text_output(gx_device_txtwrite_t *tdev)
+{
+ int i;
+ text_list_entry_t * x_entry, *next_x;
+ char TextBuffer[512], Escaped[32];
+ float xpos;
+ page_text_list_t *y_list;
+#ifdef TRACE_TXTWRITE
+ text_list_entry_t *debug_x;
+#endif
+
+ if (tdev->TextFormat == 0) {
+ fwrite("<page>\n", sizeof(unsigned char), 7, tdev->file);
+ x_entry = tdev->PageData.unsorted_text_list;
+ while (x_entry) {
+ next_x = x_entry->next;
+ gs_sprintf(TextBuffer, "<span bbox=\"%0.0f %0.0f %0.0f %0.0f\" font=\"%s\" size=\"%0.4f\">\n", x_entry->start.x, x_entry->start.y,
+ x_entry->end.x, x_entry->end.y, x_entry->FontName,x_entry->size);
+ fwrite(TextBuffer, 1, strlen(TextBuffer), tdev->file);
+ xpos = x_entry->start.x;
+ for (i=0;i<x_entry->Unicode_Text_Size;i++) {
+ escaped_Unicode(x_entry->Unicode_Text[i], (char *)&Escaped);
+ gs_sprintf(TextBuffer, "<char bbox=\"%0.0f %0.0f %0.0f %0.0f\" c=\"%s\"/>\n", xpos,
+ x_entry->start.y, xpos + x_entry->Widths[i], x_entry->end.y, Escaped);
+ fwrite(TextBuffer, 1, strlen(TextBuffer), tdev->file);
+ xpos += x_entry->Widths[i];
+ }
+ fwrite("</span>\n", sizeof(unsigned char), 8, tdev->file);
+
+ x_entry = next_x;
+ }
+ fwrite("</page>\n", sizeof(unsigned char), 8, tdev->file);
+ } else {
+
+ merge_vertically(tdev);
+ merge_horizontally(tdev);
+
+ y_list = tdev->PageData.y_ordered_list;
+ fwrite("<page>\n", sizeof(unsigned char), 7, tdev->file);
+ /* Walk the list looking for 'blocks' */
+ do {
+ page_text_list_t *temp;
+ page_text_t block;
+ page_text_list_t *block_line;
+ float BBox[4];
+
+ memset(&block, 0x00, sizeof(page_text_t));
+ memset(BBox, 0x00, sizeof(float) * 4);
+
+ while (y_list) {
+ if (block.y_ordered_list) {
+ text_list_entry_t *x_entry = y_list->x_ordered_list;
+
+ block_line = block.y_ordered_list;
+ while (x_entry) {
+ if (x_entry->start.x > BBox[2] || x_entry->end.x < BBox[0] ||
+ x_entry->start.y > (BBox[1] + (BBox[3] - BBox[1]))) {
+ ;
+ } else {
+ block_line->next = (page_text_list_t *)gs_malloc(tdev->memory->stable_memory, 1,
+ sizeof(page_text_list_t), "txtwrite alloc Y-list");
+ memset(block_line->next, 0x00, sizeof(page_text_list_t));
+ block_line = block_line->next;
+ block_line->x_ordered_list = x_entry;
+ if(x_entry->next)
+ x_entry->next->previous = x_entry->previous;
+ if (x_entry->previous)
+ x_entry->previous->next = x_entry->next;
+ else {
+ if (x_entry->next == 0x00) {
+ /* remove Y entry */
+ temp = y_list->next;
+ if (y_list->previous)
+ y_list->previous->next = y_list->next;
+ if (y_list->next)
+ y_list->next->previous = y_list->previous;
+ else {
+ if (y_list->previous == 0x00) {
+ tdev->PageData.y_ordered_list = 0x00;
+ }
+ }
+ gs_free(tdev->memory, y_list, 1, sizeof(page_text_list_t), "txtwrite free text list");
+ if (tdev->PageData.y_ordered_list == y_list)
+ tdev->PageData.y_ordered_list = temp;
+ y_list = temp;
+ x_entry = x_entry->next;
+ continue;
+ }
+ }
+ if (block_line->x_ordered_list->start.x < BBox[0])
+ BBox[0] = block_line->x_ordered_list->start.x;
+ if (block_line->x_ordered_list->start.y < BBox[1])
+ BBox[1] = block_line->x_ordered_list->start.y;
+ if (block_line->x_ordered_list->end.x < BBox[2])
+ BBox[2] = block_line->x_ordered_list->end.x;
+ if (block_line->x_ordered_list->end.y + block_line->x_ordered_list->FontBBox_topright.y < BBox[3])
+ BBox[3] = block_line->x_ordered_list->end.y + block_line->x_ordered_list->FontBBox_topright.y;
+ }
+ x_entry = x_entry->next;
+ }
+ } else {
+ block.y_ordered_list = block_line = (page_text_list_t *)gs_malloc(tdev->memory->stable_memory, 1,
+ sizeof(page_text_list_t), "txtwrite alloc Y-list");
+ memset(block_line, 0x00, sizeof(page_text_list_t));
+ block_line->x_ordered_list = y_list->x_ordered_list;
+ y_list->x_ordered_list = y_list->x_ordered_list->next;
+ if (y_list->x_ordered_list == 0x00) {
+ temp = y_list->next;
+ if (y_list->previous)
+ y_list->previous->next = y_list->next;
+ if (y_list->next)
+ y_list->next->previous = y_list->previous;
+ else {
+ if (y_list->previous == 0x00) {
+ tdev->PageData.y_ordered_list = 0x00;
+ }
+ }
+ gs_free(tdev->memory, y_list, 1, sizeof(page_text_list_t), "txtwrite free text list");
+ if (tdev->PageData.y_ordered_list == y_list)
+ tdev->PageData.y_ordered_list = temp;
+ y_list = temp;
+ continue;
+ }
+ block_line->x_ordered_list->next = block_line->x_ordered_list->previous = 0x00;
+ BBox[0] = block_line->x_ordered_list->start.x;
+ BBox[1] = block_line->x_ordered_list->start.y;
+ BBox[2] = block_line->x_ordered_list->end.x;
+ BBox[3] = block_line->x_ordered_list->end.y + block_line->x_ordered_list->FontBBox_topright.y;
+ }
+ if (y_list)
+ y_list = y_list->next;
+ }
+ /* FIXME - need to free the used memory in here */
+ fwrite("<block>\n", sizeof(unsigned char), 8, tdev->file);
+ block_line = block.y_ordered_list;
+ while (block_line) {
+ fwrite("<line>\n", sizeof(unsigned char), 7, tdev->file);
+ x_entry = block_line->x_ordered_list;
+ while(x_entry) {
+ gs_sprintf(TextBuffer, "<span bbox=\"%0.0f %0.0f %0.0f %0.0f\" font=\"%s\" size=\"%0.4f\">\n", x_entry->start.x, x_entry->start.y,
+ x_entry->end.x, x_entry->end.y, x_entry->FontName,x_entry->size);
+ fwrite(TextBuffer, 1, strlen(TextBuffer), tdev->file);
+ xpos = x_entry->start.x;
+ for (i=0;i<x_entry->Unicode_Text_Size;i++) {
+ escaped_Unicode(x_entry->Unicode_Text[i], (char *)&Escaped);
+ gs_sprintf(TextBuffer, "<char bbox=\"%0.0f %0.0f %0.0f %0.0f\" c=\"%s\"/>\n", xpos,
+ x_entry->start.y, xpos + x_entry->Widths[i], x_entry->end.y, Escaped);
+ fwrite(TextBuffer, 1, strlen(TextBuffer), tdev->file);
+ xpos += x_entry->Widths[i];
+ }
+ fwrite("</span>\n", sizeof(unsigned char), 8, tdev->file);
+ x_entry = x_entry->next;
+ }
+ fwrite("</line>\n", sizeof(unsigned char), 8, tdev->file);
+ block_line = block_line->next;
+ }
+ fwrite("</block>\n", sizeof(unsigned char), 9, tdev->file);
+ y_list = tdev->PageData.y_ordered_list;
+ } while (y_list);
+
+ fwrite("</page>\n", sizeof(unsigned char), 8, tdev->file);
+ }
+ return 0;
+}
+
+static int
+txtwrite_output_page(gx_device * dev, int num_copies, int flush)
+{
+ int code;
+ gx_device_txtwrite_t *const tdev = (gx_device_txtwrite_t *) dev;
+ text_list_entry_t * x_entry, *next_x;
+ page_text_list_t *y_list;
+ gs_parsed_file_name_t parsed;
+ const char *fmt;
+ const short BOM = 0xFEFF;
+
+ if (!tdev->file) {
+ /* Either this is the first page, or we're doing one file per page */
+ code = gx_device_open_output_file(dev, tdev->fname,
+ true, false, &tdev->file); /* binary, sequential */
+ if (code < 0)
+ return code;
+ }
+
+ switch(tdev->TextFormat) {
+ case 0:
+ case 1:
+ code = decorated_text_output(tdev);
+ if (code < 0)
+ return code;
+ break;
+
+ case 2:
+ fwrite (&BOM, sizeof(unsigned short), 1, tdev->file);
+ case 3:
+ code = simple_text_output(tdev);
+ if (code < 0)
+ return code;
+ break;
+
+ default:
+ return gs_note_error(gs_error_rangecheck);
+ break;
+ }
+
+ code = gx_default_output_page(dev, num_copies, flush);
+ if (code < 0)
+ return code;
+
+ /* free the sorted fragment list! */
+ y_list = tdev->PageData.y_ordered_list;
+ while (y_list) {
+ x_entry = y_list->x_ordered_list;
+ while (x_entry) {
+ gs_free(tdev->memory, x_entry->Unicode_Text, x_entry->Unicode_Text_Size, sizeof (usnigned short), "txtwrite free text fragment text buffer");
+ gs_free(tdev->memory, x_entry->Widths, x_entry->Unicode_Text_Size, sizeof (float), "txtwrite free widths array");
+ gs_free(tdev->memory, x_entry->FontName, 1, strlen(x_entry->FontName) + 1, "txtwrite free Font Name");
+ if (x_entry->next) {
+ x_entry = x_entry->next;
+ gs_free(tdev->memory, x_entry->previous, 1, sizeof(text_list_entry_t), "txtwrite free text fragment");
+ } else {
+ gs_free(tdev->memory, x_entry, 1, sizeof(text_list_entry_t), "txtwrite free text fragment");
+ x_entry = NULL;
+ }
+ }
+ if (y_list->next) {
+ y_list = y_list->next;
+ gs_free(tdev->memory, y_list->previous, 1, sizeof(page_text_list_t), "txtwrite free text list");
+ } else {
+ gs_free(tdev->memory, y_list, 1, sizeof(page_text_list_t), "txtwrite free text list");
+ y_list = NULL;
+ }
+ }
+ tdev->PageData.y_ordered_list = NULL;
+
+ /* free the unsorted fragment list */
+ x_entry = tdev->PageData.unsorted_text_list;
+ while (x_entry) {
+ next_x = x_entry->next;
+ gs_free(tdev->memory, x_entry->Unicode_Text, x_entry->Unicode_Text_Size, sizeof (usnigned short), "txtwrite free unsorted text fragment text buffer");
+ gs_free(tdev->memory, x_entry->Widths, x_entry->Unicode_Text_Size, sizeof (float), "txtwrite free widths array");
+ gs_free(tdev->memory, x_entry->FontName, 1, strlen(x_entry->FontName) + 1, "txtwrite free Font Name");
+ gs_free(tdev->memory, x_entry, 1, sizeof(text_list_entry_t), "txtwrite free unsorted text fragment");
+ x_entry = next_x;
+ }
+ tdev->PageData.unsorted_text_list = NULL;
+
+ code = gx_parse_output_file_name(&parsed, &fmt, tdev->fname,
+ strlen(tdev->fname), tdev->memory);
+
+ if (code >= 0 && fmt) { /* file per page */
+ code = gx_device_close_output_file(dev, tdev->fname, tdev->file);
+ tdev->file = NULL;
+ }
+ return code;
+}
+
+/* ---------------- Low-level drawing ---------------- */
+
+static int
+txtwrite_fill_rectangle(gx_device * dev, int x, int y, int w, int h,
+ gx_color_index color)
+{
+ return 0;
+}
+
+/*static int
+txtwrite_copy_alpha(gx_device * dev, const byte * data, int data_x,
+ int raster, gx_bitmap_id id, int x, int y, int w, int h,
+ gx_color_index color, int depth)
+{
+ return 0;
+}
+
+static int
+txtwrite_copy_mono(gx_device * dev, const byte * data, int dx, int raster,
+ gx_bitmap_id id, int x, int y, int w, int h,
+ gx_color_index zero, gx_color_index one)
+{
+ return 0;
+}
+static int
+txtwrite_copy_color(gx_device * dev, const byte * data,
+ int data_x, int raster, gx_bitmap_id id,
+ int x, int y, int width, int height)
+{
+ return 0;
+}
+
+static int
+txtwrite_strip_tile_rectangle(gx_device * dev, const gx_strip_bitmap * tiles,
+ int x, int y, int w, int h, gx_color_index color0, gx_color_index color1,
+ int px, int py)
+{
+ return 0;
+}
+
+static int
+txtwrite_strip_copy_rop(gx_device * dev,
+ const byte * sdata, int sourcex, uint sraster,
+ gx_bitmap_id id,
+ const gx_color_index * scolors,
+ const gx_strip_bitmap * textures,
+ const gx_color_index * tcolors,
+ int x, int y, int w, int h,
+ int phase_x, int phase_y, gs_logical_operation_t lop)
+{
+ return 0;
+}*/
+
+/* ---------------- Parameters ---------------- */
+
+static int txtwrite_get_param(gx_device *dev, char *Param, void *list)
+{
+ gx_device_txtwrite_t *const tdev = (gx_device_txtwrite_t *) dev;
+ gs_param_list * plist = (gs_param_list *)list;
+ bool bool_T = true;
+
+ if (strcmp(Param, "OutputFile") == 0) {
+ gs_param_string ofns;
+
+ ofns.data = (const byte *)tdev->fname,
+ ofns.size = strlen(tdev->fname),
+ ofns.persistent = false;
+ return param_write_string(plist, "OutputFile", &ofns);
+ }
+ if (strcmp(Param, "WantsToUnicode") == 0) {
+ return param_write_bool(plist, "WantsToUnicode", &bool_T);
+ }
+ if (strcmp(Param, "PreserveTrMode") == 0) {
+ return param_write_bool(plist, "PreserveTrMode", &bool_T);
+ }
+ if (strcmp(Param, "HighLevelDevice") == 0) {
+ return param_write_bool(plist, "HighLevelDevice", &bool_T);
+ }
+ return gs_error_undefined;
+}
+
+static int
+txtwrite_get_params(gx_device * dev, gs_param_list * plist)
+{
+ int code;
+ bool bool_T = true;
+ gs_param_string ofns;
+ gx_device_txtwrite_t *const tdev = (gx_device_txtwrite_t *) dev;
+
+ code = gx_default_get_params(dev, plist);
+ if (code < 0)
+ return code;
+
+ ofns.data = (const byte *)tdev->fname,
+ ofns.size = strlen(tdev->fname),
+ ofns.persistent = false;
+ code = param_write_string(plist, "OutputFile", &ofns);
+ if (code < 0)
+ return code;
+
+ code = param_write_bool(plist, "WantsToUnicode", &bool_T);
+ if (code < 0)
+ return code;
+
+ code = param_write_bool(plist, "PreserveTrMode", &bool_T);
+ if (code < 0)
+ return code;
+
+ code = param_write_bool(plist, "HighLevelDevice", &bool_T);
+ if (code < 0)
+ return code;
+
+ code = gs_param_write_items(plist, tdev, NULL, txt_param_items);
+ return code;
+}
+
+/* We implement put_params to ensure that we keep the important */
+/* device parameters up to date, and to prevent an /undefined error */
+static int
+txtwrite_put_params(gx_device * dev, gs_param_list * plist)
+{
+ gx_device_txtwrite_t *tdev = (gx_device_txtwrite_t *) dev;
+ int ecode = 0;
+ int code;
+ const char *param_name;
+ gs_param_string ofs;
+ bool dummy;
+
+ switch (code = param_read_string(plist, (param_name = "OutputFile"), &ofs)) {
+ case 0:
+ if (dev->LockSafetyParams &&
+ bytes_compare(ofs.data, ofs.size,
+ (const byte *)tdev->fname, strlen(tdev->fname))) {
+ ecode = gs_note_error(gs_error_invalidaccess);
+ goto ofe;
+ }
+ if (ofs.size >= gp_file_name_sizeof)
+ ecode = gs_error_limitcheck;
+ else
+ break;
+ goto ofe;
+ default:
+ ecode = code;
+ ofe:param_signal_error(plist, param_name, ecode);
+ case 1:
+ ofs.data = 0;
+ break;
+ }
+
+ if (ecode < 0)
+ return ecode;
+
+ code = param_read_int(plist, "TextFormat", &tdev->TextFormat);
+ if (code < 0)
+ return code;
+
+ code = param_read_bool(plist, "WantsToUnicode", &dummy);
+ if (code < 0)
+ return code;
+
+ code = param_read_bool(plist, "HighLevelDevice", &dummy);
+ if (code < 0)
+ return code;
+
+ code = param_read_bool(plist, "PreserveTrMode", &dummy);
+ if (code < 0)
+ return code;
+
+ code = gx_default_put_params(dev, plist);
+ if (code < 0)
+ return code;
+
+ if (ofs.data != 0) { /* Close the file if it's open. */
+ if (tdev->file != 0) {
+ fclose(tdev->file);
+ tdev->file = 0;
+ }
+ memcpy(tdev->fname, ofs.data, ofs.size);
+ tdev->fname[ofs.size] = 0;
+ }
+ return 0;
+}
+
+/* ---------------- Polygon drawing ---------------- */
+
+/*static int
+txtwrite_fill_trapezoid(gx_device * dev,
+ const gs_fixed_edge * left, const gs_fixed_edge * right,
+ fixed ybot, fixed ytop, bool swap_axes,
+ const gx_device_color * pdevc, gs_logical_operation_t lop)
+{
+ return 0;
+}
+
+static int
+txtwrite_fill_parallelogram(gx_device * dev,
+ fixed px, fixed py, fixed ax, fixed ay,
+ fixed bx, fixed by, const gx_device_color * pdevc,
+ gs_logical_operation_t lop)
+{
+ return 0;
+}
+
+static int
+txtwrite_fill_triangle(gx_device * dev,
+ fixed px, fixed py, fixed ax, fixed ay, fixed bx, fixed by,
+ const gx_device_color * pdevc, gs_logical_operation_t lop)
+{
+ return 0;
+}
+
+static int
+txtwrite_draw_thin_line(gx_device * dev,
+ fixed fx0, fixed fy0, fixed fx1, fixed fy1,
+ const gx_device_color * pdevc, gs_logical_operation_t lop,
+ fixed adjustx, fixed adjusty)
+{
+ return 0;
+}*/
+
+/* ---------------- High-level drawing ---------------- */
+
+static int
+txtwrite_fill_path(gx_device * dev, const gs_imager_state * pis, gx_path * ppath,
+ const gx_fill_params * params, const gx_device_color * pdevc,
+ const gx_clip_path * pcpath)
+{
+ return 0;
+}
+
+static int
+txtwrite_stroke_path(gx_device * dev, const gs_imager_state * pis, gx_path * ppath,
+ const gx_stroke_params * params,
+ const gx_drawing_color * pdevc, const gx_clip_path * pcpath)
+{
+ return 0;
+}
+
+#if 0
+static int
+txtwrite_fill_mask(gx_device * dev,
+ const byte * data, int dx, int raster, gx_bitmap_id id,
+ int x, int y, int w, int h,
+ const gx_drawing_color * pdcolor, int depth,
+ gs_logical_operation_t lop, const gx_clip_path * pcpath)
+{
+ return 0;
+}
+
+int
+txtwrite_begin_typed_image(gx_device * dev,
+ const gs_imager_state * pis, const gs_matrix * pmat,
+ const gs_image_common_t * pic, const gs_int_rect * prect,
+ const gx_drawing_color * pdcolor,
+ const gx_clip_path * pcpath,
+ gs_memory_t * memory, gx_image_enum_common_t ** pinfo)
+{
+ return 0;
+}
+#endif
+
+/* ------ Text imaging ------ */
+
+static int
+txtwrite_font_orig_matrix(const gs_font *font, gs_glyph cid, gs_matrix *pmat)
+{
+ int code;
+
+ switch (font->FontType) {
+ case ft_composite: /* subfonts have their own FontMatrix */
+ case ft_TrueType:
+ case ft_CID_TrueType:
+ /* The TrueType FontMatrix is 1 unit per em, which is what we want. */
+ gs_make_identity(pmat);
+ return 0;
+ case ft_encrypted:
+ case ft_encrypted2:
+ case ft_CID_encrypted:
+ case ft_user_defined:
+ case ft_PCL_user_defined:
+ case ft_GL2_stick_user_defined:
+ case ft_GL2_531:
+ /*
+ * Type 1 fonts are supposed to use a standard FontMatrix of
+ * [0.001 0 0 0.001 0 0], with a 1000-unit cell. However,
+ * Windows NT 4.0 creates Type 1 fonts, apparently derived from
+ * TrueType fonts, that use a 2048-unit cell and corresponding
+ * FontMatrix. Also, some PS programs perform font scaling by
+ * replacing FontMatrix like this :
+ *
+ * /f12 /Times-Roman findfont
+ * copyfont % (remove FID)
+ * dup /FontMatrix [0.012 0 0 0.012 0 0] put
+ * definefont
+ * /f12 1 selectfont
+ *
+ * Such fonts are their own "base font", but the orig_matrix
+ * must still be set to 0.001, not 0.012 .
+ *
+ * The old code used a heuristic to detect and correct for this here.
+ * Unfortunately it doesn't work properly when it meets a font
+ * with FontMatrix like this :
+ *
+ * /FontMatrix [1 2288 div 0 0 1 2288 div 0 0 ] def
+ *
+ * (the bug 686970). Also comparefiles\455690.pdf appears to
+ * have similar problem. Therefore we added a support to lib/gs_fonts.ps,
+ * src/zbfont.c, src/gsfont.c that provides an acces to the original
+ * font via a special key OrigFont added to the font dictionary while definefont.
+ * Now we work through this access with PS interpreter,
+ * but keep the old heuristic for other clients.
+ */
+ {
+ const gs_font *base_font = font;
+
+ while (base_font->base != base_font)
+ base_font = base_font->base;
+ if (font->FontType == ft_user_defined ||
+ font->FontType == ft_PCL_user_defined ||
+ font->FontType == ft_GL2_stick_user_defined ||
+ font->FontType == ft_GL2_531)
+ *pmat = base_font->FontMatrix;
+ else if (base_font->orig_FontMatrix.xx != 0 || base_font->orig_FontMatrix.xy != 0 ||
+ base_font->orig_FontMatrix.yx != 0 || base_font->orig_FontMatrix.yy != 0)
+ *pmat = base_font->orig_FontMatrix;
+ else {
+ /* Must not happen with PS interpreter.
+ Provide a hewuristic for other clients.
+ */
+ if (base_font->FontMatrix.xx == 1.0/2048 &&
+ base_font->FontMatrix.xy == 0 &&
+ base_font->FontMatrix.yx == 0 &&
+ any_abs(base_font->FontMatrix.yy) == 1.0/2048
+ )
+ *pmat = base_font->FontMatrix;
+ else
+ gs_make_scaling(0.001, 0.001, pmat);
+ }
+ }
+ if (font->FontType == ft_CID_encrypted && cid != -1) {
+ int fidx;
+
+ if (cid < GS_MIN_CID_GLYPH)
+ cid = GS_MIN_CID_GLYPH;
+ code = ((gs_font_cid0 *)font)->cidata.glyph_data((gs_font_base *)font,
+ cid, NULL, &fidx);
+ if (code < 0) {
+ code = ((gs_font_cid0 *)font)->cidata.glyph_data((gs_font_base *)font,
+ (gs_glyph)GS_MIN_CID_GLYPH, NULL, &fidx);
+ }
+ if (code >= 0) {
+ gs_matrix_multiply(&(gs_cid0_indexed_font(font, fidx)->FontMatrix),
+ pmat, pmat);
+ }
+ }
+ return 0;
+ default:
+ return_error(gs_error_rangecheck);
+ }
+}
+
+/*
+ * Special version of txtwrite_font_orig_matrix(), that considers FDArray font's FontMatrix too.
+ * Called only by txt_glyph_width().
+ * 'cid' is only consulted if 'font' is a CIDFontType 0 CID font.
+ */
+static int
+glyph_orig_matrix(const gs_font *font, gs_glyph cid, gs_matrix *pmat)
+{
+ int code = txtwrite_font_orig_matrix(font, cid, pmat);
+ if (code >= 0) {
+ if (font->FontType == ft_CID_encrypted) {
+ int fidx;
+
+ if (cid < GS_MIN_CID_GLYPH)
+ cid = GS_MIN_CID_GLYPH;
+ code = ((gs_font_cid0 *)font)->cidata.glyph_data((gs_font_base *)font,
+ cid, NULL, &fidx);
+ if (code < 0) {
+ code = ((gs_font_cid0 *)font)->cidata.glyph_data((gs_font_base *)font,
+ (gs_glyph)GS_MIN_CID_GLYPH, NULL, &fidx);
+ }
+ if (code >= 0) {
+ gs_matrix_multiply(&(gs_cid0_indexed_font(font, fidx)->FontMatrix),
+ pmat, pmat);
+ }
+ }
+ }
+ return code;
+}
+
+/*
+ * Compute the cached values in the text processing state from the text
+ * parameters, current_font, and pis->ctm. Return either an error code (<
+ * 0) or a mask of operation attributes that the caller must emulate.
+ * Currently the only such attributes are TEXT_ADD_TO_ALL_WIDTHS and
+ * TEXT_ADD_TO_SPACE_WIDTH. Note that this procedure fills in all the
+ * values in ppts->values, not just the ones that need to be set now.
+ */
+static int
+transform_delta_inverse(const gs_point *pdelta, const gs_matrix *pmat,
+ gs_point *ppt)
+{
+ int code = gs_distance_transform_inverse(pdelta->x, pdelta->y, pmat, ppt);
+ gs_point delta;
+
+ if (code < 0)
+ return code;
+ if (ppt->y == 0)
+ return 0;
+ /* Check for numerical fuzz. */
+ code = gs_distance_transform(ppt->x, 0.0, pmat, &delta);
+ if (code < 0)
+ return 0; /* punt */
+ if (fabs(delta.x - pdelta->x) < 0.01 && fabs(delta.y - pdelta->y) < 0.01) {
+ /* Close enough to y == 0: device space error < 0.01 pixel. */
+ ppt->y = 0;
+ }
+ return 0;
+}
+
+static
+float txt_calculate_text_size(gs_imager_state *pis, gs_font *ofont,
+ const gs_matrix *pfmat, gs_matrix *smat, gs_matrix *tmat,
+ gs_font *font, gx_device *pdev)
+{
+ gs_matrix orig_matrix;
+ double
+ sx = pdev->HWResolution[0] / 72.0,
+ sy = pdev->HWResolution[1] / 72.0;
+ float size;
+
+ /* Get the original matrix of the base font. */
+
+ txtwrite_font_orig_matrix(ofont, -1, &orig_matrix);
+ /* Compute the scaling matrix and combined matrix. */
+
+ gs_matrix_invert(&orig_matrix, smat);
+ gs_matrix_multiply(smat, pfmat, smat);
+ *tmat = ctm_only(pis);
+ tmat->tx = tmat->ty = 0;
+ gs_matrix_multiply(smat, tmat, tmat);
+
+ /* Try to find a reasonable size value. */
+
+ size = hypot(tmat->yx, tmat->yy) / sy;
+ if (size < 0.01)
+ size = hypot(tmat->xx, tmat->xy) / sx;
+ if (size < 0.01)
+ size = 1;
+
+ return(size);
+}
+
+static int
+txt_update_text_state(text_list_entry_t *ppts,
+ const textw_text_enum_t *penum,
+ gs_font *ofont, const gs_matrix *pfmat)
+{
+ gx_device *const pdev = penum->dev;
+ gs_font *font = penum->current_font;
+ gs_fixed_point cpt;
+ gs_matrix smat, tmat;
+ float size;
+ int mask = 0;
+ int code = gx_path_current_point(penum->path, &cpt);
+
+ if (code < 0)
+ return code;
+
+ size = txt_calculate_text_size(penum->pis, ofont, pfmat, &smat, &tmat, penum->current_font, pdev);
+ /* Check for spacing parameters we can handle, and transform them. */
+
+ if (penum->text.operation & TEXT_ADD_TO_ALL_WIDTHS) {
+ if (penum->current_font->WMode == 0) {
+ gs_point pt;
+
+ code = transform_delta_inverse(&penum->text.delta_all, &smat, &pt);
+ if (code < 0 || pt.y != 0)
+ mask |= TEXT_ADD_TO_ALL_WIDTHS;
+ }
+ else
+ mask |= TEXT_ADD_TO_ALL_WIDTHS;
+ }
+
+ if (penum->text.operation & TEXT_ADD_TO_SPACE_WIDTH) {
+ gs_point pt;
+
+ code = transform_delta_inverse(&penum->text.delta_space, &smat, &pt);
+ if (code < 0 || pt.y != 0 || penum->text.space.s_char != 32)
+ mask |= TEXT_ADD_TO_SPACE_WIDTH;
+ }
+ /* Store the updated values. */
+
+ tmat.xx /= size;
+ tmat.xy /= size;
+ tmat.yx /= size;
+ tmat.yy /= size;
+ tmat.tx += fixed2float(cpt.x);
+ tmat.ty += fixed2float(cpt.y);
+
+ ppts->size = size;
+ ppts->matrix = tmat;
+ ppts->render_mode = penum->pis->text_rendering_mode;
+ ppts->FontName = (char *)gs_malloc(pdev->memory->stable_memory, 1,
+ font->font_name.size + 1, "txtwrite alloc font name");
+ if (!ppts->FontName)
+ return gs_note_error(gs_error_VMerror);
+ memcpy(ppts->FontName, font->font_name.chars, font->font_name.size);
+ ppts->FontName[font->font_name.size] = 0x00;
+ ppts->render_mode = font->WMode;
+
+ if (font->PaintType == 2 && penum->pis->text_rendering_mode == 0)
+ {
+ gs_imager_state *pis = penum->pis;
+ gs_font *font = penum->current_font;
+ double scaled_width = font->StrokeWidth != 0 ? font->StrokeWidth : 0.001;
+ double saved_width = pis->line_params.half_width;
+ /*
+ * See stream_to_text in gdevpdfu.c re the computation of
+ * the scaling value.
+ */
+ double scale = 72.0 / pdev->HWResolution[1];
+
+ if (font->FontMatrix.yy != 0)
+ scaled_width *= fabs(font->orig_FontMatrix.yy) * size * tmat.yy * scale;
+ else
+ scaled_width *= fabs(font->orig_FontMatrix.xy) * size * tmat.xy * scale;
+
+ ppts->render_mode = 1;
+ ppts->PaintType0Width = scaled_width;
+
+ pis->line_params.half_width = scaled_width / 2;
+ if (code < 0)
+ return code;
+
+ pis->line_params.half_width = saved_width;
+ }
+ return (code < 0 ? code : mask);
+}
+
+static int
+store_glyph_width(txt_glyph_width_t *pwidth, int wmode, const gs_matrix *scale,
+ const gs_glyph_info_t *pinfo)
+{
+ double w, v;
+
+ gs_distance_transform(pinfo->width[wmode].x, pinfo->width[wmode].y, scale, &pwidth->xy);
+ if (wmode)
+ w = pwidth->xy.y, v = pwidth->xy.x;
+ else
+ w = pwidth->xy.x, v = pwidth->xy.y;
+ if (v != 0)
+ return 1;
+ pwidth->w = w;
+ gs_distance_transform(pinfo->v.x, pinfo->v.y, scale, &pwidth->v);
+ return 0;
+}
+
+static int
+get_missing_width(gs_font *font, int wmode, const gs_matrix *scale_c,
+ txt_glyph_widths_t *pwidths)
+{
+ gs_font_info_t finfo;
+ int code;
+
+ code = font->procs.font_info((gs_font *)font, NULL,
+ FONT_INFO_MISSING_WIDTH, &finfo);
+ if (code < 0)
+ return code;
+ if (wmode) {
+ gs_distance_transform(0.0, -finfo.MissingWidth, scale_c, &pwidths->real_width.xy);
+ pwidths->Width.xy.x = 0;
+ pwidths->Width.xy.y = pwidths->real_width.xy.y;
+ pwidths->Width.w = pwidths->real_width.w =
+ pwidths->Width.xy.y;
+ pwidths->Width.v.x = - pwidths->Width.xy.y / 2;
+ pwidths->Width.v.y = - pwidths->Width.xy.y;
+ } else {
+ gs_distance_transform(finfo.MissingWidth, 0.0, scale_c, &pwidths->real_width.xy);
+ pwidths->Width.xy.x = pwidths->real_width.xy.x;
+ pwidths->Width.xy.y = 0;
+ pwidths->Width.w = pwidths->real_width.w =
+ pwidths->Width.xy.x;
+ pwidths->Width.v.x = pwidths->Width.v.y = 0;
+ }
+ /*
+ * Don't mark the width as known, just in case this is an
+ * incrementally defined font.
+ */
+ return 1;
+}
+
+/*
+ * Get the widths (unmodified from the copied font,
+ * and possibly modified from the original font) of a given glyph.
+ * Return 1 if the width was defaulted to MissingWidth.
+ * Return TEXT_PROCESS_CDEVPROC if a CDevProc callout is needed.
+ * cdevproc_result != NULL if we restart after a CDevProc callout.
+ */
+static int
+txt_glyph_widths(gs_font *font, int wmode, gs_glyph glyph,
+ gs_font *orig_font, txt_glyph_widths_t *pwidths,
+ const double cdevproc_result[10])
+{
+ gs_font *ofont = orig_font;
+ gs_glyph_info_t info;
+ gs_matrix scale_c, scale_o;
+ int code, rcode = 0;
+ gs_point v;
+ int allow_cdevproc_callout = (orig_font->FontType == ft_CID_TrueType
+ || orig_font->FontType == ft_CID_encrypted
+ ? GLYPH_INFO_CDEVPROC : 0); /* fixme : allow more font types. */
+
+ if (ofont->FontType == ft_composite)
+ return_error(gs_error_unregistered); /* Must not happen. */
+ code = glyph_orig_matrix((const gs_font *)font, glyph, &scale_c);
+ if (code < 0)
+ return code;
+ code = glyph_orig_matrix(ofont, glyph, &scale_o);
+ if (code < 0)
+ return code;
+ gs_matrix_scale(&scale_c, 1000.0, 1000.0, &scale_c);
+ gs_matrix_scale(&scale_o, 1000.0, 1000.0, &scale_o);
+ pwidths->Width.v.x = pwidths->Width.v.y = 0;
+ pwidths->real_width.v.x = pwidths->real_width.v.y = 0;
+ pwidths->replaced_v = false;
+ if (glyph == GS_NO_GLYPH)
+ return get_missing_width(font, wmode, &scale_c, pwidths);
+ code = font->procs.glyph_info((gs_font *)font, glyph, NULL,
+ GLYPH_INFO_WIDTH0 |
+ (GLYPH_INFO_WIDTH0 << wmode) |
+ GLYPH_INFO_OUTLINE_WIDTHS |
+ (GLYPH_INFO_VVECTOR0 << wmode),
+ &info);
+ /* For CID fonts the PDF spec requires the x-component of v-vector
+ to be equal to half glyph width, and AR5 takes it from W, DW.
+ So make a compatibe data here.
+ */
+ if (font->FontType != ft_PCL_user_defined && font->FontType != ft_GL2_stick_user_defined &&
+ font->FontType != ft_GL2_531
+ && (code == gs_error_undefined || !(info.members & (GLYPH_INFO_WIDTH0 << wmode)))) {
+ code = get_missing_width(font, wmode, &scale_c, pwidths);
+ if (code < 0)
+ v.y = 0;
+ else
+ v.y = pwidths->Width.v.y;
+ if (wmode && (ofont->FontType == ft_CID_encrypted ||
+ ofont->FontType == ft_CID_TrueType)) {
+ txt_glyph_widths_t widths1;
+
+ if (get_missing_width(font, 0, &scale_c, &widths1) < 0)
+ v.x = 0;
+ else
+ v.x = widths1.Width.w / 2;
+ } else
+ v.x = pwidths->Width.v.x;
+ } else if (code < 0)
+ return code;
+ else {
+ code = store_glyph_width(&pwidths->Width, wmode, &scale_c, &info);
+ if (code < 0)
+ return code;
+ rcode |= code;
+ if (info.members & (GLYPH_INFO_VVECTOR0 << wmode))
+ gs_distance_transform(info.v.x, info.v.y, &scale_c, &v);
+ else
+ v.x = v.y = 0;
+ if (wmode && (ofont->FontType == ft_CID_encrypted ||
+ ofont->FontType == ft_CID_TrueType)) {
+ if (info.members & (GLYPH_INFO_WIDTH0 << wmode)) {
+ gs_point xy;
+
+ gs_distance_transform(info.width[0].x, info.width[0].y, &scale_c, &xy);
+ v.x = xy.x / 2;
+ } else {
+ txt_glyph_widths_t widths1;
+
+ if (get_missing_width(font, 0, &scale_c, &widths1) < 0)
+ v.x = 0;
+ else
+ v.x = widths1.Width.w / 2;
+ }
+ }
+ }
+ pwidths->Width.v = v;
+ /* Skip only if not paralel to the axis. */
+ if (code > 0 && ofont->FontType != ft_CID_encrypted &&
+ ofont->FontType != ft_CID_TrueType)
+ pwidths->Width.xy.x = pwidths->Width.xy.y = pwidths->Width.w = 0;
+ if (cdevproc_result == NULL) {
+ info.members = 0;
+ code = ofont->procs.glyph_info(ofont, glyph, NULL,
+ (GLYPH_INFO_WIDTH0 << wmode) |
+ (GLYPH_INFO_VVECTOR0 << wmode) |
+ allow_cdevproc_callout,
+ &info);
+ /* fixme : Move this call before cfont->procs.glyph_info. */
+ if (info.members & GLYPH_INFO_CDEVPROC) {
+ if (allow_cdevproc_callout)
+ return TEXT_PROCESS_CDEVPROC;
+ else
+ return_error(gs_error_rangecheck);
+ }
+ } else {
+ info.width[0].x = cdevproc_result[0];
+ info.width[0].y = cdevproc_result[1];
+ info.width[1].x = cdevproc_result[6];
+ info.width[1].y = cdevproc_result[7];
+ info.v.x = (wmode ? cdevproc_result[8] : 0);
+ info.v.y = (wmode ? cdevproc_result[9] : 0);
+ info.members = (GLYPH_INFO_WIDTH0 << wmode) |
+ (wmode ? GLYPH_INFO_VVECTOR1 : 0);
+ code = 0;
+ }
+ if (code == gs_error_undefined || !(info.members & (GLYPH_INFO_WIDTH0 << wmode)))
+ pwidths->real_width = pwidths->Width;
+ else if (code < 0)
+ return code;
+ else {
+ if ((info.members & (GLYPH_INFO_VVECTOR0 | GLYPH_INFO_VVECTOR1)) != 0)
+ pwidths->replaced_v = true;
+ else
+ info.v.x = info.v.y = 0;
+ code = store_glyph_width(&pwidths->real_width, wmode, &scale_o, &info);
+ if (code < 0)
+ return code;
+ rcode |= code;
+ gs_distance_transform(info.v.x, info.v.y, &scale_o, &pwidths->real_width.v);
+ }
+ return rcode;
+}
+
+static void
+txt_char_widths_to_uts(gs_font *font /* may be NULL for non-Type3 */,
+ txt_glyph_widths_t *pwidths)
+{
+ if (font && (font->FontType == ft_user_defined ||
+ font->FontType == ft_PCL_user_defined ||
+ font->FontType == ft_GL2_stick_user_defined ||
+ font->FontType == ft_GL2_531)) {
+ gs_matrix *pmat = &font->orig_FontMatrix;
+
+ pwidths->Width.xy.x *= pmat->xx; /* formula simplified based on wy in glyph space == 0 */
+ pwidths->Width.xy.y = 0.0; /* WMode == 0 for PDF Type 3 fonts */
+ gs_distance_transform(pwidths->real_width.xy.x, pwidths->real_width.xy.y, pmat, &pwidths->real_width.xy);
+ } else {
+ /*
+ * For other font types:
+ * - PDF design->text space is a simple scaling by 0.001.
+ * - The Width.xy.x/y that should be zeroed-out per 5.3.3 "Text Space Details" is already 0.
+ */
+ pwidths->Width.xy.x /= 1000.0;
+ pwidths->Width.xy.y /= 1000.0;
+ pwidths->real_width.xy.x /= 1000.0;
+ pwidths->real_width.xy.y /= 1000.0;
+ }
+}
+
+/* Simple routine to update the current point by the accumulated width of the
+ * text.
+ */
+static int
+txt_shift_text_currentpoint(textw_text_enum_t *penum, gs_point *wpt)
+{
+ gs_state *pgs;
+ extern_st(st_gs_state);
+
+ if (gs_object_type(penum->dev->memory, penum->pis) != &st_gs_state) {
+ /* Probably never happens. Not sure though. */
+ return_error(gs_error_unregistered);
+ }
+ pgs = (gs_state *)penum->pis;
+ return gs_moveto_aux(penum->pis, gx_current_path(pgs),
+ fixed2float(penum->origin.x) + wpt->x,
+ fixed2float(penum->origin.y) + wpt->y);
+}
+
+/* Try to convert glyph names/character codes to Unicode. We first try to see
+ * if we have any Unicode information either from a ToUnicode CMap or GlyphNames2Unicode
+ * table. If that fails we look at the glyph name to see if it starts 'uni'
+ * in which case we assume hte remainder of the name is the Unicode value. If
+ * that fails we currently just return the character code. We could go further
+ * and see if the glyph name is one we recognise (eg /A etc) and convert that.
+ * For CIDFonts we might be able to look at the Regiostry and Ordering and
+ * perhaps convert the CID into Unicode frmo that information. These are
+ * future enhancements for now.
+ */
+static int get_unicode(gs_font *font, gs_glyph glyph, gs_char ch, unsigned short *Buffer)
+{
+ gs_char unicode;
+ int code;
+ gs_const_string gnstr;
+ unsigned short fallback = ch;
+
+ unicode = font->procs.decode_glyph((gs_font *)font, glyph, ch);
+ if (unicode == GS_NO_CHAR) {
+ code = font->procs.glyph_name(font, glyph, &gnstr);
+ if (code >= 0 && gnstr.size == 7) {
+ if (!memcmp(gnstr.data, "uni", 3)) {
+ static const char *hexdigits = "0123456789ABCDEF";
+ char *d0 = strchr(hexdigits, gnstr.data[3]);
+ char *d1 = strchr(hexdigits, gnstr.data[4]);
+ char *d2 = strchr(hexdigits, gnstr.data[5]);
+ char *d3 = strchr(hexdigits, gnstr.data[6]);
+
+ if (d0 != NULL && d1 != NULL && d2 != NULL && d3 != NULL)
+ unicode = ((d0 - hexdigits) << 12) + ((d1 - hexdigits) << 8) +
+ ((d2 - hexdigits) << 4 ) + (d3 - hexdigits);
+ }
+ }
+ if (unicode == GS_NO_CHAR) {
+ single_glyph_list_t *sentry = (single_glyph_list_t *)&SingleGlyphList;
+ double_glyph_list_t *dentry = (double_glyph_list_t *)&DoubleGlyphList;
+ treble_glyph_list_t *tentry = (treble_glyph_list_t *)&TrebleGlyphList;
+ quad_glyph_list_t *qentry = (quad_glyph_list_t *)&QuadGlyphList;
+ int index = -1;
+
+ /* Search glyph to single Unicode value table */
+ while (index >= 0 && sentry->Glyph != 0) {
+ if (sentry->Glyph[0] < gnstr.data[0]) {
+ sentry++;
+ continue;
+ }
+ if (sentry->Glyph[0] > gnstr.data[0]){
+ break;
+ }
+ if (strlen(sentry->Glyph) == gnstr.size) {
+ if(memcmp(gnstr.data, sentry->Glyph, gnstr.size) == 0) {
+ index = 0;
+ break;
+ }
+ }
+ sentry++;
+ }
+ if (index != -1) {
+ *Buffer = sentry->Unicode;
+ return 1;
+ }
+ /* Search glyph to double Unicode value table */
+ while (index >= 0 && dentry->Glyph != 0) {
+ if (dentry->Glyph[0] < gnstr.data[0]) {
+ dentry++;
+ continue;
+ }
+ if (dentry->Glyph[0] > gnstr.data[0]){
+ break;
+ }
+ if (strlen(dentry->Glyph) == gnstr.size) {
+ if(memcmp(gnstr.data, dentry->Glyph, gnstr.size) == 0) {
+ index = 0;
+ break;
+ }
+ }
+ dentry++;
+ }
+ if (index != -1) {
+ memcpy(Buffer, dentry->Unicode, 2);
+ return 2;
+ }
+
+ /* Search glyph to triple Unicode value table */
+ while (index >= 0 && tentry->Glyph != 0) {
+ if (tentry->Glyph[0] < gnstr.data[0]) {
+ tentry++;
+ continue;
+ }
+ if (tentry->Glyph[0] > gnstr.data[0]){
+ break;
+ }
+ if (strlen(tentry->Glyph) == gnstr.size) {
+ if(memcmp(gnstr.data, tentry->Glyph, gnstr.size) == 0) {
+ index = 0;
+ break;
+ }
+ }
+ tentry++;
+ }
+ if (index != -1) {
+ memcpy(Buffer, tentry->Unicode, 3);
+ return 3;
+ }
+
+ /* Search glyph to quadruple Unicode value table */
+ while (index >= 0 && qentry->Glyph != 0) {
+ if (qentry->Glyph[0] < gnstr.data[0]) {
+ qentry++;
+ continue;
+ }
+ if (qentry->Glyph[0] > gnstr.data[0]){
+ break;
+ }
+ if (strlen(qentry->Glyph) == gnstr.size) {
+ if(memcmp(gnstr.data, qentry->Glyph, gnstr.size) == 0) {
+ index = 0;
+ break;
+ }
+ }
+ qentry++;
+ }
+ if (index != -1) {
+ memcpy(Buffer, qentry->Unicode, 4);
+ return 4;
+ }
+ }
+ *Buffer = fallback;
+ return 1;
+ }
+ *Buffer = (unsigned short)unicode;
+ return 1;
+}
+
+/* Routines to enumerate each glyph/character code in turn, find its width
+ * so that we can update the current point and find the end of the text, convert
+ * to Unicode if at all possible, and store some state such as the font, colour
+ * text rendering mode, writing mode, etc.
+ */
+
+static int
+txtwrite_process_cmap_text(gs_text_enum_t *pte)
+{
+ textw_text_enum_t *const penum = (textw_text_enum_t *)pte;
+ unsigned int rcode = 0;
+ gs_text_enum_t scan = *(gs_text_enum_t *)pte;
+
+ /* Composite font using a CMap */
+ for ( ; ; ) {
+ gs_glyph glyph;
+ int font_code, code;
+ gs_font *subfont;
+ gs_char chr;
+ txt_glyph_widths_t widths;
+ gs_matrix m3;
+ gs_point wanted; /* user space */
+ gs_point dpt = {0,0};
+
+ font_code = scan.orig_font->procs.next_char_glyph
+ (&scan, &chr, &glyph);
+
+ subfont = scan.fstack.items[scan.fstack.depth].font;
+
+ switch (font_code) {
+ case 0: /* no font change */
+ case 1: /* font change */
+ code = txt_glyph_widths(subfont, scan.orig_font->WMode, glyph, (gs_font *)subfont, &widths,
+ penum->cdevproc_callout ? penum->cdevproc_result : NULL);
+ if (code == TEXT_PROCESS_CDEVPROC) {
+ penum->cdevproc_callout = true;
+ pte->returned.current_glyph = glyph;
+ scan.returned.current_glyph = glyph;
+ pte->current_font = subfont;
+ scan.current_font = subfont;
+ rcode = TEXT_PROCESS_CDEVPROC;
+ break;
+ }
+ else {
+ penum->cdevproc_callout = false;
+ pte->index = scan.index;
+ }
+ code = gs_matrix_multiply(&subfont->FontMatrix, &pte->orig_font->FontMatrix, &m3);
+ code = txt_update_text_state(penum->text_state, (textw_text_enum_t *)pte, pte->orig_font, &m3);
+ txt_char_widths_to_uts(pte->orig_font, &widths); /* convert design->text space */
+ gs_distance_transform(widths.real_width.xy.x * penum->text_state->size,
+ widths.real_width.xy.y * penum->text_state->size,
+ &penum->text_state->matrix, &wanted);
+ pte->returned.total_width.x += wanted.x;
+ pte->returned.total_width.y += wanted.y;
+ penum->Widths[pte->index - 1] = wanted.x;
+
+ if (pte->text.operation & TEXT_ADD_TO_ALL_WIDTHS) {
+ gs_point tpt;
+
+ gs_distance_transform(pte->text.delta_all.x, pte->text.delta_all.y,
+ &ctm_only(pte->pis), &tpt);
+ dpt.x += tpt.x;
+ dpt.y += tpt.y;
+ }
+ if (pte->text.operation & TEXT_ADD_TO_SPACE_WIDTH && chr == pte->text.space.s_char) {
+ gs_point tpt;
+
+ gs_distance_transform(pte->text.delta_space.x, pte->text.delta_space.y,
+ &ctm_only(pte->pis), &tpt);
+ dpt.x += tpt.x;
+ dpt.y += tpt.y;
+ }
+ pte->returned.total_width.x += dpt.x;
+ pte->returned.total_width.y += dpt.y;
+
+ penum->TextBufferIndex += get_unicode((gs_font *)pte->orig_font, glyph, chr, &penum->TextBuffer[penum->TextBufferIndex]);
+ penum->Widths[pte->index - 1] += dpt.x;
+ break;
+ case 2: /* end of string */
+ return 0;
+ default: /* error */
+ return font_code;
+ }
+ if (rcode || pte->index >= pte->text.size)
+ break;
+ }
+ return rcode;
+}
+
+static int
+txtwrite_process_plain_text(gs_text_enum_t *pte)
+{
+ /* one byte regular font */
+ textw_text_enum_t *const penum = (textw_text_enum_t *)pte;
+ gs_font *font = pte->orig_font;
+ const gs_glyph *gdata = NULL;
+ gs_glyph glyph;
+ gs_char ch = 0;
+ int i, code;
+ uint operation = pte->text.operation;
+ txt_glyph_widths_t widths;
+ gs_point wanted; /* user space */
+ gs_point dpt = {0,0};
+
+ for (i=0;i<pte->text.size;i++) {
+ if (operation & (TEXT_FROM_STRING | TEXT_FROM_BYTES)) {
+ ch = pte->text.data.bytes[pte->index++];
+ } else if (operation & (TEXT_FROM_CHARS | TEXT_FROM_SINGLE_CHAR)) {
+ ch = pte->text.data.chars[pte->index++];
+ } else if (operation & (TEXT_FROM_GLYPHS | TEXT_FROM_SINGLE_GLYPH)) {
+ if (operation & TEXT_FROM_GLYPHS) {
+ gdata = pte->text.data.glyphs + (pte->index++ * sizeof (gs_glyph));
+ } else {
+ gdata = &pte->text.data.d_glyph;
+ }
+ }
+ glyph = (gdata == NULL ? pte->orig_font->procs.encode_char(pte->orig_font, ch, GLYPH_SPACE_NAME)
+ : *gdata);
+
+ code = txt_glyph_widths(font, font->WMode, glyph, (gs_font *)font, &widths, NULL);
+ if (code < 0)
+ return code;
+
+ penum->cdevproc_callout = false;
+ code = txt_update_text_state(penum->text_state, (textw_text_enum_t *)pte, pte->orig_font, &font->FontMatrix);
+ txt_char_widths_to_uts(pte->orig_font, &widths); /* convert design->text space */
+ gs_distance_transform(widths.real_width.xy.x * penum->text_state->size,
+ widths.real_width.xy.y * penum->text_state->size,
+ &penum->text_state->matrix, &wanted);
+ pte->returned.total_width.x += wanted.x;
+ pte->returned.total_width.y += wanted.y;
+ penum->Widths[pte->index - 1] = wanted.x;
+
+ if (pte->text.operation & TEXT_ADD_TO_ALL_WIDTHS) {
+ gs_point tpt;
+
+ gs_distance_transform(pte->text.delta_all.x, pte->text.delta_all.y,
+ &ctm_only(pte->pis), &tpt);
+ dpt.x += tpt.x;
+ dpt.y += tpt.y;
+ }
+ if (pte->text.operation & TEXT_ADD_TO_SPACE_WIDTH && ch == pte->text.space.s_char) {
+ gs_point tpt;
+
+ gs_distance_transform(pte->text.delta_space.x, pte->text.delta_space.y,
+ &ctm_only(pte->pis), &tpt);
+ dpt.x += tpt.x;
+ dpt.y += tpt.y;
+ }
+ pte->returned.total_width.x += dpt.x;
+ pte->returned.total_width.y += dpt.y;
+
+ penum->TextBufferIndex += get_unicode((gs_font *)pte->orig_font, glyph, ch, &penum->TextBuffer[penum->TextBufferIndex]);
+ penum->Widths[pte->index - 1] += dpt.x;
+ }
+ return 0;
+}
+
+/* Routine to add the accumulated text, and its recorded properties to our
+ * lists. We maintain a list of text on a per-page basis. Each fragment is
+ * sorted by Y co-ordinate, then by X co-ordinate, and stored that way.
+ * Eventually we will want to merge 'adjacent' fragments with the same
+ * properties, at least when outputting a simple representation. We won't
+ * do this for languages which don't read left/right or right/left though.
+ */
+static int
+txt_add_sorted_fragment(gx_device_txtwrite_t *tdev, textw_text_enum_t *penum)
+{
+ if (!tdev->PageData.y_ordered_list) {
+ /* first entry, no need to sort, just store it */
+ tdev->PageData.y_ordered_list = (page_text_list_t *)gs_malloc(tdev->memory->stable_memory, 1,
+ sizeof(page_text_list_t), "txtwrite alloc Y list entry");
+ if (!tdev->PageData.y_ordered_list)
+ return gs_note_error(gs_error_VMerror);
+ memset(tdev->PageData.y_ordered_list, 0x00, sizeof(page_text_list_t));
+ tdev->PageData.y_ordered_list->x_ordered_list = penum->text_state;
+ tdev->PageData.y_ordered_list->next = tdev->PageData.y_ordered_list->previous = NULL;
+ tdev->PageData.y_ordered_list->start = penum->text_state->start;
+ } else {
+ page_text_list_t *Y_List = tdev->PageData.y_ordered_list;
+
+ while (Y_List->next && Y_List->start.y < penum->text_state->start.y)
+ Y_List = Y_List->next;
+
+ if (Y_List->start.y == penum->text_state->start.y) {
+ /* Already have text at this y-position */
+ text_list_entry_t *X_List = Y_List->x_ordered_list;
+
+ while (X_List->next && X_List->start.x < penum->text_state->start.x)
+ X_List = X_List->next;
+
+ if (X_List->start.x > penum->text_state->start.x) {
+ /* Insert before */
+ penum->text_state->next = X_List;
+ penum->text_state->previous = X_List->previous;
+ X_List->previous = penum->text_state;
+ if (!penum->text_state->previous)
+ /* New head of list */
+ Y_List->x_ordered_list = penum->text_state;
+ else
+ penum->text_state->previous->next = penum->text_state;
+ } else {
+ /* Insert after */
+ penum->text_state->previous = X_List;
+ penum->text_state->next = X_List->next;
+ X_List->next = penum->text_state;
+ if (penum->text_state->next)
+ penum->text_state->next->previous = penum->text_state;
+ }
+ if (penum->text_state->FontBBox_bottomleft.y < Y_List->MinY)
+ Y_List->MinY = penum->text_state->FontBBox_bottomleft.y;
+ if (penum->text_state->FontBBox_bottomleft.y > Y_List->MaxY)
+ Y_List->MaxY = penum->text_state->FontBBox_bottomleft.y;
+ if (penum->text_state->FontBBox_topright.y < Y_List->MinY)
+ Y_List->MinY = penum->text_state->FontBBox_topright.y;
+ if (penum->text_state->FontBBox_topright.y > Y_List->MaxY)
+ Y_List->MaxY = penum->text_state->FontBBox_topright.y;
+ } else {
+ /* New y-position, make a Y list new record */
+ page_text_list_t *Y_Entry = (page_text_list_t *)gs_malloc(tdev->memory->stable_memory, 1,
+ sizeof(page_text_list_t), "txtwrite alloc Y-list");
+ if (!Y_Entry)
+ return gs_note_error(gs_error_VMerror);
+
+ Y_Entry->x_ordered_list = penum->text_state;
+ Y_Entry->start = penum->text_state->start;
+ if (penum->text_state->FontBBox_bottomleft.y > penum->text_state->FontBBox_topright.y) {
+ Y_Entry->MinY = penum->text_state->FontBBox_topright.y;
+ Y_Entry->MaxY = penum->text_state->FontBBox_bottomleft.y;
+ } else {
+ Y_Entry->MaxY = penum->text_state->FontBBox_topright.y;
+ Y_Entry->MinY = penum->text_state->FontBBox_bottomleft.y;
+ }
+
+ if (Y_List->start.y > penum->text_state->start.y) {
+ /* Insert before */
+ Y_Entry->next = Y_List;
+ Y_Entry->previous = Y_List->previous;
+ Y_List->previous = Y_Entry;
+ if (!Y_Entry->previous)
+ /* New head of list */
+ tdev->PageData.y_ordered_list = Y_Entry;
+ else
+ ((page_text_list_t *)Y_Entry->previous)->next = Y_Entry;
+ } else {
+ /* Insert after */
+ Y_Entry->next = Y_List->next;
+ Y_Entry->previous = Y_List;
+ Y_List->next = Y_Entry;
+ if (Y_Entry->next)
+ ((page_text_list_t *)(Y_Entry->next))->previous = Y_Entry;
+ }
+ }
+ }
+ penum->text_state = NULL;
+ return 0;
+}
+
+static int
+txt_add_fragment(gx_device_txtwrite_t *tdev, textw_text_enum_t *penum)
+{
+ text_list_entry_t *unsorted_entry, *t;
+
+ /* Create a duplicate entry for the unsorted list */
+ unsorted_entry = (text_list_entry_t *)gs_malloc(tdev->memory->stable_memory, 1,
+ sizeof(text_list_entry_t), "txtwrite alloc sorted text state");
+ if (!unsorted_entry)
+ return gs_note_error(gs_error_VMerror);
+
+ /* Calculate the start and end points of the text */
+ penum->text_state->start.x = fixed2float(penum->origin.x);
+ penum->text_state->start.y = fixed2float(penum->origin.y);
+ penum->text_state->end.x = penum->text_state->start.x + penum->returned.total_width.x;
+ penum->text_state->end.y = penum->text_state->start.y + penum->returned.total_width.y;
+ penum->text_state->Unicode_Text_Size = penum->TextBufferIndex;
+
+ *unsorted_entry = *(penum->text_state);
+
+ /* Update the saved text state with the acccumulated Unicode data */
+ /* The working buffer (penum->TextBuffer) is freed in the text_release method */
+ penum->text_state->Unicode_Text = (unsigned short *)gs_malloc(tdev->memory->stable_memory,
+ penum->TextBufferIndex, sizeof(unsigned short), "txtwrite alloc text buffer");
+ if (!penum->text_state->Unicode_Text)
+ return gs_note_error(gs_error_VMerror);
+ memcpy(penum->text_state->Unicode_Text, penum->TextBuffer, penum->TextBufferIndex * sizeof(unsigned short));
+
+ penum->text_state->Widths = (float *)gs_malloc(tdev->memory->stable_memory,
+ penum->TextBufferIndex, sizeof(float), "txtwrite alloc widths array");
+ if (!penum->text_state->Widths)
+ return gs_note_error(gs_error_VMerror);
+ memcpy(penum->text_state->Widths, penum->Widths, penum->TextBufferIndex * sizeof(float));
+
+ unsorted_entry->Unicode_Text = (unsigned short *)gs_malloc(tdev->memory->stable_memory,
+ penum->TextBufferIndex, sizeof(unsigned short), "txtwrite alloc sorted text buffer");
+ if (!unsorted_entry->Unicode_Text)
+ return gs_note_error(gs_error_VMerror);
+ memcpy(unsorted_entry->Unicode_Text, penum->TextBuffer, penum->TextBufferIndex * sizeof(unsigned short));
+
+ unsorted_entry->Widths = (float *)gs_malloc(tdev->memory->stable_memory,
+ penum->TextBufferIndex, sizeof(float), "txtwrite alloc widths array");
+ if (!unsorted_entry->Widths)
+ return gs_note_error(gs_error_VMerror);
+ memcpy(unsorted_entry->Widths, penum->Widths, penum->TextBufferIndex * sizeof(float));
+
+ unsorted_entry->FontName = (char *)gs_malloc(tdev->memory->stable_memory,
+ (strlen(penum->text_state->FontName) + 1), sizeof(unsigned char), "txtwrite alloc sorted text buffer");
+ if (!unsorted_entry->FontName)
+ return gs_note_error(gs_error_VMerror);
+ memcpy(unsorted_entry->FontName, penum->text_state->FontName, strlen(penum->text_state->FontName) * sizeof(unsigned char));
+ unsorted_entry->FontName[strlen(penum->text_state->FontName)] = 0x00;
+
+ /* First add one entry to the unsorted list */
+ if (!tdev->PageData.unsorted_text_list) {
+ tdev->PageData.unsorted_text_list = unsorted_entry;
+ unsorted_entry->next = unsorted_entry->previous = NULL;
+ } else {
+ t = tdev->PageData.unsorted_text_list;
+ while (t->next)
+ t = t->next;
+ t->next = unsorted_entry;
+ unsorted_entry->next = NULL;
+ unsorted_entry->previous = t;
+ }
+
+ /* Then add the other entry to the sorted list */
+ return txt_add_sorted_fragment(tdev, penum);
+}
+
+/* This routine selects whether the text needs to be handled as regular glyphs
+ * and character codes, or as CIDs, depending on the font type. This is required
+ * because there are ways that regular text can be handled that aren't possible
+ * with CIDFonts.
+ */
+static int
+textw_text_process(gs_text_enum_t *pte)
+{
+ textw_text_enum_t *const penum = (textw_text_enum_t *)pte;
+ gx_device_txtwrite_t *const tdev = (gx_device_txtwrite_t *) pte->dev;
+ gs_font *font = pte->orig_font;
+ gs_font_base *font_base = (gs_font_base *)pte->current_font;
+ int code = 0;
+
+ if (!penum->TextBuffer) {
+ /* We can get up to 4 Unicode points per glyph, and a glyph can be
+ * be represented by as little as one byte. So we make a very large
+ * temporary buffer to hold the Unicode string as we assemble it. When
+ * we copy it to the text fragment we will allocate only as many bytes
+ * as are required to hold the actual nukmber of Unicode values we
+ * decoded, and this temporary buffer will be discarded.
+ */
+ penum->TextBuffer = (unsigned short *)gs_malloc(tdev->memory->stable_memory,
+ pte->text.size * 4, sizeof(unsigned short), "txtwrite temporary text buffer");
+ if (!penum->TextBuffer)
+ return gs_note_error(gs_error_VMerror);
+ penum->Widths = (float *)gs_malloc(tdev->memory->stable_memory,
+ pte->text.size, sizeof(float), "txtwrite temporary widths array");
+ if (!penum->Widths)
+ return gs_note_error(gs_error_VMerror);
+ }
+ {
+ switch (font->FontType) {
+ case ft_CID_encrypted:
+ case ft_CID_TrueType:
+ case ft_composite:
+ code = txtwrite_process_cmap_text(pte);
+ break;
+ case ft_encrypted:
+ case ft_encrypted2:
+ case ft_TrueType:
+ case ft_user_defined:
+ case ft_PCL_user_defined:
+ case ft_GL2_stick_user_defined:
+ case ft_GL2_531:
+ code = txtwrite_process_plain_text(pte);
+ break;
+ default:
+ return gs_error_rangecheck;
+ break;
+ }
+ if (code == 0) {
+ if (font_base->FontBBox.p.x != font_base->FontBBox.q.x ||
+ font_base->FontBBox.p.y != font_base->FontBBox.q.y) {
+ gs_point p0, p1, p2, p3;
+ gs_matrix m;
+
+ m = ctm_only(pte->pis);
+ m.tx = m.ty = fixed2float(0);
+ gs_matrix_multiply(&font_base->FontMatrix, &m, &m);
+ gs_point_transform(font_base->FontBBox.p.x, font_base->FontBBox.p.y, &m, &p0);
+ gs_point_transform(font_base->FontBBox.p.x, font_base->FontBBox.q.y, &m, &p1);
+ gs_point_transform(font_base->FontBBox.q.x, font_base->FontBBox.p.y, &m, &p2);
+ gs_point_transform(font_base->FontBBox.q.x, font_base->FontBBox.q.y, &m, &p3);
+ penum->text_state->FontBBox_bottomleft.x = min(min(p0.x, p1.x), min(p1.x, p2.x));
+ penum->text_state->FontBBox_topright.x = max(max(p0.x, p1.x), max(p1.x, p2.x));
+ penum->text_state->FontBBox_bottomleft.y = min(min(p0.y, p1.y), min(p1.y, p2.y));
+ penum->text_state->FontBBox_topright.y = max(max(p0.y, p1.y), max(p1.y, p2.y));
+ }
+ code = txt_shift_text_currentpoint(penum, &penum->returned.total_width);
+ if (code != 0)
+ return code;
+
+ code = txt_add_fragment(tdev, penum);
+ }
+ }
+ return code;
+}
+
+/* Begin processing text. */
+
+/* Define the auxiliary procedures for text processing. */
+static int
+textw_text_resync(gs_text_enum_t *pte, const gs_text_enum_t *pfrom)
+{
+ return gs_text_resync(pte, pfrom);
+}
+static bool
+textw_text_is_width_only(const gs_text_enum_t *pte)
+{
+ return false;
+}
+static int
+textw_text_current_width(const gs_text_enum_t *pte, gs_point *pwidth)
+{
+ return gs_text_current_width(pte, pwidth);
+}
+static int
+textw_text_set_cache(gs_text_enum_t *pte, const double *pw,
+ gs_text_cache_control_t control)
+{
+ textw_text_enum_t *const penum = (textw_text_enum_t *)pte;
+
+ switch (control) {
+ case TEXT_SET_CHAR_WIDTH:
+ case TEXT_SET_CACHE_DEVICE:
+ return gs_text_set_cache(pte, pw, control);
+ case TEXT_SET_CACHE_DEVICE2:
+ if (penum->cdevproc_callout) {
+ memcpy(penum->cdevproc_result, pw, sizeof(penum->cdevproc_result));
+ return 0;
+ }
+ return gs_text_set_cache(pte, pw, control);
+ default:
+ return_error(gs_error_rangecheck);
+ }
+ return 0;
+}
+
+static int
+textw_text_retry(gs_text_enum_t *pte)
+{
+ return gs_text_retry(pte);
+}
+static void
+textw_text_release(gs_text_enum_t *pte, client_name_t cname)
+{
+ textw_text_enum_t *const penum = (textw_text_enum_t *)pte;
+ gx_device_txtwrite_t *const tdev = (gx_device_txtwrite_t *) pte->dev;
+
+ /* Free the working buffer where the Unicode was assembled from the enumerated text */
+ if (penum->TextBuffer)
+ gs_free(tdev->memory, penum->TextBuffer, 1, penum->TextBufferIndex, "txtwrite free temporary text buffer");
+ if (penum->Widths)
+ gs_free(tdev->memory, penum->Widths, sizeof(float), pte->text.size, "txtwrite free temporary widths array");
+ /* If this is copied away when we complete the text enumeration succesfully, then
+ * we set the pointer to NULL, if we get here with it non-NULL , then there was
+ * an error.
+ */
+ if (penum->text_state)
+ gs_free(tdev->memory, penum->text_state, 1, sizeof(penum->text_state), "txtwrite free text state");
+
+ gs_text_release(pte, cname);
+}
+
+/* This is the list of methods for the text enumerator */
+static const gs_text_enum_procs_t textw_text_procs = {
+ textw_text_resync, textw_text_process,
+ textw_text_is_width_only, textw_text_current_width,
+ textw_text_set_cache, textw_text_retry,
+ textw_text_release
+};
+
+/* This device method gets called by the interpreter to deal with text. It
+ * must create a text enumerator, which contains the methods for dealing with
+ * the text itself. The interpreter will use the enumerator methods to deal with
+ * the text, it won't refer to the device methods again for this text.
+ */
+static int
+txtwrite_text_begin(gx_device * dev, gs_imager_state * pis,
+ const gs_text_params_t * text, gs_font * font,
+ gx_path * path, const gx_device_color * pdcolor,
+ const gx_clip_path * pcpath,
+ gs_memory_t * mem, gs_text_enum_t ** ppenum)
+{
+ gx_device_txtwrite_t *const tdev = (gx_device_txtwrite_t *) dev;
+ textw_text_enum_t *penum;
+ int code;
+
+ /* If this is a stringwidth, we must let the default graphics library code handle it
+ * in case there is no current point (this can happen if this is the first operation
+ * we get, the current font is a CIDFont, and its descendant is a substitute type 1
+ * font). If there is no current point we throw an error in text_process and that
+ * messes up all the font handling. The test below is copied from pdfwrite
+ * (gdev_pdf_text_begin) and seems to do the job.
+ */
+ if ((!(text->operation & TEXT_DO_DRAW) && pis->text_rendering_mode != 3)
+ || path == 0 || !path_position_valid(path))
+ return gx_default_text_begin(dev, pis, text, font, path, pdcolor,
+ pcpath, mem, ppenum);
+ /* Allocate and initialize one of our text enumerators. */
+ rc_alloc_struct_1(penum, textw_text_enum_t, &st_textw_text_enum, mem,
+ return_error(gs_error_VMerror), "gdev_textw_text_begin");
+ penum->rc.free = rc_free_text_enum;
+ penum->charproc_accum = false;
+ penum->cdevproc_callout = false;
+ penum->returned.total_width.x = penum->returned.total_width.y = 0;
+ penum->TextBuffer = NULL;
+ penum->TextBufferIndex = 0;
+ /* The enumerator's text_release method frees this memory */
+ penum->text_state = (text_list_entry_t *)gs_malloc(tdev->memory->stable_memory, 1,
+ sizeof(text_list_entry_t), "txtwrite alloc text state");
+ if (!penum->text_state)
+ return gs_note_error(gs_error_VMerror);
+ memset(penum->text_state, 0x00, sizeof(text_list_entry_t));
+
+ code = gs_text_enum_init((gs_text_enum_t *)penum, &textw_text_procs,
+ dev, pis, text, font, path, pdcolor, pcpath, mem);
+ if (code < 0) {
+ /* Belt and braces; I'm not certain this is required, but its safe */
+ gs_free(tdev->memory, penum->text_state, 1, sizeof(text_list_entry_t), "txtwrite free text state");
+ penum->text_state = NULL;
+ gs_free_object(mem, penum, "textwrite_text_begin");
+ return code;
+ }
+
+ code = gx_path_current_point(penum->path, &penum->origin);
+ if (code != 0)
+ return code;
+
+ *ppenum = (gs_text_enum_t *)penum;
+
+ return 0;
+}
+
+static int
+txtwrite_strip_copy_rop(gx_device * dev,
+ const byte * sdata, int sourcex, uint sraster,
+ gx_bitmap_id id,
+ const gx_color_index * scolors,
+ const gx_strip_bitmap * textures,
+ const gx_color_index * tcolors,
+ int x, int y, int w, int h,
+ int phase_x, int phase_y, gs_logical_operation_t lop)
+{
+ return 0;
+}
+
+int
+txtwrite_dev_spec_op(gx_device *pdev, int dev_spec_op, void *data, int size)
+{
+ switch (dev_spec_op) {
+ case gxdso_get_dev_param:
+ {
+ int code;
+ dev_param_req_t *request = (dev_param_req_t *)data;
+ code = txtwrite_get_param(pdev, request->Param, request->list);
+ if (code != gs_error_undefined)
+ return code;
+ }
+ }
+ return gx_default_dev_spec_op(pdev, dev_spec_op, data, size);
+}
diff --git a/devices/vector/gdevxps.c b/devices/vector/gdevxps.c
new file mode 100644
index 000000000..9994e0163
--- /dev/null
+++ b/devices/vector/gdevxps.c
@@ -0,0 +1,2481 @@
+/* Copyright (C) 2001-2014 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+/* XPS output device */
+#include "string_.h"
+#include "stdio_.h"
+#include "gx.h"
+#include "gserrors.h"
+#include "gdevvec.h"
+#include "gxpath.h"
+#include "gzcpath.h"
+#include "stream.h"
+#include "zlib.h"
+#include "stdint_.h"
+#include "gdevtifs.h"
+#include "gsicc_create.h"
+#include "gsicc_cache.h"
+#include "gximdecode.h" /* Need so that we can unpack and decode */
+
+#define MAXPRINTERNAME 64
+#if defined(__WIN32__) && XPSPRINT==1
+int XPSPrint(char *FileName, char *PrinterName, int *reason);
+#endif
+
+#define MAXNAME 64
+#define PROFILEPATH "Documents/1/Resources/Profiles/"
+#define IMAGEPATH "Documents/1/Resources/Images/"
+
+/* default resolution. */
+#ifndef X_DPI
+# define X_DPI 96
+#endif
+#ifndef Y_DPI
+# define Y_DPI 96
+#endif
+
+/* default constants */
+#define XPS_DEFAULT_LINEWIDTH 1.0
+#define XPS_DEFAULT_LINECAP gs_cap_butt
+#define XPS_DEFAULT_LINEJOIN gs_join_miter
+#define XPS_DEFAULT_MITERLIMIT 4.0
+
+/* ---------------- Device definition ---------------- */
+
+/* TODO graphics state definitions are not yet used */
+
+/* xps join and cap values. */
+static const char *join_str[] = {"Miter", "Bevel", "Round"};
+static const char *cap_str[] = {"Flat", "Round", "Square", "Triangle"};
+
+typedef enum {XPS_JOIN, XPS_START_CAP, XPS_END_CAP, XPS_THICK, XPS_MITER_LIMIT,
+ XPS_STROKE_COLOR, XPS_FILL_COLOR} xps_attr_t;
+
+typedef struct gx_device_xps_vals {
+ double f;
+ uint64_t i;
+} val_t;
+
+/* current and default values of state atrributes */
+typedef struct gx_device_xps_attr_state {
+ val_t cur;
+ val_t def;
+ const char *fmt;
+ const char **vals;
+} attr_t;
+
+/* xps proto state - a copy of this table is made on the heap when the
+ device is opened and the current state values are filled in at run
+ time. NB not currently used. */
+attr_t xps_fixed_state[] = {
+ /* attribute current default Format String values */
+ {/* XPS_JOIN */ {0.0, 0}, {0.0, 0}, "StrokeLineJoin=\"%s\"", join_str},
+ {/* XPS_START_CAP */ {0.0, 0}, {0.0, 0}, "StrokeStartLineCap=\"%s\"", cap_str},
+ {/* XPS_END_CAP */ {0.0, 0}, {0.0, 0}, "StrokeEndLineCap=\"%s\"", cap_str},
+ {/* XPS_THICK */ {0.0, 0}, {1.0, 0}, "StrokeThickness=\"%f\"", NULL},
+ {/* XPS_MITER_LIMIT */ {0.0, 0}, {10.0, 0}, "StrokeMiterLimit=\"%f\"", NULL},
+ {/* XPS_STROKE_COLOR */ {0.0, 0}, {0.0, 0}, "Stroke=\"%X\"", NULL},
+ {/* XPS_FILL_COLOR */ {0.0, 0}, {0.0, 0}, "Fill=\"%X\"", NULL}
+};
+
+/* zip file management - a list of temporary files is maintained along
+ with the final filename to be used in the archive. filenames with
+ associated contents in temporary files are maintained until the end
+ of the job, at which time we enumerate the list and write the zip
+ archive. The exception to this is the image data and icc profiles. Image
+ data are stored in temp files and upon end image transferred to the zip
+ archive and the temp file closed (and removed). ICC profiles are written
+ directly to the archive without a temp file. */
+
+typedef struct gx_device_xps_zdata_s {
+ FILE *fp;
+ ulong count;
+} gx_device_xps_zdata_t;
+
+/* Zip info for each file in the zip archive */
+typedef struct gx_device_xps_zinfo_s {
+ ulong CRC;
+ ulong file_size;
+ gx_device_xps_zdata_t data;
+ long current_pos;
+ ushort date;
+ ushort time;
+ bool saved; /* flag to indicate file was already saved (e.g. image or profile) */
+} gx_device_xps_zinfo_t;
+
+/* a list of archive file names and their corresponding info
+ (gx_device_xps_zinfo_t) */
+typedef struct gx_device_xps_f2i_s {
+ char *filename;
+ gx_device_xps_zinfo_t *info;
+ struct gx_device_xps_f2i_s *next;
+} gx_device_xps_f2i_t;
+
+/* Used for keeping track of icc profiles that we have written. This way we
+ avoid writing the same one multiple times. It would be nice to do this
+ based upon the gs_id for images, but that id is really created after we
+ have already drawn the path. Not clear to me how to get a source ID. */
+typedef struct xps_icc_data_s {
+ int64_t hash;
+ int index;
+ struct xps_icc_data_s *next;
+} xps_icc_data_t;
+
+typedef enum {
+ xps_solidbrush,
+ xps_imagebrush,
+ xps_visualbrush
+} xps_brush_t;
+
+typedef struct xps_image_enum_s {
+ gdev_vector_image_enum_common;
+ gs_matrix mat;
+ TIFF *tif; /* in non-GC memory */
+ char file_name[MAXNAME];
+ char icc_name[MAXNAME];
+ image_decode_t decode_st;
+ int bytes_comp;
+ byte *buffer; /* Needed for unpacking/decoding of image data */
+ byte *devc_buffer; /* Needed for case where we are mapping to device colors */
+ gs_color_space *pcs; /* Needed for Sep, DeviceN, Indexed */
+ gsicc_link_t *icc_link; /* Needed for CIELAB */
+ const gs_imager_state *pis; /* Needed for color conversions of DeviceN etc */
+ FILE *fid;
+} xps_image_enum_t;
+
+gs_private_st_suffix_add3(st_xps_image_enum, xps_image_enum_t,
+ "xps_image_enum_t", xps_image_enum_enum_ptrs,
+ xps_image_enum_reloc_ptrs, st_vector_image_enum,
+ buffer, devc_buffer, pis);
+
+typedef struct gx_device_xps_s {
+ /* superclass state */
+ gx_device_vector_common;
+ /* zip container bookkeeping */
+ gx_device_xps_f2i_t *f2i;
+ gx_device_xps_f2i_t *f2i_tail;
+ /* local state */
+ int page_count; /* how many output_page calls we've seen */
+ int image_count; /* number of images so far */
+ int relationship_count; /* Pages relationships */
+ xps_icc_data_t *icc_data;
+ gx_color_index strokecolor, fillcolor;
+ xps_brush_t stroketype, filltype;
+ xps_image_enum_t *xps_pie;
+ double linewidth;
+ gs_line_cap linecap;
+ gs_line_join linejoin;
+ double miterlimit;
+ bool can_stroke;
+ unsigned char PrinterName[MAXPRINTERNAME];
+} gx_device_xps;
+
+gs_public_st_suffix_add1_final(st_device_xps, gx_device_xps,
+ "gx_device_xps", device_xps_enum_ptrs, device_xps_reloc_ptrs,
+ gx_device_finalize, st_device_vector, xps_pie);
+
+#define xps_device_body(dname, depth)\
+ std_device_dci_type_body(gx_device_xps, 0, dname, &st_device_xps, \
+ DEFAULT_WIDTH_10THS * X_DPI / 10, \
+ DEFAULT_HEIGHT_10THS * Y_DPI / 10, \
+ X_DPI, Y_DPI, \
+ (depth > 8 ? 3 : 1), depth, \
+ (depth > 1 ? 255 : 1), (depth > 8 ? 255 : 0), \
+ (depth > 1 ? 256 : 2), (depth > 8 ? 256 : 1))
+
+static dev_proc_open_device(xps_open_device);
+static dev_proc_output_page(xps_output_page);
+static dev_proc_close_device(xps_close_device);
+static dev_proc_get_params(xps_get_params);
+static dev_proc_put_params(xps_put_params);
+static dev_proc_fill_path(gdev_xps_fill_path);
+static dev_proc_stroke_path(gdev_xps_stroke_path);
+static dev_proc_finish_copydevice(xps_finish_copydevice);
+static dev_proc_begin_image(xps_begin_image);
+
+#define xps_device_procs \
+{ \
+ xps_open_device, \
+ NULL, /* get_initial_matrix */\
+ NULL, /* sync_output */\
+ xps_output_page,\
+ xps_close_device,\
+ gx_default_rgb_map_rgb_color,\
+ gx_default_rgb_map_color_rgb,\
+ gdev_vector_fill_rectangle,\
+ NULL, /* tile_rectangle */\
+ NULL, /* copy_mono */\
+ NULL, /* copy_color */\
+ NULL, /* draw_line */\
+ NULL, /* get_bits */\
+ xps_get_params,\
+ xps_put_params,\
+ NULL, /* map_cmyk_color */\
+ NULL, /* get_xfont_procs */\
+ NULL, /* get_xfont_device */\
+ NULL, /* map_rgb_alpha_color */\
+ gx_page_device_get_page_device,\
+ NULL, /* get_alpha_bits */\
+ NULL, /* copy_alpha */\
+ NULL, /* get_band */\
+ NULL, /* copy_rop */\
+ gdev_xps_fill_path,\
+ gdev_xps_stroke_path,\
+ NULL, /* fill_mask */\
+ NULL, /* gdev_vector_fill_trapezoid, */ \
+ NULL, /* gdev_vector_fill_parallelogram */ \
+ NULL, /* gdev_vector_fill_triangle */ \
+ NULL, /* draw_thin_line */\
+ xps_begin_image, /* begin_image */ \
+ NULL, /* image_data */\
+ NULL, /* end_image */\
+ NULL, /* strip_tile_rectangle */\
+ NULL, /* strip_copy_rop */\
+ NULL, /* get_clipping_box */\
+ NULL, /* begin_typed_image */\
+ NULL, /* get_bits_rectangle */\
+ NULL, /* map_color_rgb_alpha */\
+ NULL, /* create_compositor */\
+ NULL, /* get_hardware_params */\
+ NULL, /* text_begin */\
+ xps_finish_copydevice,\
+ NULL,\
+}
+
+const gx_device_xps gs_xpswrite_device = {
+ xps_device_body("xpswrite", 24),
+ xps_device_procs
+};
+
+/* Vector device procedures */
+static int
+xps_beginpage(gx_device_vector *vdev);
+static int
+xps_setlinewidth(gx_device_vector *vdev, double width);
+static int
+xps_setlinecap(gx_device_vector *vdev, gs_line_cap cap);
+static int
+xps_setlinejoin(gx_device_vector *vdev, gs_line_join join);
+static int
+xps_setmiterlimit(gx_device_vector *vdev, double limit);
+static int
+xps_setdash(gx_device_vector *vdev, const float *pattern,
+ uint count, double offset);
+static int
+xps_setlogop(gx_device_vector *vdev, gs_logical_operation_t lop,
+ gs_logical_operation_t diff);
+
+static int
+xps_can_handle_hl_color(gx_device_vector *vdev, const gs_imager_state *pis,
+ const gx_drawing_color * pdc);
+static int
+xps_setfillcolor(gx_device_vector *vdev, const gs_imager_state *pis,
+ const gx_drawing_color *pdc);
+static int
+xps_setstrokecolor(gx_device_vector *vdev, const gs_imager_state *pis,
+ const gx_drawing_color *pdc);
+
+static int
+xps_dorect(gx_device_vector *vdev, fixed x0, fixed y0,
+ fixed x1, fixed y1, gx_path_type_t type);
+static int
+xps_beginpath(gx_device_vector *vdev, gx_path_type_t type);
+
+static int
+xps_moveto(gx_device_vector *vdev, double x0, double y0,
+ double x, double y, gx_path_type_t type);
+static int
+xps_lineto(gx_device_vector *vdev, double x0, double y0,
+ double x, double y, gx_path_type_t type);
+static int
+xps_curveto(gx_device_vector *vdev, double x0, double y0,
+ double x1, double y1, double x2, double y2,
+ double x3, double y3, gx_path_type_t type);
+static int
+xps_closepath(gx_device_vector *vdev, double x, double y,
+ double x_start, double y_start, gx_path_type_t type);
+static int
+xps_endpath(gx_device_vector *vdev, gx_path_type_t type);
+
+/* Vector device function table */
+static const gx_device_vector_procs xps_vector_procs = {
+ xps_beginpage,
+ xps_setlinewidth,
+ xps_setlinecap,
+ xps_setlinejoin,
+ xps_setmiterlimit,
+ xps_setdash,
+ gdev_vector_setflat,
+ xps_setlogop,
+ xps_can_handle_hl_color,
+ xps_setfillcolor,
+ xps_setstrokecolor,
+ gdev_vector_dopath,
+ xps_dorect,
+ xps_beginpath,
+ xps_moveto,
+ xps_lineto,
+ xps_curveto,
+ xps_closepath,
+ xps_endpath
+};
+
+/* Various static content we use in all xps jobs. We don't use all of
+ these resource types */
+static char *xps_content_types = (char *)"<?xml version=\"1.0\" encoding=\"utf-8\"?>"
+ "<Types xmlns=\"http://schemas.openxmlformats.org/package/2006/content-types\">"
+ "<Default Extension=\"fdseq\" ContentType=\"application/vnd.ms-package.xps-fixeddocumentsequence+xml\" />"
+ "<Default Extension=\"rels\" ContentType=\"application/vnd.openxmlformats-package.relationships+xml\" />"
+ "<Default Extension=\"fdoc\" ContentType=\"application/vnd.ms-package.xps-fixeddocument+xml\" />"
+ "<Default Extension=\"fpage\" ContentType=\"application/vnd.ms-package.xps-fixedpage+xml\" />"
+ "<Default Extension=\"ttf\" ContentType=\"application/vnd.ms-opentype\" />"
+ "<Default Extension = \"icc\" ContentType = \"application/vnd.ms-color.iccprofile\" />"
+ "<Default Extension=\"tif\" ContentType=\"image/tiff\" />"
+ "<Default Extension=\"png\" ContentType=\"image/png\" /></Types>";
+
+static char *fixed_document_sequence = (char *)"<?xml version=\"1.0\" encoding=\"utf-8\"?>"
+ "<FixedDocumentSequence xmlns=\"http://schemas.microsoft.com/xps/2005/06\">"
+ "<DocumentReference Source=\"Documents/1/FixedDocument.fdoc\" />"
+ "</FixedDocumentSequence>";
+
+static char *fixed_document_fdoc_header = (char *)"<?xml version=\"1.0\" encoding=\"utf-8\"?>"
+ "<FixedDocument xmlns=\"http://schemas.microsoft.com/xps/2005/06\">";
+
+static char *rels_header = (char *)"<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
+ "<Relationships xmlns=\"http://schemas.openxmlformats.org/package/2006/relationships\">\n";
+
+static char *rels_fdseq = (char *)"<Relationship Type=\"http://schemas.microsoft.com/xps/2005/06/fixedrepresentation\" "
+ "Target=\"/FixedDocumentSequence.fdseq\" Id=\"Rdd12fb46c1de43ab\" />\n"
+ "</Relationships>\n";
+
+static char *rels_req_type = (char *) "\"http://schemas.microsoft.com/xps/2005/06/required-resource\"";
+
+ /* Procedures to manage the containing zip archive */
+
+/* Append a node of info for this archive file */
+static int
+zip_new_info_node(gx_device_xps *xps_dev, const char *filename)
+{
+ gx_device *dev = (gx_device *)xps_dev;
+ gs_memory_t *mem = dev->memory;
+ int lenstr;
+
+ /* NB should use GC */
+ gx_device_xps_zinfo_t *info =
+ (gx_device_xps_zinfo_t *)gs_alloc_bytes(mem->non_gc_memory, sizeof(gx_device_xps_zinfo_t), "zinfo");
+ gx_device_xps_f2i_t *f2i =
+ (gx_device_xps_f2i_t *)gs_alloc_bytes(mem->non_gc_memory, sizeof(gx_device_xps_f2i_t), "zinfo node");
+
+ if_debug1m('_', dev->memory, "new node %s\n", filename);
+
+ if (info == NULL || f2i == NULL)
+ return gs_throw_code(gs_error_Fatal);
+
+ f2i->info = info;
+ f2i->next = NULL;
+
+ if (xps_dev->f2i == 0) { /* no head */
+ xps_dev->f2i = f2i;
+ xps_dev->f2i_tail = f2i;
+ } else { /* append the node */
+ xps_dev->f2i_tail->next = f2i;
+ xps_dev->f2i_tail = f2i;
+ }
+
+ lenstr = strlen(filename);
+ f2i->filename = (char*)gs_alloc_bytes(mem->non_gc_memory, lenstr + 1, "zinfo_filename");
+ strcpy(f2i->filename, filename);
+
+ info->data.fp = 0;
+ info->data.count = 0;
+ info->saved = false;
+
+ if (gs_debug_c('_')) {
+ gx_device_xps_f2i_t *f2i = xps_dev->f2i;
+ int node = 1;
+ gx_device_xps_f2i_t *prev_f2i;
+
+ while (f2i != NULL) {
+ if_debug2m('_', dev->memory, "node:%d %s\n", node++, f2i->filename);
+ prev_f2i = f2i;
+ f2i=f2i->next;
+ }
+ if_debug1m('_', dev->memory, "tail okay=%s\n", prev_f2i == xps_dev->f2i_tail ? "yes" : "no");
+ }
+ return 0;
+}
+
+/* add a new file to the zip archive */
+static int
+zip_add_file(gx_device_xps *xps_dev, const char *filename)
+{
+ int code = zip_new_info_node(xps_dev, filename);
+ if (code < 0)
+ return gs_throw_code(gs_error_Fatal);
+ return 0;
+}
+
+/* look up the information associated with the zip archive [filename] */
+static gx_device_xps_zinfo_t *
+zip_look_up_file_info(gx_device_xps *xps_dev, const char *filename)
+{
+ gx_device_xps_f2i_t *cur = xps_dev->f2i;
+ while (cur) {
+ if (!strcmp(cur->filename, filename))
+ break;
+ cur = cur->next;
+ }
+ return (cur ? cur->info : NULL);
+}
+
+/* Add data to an archived zip file, create the file if it doesn't exist */
+static int
+zip_append_data(gs_memory_t *mem, gx_device_xps_zinfo_t *info, byte *data, uint len)
+{
+ uint count;
+
+ /* if there is no data then this is the first call for this
+ archive file, open a temporary file to store the data. */
+ if (info->data.count == 0) {
+ char *filename =
+ (char *)gs_alloc_bytes(mem->non_gc_memory, gp_file_name_sizeof,
+ "zip_append_data(filename)");
+ FILE *fp;
+
+ if (!filename) {
+ return(gs_throw_code(gs_error_VMerror));
+ }
+
+ fp = gp_open_scratch_file_rm(mem, "xpsdata-",
+ filename, "wb+");
+ gs_free_object(mem->non_gc_memory, filename, "zip_append_data(filename)");
+ info->data.fp = fp;
+ }
+
+ /* shouldn't happen unless the first call opens the temporary file
+ but writes no data. */
+ if (info->data.fp == NULL)
+ return gs_throw_code(gs_error_Fatal);
+
+ count = fwrite(data, 1, len, info->data.fp);
+ if (count != len) {
+ fclose(info->data.fp);
+ return -1;
+ }
+ /* probably unnecessary but makes debugging easier */
+ fflush(info->data.fp);
+ info->data.count += len;
+
+ return 0;
+}
+
+/* write to one of the archives (filename) in the zip archive */
+static int
+write_to_zip_file(gx_device_xps *xps_dev, const char *filename,
+ byte *data, uint len)
+{
+ gx_device *dev = (gx_device *)xps_dev;
+ gs_memory_t *mem = dev->memory;
+
+ gx_device_xps_zinfo_t *info = zip_look_up_file_info(xps_dev, filename);
+ int code = 0;
+
+ /* No information on this archive file, create a new zip entry
+ info node */
+ if (info == NULL) {
+ code = zip_add_file(xps_dev, filename);
+ if (code < 0)
+ return gs_rethrow_code(code);
+ info = zip_look_up_file_info(xps_dev, filename);
+ }
+
+ if (info == NULL)
+ return gs_throw_code(gs_error_Fatal);
+
+ code = zip_append_data(mem, info, data, len);
+ if (code < 0)
+ return gs_rethrow_code(code);
+
+ return code;
+}
+
+static void
+put_bytes(stream *zs, byte *buf, uint len)
+{
+ uint used;
+ sputs(zs, buf, len, &used);
+ /* NB return value */
+}
+
+static void
+put_u32(stream *zs, unsigned long l)
+{
+ sputc(zs, (byte)(l));
+ sputc(zs, (byte)(l >> 8));
+ sputc(zs, (byte)(l >> 16));
+ sputc(zs, (byte)(l >> 24));
+}
+
+static void
+put_u16(stream *zs, ushort s)
+{
+ sputc(zs, (byte)(s));
+ sputc(zs, (byte)(s >> 8));
+}
+
+/* TODO - the following 2 definitions need to done correctly */
+static ushort
+make_dos_date(uint year, uint month, uint day)
+{
+ uint delta_1980 = year - 1980;
+ return (delta_1980 << 9 | month << 5 | day);
+}
+
+static ushort
+make_dos_time(uint hour, uint min, uint sec)
+{
+ /* note the seconds are multiplied by 2 */
+ return (hour << 11 | min << 5 | sec >> 1);
+}
+
+/* convenience routine. */
+static int
+write_str_to_zip_file(gx_device_xps *xps_dev, const char *filename,
+ const char *str)
+{
+ return write_to_zip_file(xps_dev, filename, (byte *)str, strlen(str));
+}
+
+/* Used to add ICC profiles to the zip file. */
+static int
+add_data_to_zip_file(gx_device_xps *xps_dev, const char *filename, byte *buf, long size)
+{
+ gx_device_xps_zinfo_t *info = zip_look_up_file_info(xps_dev, filename);
+ int code;
+ long curr_pos;
+ unsigned long crc = 0;
+ stream *f;
+ ushort date, time;
+
+ /* This file should not yet exist */
+ if (info == NULL) {
+ code = zip_add_file(xps_dev, filename);
+ if (code < 0)
+ return gs_rethrow_code(code);
+ info = zip_look_up_file_info(xps_dev, filename);
+ }
+ else {
+ return gs_throw_code(gs_error_Fatal);
+ }
+ f = ((gx_device_vector*)xps_dev)->strm;
+ curr_pos = stell(f);
+
+ /* Figure out the crc */
+ crc = crc32(0L, Z_NULL, 0);
+ crc = crc32(crc, buf, size);
+
+ date = make_dos_date(2012, 2, 16);
+ time = make_dos_time(9, 15, 0);
+
+ put_u32(f, 0x04034b50); /* magic */
+ put_u16(f, 20); /* version */
+ put_u16(f, 0); /* flags */
+ put_u16(f, 0); /* method */
+ put_u16(f, time);
+ put_u16(f, date);
+ put_u32(f, crc);
+ put_u32(f, size); /* compressed */
+ put_u32(f, size); /* uncompressed */
+ put_u16(f, strlen(filename));
+ put_u16(f, 0); /* extra field length */
+ put_bytes(f, (byte *)filename, strlen(filename));
+ put_bytes(f, buf, size);
+ put_bytes(f, 0, 0); /* extra field */
+
+ /* Now we need to add the info about this file */
+ xps_dev->f2i_tail->info->CRC = crc;
+ xps_dev->f2i_tail->info->time = time;
+ xps_dev->f2i_tail->info->date = date;
+ xps_dev->f2i_tail->info->data.count = size;
+ xps_dev->f2i_tail->info->current_pos = curr_pos;
+ xps_dev->f2i_tail->info->file_size = size;
+ /* Mark the file as already stored */
+ xps_dev->f2i_tail->info->saved = true;
+ return 0;
+}
+
+/* Used to add images to the zip file. This file is added now
+and not later like the other files */
+static int
+add_file_to_zip_file(gx_device_xps *xps_dev, const char *filename, FILE *src)
+{
+ gx_device_xps_zinfo_t *info = zip_look_up_file_info(xps_dev, filename);
+ int code = 0;
+ long curr_pos;
+ unsigned long crc = 0;
+ byte buf[4];
+ uint nread;
+ unsigned long count = 0;
+ stream *f;
+ ushort date, time;
+
+ /* This file should not yet exist */
+ if (info == NULL) {
+ code = zip_add_file(xps_dev, filename);
+ if (code < 0)
+ return gs_rethrow_code(code);
+ info = zip_look_up_file_info(xps_dev, filename);
+ }
+ else {
+ return gs_throw_code(gs_error_Fatal);
+ }
+
+ f = ((gx_device_vector*)xps_dev)->strm;
+ curr_pos = stell(f);
+
+ /* Get to the start */
+ if (gp_fseek_64(src, 0, 0) < 0)
+ return gs_throw_code(gs_error_Fatal);
+
+ /* Figure out the crc */
+ crc = crc32(0L, Z_NULL, 0);
+ /* Chunks of 4 until we get to remainder */
+ while (!feof(src)) {
+ nread = fread(buf, 1, sizeof(buf), src);
+ count = count + nread;
+ crc = crc32(crc, buf, nread);
+ }
+
+ date = make_dos_date(2012, 2, 16);
+ time = make_dos_time(9, 15, 0);
+
+ put_u32(f, 0x04034b50); /* magic */
+ put_u16(f, 20); /* version */
+ put_u16(f, 0); /* flags */
+ put_u16(f, 0); /* method */
+ put_u16(f, time);
+ put_u16(f, date);
+ put_u32(f, crc);
+ put_u32(f, count); /* compressed */
+ put_u32(f, count); /* uncompressed */
+ put_u16(f, strlen(filename));
+ put_u16(f, 0); /* extra field length */
+ put_bytes(f, (byte *)filename, strlen(filename));
+ {
+ if (gp_fseek_64(src, (gs_offset_t)0, 0) < 0)
+ return gs_throw_code(gs_error_Fatal);
+ while (!feof(src)) {
+ ulong nread = fread(buf, 1, sizeof(buf), src);
+ put_bytes(f, buf, nread);
+ }
+ }
+ put_bytes(f, 0, 0); /* extra field */
+
+ /* Now we need to add the info about this file */
+ xps_dev->f2i_tail->info->CRC = crc;
+ xps_dev->f2i_tail->info->time = time;
+ xps_dev->f2i_tail->info->date = date;
+ xps_dev->f2i_tail->info->data.count = count;
+ xps_dev->f2i_tail->info->current_pos = curr_pos;
+ xps_dev->f2i_tail->info->file_size = count;
+ /* Mark the file as already stored */
+ xps_dev->f2i_tail->info->saved = true;
+ return 0;
+}
+
+/* zip up a single file moving its data from the temporary file to the
+ zip container. */
+static int
+zip_close_archive_file(gx_device_xps *xps_dev, const char *filename)
+{
+ gx_device_xps_zinfo_t *info = zip_look_up_file_info(xps_dev, filename);
+ gx_device_xps_zdata_t data = info->data;
+ byte buf[4];
+ unsigned long crc = 0;
+ int count = 0;
+ int len;
+ /* we can't use the vector stream accessor because it calls beginpage */
+ stream *f = ((gx_device_vector*)xps_dev)->strm;
+
+ if (info == NULL)
+ return -1;
+
+ /* Already stored */
+ if (info->saved)
+ return 0;
+
+ if (data.count >= 0) {
+ FILE *fp = data.fp;
+ uint nread;
+
+ if (fp == NULL)
+ return gs_throw_code(gs_error_Fatal);
+
+ crc = crc32(0L, Z_NULL, 0);
+ rewind(fp);
+ while (!feof(fp)) {
+ nread = fread(buf, 1, sizeof(buf), fp);
+ crc = crc32(crc, buf, nread);
+ count = count + nread;
+ }
+ /* If this is a TIFF file, then update the data count information.
+ During the writing of the TIFF directory there is seeking and
+ relocations that occur, which make data.count incorrect.
+ We could just do this for all the files and avoid the test. */
+ len = strlen(filename);
+ if (len > 3 && (strncmp("tif", &(filename[len - 3]), 3) == 0)) {
+ info->data.count = count;
+ data = info->data;
+ data.fp = fp;
+ }
+ }
+
+ info->current_pos = stell(f);
+ info->CRC = crc;
+
+ /* NB should use ghostscript "gp" time and date functions for
+ this, for now we hardwire the dates. */
+ info->date = make_dos_date(2012, 2, 16);
+ info->time = make_dos_time(9, 15, 0);
+
+ put_u32(f, 0x04034b50); /* magic */
+ put_u16(f, 20); /* version */
+ put_u16(f, 0); /* flags */
+ put_u16(f, 0); /* method */
+ put_u16(f, info->time);
+ put_u16(f, info->date);
+ put_u32(f, crc);
+ put_u32(f, data.count); /* compressed */
+ put_u32(f, data.count); /* uncompressed */
+ put_u16(f, strlen(filename));
+ put_u16(f, 0); /* extra field length */
+ put_bytes(f, (byte *)filename, strlen(filename));
+ {
+ FILE *fp = data.fp;
+ rewind(fp);
+ while (!feof(fp)) {
+ ulong nread = fread(buf, 1, sizeof(buf), fp);
+ put_bytes(f, buf, nread);
+ }
+ fclose(fp);
+ }
+ put_bytes(f, 0, 0); /* extra field */
+
+ /* Mark as saved */
+ info->saved = true;
+ return 0;
+}
+
+/* walk the file info node list writing all the files to the
+ archive */
+static int
+zip_close_all_archive_files(gx_device_xps *xps_dev)
+{
+ gx_device_xps_f2i_t *f2i = xps_dev->f2i;
+ while (f2i) {
+ int code = zip_close_archive_file(xps_dev, f2i->filename);
+ if (code < 0) {
+ return code;
+ }
+ f2i = f2i->next;
+ }
+ return 0;
+}
+
+/* write all files to the zip container and write the zip central
+ directory */
+static int
+zip_close_archive(gx_device_xps *xps_dev)
+{
+ gx_device_xps_f2i_t *f2i = xps_dev->f2i;
+
+ int entry_count = 0;
+ long pos_before_cd;
+ long pos_after_cd;
+ /* we can't use the vector stream accessor because it calls beginpage */
+ stream *f = ((gx_device_vector*)xps_dev)->strm;
+ int code = zip_close_all_archive_files(xps_dev);
+
+ pos_before_cd = stell(f);
+
+ if (code < 0)
+ return code;
+
+ while (f2i) {
+ gx_device_xps_zinfo_t *info = f2i->info;
+ put_u32(f, 0x02014b50); /* magic */
+ put_u16(f, 20); /* version made by */
+ put_u16(f, 20); /* version required */
+ put_u16(f, 0); /* bit flag */
+ put_u16(f, 0); /* compression method */
+ put_u16(f, info->time);
+ put_u16(f, info->date);
+ put_u32(f, info->CRC);
+ put_u32(f, info->data.count); /* compressed */
+ put_u32(f, info->data.count); /* uncompressed */
+ put_u16(f, strlen(f2i->filename));
+
+ /* probably will never use the next 6 so we just set them to
+ zero here */
+ put_u16(f, 0); /* extra field length */
+ put_u16(f, 0); /* file comment length */
+ put_u16(f, 0); /* disk number */
+ put_u16(f, 0); /* internal file attributes */
+ put_u32(f, 0); /* external file attributes */
+
+ put_u32(f, info->current_pos);
+ put_bytes(f, (byte *)f2i->filename, strlen(f2i->filename));
+
+ put_bytes(f, 0, 0); /* extra field */
+ put_bytes(f, 0, 0); /* extra comment */
+
+ entry_count++;
+ f2i = f2i->next;
+ }
+
+ pos_after_cd = stell(f);
+
+ put_u32(f, 0x06054b50);
+ put_u16(f, 0); /* number of disks */
+ put_u16(f, 0); /* disk where central directory starts */
+ put_u16(f, entry_count); /* # of records in cd */
+ put_u16(f, entry_count); /* total # of records in cd */
+ put_u32(f, pos_after_cd - pos_before_cd); /* size of central cd */
+ put_u32(f, pos_before_cd); /* offset of central directory */
+ put_u16(f, 0); /* comment length */
+ put_bytes(f, 0, 0); /* comment */
+
+ return 0;
+}
+
+/* Brush management */
+static void
+xps_setstrokebrush(gx_device_xps *xps, xps_brush_t type)
+{
+ if_debug1m('_', xps->memory, "xps_setstrokebrush:%d\n", (int)type);
+
+ xps->stroketype = type;
+}
+
+static void
+xps_setfillbrush(gx_device_xps *xps, xps_brush_t type)
+{
+ if_debug1m('_', xps->memory, "xps_setfillbrush:%d\n", (int)type);
+
+ xps->filltype = type;
+}
+
+/* Device procedures */
+static int
+xps_open_device(gx_device *dev)
+{
+ gx_device_vector * vdev = (gx_device_vector*)dev;
+ gx_device_xps * xps = (gx_device_xps*)dev;
+ int code = 0;
+
+ vdev->v_memory = dev->memory;
+ vdev->vec_procs = &xps_vector_procs;
+ gdev_vector_init(vdev);
+ code = gdev_vector_open_file_options(vdev, 512,
+ VECTOR_OPEN_FILE_SEQUENTIAL);
+ if (code < 0)
+ return gs_rethrow_code(code);
+
+ /* In case ths device has been subclassed, descend to the terminal
+ * of the chain. (Setting the variables in a subclassing device
+ * doesn't do anythign useful.....)
+ */
+ while(vdev->child)
+ vdev = (gx_device_vector *)vdev->child;
+ xps = (gx_device_xps*)vdev;
+
+ /* xps-specific initialization goes here */
+ xps->page_count = 0;
+ xps->relationship_count = 0;
+ xps->strokecolor = gx_no_color_index;
+ xps->fillcolor = gx_no_color_index;
+ xps_setstrokebrush(xps, xps_solidbrush);
+ xps_setfillbrush(xps, xps_solidbrush);
+ /* these should be the graphics library defaults instead? */
+ xps->linewidth = XPS_DEFAULT_LINEWIDTH;
+ xps->linecap = XPS_DEFAULT_LINECAP;
+ xps->linejoin = XPS_DEFAULT_LINEJOIN;
+ xps->miterlimit = XPS_DEFAULT_MITERLIMIT;
+ xps->can_stroke = true;
+ /* zip info */
+ xps->f2i = NULL;
+ xps->f2i_tail = NULL;
+
+ /* Image related stuff */
+ xps->image_count = 0;
+ xps->xps_pie = NULL;
+
+ /* ICC related stuff */
+ xps->icc_data = NULL;
+
+ code = write_str_to_zip_file(xps, (char *)"FixedDocumentSequence.fdseq",
+ fixed_document_sequence);
+ if (code < 0)
+ return gs_rethrow_code(code);
+
+ code = write_str_to_zip_file(xps, (char *)"[Content_Types].xml",
+ xps_content_types);
+ if (code < 0)
+ return gs_rethrow_code(code);
+
+ code = write_str_to_zip_file(xps,
+ (char *)"Documents/1/FixedDocument.fdoc",
+ fixed_document_fdoc_header);
+ if (code < 0)
+ return gs_rethrow_code(code);
+
+ /* Right out the magical stuff related to the fix document sequence relationship */
+ code = write_str_to_zip_file(xps, (char *)"_rels/.rels", rels_header);
+ if (code < 0)
+ return gs_rethrow_code(code);
+
+ code = write_str_to_zip_file(xps, (char *)"_rels/.rels", rels_fdseq);
+ if (code < 0)
+ return gs_rethrow_code(code);
+
+ return code;
+}
+
+/* write xps commands to Pages/%d.fpage */
+static int
+write_str_to_current_page(gx_device_xps *xps, const char *str)
+{
+ const char *page_template = "Documents/1/Pages/%d.fpage";
+ char buf[128]; /* easily enough to accommodate the string and a page number */
+
+ /* we're one ahead of the page count */
+ int code = gs_sprintf(buf, page_template, xps->page_count+1);
+ if (code < 0)
+ return gs_rethrow_code(code);
+
+ return write_str_to_zip_file(xps, buf, str);
+}
+
+/* add relationship to Pages/_rels/%d.fpage.rels */
+static int
+add_new_relationship(gx_device_xps *xps, const char *str)
+{
+ const char *rels_template = "Documents/1/Pages/_rels/%d.fpage.rels";
+ char buf[128]; /* easily enough to accommodate the string and a page number */
+ char line[300];
+ const char *fmt;
+
+ int code = gs_sprintf(buf, rels_template, xps->page_count + 1);
+ if (code < 0)
+ return gs_rethrow_code(code);
+
+ /* Check if this is our first one */
+ if (xps->relationship_count == 0)
+ write_str_to_zip_file(xps, buf, rels_header);
+
+ /* Now add the reference */
+ fmt = "<Relationship Target = \"/%s\" Id = \"R%d\" Type = %s/>\n";
+ gs_sprintf(line, fmt, str, xps->relationship_count, rels_req_type);
+
+ xps->relationship_count++;
+
+ return write_str_to_zip_file(xps, buf, line);
+}
+
+static int
+close_page_relationship(gx_device_xps *xps)
+{
+ const char *rels_template = "Documents/1/Pages/_rels/%d.fpage.rels";
+ char buf[128]; /* easily enough to accommodate the string and a page number */
+
+ int code = gs_sprintf(buf, rels_template, xps->page_count + 1);
+ if (code < 0)
+ return gs_rethrow_code(code);
+
+ write_str_to_zip_file(xps, buf, "</Relationships>");
+ return 0;
+}
+
+ /* Page management */
+
+/* Complete a page */
+static int
+xps_output_page(gx_device *dev, int num_copies, int flush)
+{
+ gx_device_xps *const xps = (gx_device_xps*)dev;
+ gx_device_vector *vdev = (gx_device_vector *)dev;
+ int code;
+
+ write_str_to_current_page(xps, "</Canvas></FixedPage>");
+ if (xps->relationship_count > 0)
+ {
+ /* Close the relationship xml */
+ code = close_page_relationship(xps);
+ if (code < 0)
+ return gs_rethrow_code(code);
+ xps->relationship_count = 0; /* Reset for next page */
+ }
+ xps->page_count++;
+
+ if (ferror(xps->file))
+ return gs_throw_code(gs_error_ioerror);
+
+ if ((code=gx_finish_output_page(dev, num_copies, flush)) < 0)
+ return code;
+
+ /* Check if we need to change the output file for separate
+ pages. NB not sure if this will work correctly. */
+ if (gx_outputfile_is_separate_pages(((gx_device_vector *)dev)->fname, dev->memory)) {
+ if ((code = xps_close_device(dev)) < 0)
+ return code;
+ code = xps_open_device(dev);
+ }
+
+ if_debug1m('_', dev->memory, "xps_output_page - page=%d\n", xps->page_count);
+ vdev->in_page = false;
+
+ return code;
+}
+
+static void
+xps_release_icc_info(gx_device *dev)
+{
+ gx_device_xps *xps = (gx_device_xps*)dev;
+ xps_icc_data_t *curr;
+ xps_icc_data_t *icc_data = xps->icc_data;
+
+ while (icc_data != NULL) {
+ curr = icc_data;
+ icc_data = icc_data->next;
+ gs_free(dev->memory->non_gc_memory, curr, sizeof(xps_icc_data_t), 1,
+ "xps_release_icc_info");
+ }
+ return;
+}
+
+/* Close the device */
+static int
+xps_close_device(gx_device *dev)
+{
+ gx_device_xps *xps = (gx_device_xps*)dev;
+ int code;
+
+ /* closing for the FixedDocument */
+ code = write_str_to_zip_file(xps, "Documents/1/FixedDocument.fdoc", "</FixedDocument>");
+ if (code < 0)
+ return gs_rethrow_code(code);
+
+ if (ferror(xps->file))
+ return gs_throw_code(gs_error_ioerror);
+
+ code = zip_close_archive(xps);
+ if (code < 0)
+ return gs_rethrow_code(code);
+
+ /* Release the icc info */
+ xps_release_icc_info(dev);
+
+#if defined(__WIN32__) && XPSPRINT==1
+ code = gdev_vector_close_file((gx_device_vector*)dev);
+ if (code < 0)
+ return gs_rethrow_code(code);
+
+ if (strlen((const char *)xps->PrinterName)) {
+ int reason;
+ code = XPSPrint(xps->fname, (char *)xps->PrinterName, &reason);
+ if (code < 0) {
+ switch(code) {
+ case -1:
+ break;
+ case -2:
+ eprintf1("ERROR: Could not create competion event: %08X\n", reason);
+ break;
+ case -3:
+ eprintf1("ERROR: Could not create MultiByteString from PrinerName: %s\n", xps->PrinterName);
+ break;
+ case -4:
+ eprintf1("ERROR: Could not start XPS print job: %08X\n", reason);
+ break;
+ case -5:
+ eprintf1("ERROR: Could not create XPS OM Object Factory: %08X\n", reason);
+ break;
+ case -6:
+ eprintf1("ERROR: Could not create MultiByteString from OutputFile: %s\n", xps->fname);
+ break;
+ case -7:
+ eprintf1("ERROR: Could not create Package from File %08X\n", reason);
+ break;
+ case -8:
+ eprintf1("ERROR: Could not write Package to stream %08X\n", reason);
+ break;
+ case -9:
+ eprintf1("ERROR: Could not close job stream: %08X\n", reason);
+ break;
+ case -10:
+ eprintf1("ERROR: Wait for completion event failed: %08X\n", reason);
+ break;
+ case -11:
+ eprintf1("ERROR: Could not get job status: %08X\n", reason);
+ break;
+ case -12:
+ eprintf("ERROR: job was cancelled\n");
+ break;
+ case -13:
+ eprintf1("ERROR: Print job failed: %08X\n", reason);
+ break;
+ case -14:
+ eprintf("ERROR: unexpected failure\n");
+ break;
+ case -15:
+ case -16:
+ eprintf("ERROR: XpsPrint.dll does not exist or is missing a required method\n");
+ break;
+ }
+ return(gs_throw_code(gs_error_invalidaccess));
+ }
+ return(0);
+ }
+#else
+ return gdev_vector_close_file((gx_device_vector*)dev);
+#endif
+}
+
+/* Respond to a device parameter query from the client */
+static int
+xps_get_params(gx_device *dev, gs_param_list *plist)
+{
+ int code = 0;
+
+ if_debug0m('_', dev->memory, "xps_get_params\n");
+
+ /* call our superclass to add its standard set */
+ code = gdev_vector_get_params(dev, plist);
+ if (code < 0)
+ return gs_rethrow_code(code);
+
+#if defined(__WIN32__) && XPSPRINT==1
+ {
+ gs_param_string ofns;
+ gx_device_xps *const xps = (gx_device_xps*)dev;
+
+ /* xps specific parameters are added to plist here */
+ ofns.data = (const byte *)&xps->PrinterName;
+ ofns.size = strlen(xps->fname);
+ ofns.persistent = false;
+ if ((code = param_write_string(plist, "PrinterName", &ofns)) < 0)
+ return code;
+ }
+#endif
+ return code;
+}
+
+/* Read the device parameters passed to us by the client */
+static int
+xps_put_params(gx_device *dev, gs_param_list *plist)
+{
+ int code = 0;
+
+ if_debug0m('_', dev->memory, "xps_put_params\n");
+
+ /* xps specific parameters are parsed here */
+#if defined(__WIN32__) && XPSPRINT==1
+ { /* NB: The following is not strictly correct since changes are */
+ /* made even if 'gdev_vector_put_params' returns an error. */
+ gs_param_name param_name;
+ gs_param_string pps;
+ gx_device_xps *const xps = (gx_device_xps*)dev;
+ switch (code = param_read_string(plist, (param_name = "PrinterName"), &pps)) {
+ case 0:
+ if (pps.size > 64) {
+ eprintf1("\nERROR: PrinterName too long (max %d)\n", MAXPRINTERNAME);
+ } else {
+ memcpy(xps->PrinterName, pps.data, pps.size);
+ xps->PrinterName[pps.size] = 0;
+ }
+ break;
+ default:
+ param_signal_error(plist, param_name, code);
+ case 1:
+ /* memset(&xps->PrinterName, 0x00, MAXPRINTERNAME);*/
+ break;
+ }
+ }
+#endif
+ /* call our superclass to get its parameters, like OutputFile */
+ code = gdev_vector_put_params(dev, plist); /* errors are handled by caller or setpagedevice */
+
+ return code;
+}
+
+static int xps_finish_copydevice(gx_device *dev, const gx_device *from_dev)
+{
+ gx_device_xps *xps = (gx_device_xps*)dev;
+
+ memset(xps->PrinterName, 0x00, MAXPRINTERNAME);
+ return 0;
+}
+
+static int
+set_state_color(gx_device_vector *vdev, const gx_drawing_color *pdc, gx_color_index *color)
+{
+ gx_device_xps *xps = (gx_device_xps *)vdev;
+
+ /* hack so beginpage is called */
+ (void)gdev_vector_stream((gx_device_vector*)xps);
+
+ /* Usually this is not an actual error but a signal to the
+ graphics library to simplify the color */
+ if (!gx_dc_is_pure(pdc)) {
+ return_error(gs_error_rangecheck);
+ }
+
+ *color = gx_dc_pure_color(pdc);
+ return 0;
+}
+
+static int
+xps_setfillcolor(gx_device_vector *vdev, const gs_imager_state *pis, const gx_drawing_color *pdc)
+{
+ gx_device_xps *xps = (gx_device_xps *)vdev;
+
+ if_debug1m('_', xps->memory, "xps_setfillcolor:%06X\n", (uint32_t)gx_dc_pure_color(pdc));
+
+ return set_state_color(vdev, pdc, &xps->fillcolor);
+}
+
+static int
+xps_setstrokecolor(gx_device_vector *vdev, const gs_imager_state *pis, const gx_drawing_color *pdc)
+{
+ gx_device_xps *xps = (gx_device_xps *)vdev;
+
+ if_debug1m('_', xps->memory, "xps_setstrokecolor:%06X\n", (uint32_t)gx_dc_pure_color(pdc));
+
+ return set_state_color(vdev, pdc, &xps->strokecolor);
+}
+
+static int
+xps_beginpage(gx_device_vector *vdev)
+{
+
+ gx_device_xps *xps = (gx_device_xps *)vdev;
+ char buf[128];
+ int code = 0;
+
+ if_debug0m('_', xps->memory, "xps_beginpage\n");
+
+ {
+ const char *template = "<PageContent Source=\"Pages/%d.fpage\" />";
+ /* Note page count is 1 less than the current page */
+ code = gs_sprintf(buf, template, xps->page_count + 1);
+ if (code < 0)
+ return gs_rethrow_code(code);
+
+ /* Put a reference to this new page in the FixedDocument */
+ code = write_str_to_zip_file(xps, "Documents/1/FixedDocument.fdoc", buf);
+ if (code < 0)
+ return gs_rethrow_code(code);
+
+ }
+
+ {
+ const char *page_size_template = "<FixedPage Width=\"%d\" Height=\"%d\" "
+ "xmlns=\"http://schemas.microsoft.com/xps/2005/06\" xml:lang=\"en-US\">\n";
+ code = gs_sprintf(buf, page_size_template,
+ (int)(xps->MediaSize[0] * 4.0/3.0), /* pts -> 1/96 inch */
+ (int)(xps->MediaSize[1] * 4.0/3.0));
+ if (code < 0)
+ return gs_rethrow_code(code);
+ code = write_str_to_current_page(xps, buf);
+ if (code < 0)
+ return gs_rethrow_code(code);
+ }
+ {
+ const char *canvas_template = "<Canvas RenderTransform=\"%g,%g,%g,%g,%g,%g\">\n";
+ code = gs_sprintf(buf, canvas_template,
+ 96.0/xps->HWResolution[0], 0.0, 0.0,
+ 96.0/xps->HWResolution[1], 0.0, 0.0);
+ if (code < 0)
+ return gs_rethrow_code(code);
+
+ code = write_str_to_current_page(xps, buf);
+ if (code < 0)
+ return gs_rethrow_code(code);
+ }
+
+ if_debug4m('_', xps->memory,
+ "page info: resx=%g resy=%g width=%d height=%d\n",
+ xps->HWResolution[0], xps->HWResolution[1],
+ (int)xps->MediaSize[0], (int)xps->MediaSize[1]);
+
+ return code;
+}
+
+static int
+xps_setlinewidth(gx_device_vector *vdev, double width)
+{
+ gx_device_xps *xps = (gx_device_xps *)vdev;
+
+ if_debug1m('_', xps->memory, "xps_setlinewidth(%lf)\n", width);
+
+ xps->linewidth = width;
+
+ return 0;
+}
+static int
+xps_setlinecap(gx_device_vector *vdev, gs_line_cap cap)
+{
+ gx_device_xps *xps = (gx_device_xps *)vdev;
+#ifdef DEBUG
+ /* Only used for debug print, so guiard to prevent warnings on non-debug builds */
+ const char *linecap_names[] = {"butt", "round", "square",
+ "triangle", "unknown"};
+#endif
+
+ if (cap < 0 || cap > gs_cap_unknown)
+ return gs_throw_code(gs_error_rangecheck);
+ if_debug1m('_', xps->memory, "xps_setlinecap(%s)\n", linecap_names[cap]);
+
+ xps->linecap = cap;
+
+ return 0;
+}
+static int
+xps_setlinejoin(gx_device_vector *vdev, gs_line_join join)
+{
+ gx_device_xps *xps = (gx_device_xps *)vdev;
+#ifdef DEBUG
+ /* Only used for debug print, so guiard to prevent warnings on non-debug builds */
+ const char *linejoin_names[] = {"miter", "round", "bevel",
+ "none", "triangle", "unknown"};
+#endif
+
+ if (join < 0 || join > gs_join_unknown)
+ return gs_throw_code(gs_error_rangecheck);
+ if_debug1m('_', xps->memory, "xps_setlinejoin(%s)\n", linejoin_names[join]);
+
+ xps->linejoin = join;
+
+ return 0;
+}
+static int
+xps_setmiterlimit(gx_device_vector *vdev, double limit)
+{
+ if_debug1m('_', vdev->memory, "xps_setmiterlimit(%lf)\n", limit);
+ return 0;
+}
+static int
+xps_setdash(gx_device_vector *vdev, const float *pattern,
+ uint count, double offset)
+{
+ gx_device_xps *xps = (gx_device_xps *)vdev;
+ if_debug2m('_', vdev->memory, "xps_setdash count:%d offset:%g\n", count, offset);
+ xps->can_stroke = (count == 0);
+ return 0;
+}
+static int
+xps_setlogop(gx_device_vector *vdev, gs_logical_operation_t lop,
+ gs_logical_operation_t diff)
+{
+ if_debug2m('_', vdev->memory, "xps_setlogop(%u,%u) set logical operation\n",
+ lop, diff);
+ /* XPS can fake some simpler modes, but we ignore this for now. */
+ return 0;
+}
+
+ /* Other state */
+
+static int
+xps_can_handle_hl_color(gx_device_vector *vdev, const gs_imager_state *pis,
+ const gx_drawing_color *pdc)
+{
+ if_debug0m('_', vdev->memory, "xps_can_handle_hl_color\n");
+ return 0;
+}
+
+/* Paths */
+static bool
+image_brush_fill(gx_path_type_t path_type, xps_brush_t brush_type)
+{
+ return brush_type == xps_imagebrush;
+}
+
+static bool
+drawing_path(gx_path_type_t path_type, xps_brush_t brush_type)
+{
+ return ((path_type & gx_path_type_stroke) || (path_type & gx_path_type_fill) ||
+ image_brush_fill(path_type, brush_type));
+}
+
+static void
+xps_finish_image_path(gx_device_vector *vdev)
+{
+ gx_device_xps *xps = (gx_device_xps *)vdev;
+ char line[300];
+ const char *fmt;
+ gs_matrix matrix;
+
+ /* Path is started. Do the image brush image brush and close the path */
+ write_str_to_current_page(xps, "\t<Path.Fill>\n");
+ write_str_to_current_page(xps, "\t\t<ImageBrush ");
+ fmt = "ImageSource = \"{ColorConvertedBitmap /%s /%s}\" Viewbox=\"%d, %d, %d, %d\" ViewboxUnits = \"Absolute\" Viewport = \"%d, %d, %d, %d\" ViewportUnits = \"Absolute\" TileMode = \"None\" >\n";
+ gs_sprintf(line, fmt, xps->xps_pie->file_name, xps->xps_pie->icc_name,
+ 0, 0, xps->xps_pie->width, xps->xps_pie->height, 0, 0,
+ xps->xps_pie->width, xps->xps_pie->height);
+ write_str_to_current_page(xps, line);
+
+ /* Now the render transform. This is applied to the image brush. Path
+ is already transformed */
+ write_str_to_current_page(xps, "\t\t\t<ImageBrush.Transform>\n");
+ fmt = "\t\t\t\t<MatrixTransform Matrix = \"%g,%g,%g,%g,%g,%g\" />\n";
+ matrix = xps->xps_pie->mat;
+ gs_sprintf(line, fmt,
+ matrix.xx, matrix.xy, matrix.yx, matrix.yy, matrix.tx, matrix.ty);
+ write_str_to_current_page(xps, line);
+ write_str_to_current_page(xps, "\t\t\t</ImageBrush.Transform>\n");
+ write_str_to_current_page(xps, "\t\t</ImageBrush>\n");
+ write_str_to_current_page(xps, "\t</Path.Fill>\n");
+ /* End this path */
+ write_str_to_current_page(xps, "</Path>\n");
+}
+
+static int
+xps_dorect(gx_device_vector *vdev, fixed x0, fixed y0,
+ fixed x1, fixed y1, gx_path_type_t type)
+{
+ gx_device_xps *xps = (gx_device_xps *)vdev;
+ char line[300];
+ const char *fmt;
+ uint32_t c;
+
+ (void)gdev_vector_stream((gx_device_vector*)xps);
+
+ if_debug9m('_', xps->memory,
+ "rect type=%d coords=%g,%g %g,%g %g,%g %g,%g\n", type,
+ fixed2float(x0), fixed2float(y0),
+ fixed2float(x0), fixed2float(y1),
+ fixed2float(x1), fixed2float(y1),
+ fixed2float(x1), fixed2float(y0));
+
+
+ /* skip non-drawing paths for now */
+ if (!drawing_path(type, xps->filltype)) {
+ if_debug1m('_', xps->memory, "xps_dorect: type not supported %x\n", type);
+ return 0;
+ }
+
+ if ((type & gx_path_type_stroke) && !xps->can_stroke) {
+ return_error(gs_error_rangecheck);
+ }
+
+ if (image_brush_fill(type, xps->filltype)) {
+ /* Do the path data */
+ fmt = "<Path Data=\"M %g, %g L %g, %g %g, %g %g, %g Z\" >\n";
+ gs_sprintf(line, fmt,
+ fixed2float(x0), fixed2float(y0),
+ fixed2float(x0), fixed2float(y1),
+ fixed2float(x1), fixed2float(y1),
+ fixed2float(x1), fixed2float(y0));
+ write_str_to_current_page(xps, line);
+ /* And now the rest of the details */
+ xps_finish_image_path(vdev);
+ } else if (type & gx_path_type_fill) {
+ /* Solid fill */
+ write_str_to_current_page(xps, "<Path ");
+ /* NB - F0 should be changed for a different winding type */
+ fmt = "Fill=\"#%06X\" Data=\"M %g,%g V %g H %g V %g Z\" ";
+ c = xps->fillcolor & 0xffffffL;
+ gs_sprintf(line, fmt, c,
+ fixed2float(x0), fixed2float(y0),
+ fixed2float(y1), fixed2float(x1),
+ fixed2float(y0));
+ write_str_to_current_page(xps, line);
+ write_str_to_current_page(xps, "/>\n");
+ } else {
+ /* Solid stroke */
+ write_str_to_current_page(xps, "<Path ");
+ fmt = "Stroke=\"#%06X\" Data=\"M %g,%g V %g H %g V %g Z\" ";
+ c = xps->strokecolor & 0xffffffL;
+ gs_sprintf(line, fmt, c,
+ fixed2float(x0), fixed2float(y0),
+ fixed2float(y1), fixed2float(x1),
+ fixed2float(y0));
+ write_str_to_current_page(xps, line);
+
+ if (type & gx_path_type_stroke) {
+ /* NB format width. */
+ fmt = "StrokeThickness=\"%g\" ";
+ gs_sprintf(line, fmt, xps->linewidth);
+ write_str_to_current_page(xps, line);
+ }
+ write_str_to_current_page(xps, "/>\n");
+ }
+ /* end and close NB \n not necessary. */
+ return 0;
+}
+
+static int
+gdev_xps_fill_path(gx_device * dev, const gs_imager_state * pis, gx_path * ppath,
+ const gx_fill_params * params,
+ const gx_drawing_color * pdcolor, const gx_clip_path * pcpath)
+{
+ if (gx_path_is_void(ppath)) {
+ return 0;
+ }
+ return gdev_vector_fill_path(dev, pis, ppath, params, pdcolor, pcpath);
+}
+
+static int
+gdev_xps_stroke_path(gx_device * dev, const gs_imager_state * pis, gx_path * ppath,
+ const gx_stroke_params * params,
+ const gx_drawing_color * pdcolor, const gx_clip_path * pcpath)
+{
+ if (gx_path_is_void(ppath)) {
+ return 0;
+ }
+ return gdev_vector_stroke_path(dev, pis, ppath, params, pdcolor, pcpath);
+}
+
+static int
+xps_beginpath(gx_device_vector *vdev, gx_path_type_t type)
+{
+ char line[300];
+ gx_device_xps *xps = (gx_device_xps *)vdev;
+ uint32_t c;
+ const char *fmt;
+
+ (void)gdev_vector_stream((gx_device_vector*)xps);
+
+ /* skip non-drawing paths for now */
+ if (!drawing_path(type, xps->filltype)) {
+ if_debug1m('_', xps->memory, "xps_beginpath: type not supported %x\n", type);
+ return 0;
+ }
+
+ if (!xps->can_stroke) {
+ return_error(gs_error_rangecheck);
+ }
+
+ c = type & gx_path_type_fill ? xps->fillcolor : xps->strokecolor;
+ c &= 0xffffffL;
+
+ if (!image_brush_fill(type, xps->filltype)) {
+ write_str_to_current_page(xps, "<Path ");
+ if (type & gx_path_type_fill)
+ fmt = "Fill=\"#%06X\" Data=\"";
+ else
+ fmt = "Stroke=\"#%06X\" Data=\"";
+ gs_sprintf(line, fmt, c);
+ write_str_to_current_page(xps, line);
+ }
+ else {
+ write_str_to_current_page(xps, "<Path Data=\"");
+ }
+
+ if_debug1m('_', xps->memory, "xps_beginpath %s\n", line);
+
+ return 0;
+}
+
+static int
+xps_moveto(gx_device_vector *vdev, double x0, double y0,
+ double x, double y, gx_path_type_t type)
+{
+ gx_device_xps *xps = (gx_device_xps *)vdev;
+ char line[300];
+
+ if_debug2m('_', xps->memory, "xps_moveto %g %g\n", x, y);
+
+ /* skip non-drawing paths for now */
+ if (!drawing_path(type, xps->filltype)) {
+ if_debug1m('_', xps->memory, "xps_moveto: type not supported %x\n", type);
+ return 0;
+ }
+
+ gs_sprintf(line, " M %g,%g", x, y);
+ write_str_to_current_page(xps, line);
+ if_debug1m('_', xps->memory, "xps_moveto %s", line);
+ return 0;
+}
+
+static int
+xps_lineto(gx_device_vector *vdev, double x0, double y0,
+ double x, double y, gx_path_type_t type)
+{
+ gx_device_xps *xps = (gx_device_xps *)vdev;
+ char line[200];
+
+ if_debug2m('_', xps->memory, "xps_lineto %g %g\n", x, y);
+
+ /* skip non-drawing paths for now */
+ if (!drawing_path(type, xps->filltype)) {
+ if_debug1m('_', xps->memory, "xps_lineto: type not supported %x\n", type);
+ return 0;
+ }
+ gs_sprintf(line, " L %g,%g", x, y);
+ write_str_to_current_page(xps, line);
+ if_debug1m('_', xps->memory, "xps_lineto %s\n", line);
+ return 0;
+}
+
+static int
+xps_curveto(gx_device_vector *vdev, double x0, double y0,
+ double x1, double y1, double x2, double y2,
+ double x3, double y3, gx_path_type_t type)
+{
+ gx_device_xps *xps = (gx_device_xps *)vdev;
+ char line[200];
+
+ /* skip non-drawing paths for now */
+ if (!drawing_path(type, xps->filltype)) {
+ if_debug1m('_', xps->memory, "xps_curveto: type not supported %x\n", type);
+ return 0;
+ }
+
+ gs_sprintf(line, " C %g,%g %g,%g %g,%g", x1, y1,
+ x2,y2,x3,y3);
+ write_str_to_current_page(xps,line);
+ if_debug1m('_', xps->memory, "xps_curveto %s\n", line);
+
+ return 0;
+}
+
+static int
+xps_closepath(gx_device_vector *vdev, double x, double y,
+ double x_start, double y_start, gx_path_type_t type)
+{
+ gx_device_xps *xps = (gx_device_xps *)vdev;
+
+ /* skip non-drawing paths for now */
+ if (!drawing_path(type, xps->filltype)) {
+ if_debug1m('_', xps->memory, "xps_closepath: type not supported %x\n", type);
+ return 0;
+ }
+
+ write_str_to_current_page(xps, " Z");
+ if_debug0m('_', xps->memory, "xps_closepath");
+
+ return 0;
+}
+
+static int
+xps_endpath(gx_device_vector *vdev, gx_path_type_t type)
+{
+ gx_device_xps *xps = (gx_device_xps *)vdev;
+ char line[200];
+ const char *fmt;
+
+ /* skip non-drawing paths for now */
+ if (!drawing_path(type, xps->filltype)) {
+ if_debug1m('_', xps->memory, "xps_endpath: type not supported %x\n", type);
+ return 0;
+ }
+
+ if (image_brush_fill(type, xps->filltype)) {
+ write_str_to_current_page(xps, "\" >\n");
+ /* And now the rest of the details */
+ xps_finish_image_path(vdev);
+ } else if (type & gx_path_type_stroke) {
+ /* NB format width. */
+ fmt = "\" StrokeThickness=\"%g\" />\n";
+ gs_sprintf(line, fmt, xps->linewidth);
+ write_str_to_current_page(xps, line);
+ } else { /* fill */
+ /* close the path data attribute */
+ write_str_to_current_page(xps, "\" />\n");
+ }
+
+ return 0;
+}
+
+/* Image handling */
+static image_enum_proc_plane_data(xps_image_data);
+static image_enum_proc_end_image(xps_image_end_image);
+static const gx_image_enum_procs_t xps_image_enum_procs = {
+ xps_image_data, xps_image_end_image
+};
+
+/* High level image support */
+/* Prototypes */
+static TIFF* tiff_from_name(gx_device_xps *dev, const char *name, int big_endian,
+ bool usebigtiff);
+static int tiff_set_values(xps_image_enum_t *pie, TIFF *tif,
+ cmm_profile_t *profile, bool force8bit);
+static void xps_tiff_set_handlers(void);
+
+/* Check if we have the ICC profile in the package */
+static xps_icc_data_t*
+xps_find_icc(const gx_device_xps *xdev, cmm_profile_t *icc_profile)
+{
+ xps_icc_data_t *icc_data = xdev->icc_data;
+
+ while (icc_data != NULL) {
+ if (icc_data->hash == gsicc_get_hash(icc_profile)) {
+ return icc_data;
+ }
+ icc_data = icc_data->next;
+ }
+ return NULL;
+}
+
+static int
+xps_create_icc_name(const gx_device_xps *xps_dev, cmm_profile_t *profile, char *name)
+{
+ xps_icc_data_t *icc_data;
+
+ icc_data = xps_find_icc(xps_dev, profile);
+ if (icc_data == NULL)
+ return gs_throw_code(gs_error_rangecheck); /* Should be there */
+
+ snprintf(name, MAXNAME, "%sProfile_%d.icc", PROFILEPATH, icc_data->index);
+ return 0;
+}
+
+static void
+xps_create_image_name(gx_device *dev, char *name)
+{
+ gx_device_xps *const xdev = (gx_device_xps *)dev;
+
+ snprintf(name, MAXNAME, "%s%d.tif", IMAGEPATH, xdev->image_count);
+ xdev->image_count++;
+}
+
+static int
+xps_add_icc_relationship(xps_image_enum_t *pie)
+{
+ gx_device_xps *xps = (gx_device_xps*) (pie->dev);
+ int code;
+
+ code = add_new_relationship(xps, pie->icc_name);
+ if (code < 0)
+ return gs_rethrow_code(code);
+
+ return 0;
+}
+
+static int
+xps_add_image_relationship(xps_image_enum_t *pie)
+{
+ gx_device_xps *xps = (gx_device_xps*) (pie->dev);
+ int code;
+
+ code = add_new_relationship(xps, pie->file_name);
+ if (code < 0)
+ return gs_rethrow_code(code);
+ return 0;
+}
+
+static int
+xps_write_profile(const gs_imager_state *pis, char *name, cmm_profile_t *profile, gx_device_xps *xps_dev)
+{
+ byte *profile_buffer;
+ int size;
+
+ /* Need V2 ICC Profile */
+ profile_buffer = gsicc_create_getv2buffer(pis, profile, &size);
+
+ /* Now go ahead and add to the zip archive */
+ return add_data_to_zip_file(xps_dev, name, profile_buffer, size);
+}
+
+static int
+xps_begin_image(gx_device *dev, const gs_imager_state *pis,
+ const gs_image_t *pim, gs_image_format_t format,
+ const gs_int_rect *prect, const gx_drawing_color *pdcolor,
+ const gx_clip_path *pcpath, gs_memory_t *mem,
+ gx_image_enum_common_t **pinfo)
+{
+ gx_device_vector *vdev = (gx_device_vector *)dev;
+ gx_device_xps *xdev = (gx_device_xps *)dev;
+ gs_color_space *pcs = pim->ColorSpace;
+ xps_image_enum_t *pie = NULL;
+ xps_icc_data_t *icc_data;
+ gs_matrix mat;
+ int code;
+ gx_clip_path cpath;
+ gs_fixed_rect bbox;
+ int bits_per_pixel;
+ int num_components;
+ int bsize;
+ cmm_profile_t *icc_profile = NULL;
+ gs_color_space_index csindex;
+ float index_decode[2];
+ gsicc_rendering_param_t rendering_params;
+ bool force8bit = false;
+
+ /* No image mask yet. Also, need a color space */
+ if (pcs == NULL || ((const gs_image1_t *)pim)->ImageMask)
+ goto use_default;
+
+ /* No indexed images that are not 8 bit. */
+ csindex = gs_color_space_get_index(pcs);
+ if (csindex == gs_color_space_index_Indexed && pim->BitsPerComponent != 8)
+ goto use_default;
+
+ /* Also need imager state for these color spaces */
+ if (pis == NULL && (csindex == gs_color_space_index_Indexed ||
+ csindex == gs_color_space_index_Separation ||
+ csindex == gs_color_space_index_DeviceN))
+ goto use_default;
+
+ gs_matrix_invert(&pim->ImageMatrix, &mat);
+ gs_matrix_multiply(&mat, &ctm_only(pis), &mat);
+
+ pie = gs_alloc_struct(mem, xps_image_enum_t, &st_xps_image_enum,
+ "xps_begin_image");
+ if (pie == 0)
+ return_error(gs_error_VMerror);
+ pie->buffer = NULL;
+ pie->devc_buffer = NULL;
+ pie->pis = NULL;
+
+ /* Set the brush types to image */
+ xps_setstrokebrush(xdev, xps_imagebrush);
+ xps_setfillbrush(xdev, xps_imagebrush);
+ pie->mat = mat;
+ xdev->xps_pie = pie;
+ /* We need this set a bit early for the ICC relationship writing */
+ pie->dev = (gx_device*) xdev;
+
+ /* If the color space is DeviceN, Sep or indexed these end up getting
+ mapped to the color space defined by the device profile. XPS only
+ support RGB indexed images so we just expand if for now. ICC link
+ creation etc is handled during the remap/concretization of the colors */
+ if (csindex == gs_color_space_index_Indexed ||
+ csindex == gs_color_space_index_Separation ||
+ csindex == gs_color_space_index_DeviceN) {
+ cmm_dev_profile_t *dev_profile;
+ pie->pcs = pcs;
+ rc_increment(pcs);
+ code = dev_proc(dev, get_profile)(dev, &(dev_profile));
+ /* Just use the "default" profile for now */
+ icc_profile = dev_profile->device_profile[0];
+ force8bit = true; /* Output image is 8 bit regardless of source */
+ } else {
+ /* An ICC, RGB, CMYK, Gray color space */
+ pie->pcs = NULL;
+ /* Get the ICC profile */
+ if (gs_color_space_is_PSCIE(pcs)) {
+ if (pcs->icc_equivalent == NULL) {
+ bool is_lab;
+ gs_colorspace_set_icc_equivalent(pcs, &is_lab, pis->memory);
+ }
+ icc_profile = pcs->icc_equivalent->cmm_icc_profile_data;
+ } else {
+ icc_profile = pcs->cmm_icc_profile_data;
+ }
+ }
+
+ /* Set up for handling case where we are in CIELAB. In this case, we are
+ going out to the default RGB color space */
+ if (icc_profile->islab) {
+ /* Create the link */
+ rendering_params.black_point_comp = gsBLACKPTCOMP_ON;
+ rendering_params.graphics_type_tag = GS_IMAGE_TAG;
+ rendering_params.override_icc = false;
+ rendering_params.preserve_black = gsBKPRESNOTSPECIFIED;
+ rendering_params.rendering_intent = gsPERCEPTUAL;
+ rendering_params.cmm = gsCMM_DEFAULT;
+ pie->icc_link = gsicc_get_link_profile(pis, dev, icc_profile,
+ pis->icc_manager->default_rgb, &rendering_params, pis->memory, false);
+ icc_profile = pis->icc_manager->default_rgb;
+ } else {
+ pie->icc_link = NULL;
+ }
+
+ /* Now we actually write out the image and icc profile data to the zip
+ package. Test if profile is already here. If not, add it. */
+ if (xps_find_icc(xdev, icc_profile) == NULL) {
+ icc_data = (xps_icc_data_t*)gs_alloc_bytes(dev->memory->non_gc_memory,
+ sizeof(xps_icc_data_t), "xps_begin_image");
+ if (icc_data == NULL)
+ gs_throw(gs_error_VMerror, "Allocation of icc_data failed");
+
+ icc_data->hash = gsicc_get_hash(icc_profile);
+ if (xdev->icc_data == NULL) {
+ icc_data->index = 0;
+ xdev->icc_data = icc_data;
+ xdev->icc_data->next = NULL;
+ } else {
+ icc_data->next = xdev->icc_data;
+ icc_data->index = icc_data->next->index + 1;
+ xdev->icc_data = icc_data;
+ }
+
+ /* Get name for mark up and for relationship. Have to wait and do
+ this after it is added to the package */
+ code = xps_create_icc_name(xdev, icc_profile, &(pie->icc_name[0]));
+ if (code < 0)
+ return gs_rethrow_code(code);
+
+ /* Add profile to the package. Here like images we are going to write
+ the data now. Rather than later. */
+ code = xps_write_profile(pis, &(pie->icc_name[0]), icc_profile, xdev);
+ if (code < 0)
+ return gs_rethrow_code(code);
+
+ /* Add ICC relationship */
+ xps_add_icc_relationship(pie);
+ } else {
+ /* Get name for mark up. We already have it in the relationship and list */
+ code = xps_create_icc_name(xdev, icc_profile, &(pie->icc_name[0]));
+ if (code < 0)
+ return gs_rethrow_code(code);
+ }
+
+ /* Get image name for mark up */
+ xps_create_image_name(dev, &(pie->file_name[0]));
+ /* Set width and height here */
+ pie->width = pim->Width;
+ pie->height = pim->Height;
+
+ if (pcpath == NULL) {
+ (*dev_proc(dev, get_clipping_box)) (dev, &bbox);
+ gx_cpath_init_local(&cpath, dev->memory);
+ code = gx_cpath_from_rectangle(&cpath, &bbox);
+ pcpath = &cpath;
+ } else {
+ /* Force vector device to do new path as the clip path is the image
+ path. I had a case where the clip path ids were the same but the
+ CTM was changing which resulted in subsequent images coming up
+ missing on the page. i.e. only the first one was shown. */
+ ((gx_device_vector*) vdev)->clip_path_id = vdev->no_clip_path_id;
+ }
+
+ code = gdev_vector_begin_image(vdev, pis, pim, format, prect,
+ pdcolor, pcpath, mem, &xps_image_enum_procs,
+ (gdev_vector_image_enum_t *)pie);
+ if (code < 0)
+ return code;
+
+ if ((pie->tif = tiff_from_name(xdev, pie->file_name, false, false)) == NULL)
+ return_error(gs_error_VMerror);
+
+ /* Null out pie. Only needed for the above vector command and tiff set up */
+ xdev->xps_pie = NULL;
+ xps_tiff_set_handlers();
+ code = tiff_set_values(pie, pie->tif, icc_profile, force8bit);
+ if (code < 0)
+ return gs_rethrow_code(code);
+ code = TIFFCheckpointDirectory(pie->tif);
+
+ num_components = gs_color_space_num_components(pcs);
+ bits_per_pixel = pim->BitsPerComponent * num_components;
+ pie->decode_st.bps = bits_per_pixel / num_components;
+ pie->bytes_comp = (pie->decode_st.bps > 8 ? 2 : 1);
+ pie->decode_st.spp = num_components;
+ pie->decode_st.unpack = NULL;
+ get_unpack_proc((gx_image_enum_common_t*)pie, &(pie->decode_st), pim->format,
+ pim->Decode);
+
+ /* The decode mapping for index colors needs an adjustment */
+ if (csindex == gs_color_space_index_Indexed) {
+ if (pim->Decode[0] == 0 &&
+ pim->Decode[1] == 255) {
+ index_decode[0] = 0;
+ index_decode[1] = 1.0;
+ } else {
+ index_decode[0] = pim->Decode[0];
+ index_decode[1] = pim->Decode[1];
+ }
+ get_map(&(pie->decode_st), pim->format, index_decode);
+ } else {
+ get_map(&(pie->decode_st), pim->format, pim->Decode);
+ }
+
+ /* Allocate our decode buffer. */
+ bsize = ((pie->decode_st.bps > 8 ? (pim->Width) * 2 : pim->Width) + 15) * num_components;
+ pie->buffer = gs_alloc_bytes(mem, bsize, "xps_begin_typed_image(buffer)");
+ if (pie->buffer == 0) {
+ gs_free_object(mem, pie, "xps_begin_typed_image");
+ *pinfo = NULL;
+ return_error(gs_error_VMerror);
+ }
+
+ /* If needed, allocate our device color buffer. We will always do 8 bit here */
+ if (csindex == gs_color_space_index_Indexed ||
+ csindex == gs_color_space_index_Separation ||
+ csindex == gs_color_space_index_DeviceN) {
+ bsize = (pim->Width + 15) * icc_profile->num_comps;
+ pie->devc_buffer = gs_alloc_bytes(mem, bsize, "xps_begin_typed_image(devc_buffer)");
+ if (pie->devc_buffer == 0) {
+ gs_free_object(mem, pie, "xps_begin_typed_image");
+ *pinfo = NULL;
+ return_error(gs_error_VMerror);
+ }
+ /* Also, the color remaps need the imager state */
+ pie->pis = pis;
+ }
+
+ *pinfo = (gx_image_enum_common_t *)pie;
+ return 0;
+use_default:
+ if (pie != NULL && pie->buffer != NULL)
+ gs_free_object(mem, pie->buffer, "xps_begin_image");
+ if (pie != NULL && pie->devc_buffer != NULL)
+ gs_free_object(mem, pie->devc_buffer, "xps_begin_image");
+ if (pie != NULL)
+ gs_free_object(mem, pie, "xps_begin_image");
+
+ return gx_default_begin_image(dev, pis, pim, format, prect,
+ pdcolor, pcpath, mem, pinfo);
+}
+
+/* Handles conversion from decoded DeviceN, Sep or Indexed space to Device color
+ space. The encoding specified by the image object is already handled at this
+ point. Slow due to the multitude of conversions that take place. I.e. conv to
+ float, frac and back to byte, but it will get the job done. Since we can have
+ indexed spaces that reference DeviceN spaces etc, we really have to do it
+ this way or code up a bunch of optimized special cases. Note here we always
+ output 8 bit regardless of input */
+static int
+set_device_colors(xps_image_enum_t *pie)
+{
+ gx_device *pdev = pie->dev;
+ const gs_imager_state *pis = pie->pis;
+ gs_color_space *pcs = pie->pcs;
+ byte *src = pie->buffer;
+ byte *des = pie->devc_buffer;
+ int num_src = gs_color_space_num_components(pcs);
+ int num_des = pdev->color_info.num_components;
+ int width = pie->width;
+ cs_proc_remap_color((*remap_color)) = pcs->type->remap_color;
+ int i, j, code = 0;
+ gs_client_color cc;
+ gx_device_color devc;
+ gx_color_value cm_values[GX_DEVICE_COLOR_MAX_COMPONENTS];
+ float scale = 1.0;
+
+ if (pie->decode_st.bps > 8) {
+ unsigned short *src_ptr = (unsigned short*)src;
+ int pos_src = 0;
+ int pos_des = 0;
+ for (i = 0; i < width; i++) {
+ for (j = 0; j < num_src; j++, pos_src++) {
+ cc.paint.values[j] = (float)(src_ptr[pos_src]) / 65535.0;
+ }
+ code = remap_color(&cc, pcs, &devc, pis, pdev, gs_color_select_source);
+ dev_proc(pdev, decode_color)(pdev, devc.colors.pure, cm_values);
+ for (j = 0; j < num_des; j++, pos_des++) {
+ des[pos_des] = (cm_values[j] >> 8);
+ }
+ }
+ } else {
+ int pos_src = 0;
+ int pos_des = 0;
+ if (gs_color_space_get_index(pcs) != gs_color_space_index_Indexed)
+ scale = 255.0;
+ for (i = 0; i < width; i++) {
+ for (j = 0; j < num_src; j++, pos_src++) {
+ cc.paint.values[j] = (float)(src[pos_src]) / scale;
+ }
+ code = remap_color(&cc, pcs, &devc, pis, pdev, gs_color_select_source);
+ dev_proc(pdev, decode_color)(pdev, devc.colors.pure, cm_values);
+ for (j = 0; j < num_des; j++, pos_des++) {
+ des[pos_des] = (cm_values[j] >> 8);
+ }
+ }
+ }
+ return code;
+}
+
+/* Chunky or planar in and chunky out */
+static int
+xps_image_data(gx_image_enum_common_t *info,
+const gx_image_plane_t *planes, int height, int *rows_used)
+{
+ xps_image_enum_t *pie = (xps_image_enum_t *)info;
+ int data_bit = planes[0].data_x * info->plane_depths[0];
+ int width_bits = pie->width * info->plane_depths[0];
+ int bytes_comp = pie->bytes_comp;
+ int i, plane;
+ int code = 0;
+ int width = pie->width;
+ int num_planes = pie->num_planes;
+ int dsize = (((width + (planes[0]).data_x) * pie->decode_st.spp *
+ pie->decode_st.bps / num_planes + 7) >> 3);
+ void *bufend = (void*)(pie->buffer + width * bytes_comp * pie->decode_st.spp);
+ byte *outbuffer;
+
+ if (width_bits != pie->bits_per_row || (data_bit & 7) != 0)
+ return_error(gs_error_rangecheck);
+ if (height > pie->height - pie->y)
+ height = pie->height - pie->y;
+
+ for (i = 0; i < height; pie->y++, i++) {
+ int pdata_x;
+ /* Plane zero done here to get the pointer to the data */
+ const byte *data_ptr = planes[0].data + planes[0].raster * i + (data_bit >> 3);
+ byte *des_ptr = pie->buffer;
+ byte *buffer = (byte *)(*pie->decode_st.unpack)(des_ptr, &pdata_x,
+ data_ptr, 0, dsize, &(pie->decode_st.map[0]),
+ pie->decode_st.spread, pie->decode_st.spp);
+
+ /* Step through the planes having decode do the repack to chunky as
+ well as any decoding needed */
+ for (plane = 1; plane < num_planes; plane++) {
+ data_ptr = planes[plane].data + planes[plane].raster * i + (data_bit >> 3);
+ des_ptr = pie->buffer + plane * pie->bytes_comp;
+ /* This does the planar to chunky conversion */
+ (*pie->decode_st.unpack)(des_ptr, &pdata_x,
+ data_ptr, 0, dsize, &(pie->decode_st.map[plane]),
+ pie->decode_st.spread, pie->decode_st.spp);
+ }
+
+ /* CIELAB does not get mapped. Handled in color management */
+ if (pie->icc_link == NULL) {
+ pie->decode_st.applymap(pie->decode_st.map, (void*)buffer,
+ pie->decode_st.spp, (void*)pie->buffer, bufend);
+ /* Index, Sep and DeviceN are mapped to color space defined by
+ device profile */
+ if (pie->pcs != NULL) {
+ /* In device color space */
+ code = set_device_colors(pie);
+ if (code < 0)
+ return gs_rethrow_code(code);
+ outbuffer = pie->devc_buffer;
+ } else {
+ /* In source color space */
+ outbuffer = pie->buffer;
+ }
+ } else {
+ /* CIELAB to default RGB */
+ gsicc_bufferdesc_t input_buff_desc;
+ gsicc_bufferdesc_t output_buff_desc;
+ gsicc_init_buffer(&input_buff_desc, 3, bytes_comp,
+ false, false, false, 0, width * bytes_comp * 3,
+ 1, width);
+ gsicc_init_buffer(&output_buff_desc, 3, bytes_comp,
+ false, false, false, 0, width * bytes_comp * 3,
+ 1, width);
+ (pie->icc_link->procs.map_buffer)(pie->dev, pie->icc_link,
+ &input_buff_desc, &output_buff_desc, (void*)buffer,
+ (void*)pie->buffer);
+ outbuffer = pie->buffer;
+ }
+ code = TIFFWriteScanline(pie->tif, outbuffer, pie->y, 0);
+ if (code < 0)
+ return code;
+ }
+ *rows_used = height;
+ return pie->y >= pie->height;
+}
+
+static int
+xps_add_tiff_image(xps_image_enum_t *pie)
+{
+ gx_device_xps *xdev = (gx_device_xps *)(pie->dev);
+ int code;
+
+ code = add_file_to_zip_file(xdev, pie->file_name, pie->fid);
+ fclose(pie->fid);
+ return code;
+}
+
+/* Clean up by releasing the buffers. */
+static int
+xps_image_end_image(gx_image_enum_common_t * info, bool draw_last)
+{
+ xps_image_enum_t *pie = (xps_image_enum_t *)info;
+ int code = 0;
+
+ /* N.B. Write the final strip, if any. */
+
+ code = TIFFWriteDirectory(pie->tif);
+ TIFFCleanup(pie->tif);
+
+ /* Stuff the image into the zip archive and close the file */
+ code = xps_add_tiff_image(pie);
+ if (code < 0)
+ goto exit;
+
+ /* Reset the brush type to solid */
+ xps_setstrokebrush((gx_device_xps *) (pie->dev), xps_solidbrush);
+ xps_setfillbrush((gx_device_xps *) (pie->dev), xps_solidbrush);
+
+ /* Add the image relationship */
+ code = xps_add_image_relationship(pie);
+
+exit:
+ if (pie->pcs != NULL)
+ rc_decrement(pie->pcs, "xps_image_end_image (pcs)");
+ if (pie->buffer != NULL)
+ gs_free_object(pie->memory, pie->buffer, "xps_image_end_image");
+ if (pie->devc_buffer != NULL)
+ gs_free_object(pie->memory, pie->devc_buffer, "xps_image_end_image");
+
+ /* ICC clean up */
+ if (pie->icc_link != NULL)
+ gsicc_release_link(pie->icc_link);
+
+ return code;
+}
+
+/* Tiff related code so that we can output all the image data in Tiff format.
+ Tiff has the advantage of supporting Gray, RGB, CMYK as well as multiple
+ bit depts and having lossless compression. Much of this was borrowed from
+ gstiffio.c */
+#define TIFF_PRINT_BUF_LENGTH 1024
+static const char tifs_msg_truncated[] = "\n*** Previous line has been truncated.\n";
+
+static int
+tiff_set_values(xps_image_enum_t *pie, TIFF *tif, cmm_profile_t *profile,
+ bool force8bit)
+{
+ int bits = 8;
+
+ TIFFSetField(tif, TIFFTAG_ROWSPERSTRIP, pie->height);
+ TIFFSetField(tif, TIFFTAG_IMAGEWIDTH, pie->width);
+ TIFFSetField(tif, TIFFTAG_IMAGELENGTH, pie->height);
+ TIFFSetField(tif, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT);
+ TIFFSetField(tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
+ TIFFSetField(tif, TIFFTAG_COMPRESSION, COMPRESSION_LZW);
+ TIFFSetField(tif, TIFFTAG_FILLORDER, FILLORDER_MSB2LSB);
+ TIFFSetField(tif, TIFFTAG_XRESOLUTION, 96.0);
+ TIFFSetField(tif, TIFFTAG_YRESOLUTION, 96.0);
+
+ switch (profile->data_cs) {
+ case gsGRAY:
+ if (pie->bits_per_pixel > 8 && !force8bit)
+ bits = 16;
+ TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, bits);
+ TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK);
+ TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, 1);
+ break;
+ case gsRGB:
+ case gsCIELAB:
+ if ((pie->num_planes > 1 && pie->bits_per_pixel > 8 && !force8bit) ||
+ (pie->num_planes == 1 && pie->bits_per_pixel / 3 > 8 && !force8bit))
+ bits = 16;
+ TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, bits);
+ TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB);
+ TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, 3);
+ break;
+ case gsCMYK:
+ if ((pie->num_planes > 1 && pie->bits_per_pixel > 8 && !force8bit) ||
+ (pie->num_planes == 1 && pie->bits_per_pixel / 4 > 8 && !force8bit))
+ bits = 16;
+ TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, bits);
+ TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_SEPARATED);
+ TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, 4);
+ break;
+ default:
+ return gs_throw_code(gs_error_rangecheck);
+ }
+ return 0;
+}
+
+/* place to hold the data for our libtiff i/o hooks */
+typedef struct tifs_io_xps_t
+{
+ gx_device_xps *pdev;
+ FILE *fid;
+} tifs_io_xps;
+
+/* libtiff i/o hooks */
+static size_t
+xps_tifsWriteProc(thandle_t fd, void* buf, size_t size)
+{
+ tifs_io_xps *tiffio = (tifs_io_xps *)fd;
+ size_t size_io = (size_t)size;
+ FILE *fid = tiffio->fid;
+ size_t count;
+
+ if ((size_t)size_io != size) {
+ return (size_t)-1;
+ }
+
+ if (fid == NULL)
+ return gs_throw_code(gs_error_Fatal);
+
+ count = fwrite(buf, 1, size, fid);
+ if (count != size) {
+ fclose(fid);
+ return gs_rethrow_code(-1);
+ }
+ fflush(fid);
+ return size;
+}
+
+static uint64_t
+xps_tifsSeekProc(thandle_t fd, uint64_t off, int origin)
+{
+ tifs_io_xps *tiffio = (tifs_io_xps *)fd;
+ gs_offset_t off_io = (gs_offset_t)off;
+ FILE *fid = tiffio->fid;
+
+ if ((uint64_t)off_io != off) {
+ return (uint64_t)-1;
+ }
+
+ if (fid == NULL && off == 0)
+ return 0;
+
+ if (fid == NULL)
+ return (uint64_t)-1;
+
+ if (gp_fseek_64(fid, (gs_offset_t)off, origin) < 0) {
+ return (uint64_t)-1;
+ }
+ return (gp_ftell_64(fid));
+}
+
+static int
+xps_tifsCloseProc(thandle_t fd)
+{
+ tifs_io_xps *tiffio = (tifs_io_xps *)fd;
+ gx_device_xps *pdev = tiffio->pdev;
+
+ gs_free(pdev->memory->non_gc_memory, tiffio, sizeof(tifs_io_xps), 1,
+ "xps_tifsCloseProc");
+ return 0;
+}
+
+static int
+xps_tifsDummyMapProc(thandle_t fd, void** pbase, toff_t* psize)
+{
+ (void)fd;
+ (void)pbase;
+ (void)psize;
+ return (0);
+}
+
+static void
+xps_tifsDummyUnmapProc(thandle_t fd, void* base, toff_t size)
+{
+ (void)fd;
+ (void)base;
+ (void)size;
+}
+
+static size_t
+xps_tifsReadProc(thandle_t fd, void* buf, size_t size)
+{
+ size_t size_io = (size_t)size;
+
+ if ((size_t)size_io != size) {
+ return (size_t)-1;
+ }
+ return 0;
+}
+
+/* Could not see where this was getting used so basically a dummy proc
+ for now. */
+static uint64_t
+xps_tifsSizeProc(thandle_t fd)
+{
+ uint64_t length = 0;
+
+ return length;
+}
+
+static void
+xps_tifsWarningHandlerEx(thandle_t client_data, const char *module,
+ const char *fmt, va_list ap)
+{
+ tifs_io_xps *tiffio = (tifs_io_xps *)client_data;
+ gx_device_xps *pdev = tiffio->pdev;
+ int count;
+ char buf[TIFF_PRINT_BUF_LENGTH];
+
+ count = vsnprintf(buf, sizeof(buf), fmt, ap);
+ if (count >= sizeof(buf) || count < 0) { /* C99 || MSVC */
+ dmlprintf1(pdev->memory, "%s", buf);
+ dmlprintf1(pdev->memory, "%s\n", tifs_msg_truncated);
+ }
+ else {
+ dmlprintf1(pdev->memory, "%s\n", buf);
+ }
+}
+
+static void
+xps_tifsErrorHandlerEx(thandle_t client_data, const char *module,
+ const char *fmt, va_list ap)
+{
+ tifs_io_xps *tiffio = (tifs_io_xps *)client_data;
+ gx_device_xps *pdev = tiffio->pdev;
+ const char *max_size_error = "Maximum TIFF file size exceeded";
+ int count;
+ char buf[TIFF_PRINT_BUF_LENGTH];
+
+ count = vsnprintf(buf, sizeof(buf), fmt, ap);
+ if (count >= sizeof(buf) || count < 0) { /* C99 || MSVC */
+ dmlprintf1(pdev->memory, "%s\n", buf);
+ dmlprintf1(pdev->memory, "%s", tifs_msg_truncated);
+ }
+ else {
+ dmlprintf1(pdev->memory, "%s\n", buf);
+ }
+
+#if (TIFFLIB_VERSION >= 20111221)
+ if (!strncmp(fmt, max_size_error, strlen(max_size_error))) {
+ dmlprintf(pdev->memory, "Use -dUseBigTIFF(=true) for BigTIFF output\n");
+ }
+#endif
+}
+
+static void
+xps_tiff_set_handlers(void)
+{
+ (void)TIFFSetErrorHandler(NULL);
+ (void)TIFFSetWarningHandler(NULL);
+ (void)TIFFSetErrorHandlerExt(xps_tifsErrorHandlerEx);
+ (void)TIFFSetWarningHandlerExt(xps_tifsWarningHandlerEx);
+}
+
+static TIFF *
+tiff_from_name(gx_device_xps *dev, const char *name, int big_endian, bool usebigtiff)
+{
+ char mode[5] = "w";
+ int modelen = 1;
+ TIFF *t;
+ tifs_io_xps *tiffio;
+ gs_memory_t *mem = dev->memory->non_gc_memory;
+ char *filename;
+
+ if (big_endian)
+ mode[modelen++] = 'b';
+ else
+ mode[modelen++] = 'l';
+
+ if (usebigtiff)
+ /* this should never happen for libtiff < 4.0 - see tiff_put_some_params() */
+ mode[modelen++] = '8';
+
+ mode[modelen] = (char)0;
+
+ tiffio = (tifs_io_xps *)gs_malloc(dev->memory->non_gc_memory,
+ sizeof(tifs_io_xps), 1, "tiff_from_name");
+ if (!tiffio) {
+ return NULL;
+ }
+ tiffio->pdev = dev;
+
+ filename = (char *)gs_alloc_bytes(mem, gp_file_name_sizeof,
+ "tiff_from_name(filename)");
+ if (!filename)
+ return NULL;
+
+ tiffio->fid = gp_open_scratch_file_rm(mem, "tif-", filename, "wb+");
+ dev->xps_pie->fid = tiffio->fid; /* We will be closing it from here */
+
+ gs_free_object(mem, filename, "tiff_from_name(filename)");
+
+ t = TIFFClientOpen(name, mode,
+ (thandle_t)tiffio, (TIFFReadWriteProc)xps_tifsReadProc,
+ (TIFFReadWriteProc)xps_tifsWriteProc, (TIFFSeekProc)xps_tifsSeekProc,
+ xps_tifsCloseProc, (TIFFSizeProc)xps_tifsSizeProc, xps_tifsDummyMapProc,
+ xps_tifsDummyUnmapProc);
+ return t;
+}
diff --git a/devices/vector/opdfread.h b/devices/vector/opdfread.h
new file mode 100644
index 000000000..f112efed8
--- /dev/null
+++ b/devices/vector/opdfread.h
@@ -0,0 +1,3964 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* $Id: opdfread.h 11951 2010-12-15 08:22:58Z ken */
+/* opdfread.ps - A procset for interpreting an ordered PDF 1.3 file. */
+/* Originally stored in gs/Resource/Init as opdfread.ps and built into
+ * the ROM file system, this file was referenced by ps2write when
+ * writing PostScript output. With the inclusion of ps2write in the
+ * PCL and XPS interpreters we can no longer rely on the PostScript
+ * resources being present, and so this file has been converted to
+ * 'C' and included as a header. The original file is now stored
+ * in gs/lib with the comments intact.
+ */
+const char *opdfread_ps [] = {
+"currentdict/DSC_OPDFREAD known{\n",
+"currentdict/DSC_OPDFREAD get\n",
+"}{\n",
+"false\n",
+"}ifelse\n",
+"10 dict begin\n",
+"/DSC_OPDFREAD exch def\n",
+"/this currentdict def\n",
+"/y 720 def\n",
+"/ebuf 200 string def\n",
+"/prnt{\n",
+"36//this/y get moveto//ebuf cvs show\n",
+"//this/y 2 copy get 12 sub put\n",
+"}bind def\n",
+"/newline{\n",
+"36//this/y get moveto\n",
+"//this/y 2 copy get 12 sub put\n",
+"}bind def\n",
+"errordict/handleerror\n",
+"{systemdict begin\n",
+"$error begin\n",
+"newerror\n",
+"{(%%[ Error handled by opdfread.ps : )print errorname//ebuf cvs print(; OffendingCommand: )\n",
+"print/command load//ebuf cvs print( ]%%)= flush\n",
+"/newerror false store vmstatus pop pop 0 ne\n",
+"{grestoreall\n",
+"}if\n",
+"errorname(VMerror)ne\n",
+"{showpage\n",
+"}if\n",
+"initgraphics\n",
+"0 720 moveto\n",
+"errorname(VMerror)eq\n",
+"{//this/ehsave known\n",
+"{clear//this/ehsave get restore 2 vmreclaim\n",
+"}if\n",
+"vmstatus exch pop exch pop\n",
+"}\n",
+"/Courier 12 selectfont\n",
+"{\n",
+"(ERROR: )//prnt exec errorname//prnt exec\n",
+"(OFFENDING COMMAND: )//prnt exec\n",
+"/command load//prnt exec\n",
+"$error/ostack known{\n",
+"(%%[STACK:)=\n",
+"(STACK:)//prnt exec\n",
+"$error/ostack get aload length{\n",
+"//newline exec\n",
+"dup mark eq{\n",
+"(-mark-)dup = show\n",
+"}{\n",
+"dup type/nametype eq{\n",
+"dup xcheck not{\n",
+"(/)show\n",
+"(/)print\n",
+"}if\n",
+"}if\n",
+"dup =//ebuf cvs show\n",
+"}ifelse\n",
+"}repeat\n",
+"}if\n",
+"}ifelse\n",
+"(%%]%)=\n",
+"//systemdict/showpage get exec\n",
+"quit\n",
+"}if\n",
+"end\n",
+"end\n",
+"}bind readonly put\n",
+"end\n",
+"50 dict begin\n",
+"/DefaultSwitch\n",
+"{\n",
+"dup where{\n",
+"pop pop\n",
+"}{\n",
+"false def\n",
+"}ifelse\n",
+"}bind def\n",
+"/=string 256 string def\n",
+"/=only{\n",
+"//=string cvs print\n",
+"}bind def\n",
+"/HexDigits(0123456789ABCDEF)readonly def\n",
+"/PrintHex\n",
+"{8{\n",
+"dup -28 bitshift 15 and//HexDigits exch 1 getinterval//=only exec\n",
+"4 bitshift\n",
+"}repeat\n",
+"pop\n",
+"}bind def\n",
+"/PDFR_DEBUG DefaultSwitch\n",
+"/PDFR_DUMP DefaultSwitch\n",
+"/PDFR_STREAM DefaultSwitch\n",
+"/TTFDEBUG DefaultSwitch\n",
+"/RotatePages DefaultSwitch\n",
+"/FitPages DefaultSwitch\n",
+"/CenterPages DefaultSwitch\n",
+"/SetPageSize DefaultSwitch\n",
+"/error\n",
+"{\n",
+"counttomark 1 sub -1 0{\n",
+"index dup type/arraytype eq{==}{=only}ifelse\n",
+"}for\n",
+"()=\n",
+"cleartomark\n",
+"....Undefined\n",
+"}bind def\n",
+"//SetPageSize{\n",
+"//RotatePages//FitPages or//CenterPages or{\n",
+"mark(/RotatePages, /FitPages and CenterPages are not allowed with /SetPageSize)//error exec\n",
+"}if\n",
+"}\n",
+"{\n",
+"//FitPages//CenterPages and{\n",
+"mark(CenterPages is not allowed with /FitPages)//error exec\n",
+"}if\n",
+"}\n",
+"ifelse\n",
+"/knownget\n",
+"{\n",
+"2 copy known{\n",
+"get true\n",
+"}{\n",
+"pop pop false\n",
+"}ifelse\n",
+"}bind def\n",
+"/IsUpper\n",
+"{dup(A)0 get ge exch(Z)0 get le and\n",
+"}bind def\n",
+"/cpa2g{\n",
+"dup length array\n",
+"0 1 2 index length 1 sub{\n",
+"dup 3 index exch get cp2g\n",
+"3 copy put pop pop\n",
+"}for\n",
+"exch pop\n",
+"}bind def\n",
+"/cpd2g{\n",
+"dup length dict exch{\n",
+"cp2g 2 index 3 1 roll put\n",
+"}forall\n",
+"}bind def\n",
+"/cps2g{\n",
+"dup length string copy\n",
+"}bind def\n",
+"/cp2gprocs\n",
+"<</arraytype//cpa2g/dicttype//cpd2g/packedarraytype//cpa2g/stringtype//cps2g >>\n",
+"def\n",
+"/cp2g{\n",
+"dup gcheck not{\n",
+"dup//cp2gprocs 1 index type\n",
+"2 copy known{\n",
+"get currentglobal 3 1 roll true setglobal exec exch setglobal\n",
+"1 index wcheck not{readonly}if\n",
+"1 index xcheck{cvx}if\n",
+"exch pop\n",
+"}{\n",
+"pop pop\n",
+"}ifelse\n",
+"}if\n",
+"}bind def\n",
+"/BlockBuffer 65535 string def\n",
+"/PDFReader currentdict def\n",
+"/ObjectRegistryMaxLength 50000 def\n",
+"/ObjectRegistry 10 dict def\n",
+"ObjectRegistry\n",
+"begin 0 ObjectRegistryMaxLength dict def end\n",
+"/CurrentObject null def\n",
+"/DoneDocumentStructure false def\n",
+"/GraphicState 20 dict begin\n",
+"/InitialTextMatrix matrix def\n",
+"/InitialMatrix matrix currentmatrix def\n",
+"currentdict end def\n",
+"/TempMatrix matrix def\n",
+"/GraphicStateStack 20 array def\n",
+"/GraphicStateStackPointer 0 def\n",
+"/InitialTextMatrixStack 20 array def\n",
+"/InitialTextMatrixStackPointer 0 def\n",
+"/PDFColorSpaces 50 dict def\n",
+"/InstalledFonts 50 dict def\n",
+"/MacRomanEncodingInverse null def\n",
+"currentglobal false setglobal\n",
+"userdict/PDFR_InitialGS gstate put\n",
+"userdict/PDFR_Patterns 50 dict put\n",
+"userdict/FuncDataReader 10 dict put\n",
+"setglobal\n",
+"/InitialExtGState 20 dict begin\n",
+"/BG2 currentblackgeneration cp2g def\n",
+"/UCR2 currentundercolorremoval cp2g def\n",
+"/TR2 currentglobal false setglobal[currentcolortransfer]exch setglobal cp2g def\n",
+"/HT currenthalftone cp2g def\n",
+"currentdict end readonly def\n",
+"/InitialGraphicState 20 dict begin\n",
+"/FontSize 0 def\n",
+"/CharacterSpacing 0 def\n",
+"/TextLeading 0 def\n",
+"/TextRenderingMode 0 def\n",
+"/WordSpacing 0 def\n",
+"currentdict end readonly def\n",
+"/SimpleColorSpaceNames 15 dict begin\n",
+"/DeviceGray true def\n",
+"/DeviceRGB true def\n",
+"/DeviceCMYK true def\n",
+"currentdict end readonly def\n",
+"/1_24_bitshift_1_sub 1 24 bitshift 1 sub def\n",
+"/ReadFontProcs 10 dict def\n",
+"/GetObject{\n",
+"dup ObjectRegistryMaxLength idiv\n",
+"//PDFReader /ObjectRegistry get exch knownget\n",
+"{exch knownget}{pop false}ifelse\n",
+"}bind def\n",
+"/PutObject\n",
+"{1 index ObjectRegistryMaxLength idiv\n",
+"//PDFReader /ObjectRegistry get 1 index knownget{\n",
+"exch pop 3 1 roll put}{\n",
+"//PDFReader /ObjectRegistry get dup begin\n",
+"1 index ObjectRegistryMaxLength dict def\n",
+"end exch get 3 1 roll put\n",
+"}ifelse\n",
+"}bind def\n",
+"/Register\n",
+"{1 index GetObject{\n",
+"dup xcheck{\n",
+"4 3 roll pop\n",
+"//PDFR_DEBUG{\n",
+"(Have a daemon for ) print 2 index ==\n",
+"}if\n",
+"exec\n",
+"}{\n",
+"dup null ne{\n",
+"mark (The object ) 4 index (is already defined : ) 4 index //error exec\n",
+"}{\n",
+"pop\n",
+"}ifelse\n",
+"3 2 roll\n",
+"exec\n",
+"}ifelse\n",
+"}{\n",
+"3 2 roll\n",
+"exec\n",
+"}ifelse\n",
+"PutObject\n",
+"} bind def\n",
+"/IsRegistered{\n",
+"GetObject{\n",
+"null ne\n",
+"}{\n",
+"false\n",
+"}ifelse\n",
+"}bind def\n",
+"/GetRegistered{\n",
+"dup GetObject not{\n",
+"exch mark exch (Object ) exch ( isn't defined before needed (1).) //error exec\n",
+"}if\n",
+"dup xcheck{\n",
+"exch mark exch (Object ) exch ( isn't defined before needed (2).) //error exec\n",
+"}{\n",
+"dup null eq{\n",
+"exch mark exch (Object ) exch ( isn't defined before needed (3).) //error exec\n",
+"}if\n",
+"exch pop\n",
+"}ifelse\n",
+"}bind def\n",
+"/StandardFontNames<<\n",
+"/Times-Roman true\n",
+"/Helvetica true\n",
+"/Courier true\n",
+"/Symbol true\n",
+"/Times-Bold true\n",
+"/Helvetica-Bold true\n",
+"/Courier-Bold true\n",
+"/ZapfDingbats true\n",
+"/Times-Italic true\n",
+"/Helvetica-Oblique true\n",
+"/Courier-Oblique true\n",
+"/Times-BoldItalic true\n",
+"/Helvetica-BoldOblique true\n",
+"/Courier-BoldOblique true\n",
+">>def\n",
+"/CleanAllResources\n",
+"{//PDFR_DEBUG{\n",
+"(CleanAllResources beg)=\n",
+"}if\n",
+"//PDFReader/ObjectRegistry get{\n",
+"dup length 0 exch 1 exch 1 sub{\n",
+"2 copy get dup xcheck{\n",
+"pop pop\n",
+"}{\n",
+"dup null eq{\n",
+"pop pop\n",
+"}{\n",
+"dup type/dicttype eq{/.Global known}{pop false}ifelse{\n",
+"pop\n",
+"}{\n",
+"//PDFR_DEBUG{\n",
+"(Dropping )print dup =\n",
+"}if\n",
+"1 index exch/DroppedObject put\n",
+"}ifelse\n",
+"}ifelse\n",
+"}ifelse\n",
+"}for\n",
+"pop\n",
+"}forall\n",
+"FontDirectory length dict begin\n",
+"FontDirectory{\n",
+"pop\n",
+"dup//StandardFontNames exch known not{\n",
+"dup null def\n",
+"}if\n",
+"pop\n",
+"}forall\n",
+"currentdict\n",
+"end{\n",
+"pop\n",
+"//PDFR_DEBUG{\n",
+"(Undefining font )print dup =\n",
+"}if\n",
+"undefinefont\n",
+"}forall\n",
+"//PDFR_DEBUG{\n",
+"(CleanAllResources end)=\n",
+"}if\n",
+"}bind def\n",
+"/PrintReference\n",
+"{\n",
+"//PDFR_DEBUG{\n",
+"({ )print\n",
+"dup{\n",
+"=only( )print\n",
+"}forall\n",
+"( })=\n",
+"}if\n",
+"}bind def\n",
+"/R\n",
+"{\n",
+"0 ne{\n",
+"exch mark exch(A referred object generation )exch( isn't 0.)//error exec\n",
+"}if\n",
+"[\n",
+"exch//GetRegistered/exec load\n",
+"]cvx\n",
+"//PrintReference exec\n",
+"}bind def\n",
+"/IsObjRef\n",
+"{\n",
+"dup type/arraytype eq{\n",
+"dup length 3 eq{\n",
+"dup xcheck exch\n",
+"dup 0 get type/integertype eq 3 2 roll and exch\n",
+"dup 1 get//GetRegistered eq 3 2 roll and exch\n",
+"2 get/exec load eq and\n",
+"}{\n",
+"pop false\n",
+"}ifelse\n",
+"}{\n",
+"pop false\n",
+"}ifelse\n",
+"}bind def\n",
+"/DoNothing\n",
+"{\n",
+"}def\n",
+"/RunTypeDaemon\n",
+"{\n",
+"dup type/dicttype eq{\n",
+"dup/Type//knownget exec{\n",
+"//PDFReader/TypeDaemons get exch\n",
+"//knownget exec{\n",
+"exec\n",
+"}if\n",
+"}if\n",
+"}if\n",
+"}bind def\n",
+"/obj\n",
+"{\n",
+"//PDFR_DEBUG{\n",
+"(Defining )print 1 index =only( )print dup =only( obj)=\n",
+"}if\n",
+"0 ne{\n",
+"exch mark exch(An object generation )exch( isn't 0.)//error exec\n",
+"}if\n",
+"}bind def\n",
+"/endobj\n",
+"{\n",
+"//PDFR_DEBUG{\n",
+"(endobj )=\n",
+"}if\n",
+"count 1 eq {pop} {",
+"dup type/dicttype eq{\n",
+"dup/.endobj_daemon//knownget exec{\n",
+"//PDFR_DEBUG{(.endobj_daemon for )print 2 index =}if\n",
+"exec\n",
+"}if\n",
+"}if\n",
+"dup type/dicttype eq{dup/ImmediateExec known}{false}ifelse{\n",
+"pop pop\n",
+"}{\n",
+"//PDFR_DEBUG{\n",
+"(Storing )print 1 index =\n",
+"}if\n",
+"//RunTypeDaemon exec\n",
+"//DoNothing 3 1 roll//Register exec\n",
+"}ifelse\n",
+"}ifelse",
+"}bind def\n",
+"/StoreBlock\n",
+"{\n",
+"//PDFR_DEBUG{\n",
+"(StoreBlock )print//PDFReader/BlockCount get =only(, Length = )print dup length =\n",
+"}if\n",
+"dup length string copy\n",
+"//PDFReader/BlockCount get exch\n",
+"//PDFReader/CurrentObject get 3 1 roll\n",
+"put\n",
+"//PDFReader/BlockCount get 1 add\n",
+"//PDFReader exch/BlockCount exch put\n",
+"}bind def\n",
+"/CheckLength\n",
+"{dup type/integertype ne{\n",
+"mark(Object length isn't an integer.)//error exec\n",
+"}if\n",
+"}bind def\n",
+"/ResolveD\n",
+"{\n",
+"3 copy pop get\n",
+"dup//IsObjRef exec{\n",
+"//PDFR_DEBUG{\n",
+"(Resolving )print//PrintReference exec\n",
+"}if\n",
+"exec\n",
+"exch exec\n",
+"}{\n",
+"exch pop\n",
+"}ifelse\n",
+"dup 4 1 roll\n",
+"put\n",
+"}bind def\n",
+"/ResolveA\n",
+"{2 index 2 index get\n",
+"dup//IsObjRef exec{\n",
+"exec\n",
+"exch exec\n",
+"3 copy put\n",
+"}{\n",
+"exch pop\n",
+"}ifelse\n",
+"exch pop exch pop\n",
+"}bind def\n",
+"/StoreStream\n",
+"{\n",
+"dup//PDFReader exch/CurrentObject exch put\n",
+"//PDFReader/BlockCount 0 put\n",
+"dup/Length//CheckLength//ResolveD exec\n",
+"//PDFR_DEBUG{\n",
+"(StoreStream Length = )print dup =\n",
+"}if\n",
+"currentfile exch()/SubFileDecode filter\n",
+"{dup//BlockBuffer readstring{\n",
+"//StoreBlock exec\n",
+"}{\n",
+"//StoreBlock exec\n",
+"exit\n",
+"}ifelse\n",
+"}loop\n",
+"pop\n",
+"//PDFReader/CurrentObject null put\n",
+"//PDFR_DEBUG{\n",
+"(StoreStream end.)=\n",
+"}if\n",
+"}bind def\n",
+"/MakeStreamDumper\n",
+"{\n",
+"//PDFR_DEBUG{\n",
+"(MakeStreamDumper beg.)=\n",
+"}if\n",
+"currentglobal exch dup gcheck setglobal\n",
+"[exch\n",
+"1 dict dup/c 0 put exch\n",
+"1024 string\n",
+"{readstring pop\n",
+"(StreamDumper )print 1 index/c get =string cvs print( )print\n",
+"dup length =string cvs print( <)print dup print(>\\n)print\n",
+"dup length\n",
+"3 2 roll\n",
+"dup/c get\n",
+"3 2 roll\n",
+"add/c exch put\n",
+"}/exec load\n",
+"]\n",
+"cvx 0()/SubFileDecode filter\n",
+"exch setglobal\n",
+"//PDFR_DEBUG{\n",
+"(MakeStreamDumper end.)=\n",
+"}if\n",
+"}bind def\n",
+"/ShortFilterNames 15 dict begin\n",
+"/AHx/ASCIIHexDecode def\n",
+"/A85/ASCII85Decode def\n",
+"/LZW/LZWDecode def\n",
+"/Fl/FlateDecode def\n",
+"/RL/RunLengthDecode def\n",
+"/CCF/CCITTFaxDecode def\n",
+"/DCT/DCTDecode def\n",
+"currentdict end readonly def\n",
+"/AppendFilters\n",
+"{\n",
+"//PDFR_DEBUG{\n",
+"(AppendFilters beg.)=\n",
+"}if\n",
+"dup 3 1 roll\n",
+"/Filter//knownget exec{\n",
+"dup type/nametype eq{\n",
+"dup//ShortFilterNames exch//knownget exec{\n",
+"exch pop\n",
+"}if\n",
+"2 index/DecodeParms//knownget exec{\n",
+"exch\n",
+"}if\n",
+"filter\n",
+"}{\n",
+"dup 0 exch 1 exch length 1 sub{\n",
+"2 copy get\n",
+"dup//ShortFilterNames exch//knownget exec{\n",
+"exch pop\n",
+"}if\n",
+"3 1 roll\n",
+"4 index/DecodeParms//knownget exec{\n",
+"exch get\n",
+"}{\n",
+"pop null\n",
+"}ifelse\n",
+"dup null eq{\n",
+"pop 3 1 roll filter exch\n",
+"}{\n",
+"3 1 roll\n",
+"4 1 roll filter exch\n",
+"}ifelse\n",
+"}for\n",
+"pop\n",
+"}ifelse\n",
+"//PDFR_DEBUG//PDFR_DUMP and{\n",
+"//MakeStreamDumper exec\n",
+"}if\n",
+"}if\n",
+"exch pop\n",
+"//PDFR_DEBUG{\n",
+"(AppendFilters end.)=\n",
+"}if\n",
+"}bind def\n",
+"/ExecuteStream\n",
+"{\n",
+"dup//PDFReader exch/CurrentObject exch put\n",
+"dup/Length//CheckLength//ResolveD exec\n",
+"//PDFR_DEBUG{\n",
+"(ExecuteStream id = )print 2 index =only( Length = )print dup =\n",
+"}if\n",
+"//PDFReader/InitialGraphicState get\n",
+"//PDFReader/GraphicState get copy pop\n",
+"//PDFReader/Operators get begin\n",
+"currentfile exch ()/SubFileDecode filter\n",
+"1 index//AppendFilters exec\n",
+"cvx mark exch\n",
+"exec\n",
+"counttomark 0 ne{\n",
+"mark(Data left on ostack after an immediate stream execution.)//error exec\n",
+"}if\n",
+"cleartomark\n",
+"end\n",
+"//PDFR_DEBUG{\n",
+"(ExecuteStream end.)=\n",
+"}if\n",
+"//PDFReader/CurrentObject null put\n",
+"dup/IsPage known{\n",
+"dup/Context get/NumCopies//knownget exec{\n",
+"1 sub{\n",
+"copypage\n",
+"}repeat\n",
+"}if\n",
+"EPS2Write not {showpage} if\n",
+"}if\n",
+"}bind def\n",
+"/stream\n",
+"{\n",
+"//PDFR_DEBUG{\n",
+"1 index =only( stream)=\n",
+"}if\n",
+"1 index GetObject{\n",
+"dup xcheck{\n",
+"exec\n",
+"1 index null PutObject\n",
+"}{\n",
+"pop\n",
+"}ifelse\n",
+"}if\n",
+"dup/ImmediateExec known{\n",
+"dup/GlobalExec//knownget exec{\n",
+"currentglobal 4 1 roll\n",
+"setglobal\n",
+"//ExecuteStream exec\n",
+"3 2 roll setglobal\n",
+"}{\n",
+"//ExecuteStream exec\n",
+"}ifelse\n",
+"}{\n",
+"//StoreStream exec\n",
+"}ifelse\n",
+"dup/.CleanResources//knownget exec{\n",
+"/All eq{\n",
+"//CleanAllResources exec\n",
+"}if\n",
+"}if\n",
+"}bind def\n",
+"/HookFont\n",
+"{\n",
+"//PDFR_DEBUG{\n",
+"(Loaded the font )print dup/FontName get =\n",
+"}if\n",
+"{\n",
+"dup/FontFileType get dup/Type1 eq exch/MMType1 eq or{\n",
+"dup/FontName get\n",
+"//PDFReader/RemoveFontNamePrefix get exec\n",
+"findfont\n",
+"exit\n",
+"}if\n",
+"dup/FontFileType get/TrueType eq{\n",
+"//PDFReader/MakeType42 get exec\n",
+"//PDFR_DEBUG{\n",
+"(Font dict <<)=\n",
+"dup{\n",
+"1 index/sfnts eq{\n",
+"exch pop\n",
+"(/sfnts [)print\n",
+"{\n",
+"(-string\\()print length//=only exec(\\)- )=\n",
+"}forall\n",
+"(])=\n",
+"}{\n",
+"exch//=only exec( )print ==\n",
+"}ifelse\n",
+"}forall\n",
+"(>>)=\n",
+"}if\n",
+"dup/FontName get exch definefont\n",
+"exit\n",
+"}if\n",
+"mark(FontHook has no proc for )2 index/FontFileType get//error exec\n",
+"}loop\n",
+"/Font exch put\n",
+"}bind def\n",
+"/endstream\n",
+"{\n",
+"}bind def\n",
+"/xref\n",
+"{\n",
+"//PDFR_DEBUG{\n",
+"(xref)=\n",
+"//PDFR_DUMP{\n",
+"//PDFReader/ObjectRegistry get ==\n",
+"}if\n",
+"}if\n",
+"end\n",
+"count 0 ne{\n",
+"mark(Excessive data on estack at the end of the interpretation.)//error exec\n",
+"}if\n",
+"currentfile 1(%%EOF)/SubFileDecode filter\n",
+"flushfile\n",
+"cleardictstack\n",
+"}bind def\n",
+"/ResolveDict\n",
+"{dup{\n",
+"pop 1 index exch\n",
+"//DoNothing//ResolveD exec\n",
+"pop\n",
+"}forall\n",
+"pop\n",
+"}bind def\n",
+"/SetupPageView\n",
+"{\n",
+"//PDFR_DEBUG{\n",
+"(SetupPageView beg)=\n",
+"}if\n",
+"//DSC_OPDFREAD not{\n",
+"//GraphicState/InitialMatrix get setmatrix\n",
+"}if\n",
+"/MediaBox get aload pop\n",
+"3 index neg 3 index neg translate\n",
+"3 -1 roll sub 3 1 roll exch sub exch\n",
+"userdict/.HWMargins//knownget exec{\n",
+"aload pop\n",
+"}{\n",
+"currentpagedevice/.HWMargins//knownget exec{\n",
+"aload pop\n",
+"}{\n",
+"0 0 0 0\n",
+"}ifelse\n",
+"}ifelse\n",
+"currentpagedevice/PageSize get aload pop\n",
+"3 -1 roll sub 3 1 roll exch sub exch\n",
+"exch 3 index sub exch 3 index sub\n",
+"//SetPageSize{\n",
+"//PDFR_DEBUG{\n",
+"(Setting page size to )print 1 index//=only exec( )print dup =\n",
+"}if\n",
+"pop pop 3 index 3 index 2 copy\n",
+"currentglobal false setglobal 3 1 roll\n",
+"currentpagedevice dup /PageSize known{\n",
+"/PageSize get aload pop}{\n",
+"0 0}ifelse\n",
+"round cvi 2 index round cvi eq\n",
+"exch round cvi 3 index round cvi eq and{\n",
+"//PDFR_DEBUG{(PageSize matches request) == flush}if\n",
+"pop pop}{\n",
+"/MediaRequested where {\n",
+"//PDFR_DEBUG{(MediaRequested is true, check against new request) == flush}if\n",
+"/MediaRequested get aload pop\n",
+"round cvi 2 index round cvi eq\n",
+"exch round cvi 3 index round cvi eq and\n",
+"{//PDFR_DEBUG{(MediaRequested same as current request, ignore) == flush}if pop pop false}\n",
+"{//PDFR_DEBUG{(MediaRequested different to current request) == flush}if true}ifelse\n",
+"}{\n",
+"//PDFR_DEBUG{(No MediaRequested yet) == flush}if\n",
+"true\n",
+"}ifelse\n",
+"{\n",
+"//PDFR_DEBUG{(Setting pagesize) == flush}if\n",
+"2 array astore\n",
+"dup /MediaRequested exch def\n",
+"<< exch /PageSize exch >> setpagedevice\n",
+"/pagesave save def\n",
+"}if\n",
+"}ifelse\n",
+"userdict/PDFR_InitialGS gstate put\n",
+"setglobal\n",
+"}if\n",
+"//RotatePages{\n",
+"2 copy gt 6 index 6 index gt ne{\n",
+"1 index 5 index le 1 index 5 index le and not\n",
+"}{\n",
+"false\n",
+"}ifelse\n",
+"}{\n",
+"false\n",
+"}ifelse\n",
+"{//CenterPages{\n",
+"//PDFR_DEBUG{\n",
+"(Rotating page, and then centering it)==\n",
+"}if\n",
+"90 rotate\n",
+"0 5 index neg translate\n",
+"5 index 1 index exch sub 2 div\n",
+"2 index 6 index sub 2 div neg\n",
+"translate\n",
+"}{\n",
+"//FitPages{\n",
+"1 index 5 index div 1 index 7 index div\n",
+"2 copy gt{\n",
+"exch\n",
+"}if\n",
+"pop dup scale\n",
+"}if\n",
+"90 rotate\n",
+"0 5 index neg translate\n",
+"}ifelse\n",
+"}{\n",
+"//CenterPages{\n",
+"//PDFR_DEBUG{\n",
+"(Ccentering page)==\n",
+"}if\n",
+"1 index 6 index sub 2 div\n",
+"1 index 6 index sub 2 div\n",
+"translate\n",
+"}{\n",
+"//FitPages{\n",
+"1 index 6 index div 1 index 6 index div\n",
+"2 copy gt{\n",
+"exch\n",
+"}if\n",
+"pop dup scale\n",
+"}if\n",
+"}ifelse\n",
+"}ifelse\n",
+"pop pop\n",
+"translate\n",
+"pop pop\n",
+"//PDFR_DEBUG{\n",
+"(SetupPageView end)=\n",
+"}if\n",
+"}bind def\n",
+"/PageContentsDaemon\n",
+"{\n",
+"//PDFR_DEBUG{\n",
+"(Executing PageContentsDaemon for )print 2 index =\n",
+"}if\n",
+"1 index exch/Context exch put\n",
+"dup/ImmediateExec true put\n",
+"dup/IsPage true put\n",
+"SetPageSize {dup/Context get//SetupPageView exec}if\n",
+"}bind def\n",
+"/FontFileDaemon\n",
+"{\n",
+"//PDFR_DEBUG{\n",
+"(Executing FontFileDaemon for )print 2 index =\n",
+"}if\n",
+"dup/FontFileType get\n",
+"2 index exch\n",
+"dup//ReadFontProcs exch//knownget exec{\n",
+"exch pop exec\n",
+"}{\n",
+"mark(FontFile reader for )2 index( isn't implemented yet.)//error exec\n",
+"}ifelse\n",
+"//PDFR_DEBUG{\n",
+"(FontFileDaemon end)=\n",
+"}if\n",
+"pop\n",
+"}bind def\n",
+"/FontDescriptorDaemon\n",
+"{\n",
+"//PDFR_DEBUG{\n",
+"(Executing FontDescriptorDaemon for )print 2 index =\n",
+"}if\n",
+"2 copy/FontResource exch put\n",
+"/Subtype get 1 index exch/FontFileType exch put\n",
+"}bind def\n",
+"/UnPDFEscape{\n",
+"dup dup length string cvs\n",
+"dup(#)search{\n",
+"{\n",
+"pop\n",
+"(16#--)2 index 0 2 getinterval\n",
+"1 index 3 2 getinterval copy pop\n",
+"cvi\n",
+"0 exch put\n",
+"0\n",
+"1 index 2 1 index length 2 sub getinterval\n",
+"3 copy putinterval\n",
+"length\n",
+"3 copy exch put\n",
+"getinterval\n",
+"(#)search not{\n",
+"pop exit\n",
+"}if\n",
+"}loop\n",
+"(\\0)search pop exch pop exch pop\n",
+"cvn\n",
+"exch pop\n",
+"}{\n",
+"pop pop\n",
+"}ifelse\n",
+"}bind def\n",
+"/TypeDaemons<<\n",
+"/Page\n",
+"{//PDFR_DEBUG{\n",
+"(Recognized a page.)=\n",
+"}if\n",
+"dup/Contents//knownget exec{\n",
+"0 get//DoNothing exch\n",
+"[\n",
+"3 index//PageContentsDaemon/exec load\n",
+"]cvx\n",
+"//Register exec\n",
+"}{\n",
+"(fixme: page with no Contents won't be printed.)=\n",
+"}ifelse\n",
+"}bind\n",
+"/FontDescriptor\n",
+"{//PDFR_DEBUG{\n",
+"(Recognized a font descriptor.)=\n",
+"}if\n",
+"dup/FontName//knownget exec{\n",
+"1 index/FontName 3 -1 roll//UnPDFEscape exec put\n",
+"}if\n",
+"dup dup/FontFile known{/FontFile}{/FontFile2}ifelse\n",
+"//knownget exec{\n",
+"0 get//DoNothing exch\n",
+"[\n",
+"3 index//FontFileDaemon/exec load\n",
+"]cvx\n",
+"//Register exec\n",
+"}{\n",
+"(Font descriptor )print 1 index =only( has no FontFile.)=\n",
+"}ifelse\n",
+"}bind\n",
+"/Font\n",
+"{//PDFR_DEBUG{\n",
+"(Recognized a font resource.)=\n",
+"}if\n",
+"dup/BaseFont//knownget exec{\n",
+"//UnPDFEscape exec 2 copy/BaseFont exch put\n",
+"//PDFReader/RemoveFontNamePrefix get exec\n",
+"currentglobal exch\n",
+"dup/Font resourcestatus{\n",
+"pop pop\n",
+"//PDFReader/GetInstalledFont get exec pop\n",
+"}{\n",
+"pop\n",
+"}ifelse\n",
+"setglobal\n",
+"}if\n",
+"dup/FontDescriptor//knownget exec{\n",
+"0 get\n",
+"dup//IsRegistered exec{\n",
+"//PDFR_DEBUG{\n",
+"(already registered )print dup =\n",
+"}if\n",
+"pop\n",
+"}{\n",
+"//DoNothing exch\n",
+"[\n",
+"3 index//FontDescriptorDaemon/exec load\n",
+"]cvx\n",
+"//Register exec\n",
+"}ifelse\n",
+"}if\n",
+"}bind\n",
+">>def\n",
+"/MakeStreamReader\n",
+"{dup\n",
+"[\n",
+"exch\n",
+"//PDFR_DEBUG{\n",
+"(Stream proc )\n",
+"/print load\n",
+"//PDFR_STREAM{\n",
+"(<)\n",
+"/print load\n",
+"}if\n",
+"}if\n",
+"1 dict dup/i -1 put\n",
+"/dup load\n",
+"/i\n",
+"/get load\n",
+"1\n",
+"/add load\n",
+"/dup load\n",
+"3\n",
+"1\n",
+"/roll load\n",
+"/i\n",
+"/exch load\n",
+"/put load\n",
+"//knownget\n",
+"/exec load\n",
+"/not load\n",
+"{()}\n",
+"/if load\n",
+"//PDFR_DEBUG{\n",
+"//PDFR_STREAM{\n",
+"/dup load\n",
+"/print load\n",
+"(>)\n",
+"/print load\n",
+"}if\n",
+"( end of stream proc.\\n)\n",
+"/print load\n",
+"}if\n",
+"]cvx\n",
+"//PDFR_DEBUG{\n",
+"(Stream reader )print dup ==\n",
+"}if\n",
+"0()/SubFileDecode filter\n",
+"exch//AppendFilters exec\n",
+"}bind def\n",
+"/RunDelayedStream\n",
+"{\n",
+"//GraphicState/InitialTextMatrix get\n",
+"//InitialTextMatrixStack//PDFReader/InitialTextMatrixStackPointer get\n",
+"2 copy get null eq{\n",
+"2 copy currentglobal true setglobal matrix exch setglobal put\n",
+"}if\n",
+"get copy pop\n",
+"//PDFReader/InitialTextMatrixStackPointer 2 copy get 1 add put\n",
+"//MakeStreamReader exec\n",
+"mark exch\n",
+"cvx exec\n",
+"counttomark 0 ne{\n",
+"mark(Data left on ostack after a delayed stream execution.)//error exec\n",
+"}if\n",
+"cleartomark\n",
+"//PDFReader/InitialTextMatrixStackPointer 2 copy get 1 sub put\n",
+"//InitialTextMatrixStack//PDFReader/InitialTextMatrixStackPointer get get\n",
+"//GraphicState/InitialTextMatrix get\n",
+"copy pop\n",
+"}bind def\n",
+"//ReadFontProcs begin\n",
+"/Type1\n",
+"{//PDFR_DEBUG{\n",
+"(ReadFontProcs.Type1)=\n",
+"}if\n",
+"dup/.endobj_daemon[4 index//HookFont/exec load]cvx put\n",
+"dup/ImmediateExec true put\n",
+"/GlobalExec true put\n",
+"}bind def\n",
+"/MMType1//Type1 def\n",
+"/TrueType\n",
+"{//PDFR_DEBUG{\n",
+"(ReadFontProcs.TrueType)=\n",
+"}if\n",
+"dup/.endobj_daemon[4 index//HookFont/exec load]cvx put\n",
+"pop\n",
+"}bind def\n",
+"end\n",
+"/.opdloadttfontdict 50 dict def\n",
+".opdloadttfontdict begin\n",
+"/maxstring 65400 def\n",
+"end\n",
+"/.InsertionSort\n",
+"{\n",
+"/CompareProc exch def\n",
+"/Array exch def\n",
+"1 1 Array length 1 sub\n",
+"{\n",
+"/Ix exch def\n",
+"/Value1 Array Ix get def\n",
+"/Jx Ix 1 sub def\n",
+"{\n",
+"Jx 0 lt{\n",
+"exit\n",
+"}if\n",
+"/Value2 Array Jx get def\n",
+"Value1 Value2 CompareProc{\n",
+"exit\n",
+"}if\n",
+"Array Jx 1 add Value2 put\n",
+"/Jx Jx 1 sub def\n",
+"}loop\n",
+"Array Jx 1 add Value1 put\n",
+"}for\n",
+"Array\n",
+"}bind def\n",
+"/putu16{\n",
+"3 copy -8 bitshift put\n",
+"exch 1 add exch 16#ff and put\n",
+"}bind def\n",
+"/putu32{\n",
+"3 copy -16 bitshift putu16\n",
+"exch 2 add exch 16#ffff and putu16\n",
+"}bind def\n",
+"/.readtable{\n",
+"dup dup 1 and add string\n",
+"dup 0 4 -1 roll getinterval\n",
+"3 -1 roll exch\n",
+"dup()ne{readstring}if pop pop\n",
+"}bind def\n",
+"/.readbigtable{\n",
+"dup maxstring lt{\n",
+".readtable\n",
+"}{\n",
+"currentuserparams/VMReclaim get -2 vmreclaim\n",
+"[4 2 roll{\n",
+"dup maxstring le{exit}if\n",
+"1 index maxstring string readstring pop 3 1 roll maxstring sub\n",
+"}loop .readtable]\n",
+"exch vmreclaim\n",
+"}ifelse\n",
+"}bind def\n",
+"/ReadTTF\n",
+"{\n",
+".opdloadttfontdict begin\n",
+"/TTFontFile exch def\n",
+"/TableDir TTFontFile 12 string readstring pop def\n",
+"/tables TTFontFile TableDir 4 getu16 16 mul string readstring pop def\n",
+"/tabarray tables length 16 idiv array def\n",
+"TableDir 0 4 getinterval(ttcf)eq{\n",
+"QUIET not{(Can't handle TrueType font Collections.)=}if\n",
+"/.loadttfonttables cvx/invalidfont signalerror\n",
+"}{\n",
+"0 16 tables length 1 sub{\n",
+"dup\n",
+"tables exch 16 getinterval\n",
+"exch 16 div cvi exch\n",
+"tabarray 3 1 roll put\n",
+"}for\n",
+"}ifelse\n",
+"tabarray{exch 8 getu32 exch 8 getu32 gt}.InsertionSort pop\n",
+"/Read TableDir length tables length add def\n",
+"/tabs[\n",
+"tabarray{\n",
+"dup 8 getu32\n",
+"Read sub\n",
+"dup 0 gt{\n",
+"dup string TTFontFile exch readstring pop pop\n",
+"Read add/Read exch def\n",
+"}{\n",
+"pop\n",
+"}ifelse\n",
+"12 getu32\n",
+"dup Read add\n",
+"/Read exch def\n",
+"TTFontFile exch .readbigtable\n",
+"}forall\n",
+"]def\n",
+"end\n",
+"}bind def\n",
+"/GetLocaType\n",
+"{\n",
+"0 1 tabarray length 1 sub{\n",
+"dup tabarray exch get\n",
+"0 4 getinterval(head)eq{\n",
+"tabs exch get\n",
+"50 gets16\n",
+"/LocaType exch def\n",
+"exit\n",
+"}{\n",
+"pop\n",
+"}ifelse\n",
+"}for\n",
+"}bind def\n",
+"/GetNumGlyphs\n",
+"{\n",
+"0 1 tabarray length 1 sub{\n",
+"dup tabarray exch get\n",
+"0 4 getinterval(maxp)eq{\n",
+"tabs exch get\n",
+"4 getu16\n",
+"/NumGlyphs exch def\n",
+"exit\n",
+"}{\n",
+"pop\n",
+"}ifelse\n",
+"}for\n",
+"}bind def\n",
+"/StringToLoca\n",
+"{\n",
+"/LocaIndex exch def\n",
+"/StringOffset 0 def\n",
+"{\n",
+"dup length StringOffset gt{\n",
+"dup\n",
+"LocaType 1 eq{\n",
+"StringOffset getu32\n",
+"LocaArray LocaIndex 3 -1 roll put\n",
+"/LocaIndex LocaIndex 1 add def\n",
+"/StringOffset StringOffset 4 add\n",
+"def\n",
+"}{\n",
+"StringOffset getu16 2 mul\n",
+"LocaArray length LocaIndex gt {",
+"LocaArray LocaIndex 3 -1 roll put",
+"}{",
+"pop",
+"}ifelse",
+"/LocaIndex LocaIndex 1 add def\n",
+"/StringOffset StringOffset 2 add\n",
+"def\n",
+"}ifelse\n",
+"}{\n",
+"pop\n",
+"LocaIndex\n",
+"exit\n",
+"}ifelse\n",
+"}loop\n",
+"}bind def\n",
+"/GetSortedLoca\n",
+"{\n",
+"NumGlyphs 1 add array/LocaArray exch def\n",
+"0 1 tabarray length 1 sub{\n",
+"dup tabarray exch get\n",
+"0 4 getinterval(loca)eq{\n",
+"tabs exch get\n",
+"exit\n",
+"}{\n",
+"pop\n",
+"}ifelse\n",
+"}for\n",
+"dup type/stringtype eq{\n",
+"0 StringToLoca pop\n",
+"}{\n",
+"0 exch\n",
+"{\n",
+"exch StringToLoca\n",
+"}forall\n",
+"pop\n",
+"}ifelse\n",
+"LocaArray{gt}.InsertionSort pop\n",
+"}bind def\n",
+"/GetWorkingString\n",
+"{\n",
+"WorkString 0\n",
+"GlyfArray GlyfStringIndex get\n",
+"putinterval\n",
+"/WorkBytes GlyfArray GlyfStringIndex get length def\n",
+"/GlyfStringIndex GlyfStringIndex 1 add def\n",
+"}bind def\n",
+"/GetWorkingBytes\n",
+"{\n",
+"/BytesToRead exch def\n",
+"WorkString 0 BytesToRead getinterval\n",
+"dup length string copy\n",
+"WorkString BytesToRead WorkBytes BytesToRead sub getinterval\n",
+"dup length string copy\n",
+"WorkString 0 3 -1 roll putinterval\n",
+"/WorkBytes WorkBytes BytesToRead sub def\n",
+"}bind def\n",
+"/GetGlyfBytes\n",
+"{\n",
+"/ToRead exch def\n",
+"WorkBytes 0 eq{\n",
+"GetWorkingString\n",
+"}if\n",
+"WorkBytes ToRead ge{\n",
+"ToRead string dup 0\n",
+"ToRead GetWorkingBytes putinterval\n",
+"}{\n",
+"ToRead string\n",
+"dup\n",
+"0\n",
+"WorkString 0 WorkBytes getinterval\n",
+"putinterval\n",
+"dup\n",
+"WorkBytes\n",
+"ToRead WorkBytes sub\n",
+"GetWorkingString\n",
+"GetWorkingBytes\n",
+"putinterval\n",
+"}ifelse\n",
+"}bind def\n",
+"/SplitGlyf\n",
+"{\n",
+"/GlyfArray exch def\n",
+"/DestArray GlyfArray length 2 mul array def\n",
+"/DestArrayIndex 0 def\n",
+"/LastLoca 0 def\n",
+"/NextLocaIndex 0 def\n",
+"/LastLocaIndex 0 def\n",
+"/GlyfStringIndex 0 def\n",
+"/WorkString maxstring string def\n",
+"/WorkBytes 0 def\n",
+"{\n",
+"LocaArray NextLocaIndex get\n",
+"LastLoca sub maxstring gt\n",
+"{\n",
+"LocaArray LastLocaIndex get LastLoca sub\n",
+"GetGlyfBytes\n",
+"DestArray DestArrayIndex 3 -1 roll put\n",
+"/DestArrayIndex DestArrayIndex 1 add def\n",
+"LocaArray LastLocaIndex get/LastLoca exch def\n",
+"}{\n",
+"/LastLocaIndex NextLocaIndex def\n",
+"/NextLocaIndex NextLocaIndex 1 add def\n",
+"NextLocaIndex NumGlyphs gt\n",
+"{\n",
+"WorkBytes\n",
+"GlyfStringIndex GlyfArray length lt{\n",
+"GlyfArray GlyfStringIndex get length\n",
+"add string dup\n",
+"0\n",
+"WorkString 0 WorkBytes getinterval\n",
+"putinterval\n",
+"dup\n",
+"WorkBytes\n",
+"GetWorkingString\n",
+"WorkString 0 WorkBytes getinterval\n",
+"putinterval\n",
+"}{\n",
+"pop\n",
+"WorkString 0 WorkBytes getinterval\n",
+"}ifelse\n",
+"dup length string copy\n",
+"DestArray DestArrayIndex 3 -1 roll put\n",
+"exit\n",
+"}if\n",
+"}ifelse\n",
+"}loop\n",
+"DestArray\n",
+"}bind def\n",
+"/ProcessTTData\n",
+"{\n",
+".opdloadttfontdict begin\n",
+"0 1 tabarray length 1 sub{\n",
+"/ix exch def\n",
+"tabarray ix get\n",
+"12 getu32 dup maxstring le{\n",
+"dup 4 mod 0 ne{\n",
+"4 div cvi 1 add 4 mul string/newstring exch def\n",
+"/oldstring tabs ix get def\n",
+"newstring 0 oldstring putinterval\n",
+"0 1 newstring length oldstring length sub 1 sub{\n",
+"newstring exch oldstring length add 0 put\n",
+"}for\n",
+"tabs ix newstring put\n",
+"}{\n",
+"pop\n",
+"}ifelse\n",
+"}{\n",
+"dup 4 mod 0 ne{\n",
+"dup maxstring idiv maxstring mul sub\n",
+"4 idiv 1 add 4 mul string/newstring exch def\n",
+"tabs ix get\n",
+"dup length 1 sub dup/iy exch def get/oldstring exch def\n",
+"newstring 0 oldstring putinterval\n",
+"0 1 newstring length oldstring length sub 1 sub{\n",
+"newstring exch oldstring length add 0 put\n",
+"}for\n",
+"tabs ix get iy newstring put\n",
+"}{\n",
+"pop\n",
+"}ifelse\n",
+"}ifelse\n",
+"}for\n",
+"0 1 tabarray length 1 sub{\n",
+"dup tabarray exch get\n",
+"dup 12 getu32 maxstring gt{\n",
+"0 4 getinterval dup(glyf)eq{\n",
+"pop\n",
+"GetLocaType\n",
+"GetNumGlyphs\n",
+"GetSortedLoca\n",
+"dup tabs exch get\n",
+"SplitGlyf\n",
+"tabs 3 1 roll put\n",
+"}{\n",
+"(Warning, table )print print( > 64Kb\\n)print\n",
+"pop\n",
+"}ifelse\n",
+"}{\n",
+"pop\n",
+"pop\n",
+"}ifelse\n",
+"}for\n",
+"end\n",
+"}bind def\n",
+"/Makesfnts\n",
+"{\n",
+".opdloadttfontdict begin\n",
+"0\n",
+"tabs{\n",
+"dup type/stringtype eq{\n",
+"pop\n",
+"1 add\n",
+"}{\n",
+"{\n",
+"type/stringtype eq{\n",
+"1 add\n",
+"}if\n",
+"}forall\n",
+"}ifelse\n",
+"}forall\n",
+"1 add\n",
+"/TTOffset\n",
+"TableDir length\n",
+"tabarray length 16 mul add\n",
+"def\n",
+"0\n",
+"tabarray{\n",
+"exch dup 1 add\n",
+"3 1 roll\n",
+"dup\n",
+"tabs exch get\n",
+"dup type/stringtype eq{\n",
+"length\n",
+"2 index exch\n",
+"TTOffset\n",
+"dup 3 1 roll add\n",
+"/TTOffset exch def\n",
+"8 exch putu32\n",
+"exch tabarray 3 1 roll\n",
+"put\n",
+"}{\n",
+"0 exch\n",
+"{\n",
+"dup type/stringtype eq{\n",
+"length add\n",
+"}{\n",
+"pop\n",
+"}ifelse\n",
+"}forall\n",
+"2 index exch\n",
+"TTOffset\n",
+"dup 3 1 roll add\n",
+"/TTOffset exch def\n",
+"8 exch putu32\n",
+"exch tabarray 3 1 roll\n",
+"put\n",
+"}ifelse\n",
+"}forall\n",
+"pop\n",
+"array\n",
+"dup 0\n",
+"TableDir length\n",
+"tables length add\n",
+"string\n",
+"dup 0 TableDir putinterval\n",
+"dup 12 tables putinterval\n",
+"put\n",
+"dup\n",
+"/ix 1 def\n",
+"tabs{\n",
+"dup type/stringtype eq{\n",
+"ix exch\n",
+"put dup\n",
+"/ix ix 1 add def\n",
+"}{\n",
+"{\n",
+"dup type/stringtype eq{\n",
+"ix exch put dup\n",
+"/ix ix 1 add def\n",
+"}{\n",
+"pop\n",
+"}ifelse\n",
+"}forall\n",
+"}ifelse\n",
+"}forall\n",
+"pop\n",
+"end\n",
+"}bind def\n",
+"/MakeType42\n",
+"{\n",
+"//PDFR_DEBUG{\n",
+"(MakeType42 beg)=\n",
+"}if\n",
+"10 dict begin\n",
+"/FontName 1 index/FontName get def\n",
+"/FontType 42 def\n",
+"/FontMatrix[1 0 0 1 0 0]def\n",
+"/FontBBox 1 index/FontBBox get def\n",
+"dup/FontResource get\n",
+"dup/Encoding known{\n",
+"//PDFReader/ObtainEncoding get exec\n",
+"/Encoding get\n",
+"}{\n",
+"pop null\n",
+"}ifelse\n",
+"/PDFEncoding exch def\n",
+"/CharStrings 2 index//PDFReader/MakeTTCharStrings get exec def\n",
+"/sfnts 2 index//MakeStreamReader exec\n",
+"ReadTTF\n",
+"ProcessTTData\n",
+"Makesfnts\n",
+"def\n",
+"/Encoding StandardEncoding def\n",
+"/PaintType 0 def\n",
+"currentdict end\n",
+"//PDFR_DEBUG{\n",
+"(MakeType42 end)=\n",
+"}if\n",
+"}bind def\n",
+"/GetInstalledFont\n",
+"{\n",
+"dup//InstalledFonts exch knownget{\n",
+"exch pop\n",
+"}{\n",
+"dup findfont dup 3 1 roll\n",
+"//InstalledFonts 3 1 roll put\n",
+"}ifelse\n",
+"}bind def\n",
+"/RemoveFontNamePrefix\n",
+"{//=string cvs true\n",
+"0 1 5{\n",
+"2 index exch get//IsUpper exec not{\n",
+"pop false exit\n",
+"}if\n",
+"}for\n",
+"{(+)search{\n",
+"pop pop\n",
+"}if\n",
+"}if\n",
+"cvn\n",
+"}bind def\n",
+"/CheckFont\n",
+"{dup/Type get/Font ne{\n",
+"mark(Resource )3 index( must have /Type/Font .)//error exec\n",
+"}if\n",
+"}bind def\n",
+"/CheckEncoding\n",
+"{dup type/nametype ne{\n",
+"dup/Type get/Encoding ne{\n",
+"mark(Resource )3 index( must have /Type/Encoding .)//error exec\n",
+"}if\n",
+"}if\n",
+"}bind def\n",
+"/ObtainEncoding\n",
+"{dup/Encoding known{\n",
+"dup dup/Encoding//CheckEncoding//ResolveD exec\n",
+"dup type dup/arraytype eq exch/packedarraytype eq or{\n",
+"pop pop\n",
+"}{\n",
+"dup type/nametype eq{\n",
+"/Encoding findresource\n",
+"}{\n",
+"dup/BaseEncoding//knownget exec not{\n",
+"/StandardEncoding\n",
+"}if\n",
+"/Encoding findresource\n",
+"exch\n",
+"/Differences//knownget exec{\n",
+"exch dup length array copy exch\n",
+"0 exch\n",
+"{\n",
+"dup type/integertype eq{\n",
+"exch pop\n",
+"}{\n",
+"3 copy put pop\n",
+"1 add\n",
+"}ifelse\n",
+"}forall\n",
+"pop\n",
+"}if\n",
+"}ifelse\n",
+"/Encoding exch put\n",
+"}ifelse\n",
+"}{\n",
+"dup/Encoding/StandardEncoding/Encoding findresource put\n",
+"}ifelse\n",
+"}bind def\n",
+"/ObtainMetrics\n",
+"{dup/Widths//knownget exec{\n",
+"1 index/Encoding get\n",
+"256 dict\n",
+"3 index/Subtype get/TrueType eq{\n",
+"1000\n",
+"}{\n",
+"1\n",
+"}ifelse\n",
+"4 index/MissingWidth//knownget exec not{\n",
+"0\n",
+"}if\n",
+"5 index/FirstChar//knownget exec not{\n",
+"0\n",
+"}if\n",
+"6 5 roll\n",
+"dup 0 exch 1 exch length 1 sub{\n",
+"2 copy get\n",
+"exch 3 index add\n",
+"7 index exch get\n",
+"dup dup null ne exch/.notdef ne and{\n",
+"6 index 3 1 roll exch\n",
+"6 index div\n",
+"3 copy pop//knownget exec{\n",
+"0 eq\n",
+"}{\n",
+"true\n",
+"}ifelse\n",
+"{put\n",
+"}{\n",
+"pop pop pop\n",
+"}ifelse\n",
+"}{\n",
+"pop pop\n",
+"}ifelse\n",
+"}for\n",
+"pop pop pop pop exch pop\n",
+"1 index exch/Metrics exch put\n",
+"}{\n",
+"dup/MissingWidth//knownget exec{\n",
+"256 dict\n",
+"2 index/Encoding get{\n",
+"dup null ne{\n",
+"3 copy 3 2 roll put\n",
+"}if\n",
+"pop\n",
+"}forall\n",
+"exch pop\n",
+"1 index exch/Metrics exch put\n",
+"}if\n",
+"}ifelse\n",
+"}bind def\n",
+"/NotDef\n",
+"{\n",
+"FontMatrix aload pop pop pop exch pop exch pop\n",
+"1 exch div exch\n",
+"1 exch div exch\n",
+"1 index 0 setcharwidth\n",
+"0 setlinewidth\n",
+"0 0 moveto\n",
+"2 copy rlineto\n",
+"1 index 0 rlineto\n",
+"neg exch neg exch rlineto\n",
+"closepath stroke\n",
+"}bind def\n",
+"/SaveResourcesToStack{",
+"[//PDFReader/OldResources known{",
+"//PDFReader/OldResources get}{null}ifelse\n",
+"//PDFReader/CurrentObject get/Context get/Resources get]",
+"//PDFReader/OldResources 3 -1 roll put}bind def\n",
+"/RestoreResourcesFromStack{",
+"//PDFReader/OldResources get dup\n",
+"0 get//PDFReader/OldResources 3 -1 roll put\n",
+"1 get//PDFReader/CurrentObject get/Context get/Resources 3 -1 roll put",
+"} bind def\n",
+"/BuildChar\n",
+"{//PDFR_DEBUG{\n",
+"(BuildChar )print dup//=only exec( )print\n",
+"}if\n",
+"exch begin\n",
+"Encoding exch get\n",
+"//PDFR_DEBUG{\n",
+"dup =\n",
+"}if\n",
+"dup null eq{\n",
+"pop//NotDef exec\n",
+"}{\n",
+"CharProcs exch//knownget exec{",
+"currentfont/Font get/Resources//knownget exec{",
+"exec SaveResourcesToStack",
+"//PDFReader/CurrentObject get/Context get",
+"/Resources 3 -1 roll put",
+"//RunDelayedStream exec RestoreResourcesFromStack",
+"}{//RunDelayedStream exec}ifelse",
+"}{\n",
+"//NotDef exec\n",
+"}ifelse\n",
+"}ifelse\n",
+"end\n",
+"}bind def\n",
+"/printdict\n",
+"{(<<)=\n",
+"{exch = ==}forall\n",
+"(>>)=\n",
+"}bind def\n",
+"/printfont\n",
+"{\n",
+"dup{\n",
+"exch dup =\n",
+"dup/Encoding eq{\n",
+"pop =\n",
+"}{\n",
+"dup/FontInfo eq exch/Private eq or{\n",
+"//printdict exec\n",
+"}{\n",
+"==\n",
+"}ifelse\n",
+"}ifelse\n",
+"}forall\n",
+"}bind def\n",
+"/ScaleMetrics\n",
+"{1 index{\n",
+"2 index div\n",
+"3 index\n",
+"3 1 roll put\n",
+"}forall\n",
+"pop\n",
+"}bind def\n",
+"/ResolveAndSetFontAux\n",
+"{exch dup\n",
+"//PDFReader/CurrentObject get/Context get/Resources get\n",
+"/Font//DoNothing//ResolveD exec\n",
+"exch//CheckFont//ResolveD exec\n",
+"dup/Font//knownget exec{\n",
+"exch pop exch pop\n",
+"}{\n",
+"{\n",
+"dup/Subtype get dup dup/Type1 eq exch/TrueType eq or exch/MMType1 eq or{\n",
+"exch pop\n",
+"dup/BaseFont get\n",
+"//RemoveFontNamePrefix exec\n",
+"//PDFR_DEBUG{\n",
+"(Font )print dup =\n",
+"}if\n",
+"1 index/FontDescriptor known{\n",
+"//PDFR_DEBUG{\n",
+"(Font from a font descriptor.)=\n",
+"}if\n",
+"1 index\n",
+"/FontDescriptor//DoNothing//ResolveD exec\n",
+"/Font//knownget exec{\n",
+"exch pop\n",
+"}{\n",
+"//PDFR_DEBUG{\n",
+"(Font descriptor has no Font resolved.)=\n",
+"}if\n",
+"//GetInstalledFont exec\n",
+"}ifelse\n",
+"}{\n",
+"//GetInstalledFont exec\n",
+"}ifelse\n",
+"exch\n",
+"dup/Encoding known not{\n",
+"1 index/Encoding get 1 index exch/Encoding exch put\n",
+"}if\n",
+"//ObtainEncoding exec\n",
+"//ObtainMetrics exec\n",
+"exch\n",
+"dup length dict copy\n",
+"dup 2 index/Encoding get\n",
+"/Encoding exch put\n",
+"1 index/Metrics//knownget exec{\n",
+"2 index/Subtype get/TrueType ne{\n",
+"1 index/FontMatrix get 0 get\n",
+"dup 0 eq{\n",
+"pop\n",
+"1 index/FontMatrix get 1 get\n",
+"dup 0 eq{pop 1}if\n",
+"}if\n",
+"0.001 div\n",
+"//ScaleMetrics exec\n",
+"}{\n",
+"1 index/sfnts known not{\n",
+"1 index/FontMatrix get 0 get\n",
+"dup 0 eq{\n",
+"pop\n",
+"1 index/FontMatrix get 1 get\n",
+"dup 0 eq{pop 1}if\n",
+"}if\n",
+"//ScaleMetrics exec\n",
+"}if\n",
+"}ifelse\n",
+"1 index exch/Metrics exch put\n",
+"}if\n",
+"1 index/BaseFont get\n",
+"exch\n",
+"dup/FID undef\n",
+"dup/UniqueID undef\n",
+"definefont\n",
+"dup 3 1 roll\n",
+"/Font exch put\n",
+"exit\n",
+"}if\n",
+"dup/Subtype get/Type3 eq{\n",
+"//ObtainEncoding exec\n",
+"2 copy exch/FontName exch put\n",
+"dup/CharProcs get//ResolveDict exec\n",
+"dup/FontType 3 put\n",
+"dup/BuildChar//BuildChar put\n",
+"dup dup/Font exch put\n",
+"dup 3 1 roll\n",
+"definefont\n",
+"2 copy ne{\n",
+"2 copy/Font exch put\n",
+"}if\n",
+"exch pop\n",
+"exit\n",
+"}if\n",
+"dup/Subtype get/Type0 eq{\n",
+"}if\n",
+"dup/Subtype get/CIDFontType0 eq{\n",
+"}if\n",
+"dup/Subtype get/CIDFontType2 eq{\n",
+"}if\n",
+"mark(Unknown font type )2 index/Subtype get//error exec\n",
+"}loop\n",
+"}ifelse\n",
+"exch scalefont setfont\n",
+"}bind def\n",
+"/ResolveAndSetFont\n",
+"{\n",
+"//ResolveAndSetFontAux exec\n",
+"}bind def\n",
+"/.knownget\n",
+"{2 copy known{\n",
+"get true\n",
+"}{\n",
+"pop pop false\n",
+"}ifelse\n",
+"}bind def\n",
+"/.min\n",
+"{2 copy lt{\n",
+"exch\n",
+"}if\n",
+"pop\n",
+"}bind def\n",
+"/.max\n",
+"{2 copy gt{\n",
+"exch\n",
+"}if\n",
+"pop\n",
+"}bind def\n",
+"/.dicttomark\n",
+"{>>\n",
+"}bind def\n",
+"/getu16{\n",
+"2 copy get 8 bitshift 3 1 roll 1 add get add\n",
+"}bind def\n",
+"/gets16{\n",
+"getu16 16#8000 xor 16#8000 sub\n",
+"}bind def\n",
+"/getu32{\n",
+"2 copy getu16 16 bitshift 3 1 roll 2 add getu16 add\n",
+"}bind def\n",
+"/gets32{\n",
+"2 copy gets16 16 bitshift 3 1 roll 2 add getu16 add\n",
+"}bind def\n",
+"/cmapformats mark\n",
+"0{\n",
+"6 256 getinterval{}forall 256 packedarray\n",
+"}bind\n",
+"2{\n",
+"/sHK_sz 2 def\n",
+"/sH_sz 8 def\n",
+"dup 2 getu16/cmapf2_tblen exch def\n",
+"dup 4 getu16/cmapf2_lang exch def\n",
+"dup 6 256 sHK_sz mul getinterval/sHKs exch def\n",
+"0\n",
+"0 1 255{\n",
+"sHKs exch\n",
+"2 mul getu16\n",
+"1 index\n",
+"1 index\n",
+"lt{exch}if pop\n",
+"}for\n",
+"/sH_len exch def\n",
+"dup 6 256 sHK_sz mul add\n",
+"cmapf2_tblen 1 index sub getinterval\n",
+"/sH_gIA exch def\n",
+"/cmapf2_glyph_array 65535 array def\n",
+"/.cmapf2_putGID{\n",
+"/cmapf2_ch cmapf2_ch_hi 8 bitshift cmapf2_ch_lo add def\n",
+"firstCode cmapf2_ch_lo le\n",
+"cmapf2_ch_lo firstCode entryCount add lt\n",
+"and{\n",
+"sH_offset idRangeOffset add\n",
+"cmapf2_ch_lo firstCode sub 2 mul\n",
+"add 6 add\n",
+"sH_gIA exch getu16\n",
+"dup 0 gt{\n",
+"idDelta add\n",
+"cmapf2_glyph_array exch cmapf2_ch exch put\n",
+"}{\n",
+"pop\n",
+"}ifelse\n",
+"}{\n",
+"}ifelse\n",
+"}def\n",
+"16#00 1 16#ff{\n",
+"/cmapf2_ch_hi exch def\n",
+"sHKs cmapf2_ch_hi sHK_sz mul getu16\n",
+"/sH_offset exch def\n",
+"sH_gIA sH_offset sH_sz getinterval\n",
+"dup 0 getu16/firstCode exch def\n",
+"dup 2 getu16/entryCount exch def\n",
+"dup 4 gets16/idDelta exch def\n",
+"dup 6 getu16/idRangeOffset exch def\n",
+"pop\n",
+"sH_offset 0 eq{\n",
+"/cmapf2_ch_lo cmapf2_ch_hi def\n",
+"/cmapf2_ch_hi 0 def\n",
+".cmapf2_putGID\n",
+"}{\n",
+"16#00 1 16#ff{\n",
+"/cmapf2_ch_lo exch def\n",
+".cmapf2_putGID\n",
+"}for\n",
+"}ifelse\n",
+"}for\n",
+"pop\n",
+"0 1 cmapf2_glyph_array length 1 sub{\n",
+"dup cmapf2_glyph_array exch get\n",
+"null eq{cmapf2_glyph_array exch 0 put}{pop}ifelse\n",
+"}for\n",
+"cmapf2_glyph_array\n",
+"}bind\n",
+"4{\n",
+"/etab exch def\n",
+"/nseg2 etab 6 getu16 def\n",
+"14/endc etab 2 index nseg2 getinterval def\n",
+"2 add\n",
+"nseg2 add/startc etab 2 index nseg2 getinterval def\n",
+"nseg2 add/iddelta etab 2 index nseg2 getinterval def\n",
+"nseg2 add/idroff etab 2 index nseg2 getinterval def\n",
+"pop\n",
+"/firstcode startc 0 getu16 16#ff00 and dup 16#f000 ne{pop 0}if def\n",
+"/lastcode firstcode def\n",
+"/striptopbyte false def\n",
+"/putglyph{\n",
+"glyphs code 3 -1 roll put/code code 1 add def\n",
+"}bind def\n",
+"/numcodes 0 def/glyphs 0 0 2 nseg2 3 sub{\n",
+"/i2 exch def\n",
+"/scode startc i2 getu16 def\n",
+"/ecode endc i2 getu16 def\n",
+"ecode lastcode gt {\n",
+"/lastcode ecode def\n",
+"}if\n",
+"}for pop\n",
+"firstcode 16#f000 ge lastcode firstcode sub 255 le and {\n",
+"lastcode 255 and\n",
+"/striptopbyte true def\n",
+"} {\n",
+"lastcode\n",
+"}ifelse\n",
+"1 add\n",
+"array def\n",
+"glyphs length 1024 ge{\n",
+".array1024z 0 1024 glyphs length 1023 sub{glyphs exch 2 index putinterval}for\n",
+"glyphs dup length 1024 sub 3 -1 roll\n",
+"putinterval\n",
+"}{\n",
+"0 1 glyphs length 1 sub{glyphs exch 0 put}for\n",
+"}ifelse\n",
+"/numcodes 0 def/code 0 def\n",
+"0 2 nseg2 3 sub{\n",
+"/i2 exch def\n",
+"/scode startc i2 getu16 def\n",
+"/ecode endc i2 getu16 def\n",
+"numcodes scode firstcode sub\n",
+"exch sub 0 .max dup/code exch code exch add def\n",
+"ecode scode sub 1 add add numcodes add/numcodes exch def\n",
+"/delta iddelta i2 gets16 def\n",
+"TTFDEBUG{\n",
+"(scode=)print scode =only\n",
+"( ecode=)print ecode =only\n",
+"( delta=)print delta =only\n",
+"( droff=)print idroff i2 getu16 =\n",
+"}if\n",
+"idroff i2 getu16 dup 0 eq{\n",
+"pop scode delta add 65535 and 1 ecode delta add 65535 and\n",
+"striptopbyte {\n",
+"/code scode 255 and def\n",
+"} {\n",
+"/code scode def\n",
+"} ifelse\n",
+"{putglyph}for\n",
+"}{\n",
+"/gloff exch 14 nseg2 3 mul add 2 add i2 add add def\n",
+"striptopbyte {\n",
+"/code scode 255 and def\n",
+"} {\n",
+"/code scode def\n",
+"} ifelse\n",
+"0 1 ecode scode sub{\n",
+"2 mul gloff add etab exch getu16\n",
+"dup 0 ne{delta add 65535 and}if putglyph\n",
+"}for\n",
+"}ifelse\n",
+"}for glyphs/glyphs null def\n",
+"}bind\n",
+"6{\n",
+"dup 6 getu16/firstcode exch def dup 8 getu16/ng exch def\n",
+"firstcode ng add array\n",
+"0 1 firstcode 1 sub{2 copy 0 put pop}for\n",
+"dup firstcode ng getinterval\n",
+"0 1 ng 1 sub{\n",
+"dup 2 mul 10 add 4 index exch getu16 3 copy put pop pop\n",
+"}for pop exch pop\n",
+"}bind\n",
+".dicttomark readonly def\n",
+"/cmaparray{\n",
+"dup 0 getu16 cmapformats exch .knownget{\n",
+"TTFDEBUG{\n",
+"(cmap: format )print 1 index 0 getu16 = flush\n",
+"}if exec\n",
+"}{\n",
+"(Can't handle format )print 0 getu16 = flush\n",
+"0 1 255{}for 256 packedarray\n",
+"}ifelse\n",
+"TTFDEBUG{\n",
+"(cmap: length=)print dup length = dup ==\n",
+"}if\n",
+"}bind def\n",
+"/postremap mark\n",
+"/Cdot/Cdotaccent\n",
+"/Edot/Edotaccent\n",
+"/Eoverdot/Edotaccent\n",
+"/Gdot/Gdotaccent\n",
+"/Ldot/Ldotaccent\n",
+"/Zdot/Zdotaccent\n",
+"/cdot/cdotaccent\n",
+"/edot/edotaccent\n",
+"/eoverdot/edotaccent\n",
+"/gdot/gdotaccent\n",
+"/ldot/ldotaccent\n",
+"/zdot/zdotaccent\n",
+".dicttomark readonly def\n",
+"/get_from_stringarray\n",
+"{1 index type/stringtype eq{\n",
+"get\n",
+"}{\n",
+"exch{\n",
+"2 copy length ge{\n",
+"length sub\n",
+"}{\n",
+"exch get exit\n",
+"}ifelse\n",
+"}forall\n",
+"}ifelse\n",
+"}bind def\n",
+"/getinterval_from_stringarray\n",
+"{\n",
+"2 index type/stringtype eq{\n",
+"getinterval\n",
+"}{\n",
+"string exch 0\n",
+"4 3 roll{\n",
+"dup length\n",
+"dup 4 index lt{\n",
+"3 index exch sub\n",
+"exch pop 3 1 roll exch pop\n",
+"}{\n",
+"dup 3 1 roll\n",
+"4 index sub\n",
+"5 index length 4 index sub\n",
+"2 copy gt{exch}if pop\n",
+"dup 3 1 roll\n",
+"5 index exch getinterval\n",
+"5 index 4 index 3 index\n",
+"getinterval\n",
+"copy pop\n",
+"exch pop add exch pop 0 exch\n",
+"dup 3 index length ge{exit}if\n",
+"}ifelse\n",
+"}forall\n",
+"pop pop\n",
+"}ifelse\n",
+"}bind def\n",
+"/string_array_size\n",
+"{dup type/stringtype eq{\n",
+"length\n",
+"}{\n",
+"0 exch{length add}forall\n",
+"}ifelse\n",
+"}bind def\n",
+"/postformats mark\n",
+"16#00010000{\n",
+"pop MacGlyphEncoding\n",
+"}\n",
+"16#00020000{\n",
+"dup dup type/arraytype eq{0 get}if length 36 lt{\n",
+"TTFDEBUG{(post format 2.0 invalid.)= flush}if\n",
+"pop[]\n",
+"}{\n",
+"/postglyphs exch def\n",
+"/post_first postglyphs dup type/arraytype eq{0 get}if def\n",
+"post_first 32 getu16/numglyphs exch def\n",
+"/glyphnames numglyphs 2 mul 34 add def\n",
+"/postpos glyphnames def\n",
+"/total_length postglyphs//string_array_size exec def\n",
+"numglyphs array 0 1 numglyphs 1 sub{\n",
+"postpos total_length ge{\n",
+"1 numglyphs 1 sub{1 index exch/.notdef put}for\n",
+"exit\n",
+"}if\n",
+"postglyphs postpos//get_from_stringarray exec\n",
+"postglyphs postpos 1 add 2 index//getinterval_from_stringarray exec cvn\n",
+"exch postpos add 1 add/postpos exch def\n",
+"2 index 3 1 roll\n",
+"put\n",
+"}for\n",
+"/postnames exch def\n",
+"numglyphs array 0 1 numglyphs 1 sub{\n",
+"dup 2 mul 34 add postglyphs exch 2//getinterval_from_stringarray exec\n",
+"dup 0 get 8 bitshift exch 1 get add dup 258 lt{\n",
+"MacGlyphEncoding exch get\n",
+"}{\n",
+"dup 32768 ge{\n",
+"pop/.notdef\n",
+"}{\n",
+"258 sub dup postnames length ge{\n",
+"TTFDEBUG{( *** warning: glyph index past end of 'post' table)= flush}if\n",
+"pop\n",
+"exit\n",
+"}if\n",
+"postnames exch get\n",
+"postremap 1 index .knownget{exch pop}if\n",
+"}ifelse\n",
+"}ifelse\n",
+"2 index 3 1 roll put\n",
+"}for\n",
+"}\n",
+"ifelse\n",
+"}bind\n",
+"16#00030000{\n",
+"pop[]\n",
+"}bind\n",
+".dicttomark readonly def\n",
+"/first_post_string\n",
+"{\n",
+"post dup type/arraytype eq{0 get}if\n",
+"}bind def\n",
+"/.getpost{\n",
+"/glyphencoding post null eq{\n",
+"TTFDEBUG{(post missing)= flush}if[]\n",
+"}{\n",
+"postformats first_post_string 0 getu32 .knownget{\n",
+"TTFDEBUG{\n",
+"(post: format )print\n",
+"first_post_string\n",
+"dup 0 getu16 =only(,)print 2 getu16 = flush\n",
+"}if\n",
+"post exch exec\n",
+"}{\n",
+"TTFDEBUG{(post: unknown format )print post 0 getu32 = flush}if[]\n",
+"}ifelse\n",
+"}ifelse def\n",
+"}bind def\n",
+"/TTParser<<\n",
+"/Pos 0\n",
+"/post null\n",
+">>def\n",
+"/readu8\n",
+"{read not{\n",
+"mark(Insufficient data in the stream.)//error exec\n",
+"}if\n",
+"}bind def\n",
+"/readu16\n",
+"{dup//readu8 exec 8 bitshift exch//readu8 exec or\n",
+"}bind def\n",
+"/reads16\n",
+"{//readu16 exec 16#8000 xor 16#8000 sub\n",
+"}bind def\n",
+"/readu32\n",
+"{dup//readu16 exec 16 bitshift exch//readu16 exec or\n",
+"}bind def\n",
+"/reads32\n",
+"{dup//reads16 exec 16 bitshift exch//readu16 exec or\n",
+"}bind def\n",
+"/SkipToPosition\n",
+"{dup//TTParser/Pos get\n",
+"exch//TTParser exch/Pos exch put\n",
+"sub\n",
+"//PDFR_DEBUG{\n",
+"(Skipping )print dup//=only exec( bytes.)=\n",
+"}if\n",
+"dup 0 eq{\n",
+"pop pop\n",
+"}{\n",
+"dup 3 1 roll\n",
+"()/SubFileDecode filter\n",
+"exch\n",
+"{1 index//BlockBuffer readstring pop length\n",
+"dup 0 eq{pop exch pop exit}if\n",
+"sub\n",
+"}loop\n",
+"0 ne{\n",
+"mark(Insufficient data in the stream for SkipToPosition.)//error exec\n",
+"}if\n",
+"}ifelse\n",
+"}bind def\n",
+"/TagBuffer 4 string def\n",
+"/ParseTTTableDirectory\n",
+"{//PDFR_DEBUG{\n",
+"(ParseTTTableDirectory beg)=\n",
+"}if\n",
+"15 dict begin\n",
+"dup//readu32 exec 16#00010000 ne{\n",
+"mark(Unknown True Type version.)//error exec\n",
+"}if\n",
+"dup//readu16 exec/NumTables exch def\n",
+"dup//readu16 exec/SearchRange exch def\n",
+"dup//readu16 exec/EntrySelector exch def\n",
+"dup//readu16 exec/RangeShift exch def\n",
+"//PDFR_DEBUG{\n",
+"(NumTables = )print NumTables =\n",
+"}if\n",
+"NumTables{\n",
+"dup//TagBuffer readstring not{\n",
+"mark(Could not read TT tag.)//error exec\n",
+"}if\n",
+"cvn\n",
+"[2 index//readu32 exec pop\n",
+"2 index//readu32 exec\n",
+"3 index//readu32 exec\n",
+"]\n",
+"//PDFR_DEBUG{\n",
+"2 copy exch//=only exec( )print ==\n",
+"}if\n",
+"def\n",
+"}repeat\n",
+"pop\n",
+"//TTParser/Pos 12 NumTables 16 mul add put\n",
+"currentdict end\n",
+"//PDFR_DEBUG{\n",
+"(ParseTTTableDirectory end)=\n",
+"}if\n",
+"}bind def\n",
+"/ParseTTcmap\n",
+"{//PDFR_DEBUG{\n",
+"(ParseTTcmap beg)=\n",
+"}if\n",
+"/cmap get aload pop\n",
+"3 1 roll\n",
+"7 dict begin\n",
+"//PDFR_DEBUG{\n",
+"(Current position = )print//TTParser/Pos get =\n",
+"(cmap position = )print dup =\n",
+"}if\n",
+"1 index exch//SkipToPosition exec\n",
+"//TTParser/Pos get/TablePos exch def\n",
+"dup//readu16 exec pop\n",
+"dup//readu16 exec/NumEncodings exch def\n",
+"//PDFR_DEBUG{\n",
+"(NumEncodings = )print NumEncodings =\n",
+"}if\n",
+"null\n",
+"NumEncodings{\n",
+"1 index//readu32 exec\n",
+"2 index//readu32 exec\n",
+"3 array dup 3 2 roll 0 exch put\n",
+"2 index null ne{\n",
+"dup 0 get 3 index 0 get sub\n",
+"3 index exch 1 exch put\n",
+"}if\n",
+"dup 4 3 roll pop 3 1 roll\n",
+"def\n",
+"}repeat\n",
+"dup 0 get\n",
+"4 3 roll exch sub\n",
+"1 exch put\n",
+"//PDFR_DEBUG{\n",
+"currentdict{\n",
+"exch dup type/integertype eq{\n",
+"//PrintHex exec( )print ==\n",
+"}{\n",
+"pop pop\n",
+"}ifelse\n",
+"}forall\n",
+"}if\n",
+"4 NumEncodings 8 mul add/HeaderLength exch def\n",
+"//TTParser/Pos//TTParser/Pos get HeaderLength add put\n",
+"0\n",
+"NumEncodings{\n",
+"16#7FFFFFF null\n",
+"currentdict{\n",
+"1 index type/integertype eq{\n",
+"exch pop dup 0 get\n",
+"dup 5 index gt{\n",
+"dup 4 index lt{\n",
+"4 1 roll\n",
+"exch pop exch pop\n",
+"}{\n",
+"pop pop\n",
+"}ifelse\n",
+"}{\n",
+"pop pop\n",
+"}ifelse\n",
+"}{\n",
+"pop pop\n",
+"}ifelse\n",
+"}forall\n",
+"//PDFR_DEBUG{\n",
+"(Obtaining subtable for )print dup ==\n",
+"}if\n",
+"3 2 roll pop\n",
+"3 copy pop\n",
+"TablePos add//SkipToPosition exec\n",
+"3 copy exch pop 1 get\n",
+"//TTParser/Pos//TTParser/Pos get 3 index add put\n",
+"string\n",
+"readstring not{\n",
+"mark(Can't read a cmap subtable.)//error exec\n",
+"}if\n",
+"2 exch put\n",
+"}repeat\n",
+"pop pop\n",
+"currentdict end\n",
+"//PDFR_DEBUG{\n",
+"(ParseTTcmap end)=\n",
+"}if\n",
+"}bind def\n",
+"/GetTTEncoding\n",
+"{//PDFR_DEBUG{\n",
+"(GetTTEncoding beg)=\n",
+"}if\n",
+"get\n",
+"exch pop\n",
+"2 get\n",
+"10 dict begin\n",
+"/TTFDEBUG//PDFR_DEBUG def\n",
+"//cmaparray exec\n",
+"end\n",
+"//PDFR_DEBUG{\n",
+"(GetTTEncoding end)=\n",
+"dup ==\n",
+"}if\n",
+"}bind def\n",
+"/InverseEncoding\n",
+"{\n",
+"256 dict begin\n",
+"dup length 1 sub -1 0{\n",
+"2 copy get\n",
+"exch\n",
+"1 index currentdict exch//knownget exec{\n",
+"dup type/arraytype eq{\n",
+"aload length 1 add array astore\n",
+"}{\n",
+"2 array astore\n",
+"}ifelse\n",
+"}if\n",
+"def\n",
+"}for\n",
+"pop\n",
+"currentdict end\n",
+"}bind def\n",
+"/GetMacRomanEncodingInverse\n",
+"{//PDFReader/MacRomanEncodingInverse get\n",
+"dup null eq{\n",
+"pop\n",
+"MacRomanEncoding//InverseEncoding exec\n",
+"dup//PDFReader exch/MacRomanEncodingInverse exch put\n",
+"}if\n",
+"}bind def\n",
+"/PutCharStringSingle\n",
+"{\n",
+"dup 3 index length lt{\n",
+"2 index exch get\n",
+"dup 0 ne{\n",
+"def\n",
+"}{\n",
+"pop pop\n",
+"}ifelse\n",
+"}{\n",
+"pop pop\n",
+"}ifelse\n",
+"}bind def\n",
+"/PutCharString\n",
+"{1 index type/nametype ne{\n",
+"mark(Bad charstring name)//error exec\n",
+"}if\n",
+"dup type/arraytype eq{\n",
+"{\n",
+"3 copy//PutCharStringSingle exec\n",
+"pop pop\n",
+"}forall\n",
+"pop\n",
+"}{\n",
+"//PutCharStringSingle exec\n",
+"}ifelse\n",
+"}bind def\n",
+"/ComposeCharStrings\n",
+"{\n",
+"//PDFR_DEBUG{\n",
+"(ComposeCharStrings beg)=\n",
+"}if\n",
+"1 index length 1 add dict begin\n",
+"/.notdef 0 def\n",
+"exch\n",
+"//TTParser/post get\n",
+"dup null ne{\n",
+"exch\n",
+"1 index length 1 sub -1 0{\n",
+"dup 3 index exch get exch\n",
+"dup 0 eq 2 index/.notdef eq or{\n",
+"pop pop\n",
+"}{\n",
+"def\n",
+"}ifelse\n",
+"}for\n",
+"}if\n",
+"exch pop exch\n",
+"{\n",
+"//PutCharString exec\n",
+"}forall\n",
+"pop\n",
+"currentdict end\n",
+"//PDFR_DEBUG{\n",
+"(ComposeCharStrings end)=\n",
+"}if\n",
+"}bind def\n",
+"/ParseTTpost\n",
+"{\n",
+"//PDFR_DEBUG{\n",
+"(ParseTTpost beg)=\n",
+"}if\n",
+"/post get aload pop\n",
+"3 1 roll\n",
+"//PDFR_DEBUG{\n",
+"(Current position = )print//TTParser/Pos get =\n",
+"(post position = )print dup =\n",
+"}if\n",
+"1 index exch//SkipToPosition exec\n",
+"//TTParser/Pos//TTParser/Pos get 4 index add put\n",
+"exch dup 65535 le{\n",
+"string\n",
+"readstring not{\n",
+"mark(Insufficient data in the stream for ParseTTpost.)//error exec\n",
+"}if\n",
+"}{\n",
+"[3 1 roll\n",
+"dup 16384 div floor cvi\n",
+"exch 1 index 16384 mul\n",
+"sub exch\n",
+"1 sub 0 1 3 -1 roll\n",
+"{\n",
+"1 add index\n",
+"16384 string readstring not{\n",
+"mark(Insufficient data in the stream for ParseTTpost.)//error exec\n",
+"}if\n",
+"}for\n",
+"counttomark -2 roll\n",
+"string readstring not{\n",
+"mark(Insufficient data in the stream for ParseTTpost.)//error exec\n",
+"}if\n",
+"]\n",
+"}ifelse\n",
+"1 dict begin\n",
+"/post exch def\n",
+"//.getpost exec\n",
+"//TTParser/post glyphencoding put\n",
+"//PDFR_DEBUG{\n",
+"(ParseTTpost end)=\n",
+"glyphencoding ==\n",
+"}if\n",
+"end\n",
+"}bind def\n",
+"/MakeTTCharStrings\n",
+"{//MakeStreamReader exec\n",
+"dup dup//ParseTTTableDirectory exec\n",
+"//TTParser/post null put\n",
+"dup/post//knownget exec{\n",
+"0 get\n",
+"1 index/cmap get 0 get\n",
+"lt{\n",
+"2 copy//ParseTTpost exec\n",
+"//ParseTTcmap exec\n",
+"}{\n",
+"2 copy//ParseTTcmap exec\n",
+"3 1 roll\n",
+"//ParseTTpost exec\n",
+"}ifelse\n",
+"}{\n",
+"//ParseTTcmap exec\n",
+"}ifelse\n",
+"{\n",
+"dup 16#00030001 known{\n",
+"//PDFR_DEBUG{\n",
+"(Using the TT cmap encoding for Windows Unicode.)=\n",
+"}if\n",
+"16#00030001//GetTTEncoding exec\n",
+"AdobeGlyphList//ComposeCharStrings exec\n",
+"exit\n",
+"}if\n",
+"dup 16#00010000 known{\n",
+"//PDFR_DEBUG{\n",
+"(Using the TT cmap encoding for Macintosh Roman.)=\n",
+"}if\n",
+"16#00010000//GetTTEncoding exec\n",
+"PDFEncoding dup null eq{\n",
+"pop//GetMacRomanEncodingInverse exec\n",
+"}{\n",
+"//InverseEncoding exec\n",
+"}ifelse\n",
+"//ComposeCharStrings exec\n",
+"exit\n",
+"}if\n",
+"dup 16#00030000 known{\n",
+"//PDFR_DEBUG{\n",
+"(Using the TT cmap encoding 3.0 - not sure why Ghostscript writes it since old versions.)=\n",
+"}if\n",
+"16#00030000//GetTTEncoding exec\n",
+"PDFEncoding dup null eq{\n",
+"pop//GetMacRomanEncodingInverse exec\n",
+"}{\n",
+"//InverseEncoding exec\n",
+"}ifelse\n",
+"//ComposeCharStrings exec\n",
+"exit\n",
+"}if\n",
+"mark(True Type cmap has no useful encodings.)//error exec\n",
+"}loop\n",
+"//PDFR_DEBUG{\n",
+"(CharStrings <<)=\n",
+"dup{\n",
+"exch\n",
+"dup type/nametype eq{\n",
+"//=only exec\n",
+"}{\n",
+"==\n",
+"}ifelse\n",
+"( )print ==\n",
+"}forall\n",
+"(>>)=\n",
+"}if\n",
+"}bind def\n",
+"/ScaleVal\n",
+"{\n",
+"aload pop\n",
+"1 index sub\n",
+"3 2 roll mul add\n",
+"}bind def\n",
+"/ScaleArg\n",
+"{\n",
+"aload pop\n",
+"1 index sub\n",
+"3 1 roll\n",
+"sub exch div\n",
+"}bind def\n",
+"/ScaleArgN\n",
+"{\n",
+"dup length 2 sub -2 0{\n",
+"2\n",
+"2 index 3 1 roll getinterval\n",
+"3 2 roll\n",
+"exch//ScaleArg exec\n",
+"1 index length 2 idiv 1 add 1 roll\n",
+"}for\n",
+"pop\n",
+"}bind def\n",
+"/ComputeFunction_10\n",
+"{\n",
+"//PDFR_DEBUG{\n",
+"(ComputeFunction_10 beg )print 1 index//=only exec( stack=)print count =\n",
+"}if\n",
+"exch\n",
+"dup 1 eq{\n",
+"pop dup length 1 sub get\n",
+"}{\n",
+"1 index length 1 sub mul\n",
+"dup dup floor sub\n",
+"dup 0 eq{\n",
+"pop cvi get\n",
+"}{\n",
+"3 1 roll floor cvi\n",
+"2 getinterval\n",
+"aload pop\n",
+"2 index mul 3 2 roll 1 exch sub 3 2 roll mul add\n",
+"}ifelse\n",
+"}ifelse\n",
+"//PDFR_DEBUG{\n",
+"(ComputeFunction_10 end )print dup//=only exec( stack=)print count =\n",
+"}if\n",
+"}bind def\n",
+"/ComputeFunction_n0\n",
+"{\n",
+"//PDFR_DEBUG{\n",
+"(ComputeFunction_n0 beg N=)print dup//=only exec( stack=)print count =\n",
+"}if\n",
+"dup 0 eq{\n",
+"pop\n",
+"}{\n",
+"dup 2 add -1 roll\n",
+"dup 3 index length 1 sub ge{\n",
+"pop 1 sub\n",
+"exch dup length 1 sub get exch\n",
+"//PDFReader/ComputeFunction_n0 get exec\n",
+"}{\n",
+"dup floor cvi dup\n",
+"4 index exch get\n",
+"3 index dup\n",
+"5 add copy\n",
+"6 2 roll\n",
+"pop pop pop pop\n",
+"1 sub\n",
+"//PDFReader/ComputeFunction_n0 get exec\n",
+"3 2 roll pop\n",
+"exch\n",
+"4 3 roll exch\n",
+"4 add 2 roll 1 add\n",
+"3 2 roll exch get\n",
+"exch 1 sub\n",
+"//PDFReader/ComputeFunction_n0 get exec\n",
+"1 index mul\n",
+"3 1 roll\n",
+"1 exch sub mul add\n",
+"}ifelse\n",
+"}ifelse\n",
+"//PDFR_DEBUG{\n",
+"(ComputeFunction_n0 end )print dup//=only exec( stack=)print count =\n",
+"}if\n",
+"}bind def\n",
+"/FunctionToProc_x01\n",
+"{\n",
+"dup/Domain get exch\n",
+"dup/Data get 0 get exch\n",
+"/Size get length\n",
+"[4 1 roll\n",
+"//PDFR_DEBUG{\n",
+"{(function beg, stack =)print count//=only exec(\\n)print}/exec load\n",
+"5 2 roll\n",
+"}if\n",
+"dup 1 gt{\n",
+"{mark exch\n",
+"3 add 2 roll\n",
+"//ScaleArgN exec\n",
+"counttomark dup\n",
+"3 add -2 roll\n",
+"pop exch\n",
+"//ComputeFunction_n0 exec\n",
+"}/exec load\n",
+"}{\n",
+"pop\n",
+"3 1/roll load//ScaleArg/exec load\n",
+"/exch load\n",
+"//ComputeFunction_10/exec load\n",
+"}ifelse\n",
+"//PDFR_DEBUG{\n",
+"(function end, stack =)/print load/count load//=only/exec load(\\n)/print load\n",
+"}if\n",
+"]cvx\n",
+"//PDFR_DEBUG{\n",
+"(Made a procedure for the 1-result function :)=\n",
+"dup ==\n",
+"}if\n",
+"}bind def\n",
+"/FunctionProcDebugBeg\n",
+"{(FunctionProcDebugBeg )print count =\n",
+"}bind def\n",
+"/FunctionProcDebugEnd\n",
+"{(FunctionProcDebugEnd )print count =\n",
+"}bind def\n",
+"/FunctionToProc_x0n\n",
+"{\n",
+"PDFR_DEBUG{\n",
+"(FunctionToProc_x0n beg m=)print dup =\n",
+"}if\n",
+"1 index/Size get length exch\n",
+"dup 7 mul 2 add array\n",
+"PDFR_DEBUG{\n",
+"dup 0//FunctionProcDebugBeg put\n",
+"}{\n",
+"dup 0//DoNothing put\n",
+"}ifelse\n",
+"dup 1/exec load put\n",
+"dup 2 5 index/Domain get put\n",
+"2 index 1 eq{\n",
+"dup 3//ScaleArg put\n",
+"}{\n",
+"dup 3//ScaleArgN put\n",
+"}ifelse\n",
+"dup 4/exec load put\n",
+"1 index 1 sub 0 exch 1 exch{\n",
+"dup 7 mul 5 add\n",
+"1 index 4 index 1 sub ne{\n",
+"dup 3 index exch 6 index put 1 add\n",
+"dup 3 index exch/copy load put 1 add\n",
+"}if\n",
+"[\n",
+"6 index/Data get 3 index get\n",
+"6 index 1 eq{\n",
+"//ComputeFunction_10/exec load\n",
+"}{\n",
+"6 index\n",
+"//ComputeFunction_n0/exec load\n",
+"}ifelse\n",
+"]cvx\n",
+"3 index exch 2 index exch put 1 add\n",
+"2 index 1 index/exec load put 1 add\n",
+"1 index 4 index 1 sub ne{\n",
+"2 index 1 index 6 index 1 add put 1 add\n",
+"2 index 1 index 1 put 1 add\n",
+"2 index 1 index/roll load put\n",
+"}if\n",
+"pop pop\n",
+"}for\n",
+"PDFR_DEBUG{\n",
+"dup dup length 2 sub//FunctionProcDebugEnd put\n",
+"}{\n",
+"dup dup length 2 sub//DoNothing put\n",
+"}ifelse\n",
+"dup dup length 1 sub/exec load put\n",
+"cvx exch pop exch pop exch pop\n",
+"//PDFR_DEBUG{\n",
+"(Made a procedure for the n-argument function :)=\n",
+"dup ==\n",
+"}if\n",
+"PDFR_DEBUG{\n",
+"(FunctionToProc_x0n end)=\n",
+"}if\n",
+"}bind def\n",
+"/MakeTableRec\n",
+"{\n",
+"0\n",
+"exec\n",
+"}bind def\n",
+"/MakeTable\n",
+"{//PDFR_DEBUG{\n",
+"(MakeTable beg )print count =\n",
+"}if\n",
+"1 index/Size get exch\n",
+"1 sub dup\n",
+"3 1 roll\n",
+"get\n",
+"array\n",
+"1 index 0 eq{\n",
+"exch pop exch pop\n",
+"}{\n",
+"dup length 1 sub -1 0{\n",
+"3 index 3 index//MakeTableRec exec\n",
+"2 index 3 1 roll put\n",
+"}for\n",
+"exch pop exch pop\n",
+"}ifelse\n",
+"//PDFR_DEBUG{\n",
+"(MakeTable end )print count =\n",
+"}if\n",
+"}bind def\n",
+"//MakeTableRec 0//MakeTable put\n",
+"/StoreSample\n",
+"{\n",
+"1 sub\n",
+"dup 0 eq{\n",
+"pop\n",
+"}{\n",
+"-1 1{\n",
+"I exch get get\n",
+"}for\n",
+"}ifelse\n",
+"I 0 get 3 2 roll put\n",
+"}bind def\n",
+"/ReadSample32\n",
+"{\n",
+"4{\n",
+"File read not{\n",
+"mark(Insufficient data for function.)//error exec\n",
+"}if\n",
+"}repeat\n",
+"pop\n",
+"3 1 roll exch\n",
+"256 mul add 256 mul add\n",
+"//1_24_bitshift_1_sub div\n",
+"}bind def\n",
+"/ReadSample\n",
+"{\n",
+"Buffer BitsLeft BitsPerSample\n",
+"{2 copy ge{\n",
+"exit\n",
+"}if\n",
+"3 1 roll\n",
+"8 add 3 1 roll\n",
+"256 mul File read not{\n",
+"mark(Insufficient data for function.)//error exec\n",
+"}if\n",
+"add\n",
+"3 1 roll\n",
+"}loop\n",
+"sub dup\n",
+"2 index exch\n",
+"neg bitshift\n",
+"2 copy exch bitshift\n",
+"4 3 roll exch sub\n",
+"/Buffer exch def\n",
+"exch/BitsLeft exch def\n",
+"Div div\n",
+"}bind def\n",
+"/ReadSamplesRec\n",
+"{0\n",
+"exec\n",
+"}bind def\n",
+"/ReadSamples\n",
+"{\n",
+"//PDFR_DEBUG{\n",
+"(ReadSamples beg )print count =\n",
+"}if\n",
+"dup 1 eq{\n",
+"pop\n",
+"0 1 Size 0 get 1 sub{\n",
+"I exch 0 exch put\n",
+"0 1 M 1 sub{\n",
+"dup Range exch 2 mul 2 getinterval\n",
+"//PDFR_DEBUG{\n",
+"(Will read a sample ... )print\n",
+"}if\n",
+"BitsPerSample 32 eq{//ReadSample32}{//ReadSample}ifelse\n",
+"exec exch//ScaleVal exec\n",
+"//PDFR_DEBUG{\n",
+"(value=)print dup =\n",
+"}if\n",
+"exch Table exch get\n",
+"Size length//StoreSample exec\n",
+"}for\n",
+"}for\n",
+"}{\n",
+"1 sub\n",
+"dup Size exch get 0 exch 1 exch 1 sub{\n",
+"I exch 2 index exch put\n",
+"dup//ReadSamplesRec exec\n",
+"}for\n",
+"pop\n",
+"}ifelse\n",
+"//PDFR_DEBUG{\n",
+"(ReadSamples end )print count =\n",
+"}if\n",
+"}bind def\n",
+"//ReadSamplesRec 0//ReadSamples put\n",
+"/StreamToArray\n",
+"{//PDFR_DEBUG{\n",
+"(StreamToArray beg )print count =\n",
+"}if\n",
+"userdict/FuncDataReader get begin\n",
+"dup/BitsPerSample get/BitsPerSample exch def\n",
+"dup/Size get length/N exch def\n",
+"dup/Range get length 2 idiv/M exch def\n",
+"1 BitsPerSample bitshift 1 sub/Div exch def\n",
+"/BitsLeft 0 def\n",
+"/Buffer 0 def\n",
+"dup/Size get/Size exch def\n",
+"dup/Range get/Range exch def\n",
+"/File 1 index//MakeStreamReader exec def\n",
+"/I[N{0}repeat]def\n",
+"M array\n",
+"dup length 1 sub -1 0{\n",
+"2 index N//MakeTable exec\n",
+"2 index 3 1 roll put\n",
+"}for\n",
+"/Table exch def\n",
+"N//ReadSamples exec\n",
+"PDFR_DEBUG{\n",
+"(Table = )print Table ==\n",
+"}if\n",
+"/Data Table put\n",
+"end\n",
+"//PDFR_DEBUG{\n",
+"(StreamToArray end )print count =\n",
+"}if\n",
+"}bind def\n",
+"/FunctionToProc10\n",
+"{\n",
+"PDFR_DEBUG{\n",
+"(FunctionToProc10 beg, Range = )print dup/Range get ==\n",
+"}if\n",
+"dup/Order//knownget exec{\n",
+"1 ne{\n",
+"(Underimplemented function Type 0 Order 3.)=\n",
+"}if\n",
+"}if\n",
+"dup//StreamToArray exec\n",
+"dup/Range get length dup 2 eq{\n",
+"pop//FunctionToProc_x01 exec\n",
+"}{\n",
+"2 idiv//FunctionToProc_x0n exec\n",
+"}ifelse\n",
+"PDFR_DEBUG{\n",
+"(FunctionToProc10 end)=\n",
+"}if\n",
+"}bind def\n",
+"/FunctionToProc12\n",
+"{begin\n",
+"currentdict/C0//knownget exec{length 1 eq}{true}ifelse{\n",
+"N\n",
+"currentdict/C0//knownget exec{\n",
+"0 get\n",
+"}{\n",
+"0\n",
+"}ifelse\n",
+"currentdict/C1//knownget exec{\n",
+"0 get\n",
+"}{\n",
+"1\n",
+"}ifelse\n",
+"1 index sub\n",
+"[4 1 roll\n",
+"{\n",
+"4 2 roll\n",
+"exp mul add\n",
+"}aload pop\n",
+"]cvx\n",
+"}{\n",
+"[\n",
+"0 1 C0 length 1 sub{\n",
+"N\n",
+"C0 2 index get\n",
+"C1 3 index get\n",
+"4 3 roll pop\n",
+"1 index sub\n",
+"[/dup load\n",
+"5 2 roll\n",
+"{\n",
+"4 2 roll\n",
+"exp mul add\n",
+"exch\n",
+"}aload pop\n",
+"]cvx\n",
+"/exec load\n",
+"}for\n",
+"/pop load\n",
+"]cvx\n",
+"}ifelse\n",
+"end\n",
+"//PDFR_DEBUG{\n",
+"(FunctionType2Proc : )print dup ==\n",
+"}if\n",
+"}bind def\n",
+"/FunctionToProc14\n",
+"{//MakeStreamReader exec cvx exec\n",
+"//PDFR_DEBUG{\n",
+"(FunctionType4Proc : )print dup ==\n",
+"}if\n",
+"}bind def\n",
+"/FunctionToProc1\n",
+"{\n",
+"dup/FunctionType get\n",
+"{dup 0 eq{\n",
+"pop//FunctionToProc10 exec exit\n",
+"}if\n",
+"dup 2 eq{\n",
+"pop//FunctionToProc12 exec exit\n",
+"}if\n",
+"dup 4 eq{\n",
+"pop//FunctionToProc14 exec exit\n",
+"}if\n",
+"mark exch(Function type )exch( isn't implemented yet.)//error exec\n",
+"}loop\n",
+"}bind def\n",
+"/FunctionToProc20\n",
+"{\n",
+"PDFR_DEBUG{\n",
+"(FunctionToProc20, Range = )print dup/Range get ==\n",
+"}if\n",
+"dup/Order//knownget exec{\n",
+"1 ne{\n",
+"(Underimplemented function Type 0 Order 3.)=\n",
+"}if\n",
+"}if\n",
+"dup//StreamToArray exec\n",
+"dup/Range get length dup 2 eq{\n",
+"pop//FunctionToProc_x01 exec\n",
+"}{\n",
+"2 idiv//FunctionToProc_x0n exec\n",
+"}ifelse\n",
+"}bind def\n",
+"/FunctionToProc\n",
+"{//PDFR_DEBUG{\n",
+"(FunctionToProc beg )print count =\n",
+"}if\n",
+"dup type/dicttype eq{\n",
+"dup/Domain get length 2 idiv\n",
+"{\n",
+"dup 1 eq{\n",
+"pop//FunctionToProc1 exec exit\n",
+"}if\n",
+"dup 2 eq{\n",
+"pop//FunctionToProc20 exec exit\n",
+"}if\n",
+"mark(Functions with many arguments aren't implemented yet.)//error exec\n",
+"}loop\n",
+"}{\n",
+"//PDFR_DEBUG{(Not a function dict, assume already a procedure.)print}if\n",
+"}ifelse\n",
+"//PDFR_DEBUG{\n",
+"(FunctionToProc end )print count =\n",
+"}if\n",
+"}bind def\n",
+"/spotfunctions mark\n",
+"/Round{\n",
+"abs exch abs 2 copy add 1 le{\n",
+"dup mul exch dup mul add 1 exch sub\n",
+"}{\n",
+"1 sub dup mul exch 1 sub dup mul add 1 sub\n",
+"}ifelse\n",
+"}\n",
+"/Diamond{\n",
+"abs exch abs 2 copy add .75 le{\n",
+"dup mul exch dup mul add 1 exch sub\n",
+"}{\n",
+"2 copy add 1.23 le{\n",
+".85 mul add 1 exch sub\n",
+"}{\n",
+"1 sub dup mul exch 1 sub dup mul add 1 sub\n",
+"}ifelse\n",
+"}ifelse\n",
+"}\n",
+"/Ellipse{\n",
+"abs exch abs 2 copy 3 mul exch 4 mul add 3 sub dup 0 lt{\n",
+"pop dup mul exch .75 div dup mul add 4 div 1 exch sub\n",
+"}{\n",
+"dup 1 gt{\n",
+"pop 1 exch sub dup mul exch 1 exch sub\n",
+".75 div dup mul add 4 div 1 sub\n",
+"}{\n",
+".5 exch sub exch pop exch pop\n",
+"}ifelse\n",
+"}ifelse\n",
+"}\n",
+"/EllipseA{dup mul .9 mul exch dup mul add 1 exch sub}\n",
+"/InvertedEllipseA{dup mul .9 mul exch dup mul add 1 sub}\n",
+"/EllipseB{dup 5 mul 8 div mul exch dup mul exch add sqrt 1 exch sub}\n",
+"/EllipseC{dup mul .9 mul exch dup mul add 1 exch sub}\n",
+"/InvertedEllipseC{dup mul .9 mul exch dup mul add 1 sub}\n",
+"/Line{exch pop abs neg}\n",
+"/LineX{pop}\n",
+"/LineY{exch pop}\n",
+"/Square{abs exch abs 2 copy lt{exch}if pop neg}\n",
+"/Cross{abs exch abs 2 copy gt{exch}if pop neg}\n",
+"/Rhomboid{abs exch abs 0.9 mul add 2 div}\n",
+"/DoubleDot{2{360 mul sin 2 div exch}repeat add}\n",
+"/InvertedDoubleDot{2{360 mul sin 2 div exch}repeat add neg}\n",
+"/SimpleDot{dup mul exch dup mul add 1 exch sub}\n",
+"/InvertedSimpleDot{dup mul exch dup mul add 1 sub}\n",
+"/CosineDot{180 mul cos exch 180 mul cos add 2 div}\n",
+"/Double{exch 2 div exch 2{360 mul sin 2 div exch}repeat add}\n",
+"/InvertedDouble{\n",
+"exch 2 div exch 2{360 mul sin 2 div exch}repeat add neg\n",
+"}\n",
+".dicttomark readonly def\n",
+"/CheckColorSpace\n",
+"{\n",
+"dup type/arraytype ne{\n",
+"mark(Resource )3 index( must be an array.)//error exec\n",
+"}if\n",
+"}bind def\n",
+"/SubstitutePDFColorSpaceRec\n",
+"{0\n",
+"exec\n",
+"}bind def\n",
+"/SubstitutePDFColorSpace\n",
+"{\n",
+"{\n",
+"dup 0 get/Pattern eq{\n",
+"dup length 1 gt{\n",
+"dup dup 1//CheckColorSpace//ResolveA exec\n",
+"dup type/nametype ne{\n",
+"//SubstitutePDFColorSpaceRec exec\n",
+"}if\n",
+"1 exch put\n",
+"}if\n",
+"exit\n",
+"}if\n",
+"dup 0 get/Indexed eq{\n",
+"exit\n",
+"}if\n",
+"dup 0 get/Separation eq{\n",
+"dup dup 2//CheckColorSpace//ResolveA exec\n",
+"dup type/nametype ne{\n",
+"//SubstitutePDFColorSpaceRec exec\n",
+"}if\n",
+"2 exch put\n",
+"exit\n",
+"}if\n",
+"dup 0 get/CalGray eq{\n",
+"1 get\n",
+"dup/Gamma//knownget exec{\n",
+"[exch[exch/exp load]cvx dup dup]\n",
+"1 index exch/DecodeLMN exch put\n",
+"}if\n",
+"[exch/CIEBasedA exch]\n",
+"exit\n",
+"}if\n",
+"dup 0 get/CalRGB eq{\n",
+"1 get\n",
+"dup/Matrix//knownget exec{\n",
+"1 index exch/MatrixLMN exch put\n",
+"}if\n",
+"dup/Gamma//knownget exec{\n",
+"aload pop\n",
+"[exch/exp load]cvx\n",
+"3 1 roll\n",
+"[exch/exp load]cvx\n",
+"3 1 roll\n",
+"[exch/exp load]cvx\n",
+"3 1 roll\n",
+"3 array astore\n",
+"1 index exch/DecodeLMN exch put\n",
+"}if\n",
+"[exch/CIEBasedABC exch]\n",
+"exit\n",
+"}if\n",
+"dup 0 get/Lab eq{\n",
+"1 get\n",
+"begin\n",
+"currentdict/Range//knownget exec{aload pop}{-100 100 -100 100}ifelse\n",
+"0 100 6 2 roll 6 array astore\n",
+"/RangeABC exch def\n",
+"/DecodeABC[{16 add 116 div}bind{500 div}bind{200 div}bind]def\n",
+"/MatrixABC[1 1 1 1 0 0 0 0 -1]def\n",
+"{dup 6 29 div ge{dup dup mul mul}{4 29 div sub 108 841 div mul}ifelse}\n",
+"/DecodeLMN[\n",
+"[3 index aload pop WhitePoint 0 get/mul load]cvx\n",
+"[4 index aload pop WhitePoint 1 get/mul load]cvx\n",
+"[5 index aload pop WhitePoint 2 get/mul load]cvx\n",
+"]def pop\n",
+"//PDFR_DEBUG{\n",
+"(Constructed from Lab <<)=\n",
+"currentdict{exch = ==}forall\n",
+"(>>)=\n",
+"}if\n",
+"[/CIEBasedABC currentdict]\n",
+"end\n",
+"exit\n",
+"pop\n",
+"}if\n",
+"dup 0 get/CIEBasedA eq{exit}if\n",
+"dup 0 get/CIEBasedABC eq{exit}if\n",
+"mark exch(Unimplemented color space )exch//error exec\n",
+"}loop\n",
+"}bind def\n",
+"//SubstitutePDFColorSpaceRec 0//SubstitutePDFColorSpace put\n",
+"/ResolveArrayElement\n",
+"{2 copy get\n",
+"dup type dup/arraytype eq exch\n",
+"/packedarraytype eq or{\n",
+"dup length 1 ge exch xcheck and{\n",
+"2 copy get\n",
+"dup 0 get type/integertype eq\n",
+"1 index 1 get type dup/arraytype\n",
+"eq exch\n",
+"/packedarraytype eq or\n",
+"and{\n",
+"exec\n",
+"2 index 4 1 roll put\n",
+"}{\n",
+"pop pop\n",
+"}ifelse\n",
+"}{\n",
+"pop\n",
+"}ifelse\n",
+"}{\n",
+"pop pop\n",
+"}ifelse\n",
+"}bind def\n",
+"/ResolveColorSpaceArrayRec\n",
+"{0\n",
+"exec\n",
+"}bind def\n",
+"/SetColorSpaceSafe\n",
+"{\n",
+"PDFR_DEBUG{\n",
+"(SetColorSpaceSafe beg)=\n",
+"}if\n",
+"currentcolorspace dup type/arraytype eq{\n",
+"1 index type/arraytype eq{\n",
+"dup length 2 index length eq{\n",
+"false exch\n",
+"dup length 0 exch 1 exch 1 sub{\n",
+"dup\n",
+"4 index exch get exch\n",
+"2 index exch get\n",
+"ne{\n",
+"exch pop true exch exit\n",
+"}if\n",
+"}for\n",
+"pop\n",
+"{\n",
+"setcolorspace\n",
+"}{\n",
+"pop\n",
+"}ifelse\n",
+"}{\n",
+"pop setcolorspace\n",
+"}ifelse\n",
+"}{\n",
+"pop setcolorspace\n",
+"}ifelse\n",
+"}{\n",
+"pop setcolorspace\n",
+"}ifelse\n",
+"PDFR_DEBUG{\n",
+"(SetColorSpaceSafe end)=\n",
+"}if\n",
+"}bind def\n",
+"/ResolveColorSpaceArray\n",
+"{\n",
+"//PDFR_DEBUG{\n",
+"(ResolveColorSpaceArray beg )print dup ==\n",
+"}if\n",
+"dup 0 get/Indexed eq{\n",
+"1//ResolveArrayElement exec\n",
+"dup dup 1 get\n",
+"dup type/arraytype eq{\n",
+"//SubstitutePDFColorSpace exec\n",
+"//ResolveColorSpaceArrayRec exec\n",
+"1 exch put\n",
+"}{\n",
+"pop pop\n",
+"}ifelse\n",
+"}if\n",
+"dup 0 get/Separation eq{\n",
+"dup dup 1 get UnPDFEscape 1 exch put\n",
+"3//ResolveArrayElement exec\n",
+"dup 3 get//FunctionToProc exec\n",
+"2 copy 3 exch put\n",
+"pop\n",
+"}if\n",
+"dup 0 get/Pattern eq{\n",
+"dup length 1 gt{dup\n",
+"1 get dup type/arraytype eq{\n",
+"ResolveColorSpaceArray\n",
+"1 index 1 3 -1 roll put\n",
+"}{pop}ifelse}if\n",
+"}if\n",
+"PDFR_DEBUG{\n",
+"(Construcrted color space :)=\n",
+"dup ==\n",
+"}if\n",
+"//PDFR_DEBUG{\n",
+"(ResolveColorSpaceArray end )print dup ==\n",
+"}if\n",
+"}bind def\n",
+"//ResolveColorSpaceArrayRec 0//ResolveColorSpaceArray put\n",
+"/ResolveColorSpace\n",
+"{\n",
+"//PDFR_DEBUG{\n",
+"(ResolveColorSpace beg )print dup =\n",
+"}if\n",
+"dup//SimpleColorSpaceNames exch known not{\n",
+"dup//PDFColorSpaces exch//knownget exec{\n",
+"exch pop\n",
+"//PDFR_DEBUG{\n",
+"(ResolveColorSpace known )=\n",
+"}if\n",
+"}{\n",
+"dup\n",
+"//PDFReader/CurrentObject get/Context get/Resources get\n",
+"/ColorSpace//DoNothing//ResolveD exec\n",
+"exch//CheckColorSpace//ResolveD exec\n",
+"dup type/arraytype eq{\n",
+"//SubstitutePDFColorSpace exec\n",
+"//ResolveColorSpaceArray exec\n",
+"dup//PDFColorSpaces 4 2 roll put\n",
+"}if\n",
+"}ifelse\n",
+"}if\n",
+"//PDFR_DEBUG{\n",
+"(ResolveColorSpace end )print dup ==\n",
+"}if\n",
+"}bind def\n",
+"/CheckPattern\n",
+"{\n",
+"dup/PatternType//knownget exec{\n",
+"dup 1 ne{\n",
+"mark(Resource )4 index( is a shading, which can't be handled at level 2. )//error exec\n",
+"}if\n",
+"pop\n",
+"}if\n",
+"dup/Type knownget{\n",
+"/Pattern ne{\n",
+"mark(Resource )4 index( must have /Type/Pattern .)//error exec\n",
+"}if\n",
+"}if\n",
+"}bind def\n",
+"/PaintProc\n",
+"{/Context get\n",
+"//RunDelayedStream exec\n",
+"}bind def\n",
+"/ResolvePattern\n",
+"{\n",
+"dup\n",
+"userdict/PDFR_Patterns get\n",
+"exch//knownget exec{\n",
+"exch pop\n",
+"}{\n",
+"dup\n",
+"//PDFReader/CurrentObject get/Context get/Resources get\n",
+"/Pattern//DoNothing//ResolveD exec\n",
+"exch//CheckPattern//ResolveD exec\n",
+"dup dup/Context exch put\n",
+"dup/Resources//DoNothing//ResolveD exec pop\n",
+"dup/PaintProc//PaintProc put\n",
+"gsave userdict/PDFR_InitialGS get setgstate\n",
+"currentglobal exch false setglobal\n",
+"dup/Matrix get\n",
+"makepattern\n",
+"exch setglobal\n",
+"grestore\n",
+"dup userdict/PDFR_Patterns get\n",
+"4 2 roll\n",
+"put\n",
+"}ifelse\n",
+"}bind def\n",
+"/SetColor\n",
+"{//PDFR_DEBUG{\n",
+"(SetColor beg)=\n",
+"}if\n",
+"currentcolorspace dup type/nametype eq{\n",
+"pop setcolor\n",
+"}{\n",
+"0 get/Pattern eq{\n",
+"//ResolvePattern exec setpattern\n",
+"}{\n",
+"setcolor\n",
+"}ifelse\n",
+"}ifelse\n",
+"//PDFR_DEBUG{\n",
+"(SetColor end)=\n",
+"}if\n",
+"}bind def\n",
+"/ImageKeys 15 dict begin\n",
+"/BPC/BitsPerComponent def\n",
+"/CS/ColorSpace def\n",
+"/D/Decode def\n",
+"/DP/DecodeParms def\n",
+"/F/Filter def\n",
+"/H/Height def\n",
+"/IM/ImageMask def\n",
+"/I/Interpolate def\n",
+"/W/Width def\n",
+"currentdict end readonly def\n",
+"/ImageValues 15 dict begin\n",
+"/G/DeviceGray def\n",
+"/RGB/DeviceRGB def\n",
+"/CMYK/DeviceCMYK def\n",
+"/I/Indexed def\n",
+"/AHx/ASCIIHexDecode def\n",
+"/A85/ASCII85Decode def\n",
+"/LZW/LZWDecode def\n",
+"/Fl/FlateDecode def\n",
+"/RL/RunLengthDecode def\n",
+"/CCF/CCITTFaxDecode def\n",
+"/DCT/DCTDecode def\n",
+"currentdict end readonly def\n",
+"/GetColorSpaceRange\n",
+"{2 index/ColorSpace get\n",
+"dup type/arraytype eq{\n",
+"1 get\n",
+"}if\n",
+"exch//knownget exec{\n",
+"exch pop\n",
+"}if\n",
+"}bind def\n",
+"/DecodeArrays 15 dict begin\n",
+"/DeviceGray{[0 1]}def\n",
+"/DeviceRGB{[0 1 0 1 0 1]}def\n",
+"/DeviceCMYK{[0 1 0 1 0 1 0 1]}def\n",
+"/Indexed{\n",
+"dup/BitsPerComponent get 1 exch bitshift 1 sub[exch 0 exch]\n",
+"}def\n",
+"/Separation{[0 1]}def\n",
+"/CIEBasedA{[0 1]/RangeA//GetColorSpaceRange exec}def\n",
+"/CIEBasedABC{[0 1 0 1 0 1]/RangeABC//GetColorSpaceRange exec}def\n",
+"currentdict end readonly def\n",
+"/Substitute\n",
+"{1 index//knownget exec{\n",
+"exch pop\n",
+"}if\n",
+"}bind def\n",
+"/DebugImagePrinting\n",
+"{\n",
+"//PDFR_DEBUG{\n",
+"(Image :)=\n",
+"dup{exch//=only exec( )print ==\n",
+"}forall\n",
+"}if\n",
+"}bind def\n",
+"/CompleteImage\n",
+"{\n",
+"dup/ColorSpace known{\n",
+"dup/ColorSpace//CheckColorSpace//ResolveD exec pop\n",
+"}if\n",
+"dup/Decode known not{\n",
+"dup/ColorSpace//knownget exec{\n",
+"dup type/arraytype eq{\n",
+"0 get\n",
+"}if\n",
+"//DecodeArrays exch get exec\n",
+"}{\n",
+"[0 1]\n",
+"}ifelse\n",
+"1 index exch/Decode exch put\n",
+"}if\n",
+"dup/ImageMatrix[2 index/Width get 0 0 5 index/Height get neg\n",
+"0 7 index/Height get]put\n",
+"//DebugImagePrinting exec\n",
+"}bind def\n",
+"/CompleteInlineImage\n",
+"{\n",
+"//PDFR_DEBUG{\n",
+"(CompleteInlineImage beg)=\n",
+"}if\n",
+"dup/ImageType known not{\n",
+"dup/ImageType 1 put\n",
+"}if\n",
+"dup length dict exch{\n",
+"exch//ImageKeys//Substitute exec\n",
+"dup/Filter eq{\n",
+"exch//ImageValues//Substitute exec exch\n",
+"}if\n",
+"dup/ColorSpace eq{\n",
+"exch\n",
+"dup//ImageValues exch//knownget exec{\n",
+"exch pop\n",
+"}{\n",
+"//ResolveColorSpace exec\n",
+"}ifelse\n",
+"exch\n",
+"}if\n",
+"exch\n",
+"2 index 3 1 roll put\n",
+"}forall\n",
+"//CompleteImage exec\n",
+"dup/DataSource 2 copy get\n",
+"2 index//AppendFilters exec put\n",
+"//PDFR_DEBUG{\n",
+"(CompleteInlineImage end)=\n",
+"}if\n",
+"}bind def\n",
+"/CompleteOutlineImage\n",
+"{\n",
+"currentglobal exch dup gcheck setglobal\n",
+"//PDFR_DEBUG{\n",
+"(CompleteOutlineImage beg)=\n",
+"}if\n",
+"dup dup//MakeStreamReader exec/DataSource exch put\n",
+"dup/ImageType known not{\n",
+"//CompleteImage exec\n",
+"dup/ImageType 1 put\n",
+"dup/ColorSpace known{\n",
+"dup/ColorSpace//CheckColorSpace//ResolveD exec\n",
+"dup type/arraytype eq{\n",
+"//ResolveColorSpaceArray exec\n",
+"//SubstitutePDFColorSpace exec\n",
+"1 index exch/ColorSpace exch put\n",
+"}{\n",
+"pop\n",
+"}ifelse\n",
+"}if\n",
+"}if\n",
+"//PDFR_DEBUG{\n",
+"(CompleteOutlineImage end)=\n",
+"}if\n",
+"exch setglobal\n",
+"}bind def\n",
+"/DoImage\n",
+"{\n",
+"//PDFR_DEBUG{\n",
+"(DoImage beg)=\n",
+"}if\n",
+"gsave\n",
+"dup/ColorSpace//knownget exec{setcolorspace}if\n",
+"dup/ImageMask//knownget exec not{false}if\n",
+"{imagemask}{image}ifelse\n",
+"grestore\n",
+"//PDFR_DEBUG{\n",
+"(DoImage end)=\n",
+"}if\n",
+"}bind def\n",
+"/GSave\n",
+"{\n",
+"gsave\n",
+"//PDFReader/GraphicStateStackPointer get\n",
+"dup//GraphicStateStack exch get null eq{\n",
+"dup//GraphicStateStack exch//InitialGraphicState length dict put\n",
+"}if\n",
+"dup//GraphicStateStack exch get\n",
+"//GraphicState exch copy pop\n",
+"1 add//PDFReader exch/GraphicStateStackPointer exch put\n",
+"}bind def\n",
+"/GRestore\n",
+"{\n",
+"grestore\n",
+"//PDFReader/GraphicStateStackPointer get\n",
+"1 sub dup\n",
+"//PDFReader exch/GraphicStateStackPointer exch put\n",
+"//GraphicStateStack exch get\n",
+"//GraphicState copy pop\n",
+"}bind def\n",
+"/SetFont\n",
+"{dup//GraphicState exch/FontSize exch put\n",
+"//ResolveAndSetFont exec\n",
+"//GraphicState/FontMatrixNonHV currentfont/FontMatrix get 1 get 0 ne put\n",
+"}bind def\n",
+"/ShowText\n",
+"{//GraphicState/TextRenderingMode get 0 eq{\n",
+"//GraphicState/WordSpacing get 0\n",
+"32\n",
+"//GraphicState/CharacterSpacing get 0\n",
+"6 5 roll\n",
+"//GraphicState/FontMatrixNonHV get{\n",
+"[\n",
+"7 -2 roll pop\n",
+"5 -2 roll pop\n",
+"5 -1 roll\n",
+"{\n",
+"exch\n",
+"pop\n",
+"3 index add\n",
+"exch 2 index eq{3 index add}if\n",
+"4 1 roll\n",
+"}\n",
+"currentfont/FontMatrix get 0 get 0 ne{\n",
+"1 1 index length 1 sub getinterval cvx\n",
+"}if\n",
+"5 index\n",
+"cshow\n",
+"pop pop pop]\n",
+"xshow\n",
+"}{\n",
+"awidthshow\n",
+"}ifelse\n",
+"}{\n",
+"//GraphicState/CharacterSpacing get 0 eq\n",
+"//GraphicState/FontMatrixNonHV get not and\n",
+"//GraphicState/WordSpacing get 0 eq and{\n",
+"true charpath\n",
+"}{\n",
+"{\n",
+"exch\n",
+"pop 0\n",
+"currentpoint 5 4 roll\n",
+"( )dup 0 3 index put true charpath\n",
+"5 1 roll\n",
+"moveto rmoveto\n",
+"//GraphicState/CharacterSpacing get 0 rmoveto\n",
+"32 eq{\n",
+"//GraphicState/WordSpacing get 0 rmoveto\n",
+"}if\n",
+"}\n",
+"//GraphicState/FontMatrixNonHV get dup not exch{\n",
+"pop currentfont/FontMatrix get 0 get 0 ne\n",
+"}if{\n",
+"1 1 index length 1 sub getinterval cvx\n",
+"}if\n",
+"exch cshow\n",
+"}ifelse\n",
+"}ifelse\n",
+"}bind def\n",
+"/ShowTextBeg\n",
+"{//GraphicState/TextRenderingMode get 0 ne{\n",
+"currentpoint newpath moveto\n",
+"}if\n",
+"}bind def\n",
+"/ShowTextEnd\n",
+"{//GraphicState/TextRenderingMode get\n",
+"{dup 1 eq{\n",
+"stroke exit\n",
+"}if\n",
+"dup 2 eq{\n",
+"gsave fill grestore stroke exit\n",
+"}if\n",
+"dup 3 eq{\n",
+"currentpoint newpath moveto\n",
+"}if\n",
+"dup 4 eq{\n",
+"gsave fill grestore clip exit\n",
+"}if\n",
+"dup 5 eq{\n",
+"gsave stroke grestore clip exit\n",
+"}if\n",
+"dup 6 eq{\n",
+"gsave fill grestore gsave stroke grestore fill exit\n",
+"}if\n",
+"dup 7 eq{\n",
+"clip exit\n",
+"}if\n",
+"exit\n",
+"}loop\n",
+"pop\n",
+"}bind def\n",
+"/ShowTextWithGlyphPositioning\n",
+"{//ShowTextBeg exec\n",
+"{dup type/stringtype eq{\n",
+"//ShowText exec\n",
+"}{\n",
+"neg 1000 div//GraphicState/FontSize get mul 0 rmoveto\n",
+"}ifelse\n",
+"}forall\n",
+"//ShowTextEnd exec\n",
+"}bind def\n",
+"/CheckFont\n",
+"{dup/Type get/ExtGState ne{\n",
+"mark(Resource )3 index( must have /Type/ExtGState.)//error exec\n",
+"}if\n",
+"}bind def\n",
+"/SetTransfer\n",
+"{\n",
+"//PDFR_DEBUG{(SetTransfer beg )print count =}if\n",
+"dup type/arraytype eq 1 index xcheck not and{\n",
+"0 4 getinterval aload pop\n",
+"setcolortransfer\n",
+"}{\n",
+"settransfer\n",
+"}ifelse\n",
+"//PDFR_DEBUG{(SetTransfer end )print count =}if\n",
+"}bind def\n",
+"/CheckExtGState\n",
+"{dup/Type get/ExtGState ne{\n",
+"mark(Resource )3 index( must have /Type/ExtGState.)//error exec\n",
+"}if\n",
+"}bind def\n",
+"/CheckHalftone\n",
+"{dup/HalftoneType known not{\n",
+"mark(Resource )3 index( must have /HalftoneType.)//error exec\n",
+"}if\n",
+"}bind def\n",
+"/ResolveFunction\n",
+"{\n",
+"//PDFR_DEBUG{(ResolveFunction beg )print dup = count =}if\n",
+"2 copy get//IsObjRef exec{\n",
+"2 copy//DoNothing//ResolveD exec\n",
+"3 copy put pop\n",
+"}if\n",
+"2 copy get dup type/arraytype eq exch xcheck and not{\n",
+"2 copy get\n",
+"dup type/arraytype eq 1 index xcheck not and{\n",
+"dup length 1 sub -1 0{\n",
+"2 copy//DoNothing ResolveA\n",
+"dup/Identity eq{\n",
+"pop 2 copy{}put\n",
+"}{\n",
+"//FunctionToProc exec\n",
+"3 copy put pop\n",
+"}ifelse\n",
+"pop\n",
+"}for\n",
+"}{\n",
+"dup/Default eq{\n",
+"}{\n",
+"dup/Identity eq{\n",
+"pop{}\n",
+"}{dup type/nametype eq{\n",
+"//spotfunctions exch get\n",
+"}{\n",
+"//FunctionToProc exec\n",
+"}ifelse\n",
+"}ifelse\n",
+"}ifelse\n",
+"}ifelse\n",
+"3 copy put\n",
+"exch pop\n",
+"}{\n",
+"1 index exch get\n",
+"}ifelse\n",
+"//PDFR_DEBUG{(ResolveFunction end )print dup == count =}if\n",
+"}bind def\n",
+"/ResolveFunctionSafe\n",
+"{2 copy known{\n",
+"//ResolveFunction exec\n",
+"}if\n",
+"pop\n",
+"}bind def\n",
+"/CreateHalftoneThresholds\n",
+"{\n",
+"dup/Thresholds known not{\n",
+"dup/HalftoneType get 10 eq{\n",
+"dup dup//MakeStreamReader exec\n",
+"/Thresholds exch put\n",
+"}if\n",
+"dup/HalftoneType get dup 3 eq exch 6 eq or{\n",
+"dup dup//MakeStreamReader exec\n",
+"//BlockBuffer readstring pop\n",
+"dup length\n",
+"dup 0 eq{\n",
+"mark(Could not read Thresholds)//error exec\n",
+"}if\n",
+"string copy/Thresholds exch put\n",
+"dup/HalftoneType 3 put\n",
+"}if\n",
+"}if\n",
+"}bind def\n",
+"/SetExtGState\n",
+"{\n",
+"//PDFReader/CurrentObject get/Context get/Resources get\n",
+"/ExtGState//DoNothing//ResolveD exec\n",
+"exch//CheckExtGState//ResolveD exec\n",
+"dup/LW//knownget exec{\n",
+"setlinewidth\n",
+"}if\n",
+"dup/LC//knownget exec{\n",
+"setlinecap\n",
+"}if\n",
+"dup/LJ//knownget exec{\n",
+"setlinejoin\n",
+"}if\n",
+"dup/ML//knownget exec{\n",
+"setmeterlimit\n",
+"}if\n",
+"dup/D//knownget exec{\n",
+"setdash\n",
+"}if\n",
+"dup/RI//knownget exec{\n",
+"mark(Unimplemented ExtGState.RI)//error exec\n",
+"}if\n",
+"dup/OP//knownget exec{\n",
+"setoverprint\n",
+"}if\n",
+"dup/op//knownget exec{\n",
+"setoverprint\n",
+"}if\n",
+"dup/OPM//knownget exec{\n",
+"mark(Unimplemented ExtGState.OPM)//error exec\n",
+"}if\n",
+"dup/Font//knownget exec{\n",
+"mark(Unimplemented ExtGState.Font)//error exec\n",
+"}if\n",
+"dup/BG known{\n",
+"/BG//ResolveFunction exec\n",
+"setblackgeneration\n",
+"}if\n",
+"dup/BG2 known{\n",
+"/BG2//ResolveFunction exec\n",
+"dup/Default eq{\n",
+"//InitialExtGState/BG2 get\n",
+"}if\n",
+"setblackgeneration\n",
+"}if\n",
+"dup/UCR known{\n",
+"/UCR//ResolveFunction exec\n",
+"setundercolorremoval\n",
+"}if\n",
+"dup/UCR2 known{\n",
+"/UCR2//ResolveFunction exec\n",
+"dup/Default eq{\n",
+"//InitialExtGState/UCR2 get\n",
+"}if\n",
+"setundercolorremoval\n",
+"}if\n",
+"dup/TR known{\n",
+"/TR//ResolveFunction exec\n",
+"//SetTransfer exec\n",
+"}if\n",
+"dup/TR2 known{\n",
+"/TR2//ResolveFunction exec\n",
+"dup/Default eq{\n",
+"pop//InitialExtGState/TR2 get\n",
+"aload pop setcolortransfer\n",
+"}{\n",
+"//SetTransfer exec\n",
+"}ifelse\n",
+"}if\n",
+"dup/HT//knownget exec{\n",
+"dup/Default eq{\n",
+"pop//InitialExtGState/HT get\n",
+"sethalftone\n",
+"}{\n",
+"//PDFR_DEBUG{(Ht beg)=}if\n",
+"pop dup/HT//CheckHalftone//ResolveD exec\n",
+"/SpotFunction//ResolveFunctionSafe exec\n",
+"/TransferFunction//ResolveFunctionSafe exec\n",
+"null exch\n",
+"dup/HalftoneType get dup 5 eq exch dup 4 eq exch 2 eq or or{\n",
+"dup{\n",
+"dup//IsObjRef exec{\n",
+"pop\n",
+"1 index exch//CheckHalftone ResolveD\n",
+"}if\n",
+"dup type/dicttype eq{\n",
+"dup/SpotFunction//ResolveFunctionSafe exec\n",
+"/TransferFunction//ResolveFunctionSafe exec\n",
+"//CreateHalftoneThresholds exec\n",
+"dup/HalftoneType get 5 gt{\n",
+"4 3 roll pop\n",
+"dup 4 1 roll\n",
+"}if\n",
+"}if\n",
+"pop pop\n",
+"}forall\n",
+"}if\n",
+"//CreateHalftoneThresholds exec\n",
+"//PDFR_DEBUG{\n",
+"(HT:)=\n",
+"dup{\n",
+"1 index/Default eq{\n",
+"(Default <<)=\n",
+"exch pop\n",
+"{exch = ==}forall\n",
+"(>>)=\n",
+"}{\n",
+"exch = ==\n",
+"}ifelse\n",
+"}forall\n",
+"(HT end)= flush\n",
+"}if\n",
+"exch dup null ne{\n",
+"(Warning: Ignoring a halftone with a Level 3 component halftone Type )print dup/HalftoneType get =\n",
+"pop pop\n",
+"}{\n",
+"pop\n",
+"dup/HalftoneType get 5 gt{\n",
+"(Warning: Ignoring a Level 3 halftone Type )print dup/HalftoneType get =\n",
+"pop\n",
+"}{\n",
+"sethalftone\n",
+"}ifelse\n",
+"}ifelse\n",
+"//PDFR_DEBUG{(HT set)= flush}if\n",
+"}ifelse\n",
+"}if\n",
+"dup/FL//knownget exec{\n",
+"setflattness\n",
+"}if\n",
+"dup/SM//knownget exec{\n",
+"setsmoothness\n",
+"}if\n",
+"dup/SA//knownget exec{\n",
+"setstrokeadjust\n",
+"}if\n",
+"dup/BM//knownget exec{\n",
+"mark(Unimplemented ExtGState.BM)//error exec\n",
+"}if\n",
+"dup/SMask//knownget exec{\n",
+"mark(Unimplemented ExtGState.SMask)//error exec\n",
+"}if\n",
+"dup/CA//knownget exec{\n",
+"mark(Unimplemented ExtGState.CA)//error exec\n",
+"}if\n",
+"dup/ca//knownget exec{\n",
+"mark(Unimplemented ExtGState.ca)//error exec\n",
+"}if\n",
+"dup/AIS//knownget exec{\n",
+"mark(Unimplemented ExtGState.AIS)//error exec\n",
+"}if\n",
+"dup/TK//knownget exec{\n",
+"mark(Unimplemented ExtGState.TK)//error exec\n",
+"}if\n",
+"pop\n",
+"}bind def\n",
+"/CheckXObject\n",
+"{dup/Subtype get dup/Image ne exch dup/Form ne exch/PS ne and and{\n",
+"mark(Resource )3 index( must have /Subtype /Image or /Form or /PS.)//error exec\n",
+"}if\n",
+"}bind def\n",
+"/DoXObject\n",
+"{\n",
+"//PDFReader/CurrentObject get/Context get/Resources get\n",
+"/XObject//DoNothing//ResolveD exec\n",
+"exch//CheckXObject//ResolveD exec\n",
+"dup/Subtype get\n",
+"dup/Image eq{\n",
+"pop\n",
+"//CompleteOutlineImage exec\n",
+"//DoImage exec\n",
+"}{\n",
+"dup/PS eq{\n",
+"PDFR_DEBUG{\n",
+"(Executing a PS Xobject)=\n",
+"}if\n",
+"pop\n",
+"//RunDelayedStream exec\n",
+"}{\n",
+"dup/Form eq{\n",
+"pop\n",
+"PDFR_DEBUG{\n",
+"(Executing a Form XObject)=\n",
+"}if\n",
+"//PDFReader/CurrentObject get exch\n",
+"dup//PDFReader exch<< exch/Context exch >>/CurrentObject exch put\n",
+"dup/Matrix get concat\n",
+"dup/BBox get aload pop exch 3 index sub exch 2 index sub rectclip\n",
+"//RunDelayedStream exec\n",
+"//PDFReader exch/CurrentObject exch put\n",
+"}{\n",
+"mark exch(unimplemented XObject type )exch//error exec\n",
+"}ifelse\n",
+"}ifelse\n",
+"}ifelse\n",
+"}bind def\n",
+"/Operators 50 dict begin\n",
+"/q{//GSave exec}bind def\n",
+"/Q{//GRestore exec}bind def\n",
+"/cm{//TempMatrix astore concat}bind def\n",
+"/i{1 .min setflat}bind def\n",
+"/J/setlinecap load def\n",
+"/d/setdash load def\n",
+"/j/setlinejoin load def\n",
+"/w/setlinewidth load def\n",
+"/M/setmiterlimit load def\n",
+"/gs{SetExtGState}bind def\n",
+"/g/setgray load def\n",
+"/rg/setrgbcolor load def\n",
+"/k/setcmykcolor load def\n",
+"/cs{//ResolveColorSpace exec//SetColorSpaceSafe exec\n",
+"}bind def\n",
+"/sc/setcolor load def\n",
+"/scn{//SetColor exec}bind def\n",
+"/G/setgray load def\n",
+"/RG/setrgbcolor load def\n",
+"/K/setcmykcolor load def\n",
+"/CS//cs def\n",
+"/ri{SetColorRenderingIntent}bind def\n",
+"/SC/setcolor load def\n",
+"/SCN{//SetColor exec}bind def\n",
+"/m/moveto load def\n",
+"/l/lineto load def\n",
+"/c/curveto load def\n",
+"/v{currentpoint 6 2 roll curveto}bind def\n",
+"/y{2 copy curveto}bind def\n",
+"/re{\n",
+"4 2 roll moveto exch dup 0 rlineto 0 3 -1 roll rlineto neg 0 rlineto\n",
+"closepath\n",
+"}def\n",
+"/h/closepath load def\n",
+"/n/newpath load def\n",
+"/S/stroke load def\n",
+"/s{closepath stroke}bind def\n",
+"/f/fill load def\n",
+"/f*/eofill load def\n",
+"/B{gsave fill grestore stroke}bind def\n",
+"/b{closepath gsave fill grestore stroke}bind def\n",
+"/B*{gsave eofill grestore stroke}bind def\n",
+"/b*{closepath gsave eofill grestore stroke}bind def\n",
+"/W/clip load def\n",
+"/W*/eoclip load def\n",
+"/sh{\n",
+"ResolveShading\n",
+"dup/Background known{\n",
+"gsave\n",
+"dup/ColorSpace get setcolorspace\n",
+"dup/Background get aload pop setcolor\n",
+"pathbbox\n",
+"2 index sub exch 3 index sub exch\n",
+"rectfill\n",
+"grestore\n",
+"}if\n",
+"shfill\n",
+"}bind def\n",
+"/Do{//DoXObject exec}bind def\n",
+"/BI{currentglobal false setglobal<<}bind def\n",
+"/ID{>>\n",
+"dup/DataSource currentfile\n",
+"2 index/F//knownget exec{\n",
+"/A85 eq{\n",
+"0(~>)/SubFileDecode filter\n",
+"}if\n",
+"}if\n",
+"put\n",
+"//CompleteInlineImage exec\n",
+"exch setglobal\n",
+"//DoImage exec\n",
+"}bind def\n",
+"/EI{}bind def\n",
+"/BT{gsave//GraphicState/InitialTextMatrix get currentmatrix pop}bind def\n",
+"/ET{grestore}bind def\n",
+"/Tc{//GraphicState exch/CharacterSpacing exch put}bind def\n",
+"/TL{//GraphicState exch/TextLeading exch put}bind def\n",
+"/Tr{//GraphicState exch/TextRenderingMode exch put}bind def\n",
+"/Ts{\n",
+"mark(Unimplemented SetTextRise)//error exec\n",
+"}bind def\n",
+"/Tw{//GraphicState exch/WordSpacing exch put}bind def\n",
+"/Tz{\n",
+"mark(Unimplemented SetHorizontalTextScaling)//error exec\n",
+"}bind def\n",
+"/Td{translate 0 0 moveto}bind def\n",
+"/TD{dup neg//TL exec//Td exec}bind def\n",
+"/Tm{//GraphicState/InitialTextMatrix get setmatrix\n",
+"//TempMatrix astore concat\n",
+"0 0 moveto}bind def\n",
+"/T*{0//GraphicState/TextLeading get neg//Td exec}bind def\n",
+"/Tj{//ShowTextBeg exec//ShowText exec//ShowTextEnd exec}bind def\n",
+"/'{//T* exec//ShowText exec//ShowTextEnd exec}bind def\n",
+"/\"{3 2 roll//Tw exec exch//Tc exec//' exec}bind def\n",
+"/TJ//ShowTextWithGlyphPositioning def\n",
+"/Tf//SetFont def\n",
+"/d0/setcharwidth load def\n",
+"/d1/setcachedevice load def\n",
+"/BDC{pop pop}bind def\n",
+"/BMC{pop}bind def\n",
+"/EMC{}bind def\n",
+"/BX{BeginCompatibilitySection}bind def\n",
+"/EX{EndCompatibilitySection}bind def\n",
+"/DP{DefineMarkedContentPointWithPropertyList}bind def\n",
+"/MP{DefineMarkedContentPoint}bind def\n",
+"/PS{cvx exec}bind def\n",
+"currentdict end def\n",
+"//PDFR_STREAM{\n",
+"//Operators length dict begin\n",
+"//Operators{\n",
+"exch dup\n",
+"[exch//=only/exec load\n",
+"( )/print load\n",
+"8 7 roll\n",
+"dup type/arraytype eq{\n",
+"/exec load\n",
+"}if\n",
+"( )/print load\n",
+"]cvx\n",
+"def\n",
+"}forall\n",
+"currentdict end/Operators exch def\n",
+"}if\n",
+"/.registerencoding\n",
+"{pop pop\n",
+"}bind def\n",
+"/.defineencoding\n",
+"{def\n",
+"}bind def\n",
+"/.findencoding\n",
+"{load\n",
+"}bind def\n",
+0x00
+};
diff --git a/devices/vector/whitelst.c b/devices/vector/whitelst.c
new file mode 100644
index 000000000..cfd9c6730
--- /dev/null
+++ b/devices/vector/whitelst.c
@@ -0,0 +1,579 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* $Id: whitelst.c 11608 2010-08-06 11:11:40Z ken $ */
+/* The following font names are taken from the Adobe webs site
+ (http://www.adobe.com/type/browser/legal/embeddingeula.html)
+ and comprise a list of font names for which permission is granted to embed
+ either for preview and print or fully editable. These names are used to
+ override the embedding flags in TrueType fonts for pdfwrite and ps2write.
+
+ Its not entirely clear whether the font names should include TM and R
+ characters or not, and the only example font we have contains neither. For
+ the moment the fonts are stored without TM and R characters but this may
+ need to be altered.
+ */
+#include "whitelst.h"
+
+#define WHITE_LIST_SIZE 483
+
+static const char EmbeddingWhiteList[][WHITE_LIST_SIZE] = {
+"Aachen",
+"Adobe Arabic",
+"Adobe Caslon",
+"Adobe Devanagari",
+"Adobe Fangsong",
+"Adobe FanHeiti",
+"Adobe Garamond",
+"Adobe Gothic",
+"Adobe Hebrew",
+"Adobe Heiti",
+"Adobe Jenson",
+"Adobe Kaiti",
+"Adobe Ming",
+"Adobe Myungjo",
+"Adobe Naskh",
+"Adobe Pi",
+"Adobe Song",
+"Adobe Text",
+"Adobe Thai",
+"Adobe Wood Type",
+"Albertus",
+"Aldus",
+"Alexa",
+"Americana",
+"Amigo",
+"Andreas",
+"Antique Olive",
+"Apollo",
+"Arcadia",
+"Arcana",
+"Ariadne",
+"Arno",
+"Arnold Böcklin",
+"Ashley Script",
+"Astrology Pi",
+"Audio Pi",
+"Auriol",
+"Avenir",
+"Baker",
+"Baker Signet",
+"Balzano",
+"Banco",
+"Banshee",
+"Baskerville Cyrillic",
+"Bauer Bodoni",
+"Bell",
+"Bell Centennial",
+"Bell Gothic",
+"Belwe",
+"Bembo",
+"Berling",
+"Bermuda",
+"Bernhard",
+"Bernhard Modern",
+"Bickham Script",
+"Biffo",
+"Birch",
+"Blackoak",
+"Blue Island",
+"Bodoni",
+"Border Pi",
+"Briem Akademi",
+"Briem Script",
+"Brioso",
+"Bruno",
+"Brush Script",
+"Bulmer",
+"Bundesbahn Pi",
+"Caflisch Script",
+"Caflisch Script Web",
+"Calcite",
+"Caliban",
+"Calvert",
+"Candida",
+"Cantoria",
+"Caravan Borders",
+"Carolina",
+"Carta",
+"Cascade Script",
+"Caslon 3",
+"Caslon 540",
+"Caslon Open Face",
+"Castellar",
+"Caxton",
+"Centaur",
+"Century Expanded",
+"Century Old Style",
+"Chaparral",
+"Charlemagne",
+"Charme",
+"Cheq",
+"Clairvaux",
+"Clarendon",
+"Clearface Gothic",
+"Cloister",
+"Club Type",
+"Club Type Mercurius",
+"Cochin",
+"Conga Brava",
+"Conga Brava Stencil",
+"Conga Brava Std Stencil",
+"Cooper Black",
+"Copal",
+"Copperplate Gothic",
+"Coriander",
+"Corona",
+"Coronet",
+"Cottonwood",
+"Courier",
+"Critter",
+"Cronos",
+"Cutout",
+"Dante",
+"Decoration Pi",
+"Delphin",
+"DIN Schriften",
+"Diotima",
+"Diskus",
+"Dom Casual",
+"Dorchester Script",
+"Doric",
+"Duc de Berry",
+"Eccentric",
+"Egyptienne F",
+"Ehrhardt",
+"Electra",
+"Ellington",
+"Else NPL",
+"Engravers",
+"European Pi",
+"Eurostile",
+"Ex Ponto",
+"Excelsior",
+"Excelsior Cyrillic",
+"Fairfield",
+"Falstaff",
+"Fette Fraktur",
+"Flood",
+"Florens",
+"Flyer",
+"Folio",
+"Forte",
+"Fournier",
+"Franklin Gothic",
+"Freestyle Script",
+"Friz Quadrata",
+"Frutiger",
+"Fusaka",
+"Futura",
+"Galahad",
+"Game Pi",
+"Garamond 3",
+"Garamond Premier",
+"Garth Graphic",
+"Gazette",
+"Giddyup",
+"Giddyup Web",
+"Gill Floriated Capitals",
+"Gill Sans",
+"Glypha",
+"Gothic 13",
+"Goudy",
+"Goudy Text",
+"Granjon",
+"Graphite",
+"Guardi",
+"Hadriano",
+"Hardwood",
+"Heisei Kaku Gothic",
+"Heisei Maru Gothic",
+"Heisei Mincho",
+"Helvetica",
+"Helvetica Cyrillic",
+"Helvetica Inserat",
+"Helvetica Inserat Cyrillic",
+"Helvetica Neue",
+"Helvetica Rounded",
+"Herculanum",
+"Hiroshige",
+"Hobo",
+"Holiday Pi",
+"Horley Old Style",
+"HY Gothic",
+"HY GungSo",
+"HY Kak Headline Std",
+"HY Rounded Gothic",
+"Hypatia",
+"Hypatia Sans",
+"Immi 505",
+"Impact",
+"Impressum",
+"Industria",
+"Inflex",
+"Insignia",
+"Ironwood",
+"Isabella",
+"Italia",
+"ITC American Typewriter",
+"ITC Anna",
+"ITC Avant Garde Gothic",
+"ITC Bauhaus",
+"ITC Beesknees",
+"ITC Benguiat",
+"ITC Benguiat Gothic",
+"ITC Berkeley Oldstyle",
+"ITC Bookman",
+"ITC Caslon 224",
+"ITC Century",
+"ITC Century Handtooled",
+"ITC Cerigo",
+"ITC Cheltenham",
+"ITC Cheltenham Handtooled",
+"ITC Clearface",
+"ITC Cushing",
+"ITC Eras",
+"ITC Esprit",
+"ITC Fenice",
+"ITC Flora",
+"ITC Franklin Gothic",
+"ITC Galliard",
+"ITC Garamond",
+"ITC Garamond Handtooled",
+"ITC Giovanni",
+"ITC Goudy Sans",
+"ITC Highlander",
+"ITC Isadora",
+"ITC Kabel",
+"ITC Korinna",
+"ITC Leawood",
+"ITC Legacy Sans",
+"ITC Legacy Serif",
+"ITC Lubalin Graph",
+"ITC Machine",
+"ITC Mendoza Roman",
+"ITC Mona Lisa",
+"ITC Motter Corpus",
+"ITC New Baskerville",
+"ITC Novarese",
+"ITC Officina Sans",
+"ITC Officina Serif",
+"ITC Ozwald",
+"ITC Quorum",
+"ITC Serif Gothic",
+"ITC Slimbach",
+"ITC Souvenir",
+"ITC Stone Informal",
+"ITC Stone Sans",
+"ITC Stone Serif",
+"ITC Symbol",
+"ITC Tiepolo",
+"ITC Tiffany",
+"ITC Usherwood",
+"ITC Veljovic",
+"ITC Weidemann",
+"ITC Zapf Chancery",
+"ITC Zapf Dingbats",
+"Janson Text",
+"Jimbo",
+"Joanna",
+"Juniper",
+"Kabel",
+"Kaufmann",
+"Kazuraki SP2N",
+"Kepler",
+"Khaki",
+"Kigali",
+"Kinesis",
+"Kino",
+"Klang",
+"Koch Antiqua",
+"Kolo",
+"Kompakt",
+"Kozuka Gothic",
+"Kozuka Mincho",
+"Künstler Script",
+"Latin",
+"Leander Script",
+"Legault",
+"Letter Gothic",
+"Life",
+"LinoLetter",
+"Linoscript",
+"Linotext",
+"Linotype Centennial",
+"Linotype Didot",
+"Lithos",
+"LogoArl",
+"LogoCut",
+"LogoLine",
+"Lucida",
+"Lucida Math",
+"Lucida Sans",
+"Lucida Sans Typewriter",
+"Lucida Typewriter",
+"Madrone",
+"Manito",
+"Marigold",
+"Mathematical Pi",
+"Matura",
+"Maximus",
+"Medici Script",
+"Melior",
+"Memphis",
+"Mercurius",
+"Meridien",
+"Mesquite",
+"Mezz",
+"Mezz Web",
+"MICR",
+"Minion",
+"Minion Cyrillic",
+"Minion Web",
+"Minister",
+"Mistral",
+"Mojo",
+"Monoline Script",
+"Monotype Goudy Modern",
+"Monotype Grotesque",
+"Monotype Italian Old Style",
+"Monotype Modern",
+"Monotype Old Style",
+"Monotype Scotch Roman",
+"Monotype Script",
+"Montara",
+"Moonglow",
+"MVB Bossa Nova",
+"MVB Celestia Antiqua",
+"MVB Emmascript",
+"MVB Greymantle",
+"MVB Magnesium",
+"MVB Magnolia",
+"Myriad",
+"Myriad Arabic",
+"Myriad Hebrew",
+"Myriad Hebrew Cursive",
+"Myriad Web",
+"Mythos",
+"National Codes",
+"National Codes Pi",
+"Neue Hammer Unziale",
+"Neuland",
+"Neuzeit S",
+"New Aster",
+"New Berolina",
+"New Caledonia",
+"New Century Schoolbook",
+"News Gothic",
+"Notre Dame",
+"Nueva",
+"Nuptial Script",
+"Nyx",
+"Ocean Sans",
+"OCR-A",
+"OCR-B",
+"Octavian",
+"Old Claude",
+"Old Style 7",
+"Olympian",
+"Omnia",
+"Ondine",
+"Onyx",
+"Optima",
+"Orator",
+"Orgánica GMM",
+"Origami",
+"Ouch!",
+"Oxford",
+"Palace Script",
+"Palatino",
+"Parisian",
+"Park Avenue",
+"Peignot",
+"Pelican",
+"Penumbra",
+"Penumbra Flare",
+"Penumbra Half Serif",
+"Penumbra Sans",
+"Penumbra Serif",
+"Penumbra Web",
+"Pepita",
+"Pepperwood",
+"Perpetua",
+"Photina",
+"Plantin",
+"PMN Caecilia",
+"Poetica",
+"Pompeia",
+"Pompeijana",
+"Ponderosa",
+"Poplar",
+"Postino",
+"Present",
+"Prestige Elite",
+"Quake",
+"Rad",
+"Raleigh",
+"Raphael",
+"Reliq",
+"Reporter",
+"Revue",
+"Rockwell",
+"Romic",
+"Rosewood",
+"Rotation",
+"Rotis Sans Serif",
+"Rotis Semi Sans",
+"Rotis Semi Serif",
+"Rotis Serif",
+"Ruling Script",
+"Runic",
+"Russell Oblique",
+"Russell Square",
+"Rusticana",
+"Ruzicka Freehand",
+"Ryo Display",
+"Ryo Display PlusN",
+"Ryo Gothic",
+"Ryo Gothic PlusN",
+"Ryo Text",
+"Ryo Text PlusN",
+"Sabon",
+"San Marco",
+"Sanvito",
+"Sassafras",
+"Sava",
+"Serifa",
+"Serlio",
+"Serpentine",
+"Shannon",
+"Shelley",
+"Sho",
+"Shuriken Boy",
+"Silentium",
+"Simoncini Garamond",
+"Smaragd",
+"SMGothic",
+"SMMyungjo",
+"Snell Roundhand",
+"Sonata",
+"Source Sans",
+"Spartan",
+"Spectrum",
+"Spring",
+"Spumoni",
+"Stempel Garamond",
+"Stempel Schneidler",
+"Stencil",
+"Strayhorn",
+"Strumpf",
+"Studz",
+"Symbol",
+"Syntax",
+"Tekton",
+"Tempo",
+"Times",
+"Times Europa",
+"Times New Roman",
+"Times Ten",
+"Times Ten Cyrillic",
+"Toolbox",
+"Trade Gothic",
+"Trajan",
+"Trajan Sans",
+"Trump Mediäval",
+"Umbra",
+"Univers",
+"Universal",
+"University",
+"Utopia",
+"VAG Rounded",
+"Vectora",
+"Versailles",
+"Verve",
+"Visigoth",
+"Viva",
+"Voluta Script",
+"Warning Pi",
+"Warnock",
+"Waters Titling",
+"Weiss",
+"Wendy",
+"Wiesbaden Swing",
+"Wilhelm Klingspor Gotisch",
+"Wilke",
+"Willow",
+"Wittenberger Fraktur",
+"Zebrawood",
+"Zipty Do"
+};
+
+static int whitelist_strncmp(const char *s1, const char *s2, int length)
+{
+ int s1_index, s2_index, result = 0;
+
+ s1_index = s2_index = 0;
+
+ while (s2_index < length && s1[s1_index] != 0x00) {
+ while (s1[s1_index] == ' ')
+ s1_index++;
+ while (s2[s2_index] == ' ' && s2_index < length)
+ s2_index++;
+ if (s2_index > length) {
+ if (s1[s1_index] == 0x00)
+ return 0;
+ return 1;
+ }
+ if (s1[s1_index] == 0x00) {
+ if (s2_index > length)
+ return 0;
+ return -1;
+ }
+ if (s1[s1_index] == s2[s2_index]) {
+ s1_index++;
+ s2_index++;
+ continue;
+ }
+ if(s1[s1_index] < s2[s2_index])
+ return -1;
+ if(s1[s1_index] > s2[s2_index])
+ return 1;
+ }
+ return result;
+}
+
+int IsInWhiteList (const char *Name, int size)
+{
+ int low = 0, mid, high = WHITE_LIST_SIZE, test;
+
+ while (low < high) {
+ /* bisect current range */
+ mid = (low + high) / 2;
+ test = whitelist_strncmp(EmbeddingWhiteList[mid], Name, size);
+ if (test == 0)
+ return 1;
+ /* Not a match, select either upper or lower group and try again */
+ if(test < 0)
+ low = mid + 1;
+ else
+ high = mid - 1;
+ }
+ if (low == high) {
+ if (whitelist_strncmp(EmbeddingWhiteList[low], Name, size) == 0)
+ return 1;
+ }
+ return 0;
+}
diff --git a/devices/vector/whitelst.h b/devices/vector/whitelst.h
new file mode 100644
index 000000000..f2235a5a8
--- /dev/null
+++ b/devices/vector/whitelst.h
@@ -0,0 +1,25 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* $Id: whitelst.h 11099 2010-04-21 19:51:07Z robin $ */
+/* Declare routine for testing white listing of font names */
+
+#ifndef whitelst_INCLUDED
+# define whitelst_INCLUDED
+
+int IsInWhiteList (const char *Name, int size);
+
+#endif /* whitelst_INCLUDED */